Bind() публичный статический Метод

Creates expression that converts arg to target type.
public static Bind ( Expression arg, Type target, Expression ctx = null ) : Expression
arg System.Linq.Expressions.Expression Source expression to be converted.
target System.Type Target type.
ctx System.Linq.Expressions.Expression Expression with current context.
Результат System.Linq.Expressions.Expression
Пример #1
0
        static Expression BindArrayAccess(Expression arr, Expression key, Expression ctx, AccessMask access, Expression rvalue)
        {
            Debug.Assert(key.Type == typeof(IntStringKey));

            if (access.EnsureObject())
            {
                // (arr ?? arr = []).EnsureItemObject(key)
                return(Expression.Call(
                           EnsureNotNullPhpArray(arr),
                           Cache.Operators.PhpArray_EnsureItemObject, key));
            }
            else if (access.EnsureArray())
            {
                // (arr ?? arr = []).EnsureItemArray(key)
                return(Expression.Call(
                           EnsureNotNullPhpArray(arr),
                           Cache.Operators.PhpArray_EnsureItemArray, key));
            }
            else if (access.EnsureAlias())
            {
                // (arr ?? arr = []).EnsureItemAlias(key)
                return(Expression.Call(
                           EnsureNotNullPhpArray(arr),
                           Cache.Operators.PhpArray_EnsureItemAlias, key));
            }
            else if (access.WriteAlias())
            {
                Debug.Assert(rvalue.Type == typeof(PhpAlias));
                rvalue = ConvertExpression.Bind(rvalue, typeof(PhpAlias), ctx);

                // (arr ?? arr = []).SetItemAlias(key, value)
                return(Expression.Call(
                           EnsureNotNullPhpArray(arr),
                           Cache.Operators.PhpArray_SetItemAlias, key, rvalue));
            }
            else if (access.Unset())
            {
                Debug.Assert(rvalue == null);

                // remove key

                // arr.RemoveKey(name)
                // TODO: if (arr != null)
                return(Expression.Call(arr, Cache.Operators.PhpArray_RemoveKey, key));
            }
            else if (access.Write())
            {
                rvalue = ConvertExpression.Bind(rvalue, typeof(PhpValue), ctx);

                return(Expression.Call(
                           EnsureNotNullPhpArray(arr),
                           Cache.Operators.PhpArray_SetItemValue, key, rvalue));
            }
            else
            {
                // read
                // TODO: (arr != null) ? arr[key] : (quiet ? void : ERROR)
                return(Expression.Call(arr, Cache.Operators.PhpArray_GetItemValue, key));
            }
        }
Пример #2
0
        public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)
        {
            var restrictions = BindingRestrictions.Empty;

            PhpTypeInfo phptype;
            Expression  target_expr;

            //
            var ctx     = args[0];
            var fldName = ResolveName(args, ref restrictions);

            //
            if (target.LimitType == typeof(PhpTypeInfo))    // static field
            {
                target_expr = null;
                phptype     = (PhpTypeInfo)target.Value;

                //
                restrictions = restrictions.Merge(BindingRestrictions.GetInstanceRestriction(target.Expression, phptype));
            }
            else
            {
                var isobject = BinderHelpers.TryTargetAsObject(target, out DynamicMetaObject instance);
                restrictions = restrictions.Merge(instance.Restrictions);

                if (isobject == false)
                {
                    var defaultexpr = ConvertExpression.BindDefault(_returnType);

                    if (!_access.Quiet())
                    {
                        // PhpException.VariableMisusedAsObject(target, _access.ReadRef)
                        var throwcall = Expression.Call(typeof(PhpException), "VariableMisusedAsObject", Array.Empty <Type>(),
                                                        ConvertExpression.BindToValue(target.Expression), Expression.Constant(_access.EnsureAlias()));
                        defaultexpr = Expression.Block(throwcall, defaultexpr);
                    }

                    return(new DynamicMetaObject(defaultexpr, restrictions));
                }

                phptype     = instance.RuntimeType.GetPhpTypeInfo();
                target_expr = target_expr = Expression.Convert(instance.Expression, instance.RuntimeType);
            }

            Debug.Assert(IsClassConst == (target_expr == null));

            //
            var getter = IsClassConst
                ? BinderHelpers.BindClassConstant(phptype, _classContext, fldName, ctx.Expression)
                : BinderHelpers.BindField(phptype, _classContext, target_expr, fldName, ctx.Expression, _access, null);

            if (getter != null)
            {
                //
                return(new DynamicMetaObject(ConvertExpression.Bind(getter, _returnType, ctx.Expression), restrictions));
            }

            // field not found
            throw new NotImplementedException();
        }
Пример #3
0
                bool TryBindArgument(int srcarg, Type targetType, out Expression expr)
                {
                    var args = _args;

                    if (srcarg >= 0 && srcarg < args.Length)
                    {
                        // skip RuntimeChain's
                        srcarg = MapToArgsIndex(srcarg);

                        //
                        if (srcarg < args.Length)
                        {
                            expr = args[srcarg];

                            // apply the runtime chain:
                            if (srcarg + 1 < args.Length)
                            {
                                BinderHelpers.TryAppendRuntimeChain(ref expr, args[srcarg + 1], _ctx, _classContext, targetType == typeof(PhpAlias));
                            }

                            //
                            if (targetType != null)
                            {
                                expr = ConvertExpression.Bind(expr, targetType, _ctx);
                            }

                            //
                            return(true);
                        }
                    }

                    // not provided
                    expr = null;
                    return(false);
                }
Пример #4
0
                public override Expression BindArgument(int srcarg, ParameterInfo targetparam = null)
                {
                    Debug.Assert(srcarg >= 0);

                    if (TryBindArgument(srcarg, targetparam?.ParameterType, out var expr))
                    {
                        return(expr);
                    }
                    else
                    {
                        if (targetparam != null)
                        {
                            if (targetparam.HasDefaultValue)
                            {
                                return(ConvertExpression.Bind(Expression.Constant(targetparam.DefaultValue), targetparam.ParameterType, _ctx));
                            }
                            else
                            {
                                var defaultValueAttr = targetparam.GetCustomAttribute <DefaultValueAttribute>();
                                if (defaultValueAttr != null)
                                {
                                    return(ConvertExpression.Bind(BindDefaultValue(targetparam.Member.DeclaringType, defaultValueAttr), targetparam.ParameterType, _ctx));
                                }
                            }

                            //
                            return(ConvertExpression.BindDefault(targetparam.ParameterType));
                        }

                        return(Expression.Constant(null, typeof(object)));
                    }
                }
