Пример #1
0
        internal override void Bind(Binder binder)
        {
            // We have a series of temporary variables, so we will need
            // to discard them after the call is made
            binder.Bookmark();

            // We have the process
            // Eval arg i, assign variable i, eval arg i+1, assign variable i+1
            // so if we, for instance, bind every argument, then bind every temp
            // variable, we are sure to clobber our own arguments
            for (int i = 0; i < Arguments.Length; i++)
            {
                Arguments[i].Bind(binder);
                binder.BindVariable(ArgumentVariables[i]);
            }

            binder.Checkout();

            bool fullyResolvedTypes;

            if (Callee == null)
            {
                fullyResolvedTypes = FunctionName.GetKnownType() != null;
            }
            else
            {
                fullyResolvedTypes = Callee.GetKnownType() != null;
            }

            RedwoodType[] argumentTypes = new RedwoodType[Arguments.Length];
            for (int i = 0; i < Arguments.Length; i++)
            {
                argumentTypes[i] = Arguments[i].GetKnownType();
                // Not really sure whether we care about this, but it might be useful
                // for compiling variable assignments
                ArgumentVariables[i].KnownType = argumentTypes[i];
                fullyResolvedTypes            &= argumentTypes[i] != null;
            }

            // If we have fully resolved types (ie. all argument types are known
            // and so is the callee type) we definitely know the return type
            FullyResolved = fullyResolvedTypes;
            // We have two options for determining an exact type, either:
            // 1) The method is fully resolved. One lambda type will come out of
            //    the many,
            // or
            // 2) the method is not fully resolved, but the provided arguments
            //    narrow the methods down a single option.
            if (Callee == null)
            {
                LambdaType = FunctionName.GetKnownType();
                // If we have a lambda group, we should try to resolve it
                ResolveLambdaGroupOverload(argumentTypes);
            }
            else if (Callee.GetKnownType() == null)
            {
                LambdaType = null;
            }
            else if (Callee.GetKnownType().CSharpType == null)
            {
                LambdaType = Callee
                             .GetKnownType()
                             .GetKnownTypeOfMember(FunctionName.Name);

                // Make sure that calls to class functions also have the correct
                // lambda type
                ResolveLambdaGroupOverload(argumentTypes);
            }
            else if (Callee.GetKnownType().CSharpType == typeof(RedwoodType))
            {
                if (!Callee.Constant)
                {
                    LambdaType = null;
                }
                else if (Callee.EvaluateConstant() is RedwoodType calleeType)
                {
                    if (calleeType.CSharpType == null)
                    {
                        LambdaType = calleeType
                                     .GetKnownTypeForStaticMember(FunctionName.Name);
                        ResolveLambdaGroupOverload(argumentTypes);
                    }
                    else
                    {
                        MethodInfo[] infos;
                        bool         methodExists = MemberResolver.TryResolveMethod(
                            null,
                            calleeType,
                            FunctionName.Name,
                            true,
                            out infos);

                        if (!methodExists)
                        {
                            throw new NotImplementedException();
                        }

                        BindExternal(infos);
                    }
                }
                else
                {
                    // This should never happen?
                    throw new NotImplementedException();
                }
            }
            else
            {
                MethodInfo[] infos;
                bool         methodExists = MemberResolver.TryResolveMethod(
                    null,
                    Callee.GetKnownType(),
                    FunctionName.Name,
                    false,
                    out infos);

                if (!methodExists)
                {
                    // TODO: Can an object have a lambda field?
                    throw new NotImplementedException();
                }

                BindExternal(infos);
            }

            void BindExternal(MethodInfo[] infos)
            {
                ExternalMethodGroup = new MethodGroup(infos);
                ExternalMethodGroup.SelectOverloads(argumentTypes);

                if (ExternalMethodGroup.infos.Length == 0)
                {
                    throw new NotImplementedException();
                }
                else if (ExternalMethodGroup.infos.Length == 1)
                {
                    RedwoodType returnType =
                        RedwoodType.GetForCSharpType(ExternalMethodGroup.infos[0].ReturnType);

                    RedwoodType[] paramTypes = ExternalMethodGroup.infos[0].GetParameters()
                                               .Select(param => RedwoodType.GetForCSharpType(param.ParameterType))
                                               .ToArray();

                    LambdaType = RedwoodType.GetForLambdaArgsTypes(
                        typeof(ExternalLambda),
                        returnType,
                        paramTypes);
                }
            }
        }
