Proper exception handling
authorLars Knoll <lars.knoll@digia.com>
Sun, 21 Oct 2012 22:18:31 +0000 (00:18 +0200)
committerLars Knoll <lars.knoll@digia.com>
Wed, 24 Oct 2012 06:46:49 +0000 (08:46 +0200)
Implement exceptions using setjmp/longjmp. The
advantage is that this removes all exception
handling overhead from regular code, the only
code that still has a (very small) overhead
is the try{} catch() {} statement.

Change-Id: I43d6a60dfc9dfd4b7a20d2e99ab0a9315b4d8a2f
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
12 files changed:
llvm_runtime.cpp
main.cpp
qmljs_objects.h
qmljs_runtime.cpp
qmljs_runtime.h
qv4codegen.cpp
qv4codegen_p.h
qv4ir.cpp
qv4ir_p.h
qv4isel_llvm.cpp
qv4isel_masm.cpp
qv4isel_masm_p.h

index fbc534f..158cb66 100644 (file)
@@ -457,11 +457,6 @@ void __qmljs_llvm_throw(Context *context, Value *value)
     __qmljs_throw(*value, context);
 }
 
-void __qmljs_llvm_rethrow(Context *context, Value *result)
-{
-    *result = __qmljs_rethrow(context);
-}
-
 void __qmljs_llvm_get_this_object(Context *ctx, Value *result)
 {
     *result = __qmljs_get_thisObject(ctx);
index d20b350..f5f51c3 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -44,6 +44,7 @@
 #endif
 
 #include "qmljs_objects.h"
+#include "qmljs_runtime.h"
 #include "qv4codegen_p.h"
 #include "qv4isel_masm_p.h"
 #include "qv4isel_moth_p.h"
@@ -239,7 +240,6 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS
             return;
     }
 
-    ctx->hasUncaughtException = false;
     if (! ctx->activation.isObject())
         ctx->activation = VM::Value::fromObject(new QQmlJS::VM::Object());
 
@@ -247,6 +247,15 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS
         ctx->activation.objectValue()->setProperty(ctx, *local, QQmlJS::VM::Value::undefinedValue());
     }
 
+    void * buf = __qmljs_create_exception_handler(ctx);
+    if (setjmp(*(jmp_buf *)buf)) {
+        if (VM::ErrorObject *e = ctx->result.asErrorObject())
+            std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl;
+        else
+            std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl;
+        return;
+    }
+
     if (useMoth) {
         Moth::VME vme;
         vme(ctx, code);
@@ -254,12 +263,7 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS
         globalCode->code(ctx, globalCode->codeData);
     }
 
-    if (ctx->hasUncaughtException) {
-        if (VM::ErrorObject *e = ctx->result.asErrorObject())
-            std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl;
-        else
-            std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl;
-    } else if (! ctx->result.isUndefined()) {
+    if (! ctx->result.isUndefined()) {
         if (! qgetenv("SHOW_EXIT_VALUE").isNull())
             std::cout << "exit value: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl;
     }
index a4c9082..a98b640 100644 (file)
@@ -49,6 +49,7 @@
 #include <QtCore/QRegularExpression>
 #include <cstdio>
 #include <cassert>
+#include <setjmp.h>
 
 namespace QQmlJS {
 
@@ -459,6 +460,13 @@ struct ExecutionEngine
     String *id_arguments;
     String *id___proto__;
 
+    struct ExceptionHandler {
+        Context *context;
+        jmp_buf stackFrame;
+    };
+
+    QVector<ExceptionHandler> unwindStack;
+
     ExecutionEngine();
 
     Context *newContext();
index 8a1ffc2..f0c7326 100644 (file)
@@ -284,7 +284,6 @@ void Context::init(ExecutionEngine *eng)
     vars = 0;
     varCount = 0;
     calledAsConstructor = false;
-    hasUncaughtException = false;
 }
 
 Value *Context::lookupPropertyDescriptor(String *name)
@@ -302,7 +301,7 @@ Value *Context::lookupPropertyDescriptor(String *name)
 void Context::throwError(Value value)
 {
     result = value;
-    hasUncaughtException = true;
+    __qmljs_builtin_throw(value, this);
 }
 
 void Context::throwError(const QString &message)
@@ -330,10 +329,11 @@ void Context::throwReferenceError(Value value)
     throwError(Value::fromObject(engine->newErrorObject(Value::fromString(this, msg))));
 }
 