Пример #5
0
                public override Expression BindArgument(int srcarg, ParameterInfo targetparam = null)
                {
                    Debug.Assert(srcarg >= 0);

                    if (srcarg < _args.Length)
                    {
                        var expr = _args[srcarg];

                        return((targetparam != null)
                            ? ConvertExpression.Bind(expr, targetparam.ParameterType, _ctx)
                            : expr);
                    }
                    else
                    {
                        if (targetparam != null)
                        {
                            if (targetparam.HasDefaultValue)
                            {
                                return(ConvertExpression.Bind(Expression.Constant(targetparam.DefaultValue), targetparam.ParameterType, _ctx));
                            }

                            return(ConvertExpression.BindDefault(targetparam.ParameterType));
                        }

                        return(Expression.Constant(null, typeof(object)));
                    }
                }
                public override Expression BindArgument(int srcarg, ParameterInfo targetparam = null)
                {
                    Debug.Assert(srcarg >= 0);

                    if (srcarg < _args.Length)
                    {
                        var expr = _args[srcarg];

                        return((targetparam != null)
                            ? ConvertExpression.Bind(expr, targetparam.ParameterType, _ctx)
                            : expr);
                    }
                    else
                    {
                        if (targetparam != null)
                        {
                            if (targetparam.HasDefaultValue)
                            {
                                return(ConvertExpression.Bind(Expression.Constant(targetparam.DefaultValue), targetparam.ParameterType, _ctx));
                            }
                            else if (targetparam.GetCustomAttribute <DefaultValueAttribute>() != null)
                            {
                                // TODO: DefaultValueAttribute
                                //Debug.Fail("default value lost");
                            }

                            return(ConvertExpression.BindDefault(targetparam.ParameterType));
                        }

                        return(Expression.Constant(null, typeof(object)));
                    }
                }
Пример #7
0
        public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)
        {
            var restrictions = BindingRestrictions.Empty;

            Expression target_expr;
            object     target_value;

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

            var fldName      = ResolveName(args, ref restrictions);
            var runtime_type = target_value.GetType();

            //
            if (target_expr.Type != runtime_type)
            {
                restrictions = restrictions.Merge(BindingRestrictions.GetTypeRestriction(target_expr, runtime_type));
                target_expr  = Expression.Convert(target_expr, runtime_type);
            }

            var getter = BinderHelpers.BindField(runtime_type.GetPhpTypeInfo(), _classContext, target_expr, fldName, null, _access, null);

            if (getter != null)
            {
                //
                return(new DynamicMetaObject(ConvertExpression.Bind(getter, _returnType), restrictions));
            }

            // field not found
            throw new NotImplementedException();
        }
Пример #8
0
                public override Expression BindArgument(int srcarg, ParameterInfo targetparam = null)
                {
                    Debug.Assert(srcarg >= 0);

                    // cache the argument value

                    var key = new TmpVarKey()
                    {
                        Priority = 0 /*first*/, ArgIndex = srcarg, Prefix = "arg"
                    };
                    TmpVarValue value;

                    if (!_tmpvars.TryGetValue(key, out value))
                    {
                        value = new TmpVarValue();

                        value.TrueInitializer  = Expression.ArrayIndex(_argsarray, Expression.Constant(srcarg));
                        value.FalseInitializer = ConvertExpression.BindDefault(value.TrueInitializer.Type); // ~ default(_argsarray.Type.GetElementType())
                        value.Expression       = Expression.Variable(value.TrueInitializer.Type, "arg_" + srcarg);

                        //
                        _tmpvars[key] = value;
                    }

                    if (targetparam != null)
                    {
                        // create specialized variable with default value
                        if (targetparam.HasDefaultValue)
                        {
                            var @default         = targetparam.DefaultValue;
                            var defaultValueExpr = Expression.Constant(@default);
                            var defaultValueStr  = (@default != null) ? @default.ToString() : "NULL";

                            //
                            var key2 = new TmpVarKey()
                            {
                                Priority = 1 /*after key*/, ArgIndex = srcarg, Prefix = "arg(" + defaultValueStr + ")"
                            };
                            TmpVarValue value2;
                            if (!_tmpvars.TryGetValue(key2, out value2))
                            {
                                value2 = new TmpVarValue();

                                value2.TrueInitializer  = ConvertExpression.Bind(value.Expression, targetparam.ParameterType, _ctx);   // reuse the value already obtained from argv
                                value2.FalseInitializer = ConvertExpression.Bind(defaultValueExpr, value2.TrueInitializer.Type, _ctx); // ~ default(targetparam)
                                value2.Expression       = Expression.Variable(value2.TrueInitializer.Type, "arg_" + srcarg + "_" + defaultValueStr);

                                //
                                _tmpvars[key2] = value2;
                            }

                            return(value2.Expression);   // already converted to targetparam.ParameterType
                        }
                    }

                    return((targetparam == null)
                        ? value.Expression
                        : ConvertExpression.Bind(value.Expression, targetparam.ParameterType, _ctx));
                }
Пример #9
0
        public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)
        {
            bool hasTargetInstance = (target.LimitType != typeof(TargetTypeParam));

            var bound = new CallSiteContext(!hasTargetInstance)
            {
                ClassContext = _classContext,
                Name         = _name
            }
            .ProcessArgs(target, args, hasTargetInstance);

            if (hasTargetInstance)
            {
                var isobject = bound.TargetType != null;
                if (isobject == false)
                {
                    var defaultexpr = ConvertExpression.BindDefault(_returnType);

                    if (!_access.Quiet())
                    {
                        // PhpException.VariableMisusedAsObject(target, _access.ReadRef)
                        var throwcall = BinderHelpers.VariableMisusedAsObject(target.Expression, _access.EnsureAlias());
                        defaultexpr = Expression.Block(throwcall, defaultexpr);
                    }

                    return(new DynamicMetaObject(defaultexpr, bound.Restrictions));
                }

                // instance := (T)instance
                bound.TargetInstance = Expression.Convert(bound.TargetInstance, bound.TargetType.Type);
            }

            Debug.Assert(IsClassConst ? (bound.TargetInstance == null) : true);

            //
            var getter = IsClassConst
                ? BinderHelpers.BindClassConstant(bound.TargetType, bound.ClassContext, bound.Name, bound.Context)
                : BinderHelpers.BindField(bound.TargetType, bound.ClassContext, bound.TargetInstance, bound.Name, bound.Context, _access, null);

            if (getter != null)
            {
                //
                return(new DynamicMetaObject(ConvertExpression.Bind(getter, _returnType, bound.Context), bound.Restrictions));
            }

            if (IsClassConst)
            {
                // error: constant not defined
                // ...
            }

            // unreachable: property not found
            throw new InvalidOperationException($"{bound.TargetType.Name}::{bound.Name} could not be resolved.");
        }
