Beispiel #1
0
        static Expression BindMagicMethod(PhpTypeInfo type, Type classCtx, Expression target, Expression ctx, TypeMethods.MagicMethods magic, string field, Expression rvalue = null)
        {
            var m = FindMagicMethod(type, magic);

            if (m != null)
            {
                var methods = m.Methods.Length == 1
                    ? (m.Methods[0].IsVisible(classCtx) ? m.Methods : Array.Empty <MethodInfo>())    // optimization for array[1]
                    : m.Methods.Where(x => x.IsVisible(classCtx)).ToArray();

                if (methods.Length != 0)
                {
                    switch (magic)
                    {
                    case TypeMethods.MagicMethods.__set:
                        // __set(name, value)
                        return(OverloadBinder.BindOverloadCall(typeof(void), target, methods, ctx, new Expression[] { Expression.Constant(field), rvalue }));

                    default:
                        // __get(name), __unset(name), __isset(name)
                        return(OverloadBinder.BindOverloadCall(methods[0].ReturnType, target, methods, ctx, new Expression[] { Expression.Constant(field) }));
                    }
                }
                else
                {
                    // TODO: ERR inaccessible
                }
            }

            return(null);
        }
Beispiel #2
0
        public sealed override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)
        {
            var restrictions = BindingRestrictions.Empty;
            var argsList     = new List <DynamicMetaObject>(args);

            // ctx, [Target], [TypeName], [RoutineName], [args...]
            Debug.Assert(target?.Value is Context);

            var ctx = target;

            target = PopTargetOrNull(argsList);
            var tinfo    = PopTypeInfoOrNull(argsList, ref restrictions);
            var nameExpr = PopNameExpression(argsList, ref restrictions);

            //
            Expression invocation;

            //
            var methods = ResolveMethods(ctx, tinfo, nameExpr, ref target, argsList, ref restrictions);

            if (methods != null && methods.Length != 0)
            {
                // TODO: visibility

                var expr_args = argsList.Select(x => x.Expression).ToArray();
                invocation = OverloadBinder.BindOverloadCall(_returnType, target?.Expression, methods, ctx.Expression, expr_args, tinfo);
            }
            else
            {
                invocation = BindMissingMethod(ctx, tinfo, nameExpr, target, argsList, ref restrictions);
            }

            // TODO: by alias or by value
            return(new DynamicMetaObject(invocation, restrictions));
        }
Beispiel #3
0
        protected override Expression BindMissingMethod(CallSiteContext bound)
        {
            if (bound.TargetType == null)   // already reported - class cannot be found
            {
                return(ConvertExpression.BindDefault(this.ReturnType));
            }

            var call = BinderHelpers.FindMagicMethod(bound.TargetType, (bound.TargetInstance == null) ? TypeMethods.MagicMethods.__callstatic : TypeMethods.MagicMethods.__call);

            if (call != null)
            {
                var name_expr = (_name != null) ? Expression.Constant(_name) : bound.IndirectName;

                // T.__callStatic(name, array)
                var call_args = new Expression[]
                {
                    name_expr,
                    BinderHelpers.NewPhpArray(bound.Arguments),
                };
                return(OverloadBinder.BindOverloadCall(_returnType, bound.TargetInstance, call.Methods, bound.Context, call_args, true, lateStaticType: bound.TargetType));
            }

            //
            return(base.BindMissingMethod(bound));
        }
Beispiel #4
0
        protected override Expression BindMissingMethod(CallSiteContext bound)
        {
            var name_expr = (_name != null) ? Expression.Constant(_name) : bound.IndirectName;

            // resolve target expression:
            var isobject = bound.TargetType != null;

            if (isobject == false)
            {
                /* Template:
                 * PhpException.MethodOnNonObject(name_expr); // aka PhpException.Throw(Error, method_called_on_non_object, name_expr)
                 * return NULL;
                 */
                var throwcall = Expression.Call(typeof(PhpException), "MethodOnNonObject", Array.Empty <Type>(), ConvertExpression.Bind(name_expr, typeof(string), bound.Context));
                return(Expression.Block(throwcall, ConvertExpression.BindDefault(this.ReturnType)));
            }

            var call = BinderHelpers.FindMagicMethod(bound.TargetType, TypeMethods.MagicMethods.__call);

            if (call != null)
            {
                // target.__call(name, array)
                var call_args = new Expression[]
                {
                    name_expr,
                    BinderHelpers.NewPhpArray(bound.Arguments),
                };
                return(OverloadBinder.BindOverloadCall(_returnType, bound.TargetInstance, call.Methods, bound.Context, call_args, false));
            }

            return(base.BindMissingMethod(bound));
        }