-void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc)
+void Context::initCallContext(Context *parent, const Value *object, FunctionObject *f, Value *args, unsigned argc)
 {
-    engine = e;
-    parent = f->scope;
+    engine = parent->engine;
+    this->parent = f->scope;
+    assert(this->parent == f->scope);
     result = Value::undefinedValue();
 
     if (f->needsActivation)
@@ -360,23 +360,22 @@ void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionO
     vars = f->varList;
     varCount = f->varCount;
     locals = varCount ? new Value[varCount] : 0;
-    hasUncaughtException = false;
     calledAsConstructor = false;
     if (varCount)
         std::fill(locals, locals + varCount, Value::undefinedValue());
 }
 
-void Context::leaveCallContext(FunctionObject *f)
+void Context::leaveCallContext()
 {
-    if (! f->needsActivation) {
+    if (!activation.isNull()) {
         delete[] locals;
         locals = 0;
     }
 }
 
-void Context::initConstructorContext(ExecutionEngine *e, Value *object, FunctionObject *f, Value *args, unsigned argc)
+void Context::initConstructorContext(Context *parent, Value *object, FunctionObject *f, Value *args, unsigned argc)
 {
-    initCallContext(e, object, f, args, argc);
+    initCallContext(parent, object, f, args, argc);
     calledAsConstructor = true;
 }
 
@@ -390,7 +389,7 @@ void Context::leaveConstructorContext(FunctionObject *f)
     if (! thisObject.isObject())
         thisObject.objectValue()->prototype = engine->objectPrototype;
 
-    leaveCallContext(f);
+    leaveCallContext();
 }
 
 extern "C" {
@@ -1205,13 +1204,9 @@ Value __qmljs_call_property(Context *context, Value base, String *name, Value *a
     if (FunctionObject *f = func.asFunctionObject()) {
         Context k;
         Context *ctx = f->needsActivation ? context->engine->newContext() : &k;
-        ctx->initCallContext(context->engine, &thisObject, f, args, argc);
+        ctx->initCallContext(context, &thisObject, f, args, argc);
         f->call(ctx);
-        if (ctx->hasUncaughtException) {
-            context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception
-            context->result = ctx->result;
-        }
-        ctx->leaveCallContext(f);
+        ctx->leaveCallContext();
         result = ctx->result;
     } else {
         context->throwTypeError();
@@ -1224,13 +1219,9 @@ Value __qmljs_call_function(Context *context, Value thisObject, FunctionObject *
     Context k;
     Context *ctx = f->needsActivation ? context->engine->newContext() : &k;
     const Value *that = thisObject.isUndefined() ? 0 : &thisObject;
-    ctx->initCallContext(context->engine, that, f, args, argc);
+    ctx->initCallContext(context, that, f, args, argc);
     f->call(ctx);
-    if (ctx->hasUncaughtException) {
-        context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception
-        context->result = ctx->result;
-    }
-    ctx->leaveCallContext(f);
+    ctx->leaveCallContext();
     return ctx->result;
 }
 
@@ -1259,12 +1250,8 @@ Value __qmljs_construct_value(Context *context, Value func, Value *args, int arg
     if (FunctionObject *f = func.asFunctionObject()) {
         Context k;
         Context *ctx = f->needsActivation ? context->engine->newContext() : &k;
-        ctx->initConstructorContext(context->engine, 0, f, args, argc);
+        ctx->initConstructorContext(context, 0, f, args, argc);
         f->construct(ctx);
-        if (ctx->hasUncaughtException) {
-            context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception
-            context->result = ctx->result;
-        }
         ctx->leaveConstructorContext(f);
         return ctx->result;
     }
@@ -1282,13 +1269,9 @@ Value __qmljs_construct_property(Context *context, Value base, String *name, Val
     if (FunctionObject *f = func.asFunctionObject()) {
         Context k;
         Context *ctx = f->needsActivation ? context->engine->newContext() : &k;
-        ctx->initConstructorContext(context->engine, 0, f, args, argc);
+        ctx->initConstructorContext(context, 0, f, args, argc);
         ctx->calledAsConstructor = true;
         f->construct(ctx);
-        if (ctx->hasUncaughtException) {
-            context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception
-            context->result = ctx->result;
-        }
         ctx->leaveConstructorContext(f);
         return ctx->result;
     }
@@ -1298,11 +1281,37 @@ Value __qmljs_construct_property(Context *context, Value base, String *name, Val
 
 void __qmljs_throw(Value value, Context *context)
 {
-    context->hasUncaughtException = true;
-    context->result = value;
+    assert(!context->engine->unwindStack.isEmpty());
+
+    ExecutionEngine::ExceptionHandler handler = context->engine->unwindStack.last();
+
+    // clean up call contexts
+    while (context != handler.context) {
+        context->leaveCallContext();
+        context = context->parent;
+    }
+
+    handler.context->result = value;
+
+    longjmp(handler.stackFrame, 1);
+}
+
+void *__qmljs_create_exception_handler(Context *context)
+{
+    context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler());
+    ExecutionEngine::ExceptionHandler *handler = &context->engine->unwindStack.last();
+    handler->context = context;
+    return handler->stackFrame;
+}
+
+void __qmljs_delete_exception_handler(Context *context)
+{
+    assert(!context->engine->unwindStack.isEmpty());
+
+    context->engine->unwindStack.pop_back();
 }
 
-Value __qmljs_rethrow(Context *context)
+Value __qmljs_get_exception(Context *context)
 {
     return context->result;
 }
@@ -1317,11 +1326,6 @@ void __qmljs_builtin_throw(Value val, Context *context)
     __qmljs_throw(val, context);
 }
 
-Value __qmljs_builtin_rethrow(Context *context)
-{
-    return context->result;
-}
-
 } // extern "C"
 
 
index 012c556..e424b08 100644 (file)
@@ -105,7 +105,7 @@ Value __qmljs_construct_value(Context *context, Value func, Value *args, int arg
 
 Value __qmljs_builtin_typeof(Value val, Context *context);
 void __qmljs_builtin_throw(Value val, Context *context);
-Value __qmljs_builtin_rethrow(Context *context);
+
 
 // constructors
 Value __qmljs_init_closure(IR::Function *clos, Context *ctx);
@@ -182,7 +182,10 @@ Value __qmljs_delete_value(Context *ctx, Value value);
 
 Value __qmljs_typeof(Value value, Context *ctx);
 void __qmljs_throw(Value value, Context *context);
-Value __qmljs_rethrow(Context *context);
+// actually returns a jmp_buf *
+void *__qmljs_create_exception_handler(Context *context);
+void __qmljs_delete_exception_handler(Context *context);
+Value __qmljs_get_exception(Context *context);
 
 // binary operators
 Value __qmljs_instanceof(Value left, Value right, Context *ctx);
@@ -570,7 +573,6 @@ struct Context {
     String **vars;
     unsigned int varCount;
     int calledAsConstructor;
-    int hasUncaughtException;
 
     Value *lookupPropertyDescriptor(String *name);
 
@@ -600,10 +602,10 @@ struct Context {
     void throwUnimplemented(const QString &message);
 #endif
 
-    void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc);
-    void leaveCallContext(FunctionObject *f);
+    void initCallContext(Context *parent, const Value *object, FunctionObject *f, Value *args, unsigned argc);
+    void leaveCallContext();
 
-    void initConstructorContext(ExecutionEngine *e, Value *object, FunctionObject *f, Value *args, unsigned argc);
+    void initConstructorContext(Context *parent, Value *object, FunctionObject *f, Value *args, unsigned argc);
     void leaveConstructorContext(FunctionObject *f);
 };
 
index 5dd0041..a037739 100644 (file)
@@ -294,7 +294,6 @@ Codegen::Codegen()
     , _block(0)
     , _exitBlock(0)
     , _throwBlock(0)
-    , _handlersBlock(0)
     , _returnAddress(0)
     , _mode(GlobalCode)
     , _env(0)
@@ -1069,8 +1068,9 @@ bool Codegen::visit(FunctionExpression *ast)
 
 bool Codegen::visit(IdentifierExpression *ast)
 {
+    int index = _env->findMember(ast->name.toString());
+
     if (! _function->hasDirectEval && _env->parent) {
-        int index = _env->findMember(ast->name.toString());
         if (index != -1) {
             _expr.code = _block->TEMP(index);
             return false;
@@ -1082,6 +1082,12 @@ bool Codegen::visit(IdentifierExpression *ast)
         }
     }
 
+    if (index >= _env->vars.size()) {
+        // named local variable, e.g. in a catch statement
+        _expr.code = _block->TEMP(index);
+        return false;
+    }
+
     _expr.code = _block->NAME(ast->name.toString(),
                               ast->identifierToken.startLine,
                               ast->identifierToken.startColumn);
@@ -1484,7 +1490,6 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
     qSwap(_block, entryBlock);
     qSwap(_exitBlock, exitBlock);
     qSwap(_throwBlock, throwBlock);
-    qSwap(_handlersBlock, handlersBlock);
     qSwap(_returnAddress, returnAddress);
 
     for (FormalParameterList *it = formals; it; it = it->next) {
@@ -1512,17 +1517,13 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
 
     _block->JUMP(_exitBlock);
 
-    _throwBlock->JUMP(_function->handlersBlock);
-
-    _handlersBlock->MOVE(_handlersBlock->TEMP(_returnAddress),
-                         _handlersBlock->CALL(_handlersBlock->NAME(IR::Name::builtin_rethrow, 0, 0), 0));
-    _handlersBlock->JUMP(_exitBlock);
+    // ### should not be required
+    _throwBlock->JUMP(_exitBlock);
 
     qSwap(_function, function);
     qSwap(_block, entryBlock);
     qSwap(_exitBlock, exitBlock);
     qSwap(_throwBlock, throwBlock);
-    qSwap(_handlersBlock, handlersBlock);
     qSwap(_returnAddress, returnAddress);
 
     leaveEnvironment();
@@ -1858,9 +1859,74 @@ bool Codegen::visit(ThrowStatement *ast)
     return false;
 }
 
-bool Codegen::visit(TryStatement *)
+bool Codegen::visit(TryStatement *ast)
 {
-    assert(!"not implemented");
+    IR::BasicBlock *tryBody = _function->newBasicBlock();
+    IR::BasicBlock *catchBody = ast->catchExpression ?  _function->newBasicBlock() : 0;
+    IR::BasicBlock *finallyBody = ast->finallyExpression ? _function->newBasicBlock() : 0;
+    IR::BasicBlock *after = _function->newBasicBlock();
+    assert(catchBody || finallyBody);
+
+    int inCatch = 0;
+    if (catchBody && finallyBody) {
+        inCatch = _block->newTemp();
+        move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, false));
+    }
+
+    int hasException = _block->newTemp();
+    move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), 0));
+
+    _block->CJUMP(_block->TEMP(hasException), catchBody ? catchBody : finallyBody, tryBody);
+
+    _block = tryBody;
+    statement(ast->statement);
+    _block->JUMP(finallyBody ? finallyBody : after);
+
+    // regular flow does not go into the catch statement
+    if (catchBody) {
+        _block = catchBody;
+
+        if (finallyBody) {
+            if (inCatch != 0) {
+                // an exception got thrown within catch. Go to finally
+                // and then rethrow
+                IR::BasicBlock *b = _function->newBasicBlock();
+                _block->CJUMP(_block->TEMP(inCatch), finallyBody, b);
+                _block = b;
+            }
+            // if we have finally we need to clear the exception here, so we don't rethrow
+            move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, true));
+            move(_block->TEMP(hasException), _block->CONST(IR::BoolType, false));
+        } else {
+            // otherwise remove the exception handler, so that throw inside catch will
+            // give the correct result
+            _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0));
+        }
+
+        const int exception = _block->newTemp();
+        move(_block->TEMP(exception), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0));
+
+        // the variable ued in the catch statement is local and hides any global
+        // variable with the same name.
+        int hiddenIndex = _env->findMember(ast->catchExpression->name.toString());
+        _env->members.insert(ast->catchExpression->name.toString(), exception);
+
+        statement(ast->catchExpression->statement);
+
+        // reset the variable name to the one from the outer scope
+        _env->members.insert(ast->catchExpression->name.toString(), hiddenIndex);
+        _block->JUMP(finallyBody ? finallyBody : after);
+    }
+
+    if (finallyBody) {
+        _block = finallyBody;
+        _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0));
+        statement(ast->finallyExpression->statement);
+        _block->CJUMP(_block->TEMP(hasException), _throwBlock, after);
+    }
+
+    _block = after;
+
     return false;
 }
 
