예제 #1
0
        /// <summary>
        /// 
        /// 
        /// The difference
        /// between RETURN_BLOCK and RETURN_CLOSURE *is* meaningful, as the former will attempt to return context
        /// to parent scope and the latter will attempt to return context to saved scope. Mixing up the two will
        /// cause memory corruption and probable execution failure.
        /// 
        /// Note that we push arguments for a call from 0...n (end), and recovered the same way, as we shift them and 
        /// move them to the new stack, where they will be removed in LIFO order.
        /// </summary>
        /// <param name="vm"></param>
        /// <param name="i"></param>
        internal static void CallFunction(VirtualMachine vm, Instruction i)
        {
            Value callable = vm.Stack.Shift();
            if (callable.Type != ValueTypes.CODE_BLOCK) {
                vm.RaiseError("Tried to call a non-callable value as a function");
                return;
            }

            if (i.Type == OpCodeTypes.CALL_CLOSURE && callable.CodeBlock_Value.Closure == null) {
                vm.RaiseError("Tried to call a non-closure block with lexical scope.");
                return;
            }

            if ( (i.Type == OpCodeTypes.CALL_BLOCK || i.Type == OpCodeTypes.CALL_FUNCTION) && callable.CodeBlock_Value.Closure != null) {
                vm.RaiseError("Tried to call a closure with dynamic scope.");
                return;
            }

            // Create a new environment and shift arguments onto the stack
            Environment env = null;
            if (i.Type == OpCodeTypes.CALL_BLOCK) {             // dynamically-scoped
                env = vm.PushCurrentEnvironment();
            } else if (i.Type == OpCodeTypes.CALL_FUNCTION) {   // non-closure lexical scope (function)
                env = vm.PushClosure();
            } else if (i.Type == OpCodeTypes.CALL_CLOSURE) {    // lexically-scoped (closure)
                env = callable.CodeBlock_Value.Closure;
            } else {
                vm.RaiseError("Attempt to call block or closure with unknown opcode " + i.Type);
                return;
            }

            for (int aInc = callable.CodeBlock_Value.ArgumentsArity.Count() - 1; aInc >= 0; aInc--) {
                Value v = callable.CodeBlock_Value.ArgumentsArity[aInc];
                Value arg = vm.Stack.Shift();
                if (v.Type != ValueTypes.ANY_TYPE && arg.Type != v.Type) {
                    vm.RaiseError("Argument arity error: expected type " + arg.Type + ", saw " + v.Type);
                    return;
                }
                env.Stack.Push(arg);
            }

            // If we're operating in lexical scope, save the current environment so we can return
            // back to it.
            if (i.Type == OpCodeTypes.CALL_CLOSURE || i.Type == OpCodeTypes.CALL_FUNCTION) {
                vm.ClosureStack.Add(vm.CurrentEnvironment);
            }

            // Finally, add the code block to the VM's function call list. We only use this to
            // enable tail calls so we don't lose tex start locations and function arity
            vm.FunctionCallList.Add(callable.CodeBlock_Value);

            vm.CurrentEnvironment = env;
            vm.ByteCode.ProgramCounter = callable.CodeBlock_Value.StartProgramCounter;
        }