Beispiel #5
0
        protected override Expression BindMissingMethod(CallSiteContext bound)
        {
            var type = bound.TargetType;

            if (type == null)   // already reported - class cannot be found
            {
                return(ConvertExpression.BindDefault(this.ReturnType));
            }

            if (bound.TargetInstance != null && bound.CurrentTargetInstance != null) // it has been checked it is a subclass of TargetType
            {
                // ensure current scope's __call() is favoured over the specified class
                type = bound.CurrentTargetInstance.GetPhpTypeInfo();
            }

            var call = BinderHelpers.FindMagicMethod(type, (bound.TargetInstance == null) ? TypeMethods.MagicMethods.__callstatic : TypeMethods.MagicMethods.__call);

            if (call != null)
            {
                Expression[] call_args;

                var name_expr = (_name != null) ? Expression.Constant(_name) : bound.IndirectName;

                if (call.Methods.All(IsClrMagicCallWithParams))
                {
                    // Template: target.__call(name, arg1, arg2, ...)
                    // flatterns the arguments:
                    call_args = ArrayUtils.AppendRange(name_expr, bound.Arguments);
                }
                else
                {
                    // Template: target.__call(name, array)
                    // regular PHP behavior:
                    call_args = new Expression[]
                    {
                        name_expr,
                        BinderHelpers.NewPhpArray(bound.Arguments, bound.Context, bound.ClassContext),
                    };
                }

                return(OverloadBinder.BindOverloadCall(_returnType, bound.TargetInstance, call.Methods, bound.Context, call_args,
                                                       isStaticCallSyntax: true,
                                                       lateStaticType: bound.TargetType,
                                                       classContext: bound.ClassContext));
            }

            //
            return(base.BindMissingMethod(bound));
        }
Beispiel #6
0
        public static TObjectCreator BindToCreator(ConstructorInfo[] ctors)
        {
            // (Context ctx, PhpValue[] arguments)
            var ps = new ParameterExpression[] { Expression.Parameter(typeof(Context), "ctx"), Expression.Parameter(typeof(PhpValue[]), "argv") };

            // invoke targets
            var invocation = OverloadBinder.BindOverloadCall(typeof(object), null, ctors, ps[0], ps[1]);

            Debug.Assert(invocation.Type == typeof(object));

            // compile & create delegate
            var lambda = Expression.Lambda <TObjectCreator>(invocation, ctors[0].Name + "#" + ctors.Length, true, ps);

            return(lambda.Compile());
        }
Beispiel #7
0
        public static PhpCallable BindToPhpCallable(MethodBase[] targets)
        {
            Debug.Assert(targets.All(t => t.IsStatic), "Only static methods can be bound to PhpCallable delegate.");

            // (Context ctx, PhpValue[] arguments)
            var ps = new ParameterExpression[] { Expression.Parameter(typeof(Context), "ctx"), Expression.Parameter(typeof(PhpValue[]), "argv") };

            // invoke targets
            var invocation = OverloadBinder.BindOverloadCall(typeof(PhpValue), null, targets, ps[0], ps[1]);

            Debug.Assert(invocation.Type == typeof(PhpValue));

            // compile & create delegate
            var lambda = Expression.Lambda <PhpCallable>(invocation, targets[0].Name + "#" + targets.Length, true, ps);

            return(lambda.Compile());
        }