index 8e9f505..95fedb5 100644 (file)
@@ -312,7 +312,6 @@ private:
     IR::BasicBlock *_block;
     IR::BasicBlock *_exitBlock;
     IR::BasicBlock *_throwBlock;
-    IR::BasicBlock *_handlersBlock;
     unsigned _returnAddress;
     Mode _mode;
     Environment *_env;
index f3db7e9..40c85c8 100644 (file)
--- a/qv4ir.cpp
+++ b/qv4ir.cpp
@@ -213,12 +213,34 @@ void Name::init(Builtin builtin, quint32 line, quint32 column)
     this->column = column;
 }
 
+static const char *builtin_to_string(Name::Builtin b)
+{
+    switch (b) {
+    case Name::builtin_invalid:
+        return "builtin_invalid";
+    case Name::builtin_typeof:
+        return "builtin_typeof";
+    case Name::builtin_delete:
+        return "builtin_delete";
+    case Name::builtin_throw:
+        return "builtin_throw";
+    case Name::builtin_create_exception_handler:
+        return "builtin_create_exception_handler";
+    case Name::builtin_delete_exception_handler:
+        return "builtin_delete_exception_handler";
+    case Name::builtin_get_exception:
+        return "builtin_get_exception";
+    }
+    return "builtin_(###FIXME)";
+};
+
+
 void Name::dump(QTextStream &out)
 {
     if (id)
         out << *id;
     else
-        out << "__qmljs_builtin_%" << (int) builtin;
+        out << builtin_to_string(builtin);
 }
 
 void Temp::dump(QTextStream &out)
