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 if (p.IsImportLocalsParameter()) { // no way we can implement this throw new NotImplementedException(); // TODO: empty array & report warning } else if (p.IsImportCallerArgsParameter()) { // we don't have this info throw new NotImplementedException(); // TODO: empty array & report warning } else if (p.IsImportCallerClassParameter()) { // TODO: pass classctx from the callsite throw new NotImplementedException(); } 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) { instance = Expression.Convert(instance, method.DeclaringType); } if (instance != null && method.IsVirtual) // NOTE: only needed for parent::foo(), static::foo() and self::foo() ? { // 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)); }
public static Expression BindToCall(Expression instance, MethodBase method, Expression ctx, OverloadBinder.ArgumentsBinder args, bool isStaticCallSyntax, PhpTypeInfo lateStaticType) { 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 if (p.IsQueryValueParameter()) { if (p.ParameterType == typeof(QueryValue <CallerScript>)) { // we don't have this info throw new NotSupportedException(); } else if (p.ParameterType == typeof(QueryValue <CallerArgs>)) { // we don't have this info throw new NotImplementedException(); // TODO: empty array & report warning } else if (p.ParameterType == typeof(QueryValue <LocalVariables>)) { // pass NULL value if we don't have the locals boundargs[i] = Expression.Default(p.ParameterType); // TODO: report it might get wrong // TODO: if we create IPhpCallback in compile-time, we know the routine needs the locals, ... } } else if (p.IsLateStaticParameter()) { if (lateStaticType != null) { // Template: PhpTypeInfoExtension.GetPhpTypeInfo<lateStaticType>() boundargs[i] = Expression.Call( null, typeof(PhpTypeInfoExtension).GetMethod("GetPhpTypeInfo", Cache.Types.Empty).MakeGenericMethod(lateStaticType.Type.AsType())); } else { throw new InvalidOperationException("static context not available."); } } else if (p.IsImportCallerClassParameter()) { // TODO: pass classctx from the callsite throw new NotImplementedException(); } else if (p.IsImportCallerStaticClassParameter()) { throw new NotSupportedException(); // we don't know current late static bound type } 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 (HasToBeCalledNonVirtually(instance, method, isStaticCallSyntax)) { // 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. instance = Expression.Convert(instance, method.DeclaringType); 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; } if (instance != null && !method.DeclaringType.IsAssignableFrom(instance.Type)) { instance = Expression.Convert(instance, method.DeclaringType); } // NOTE: instead of "HasToBeCalledNonVirtually" magic above, it would be great to just use ".call" opcode always (as of now Linq cannot do that) // return(Expression.Call(instance, (MethodInfo)method, boundargs)); }