Ejemplo n.º 1
0
        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));
        }
Ejemplo n.º 2
0
        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));
        }