index efae2e4..7853cc2 100644 (file)
--- a/qv4ir_p.h
+++ b/qv4ir_p.h
@@ -272,7 +272,9 @@ struct Name: Expr {
         builtin_typeof,
         builtin_delete,
         builtin_throw,
-        builtin_rethrow
+        builtin_create_exception_handler,
+        builtin_delete_exception_handler,
+        builtin_get_exception
     };
 
     const QString *id;
index eae3bc4..040111f 100644 (file)
@@ -935,13 +935,6 @@ void LLVMInstructionSelection::genCallName(IR::Call *e, llvm::Value *result)
             _llvmValue = llvm::UndefValue::get(_valueTy);
             return;
 
-        case IR::Name::builtin_rethrow:
-            CreateCall2(_llvmModule->getFunction("__qmljs_llvm_rethrow"),
-                        _llvmFunction->arg_begin(), result);
-            _llvmValue = CreateLoad(result);
-            return;
-        }
-
         Q_UNREACHABLE();
     } else {
         llvm::Value *name = getIdentifier(*base->id);
index e5c78ab..5254b6d 100644 (file)
@@ -139,6 +139,11 @@ void InstructionSelection::operator()(IR::Function *function)
         functions[ctl.externalFunction.value()] = ctl.functionName;
     }
 