Пример #10
0
        /// <summary>
        /// Binds recursion check for property magic method.
        /// </summary>
        static Expression InvokeHandler(Expression ctx, Expression target, string field, Expression getter, AccessMask access, Expression @default = null, Type resultType = null)
        {
            // default
            resultType = resultType ?? Cache.Types.PhpValue[0];
            @default   = @default ?? Expression.Field(null, Cache.Properties.PhpValue_Null); // TODO: ERR field not found
            @default   = ConvertExpression.Bind(@default, resultType, ctx);

            if (getter == null)
            {
                return(@default);
            }
            else
            {
                /* Template:
                 * var token;
                 * try {
                 *   return (token = new Context.RecursionCheckToken(_ctx, target, access))).IsInRecursion)
                 *     ? default
                 *     : getter;
                 * } finally {
                 *   token.Dispose();
                 * }
                 */

                // recursion prevention key ~ do not invoke getter twice for the same field
                int subkey1 = access.Write() ? 1 : access.Unset() ? 2 : access.Isset() ? 3 : 4;
                int subkey  = field.GetHashCode() ^ (1 << subkey1);

                // Template: RecursionCheckToken token;
                var tokenvar = Expression.Variable(typeof(Context.RecursionCheckToken), "token");

                // Template: token = new RecursionCheckToken(_ctx, (object)target, (int)subkey))
                var tokenassign = Expression.Assign(tokenvar, Expression.New(Cache.RecursionCheckToken.ctor_ctx_object_int,
                                                                             ctx, Expression.Convert(target, Cache.Types.Object[0]), Expression.Constant(subkey)));

                // bind getter access
                if (access.EnsureAlias() || access.EnsureArray() || access.EnsureObject())
                {
                    getter = BindAccess(getter, ctx, access, rvalue: null);
                }

                getter = ConvertExpression.Bind(getter, resultType, ctx);

                //
                return(Expression.Block(resultType,
                                        new[] { tokenvar },
                                        Expression.TryFinally(
                                            Expression.Condition(Expression.Property(tokenassign, Cache.RecursionCheckToken.IsInRecursion),
                                                                 @default,
                                                                 getter),
                                            Expression.Call(tokenvar, Cache.RecursionCheckToken.Dispose)
                                            )));
            }
        }
Пример #11
0
        public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)
        {
            bool hasTargetInstance = (target.LimitType != typeof(TargetTypeParam));

            var bound = new CallSiteContext()
            {
                ClassContext = _classContext,
                Name         = _name
            }
            .ProcessArgs(target, args, hasTargetInstance);

            if (hasTargetInstance)
            {
                var isobject = bound.TargetType != null;
                if (isobject == false)
                {
                    var defaultexpr = ConvertExpression.BindDefault(_returnType);

                    if (!_access.Quiet())
                    {
                        // PhpException.VariableMisusedAsObject(target, _access.ReadRef)
                        var throwcall = Expression.Call(typeof(PhpException), "VariableMisusedAsObject", Array.Empty <Type>(),
                                                        ConvertExpression.BindToValue(target.Expression), Expression.Constant(_access.EnsureAlias()));
                        defaultexpr = Expression.Block(throwcall, defaultexpr);
                    }

                    return(new DynamicMetaObject(defaultexpr, bound.Restrictions));
                }

                // instance := (T)instance
                bound.TargetInstance = Expression.Convert(bound.TargetInstance, bound.TargetType.Type.AsType());
            }

            Debug.Assert(IsClassConst ? (bound.TargetInstance == null) : true);

            //
            var getter = IsClassConst
                ? BinderHelpers.BindClassConstant(bound.TargetType, bound.ClassContext, bound.Name, bound.Context)
                : BinderHelpers.BindField(bound.TargetType, bound.ClassContext, bound.TargetInstance, bound.Name, bound.Context, _access, null);

            if (getter != null)
            {
                //
                return(new DynamicMetaObject(ConvertExpression.Bind(getter, _returnType, bound.Context), bound.Restrictions));
            }

            // field not found
            throw new NotImplementedException();
        }
Пример #12
0
        public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)
        {
            var restrictions = BindingRestrictions.Empty;

            PhpTypeInfo phptype;
            Expression  target_expr;

            //
            var fldName = ResolveName(args, ref restrictions);

            //
            if (target.LimitType == typeof(PhpTypeInfo))    // static field
            {
                target_expr = null;
                phptype     = (PhpTypeInfo)target.Value;

                //
                restrictions = restrictions.Merge(BindingRestrictions.GetInstanceRestriction(target.Expression, target_expr));
            }
            else
            {
                // instance field
                object target_value;
                BinderHelpers.TargetAsObject(target, out target_expr, out target_value, ref restrictions);

                var runtime_type = target_value.GetType();

                //
                if (target_expr.Type != runtime_type)
                {
                    restrictions = restrictions.Merge(BindingRestrictions.GetTypeRestriction(target_expr, runtime_type));
                    target_expr  = Expression.Convert(target_expr, runtime_type);
                }

                phptype = runtime_type.GetPhpTypeInfo();
            }

            //
            var getter = BinderHelpers.BindField(phptype, _classContext, target_expr, fldName, null, _access, null);

            if (getter != null)
            {
                //
                return(new DynamicMetaObject(ConvertExpression.Bind(getter, _returnType), restrictions));
            }

            // field not found
            throw new NotImplementedException();
        }
Пример #13
0
                public override Expression BindParams(int fromarg, Type element_type)
                {
                    var values = new List <Expression>();

                    for (int i = fromarg; i < _args.Length; i++)
                    {
                        values.Add(ConvertExpression.Bind(_args[i], element_type, _ctx));
                    }

                    if (values.Count == 0)
                    {
                        // TODO: return static singleton with empty array
                    }

                    return(Expression.NewArrayInit(element_type, values));
                }
