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)); }
public static Expression BindToCall(Expression instance, MethodBase method, Expression ctx, OverloadBinder.ArgumentsBinder args) { Debug.Assert(method is MethodInfo || method is ConstructorInfo); var ps = method.GetParameters(); var boundargs = new Expression[ps.Length]; int argi = 0; for (int i = 0; i < ps.Length; i++) { var p = ps[i]; if (argi == 0 && p.IsImplicitParameter()) { if (p.IsContextParameter()) boundargs[i] = ctx; else throw new NotImplementedException(); } else { if (i == ps.Length - 1 && p.IsParamsParameter()) { var element_type = p.ParameterType.GetElementType(); boundargs[i] = args.BindParams(argi, element_type); break; } else { boundargs[i] = args.BindArgument(argi, p); } // argi++; } } // Debug.Assert(boundargs.All(x => x != null)); // if (method.IsStatic) { instance = null; } // if (method.IsConstructor) { return Expression.New((ConstructorInfo)method, boundargs); } if (instance != null && method.IsVirtual) { // Ugly hack here, // we NEED to call the method nonvirtually, but LambdaCompiler emits .callvirt always and there is no way how to change it (except we can emit all the stuff by ourselfs). // We use DynamicMethod to emit .call inside, and use its MethodInfo which is static. // LambdaCompiler generates .call to static DynamicMethod which calls our method via .call as well, // after all the inlining, there should be no overhead. method = WrapInstanceMethodToStatic((MethodInfo)method); // var newargs = new Expression[boundargs.Length + 1]; newargs[0] = instance; Array.Copy(boundargs, 0, newargs, 1, boundargs.Length); boundargs = newargs; instance = null; } // return Expression.Call(instance, (MethodInfo)method, boundargs); }