Beispiel #8
0
        public sealed override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)
        {
            var bound = CreateContext().ProcessArgs(target, args, HasTarget);

            Expression invocation;

            //
            var methods = ResolveMethods(bound);

            if (methods != null && methods.Length != 0)
            {
                // late static bound type, 'static' if available, otherwise the target type
                var lateStaticTypeArg = (object)bound.LateStaticType ?? bound.TargetType;

                if (bound.HasArgumentUnpacking)
                {
                    var args_var = Expression.Variable(typeof(PhpValue[]), "args_array");

                    /*
                     * args_var = ArgumentsToArray()
                     * call(...args_var...)
                     */

                    invocation = Expression.Block(new[] { args_var },
                                                  Expression.Assign(args_var, BinderHelpers.UnpackArgumentsToArray(methods, bound.Arguments, bound.Context, bound.ClassContext)),
                                                  OverloadBinder.BindOverloadCall(_returnType, bound.TargetInstance, methods, bound.Context, args_var,
                                                                                  isStaticCallSyntax: bound.IsStaticSyntax,
                                                                                  lateStaticType: lateStaticTypeArg)
                                                  );
                }
                else
                {
                    invocation = OverloadBinder.BindOverloadCall(_returnType, bound.TargetInstance, methods, bound.Context, bound.Arguments,
                                                                 isStaticCallSyntax: bound.IsStaticSyntax,
                                                                 lateStaticType: lateStaticTypeArg,
                                                                 classContext: bound.ClassContext);
                }
            }
            else
            {
                invocation = BindMissingMethod(bound);
            }

            // TODO: by alias or by value
            return(new DynamicMetaObject(invocation, bound.Restrictions));
        }
Beispiel #9
0
        protected override Expression BindMissingMethod(CallSiteContext bound)
        {
            var name_expr = (_name != null) ? Expression.Constant(_name) : bound.IndirectName;

            // resolve target expression:
            var isobject = bound.TargetType != null;

            if (isobject == false)
            {
                /* Template:
                 * PhpException.MethodOnNonObject(name_expr);
                 * return NULL;
                 */
                var throwcall = Expression.Call(typeof(PhpException), "MethodOnNonObject", Array.Empty <Type>(), ConvertExpression.Bind(name_expr, typeof(string), bound.Context));
                return(Expression.Block(throwcall, ConvertExpression.BindDefault(this.ReturnType)));
            }

            var call = BinderHelpers.FindMagicMethod(bound.TargetType, TypeMethods.MagicMethods.__call);

            if (call != null)
            {
                Expression[] call_args;

                if (call.Methods.All(IsClrMagicCallWithParams))
                {
                    // Template: target.__call(name, arg1, arg2, ...)
                    // flatterns the arguments:
                    call_args = ArrayUtils.AppendRange(name_expr, bound.Arguments);
                }
                else
                {
                    // Template: target.__call(name, array)
                    // regular PHP behavior:
                    call_args = new Expression[]
                    {
                        name_expr,
                        BinderHelpers.NewPhpArray(bound.Arguments, bound.Context, bound.ClassContext),
                    };
                }

                return(OverloadBinder.BindOverloadCall(_returnType, bound.TargetInstance, call.Methods, bound.Context, call_args, false));
            }

            return(base.BindMissingMethod(bound));
        }
Beispiel #10
0
        public static PhpInvokable BindToPhpInvokable(MethodInfo[] methods)
        {
            // (Context ctx, object target, PhpValue[] arguments)
            var ps = new ParameterExpression[] {
                Expression.Parameter(typeof(Context), "ctx"),
                Expression.Parameter(typeof(object), "target"),
                Expression.Parameter(typeof(PhpValue[]), "argv")
            };

            // invoke targets
            var invocation = OverloadBinder.BindOverloadCall(typeof(PhpValue), ps[1], methods, ps[0], ps[2]);

            Debug.Assert(invocation.Type == typeof(PhpValue));

            // compile & create delegate
            var lambda = Expression.Lambda <PhpInvokable>(invocation, methods[0].Name + "#" + methods.Length, true, ps);

            return(lambda.Compile());
        }