Пример #14
0
        /// <summary>
        /// Binds recursion check for property magic method.
        /// </summary>
        static Expression InvokeHandler(Expression ctx, Expression target, string field, Expression getter, AccessFlags access, Expression @default = null, Type resultType = null)
        {
            // default
            resultType = resultType ?? Cache.Types.PhpValue[0];
            @default   = @default ?? Expression.Field(null, Cache.Properties.PhpValue_Null); // TODO: ERR field not found
            @default   = ConvertExpression.Bind(@default, resultType, ctx);

            if (getter == null)
            {
                return(@default);
            }
            else
            {
                /* Template:
                 * var token;
                 * try {
                 *   return (token = new Context.RecursionCheckToken(_ctx, target, access))).IsInRecursion)
                 *     ? default
                 *     : getter;
                 * } finally {
                 *   token.Dispose();
                 * }
                 */

                int subkey = access.Write() ? 1 : access.Unset() ? 2 : access.Isset() ? 3 : 4;  // recursion prevention scope

                // Template: RecursionCheckToken token;
                var tokenvar = Expression.Variable(typeof(Context.RecursionCheckToken), "token");

                // Template: token = new RecursionCheckToken(_ctx, (object)target, (int)access))
                var tokenassign = Expression.Assign(tokenvar, Expression.New(Cache.RecursionCheckToken.ctor_ctx_object_int,
                                                                             ctx, Expression.Convert(target, Cache.Types.Object[0]), Expression.Constant(subkey)));

                //
                return(Expression.Block(resultType,
                                        new[] { tokenvar },
                                        Expression.TryFinally(
                                            Expression.Condition(Expression.Property(tokenassign, Cache.RecursionCheckToken.IsInRecursion),
                                                                 @default,
                                                                 ConvertExpression.Bind(getter, resultType, ctx)),
                                            Expression.Call(tokenvar, Cache.RecursionCheckToken.Dispose)
                                            )));
            }
        }
Пример #15
0
                public override Expression BindParams(int fromarg, Type element_type)
                {
                    var count = _args.Length - fromarg;

                    if (count <= 0)
                    {
                        // empty array:

                        // return static singleton with empty array
                        // Template: Array.Empty<element_type>()
                        return(Expression.Call(typeof(Array), "Empty", new[] { element_type }));
                    }

                    var values = new Expression[count];

                    for (int i = 0; i < count; i++)
                    {
                        values[i] = ConvertExpression.Bind(_args[fromarg + i], element_type, _ctx);
                    }

                    return(Expression.NewArrayInit(element_type, values));
                }
Пример #16
0
        /// <summary>
        /// Creates expression with overload resolution.
        /// </summary>
        /// <param name="treturn">Expected return type.</param>
        /// <param name="target">Target instance in case of instance methods.</param>
        /// <param name="methods">List of methods to resolve overload.</param>
        /// <param name="ctx">Expression of current runtime context.</param>
        /// <param name="args">Provides arguments.</param>
        /// <param name="lateStaticType">Optional type used to statically invoke the method (late static type).</param>
        /// <returns>Expression representing overload call with resolution or <c>null</c> in case binding should be restarted with updated array of <paramref name="methods"/>.</returns>
        static Expression BindOverloadCall(Type treturn, Expression target, ref MethodBase[] methods, Expression ctx, ArgumentsBinder args, PhpTypeInfo lateStaticType = null)
        {
            if (methods == null || args == null)
            {
                throw new ArgumentNullException();
            }

            // overload resolution

            // order methods

            /*
             * cost1 = CostOf(m1)
             * cost2 = CostOf(m2)
             * ...
             * best = Min(cost1, .., costN)
             * if (cost1 == best) m1( ... )
             * ...
             * default(T) // unreachable
             */

            var locals = new List <ParameterExpression>();
            var body   = new List <Expression>();

            Expression invoke = Expression.Default(treturn);

            //
            if (methods.Length == 0)
            {
                throw new ArgumentException();    // no method to call
                // invoke = ERR
            }
            if (methods.Length == 1)
            {
                // just this piece of code is enough:
                invoke = ConvertExpression.Bind(BindCastToFalse(BinderHelpers.BindToCall(target, methods[0], ctx, args, lateStaticType), DoCastToFalse(methods[0], treturn)), treturn, ctx);
            }
            else
            {
                var list = new List <MethodCostInfo>();

                // collect arguments, that have same type across all provided methods => we don't have to check costof()
                var makeCostOf = DifferentArgumentTypeIndexes(methods); // parameters which costof() have to be calculated and compared to others

                // costX = CostOf(mX)
                foreach (var m in methods)
                {
                    ConversionCost mincost; // minimal cost resolved in compile time
                    var            expr_cost = BindCostOf(m, args, makeCostOf, out mincost);
                    if (mincost >= ConversionCost.NoConversion)
                    {
                        continue;   // we don't have to try this overload
                    }
                    var const_cost = expr_cost as ConstantExpression;
                    var cost_var   = Expression.Variable(typeof(ConversionCost), "cost" + list.Count);

                    if (const_cost == null)
                    {
                        body.Add(Expression.Assign(cost_var, expr_cost));   // costX = CostOf(m)
                    }

                    list.Add(new MethodCostInfo()
                    {
                        Method      = m,
                        CostExpr    = (Expression)const_cost ?? cost_var,
                        MinimalCost = mincost,
                    });
                }

                if (list.Count != 0)
                {
                    var minresolved = ConversionCost.Error;
                    foreach (var c in list.Where(c => c.ResolvedCost.HasValue))
                    {
                        minresolved = CostOf.Min(minresolved, c.ResolvedCost.Value);
                    }

                    if (list.All(c => c.ResolvedCost.HasValue))
                    {
                        for (int i = 0; i < list.Count; i++)
                        {
                            if (list[i].ResolvedCost.Value == minresolved)
                            {
                                list = new List <MethodCostInfo>()
                                {
                                    list[i]
                                };
                                break;
                            }
                        }
                    }
                    else
                    {
                        // if minimal cost is greater than some resolved cost, we can reduce it
                        if (minresolved < ConversionCost.Error)
                        {
                            for (int i = list.Count - 1; i >= 0; i--)
                            {
                                if (list[i].MinimalCost > minresolved)
                                {
                                    list.RemoveAt(i);
                                }
                            }
                        }
                    }
                }

                if (list.Count < methods.Length)
                {
                    // restart binding with reduced list
                    methods = list.Select(l => l.Method).ToArray();
                    return(null);
                }

                Debug.Assert(list.Count != 0);

                // declare costI local variables
                locals.AddRange(list.Select(l => l.CostExpr).OfType <ParameterExpression>());

                // best = Min( cost1, .., costN )
                var        expr_best     = Expression.Variable(typeof(ConversionCost), "best");
                var        min_cost_cost = typeof(CostOf).GetMethod("Min", typeof(ConversionCost), typeof(ConversionCost));
                Expression minexpr       = list[0].CostExpr;
                for (int i = 1; i < list.Count; i++)
                {
                    minexpr = Expression.Call(min_cost_cost, list[i].CostExpr, minexpr);
                }
                body.Add(Expression.Assign(expr_best, minexpr));
                locals.Add(expr_best);

                // switch over method costs
                for (int i = list.Count - 1; i >= 0; i--)
                {
                    // (best == costI) mI(...) : ...

                    var m     = list[i].Method;
                    var mcall = ConvertExpression.Bind(BindCastToFalse(BinderHelpers.BindToCall(target, m, ctx, args, lateStaticType), DoCastToFalse(m, treturn)), treturn, ctx);
                    invoke = Expression.Condition(Expression.Equal(expr_best, list[i].CostExpr), mcall, invoke);
                }
            }

            //
            body.Insert(0, args.CreatePreamble(locals));

            //
            body.Add(invoke);

            // return Block { ... ; invoke; }
            return(Expression.Block(treturn, locals, body));
        }
