예제 #1
0
        public PhpValue GetValue(PhpValue value, Context ctx, Type classContext)
        {
            var receiver = value.AsObject();

            if (receiver != null)
            {
                // CONSIDER: value = Operators.PropertyGetValue( .. )

                var t = receiver.GetPhpTypeInfo();
                if (BinderHelpers.TryResolveDeclaredProperty(t, classContext, false, Name, out var p))
                {
                    value = p.GetValue(ctx, receiver);
                }
                else
                {
                    value = Operators.RuntimePropertyGetValue(ctx, t, receiver, propertyName: Name);
                }

                return(Next.GetValue(value, ctx, classContext));
            }
            else
            {
                PhpException.VariableMisusedAsObject(value, false);
                return(PhpValue.Null);
            }
        }
예제 #2
0
            public override Expression Bind(Expression ctx, Expression target)
            {
                Debug.Assert(ctx != null);

                // Context.GetStatics<_statics>().FIELD
                var getstatics = BinderHelpers.GetStatic_T_Method(_field.DeclaringType);

                return(Expression.Field(Expression.Call(ctx, getstatics), _field));
            }
예제 #3
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();
            }

            // try to find __call() first if we have $this
            var call = (bound.TargetInstance != null) ? BinderHelpers.FindMagicMethod(type, TypeMethods.MagicMethods.__call) : null;

            if (call == null)
            {
                // look for __callStatic()
                call = BinderHelpers.FindMagicMethod(type, TypeMethods.MagicMethods.__callstatic);
            }

            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));
        }
예제 #4
0
        static Func <Context, object> CreateStaticsGetter(Type _statics)
        {
            Debug.Assert(_statics.Name == "_statics");
            Debug.Assert(_statics.IsNested);

            var getter = BinderHelpers.GetStatic_T_Method(_statics);    // ~ Context.GetStatics<_statics>, in closure

            // TODO: getter.CreateDelegate
            return(ctx => getter.Invoke(ctx, ArrayUtils.EmptyObjects));
        }
예제 #5
0
 public ParameterWrapper(ActionBinder binder, ParameterInfo info)
     : this(binder, info, info.ParameterType, SymbolId.Empty, false, false, false)
 {
     _prohibitNull = CompilerHelpers.ProhibitsNull(info);
     _isParams     = CompilerHelpers.IsParamArray(info);
     _isParamsDict = BinderHelpers.IsParamDictionary(info);
     if (_isParams || _isParamsDict)
     {
         // params arrays & dictionaries don't allow assignment by keyword
         _name = SymbolTable.StringToId("<unknown>");
     }
     else
     {
         _name = SymbolTable.StringToId(info.Name ?? "<unknown>");
     }
 }
예제 #6
0
            /// <summary>
            /// Creates Func&lt;object, T&gt; depending on the access.
            /// </summary>
            Delegate CompileAccess(AccessMask access)
            {
                var pinstance = Expression.Parameter(typeof(object));

                var expr = Bind(null, Expression.Convert(pinstance, _field.DeclaringType));

                if (access == AccessMask.Read)
                {
                    expr = ConvertExpression.BindToValue(expr);
                }
                else
                {
                    expr = BinderHelpers.BindAccess(expr, null, access, null);
                }

                return(Expression.Lambda(expr, true, pinstance).Compile());
            }
예제 #7
0
            public override Expression Bind(Expression ctx, Expression target)
            {
                Debug.Assert(ctx != null);

                if (Field.IsLiteral)
                {
                    return(Expression.Constant(Field.GetValue(null)));
                }
                else if (Field.IsStatic)
                {
                    return(Expression.Field(null, Field));
                }
                else
                {
                    // Context.GetStatics<_statics>().FIELD
                    var getstatics = BinderHelpers.GetStatic_T_Method(Field.DeclaringType);
                    return(Expression.Field(Expression.Call(ctx, getstatics), Field));
                }
            }
예제 #8
0
            Delegate CompileAccess(AccessMask access)
            {
                var pctx      = Expression.Parameter(typeof(Context));
                var pinstance = Expression.Parameter(typeof(object));

                var expr = Bind(pctx, Expression.Convert(pinstance, Field.DeclaringType));

                if (access == AccessMask.Read)
                {
                    expr = ConvertExpression.BindToValue(expr);
                }
                else
                {
                    expr = BinderHelpers.BindAccess(expr, null, access, null);
                }

                //
                return(Expression.Lambda(expr, tailCall: true, parameters: new[] { pctx, pinstance }).Compile());
            }