Beispiel #11
0
        protected override Expression BindMissingMethod(DynamicMetaObject ctx, PhpTypeInfo tinfo, DynamicMetaObject nameMeta, DynamicMetaObject target, IList <DynamicMetaObject> args, ref BindingRestrictions restrictions)
        {
            var call = BinderHelpers.FindMagicMethod(tinfo, target == null ? TypeMethods.MagicMethods.__callstatic : TypeMethods.MagicMethods.__call);

            if (call != null)
            {
                var name_expr = (_name != null) ? Expression.Constant(_name) : nameMeta?.Expression;

                // T.__callStatic(name, array)
                var call_args = new Expression[]
                {
                    name_expr,
                    BinderHelpers.NewPhpArray(ctx.Expression, args.Select(a => a.Expression)),
                };
                return(OverloadBinder.BindOverloadCall(_returnType, target?.Expression, call.Methods, ctx.Expression, call_args));
            }

            //
            return(base.BindMissingMethod(ctx, tinfo, nameMeta, target, args, ref restrictions));
        }
Beispiel #12
0
        public sealed override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)
        {
            var bound = CreateContext().ProcessArgs(target, args, HasTarget);

            Expression invocation;

            //
            var methods = ResolveMethods(bound);

            if (methods != null && methods.Length != 0)
            {
                invocation = OverloadBinder.BindOverloadCall(_returnType, bound.TargetInstance, methods, bound.Context, bound.Arguments, bound.TargetType);
            }
            else
            {
                invocation = BindMissingMethod(bound);
            }

            // TODO: by alias or by value
            return(new DynamicMetaObject(invocation, bound.Restrictions));
        }
Beispiel #13
0
        protected override Expression BindMissingMethod(DynamicMetaObject ctx, PhpTypeInfo tinfo, DynamicMetaObject nameMeta, DynamicMetaObject target, IList <DynamicMetaObject> args, ref BindingRestrictions restrictions)
        {
            var name_expr = (_name != null) ? Expression.Constant(_name) : nameMeta?.Expression;

            // resolve target expression:
            Expression target_expr;
            object     target_value;

            BinderHelpers.TargetAsObject(target, out target_expr, out target_value, ref restrictions);

            if (target_value == null)
            {
                /* Template:
                 * PhpException.MethodOnNonObject(name_expr); // aka PhpException.Throw(Error, method_called_on_non_object, name_expr)
                 * return NULL;
                 */
                var throwcall = Expression.Call(typeof(PhpException), "MethodOnNonObject", Array.Empty <Type>(), ConvertExpression.Bind(name_expr, typeof(string), ctx.Expression));
                return(Expression.Block(throwcall, ConvertExpression.BindDefault(this.ReturnType)));
            }

            Debug.Assert(ReflectionUtils.IsClassType(target_value.GetType().GetTypeInfo()));

            tinfo = target_value.GetPhpTypeInfo();

            var call = BinderHelpers.FindMagicMethod(tinfo, TypeMethods.MagicMethods.__call);

            if (call != null)
            {
                // target.__call(name, array)
                var call_args = new Expression[]
                {
                    name_expr,
                    BinderHelpers.NewPhpArray(ctx.Expression, args.Select(a => a.Expression)),
                };
                return(OverloadBinder.BindOverloadCall(_returnType, target.Expression, call.Methods, ctx.Expression, call_args));
            }

            return(base.BindMissingMethod(ctx, tinfo, nameMeta, target, args, ref restrictions));
        }
Beispiel #14
0
        public sealed override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)
        {
            var restrictions = BindingRestrictions.Empty;
            var argsList     = new List <DynamicMetaObject>(args);

            // get target and context

            DynamicMetaObject ctx;

            if (HasTarget)
            {
                ctx = args[0];
                argsList.RemoveAt(0);
            }
            else
            {
                ctx    = target;
                target = null;
            }

            //
            Expression invocation;

            var methods = ResolveMethods(ctx, ref target, argsList, ref restrictions);

            if (methods != null && methods.Length != 0)
            {
                var expr_args = argsList.Select(x => x.Expression).ToArray();
                invocation = OverloadBinder.BindOverloadCall(_returnType, target?.Expression, methods, ctx.Expression, expr_args);
            }
            else
            {
                invocation = BindMissingMethod(ctx, target, argsList, ref restrictions);
            }

            // TODO: by alias or by value
            return(new DynamicMetaObject(invocation, restrictions));
        }