Пример #17
0
                public override Expression BindParams(int fromarg, Type element_type)
                {
                    /*
                     * length = argc - fromarg;
                     * IF (length > 0)
                     *   Array.Copy(values, argv, fromarg)
                     * ELSE
                     *   Array.Empty()
                     */

                    /*
                     */

                    var var_length = Expression.Variable(typeof(int), "params_length");
                    var var_array  = Expression.Variable(element_type.MakeArrayType(), "params_array");

                    //
                    Expression expr_emptyarr = Expression.NewArrayInit(element_type);
                    Expression expr_newarr   = Expression.Assign(var_array, Expression.NewArrayBounds(element_type, var_length)); // array = new [length];

                    if (element_type == _argsarray.Type.GetElementType())
                    {
                        if (fromarg == 0)
                        {
                            // return argv;
                            return(_argsarray);
                        }
                        else
                        {
                            // static void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
                            var array_copy = typeof(Array).GetMethod("Copy", typeof(Array), typeof(int), typeof(Array), typeof(int), typeof(int)); // TODO: to cache

                            expr_newarr = Expression.Block(
                                expr_newarr,
                                Expression.Call(array_copy, _argsarray, Expression.Constant(fromarg), var_array, Expression.Constant(0), var_length),   // Array.Copy(argv, fromarg, array, 0, length)
                                var_array
                                );
                        }
                    }
                    else
                    {
                        /* newarr = new T[length];
                         * while (--length >= 0) newarr[length] = convert(argv[fromarg + length]);
                         * lblend: return newarr;
                         */
                        var lblend = Expression.Label("lblend");
                        expr_newarr = Expression.Block(var_array.Type,
                                                       expr_newarr,
                                                       Expression.Loop(
                                                           Expression.IfThenElse(
                                                               Expression.GreaterThanOrEqual(Expression.PreDecrementAssign(var_length), Expression.Constant(0)),
                                                               Expression.Assign(
                                                                   Expression.ArrayAccess(var_array, var_length),
                                                                   ConvertExpression.Bind(Expression.ArrayIndex(_argsarray, Expression.Add(Expression.Constant(fromarg), var_length)), element_type, _ctx)),
                                                               Expression.Break(lblend)
                                                               )),
                                                       Expression.Label(lblend),
                                                       var_array);
                    }

                    return(Expression.Block(
                               var_array.Type,
                               new ParameterExpression[] { var_length, var_array },
                               Expression.Assign(var_length, Expression.Subtract(BindArgsCount(), Expression.Constant(fromarg))), // length = argc - fromarg;
                               Expression.Condition(
                                   Expression.GreaterThan(var_length, Expression.Constant(0)),                                    // return (length > 0) ? newarr : emptyarr;
                                   expr_newarr, expr_emptyarr)));
                }
Пример #18
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));
        }
Пример #19
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));
        }
Пример #20
0
                public override Expression BindArgument(int srcarg, ParameterInfo targetparam = null)
                {
                    Debug.Assert(srcarg >= 0);

                    // cache the argument value

                    var key = new TmpVarKey()
                    {
                        Priority = 0 /*first*/, ArgIndex = srcarg, Prefix = "arg"
                    };
                    TmpVarValue value;

                    if (!_tmpvars.TryGetValue(key, out value))
                    {
                        value = new TmpVarValue();

                        value.TrueInitializer  = Expression.ArrayIndex(_argsarray, Expression.Constant(srcarg));
                        value.FalseInitializer = ConvertExpression.BindDefault(value.TrueInitializer.Type); // ~ default(_argsarray.Type.GetElementType())
                        value.Expression       = Expression.Variable(value.TrueInitializer.Type, "arg_" + srcarg);

                        //
                        _tmpvars[key] = value;
                    }

                    if (targetparam != null)
                    {
                        DefaultValueAttribute defaultValueAttr = null;

                        // create specialized variable with default value
                        if (targetparam.HasDefaultValue || (defaultValueAttr = targetparam.GetCustomAttribute <DefaultValueAttribute>()) != null)
                        {
                            Debug.Assert(!targetparam.ParameterType.IsByRef);   // parameter with a default value cannot be byref (at least we don't expect it)

                            // just for debugging purposes:
                            var defaultValueStr = defaultValueAttr != null
                                ? defaultValueAttr.FieldName
                                : targetparam.DefaultValue?.ToString() ?? "NULL";

                            // the default value expression
                            var defaultValueExpr = defaultValueAttr != null
                                ? BindDefaultValue(targetparam.Member.DeclaringType, defaultValueAttr)
                                : Expression.Constant(targetparam.DefaultValue);

                            //
                            var key2 = new TmpVarKey()
                            {
                                Priority = 1 /*after key*/, ArgIndex = srcarg, Prefix = "default(" + defaultValueStr + ")"
                            };
                            TmpVarValue value2;
                            if (!_tmpvars.TryGetValue(key2, out value2))
                            {
                                value2 = new TmpVarValue();

                                value2.TrueInitializer  = ConvertExpression.Bind(value.Expression, targetparam.ParameterType, _ctx);   // reuse the value already obtained from argv
                                value2.FalseInitializer = ConvertExpression.Bind(defaultValueExpr, value2.TrueInitializer.Type, _ctx); // ~ default(targetparam)
                                value2.Expression       = Expression.Variable(value2.TrueInitializer.Type, "default_" + srcarg + "_" + defaultValueStr);

                                //
                                _tmpvars[key2] = value2;
                            }

                            return(value2.Expression);   // already converted to targetparam.ParameterType
                        }
                    }

                    if (targetparam == null)
                    {
                        return(value.Expression);
                    }
                    else
                    {
                        var ptype = targetparam.ParameterType;

                        // TODO: ptype.IsByRef -> implement write-back after the invocation
                        if (ptype.IsByRef)
                        {
                            ptype = ptype.GetElementType(); // LINQ will create a local variable for it implicitly
                        }

                        return(ConvertExpression.Bind(value.Expression, ptype, _ctx));
                    }
                }