예제 #9
0
            public ClrFieldProperty(PhpTypeInfo tinfo, FieldInfo field)
                : base(tinfo)
            {
                _field = field ?? throw new ArgumentNullException(nameof(field));

                //
                _lazyGetter       = new Lazy <Func <object, PhpValue> >(() => (Func <object, PhpValue>)CompileAccess(AccessMask.Read));
                _lazyEnsureAlias  = new Lazy <Func <object, PhpAlias> >(() => (Func <object, PhpAlias>)CompileAccess(AccessMask.ReadRef));
                _lazyEnsureObject = new Lazy <Func <object, object> >(() => (Func <object, object>)CompileAccess(AccessMask.EnsureObject));
                _lazyEnsureArray  = new Lazy <Func <object, IPhpArray> >(() => (Func <object, IPhpArray>)CompileAccess(AccessMask.EnsureArray));

                // SetValue(instance, PhpValue): void
                _lazySetValue = new Lazy <Action <Context, object, PhpValue> >(() =>
                {
                    if (IsReadOnly)
                    {
                        // error
                        return(new Action <Context, object, PhpValue>((_, _instance, _value) =>
                        {
                            PhpException.ErrorException(Resources.ErrResources.readonly_property_written, ContainingType.Name, PropertyName);
                        }));
                    }

                    var pctx      = Expression.Parameter(typeof(Context));
                    var pinstance = Expression.Parameter(typeof(object));
                    var pvalue    = Expression.Parameter(typeof(PhpValue));

                    // expr: <instance>.<field>
                    var expr = Bind(pctx, Expression.Convert(pinstance, _field.DeclaringType));

                    // expr: <field> := <value>
                    expr = BinderHelpers.BindAccess(expr, pctx, AccessMask.Write, pvalue);

                    //
                    var lambda = Expression.Lambda(Expression.Block(expr, Expression.Empty()), pctx, pinstance, pvalue);

                    return((Action <Context, object, PhpValue>)lambda.Compile());
                });
            }
예제 #10
0
        /// <summary>
        /// Resolves lazy constant field in form of:<br/>
        /// public static readonly Func&lt;Context, TResult&gt; FIELD;
        /// </summary>
        internal static bool TryBindLazyConstantField(FieldInfo fld, out Func <Context, PhpValue> getter)
        {
            if (fld.IsInitOnly && fld.IsStatic)
            {
                var rtype = fld.FieldType;
                if (rtype.IsGenericType && rtype.GetGenericTypeDefinition() == typeof(Func <,>))
                {
                    // Func<Context, TResult>
                    var g = rtype.GenericTypeArguments;
                    if (g.Length == 2 && g[0] == typeof(Context))
                    {
                        var getter1 = (MulticastDelegate)fld.GetValue(null); // initonly

                        getter = BinderHelpers.BindFuncInvoke <PhpValue>(getter1);
                        return(true);
                    }
                }
            }

            getter = null;
            return(false);
        }
예제 #11
0
        internal MSAst Call(string methodName, MSAst instance = null, Type type = null, params MSAst[] arguments)
        {
            if (((instance == null) && (type == null)) || ((instance != null) && (type != null)))
            {
                throw new ArgumentException("Either 'instance' or 'type' must be specified, but not both.");
            }

            List <MethodBase> candidates;

            var isCandidate =
                (Func <MethodBase, bool>)
                    (m => (m.Name == methodName) &&
                    ((m.GetParameters().Length == arguments.Length) ||
                     (BinderHelpers.IsParamsMethod(m) && ((m.GetParameters().Length + 1) <= arguments.Length))));

            if (type != null)
            {
                candidates = type
                             .GetMethods(BindingFlags.Public | BindingFlags.Static)
                             .Where(isCandidate)
                             .Cast <MethodBase>()
                             .ToList();
            }
            else
            {
                candidates = instance.Type
                             .GetMethods(BindingFlags.Public | BindingFlags.Static)
                             .Where(isCandidate)
                             .Cast <MethodBase>()
                             .ToList();
            }

            return(_binder.Binder.CallMethod(
                       _sxeContext.OverloadResolver.CreateOverloadResolver(
                           arguments.Select(o => DynamicUtils.ObjectToMetaObject(null, o)).ToList(),
                           new CallSignature(arguments.Length),
                           CallTypes.None),
                       candidates).Expression);
        }