Beispiel #15
0
        protected override Expression BindMissingMethod(DynamicMetaObject ctx, DynamicMetaObject target, IList <DynamicMetaObject> args, ref BindingRestrictions restrictions)
        {
            var tinfo = target.RuntimeType.GetPhpTypeInfo();
            var call  = (PhpMethodInfo)tinfo.DeclaredMethods[TypeMethods.MagicMethods.__call];

            if (call != null)
            {
                if (_name == null)
                {
                    throw new NotImplementedException();
                }

                // target.__call(name, array)
                var call_args = new Expression[]
                {
                    Expression.Constant(_name),
                    BinderHelpers.NewPhpArray(ctx.Expression, args.Select(a => a.Expression)),
                };
                return(OverloadBinder.BindOverloadCall(_returnType, target.Expression, call.Methods, ctx.Expression, call_args));
            }

            return(base.BindMissingMethod(ctx, target, args, ref restrictions));
        }
Beispiel #16
0
        public static TObjectCreator BindToCreator(Type type, ConstructorInfo[] ctors)
        {
            Debug.Assert(ctors.All(ctor => ctor is ConstructorInfo));
            Debug.Assert(ctors.All(ctor => ctor.DeclaringType == type));

            // (Context ctx, PhpValue[] arguments)
            var ps = new ParameterExpression[] { Expression.Parameter(typeof(Context), "ctx"), Expression.Parameter(typeof(PhpValue[]), "argv") };

            if (ctors.Length != 0)
            {
                // invoke targets
                var invocation = OverloadBinder.BindOverloadCall(type, null, ctors, ps[0], ps[1]);
                Debug.Assert(invocation.Type == type);

                // compile & create delegate
                var lambda = Expression.Lambda <TObjectCreator>(invocation, ctors[0].Name + "#" + ctors.Length, true, ps);
                return(lambda.Compile());
            }
            else
            {
                // TODO: lambda {error; NULL;}
                throw new ArgumentException("No constructor accessible for " + type.FullName);
            }
        }
Beispiel #17
0
        public static Expression BindField(PhpTypeInfo type, Type classCtx, Expression target, string field, Expression ctx, AccessFlags access, Expression rvalue)
        {
            if (access.Write() != (rvalue != null))
            {
                throw new ArgumentException();
            }

            // lookup a declared field
            for (var t = type; t != null; t = t.BaseType)
            {
                var expr = t.DeclaredFields.Bind(field, classCtx, target, ctx, (target != null) ? TypeFields.FieldKind.InstanceField : TypeFields.FieldKind.StaticField);
                if (expr != null)
                {
                    return(BindAccess(expr, ctx, access, rvalue));
                }
            }

            // lookup __get, __set, ...
            TypeMethods.MagicMethods magic;
            if (access.Write())
            {
                magic = TypeMethods.MagicMethods.__set;
            }
            else if (access.Unset())
            {
                magic = TypeMethods.MagicMethods.__unset;
            }
            else if (access.Isset())
            {
                magic = TypeMethods.MagicMethods.__isset;
            }
            else
            {
                magic = TypeMethods.MagicMethods.__get;
            }

            for (var t = type; t != null; t = t.BaseType)
            {
                var m = (PhpMethodInfo)t.DeclaredMethods[magic];
                if (m != null)
                {
                    if (m.Methods.Length == 1 && m.Methods[0].IsVisible(classCtx))
                    {
                        switch (magic)
                        {
                        case TypeMethods.MagicMethods.__set:
                            // __set(name, value)
                            return(OverloadBinder.BindOverloadCall(rvalue.Type, target, m.Methods, ctx, new Expression[] { Expression.Constant(field), rvalue }));

                        default:
                            // __get(name), __unset(name), __isset(name)
                            return(OverloadBinder.BindOverloadCall(m.Methods[0].ReturnType, target, m.Methods, ctx, new Expression[] { Expression.Constant(field) }));
                        }
                    }

                    break;
                }
            }

            // runtime fields
            var __runtimeflds = type.RuntimeFieldsHolder;

            if (__runtimeflds != null)
            {
                var __runtimeflds_field = Expression.Field(target, __runtimeflds);
                var key = Expression.Constant(new IntStringKey(field));

                return(BindArrayAccess(__runtimeflds_field, key, ctx, access, rvalue));
            }

            // TODO: dynamic

            //
            return(null);
        }