Пример #21
0
        public static Expression BindField(PhpTypeInfo type, Type classCtx, Expression target, string field, Expression ctx, AccessMask access, Expression rvalue)
        {
            if (access.Write() != (rvalue != null))
            {
                throw new ArgumentException();
            }

            // lookup a declared field
            for (var t = type; t != null; t = t.BaseType)
            {
                foreach (var p in t.DeclaredFields.GetPhpProperties(field))
                {
                    if (p.IsStatic == (target == null) && p.IsVisible(classCtx))
                    {
                        return(BindAccess(p.Bind(ctx, target), ctx, access, rvalue));
                    }
                }
            }

            //
            // runtime fields
            //

            if (type.RuntimeFieldsHolder != null)                                         // we don't handle magic methods without the runtime fields
            {
                var runtimeflds = Expression.Field(target, type.RuntimeFieldsHolder);     // Template: target->__runtime_fields
                var fieldkey    = Expression.Constant(new IntStringKey(field));           // Template: IntStringKey(field)
                var resultvar   = Expression.Variable(Cache.Types.PhpValue[0], "result"); // Template: PhpValue result;

                // Template: runtimeflds != null && runtimeflds.TryGetValue(field, out result)
                var trygetfield   = Expression.AndAlso(Expression.ReferenceNotEqual(runtimeflds, Expression.Constant(null)), Expression.Call(runtimeflds, Cache.Operators.PhpArray_TryGetValue, fieldkey, resultvar));
                var containsfield = Expression.AndAlso(Expression.ReferenceNotEqual(runtimeflds, Expression.Constant(null)), Expression.Call(runtimeflds, Cache.Operators.PhpArray_ContainsKey, fieldkey));

                Expression result;

                //
                if (access.EnsureObject())
                {
                    // (object)target->field->

                    // Template: runtimeflds.EnsureObject(key)
                    result = Expression.Call(EnsureNotNullPhpArray(runtimeflds), Cache.Operators.PhpArray_EnsureItemObject, fieldkey);

                    var __get = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__get, field, null);
                    if (__get != null)
                    {
                        // Template: runtimeflds.Contains(key) ? runtimeflds.EnsureObject(key) : ( __get(key) ?? runtimeflds.EnsureObject(key))
                        return(Expression.Condition(containsfield,
                                                    Expression.Call(runtimeflds, Cache.Operators.PhpArray_EnsureItemObject, fieldkey),
                                                    InvokeHandler(ctx, target, field, __get, access, result, typeof(object))));
                    }
                    else
                    {
                        return(result);
                    }
                }
                else if (access.EnsureArray())
                {
                    // (IPhpArray)target->field[] =
                    result = Expression.Call(EnsureNotNullPhpArray(runtimeflds), Cache.Operators.PhpArray_EnsureItemArray, fieldkey);

                    var __get = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__get, field, null);
                    if (__get != null)
                    {
                        // Template: runtimeflds.Contains(key) ? runtimeflds.EnsureArray(key) : ( __get(key) ?? runtimeflds.EnsureArray(key))
                        return(Expression.Condition(containsfield,
                                                    Expression.Call(runtimeflds, Cache.Operators.PhpArray_EnsureItemArray, fieldkey),
                                                    InvokeHandler(ctx, target, field, __get, access, result, typeof(IPhpArray))));
                    }
                    else
                    {
                        // runtimeflds.EnsureItemArray(key)
                        return(result);
                    }
                }
                else if (access.EnsureAlias())
                {
                    // (PhpAlias)&target->field

                    result = Expression.Call(EnsureNotNullPhpArray(runtimeflds), Cache.Operators.PhpArray_EnsureItemAlias, fieldkey);

                    var __get = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__get, field, null);
                    if (__get != null)
                    {
                        // Template: runtimeflds.Contains(key) ? runtimeflds.EnsureItemAlias(key) : ( __get(key) ?? runtimeflds.EnsureItemAlias(key))
                        return(Expression.Condition(containsfield,
                                                    Expression.Call(runtimeflds, Cache.Operators.PhpArray_EnsureItemAlias, fieldkey),
                                                    InvokeHandler(ctx, target, field, __get, access, result, typeof(PhpAlias))));
                    }
                    else
                    {
                        // runtimeflds.EnsureItemAlias(key)
                        return(result);
                    }
                }
                else if (access.Unset())
                {
                    // unset(target->field)
                    // Template: if (!runtimeflds.RemoveKey(key)) __unset(key)

                    var removekey = Expression.Call(runtimeflds, Cache.Operators.PhpArray_RemoveKey, fieldkey);
                    var __unset   = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__unset, field, null);
                    if (__unset != null)
                    {
                        return(Expression.IfThen(
                                   Expression.OrElse(Expression.ReferenceEqual(runtimeflds, Expression.Constant(null)), Expression.IsFalse(removekey)),
                                   InvokeHandler(ctx, target, field, __unset, access, Expression.Block(), typeof(void))));
                    }
                    else
                    {
                        // if (runtimeflds != null) runtimeflds.RemoveKey(key)
                        return(Expression.IfThen(
                                   Expression.ReferenceNotEqual(runtimeflds, Expression.Constant(null)),
                                   removekey));
                    }
                }
                else if (access.Write())
                {
                    var __set = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__set, field, rvalue);

                    if (access.WriteAlias())
                    {
                        // target->field = (PhpAlias)&rvalue
                        Debug.Assert(rvalue.Type == typeof(PhpAlias));
                        rvalue = ConvertExpression.Bind(rvalue, typeof(PhpAlias), ctx);

                        // EnsureNotNull(runtimeflds).SetItemAlias(key, rvalue)
                        result = Expression.Call(EnsureNotNullPhpArray(runtimeflds), Cache.Operators.PhpArray_SetItemAlias, fieldkey, rvalue);

                        if (__set != null)
                        {
                            // if (ContainsKey(key)) ? runtimeflds.SetItemAlias(rvalue) : (__set(key, rvalue) ?? runtimeflds.SetItemAlias(key, rvalue)
                            return(Expression.Condition(containsfield,
                                                        Expression.Call(runtimeflds, Cache.Operators.PhpArray_SetItemAlias, fieldkey, rvalue),
                                                        InvokeHandler(ctx, target, field, __set, access, result, typeof(void))));
                        }
                        else
                        {
                            return(result);
                        }
                    }
                    else
                    {
                        // target->field = rvalue
                        rvalue = ConvertExpression.Bind(rvalue, typeof(PhpValue), ctx);

                        /* Template:
                         * return runtimeflds != null && runtimeflds.ContainsKey(field)
                         *   ? runtimeflds.SetItemValue(key, rvalue)
                         *   : (__set(field, value) ?? runtimeflds.SetItemValue(key, value))
                         */

                        result = Expression.Call(EnsureNotNullPhpArray(runtimeflds), Cache.Operators.PhpArray_SetItemValue, fieldkey, rvalue);

                        if (__set != null)
                        {
                            return(Expression.Condition(containsfield,
                                                        Expression.Call(runtimeflds, Cache.Operators.PhpArray_SetItemValue, fieldkey, rvalue),
                                                        InvokeHandler(ctx, target, field, __set, access, result, typeof(void))));
                        }
                        else
                        {
                            return(result);
                        }
                    }
                }
                else if (access.Isset())
                {
                    // isset(target->field)

                    var __isset = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__isset, field, null);

                    // Template: (TryGetField(result) || (bool)__isset(key)) ? true : void
                    result = Expression.Condition(Expression.OrElse(
                                                      trygetfield,
                                                      ConvertExpression.BindToBool(InvokeHandler(ctx, target, field, __isset, access))),
                                                  Expression.Property(null, Cache.Properties.PhpValue_True),
                                                  Expression.Field(null, Cache.Properties.PhpValue_Void));
                }
                else
                {
                    // = target->field

                    /* Template:
                     * return runtimeflds.TryGetValue(field, out result) ? result : (__get(field) ?? ERR);
                     */
                    var __get = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__get, field, null);
                    result = Expression.Condition(trygetfield,
                                                  resultvar,
                                                  InvokeHandler(ctx, target, field, __get, access)); // TODO: @default = { ThrowError; return null; }
                }

                //
                return(Expression.Block(result.Type, new[] { resultvar }, result));
            }

            // TODO: IDynamicMetaObject

            //
            return(null);
        }