예제 #12
0
        public PhpAlias GetAlias(ref PhpValue value, Context ctx, Type classContext)
        {
            var receiver = PhpValue.EnsureObject(ref value);
            var t        = receiver.GetPhpTypeInfo();

            PhpValue tmp;

            if (BinderHelpers.TryResolveDeclaredProperty(t, classContext, false, Name, out var prop))
            {
                switch (Next.Operation)
                {
                case RuntimeChainOperation.Property:
                    tmp = PhpValue.FromClass(prop.EnsureObject(ctx, receiver));
                    break;

                case RuntimeChainOperation.ArrayItem:
                    tmp = PhpValue.Create(prop.EnsureArray(ctx, receiver));
                    break;

                case RuntimeChainOperation.End:
                    return(prop.EnsureAlias(ctx, receiver));

                default:
                    throw new InvalidOperationException();
                }
            }
            else
            {
                // Template: runtimeflds.Contains(key) ? runtimeflds.EnsureObject(key) : ( __get(key) ?? runtimeflds.EnsureObject(key))

                var runtimeFields = t.GetRuntimeFields(receiver);
                if (runtimeFields == null || !runtimeFields.Contains(Name))
                {
                    //
                    var __get = t.RuntimeMethods[TypeMethods.MagicMethods.__get];
                    if (__get != null)
                    {
                        // NOTE: magic methods must have public visibility, therefore the visibility check is unnecessary

                        // int subkey1 = access.Write() ? 1 : access.Unset() ? 2 : access.Isset() ? 3 : 4;
                        int subkey = Name.GetHashCode() ^ (1 << 4 /*subkey1*/);

                        using (var token = new Context.RecursionCheckToken(ctx, receiver, subkey))
                        {
                            if (!token.IsInRecursion)
                            {
                                tmp = __get.Invoke(ctx, receiver, Name);
                                return(Next.GetAlias(ref tmp, ctx, classContext));
                            }
                        }
                    }
                }

                if (runtimeFields == null)
                {
                    runtimeFields = t.EnsureRuntimeFields(receiver);
                }

                //

                switch (Next.Operation)
                {
                case RuntimeChainOperation.Property:
                    tmp = PhpValue.FromClass(runtimeFields.EnsureItemObject(Name));
                    break;

                case RuntimeChainOperation.ArrayItem:
                    tmp = PhpValue.Create(runtimeFields.EnsureItemArray(Name));
                    break;

                case RuntimeChainOperation.End:
                    return(runtimeFields.EnsureItemAlias(Name));

                default:
                    throw new InvalidOperationException();
                }
            }

            // chain:
            return(Next.GetAlias(ref tmp, ctx, classContext));
        }
예제 #13
0
        internal MethodTarget MakeParamsExtended(int argCount, SymbolId[] names, int[] nameIndexes)
        {
            Debug.Assert(BinderHelpers.IsParamsMethod(Method));

            List <ArgBuilder> newArgBuilders = new List <ArgBuilder>(_argBuilders.Count);

            // current argument that we consume, initially skip this if we have it.
            int        curArg            = CompilerHelpers.IsStatic(_method) ? 0 : 1;
            int        kwIndex           = -1;
            ArgBuilder paramsDictBuilder = null;

            foreach (ArgBuilder ab in _argBuilders)
            {
                SimpleArgBuilder sab = ab as SimpleArgBuilder;
                if (sab != null)
                {
                    // we consume one or more incoming argument(s)
                    if (sab.IsParamsArray)
                    {
                        // consume all the extra arguments
                        int paramsUsed = argCount -
                                         GetConsumedArguments() -
                                         names.Length +
                                         (CompilerHelpers.IsStatic(_method) ? 1 : 0);

                        newArgBuilders.Add(new ParamsArgBuilder(
                                               sab.ParameterInfo,
                                               sab.Type.GetElementType(),
                                               curArg,
                                               paramsUsed
                                               ));

                        curArg += paramsUsed;
                    }
                    else if (sab.IsParamsDict)
                    {
                        // consume all the kw arguments
                        kwIndex           = newArgBuilders.Count;
                        paramsDictBuilder = sab;
                    }
                    else
                    {
                        // consume the argument, adjust its position:
                        newArgBuilders.Add(sab.MakeCopy(curArg++));
                    }
                }
                else
                {
                    // CodeContext, null, default, etc...  we don't consume an
                    // actual incoming argument.
                    newArgBuilders.Add(ab);
                }
            }

            if (kwIndex != -1)
            {
                newArgBuilders.Insert(kwIndex, new ParamsDictArgBuilder(paramsDictBuilder.ParameterInfo, curArg, names, nameIndexes));
            }

            return(new MethodTarget(_binder, Method, argCount, _instanceBuilder, newArgBuilders, _returnBuilder));
        }
