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); } }
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)); }
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)); }
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)); }
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>"); } }
/// <summary> /// Creates Func<object, T> 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()); }
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)); } }
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()); }
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()); }); }
/// <summary> /// Resolves lazy constant field in form of:<br/> /// public static readonly Func<Context, TResult> 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); }
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); }
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)); }
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)); }
/// <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); }
/// <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)) { }
/// <summary> /// Determines the parameter is considered as implicitly passed by runtime. /// </summary> public static bool IsImplicitParameter(ParameterInfo p) => BinderHelpers.IsImplicitParameter(p);
/// <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)); }