/// <summary>Declare an arg variable at the start of the args set.</summary>
        internal ArgVariable DeclareHoisted(string name, Type type)
        {
            // Create it:
            ArgVariable arg = new ArgVariable(name);

            // Set the type:
            arg.Type = type;

            // Add a blank arg to the end of the set:
            Arguments.Add(null);

            // Time to shift all the args over except for 'this' (arg 0)!
            for (int i = Arguments.Count - 1; i > 0; i--)
            {
                // Get it:
                ArgVariable currentArg = Arguments[i];

                // Update the index:
                currentArg.ArgumentID = i + 1;

                // Set it back:
                Arguments[i + 1] = currentArg;
            }

            // Declare the arg in the scope:
            (InitialScope as DeclarativeScope).Declare(arg);

            return(arg);
        }
        /// <summary>Gets the compiled function for the given args set. Note that this args set
        /// is the actual types and always includes the 'this' keywords type.</summary>
        public Library.UserDefinedFunction GetCompiled(Type[] args, ScriptEngine engine, bool isConstructor)
        {
            int genCount = GeneratedMethods.Count;

            for (int i = 0; i < genCount; i++)
            {
                if (GeneratedMethods[i].ArgsEqual(args))
                {
                    // Got a match! Already compiled it.
                    return(GeneratedMethods[i]);
                }
            }

            // Need to compile it now with our given arg types.
            int argCount = args == null?0:args.Length;

            // The source contains a fixed number of args; it goes up to this:
            int maxArg = Arguments.Count;

            // First, map args to a block of ArgVariable objects:
            ArgVariable[] argsSet = new ArgVariable[argCount];

            for (int i = 0; i < argCount; i++)
            {
                // Setup the arg variable:
                ArgVariable curArg;

                if (i < maxArg)
                {
                    // Use the existing args object so it correctly updates in the scope:
                    curArg = Arguments[i];
                }
                else
                {
                    // Need to create a fake one:
                    curArg = new ArgVariable("@unused-arg-" + i);
                }

                // Apply type (a little different here as we have to account for null too):
                curArg.RawType = args[i];

                // Apply to set:
                argsSet[i] = curArg;
            }

            // If we're passing less args than the source supports, update the types of those unused args to being 'Undefined':
            for (int i = argCount; i < maxArg; i++)
            {
                Arguments[i].Type = typeof(Nitrassic.Undefined);
            }

            // Create info:
            OptimizationInfo info = new OptimizationInfo(engine);

            info.IsConstructor = isConstructor;

            // Generate it!
            return(GenerateCode(argsSet, info));
        }
        /// <summary>Gets the compiled function for the given args set. Note that this args set
        /// is the actual values and always includes the 'this' keywords value.</summary>
        public Library.UserDefinedFunction GetCompiled(ScriptEngine engine, object[] args)
        {
            int genCount = GeneratedMethods.Count;

            for (int i = 0; i < genCount; i++)
            {
                if (GeneratedMethods[i].ArgsEqual(args))
                {
                    // Got a match! Already compiled it.
                    return(GeneratedMethods[i]);
                }
            }

            // Need to compile it now with our given arg types.
            int argCount = args.Length;

            if (argCount > Arguments.Count)
            {
                // The actual source can only handle so many args.
                argCount = Arguments.Count;
            }

            // First, map args to Arguments:
            ArgVariable[] argsSet = new ArgVariable[argCount];

            for (int i = 0; i < argCount; i++)
            {
                // Setup the arg variable:
                ArgVariable curArg = new ArgVariable(Arguments[i].Name);

                // Apply type:
                curArg.Type = args[i].GetType();

                // Apply to set:
                argsSet[i] = curArg;
            }

            // Generate it!
            return(GenerateCode(argsSet, new OptimizationInfo(engine)));
        }
        /// <summary>
        /// Generates IL for the script.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        protected override void GenerateCode(ArgVariable[] arguments, ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Method signature: object FunctionDelegate(object thisObj, object[] arguments)

            // Transfer the function name into the scope.
            if (!string.IsNullOrEmpty(Name) &&
                IncludeNameInScope &&
                !HasArgument(Name) &&
                optimizationInfo.MethodOptimizationHints.HasVariable(Name))
            {
                var functionName = new NameExpression(InitialScope, Name);

                functionName.ApplyType(optimizationInfo, typeof(object));

                functionName.GenerateSet(generator, optimizationInfo, false, typeof(object), delegate(bool two)
                {
                    generator.LoadInt64(MethodID);
                    generator.Call(ReflectionHelpers.MethodLookup_Load);

                    if (two)
                    {
                        // Duplicate it:
                        generator.Duplicate();
                    }
                }, false);
            }

            // Transfer the arguments object into the scope.
            if (MethodOptimizationHints.HasArguments && !HasArgument("arguments"))
            {
                var argsSet = new NameExpression(this.InitialScope, "arguments");

                argsSet.ApplyType(optimizationInfo, typeof(object));

                argsSet.GenerateSet(generator, optimizationInfo, false, typeof(object), delegate(bool two)
                {
                    // argumentValues

                    // Build an object[] from the arg values.

                    // Define an array:
                    int argCount = (arguments == null)?0 : arguments.Length;

                    generator.LoadInt32(argCount);
                    generator.NewArray(typeof(object));

                    for (int a = 0; a < argCount; a++)
                    {
                        // One of many args:
                        ArgVariable currentArg = arguments[a];

                        generator.Duplicate();
                        generator.LoadInt32(a);
                        currentArg.Get(generator);
                        EmitConversion.ToAny(generator, currentArg.Type);
                        generator.StoreArrayElement(typeof(object));
                    }


                    generator.NewObject(ReflectionHelpers.Arguments_Constructor);

                    if (two)
                    {
                        generator.Duplicate();
                    }
                }, false);
            }

            // Temp cache return var/target:
            var retVar  = optimizationInfo.ReturnVariable;
            var retTarg = optimizationInfo.ReturnTarget;

            optimizationInfo.ReturnVariable = null;
            optimizationInfo.ReturnTarget   = null;

            // Initialize any declarations.
            (this.InitialScope as DeclarativeScope).GenerateDeclarations(generator, optimizationInfo);

            // Generate code for the body of the function.
            this.AbstractSyntaxTree.GenerateCode(generator, optimizationInfo);

            // Define the return target - this is where the return statement jumps to.
            // ReturnTarget can be null if there were no return statements.
            if (optimizationInfo.ReturnTarget != null)
            {
                generator.DefineLabelPosition(optimizationInfo.ReturnTarget);
            }

            // Load the return value.  If the variable is null, there were no return statements.
            if (optimizationInfo.ReturnVariable != null)
            {
                // Return the value stored in the variable.  Will be null if execution hits the end
                // of the function without encountering any return statements.
                generator.LoadVariable(optimizationInfo.ReturnVariable);
            }

            // Restore:
            optimizationInfo.ReturnVariable = retVar;
            optimizationInfo.ReturnTarget   = retTarg;
        }