static int _requiredMaxStackSizeFor(Statement* statement, int depth = 0) { if (!statement) return 1; switch (statement->statementType()) { case STMT_LEAF: #if DEBUG_SCRIPTVM_CORE _printIndents(depth); printf("-> STMT_LEAF\n"); #endif return 1; case STMT_LIST: { #if DEBUG_SCRIPTVM_CORE _printIndents(depth); printf("-> STMT_LIST\n"); #endif Statements* stmts = (Statements*) statement; int max = 0; for (int i = 0; stmts->statement(i); ++i) { int size = _requiredMaxStackSizeFor( stmts->statement(i), depth+1 ); if (max < size) max = size; } return max + 1; } case STMT_BRANCH: { #if DEBUG_SCRIPTVM_CORE _printIndents(depth); printf("-> STMT_BRANCH\n"); #endif BranchStatement* branchStmt = (BranchStatement*) statement; int max = 0; for (int i = 0; branchStmt->branch(i); ++i) { int size = _requiredMaxStackSizeFor( branchStmt->branch(i), depth+1 ); if (max < size) max = size; } return max + 1; } case STMT_LOOP: { #if DEBUG_SCRIPTVM_CORE _printIndents(depth); printf("-> STMT_LOOP\n"); #endif While* whileStmt = (While*) statement; if (whileStmt->statements()) return _requiredMaxStackSizeFor( whileStmt->statements() ) + 1; else return 1; } } return 1; // actually just to avoid compiler warning }
VMExecStatus_t ScriptVM::exec(VMParserContext* parserContext, VMExecContext* execContex, VMEventHandler* handler) { m_parserContext = dynamic_cast<ParserContext*>(parserContext); if (!m_parserContext) { std::cerr << "No VM parser context provided. Did you load a script?.\n"; return VMExecStatus_t(VM_EXEC_NOT_RUNNING | VM_EXEC_ERROR); } // a ParserContext object is always tied to exactly one ScriptVM object assert(m_parserContext->functionProvider == this); ExecContext* ctx = dynamic_cast<ExecContext*>(execContex); if (!ctx) { std::cerr << "Invalid VM exec context.\n"; return VMExecStatus_t(VM_EXEC_NOT_RUNNING | VM_EXEC_ERROR); } EventHandler* h = dynamic_cast<EventHandler*>(handler); if (!h) return VM_EXEC_NOT_RUNNING; m_parserContext->execContext = ctx; ctx->status = VM_EXEC_RUNNING; StmtFlags_t flags = STMT_SUCCESS; int& frameIdx = ctx->stackFrame; if (frameIdx < 0) { // start condition ... frameIdx = -1; ctx->pushStack(h); } while (flags == STMT_SUCCESS && frameIdx >= 0) { if (frameIdx >= ctx->stack.size()) { // should never happen, otherwise it's a bug ... std::cerr << "CRITICAL: VM stack overflow! (" << frameIdx << ")\n"; flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); break; } ExecContext::StackFrame& frame = ctx->stack[frameIdx]; switch (frame.statement->statementType()) { case STMT_LEAF: { #if DEBUG_SCRIPTVM_CORE _printIndents(frameIdx); printf("-> STMT_LEAF\n"); #endif LeafStatement* leaf = (LeafStatement*) frame.statement; flags = leaf->exec(); ctx->popStack(); break; } case STMT_LIST: { #if DEBUG_SCRIPTVM_CORE _printIndents(frameIdx); printf("-> STMT_LIST subidx=%d\n", frame.subindex); #endif Statements* stmts = (Statements*) frame.statement; if (stmts->statement(frame.subindex)) { ctx->pushStack( stmts->statement(frame.subindex++) ); } else { #if DEBUG_SCRIPTVM_CORE _printIndents(frameIdx); printf("[END OF LIST] subidx=%d\n", frame.subindex); #endif ctx->popStack(); } break; } case STMT_BRANCH: { #if DEBUG_SCRIPTVM_CORE _printIndents(frameIdx); printf("-> STMT_BRANCH\n"); #endif if (frame.subindex < 0) ctx->popStack(); else { BranchStatement* branchStmt = (BranchStatement*) frame.statement; frame.subindex = branchStmt->evalBranch(); if (frame.subindex >= 0) { ctx->pushStack( branchStmt->branch(frame.subindex) ); frame.subindex = -1; } else ctx->popStack(); } break; } case STMT_LOOP: { #if DEBUG_SCRIPTVM_CORE _printIndents(frameIdx); printf("-> STMT_LOOP\n"); #endif While* whileStmt = (While*) frame.statement; if (whileStmt->evalLoopStartCondition() && whileStmt->statements()) { ctx->pushStack( whileStmt->statements() ); } else ctx->popStack(); } } } if (flags & STMT_SUSPEND_SIGNALLED) { ctx->status = VM_EXEC_SUSPENDED; } else { ctx->status = VM_EXEC_NOT_RUNNING; if (flags & STMT_ERROR_OCCURRED) ctx->status = VM_EXEC_ERROR; ctx->reset(); } m_parserContext->execContext = NULL; m_parserContext = NULL; return ctx->status; }