예제 #14
0
        /// <summary>
        /// Gets <see cref="Expression"/> representing field value.
        /// </summary>
        /// <param name="name">Class constant name.</param>
        /// <param name="classCtx">Current class context. Can be <c>null</c>.</param>
        /// <param name="target">Expression representing self instance.</param>
        /// <param name="ctx">Expression representing current <see cref="Context"/>.</param>
        /// <param name="kind">Field kind.</param>
        /// <returns><see cref="Expression"/> instance or <c>null</c> if constant does not exist.</returns>
        internal Expression Bind(string name, Type classCtx, Expression target, Expression ctx, FieldKind kind)
        {
            FieldInfo fld;

            //
            if (_fields != null && _fields.TryGetValue(name, out fld) && TypeMembersUtils.IsVisible(fld, classCtx))
            {
                if (fld.IsPublic && fld.IsLiteral)
                {
                    if (kind == FieldKind.Constant)
                    {
                        return(Expression.Constant(fld.GetValue(null)));
                    }
                }

                if (fld.IsStatic)
                {
                    if (kind == FieldKind.InstanceField)
                    {
                        // TODO: Err: static field accessed with instance
                    }
                    return(Expression.Field(null, fld));
                }

                if (kind == FieldKind.InstanceField)
                {
                    Debug.Assert(target != null);
                    return(Expression.Field(target, fld));
                }
            }

            //
            if (kind != FieldKind.InstanceField && _staticsFields != null && _staticsFields.TryGetValue(name, out fld))
            {
                if ((kind == FieldKind.Constant && fld.IsInitOnly) ||
                    (kind == FieldKind.StaticField))
                {
                    Debug.Assert(target == null);
                    Debug.Assert(ctx != null);

                    // Context.GetStatics<_statics>().FIELD
                    var getstatics = BinderHelpers.GetStatic_T_Method(fld.DeclaringType);
                    return(Expression.Field(Expression.Call(ctx, getstatics), fld));
                }
            }

            //
            PropertyInfo p;

            if (kind != FieldKind.Constant && _properties != null && _properties.TryGetValue(name, out p))
            {
                var isstatic = p.GetMethod.IsStatic;
                if ((kind == FieldKind.StaticField) == isstatic)
                {
                    Debug.Assert((target == null) == isstatic);
                    return(Expression.Property(target, p));
                }
            }

            //
            return(null);
        }
예제 #15
0
 /// <summary>
 /// Type and whether the parameter is a params-array or params-dictionary is derived from info.
 /// </summary>
 public SimpleArgBuilder(ParameterInfo info, int index)
     : this(info, info.ParameterType, index, CompilerHelpers.IsParamArray(info), BinderHelpers.IsParamDictionary(info))
 {
 }
예제 #16
0
 /// <summary>
 /// Determines the parameter is considered as implicitly passed by runtime.
 /// </summary>
 public static bool IsImplicitParameter(ParameterInfo p) => BinderHelpers.IsImplicitParameter(p);
예제 #17
0
        /// <summary>
        /// Builds a new MethodCandidate which takes count arguments and the provided list of keyword arguments.
        ///
        /// The basic idea here is to figure out which parameters map to params or a dictionary params and
        /// fill in those spots w/ extra ParameterWrapper's.
        /// </summary>
        internal MethodCandidate MakeParamsExtended(ActionBinder binder, int count, SymbolId[] names)
        {
            Debug.Assert(BinderHelpers.IsParamsMethod(_target.Method));

            List <ParameterWrapper> newParameters = new List <ParameterWrapper>(count);
            // if we don't have a param array we'll have a param dict which is type object
            Type elementType = null;
            int  index = -1, kwIndex = -1;

            // keep track of which kw args map to a real argument, and which ones
            // map to the params dictionary.
            List <SymbolId> unusedNames       = new List <SymbolId>(names);
            List <int>      unusedNameIndexes = new List <int>();

            for (int i = 0; i < unusedNames.Count; i++)
            {
                unusedNameIndexes.Add(i);
            }

            for (int i = 0; i < _parameters.Count; i++)
            {
                ParameterWrapper pw = _parameters[i];

                if (_parameters[i].IsParamsDict)
                {
                    kwIndex = i;
                }
                else if (_parameters[i].IsParamsArray)
                {
                    elementType = pw.Type.GetElementType();
                    index       = i;
                }
                else
                {
                    for (int j = 0; j < unusedNames.Count; j++)
                    {
                        if (unusedNames[j] == _parameters[i].Name)
                        {
                            unusedNames.RemoveAt(j);
                            unusedNameIndexes.RemoveAt(j);
                            break;
                        }
                    }
                    newParameters.Add(pw);
                }
            }

            if (index != -1)
            {
                while (newParameters.Count < (count - unusedNames.Count))
                {
                    ParameterWrapper param = new ParameterWrapper(binder, elementType, SymbolId.Empty, false);
                    newParameters.Insert(System.Math.Min(index, newParameters.Count), param);
                }
            }

            if (kwIndex != -1)
            {
                foreach (SymbolId si in unusedNames)
                {
                    newParameters.Add(new ParameterWrapper(binder, typeof(object), si, false));
                }
            }
            else if (unusedNames.Count != 0)
            {
                // unbound kw args and no where to put them, can't call...
                // TODO: We could do better here because this results in an incorrect arg # error message.
                return(null);
            }

            // if we have too many or too few args we also can't call
            if (count != newParameters.Count)
            {
                return(null);
            }

            return(new MethodCandidate(_target.MakeParamsExtended(count, unusedNames.ToArray(), unusedNameIndexes.ToArray()), newParameters));
        }