public static Bind ( |
||
arg | Source expression to be converted. | |
target | Target type. | |
ctx | Expression with current context. | |
Результат |
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)); } }
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(); }
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); }
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))); } }
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))); } }
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(); }
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)); }
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."); }
/// <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) ))); } }
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(); }
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(); }
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)); }
/// <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) ))); } }
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)); }
/// <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)); }
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))); }
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)); }
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)); }
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)); } }
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); }
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); }
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))))); }
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)); }
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); }