+    foreach (CatchBlockToLink cbl, _catchHandlers) {
+        Label target = _addrs.value(cbl.catchBlock);
+        linkBuffer.patch(cbl.ptr, linkBuffer.locationOf(target));
+    }
+
 #if OS(LINUX)
     char* disasmOutput = 0;
     size_t disasmLength = 0;
@@ -204,7 +209,6 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu
             IR::Temp *arg = call->args->expr->asTemp();
             assert(arg != 0);
             generateFunctionCall(result, __qmljs_builtin_typeof, arg, ContextRegister);
-            checkExceptions();
         }
             break;
         case IR::Name::builtin_delete:
@@ -214,13 +218,18 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu
             IR::Temp *arg = call->args->expr->asTemp();
             assert(arg != 0);
             generateFunctionCall(Void, __qmljs_builtin_throw, arg, ContextRegister);
-            checkExceptions();
         }
             break;
-        case IR::Name::builtin_rethrow:
-            // don't use callRuntimeMethod, as we need to return to avoid checking the exceptions
-            generateFunctionCall(result, __qmljs_builtin_rethrow, ContextRegister);
-            return;
+        case IR::Name::builtin_create_exception_handler:
+            generateFunctionCall(ReturnValueRegister, __qmljs_create_exception_handler, ContextRegister);
+            generateFunctionCall(result, setjmp, ReturnValueRegister);
+            break;
+        case IR::Name::builtin_delete_exception_handler:
+            generateFunctionCall(Void, __qmljs_delete_exception_handler, ContextRegister);
+            break;
+        case IR::Name::builtin_get_exception:
+            generateFunctionCall(result, __qmljs_get_exception, ContextRegister);
+            break;
         }
     }
 }
