Ejemplo n.º 1
0
        /// <summary>
        /// Calls the specified function, supporting most cases. The called function must not yield.
        /// </summary>
        /// <param name="func">The function; it must be a Function or ClrFunction or have a call metamethod defined.</param>
        /// <param name="args">The arguments.</param>
        /// <returns></returns>
        /// <exception cref="ScriptRuntimeException">If the function yields, returns a tail call request with continuations/handlers or, of course, if it encounters errors.</exception>
        public DynValue Call(DynValue func, params DynValue[] args)
        {
            if (func.Type == DataType.Function)
            {
                return(this.GetScript().Call(func, args));
            }
            else if (func.Type == DataType.ClrFunction)
            {
                while (true)
                {
                    DynValue ret = func.Callback.Invoke(this, args, false);

                    if (ret.Type == DataType.YieldRequest)
                    {
                        throw ScriptRuntimeException.CannotYield();
                    }
                    else if (ret.Type == DataType.TailCallRequest)
                    {
                        var tail = ret.TailCallData;

                        if (tail.Continuation != null || tail.ErrorHandler != null)
                        {
                            throw new ScriptRuntimeException("the function passed cannot be called directly. wrap in a script function instead.");
                        }
                        else
                        {
                            args = tail.Args;
                            func = tail.Function;
                        }
                    }
                    else
                    {
                        return(ret);
                    }
                }
            }
            else
            {
                int maxloops = 10;

                while (maxloops > 0)
                {
                    DynValue v = this.GetMetamethod(func, "__call");

                    if (v == null && v.IsNil())
                    {
                        throw ScriptRuntimeException.AttemptToCallNonFunc(func.Type);
                    }

                    func = v;

                    if (func.Type == DataType.Function || func.Type == DataType.ClrFunction)
                    {
                        return(Call(func, args));
                    }
                }

                throw ScriptRuntimeException.LoopInCall();
            }
        }
        private DynValue Processing_Loop(int instructionPtr)
        {
            // This is the main loop of the processor, has a weird control flow and needs to be as fast as possible.
            // This sentence is just a convoluted way to say "don't complain about gotos".

repeat_execution:

            try
            {
                while (true)
                {
                    Instruction i = m_RootChunk.Code[instructionPtr];

                    if (m_Debug.DebuggerAttached != null)
                    {
                        ListenDebugger(i, instructionPtr);
                    }

                    ++instructionPtr;

                    switch (i.OpCode)
                    {
                    case OpCode.Nop:
                    case OpCode.Debug:
                        break;

                    case OpCode.Pop:
                        m_ValueStack.RemoveLast(i.NumVal);
                        break;

                    case OpCode.Copy:
                        m_ValueStack.Push(m_ValueStack.Peek(i.NumVal));
                        break;

                    case OpCode.Swap:
                        ExecSwap(i);
                        break;

                    case OpCode.Literal:
                        m_ValueStack.Push(i.Value);
                        break;

                    case OpCode.Add:
                        instructionPtr = ExecAdd(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Concat:
                        instructionPtr = ExecConcat(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Neg:
                        instructionPtr = ExecNeg(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Sub:
                        instructionPtr = ExecSub(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Mul:
                        instructionPtr = ExecMul(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Div:
                        instructionPtr = ExecDiv(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Mod:
                        instructionPtr = ExecMod(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Power:
                        instructionPtr = ExecPower(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Eq:
                        instructionPtr = ExecEq(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.LessEq:
                        instructionPtr = ExecLessEq(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Less:
                        instructionPtr = ExecLess(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Len:
                        instructionPtr = ExecLen(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Call:
                    case OpCode.ThisCall:
                        instructionPtr = Internal_ExecCall(i.NumVal, instructionPtr, null, null, i.OpCode == OpCode.ThisCall, i.Name);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Scalar:
                        m_ValueStack.Push(m_ValueStack.Pop().ToScalar());
                        break;

                    case OpCode.Not:
                        ExecNot(i);
                        break;

                    case OpCode.CNot:
                        ExecCNot(i);
                        break;

                    case OpCode.JfOrPop:
                    case OpCode.JtOrPop:
                        instructionPtr = ExecShortCircuitingOperator(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.JNil:
                    {
                        DynValue v = m_ValueStack.Pop().ToScalar();

                        if (v.Type == DataType.Nil || v.Type == DataType.Void)
                        {
                            instructionPtr = i.NumVal;
                        }
                    }
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Jf:
                        instructionPtr = JumpBool(i, false, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Jump:
                        instructionPtr = i.NumVal;
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.MkTuple:
                        ExecMkTuple(i);
                        break;

                    case OpCode.Enter:
                        NilifyBlockData(i);
                        break;

                    case OpCode.Leave:
                    case OpCode.Exit:
                        ClearBlockData(i);
                        break;

                    case OpCode.Closure:
                        ExecClosure(i);
                        break;

                    case OpCode.BeginFn:
                        ExecBeginFn(i);
                        break;

                    case OpCode.ToBool:
                        m_ValueStack.Push(DynValue.NewBoolean(m_ValueStack.Pop().ToScalar().CastToBool()));
                        break;

                    case OpCode.Args:
                        ExecArgs(i);
                        break;

                    case OpCode.Ret:
                        instructionPtr = ExecRet(i);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        if (instructionPtr < 0)
                        {
                            goto return_to_native_code;
                        }
                        break;

                    case OpCode.Incr:
                        ExecIncr(i);
                        break;

                    case OpCode.ToNum:
                        ExecToNum(i);
                        break;

                    case OpCode.JFor:
                        instructionPtr = ExecJFor(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.NewTable:
                        m_ValueStack.Push(DynValue.NewTable(this.m_Script));
                        break;

                    case OpCode.IterPrep:
                        ExecIterPrep(i);
                        break;

                    case OpCode.IterUpd:
                        ExecIterUpd(i);
                        break;

                    case OpCode.ExpTuple:
                        ExecExpTuple(i);
                        break;

                    case OpCode.Local:
                        m_ValueStack.Push(m_ExecutionStack.Peek().LocalScope[i.Symbol.i_Index].AsReadOnly());
                        break;

                    case OpCode.Upvalue:
                        m_ValueStack.Push(m_ExecutionStack.Peek().ClosureScope[i.Symbol.i_Index].AsReadOnly());
                        break;

                    case OpCode.StoreUpv:
                        ExecStoreUpv(i);
                        break;

                    case OpCode.StoreLcl:
                        ExecStoreLcl(i);
                        break;

                    case OpCode.TblInitN:
                        ExecTblInitN(i);
                        break;

                    case OpCode.TblInitI:
                        ExecTblInitI(i);
                        break;

                    case OpCode.Index:
                        instructionPtr = ExecIndex(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.IndexSet:
                        instructionPtr = ExecIndexSet(i, instructionPtr);
                        if (instructionPtr == YIELD_SPECIAL_TRAP)
                        {
                            goto yield_to_calling_coroutine;
                        }
                        break;

                    case OpCode.Invalid:
                        throw new NotImplementedException(string.Format("Invalid opcode : {0}", i.Name));

                    default:
                        throw new NotImplementedException(string.Format("Execution for {0} not implented yet!", i.OpCode));
                    }
                }

yield_to_calling_coroutine:

                DynValue yieldRequest = m_ValueStack.Pop().ToScalar();

                if (m_CanYield)
                {
                    return(yieldRequest);
                }
                else if (this.State == CoroutineState.Main)
                {
                    throw ScriptRuntimeException.CannotYieldMain();
                }
                else
                {
                    throw ScriptRuntimeException.CannotYield();
                }
            }
            catch (InterpreterException ex)
            {
                FillDebugData(ex, instructionPtr);

                if (!(ex is ScriptRuntimeException))
                {
                    throw;
                }

                if (m_Debug.DebuggerAttached != null)
                {
                    if (m_Debug.DebuggerAttached.SignalRuntimeException((ScriptRuntimeException)ex))
                    {
                        if (instructionPtr >= 0 && instructionPtr < this.m_RootChunk.Code.Count)
                        {
                            ListenDebugger(m_RootChunk.Code[instructionPtr], instructionPtr);
                        }
                    }
                }

                for (int i = 0; i < m_ExecutionStack.Count; i++)
                {
                    var c = m_ExecutionStack.Peek(i);

                    if (c.ErrorHandlerBeforeUnwind != null)
                    {
                        ex.DecoratedMessage = PerformMessageDecorationBeforeUnwind(c.ErrorHandlerBeforeUnwind, ex.DecoratedMessage, GetCurrentSourceRef(instructionPtr));
                    }
                }


                while (m_ExecutionStack.Count > 0)
                {
                    CallStackItem csi = PopToBasePointer();

                    if (csi.ErrorHandler != null)
                    {
                        instructionPtr = csi.ReturnAddress;

                        if (csi.ClrFunction == null)
                        {
                            var argscnt = (int)(m_ValueStack.Pop().Number);
                            m_ValueStack.RemoveLast(argscnt + 1);
                        }

                        var cbargs = new DynValue[] { DynValue.NewString(ex.DecoratedMessage) };

                        DynValue handled = csi.ErrorHandler.Invoke(new ScriptExecutionContext(this, csi.ErrorHandler, GetCurrentSourceRef(instructionPtr)), cbargs);

                        m_ValueStack.Push(handled);

                        goto repeat_execution;
                    }
                    else if ((csi.Flags & CallStackItemFlags.EntryPoint) != 0)
                    {
                        throw;
                    }
                }

                throw;
            }

return_to_native_code:
            return(m_ValueStack.Pop());
        }