diff options
author | Lars Knoll <[email protected]> | 2013-10-18 15:42:17 +0200 |
---|---|---|
committer | The Qt Project <[email protected]> | 2013-10-29 10:38:45 +0100 |
commit | 5229a8b259286c9ea61036fd6b4bd0039104a206 (patch) | |
tree | 277d62ecedeaf703ce778d86f8cbcb94b9a57fe2 /src | |
parent | 570686d42176af193b15abfe4b7bc17d831f4cf6 (diff) |
Rework exception handling
Start the work to remove c++ exceptions from our JS
exception handling. Rather rely on engine->hasException.
Check the flag after we return from any runtime call in the
JIT.
Implement new try/catch handling code in qv4codegen and
for the JIT that doesn't rely on exceptions. As an added
bonus, we can remove the Try statement in the IR.
Change-Id: Ic95addd6ae03371c43c47e04cac26afdce23a061
Reviewed-by: Simon Hausmann <[email protected]>
Diffstat (limited to 'src')
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 175 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 18 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 14 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_masm.cpp | 78 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_masm_p.h | 44 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_moth.cpp | 27 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_moth_p.h | 4 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_p.cpp | 12 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_p.h | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4jsir.cpp | 51 | ||||
-rw-r--r-- | src/qml/compiler/qv4jsir_p.h | 37 | ||||
-rw-r--r-- | src/qml/compiler/qv4regalloc.cpp | 7 | ||||
-rw-r--r-- | src/qml/compiler/qv4ssa.cpp | 23 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4context.cpp | 63 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4context_p.h | 23 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 16 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtime_p.h | 6 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 34 |
20 files changed, 246 insertions, 397 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index a68920235e..b7ea7491d6 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -403,7 +403,6 @@ Codegen::Codegen(bool strict) , _function(0) , _block(0) , _exitBlock(0) - , _throwBlock(0) , _returnAddress(0) , _env(0) , _loop(0) @@ -1074,13 +1073,13 @@ bool Codegen::visit(BinaryExpression *ast) { if (ast->op == QSOperator::And) { if (_expr.accept(cx)) { - V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); condition(ast->left, iftrue, _expr.iffalse); _block = iftrue; condition(ast->right, _expr.iftrue, _expr.iffalse); } else { - V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); const unsigned r = _block->newTemp(); @@ -1096,13 +1095,13 @@ bool Codegen::visit(BinaryExpression *ast) return false; } else if (ast->op == QSOperator::Or) { if (_expr.accept(cx)) { - V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); condition(ast->left, _expr.iftrue, iffalse); _block = iffalse; condition(ast->right, _expr.iftrue, _expr.iffalse); } else { - V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); const unsigned r = _block->newTemp(); move(_block->TEMP(r), *expression(ast->left)); @@ -1253,9 +1252,9 @@ bool Codegen::visit(CallExpression *ast) bool Codegen::visit(ConditionalExpression *ast) { - V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); const unsigned t = _block->newTemp(); @@ -1761,6 +1760,8 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, { Loop *loop = 0; qSwap(_loop, loop); + QStack<V4IR::BasicBlock *> exceptionHandlers; + qSwap(_exceptionHandlers, exceptionHandlers); ScopeAndFinally *scopeAndFinally = 0; @@ -1768,9 +1769,8 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, V4IR::Function *function = _module->newFunction(name, _function); int functionIndex = _module->functions.count() - 1; - V4IR::BasicBlock *entryBlock = function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *exitBlock = function->newBasicBlock(groupStartBlock(), V4IR::Function::DontInsertBlock); - V4IR::BasicBlock *throwBlock = function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *entryBlock = function->newBasicBlock(groupStartBlock(), 0); + V4IR::BasicBlock *exitBlock = function->newBasicBlock(groupStartBlock(), 0, V4IR::Function::DontInsertBlock); function->hasDirectEval = _env->hasDirectEval; function->usesArgumentsObject = _env->parent && (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); function->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); @@ -1827,15 +1827,10 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(V4IR::UndefinedType, 0)); exitBlock->RET(exitBlock->TEMP(returnAddress)); - V4IR::ExprList *throwArgs = function->New<V4IR::ExprList>(); - throwArgs->expr = throwBlock->TEMP(returnAddress); - throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); - throwBlock->JUMP(exitBlock); qSwap(_function, function); qSwap(_block, entryBlock); qSwap(_exitBlock, exitBlock); - qSwap(_throwBlock, throwBlock); qSwap(_returnAddress, returnAddress); qSwap(_scopeAndFinally, scopeAndFinally); @@ -1871,9 +1866,9 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, qSwap(_function, function); qSwap(_block, entryBlock); qSwap(_exitBlock, exitBlock); - qSwap(_throwBlock, throwBlock); qSwap(_returnAddress, returnAddress); qSwap(_scopeAndFinally, scopeAndFinally); + qSwap(_exceptionHandlers, exceptionHandlers); qSwap(_loop, loop); leaveEnvironment(); @@ -1973,9 +1968,9 @@ bool Codegen::visit(DebuggerStatement *) bool Codegen::visit(DoWhileStatement *ast) { - V4IR::BasicBlock *loopbody = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *loopcond = _function->newBasicBlock(loopbody); - V4IR::BasicBlock *loopend = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *loopbody = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + V4IR::BasicBlock *loopcond = _function->newBasicBlock(loopbody, exceptionHandler()); + V4IR::BasicBlock *loopend = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); enterLoop(ast, loopbody, loopend, loopcond); @@ -2014,9 +2009,9 @@ bool Codegen::visit(ExpressionStatement *ast) bool Codegen::visit(ForEachStatement *ast) { - V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin); - V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin, exceptionHandler()); + V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); enterLoop(ast, foreachin, foreachend, foreachin); @@ -2052,10 +2047,10 @@ bool Codegen::visit(ForEachStatement *ast) bool Codegen::visit(ForStatement *ast) { - V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond); - V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond); - V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond, exceptionHandler()); + V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond, exceptionHandler()); + V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); enterLoop(ast, forcond, forend, forstep); @@ -2085,9 +2080,10 @@ bool Codegen::visit(ForStatement *ast) bool Codegen::visit(IfStatement *ast) { - V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(groupStartBlock()) : 0; - V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + V4IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(groupStartBlock(), exceptionHandler()) : 0; + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + condition(ast->expression, iftrue, ast->ko ? iffalse : endif); _block = iftrue; @@ -2127,7 +2123,7 @@ bool Codegen::visit(LabelledStatement *ast) AST::cast<AST::LocalForEachStatement *>(ast->statement)) { statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. } else { - V4IR::BasicBlock *breakBlock = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *breakBlock = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); enterLoop(ast->statement, 0, breakBlock, /*continueBlock*/ 0); statement(ast->statement); _block->JUMP(breakBlock); @@ -2140,9 +2136,9 @@ bool Codegen::visit(LabelledStatement *ast) bool Codegen::visit(LocalForEachStatement *ast) { - V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin); - V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin, exceptionHandler()); + V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); enterLoop(ast, foreachin, foreachend, foreachin); @@ -2178,10 +2174,10 @@ bool Codegen::visit(LocalForEachStatement *ast) bool Codegen::visit(LocalForStatement *ast) { - V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond); - V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond); - V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond, exceptionHandler()); + V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond, exceptionHandler()); + V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); enterLoop(ast, forcond, forend, forstep); @@ -2225,11 +2221,11 @@ bool Codegen::visit(ReturnStatement *ast) bool Codegen::visit(SwitchStatement *ast) { - V4IR::BasicBlock *switchend = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *switchend = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); if (ast->block) { Result lhs = expression(ast->expression); - V4IR::BasicBlock *switchcond = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *switchcond = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); _block->JUMP(switchcond); V4IR::BasicBlock *previousBlock = 0; @@ -2240,7 +2236,7 @@ bool Codegen::visit(SwitchStatement *ast) for (CaseClauses *it = ast->block->clauses; it; it = it->next) { CaseClause *clause = it->clause; - _block = _function->newBasicBlock(groupStartBlock()); + _block = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); blockMap[clause] = _block; if (previousBlock && !previousBlock->isTerminated()) @@ -2253,7 +2249,7 @@ bool Codegen::visit(SwitchStatement *ast) } if (ast->block->defaultClause) { - _block = _function->newBasicBlock(groupStartBlock()); + _block = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); blockMap[ast->block->defaultClause] = _block; if (previousBlock && !previousBlock->isTerminated()) @@ -2268,7 +2264,7 @@ bool Codegen::visit(SwitchStatement *ast) for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { CaseClause *clause = it->clause; - _block = _function->newBasicBlock(groupStartBlock()); + _block = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); blockMap[clause] = _block; if (previousBlock && !previousBlock->isTerminated()) @@ -2289,7 +2285,7 @@ bool Codegen::visit(SwitchStatement *ast) CaseClause *clause = it->clause; Result rhs = expression(clause->expression); V4IR::BasicBlock *iftrue = blockMap[clause]; - V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); _block = iffalse; } @@ -2298,7 +2294,7 @@ bool Codegen::visit(SwitchStatement *ast) CaseClause *clause = it->clause; Result rhs = expression(clause->expression); V4IR::BasicBlock *iftrue = blockMap[clause]; - V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); _block = iffalse; } @@ -2318,7 +2314,9 @@ bool Codegen::visit(ThrowStatement *ast) { Result expr = expression(ast->expression); move(_block->TEMP(_returnAddress), *expr); - _block->JUMP(_throwBlock); + V4IR::ExprList *throwArgs = _function->New<V4IR::ExprList>(); + throwArgs->expr = _block->TEMP(_returnAddress); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); return false; } @@ -2330,21 +2328,24 @@ bool Codegen::visit(TryStatement *ast) (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) throwSyntaxError(ast->catchExpression->identifierToken, QCoreApplication::translate("qv4codegen", "Catch variable name may not be eval or arguments in strict mode")); - V4IR::BasicBlock *tryBody = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *catchBody = _function->newBasicBlock(groupStartBlock()); // We always need a finally body to clean up the exception handler - V4IR::BasicBlock *finallyBody = _function->newBasicBlock(groupStartBlock()); + // exceptions thrown in finally get catched by the surrounding catch block + V4IR::BasicBlock *finallyBody = _function->newBasicBlock(groupStartBlock(), exceptionHandler(), V4IR::Function::DontInsertBlock); - V4IR::BasicBlock *throwBlock = _function->newBasicBlock(groupStartBlock()); - V4IR::ExprList *throwArgs = _function->New<V4IR::ExprList>(); - throwArgs->expr = throwBlock->TEMP(_returnAddress); - throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); - throwBlock->JUMP(catchBody); - qSwap(_throwBlock, throwBlock); + // the Catch block for catch is the block itself. We protect against recursion by checking the + // hasException boolean + // all basic blocks within try and catch get catched by catchBody + V4IR::BasicBlock *catchBody = _function->newBasicBlock(groupStartBlock(), 0, V4IR::Function::DontInsertBlock); + catchBody->catchBlock = catchBody; + pushExceptionHandler(catchBody); int hasException = _block->newTemp(); move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false)); + V4IR::BasicBlock *tryBody = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + _block->JUMP(tryBody); + + // ### Remove // Pass the hidden "needRethrow" TEMP to the // builtin_delete_exception_handler, in order to have those TEMPs alive for // the duration of the exception handling block. @@ -2354,23 +2355,24 @@ bool Codegen::visit(TryStatement *ast) ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, finishTryArgs); _scopeAndFinally = &tcf; - int exception_to_rethrow = _block->newTemp(); - - _block->TRY(tryBody, catchBody, - _function->newString(ast->catchExpression ? ast->catchExpression->name.toString() : QString()), - _block->TEMP(exception_to_rethrow)); - _block = tryBody; statement(ast->statement); _block->JUMP(finallyBody); + _function->insertBasicBlock(catchBody); _block = catchBody; if (ast->catchExpression) { // check if an exception got thrown within catch. Go to finally // and then rethrow - V4IR::BasicBlock *b = _function->newBasicBlock(groupStartBlock()); - _block->CJUMP(_block->TEMP(hasException), finallyBody, b); + V4IR::BasicBlock *cleanupCatchScope = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + V4IR::BasicBlock *b = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + _block->CJUMP(_block->TEMP(hasException), cleanupCatchScope, b); + + _block = cleanupCatchScope; + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0), 0)); + _block->JUMP(finallyBody); + _block = b; } @@ -2378,12 +2380,16 @@ bool Codegen::visit(TryStatement *ast) if (ast->catchExpression) { ++_function->insideWithOrCatch; + V4IR::ExprList *catchArgs = _function->New<V4IR::ExprList>(); + catchArgs->init(_block->STRING(_function->newString(ast->catchExpression->name.toString()))); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_push_catch_scope, 0, 0), catchArgs)); { ScopeAndFinally scope(_scopeAndFinally, ScopeAndFinally::CatchScope); _scopeAndFinally = &scope; statement(ast->catchExpression->statement); _scopeAndFinally = scope.parent; } + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0), 0)); --_function->insideWithOrCatch; move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false)); } @@ -2391,21 +2397,20 @@ bool Codegen::visit(TryStatement *ast) _scopeAndFinally = tcf.parent; - qSwap(_throwBlock, throwBlock); + // exceptions thrown in finally get catched by the surrounding catch statement + popExceptionHandler(); - V4IR::BasicBlock *after = _function->newBasicBlock(groupStartBlock()); - _block = finallyBody; - - _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), finishTryArgs)); + _function->insertBasicBlock(finallyBody); + _block = finallyBody; if (ast->finallyExpression && ast->finallyExpression->statement) statement(ast->finallyExpression->statement); - V4IR::BasicBlock *rethrowBlock = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *rethrowBlock = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + V4IR::BasicBlock *after = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); _block->CJUMP(_block->TEMP(hasException), rethrowBlock, after); _block = rethrowBlock; - move(_block->TEMP(_returnAddress), _block->TEMP(exception_to_rethrow)); - _block->JUMP(_throwBlock); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_rethrow, /*line*/0, /*column*/0), 0)); _block = after; @@ -2420,14 +2425,13 @@ void Codegen::unwindException(Codegen::ScopeAndFinally *outest) while (_scopeAndFinally != outest) { switch (_scopeAndFinally->type) { case ScopeAndFinally::WithScope: - _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0))); // fall through case ScopeAndFinally::CatchScope: + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0))); _scopeAndFinally = _scopeAndFinally->parent; --_function->insideWithOrCatch; break; case ScopeAndFinally::TryScope: { - _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), _scopeAndFinally->finishTryArgs)); ScopeAndFinally *tc = _scopeAndFinally; _scopeAndFinally = tc->parent; if (tc->finally && tc->finally->statement) @@ -2448,9 +2452,9 @@ bool Codegen::visit(VariableStatement *ast) bool Codegen::visit(WhileStatement *ast) { - V4IR::BasicBlock *whilecond = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *whilebody = _function->newBasicBlock(whilecond); - V4IR::BasicBlock *whileend = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *whilecond = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + V4IR::BasicBlock *whilebody = _function->newBasicBlock(whilecond, exceptionHandler()); + V4IR::BasicBlock *whileend = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); enterLoop(ast, whilecond, whileend, whilecond); @@ -2472,7 +2476,17 @@ bool Codegen::visit(WithStatement *ast) { _function->hasWith = true; - V4IR::BasicBlock *withBlock = _function->newBasicBlock(groupStartBlock()); + // need an exception handler for with to cleanup the with scope + V4IR::BasicBlock *withExceptionHandler = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); + withExceptionHandler->EXP(withExceptionHandler->CALL(withExceptionHandler->NAME(V4IR::Name::builtin_pop_scope, 0, 0), 0)); + if (!exceptionHandler()) + withExceptionHandler->EXP(withExceptionHandler->CALL(withExceptionHandler->NAME(V4IR::Name::builtin_rethrow, 0, 0), 0)); + else + withExceptionHandler->JUMP(exceptionHandler()); + + pushExceptionHandler(withExceptionHandler); + + V4IR::BasicBlock *withBlock = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); _block->JUMP(withBlock); _block = withBlock; @@ -2491,8 +2505,9 @@ bool Codegen::visit(WithStatement *ast) } --_function->insideWithOrCatch; _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0), 0)); + popExceptionHandler(); - V4IR::BasicBlock *next = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *next = _function->newBasicBlock(groupStartBlock(), exceptionHandler()); _block->JUMP(next); _block = next; diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 66b80c377b..652f395a88 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -272,6 +272,22 @@ protected: return it->groupStartBlock; return 0; } + V4IR::BasicBlock *exceptionHandler() const + { + if (_exceptionHandlers.isEmpty()) + return 0; + return _exceptionHandlers.top(); + } + void pushExceptionHandler(V4IR::BasicBlock *handler) + { + handler->isExceptionHandler = true; + _exceptionHandlers.push(handler); + } + void popExceptionHandler() + { + Q_ASSERT(!_exceptionHandlers.isEmpty()); + _exceptionHandlers.pop(); + } V4IR::Expr *member(V4IR::Expr *base, const QString *name); V4IR::Expr *subscript(V4IR::Expr *base, V4IR::Expr *index); @@ -428,7 +444,6 @@ protected: V4IR::Function *_function; V4IR::BasicBlock *_block; V4IR::BasicBlock *_exitBlock; - V4IR::BasicBlock *_throwBlock; unsigned _returnAddress; Environment *_env; Loop *_loop; @@ -436,6 +451,7 @@ protected: ScopeAndFinally *_scopeAndFinally; QHash<AST::Node *, Environment *> _envMap; QHash<AST::FunctionExpression *, int> _functionMap; + QStack<V4IR::BasicBlock *> _exceptionHandlers; bool _strictMode; QList<QQmlError> _errors; diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 3c28633491..b9163b38d3 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -64,13 +64,11 @@ QT_BEGIN_NAMESPACE F(LoadProperty, loadProperty) \ F(StoreProperty, storeProperty) \ F(Push, push) \ - F(EnterTry, enterTry) \ F(CallValue, callValue) \ F(CallProperty, callProperty) \ F(CallElement, callElement) \ F(CallActivationProperty, callActivationProperty) \ F(CallBuiltinThrow, callBuiltinThrow) \ - F(CallBuiltinFinishTry, callBuiltinFinishTry) \ F(CallBuiltinPushScope, callBuiltinPushScope) \ F(CallBuiltinPopScope, callBuiltinPopScope) \ F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ @@ -274,13 +272,6 @@ union Instr MOTH_INSTR_HEADER quint32 value; }; - struct instr_enterTry { - MOTH_INSTR_HEADER - ptrdiff_t tryOffset; - ptrdiff_t catchOffset; - int exceptionVarName; - Param exceptionVar; - }; struct instr_callValue { MOTH_INSTR_HEADER quint32 argc; @@ -315,9 +306,6 @@ union Instr MOTH_INSTR_HEADER Param arg; }; - struct instr_callBuiltinFinishTry { - MOTH_INSTR_HEADER - }; struct instr_callBuiltinPushScope { MOTH_INSTR_HEADER Param arg; @@ -498,13 +486,11 @@ union Instr instr_loadProperty loadProperty; instr_storeProperty storeProperty; instr_push push; - instr_enterTry enterTry; instr_callValue callValue; instr_callProperty callProperty; instr_callElement callElement; instr_callActivationProperty callActivationProperty; instr_callBuiltinThrow callBuiltinThrow; - instr_callBuiltinFinishTry callBuiltinFinishTry; instr_callBuiltinPushScope callBuiltinPushScope; instr_callBuiltinPopScope callBuiltinPopScope; instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp index d5e67d91c3..6fe0f27ec3 100644 --- a/src/qml/compiler/qv4isel_masm.cpp +++ b/src/qml/compiler/qv4isel_masm.cpp @@ -166,7 +166,6 @@ protected: virtual void visitJump(V4IR::Jump *) {} virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); } virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); } - virtual void visitTry(V4IR::Try *s) { s->exceptionVar->accept(this); } virtual void visitPhi(V4IR::Phi *) { Q_UNREACHABLE(); } }; @@ -236,6 +235,7 @@ Assembler::Assembler(InstructionSelection *isel, V4IR::Function* function, QV4:: void Assembler::registerBlock(V4IR::BasicBlock* block, V4IR::BasicBlock *nextBlock) { _addrs[block] = label(); + catchBlock = block->catchBlock; _nextBlock = nextBlock; } @@ -557,6 +557,10 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) foreach (const DataLabelPatch &p, _dataLabelPatches) linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); + // link exception handlers + foreach(Jump jump, exceptionPropagationJumps) + linkBuffer.link(jump, linkBuffer.locationOf(exceptionReturnLabel)); + { QHashIterator<V4IR::BasicBlock *, QVector<DataLabelPtr> > it(_labelPatches); while (it.hasNext()) { @@ -648,9 +652,7 @@ void InstructionSelection::run(int functionIndex) { V4IR::Function *function = irModule->functions[functionIndex]; QVector<Lookup> lookups; - QSet<V4IR::BasicBlock*> reentryBlocks; qSwap(_function, function); - qSwap(_reentryBlocks, reentryBlocks); V4IR::Optimizer opt(_function); opt.run(); @@ -714,17 +716,6 @@ void InstructionSelection::run(int functionIndex) _block = _function->basicBlocks[i]; _as->registerBlock(_block, nextBlock); - if (_reentryBlocks.contains(_block)) { - _as->enterStandardStackFrame(); -#ifdef ARGUMENTS_IN_REGISTERS - _as->move(Assembler::registerForArgument(0), Assembler::ContextRegister); - _as->move(Assembler::registerForArgument(1), Assembler::LocalsRegister); -#else - _as->loadPtr(addressForArgument(0), Assembler::ContextRegister); - _as->loadPtr(addressForArgument(1), Assembler::LocalsRegister); -#endif - } - foreach (V4IR::Stmt *s, _block->statements) { if (s->location.isValid()) _as->recordLineNumber(s->location.startLine); @@ -736,7 +727,6 @@ void InstructionSelection::run(int functionIndex) compilationUnit->codeRefs[functionIndex] = codeRef; qSwap(_function, function); - qSwap(_reentryBlocks, reentryBlocks); delete _as; _as = oldAssembler; qSwap(_removableJumps, removableJumps); @@ -830,62 +820,18 @@ void InstructionSelection::callBuiltinThrow(V4IR::Expr *arg) { generateFunctionCall(Assembler::Void, __qmljs_throw, Assembler::ContextRegister, Assembler::PointerToValue(arg)); + _as->jumpToExceptionHandler(); } -typedef void *(*MiddleOfFunctionEntryPoint(ExecutionContext *, void *localsPtr)); -static void *tryWrapper(ExecutionContext *context, void *localsPtr, MiddleOfFunctionEntryPoint tryBody, MiddleOfFunctionEntryPoint catchBody, - QV4::StringRef exceptionVarName, ValueRef exceptionVar) -{ - exceptionVar = Primitive::undefinedValue(); - void *addressToContinueAt = 0; - SafeValue *jsStackTop = context->engine->jsStackTop; - bool caughtException = false; - try { - addressToContinueAt = tryBody(context, localsPtr); - } catch (...) { - context->engine->jsStackTop = jsStackTop; - exceptionVar = context->catchException(); - caughtException = true; - } - // Can't nest try { ... } catch (...) {} due to inability of nesting foreign exceptions - // with common CXX ABI. - if (caughtException) { - try { - ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(exceptionVarName, exceptionVar, context); - addressToContinueAt = catchBody(catchContext, localsPtr); - context = __qmljs_builtin_pop_scope(catchContext); - } catch (...) { - context->engine->jsStackTop = jsStackTop; - exceptionVar = context->catchException(); - addressToContinueAt = catchBody(context, localsPtr); - } - } - return addressToContinueAt; -} - -void InstructionSelection::visitTry(V4IR::Try *t) +void InstructionSelection::callBuiltinReThrow() { - // Call tryWrapper, which is going to re-enter the same function at the address of the try block. At then end - // of the try function the JIT code will return with the address of the sub-sequent instruction, which tryWrapper - // returns and to which we jump to. - - _reentryBlocks.insert(t->tryBlock); - _reentryBlocks.insert(t->catchBlock); - - generateFunctionCall(Assembler::ReturnValueRegister, tryWrapper, Assembler::ContextRegister, Assembler::LocalsRegister, - Assembler::ReentryBlock(t->tryBlock), Assembler::ReentryBlock(t->catchBlock), - Assembler::PointerToString(*t->exceptionVarName), Assembler::PointerToValue(t->exceptionVar)); - _as->jump(Assembler::ReturnValueRegister); + _as->jumpToExceptionHandler(); } -void InstructionSelection::callBuiltinFinishTry() +void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName) { - // This assumes that we're in code that was called by tryWrapper, so we return to try wrapper - // with the address that we'd like to continue at, which is right after the ret below. - Assembler::DataLabelPtr continuation = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ReturnValueRegister); - _as->leaveStandardStackFrame(); - _as->ret(); - _as->addPatch(continuation, _as->label()); + Assembler::Pointer s = _as->loadStringAddress(Assembler::ScratchRegister, exceptionName); + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_catch_scope, s, Assembler::ContextRegister); } void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) @@ -2052,6 +1998,8 @@ void InstructionSelection::visitRet(V4IR::Ret *s) Q_UNUSED(s); } + _as->exceptionReturnLabel = _as->label(); + const int locals = _as->stackLayout().calculateJSStackFrameSize(); _as->subPtr(Assembler::TrustedImm32(sizeof(QV4::SafeValue)*locals), Assembler::LocalsRegister); _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext, engine)), Assembler::ScratchRegister); diff --git a/src/qml/compiler/qv4isel_masm_p.h b/src/qml/compiler/qv4isel_masm_p.h index bd4c564ab9..3d1b271cdc 100644 --- a/src/qml/compiler/qv4isel_masm_p.h +++ b/src/qml/compiler/qv4isel_masm_p.h @@ -49,6 +49,7 @@ #include "private/qv4lookup_p.h" #include <QtCore/QHash> +#include <QtCore/QStack> #include <config.h> #include <wtf/Vector.h> @@ -62,6 +63,7 @@ QT_BEGIN_NAMESPACE namespace QQmlJS { namespace MASM { + class InstructionSelection; struct CompilationUnit : public QV4::CompiledData::CompilationUnit @@ -88,6 +90,13 @@ struct RelativeCall { {} }; + +template <typename T> +struct ExceptionCheck { + enum { NeedsCheck = 1 }; +}; + + class Assembler : public JSC::MacroAssembler { public: @@ -874,6 +883,23 @@ public: void enterStandardStackFrame(); void leaveStandardStackFrame(); + void checkException() { + loadPtr(Address(ContextRegister, qOffsetOf(QV4::ExecutionContext, engine)), ScratchRegister); + load32(Address(ScratchRegister, qOffsetOf(QV4::ExecutionEngine, hasException)), ScratchRegister); + Jump exceptionThrown = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); + if (catchBlock) + addPatch(catchBlock, exceptionThrown); + else + exceptionPropagationJumps.append(exceptionThrown); + } + void jumpToExceptionHandler() { + Jump exceptionThrown = jump(); + if (catchBlock) + addPatch(catchBlock, exceptionThrown); + else + exceptionPropagationJumps.append(exceptionThrown); + } + template <int argumentNumber, typename T> void loadArgumentOnStackOrRegister(const T &value) { @@ -917,7 +943,6 @@ public: enum { Size = 0 }; }; - template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) { @@ -954,10 +979,15 @@ public: callAbsolute(functionName, function); - storeReturnValue(r); - if (stackSpaceNeeded) add32(TrustedImm32(stackSpaceNeeded), StackPointerRegister); + + if (ExceptionCheck<Callable>::NeedsCheck) { + checkException(); + } + + storeReturnValue(r); + } template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> @@ -1351,6 +1381,9 @@ public: const StackLayout stackLayout() const { return _stackLayout; } ConstantTable &constantTable() { return _constTable; } + Label exceptionReturnLabel; + V4IR::BasicBlock * catchBlock; + QVector<Jump> exceptionPropagationJumps; private: const StackLayout _stackLayout; ConstantTable _constTable; @@ -1410,7 +1443,8 @@ protected: virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result); virtual void callBuiltinDeleteValue(V4IR::Temp *result); virtual void callBuiltinThrow(V4IR::Expr *arg); - virtual void callBuiltinFinishTry(); + virtual void callBuiltinReThrow(); + virtual void callBuiltinPushCatchScope(const QString &exceptionName); virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result); virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result); virtual void callBuiltinPushWithScope(V4IR::Temp *arg); @@ -1469,7 +1503,6 @@ protected: virtual void visitJump(V4IR::Jump *); virtual void visitCJump(V4IR::CJump *); virtual void visitRet(V4IR::Ret *); - virtual void visitTry(V4IR::Try *); Assembler::Jump genTryDoubleConversion(V4IR::Expr *src, Assembler::FPRegisterID dest); Assembler::Jump genInlineBinop(V4IR::AluOp oper, V4IR::Expr *leftSource, @@ -1589,7 +1622,6 @@ private: V4IR::Function* _function; QSet<V4IR::Jump *> _removableJumps; Assembler* _as; - QSet<V4IR::BasicBlock*> _reentryBlocks; CompilationUnit *compilationUnit; }; diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index d354bf66a5..2e31aa10a0 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -684,22 +684,6 @@ void InstructionSelection::visitRet(V4IR::Ret *s) addInstruction(ret); } -void InstructionSelection::visitTry(V4IR::Try *t) -{ - Instruction::EnterTry enterTry; - enterTry.tryOffset = 0; - enterTry.catchOffset = 0; - enterTry.exceptionVarName = registerString(*t->exceptionVarName); - enterTry.exceptionVar = getParam(t->exceptionVar); - ptrdiff_t enterTryLoc = addInstruction(enterTry); - - ptrdiff_t tryLoc = enterTryLoc + (((const char *)&enterTry.tryOffset) - ((const char *)&enterTry)); - _patches[t->tryBlock].append(tryLoc); - - ptrdiff_t catchLoc = enterTryLoc + (((const char *)&enterTry.catchOffset) - ((const char *)&enterTry)); - _patches[t->catchBlock].append(catchLoc); -} - void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) { Instruction::CallActivationProperty call; @@ -788,10 +772,15 @@ void InstructionSelection::callBuiltinThrow(V4IR::Expr *arg) addInstruction(call); } -void InstructionSelection::callBuiltinFinishTry() +void InstructionSelection::callBuiltinReThrow() { - Instruction::CallBuiltinFinishTry call; - addInstruction(call); + // ### +} + + +void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName) +{ + // #### } void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index 725dd264aa..8be28b5605 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -82,7 +82,6 @@ protected: virtual void visitJump(V4IR::Jump *); virtual void visitCJump(V4IR::CJump *); virtual void visitRet(V4IR::Ret *); - virtual void visitTry(V4IR::Try *); virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); virtual void callBuiltinTypeofMember(V4IR::Expr *base, const QString &name, V4IR::Temp *result); @@ -94,7 +93,8 @@ protected: virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result); virtual void callBuiltinDeleteValue(V4IR::Temp *result); virtual void callBuiltinThrow(V4IR::Expr *arg); - virtual void callBuiltinFinishTry(); + virtual void callBuiltinReThrow(); + virtual void callBuiltinPushCatchScope(const QString &exceptionName); virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result); virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result); virtual void callBuiltinPushWithScope(V4IR::Temp *arg); diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 483cb8e6f9..7d10230f43 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -266,9 +266,15 @@ void IRDecoder::callBuiltin(V4IR::Call *call, V4IR::Temp *result) callBuiltinThrow(arg); } return; - case V4IR::Name::builtin_finish_try: - callBuiltinFinishTry(); - return; + case V4IR::Name::builtin_rethrow: { + callBuiltinReThrow(); + } return; + + case V4IR::Name::builtin_push_catch_scope: { + V4IR::String *s = call->args->expr->asString(); + Q_ASSERT(s); + callBuiltinPushCatchScope(*s->value); + } return; case V4IR::Name::builtin_foreach_iterator_object: { V4IR::Temp *arg = call->args->expr->asTemp(); diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index 8fd59b609c..9895e2fd04 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -119,7 +119,8 @@ public: // to implement by subclasses: virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result) = 0; virtual void callBuiltinDeleteValue(V4IR::Temp *result) = 0; virtual void callBuiltinThrow(V4IR::Expr *arg) = 0; - virtual void callBuiltinFinishTry() = 0; + virtual void callBuiltinReThrow() = 0; + virtual void callBuiltinPushCatchScope(const QString &exceptionName) = 0; virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) = 0; virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) = 0; virtual void callBuiltinPushWithScope(V4IR::Temp *arg) = 0; diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 50afcf29c2..f0a37895b5 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -217,11 +217,6 @@ struct RemoveSharedExpressions: V4IR::StmtVisitor, V4IR::ExprVisitor s->expr = cleanup(s->expr); } - virtual void visitTry(Try *) - { - // nothing to do for Try statements - } - virtual void visitPhi(V4IR::Phi *) { Q_UNIMPLEMENTED(); } // expressions @@ -400,8 +395,10 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_delete"; case Name::builtin_throw: return "builtin_throw"; - case Name::builtin_finish_try: - return "builtin_finish_try"; + case Name::builtin_rethrow: + return "builtin_rethrow"; + case Name::builtin_push_catch_scope: + return "builtin_push_catch_scope"; case V4IR::Name::builtin_foreach_iterator_object: return "builtin_foreach_iterator_object"; case V4IR::Name::builtin_foreach_next_property_name: @@ -585,13 +582,6 @@ void Ret::dump(QTextStream &out, Mode) out << ';'; } -void Try::dump(QTextStream &out, Stmt::Mode mode) -{ - out << "try L" << tryBlock->index << "; catch exception in "; - exceptionVar->dump(out); - out << " with the name " << exceptionVarName << " and go to L" << catchBlock->index << ';'; -} - void Phi::dump(QTextStream &out, Stmt::Mode mode) { targetTemp->dump(out); @@ -653,9 +643,9 @@ const QString *Function::newString(const QString &text) return &*strings.insert(text); } -BasicBlock *Function::newBasicBlock(BasicBlock *containingLoop, BasicBlockInsertMode mode) +BasicBlock *Function::newBasicBlock(BasicBlock *containingLoop, BasicBlock *catchBlock, BasicBlockInsertMode mode) { - BasicBlock *block = new BasicBlock(this, containingLoop); + BasicBlock *block = new BasicBlock(this, containingLoop, catchBlock); return mode == InsertBlock ? insertBasicBlock(block) : block; } @@ -905,33 +895,12 @@ Stmt *BasicBlock::RET(Temp *expr) return s; } -Stmt *BasicBlock::TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString *exceptionVarName, Temp *exceptionVar) -{ - if (isTerminated()) - return 0; - - Try *t = function->New<Try>(); - t->init(tryBlock, catchBlock, exceptionVarName, exceptionVar); - appendStatement(t); - - assert(! out.contains(tryBlock)); - out.append(tryBlock); - - assert(! out.contains(catchBlock)); - out.append(catchBlock); - - assert(! tryBlock->in.contains(this)); - tryBlock->in.append(this); - - assert(! catchBlock->in.contains(this)); - catchBlock->in.append(this); - - return t; -} - void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) { - out << 'L' << index << ':' << endl; + out << 'L' << index << ':'; + if (catchBlock) + out << " (catchBlock L" << catchBlock->index << ")"; + out << endl; foreach (Stmt *s, statements) { out << '\t'; s->dump(out, mode); diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 8d090cab1e..ee8e0c0636 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -119,7 +119,6 @@ struct Move; struct Jump; struct CJump; struct Ret; -struct Try; struct Phi; enum AluOp { @@ -215,7 +214,6 @@ struct StmtVisitor { virtual void visitJump(Jump *) = 0; virtual void visitCJump(CJump *) = 0; virtual void visitRet(Ret *) = 0; - virtual void visitTry(Try *) = 0; virtual void visitPhi(Phi *) = 0; }; @@ -312,7 +310,8 @@ struct Name: Expr { builtin_typeof, builtin_delete, builtin_throw, - builtin_finish_try, + builtin_rethrow, + builtin_push_catch_scope, builtin_foreach_iterator_object, builtin_foreach_next_property_name, builtin_push_with_scope, @@ -552,7 +551,6 @@ struct Stmt { virtual Jump *asJump() { return 0; } virtual CJump *asCJump() { return 0; } virtual Ret *asRet() { return 0; } - virtual Try *asTry() { return 0; } virtual Phi *asPhi() { return 0; } virtual void dump(QTextStream &out, Mode mode = HIR) = 0; @@ -646,28 +644,6 @@ struct Ret: Stmt { virtual void dump(QTextStream &out, Mode); }; -struct Try: Stmt { - BasicBlock *tryBlock; - BasicBlock *catchBlock; - const QString *exceptionVarName; - Temp *exceptionVar; // place to store the caught exception, for use when re-throwing - - void init(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString *exceptionVarName, Temp *exceptionVar) - { - this->tryBlock = tryBlock; - this->catchBlock = catchBlock; - this->exceptionVarName = exceptionVarName; - this->exceptionVar = exceptionVar; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitTry(this); } - virtual Try *asTry() { return this; } - - virtual void dump(QTextStream &out, Mode mode); -}; - struct Phi: Stmt { Temp *targetTemp; @@ -751,7 +727,7 @@ struct Function { DontInsertBlock }; - BasicBlock *newBasicBlock(BasicBlock *containingLoop, BasicBlockInsertMode mode = InsertBlock); + BasicBlock *newBasicBlock(BasicBlock *containingLoop, BasicBlock *catchBlock, BasicBlockInsertMode mode = InsertBlock); const QString *newString(const QString &text); void RECEIVE(const QString &name) { formals.append(newString(name)); } @@ -771,17 +747,21 @@ struct Function { struct BasicBlock { Function *function; + BasicBlock *catchBlock; QVector<Stmt *> statements; QVector<BasicBlock *> in; QVector<BasicBlock *> out; QBitArray liveIn; QBitArray liveOut; int index; + bool isExceptionHandler; AST::SourceLocation nextLocation; - BasicBlock(Function *function, BasicBlock *containingLoop) + BasicBlock(Function *function, BasicBlock *containingLoop, BasicBlock *catcher) : function(function) + , catchBlock(catcher) , index(-1) + , isExceptionHandler(false) , _containingGroup(containingLoop) , _groupStart(false) {} @@ -837,7 +817,6 @@ struct BasicBlock { Stmt *JUMP(BasicBlock *target); Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); Stmt *RET(Temp *expr); - Stmt *TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString *exceptionVarName, Temp *exceptionVar); void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); diff --git a/src/qml/compiler/qv4regalloc.cpp b/src/qml/compiler/qv4regalloc.cpp index c35ee860f7..5d1265b4b6 100644 --- a/src/qml/compiler/qv4regalloc.cpp +++ b/src/qml/compiler/qv4regalloc.cpp @@ -193,7 +193,8 @@ protected: // IRDecoder virtual void callBuiltinDeleteName(const QString &, V4IR::Temp *) {} virtual void callBuiltinDeleteValue(V4IR::Temp *) {} virtual void callBuiltinThrow(V4IR::Expr *) {} - virtual void callBuiltinFinishTry() {} + virtual void callBuiltinReThrow() {} + virtual void callBuiltinPushCatchScope(const QString &) {}; virtual void callBuiltinForeachIteratorObject(V4IR::Temp *, V4IR::Temp *) {} virtual void callBuiltinForeachNextProperty(V4IR::Temp *, V4IR::Temp *) {} virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *, V4IR::Temp *) {} @@ -506,9 +507,6 @@ protected: // IRDecoder virtual void visitRet(V4IR::Ret *s) { addUses(s->expr->asTemp(), Use::CouldHaveRegister); } - virtual void visitTry(V4IR::Try *) - { Q_UNREACHABLE(); } // this should never happen, we do not optimize when there is a try in the function - virtual void visitPhi(V4IR::Phi *s) { addDef(s->targetTemp, true); @@ -954,7 +952,6 @@ protected: virtual void visitJump(Jump *) {} virtual void visitCJump(CJump *s) { s->cond->accept(this); } virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitTry(Try *) { Q_UNREACHABLE(); } virtual void visitPhi(Phi *) {} }; } // anonymous namespace diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index d1ebbcc26b..77ee957538 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -118,6 +118,11 @@ void showMeTheCode(Function *function) str.append('L'); str.append(QByteArray::number(bb->index)); str.append(':'); + if (bb->catchBlock) { + str.append(" (exception handler L"); + str.append(QByteArray::number(bb->catchBlock->index)); + str.append(')'); + } for (int i = 66 - str.length(); i; --i) str.append(' '); qout << str; @@ -481,8 +486,6 @@ protected: virtual void visitJump(Jump *) {} virtual void visitCJump(CJump *s) { s->cond->accept(this); } virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitTry(Try *) { // ### TODO - } virtual void visitCall(Call *e) { e->base->accept(this); @@ -725,7 +728,6 @@ protected: virtual void visitJump(Jump *) {} virtual void visitCJump(CJump *s) { s->cond->accept(this); } virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitTry(Try *s) { /* this should never happen */ } virtual void visitConst(Const *) {} virtual void visitString(String *) {} @@ -959,7 +961,6 @@ protected: virtual void visitJump(Jump *) {} virtual void visitCJump(CJump *s) { s->cond->accept(this); } virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitTry(Try *) {} virtual void visitPhi(Phi *s) { addDef(s->targetTemp); @@ -1310,7 +1311,6 @@ private: virtual void visitJump(Jump *) {} virtual void visitCJump(CJump *s) { s->cond->accept(this); } virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitTry(Try *s) { s->exceptionVar->accept(this); } virtual void visitPhi(Phi *s) { s->targetTemp->accept(this); foreach (Expr *e, s->d->incoming) @@ -1520,7 +1520,6 @@ protected: virtual void visitJump(Jump *) { _ty = TypingResult(MissingType); } virtual void visitCJump(CJump *s) { _ty = run(s->cond); } virtual void visitRet(Ret *s) { _ty = run(s->expr); } - virtual void visitTry(Try *s) { setType(s->exceptionVar, VarType); _ty = TypingResult(MissingType); } virtual void visitPhi(Phi *s) { _ty = run(s->d->incoming[0]); for (int i = 1, ei = s->d->incoming.size(); i != ei; ++i) { @@ -1796,7 +1795,6 @@ protected: run(s->cond, BoolType); } virtual void visitRet(Ret *s) { run(s->expr); } - virtual void visitTry(Try *) {} virtual void visitPhi(Phi *s) { Type ty = s->targetTemp->type; for (int i = 0, ei = s->d->incoming.size(); i != ei; ++i) @@ -1818,7 +1816,7 @@ void splitCriticalEdges(Function *f) #endif // create the basic block: - BasicBlock *newBB = new BasicBlock(f, bb->containingGroup()); + BasicBlock *newBB = new BasicBlock(f, bb->containingGroup(), bb->catchBlock); newBB->index = f->basicBlocks.last()->index + 1; f->basicBlocks.append(newBB); Jump *s = f->New<Jump>(); @@ -1962,7 +1960,7 @@ void cleanupBasicBlocks(Function *function) W.removeFirst(); if (toRemove.contains(bb)) continue; - if (bb->in.isEmpty()) { + if (bb->in.isEmpty() && !bb->isExceptionHandler) { foreach (BasicBlock *outBB, bb->out) { int idx = outBB->in.indexOf(bb); if (idx != -1) { @@ -2103,7 +2101,6 @@ protected: virtual void visitJump(Jump *) {} virtual void visitCJump(CJump *s) { check(s->cond); } virtual void visitRet(Ret *s) { check(s->expr); } - virtual void visitTry(Try *) { Q_UNREACHABLE(); } virtual void visitPhi(Phi *s) { for (int i = 0, ei = s->d->incoming.size(); i != ei; ++i) check(s->d->incoming[i]); @@ -2166,6 +2163,11 @@ namespace { /// Important: this assumes that there are no critical edges in the control-flow graph! void purgeBB(BasicBlock *bb, Function *func, DefUsesCalculator &defUses, QVector<Stmt *> &W) { + // don't purge blocks that are entry points for catch statements. They might not be directly + // connected, but are required anyway + if (bb->isExceptionHandler) + return; + QVector<BasicBlock *> toPurge; toPurge.append(bb); @@ -2603,7 +2605,6 @@ protected: virtual void visitJump(Jump *) {} virtual void visitCJump(CJump *s) { s->cond->accept(this); } virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitTry(Try *) {} virtual void visitPhi(Phi *s) { // Handled separately } diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index f3d2ab66ed..c14d987171 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -461,69 +461,6 @@ ReturnedValue ExecutionContext::getProperty(const StringRef name) return 0; } -ReturnedValue ExecutionContext::getPropertyNoThrow(const StringRef name) -{ - Scope scope(this); - ScopedValue v(scope); - name->makeIdentifier(); - - if (name->equals(engine->id_this)) - return callData->thisObject.asReturnedValue(); - - bool hasWith = false; - bool hasCatchScope = false; - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { - if (ctx->type == Type_WithContext) { - ScopedObject w(scope, static_cast<WithContext *>(ctx)->withObject); - hasWith = true; - bool hasProperty = false; - v = w->get(name, &hasProperty); - if (hasProperty) { - return v.asReturnedValue(); - } - continue; - } - - else if (ctx->type == Type_CatchContext) { - hasCatchScope = true; - CatchContext *c = static_cast<CatchContext *>(ctx); - if (c->exceptionVarName->isEqualTo(name)) - return c->exceptionValue.asReturnedValue(); - } - - else if (ctx->type >= Type_CallContext) { - QV4::CallContext *c = static_cast<CallContext *>(ctx); - ScopedFunctionObject f(scope, c->function); - if (f->needsActivation || hasWith || hasCatchScope) { - for (unsigned int i = 0; i < f->varCount; ++i) - if (f->varList[i]->isEqualTo(name)) - return c->locals[i].asReturnedValue(); - for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) - if (f->formalParameterList[i]->isEqualTo(name)) - return c->callData->args[i].asReturnedValue(); - } - if (c->activation) { - bool hasProperty = false; - v = c->activation->get(name, &hasProperty); - if (hasProperty) - return v.asReturnedValue(); - } - if (f->function && f->function->isNamedExpression() - && name->equals(f->function->name)) - return f.asReturnedValue(); - } - - else if (ctx->type == Type_GlobalContext) { - GlobalContext *g = static_cast<GlobalContext *>(ctx); - bool hasProperty = false; - v = g->global->get(name, &hasProperty); - if (hasProperty) - return v.asReturnedValue(); - } - } - return Encode::undefined(); -} - ReturnedValue ExecutionContext::getPropertyAndBase(const StringRef name, ObjectRef base) { Scope scope(this); diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index e5b0c431ca..e85350451b 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -127,21 +127,20 @@ struct Q_QML_EXPORT ExecutionContext void createMutableBinding(const StringRef name, bool deletable); - void Q_NORETURN throwError(const QV4::ValueRef value); - void Q_NORETURN throwError(const QString &message); - void Q_NORETURN throwSyntaxError(const QString &message); - void Q_NORETURN throwSyntaxError(const QString &message, const QString &fileName, int line, int column); - void Q_NORETURN throwTypeError(); - void Q_NORETURN throwTypeError(const QString &message); - void Q_NORETURN throwReferenceError(const ValueRef value); - void Q_NORETURN throwReferenceError(const QString &value, const QString &fileName, int line, int column); - void Q_NORETURN throwRangeError(const ValueRef value); - void Q_NORETURN throwURIError(const ValueRef msg); - void Q_NORETURN throwUnimplemented(const QString &message); + void throwError(const QV4::ValueRef value); + void throwError(const QString &message); + void throwSyntaxError(const QString &message); + void throwSyntaxError(const QString &message, const QString &fileName, int line, int column); + void throwTypeError(); + void throwTypeError(const QString &message); + void throwReferenceError(const ValueRef value); + void throwReferenceError(const QString &value, const QString &fileName, int line, int column); + void throwRangeError(const ValueRef value); + void throwURIError(const ValueRef msg); + void throwUnimplemented(const QString &message); void setProperty(const StringRef name, const ValueRef value); ReturnedValue getProperty(const StringRef name); - ReturnedValue getPropertyNoThrow(const StringRef name); ReturnedValue getPropertyAndBase(const StringRef name, ObjectRef base); bool deleteProperty(const StringRef name); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 2c68c4f400..3449c1aa3c 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -809,7 +809,7 @@ QmlExtensions *ExecutionEngine::qmlExtensions() void ExecutionEngine::throwException(const ValueRef value) { - Q_ASSERT(!hasException); +// Q_ASSERT(!hasException); hasException = true; exceptionValue = value; QV4::Scope scope(this); @@ -822,8 +822,6 @@ void ExecutionEngine::throwException(const ValueRef value) if (debugger) debugger->aboutToThrow(value); - UnwindHelper::prepareForUnwind(current); - throwInternal(); } ReturnedValue ExecutionEngine::catchException(ExecutionContext *catchingContext, StackTrace *trace) diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 24fb4ad923..3eaf08dd2a 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -323,10 +323,10 @@ struct Q_QML_EXPORT ExecutionEngine // Exception handling SafeValue exceptionValue; - bool hasException; + quint32 hasException; StackTrace exceptionStackTrace; - void Q_NORETURN throwException(const ValueRef value); + void throwException(const ValueRef value); ReturnedValue catchException(ExecutionContext *catchingContext, StackTrace *trace); // Use only inside catch(...) -- will re-throw if no JS exception diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 6f914fa3c2..9b16f4e45a 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -769,6 +769,9 @@ ReturnedValue __qmljs_call_activation_property(ExecutionContext *context, const ScopedObject base(scope); ScopedValue func(scope, context->getPropertyAndBase(name, base)); + if (context->engine->hasException) + return Encode::undefined(); + if (base) callData->thisObject = base; @@ -866,6 +869,9 @@ ReturnedValue __qmljs_construct_activation_property(ExecutionContext *context, c { Scope scope(context); ScopedValue func(scope, context->getProperty(name)); + if (context->engine->hasException) + return Encode::undefined(); + Object *f = func->asObject(); if (!f) context->throwTypeError(); @@ -931,7 +937,9 @@ ReturnedValue __qmljs_builtin_typeof(ExecutionContext *ctx, const ValueRef value QV4::ReturnedValue __qmljs_builtin_typeof_name(ExecutionContext *context, const StringRef name) { Scope scope(context); - ScopedValue prop(scope, context->getPropertyNoThrow(name)); + ScopedValue prop(scope, context->getProperty(name)); + // typeof doesn't throw. clear any possible exception + context->engine->hasException = false; return __qmljs_builtin_typeof(context, prop); } @@ -959,9 +967,11 @@ ExecutionContext *__qmljs_builtin_push_with_scope(const ValueRef o, ExecutionCon return ctx->newWithContext(obj); } -ExecutionContext *__qmljs_builtin_push_catch_scope(const StringRef exceptionVarName, const ValueRef exceptionValue, ExecutionContext *ctx) +ExecutionContext *__qmljs_builtin_push_catch_scope(const StringRef exceptionVarName, ExecutionContext *ctx) { - return ctx->newCatchContext(exceptionVarName, exceptionValue); + Scope scope(ctx); + ScopedValue v(scope, ctx->engine->catchException(ctx, 0)); + return ctx->newCatchContext(exceptionVarName, v); } ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx) diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 4ede7ae991..50a1ce17d1 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -127,9 +127,9 @@ QV4::ReturnedValue __qmljs_builtin_typeof_name(QV4::ExecutionContext *context, c QV4::ReturnedValue __qmljs_builtin_typeof_member(QV4::ExecutionContext* context, const QV4::ValueRef base, const QV4::StringRef name); QV4::ReturnedValue __qmljs_builtin_typeof_element(QV4::ExecutionContext* context, const QV4::ValueRef base, const QV4::ValueRef index); -void Q_NORETURN __qmljs_builtin_rethrow(QV4::ExecutionContext *context); +void __qmljs_builtin_rethrow(QV4::ExecutionContext *context); QV4::ExecutionContext *__qmljs_builtin_push_with_scope(const QV4::ValueRef o, QV4::ExecutionContext *ctx); -QV4::ExecutionContext *__qmljs_builtin_push_catch_scope(const QV4::StringRef exceptionVarName, const QV4::ValueRef exceptionValue, QV4::ExecutionContext *ctx); +QV4::ExecutionContext *__qmljs_builtin_push_catch_scope(const QV4::StringRef exceptionVarName, QV4::ExecutionContext *ctx); QV4::ExecutionContext *__qmljs_builtin_pop_scope(QV4::ExecutionContext *ctx); void __qmljs_builtin_declare_var(QV4::ExecutionContext *ctx, bool deletable, const QV4::StringRef name); void __qmljs_builtin_define_property(QV4::ExecutionContext *ctx, const QV4::ValueRef object, const QV4::StringRef name, QV4::ValueRef val); @@ -199,7 +199,7 @@ QV4::ReturnedValue __qmljs_delete_subscript(QV4::ExecutionContext *ctx, const QV ReturnedValue __qmljs_delete_member(QV4::ExecutionContext *ctx, const QV4::ValueRef base, const QV4::StringRef name); ReturnedValue __qmljs_delete_name(QV4::ExecutionContext *ctx, const QV4::StringRef name); -void Q_NORETURN __qmljs_throw(QV4::ExecutionContext*, const QV4::ValueRef value); +void __qmljs_throw(QV4::ExecutionContext*, const QV4::ValueRef value); // binary operators typedef QV4::ReturnedValue (*BinOp)(const QV4::ValueRef left, const QV4::ValueRef right); diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 64b49f9a05..04a07093c1 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -365,40 +365,6 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *&code, __qmljs_throw(context, VALUEPTR(instr.arg)); MOTH_END_INSTR(CallBuiltinThrow) - MOTH_BEGIN_INSTR(EnterTry) - VALUE(instr.exceptionVar) = QV4::Primitive::undefinedValue(); - bool caughtException = false; - try { - const uchar *tryCode = ((uchar *)&instr.tryOffset) + instr.tryOffset; - run(context, tryCode, stack, stackSize); - code = tryCode; - context->interpreterInstructionPointer = &code; - } catch (...) { - STOREVALUE(instr.exceptionVar, context->catchException()); - caughtException = true; - } - if (caughtException) { - try { - QV4::ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(runtimeStrings[instr.exceptionVarName], VALUEPTR(instr.exceptionVar), context); - const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset; - run(catchContext, catchCode, stack, stackSize); - code = catchCode; - context->interpreterInstructionPointer = &code; - context = __qmljs_builtin_pop_scope(catchContext); - } catch (...) { - STOREVALUE(instr.exceptionVar, context->catchException()); - const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset; - run(context, catchCode, stack, stackSize); - code = catchCode; - context->interpreterInstructionPointer = &code; - } - } - MOTH_END_INSTR(EnterTry) - - MOTH_BEGIN_INSTR(CallBuiltinFinishTry) - return QV4::ReturnedValue(0); - MOTH_END_INSTR(CallBuiltinFinishTry) - MOTH_BEGIN_INSTR(CallBuiltinPushScope) context = __qmljs_builtin_push_with_scope(VALUEPTR(instr.arg), context); MOTH_END_INSTR(CallBuiltinPushScope) |