@@ -234,7 +243,6 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result)
     int argc = prepareVariableArguments(call->args);
     IR::Temp* thisObject = 0;
     generateFunctionCall(result, __qmljs_call_value, ContextRegister, thisObject, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc));
-    checkExceptions();
 }
 
 void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result)
@@ -245,7 +253,6 @@ void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result)
 
     int argc = prepareVariableArguments(call->args);
     generateFunctionCall(result, __qmljs_call_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc));
-    checkExceptions();
 }
 
 void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result)
@@ -264,7 +271,6 @@ void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result)
 
     int argc = prepareVariableArguments(call->args);
     generateFunctionCall(result, __qmljs_construct_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc));
-    checkExceptions();
 }
 
 void InstructionSelection::constructValue(IR::New *call, IR::Temp *result)
@@ -274,14 +280,6 @@ void InstructionSelection::constructValue(IR::New *call, IR::Temp *result)
 
     int argc = prepareVariableArguments(call->args);
     generateFunctionCall(result, __qmljs_construct_value, ContextRegister, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc));
-    checkExceptions();
-}
-
-void InstructionSelection::checkExceptions()
-{
-    Address addr(ContextRegister, offsetof(Context, hasUncaughtException));
-    Jump jmp = branch8(NotEqual, addr, TrustedImm32(0));
-    _patches[_function->handlersBlock].append(jmp);
 }
 
 void InstructionSelection::visitExp(IR::Exp *s)
