Exemplo n.º 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;
        }
Exemplo n.º 2
0
        /// <summary>
        /// Starts a code block definition by pushing a new block into the vm for further definition
        /// </summary>
        /// <param name="vm"></param>
        /// <param name="i"></param>
        internal static void StartBlock(VirtualMachine vm, Instruction i)
        {
            if (vm.CodeBlockBeingDefined != null) {
                throw new Exception("VM Error: Code block already being defined.");
            }
            CodeBlock call = new CodeBlock();
            foreach (Value v in i.Args) {
                call.ArgumentsArity.Add(v);
            }
            int inc = vm.ByteCode.ProgramCounter + 1;
            call.StartProgramCounter = inc;

            if (i.Type == OpCodeTypes.START_CLOSURE) {
                Environment env = vm.PushClosure();
                call.Closure = env;
            }

            vm.CodeBlockBeingDefined = call;
        }