Fix interpreter exception handling.
authorErik Verbruggen <erik.verbruggen@digia.com>
Wed, 14 Nov 2012 09:15:19 +0000 (10:15 +0100)
committerErik Verbruggen <erik.verbruggen@digia.com>
Wed, 14 Nov 2012 10:45:03 +0000 (11:45 +0100)
The stack frame of the interpreting function is restored, but all the
datastructures live on the heap. So, save them out on handler creation,
and restore them afterwards.

Change-Id: I84b84007cc9944b56926bf0387c2798f7841cd2a
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
moth/qv4vme_moth.cpp
moth/qv4vme_moth_p.h
qmljs_objects.h
qmljs_runtime.cpp

index 66df75f..e9c43d4 100644 (file)
@@ -197,13 +197,30 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code
             TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof(args[0], context);
             break;
         case Instr::instr_callBuiltin::builtin_throw:
-            TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof(args[0], context);
+            TRACE(builtin_throw, "Throwing now...%s", "");
+            __qmljs_builtin_throw(args[0], context);
             break;
-        case Instr::instr_callBuiltin::builtin_create_exception_handler:
+        case Instr::instr_callBuiltin::builtin_create_exception_handler: {
+            TRACE(builtin_create_exception_handler, "%s", "");
             buf = __qmljs_create_exception_handler(context);
-            TEMP(instr.targetTempIndex) = VM::Value::fromInt32(setjmp(* static_cast<jmp_buf *>(buf)));
-            break;
+            // The targetTempIndex is the only value we need from the instr to
+            // continue execution when an exception is caught.
+            int targetTempIndex = instr.targetTempIndex;
+            int didThrow = setjmp(* static_cast<jmp_buf *>(buf));
+            // Two ways to come here: after a create, or after a throw.
+            if (didThrow)
+                // At this point, the interpreter state can be anything but
+                // valid, so first restore the state. This includes all relevant
+                // locals.
+                restoreState(context, stack, targetTempIndex, code);
+            else
+                // Save the state and any variables we need when catching an
+                // exception, so we can restore the state at that point.
+                saveState(context, stack, targetTempIndex, code);
+            TEMP(targetTempIndex) = VM::Value::fromInt32(didThrow);
+        } break;
         case Instr::instr_callBuiltin::builtin_delete_exception_handler:
+            TRACE(builtin_delete_exception_handler, "%s", "");
             __qmljs_delete_exception_handler(context);
             break;
         case Instr::instr_callBuiltin::builtin_get_exception:
@@ -328,3 +345,24 @@ void VME::exec(VM::Context *ctxt, const uchar *code)
     vme(ctxt, code);
 }
 
+void VME::restoreState(VM::Context *context, QVector<VM::Value> &stack, int &targetTempIndex, const uchar *&code)
+{
+    typedef VM::ExecutionEngine::ExceptionHandler EH;
+    EH::InterpreterState *state = context->engine->unwindStack.last()->interpreterState;
+    assert(state);
+    stack.resize(state->stack.size());
+    ::memcpy(stack.data(), state->stack.data(), sizeof(VM::Value) * state->stack.size());
+    targetTempIndex = state->targetTempIndex;
+    code = state->code;
+}
+
+void VME::saveState(VM::Context *context, const QVector<VM::Value> &stack, int targetTempIndex, const uchar *code)
+{
+    typedef VM::ExecutionEngine::ExceptionHandler EH;
+    EH::InterpreterState *state = new EH::InterpreterState;
+    context->engine->unwindStack.last()->interpreterState = state;
+    state->stack.resize(stack.size());
+    ::memcpy(state->stack.data(), stack.data(), sizeof(VM::Value) * stack.size());
+    state->targetTempIndex = targetTempIndex;
+    state->code = code;
+}
index db1507d..b1bc45b 100644 (file)
@@ -21,6 +21,10 @@ public:
 #ifdef MOTH_THREADED_INTERPRETER
     static void **instructionJumpTable();
 #endif
+
+private:
+    static void restoreState(VM::Context *context, QVector<VM::Value> &stack, int &targetTempIndex, const uchar *&code);
+    static void saveState(VM::Context *context, const QVector<VM::Value> &stack, int targetTempIndex, const uchar *code);
 };
 
 } // namespace Moth
index 857b12b..f40eab0 100644 (file)
@@ -582,13 +582,24 @@ struct ExecutionEngine
     String *id___proto__;
 
     struct ExceptionHandler {
+        struct InterpreterState {
+            QVector<VM::Value> stack;
+            int targetTempIndex;
+            const uchar *code;
+        };
+
         Context *context;
         jmp_buf stackFrame;
+        InterpreterState *interpreterState;
+
+        ExceptionHandler(): interpreterState(0) {}
+        ~ExceptionHandler() { delete interpreterState; }
     };
 
-    QVector<ExceptionHandler> unwindStack;
+    QVector<ExceptionHandler *> unwindStack;
 
     ExecutionEngine();
+    ~ExecutionEngine() { qDeleteAll(unwindStack); }
 
     Context *newContext();
 
index d70a851..668be71 100644 (file)
@@ -1084,23 +1084,23 @@ void __qmljs_throw(Value value, Context *context)
 {
     assert(!context->engine->unwindStack.isEmpty());
 
-    ExecutionEngine::ExceptionHandler handler = context->engine->unwindStack.last();
+    ExecutionEngine::ExceptionHandler *handler = context->engine->unwindStack.last();
 
     // clean up call contexts
-    while (context != handler.context) {
+    while (context != handler->context) {
         context->leaveCallContext();
         context = context->parent;
     }
 
-    handler.context->result = value;
+    handler->context->result = value;
 
-    longjmp(handler.stackFrame, 1);
+    longjmp(handler->stackFrame, 1);
 }
 
 void *__qmljs_create_exception_handler(Context *context)
 {
-    context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler());
-    ExecutionEngine::ExceptionHandler *handler = &context->engine->unwindStack.last();
+    ExecutionEngine::ExceptionHandler *handler = new ExecutionEngine::ExceptionHandler;
+    context->engine->unwindStack.append(handler);
     handler->context = context;
     return handler->stackFrame;
 }
@@ -1109,6 +1109,7 @@ void __qmljs_delete_exception_handler(Context *context)
 {
     assert(!context->engine->unwindStack.isEmpty());
 
+    delete context->engine->unwindStack.last();
     context->engine->unwindStack.pop_back();
 }