@@ -325,7 +323,6 @@ void InstructionSelection::visitMove(IR::Move *s)
 
             if (IR::Temp *t = s->source->asTemp()) {
                 generateFunctionCall(Void, __qmljs_set_activation_property, ContextRegister, propertyName, t);
-                checkExceptions();
                 return;
             } else {
                 Q_UNREACHABLE();
@@ -337,7 +334,6 @@ void InstructionSelection::visitMove(IR::Move *s)
                 } else {
                     String *propertyName = identifier(*n->id);
                     generateFunctionCall(t, __qmljs_get_activation_property, ContextRegister, propertyName);
-                    checkExceptions();
                 }
                 return;
             } else if (IR::Const *c = s->source->asConst()) {
@@ -399,14 +395,12 @@ void InstructionSelection::visitMove(IR::Move *s)
                 //__qmljs_get_property(ctx, result, object, name);
                 if (IR::Temp *base = m->base->asTemp()) {
                     generateFunctionCall(t, __qmljs_get_property, ContextRegister, base, identifier(*m->name));
-                    checkExceptions();
                     return;
                 }
                 assert(!"wip");
                 return;
             } else if (IR::Subscript *ss = s->source->asSubscript()) {
                 generateFunctionCall(t, __qmljs_get_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp());
-                checkExceptions();
                 return;
             } else if (IR::Unop *u = s->source->asUnop()) {
                 if (IR::Temp *e = u->expr->asTemp()) {
@@ -491,7 +485,6 @@ void InstructionSelection::visitMove(IR::Move *s)
             if (IR::Temp *base = m->base->asTemp()) {
                 if (IR::Temp *t = s->source->asTemp()) {
                     generateFunctionCall(Void, __qmljs_set_property, ContextRegister, base, identifier(*m->name), t);
-                    checkExceptions();
                     return;
                 } else {
                     Q_UNREACHABLE();
@@ -500,7 +493,6 @@ void InstructionSelection::visitMove(IR::Move *s)
         } else if (IR::Subscript *ss = s->target->asSubscript()) {
             if (IR::Temp *t2 = s->source->asTemp()) {
                 generateFunctionCall(Void, __qmljs_set_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t2);
-                checkExceptions();
                 return;
             } else {
                 Q_UNIMPLEMENTED();
@@ -554,7 +546,6 @@ void InstructionSelection::visitMove(IR::Move *s)
                 }
                 if (op) {
                     generateFunctionCallImp(Void, opName, op, t, identifier(*n->id), ContextRegister);
-                    checkExceptions();
                 }
                 return;
             }
@@ -583,7 +574,6 @@ void InstructionSelection::visitMove(IR::Move *s)
                     IR::Temp* base = ss->base->asTemp();
                     IR::Temp* index = ss->index->asTemp();
                     generateFunctionCallImp(Void, opName, op, base, index, t, ContextRegister);
-                    checkExceptions();
                 }
                 return;
             }
@@ -612,7 +602,6 @@ void InstructionSelection::visitMove(IR::Move *s)
                     IR::Temp* base = m->base->asTemp();
                     String* member = identifier(*m->name);
                     generateFunctionCallImp(Void, opName, op, t, base, member, ContextRegister);
-                    checkExceptions();
                 }
                 return;
             }
@@ -730,14 +719,12 @@ void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* na
 
     int argc = prepareVariableArguments(args);
     generateFunctionCallImp(result, name, method, ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), TrustedImm32(argc));
-    checkExceptions();
 }
 
 void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args)
 {
     int argc = prepareVariableArguments(args);
     generateFunctionCallImp(result, name, method, ContextRegister, baseAddressForCallArguments(), TrustedImm32(argc));
-    checkExceptions();
 }
 
 template <typename Result, typename Source>
index 8565ad2..49f712f 100644 (file)
@@ -216,7 +216,6 @@ protected:
     void constructProperty(IR::New *ctor, IR::Temp *result);
     void callValue(IR::Call *call, IR::Temp *result);
     void constructValue(IR::New *call, IR::Temp *result);
-    void checkExceptions();
 
     virtual void visitExp(IR::Exp *);
     virtual void visitEnter(IR::Enter *);
@@ -523,6 +522,10 @@ private:
         FunctionPtr externalFunction;
         const char* functionName;
     };
+    struct CatchBlockToLink {
+        DataLabelPtr ptr;
+        IR::BasicBlock *catchBlock;
+    };
 
     void storeValue(VM::Value value, Address destination)
     {
@@ -544,6 +547,7 @@ private:
     uchar *_codePtr;
     QHash<IR::BasicBlock *, QVector<Jump> > _patches;
     QHash<IR::BasicBlock *, Label> _addrs;
+    QList<CatchBlockToLink> _catchHandlers;
     QList<CallToLink> _callsToLink;
 };