/// <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; }