Пример #22
0
        static Expression BindAccess(Expression expr, Expression ctx, AccessMask access, Expression rvalue)
        {
            if (access.EnsureObject())
            {
                if (expr.Type == typeof(PhpAlias))
                {
                    // ((PhpAlias)fld).EnsureObject()
                    expr = Expression.Call(expr, Cache.Operators.PhpAlias_EnsureObject);
                }
                else if (expr.Type == typeof(PhpValue))
                {
                    // ((PhpValue)fld).EnsureObject()
                    expr = Expression.Call(expr, Cache.Operators.PhpValue_EnsureObject);
                }
                else
                {
                    // getter // TODO: ensure it is not null
                    Debug.Assert(!expr.Type.GetTypeInfo().IsValueType);
                }
            }
            else if (access.EnsureArray())
            {
                if (expr.Type == typeof(PhpAlias))
                {
                    // ((PhpAlias)fld).EnsureArray()
                    expr = Expression.Call(expr, Cache.Operators.PhpAlias_EnsureArray);
                }
                else if (expr.Type == typeof(PhpValue))
                {
                    // ((PhpValue)fld).EnsureArray()
                    expr = Expression.Call(expr, Cache.Operators.PhpValue_EnsureArray);
                }
                else if (expr.Type == typeof(PhpArray))
                {
                    // (PhpArray)fld // TODO: ensure it is not null
                }
                else
                {
                    // getter
                }
            }
            else if (access.EnsureAlias())
            {
                if (expr.Type == typeof(PhpAlias))
                {
                    // (PhpAlias)getter
                }
                else if (expr.Type == typeof(PhpValue))
                {
                    // ((PhpValue)fld).EnsureAlias()
                    expr = Expression.Call(expr, Cache.Operators.PhpValue_EnsureAlias);
                }
                else
                {
                    // getter // cannot read as reference
                }
            }
            else if (access.WriteAlias())
            {
                // write alias

                Debug.Assert(rvalue.Type == typeof(PhpAlias));
                rvalue = ConvertExpression.Bind(rvalue, typeof(PhpAlias), ctx);

                if (expr.Type == typeof(PhpAlias))
                {
                    // ok
                }
                else if (expr.Type == typeof(PhpValue))
                {
                    // fld = PhpValue.Create(alias)
                    rvalue = Expression.Call(typeof(PhpValue).GetMethod("Create", Cache.Types.PhpAlias), rvalue);
                }
                else
                {
                    // fld is not aliasable
                    Debug.Assert(false, "Cannot assign aliased value to field of type " + expr.Type.ToString());
                    rvalue = ConvertExpression.Bind(rvalue, expr.Type, ctx);
                }

                expr = Expression.Assign(expr, rvalue);
            }
            else if (access.Unset())
            {
                Debug.Assert(rvalue == null);

                expr = Expression.Assign(expr, ConvertExpression.BindDefault(expr.Type));
            }
            else if (access.Write())
            {
                // write by value

                if (expr.Type == typeof(PhpAlias))
                {
                    // Template: fld.Value = (PhpValue)value
                    expr = Expression.Assign(Expression.PropertyOrField(expr, "Value"), ConvertExpression.Bind(rvalue, typeof(PhpValue), ctx));
                }
                else if (expr.Type == typeof(PhpValue))
                {
                    // Template: Operators.SetValue(ref fld, (PhpValue)value)
                    expr = Expression.Call(Cache.Operators.SetValue_PhpValueRef_PhpValue, expr, ConvertExpression.Bind(rvalue, typeof(PhpValue), ctx));
                }
                else
                {
                    // Template: fld = value
                    // default behaviour by value to value
                    expr = Expression.Assign(expr, ConvertExpression.Bind(rvalue, expr.Type, ctx));
                }
            }
            else if (access.ReadCopy())
            {
                // dereference & copy
                if (expr.Type == typeof(PhpValue))
                {
                    // Template: value.GetValue().DeepCopy()
                    expr = Expression.Call(Expression.Call(expr, Cache.Operators.PhpValue_GetValue), Cache.Operators.PhpValue_DeepCopy);
                }
                else if (expr.Type == typeof(PhpAlias))
                {
                }
            }

            //
            return(expr);
        }