Пример #2
0
        internal override IEnumerable <Instruction> Compile()
        {
            // First, create all the temporary variables we need
            // in order to compute
            List <Instruction> instructions = new List <Instruction>();

            RedwoodType[] argumentTypes     = new RedwoodType[Arguments.Length];
            int[]         argumentLocations = new int[Arguments.Length];
            for (int i = 0; i < Arguments.Length; i++)
            {
                argumentTypes[i]     = Arguments[i].GetKnownType();
                argumentLocations[i] = ArgumentVariables[i].Location;
                instructions.AddRange(Arguments[i].Compile());

                // Make sure that argument is coerced to the correct type before
                // we send it off, but make sure not to do it when the callee
                // is vague about the type, or we're still not fully resolved
                // (in which case the TryCallInstruction should ideally handle conversion)
                if (FullyResolved &&
                    LambdaType?.GenericArguments != null &&
                    LambdaType.GenericArguments.Length != 0 &&
                    LambdaType.GenericArguments[i] != null)
                {
                    instructions.AddRange(
                        Compiler.CompileImplicitConversion(
                            argumentTypes[i],
                            LambdaType.GenericArguments[i]
                            )
                        );
                }


                instructions.Add(Compiler.CompileVariableAssign(ArgumentVariables[i]));
            }

            if (Callee == null)
            {
                instructions.AddRange(FunctionName.Compile());
                // TODO: Should this just use LambdaType?
                RedwoodType knownType = FunctionName.GetKnownType();
                CompileCallNoCallee(instructions, argumentTypes, argumentLocations, knownType);
            }
            else
            {
                RedwoodType calleeType = Callee.GetKnownType();
                // Calls that are not fully resolved MUST rely on some amount of
                // dynamic resolution
                if (calleeType == null || !FullyResolved)
                {
                    instructions.AddRange(Callee.Compile());

                    // Try to resolve on the fly if we can't figure it out
                    instructions.Add(new LookupExternalMemberLambdaInstruction(FunctionName.Name, calleeType));
                    instructions.Add(new TryCallInstruction(argumentTypes, argumentLocations));
                }
                else if (calleeType.CSharpType == null)
                {
                    instructions.AddRange(Callee.Compile());

                    instructions.Add(
                        new LookupDirectMemberInstruction(
                            calleeType.GetSlotNumberForOverload(FunctionName.Name, argumentTypes)));
                    instructions.Add(new InternalCallInstruction(argumentLocations));
                }
                else if (calleeType.CSharpType == typeof(RedwoodType))
                {
                    if (!Callee.Constant)
                    {
                        instructions.AddRange(Callee.Compile());
                        instructions.Add(new TryCallInstruction(
                                             FunctionName.Name,
                                             calleeType,
                                             argumentTypes,
                                             argumentLocations)
                                         );
                    }
                    else if (Callee.EvaluateConstant() is RedwoodType type)
                    {
                        if (type.CSharpType == null)
                        {
                            int index = type.staticSlotMap[FunctionName.Name];
                            instructions.Add(new LookupDirectStaticMemberInstruction(type, index));

                            // Since this is a static member on the class and may be an
                            // overload, let's defer to our logic for a simple calls.
                            CompileCallNoCallee(
                                instructions,
                                argumentTypes,
                                argumentLocations,
                                type.staticSlotTypes[index]
                                );
                        }
                        else
                        {
                            instructions.Add(new LoadConstantInstruction(null));
                            instructions.Add(new BuildExternalLambdaInstruction(ExternalMethodGroup));
                            instructions.Add(new ExternalCallInstruction(argumentLocations));
                        }
                    }
                    else
                    {
                        throw new NotImplementedException();
                    }
                }
                else
                {
                    instructions.AddRange(Callee.Compile());

                    instructions.Add(new BuildExternalLambdaInstruction(ExternalMethodGroup));
                    instructions.Add(new ExternalCallInstruction(argumentLocations));
                }
            }

            return(instructions);
        }