Пример #23
0
 public static Expression NewPhpArray(Expression ctx, IEnumerable <Expression> values)
 {
     return(Expression.Call(
                typeof(PhpArray), "New", Cache.Types.Empty, // PhpArray.New(values[])
                Expression.NewArrayInit(typeof(PhpValue), values.Select(x => ConvertExpression.Bind(x, typeof(PhpValue), ctx)))));
 }
Пример #24
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:
            var isobject = BinderHelpers.TryTargetAsObject(target, out DynamicMetaObject instance);

            restrictions = restrictions.Merge(instance.Restrictions);

            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), ctx.Expression));
                return(Expression.Block(throwcall, ConvertExpression.BindDefault(this.ReturnType)));
            }

            Debug.Assert(ReflectionUtils.IsClassType(instance.RuntimeType.GetTypeInfo()));

            tinfo = instance.RuntimeType.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));
        }
Пример #25
0
        static Expression BindAccess(Expression expr, Expression ctx, AccessMask access, Expression rvalue)
        {
            if (access.EnsureObject())
            {
                if (expr.Type == typeof(PhpAlias))
                {
                    // ((PhpAlias)fld).EnsureObject()
                    expr = Expression.Call(expr, Cache.Operators.PhpAlias_EnsureObject);
                }
                else if (expr.Type == typeof(PhpValue))
                {
                    // ((PhpValue)fld).EnsureObject()
                    expr = Expression.Call(expr, Cache.Operators.PhpValue_EnsureObject);
                }
                else
                {
                    // getter // TODO: ensure it is not null
                    Debug.Assert(!expr.Type.GetTypeInfo().IsValueType);
                }
            }
            else if (access.EnsureArray())
            {
                if (expr.Type == typeof(PhpAlias))
                {
                    // ((PhpAlias)fld).EnsureArray()
                    expr = Expression.Call(expr, Cache.Operators.PhpAlias_EnsureArray);
                }
                else if (expr.Type == typeof(PhpValue))
                {
                    // ((PhpValue)fld).EnsureArray()
                    expr = Expression.Call(expr, Cache.Operators.PhpValue_EnsureArray);
                }
                else if (expr.Type == typeof(PhpArray))
                {
                    // (PhpArray)fld // TODO: ensure it is not null
                }
                else
                {
                    // Operators.EnsureArray( fld )
                    // TODO: string
                    expr = Expression.Call(Cache.Operators.Object_EnsureArray, expr);
                }
            }
            else if (access.EnsureAlias())
            {
                if (expr.Type == typeof(PhpAlias))
                {
                    // (PhpAlias)getter
                }
                else if (expr.Type == typeof(PhpValue))
                {
                    // ((PhpValue)fld).EnsureAlias()
                    expr = Expression.Call(expr, Cache.Operators.PhpValue_EnsureAlias);
                }
                else
                {
                    // getter // cannot read as reference
                }
            }
            else if (access.WriteAlias())
            {
                // write alias

                Debug.Assert(rvalue.Type == typeof(PhpAlias));
                rvalue = ConvertExpression.Bind(rvalue, typeof(PhpAlias), ctx);

                if (expr.Type == typeof(PhpAlias))
                {
                    // ok
                }
                else if (expr.Type == typeof(PhpValue))
                {
                    // fld = PhpValue.Create(alias)
                    rvalue = Expression.Call(typeof(PhpValue).GetMethod("Create", Cache.Types.PhpAlias), rvalue);
                }
                else
                {
                    // fld is not aliasable
                    Debug.Assert(false, "Cannot assign aliased value to field of type " + expr.Type.ToString());
                    rvalue = ConvertExpression.Bind(rvalue, expr.Type, ctx);
                }

                expr = Expression.Assign(expr, rvalue);
            }
            else if (access.Unset())
            {
                Debug.Assert(rvalue == null);

                expr = Expression.Assign(expr, ConvertExpression.BindDefault(expr.Type));
            }
            else if (access.Write())
            {
                // write by value

                if (expr.Type == typeof(PhpAlias))
                {
                    // Template: fld.Value = (PhpValue)value
                    expr = Expression.Assign(Expression.Field(expr, Cache.PhpAlias.Value), ConvertExpression.Bind(rvalue, typeof(PhpValue), ctx));
                }
                else if (expr.Type == typeof(PhpValue))
                {
                    // Template: Operators.SetValue(ref fld, (PhpValue)value)
                    expr = Expression.Call(Cache.Operators.SetValue_PhpValueRef_PhpValue, expr, ConvertExpression.Bind(rvalue, typeof(PhpValue), ctx));
                }
                else
                {
                    // Template: fld = value
                    // default behaviour by value to value
                    expr = Expression.Assign(expr, ConvertExpression.Bind(rvalue, expr.Type, ctx));
                }
            }
            else if (access.ReadValue())
            {
                // dereference
                if (expr.Type == typeof(PhpValue))
                {
                    // Template: value.GetValue().DeepCopy()
                    expr = Expression.Call(expr, Cache.Operators.PhpValue_GetValue);
                }
                else if (expr.Type == typeof(PhpAlias))
                {
                    // Template: alias.Value // expecting alias cannot be null ref
                    expr = Expression.Field(expr, Cache.PhpAlias.Value);
                }
            }
            else if (access.ReadValueCopy())
            {
                // dereference & copy
                if (expr.Type == typeof(PhpValue))
                {
                    // Template: value.GetValue().DeepCopy()
                    expr = Expression.Call(Expression.Call(expr, Cache.Operators.PhpValue_GetValue), Cache.Operators.PhpValue_DeepCopy);
                }
                else if (expr.Type == typeof(PhpAlias))
                {
                    // TODO: specify - ReadCopy | ReadValue | ReadValueCopy - currently not consistent
                }
            }
            else if (access.Isset())
            {
                if (expr.Type == typeof(PhpAlias))
                {
                    expr = Expression.Field(expr, Cache.PhpAlias.Value);
                }

                //
                if (expr.Type == typeof(PhpValue))
                {
                    // Template: Operators.IsSet( value )
                    expr = Expression.Call(Cache.Operators.IsSet_PhpValue, expr);
                }
                else if (!expr.Type.GetTypeInfo().IsValueType)
                {
                    // Template: value != null
                    expr = Expression.ReferenceNotEqual(expr, Expression.Constant(null, typeof(object)));
                }
                else
                {
                    // if there is bound typed symbol, it is always set:
                    expr = Expression.Constant(true, typeof(bool));
                }
            }

            // Read, IsSet
            return(expr);
        }