Cleanup: Get rid of manual unwind stack
authorSimon Hausmann <simon.hausmann@digia.com>
Mon, 4 Mar 2013 11:12:21 +0000 (12:12 +0100)
committerLars Knoll <lars.knoll@digia.com>
Mon, 4 Mar 2013 12:15:10 +0000 (13:15 +0100)
Instead of doing the ExecutionContext unwinding at the time of throw, do it
at the time of catch, conveniently through an accept() method of the Exception
object. That allows us to get rid of the separate handler registration stack.

The only tricky part are that some execution contexts are allocated on the stack.
When exceptions are thrown through that, we have to catch, unwinding up until that
point (or rather the parent of the stack allocated context that's going to be
deleted) and then simply rethrow.

This patch also gets rid of the __builtin_delete_exception_handler. The next patch
will rename the remainder to what it really does now.

Change-Id: I00bb113b3a2fe24f7054c03fdfb8fed5cc1258b1
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/v4/llvm_runtime.cpp
src/v4/moth/qv4vme_moth.cpp
src/v4/moth/qv4vme_moth_p.h
src/v4/qmljs_engine.h
src/v4/qmljs_runtime.cpp
src/v4/qmljs_runtime.h
src/v4/qv4functionobject.cpp
src/v4/qv4globalobject.cpp
src/v4/qv4isel_masm.cpp
tools/v4/main.cpp

index 0322ecc..c3aede7 100644 (file)
@@ -476,13 +476,14 @@ void __qmljs_llvm_throw(ExecutionContext *context, Value *value)
 
 void __qmljs_llvm_create_exception_handler(ExecutionContext *context, Value *result)
 {
-    void *buf = __qmljs_create_exception_handler(context);
-    *result = Value::fromInt32(setjmp(* static_cast<jmp_buf *>(buf)));
+    // ### FIXME.
+    __qmljs_create_exception_handler(context);
+    *result = Value::undefinedValue();
 }
 
 void __qmljs_llvm_delete_exception_handler(ExecutionContext *context)
 {
-    __qmljs_delete_exception_handler(context);
+    // ### FIXME.
 }
 
 void __qmljs_llvm_get_exception(ExecutionContext *context, Value *result)
index caa5810..1238b36 100644 (file)
@@ -270,13 +270,15 @@ VM::Value VME::run(QQmlJS::VM::ExecutionContext *context, const uchar *&code,
             const uchar *tryCode = code;
             run(context, tryCode, stack, stackSize);
             code = tryCode;
-        } catch (const VM::Exception &) {
+        } catch (VM::Exception &ex) {
+            ex.accept(context);
             try {
                 *result = VM::Value::fromInt32(1);
                 const uchar *catchCode = code;
                 run(context, catchCode, stack, stackSize);
                 code = catchCode;
-            } catch (const VM::Exception &) {
+            } catch (VM::Exception &ex) {
+                ex.accept(context);
                 *result = VM::Value::fromInt32(1);
                 const uchar *catchCode = code;
                 run(context, catchCode, stack, stackSize);
@@ -286,7 +288,6 @@ VM::Value VME::run(QQmlJS::VM::ExecutionContext *context, const uchar *&code,
     MOTH_END_INSTR(CallBuiltinCreateExceptionHandler)
 
     MOTH_BEGIN_INSTR(CallBuiltinDeleteExceptionHandler)
-        __qmljs_delete_exception_handler(context);
         return VM::Value();
     MOTH_END_INSTR(CallBuiltinDeleteExceptionHandler)
 
@@ -488,17 +489,3 @@ VM::Value VME::exec(VM::ExecutionContext *ctxt, const uchar *code)
     VME vme;
     return vme.run(ctxt, code);
 }
-
-void VME::restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code)
-{
-    VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last();
-    target = handler.target;
-    code = handler.code;
-}
-
-void VME::saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code)
-{
-    VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last();
-    handler.target = target;
-    handler.code = code;
-}
index 0b7fc9b..7c09fc0 100644 (file)
@@ -27,9 +27,6 @@ private:
             , void ***storeJumpTable = 0
 #endif
             );
-
-    static void restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code);
-    static void saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code);
 };
 
 } // namespace Moth
index cc978c6..2289e73 100644 (file)
@@ -179,14 +179,6 @@ struct Q_V4_EXPORT ExecutionEngine
     String *id_set;
     String *id_eval;
 
-    struct ExceptionHandler {
-        ExecutionContext *context;
-        const uchar *code; // Interpreter state
-        Value *target; // Interpreter state
-        jmp_buf stackFrame;
-    };
-
-    QVector<ExceptionHandler> unwindStack;
     Value exception;
 
     QVector<Function *> functions;
index 4047a5c..1f73753 100644 (file)
@@ -112,6 +112,38 @@ QString numberToString(double num, int radix = 10)
     return str;
 }
 
+Exception::Exception(ExecutionContext *throwingContext)
+{
+    this->throwingContext = throwingContext;
+    accepted = false;
+}
+
+Exception::~Exception()
+{
+    assert(accepted);
+}
+
+void Exception::accept(ExecutionContext *catchingContext)
+{
+    assert(!accepted);
+    accepted = true;
+    partiallyUnwindContext(catchingContext);
+}
+
+void Exception::partiallyUnwindContext(ExecutionContext *catchingContext)
+{
+    if (!throwingContext)
+        return;
+    ExecutionContext *context = throwingContext;
+    while (context != catchingContext) {
+        ExecutionContext *parent = context->parent;
+        if (!context->withObject)
+            context->leaveCallContext();
+        context = parent;
+    }
+    throwingContext = context;
+}
+
 extern "C" {
 
 void __qmljs_init_closure(ExecutionContext *ctx, Value *result, VM::Function *clos)
@@ -955,40 +987,17 @@ void __qmljs_construct_property(ExecutionContext *context, Value *result, const
 
 void __qmljs_throw(ExecutionContext *context, const Value &value)
 {
-    assert(!context->engine->unwindStack.isEmpty());
-
     if (context->engine->debugger)
         context->engine->debugger->aboutToThrow(value);
 
-    ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last();
-
-    // clean up call contexts
-    while (context != handler.context) {
-        ExecutionContext *parent = context->parent;
-        if (!context->withObject)
-            context->leaveCallContext();
-        context = parent;
-    }
-
     context->engine->exception = value;
 
-    throw Exception();
+    throw Exception(context);
 }
 
-Q_V4_EXPORT void __qmljs_create_exception_handler(ExecutionContext *context)
+Q_V4_EXPORT void __qmljs_create_exception_handler(ExecutionContext *context)
 {
     context->engine->exception = Value::undefinedValue();
-    context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler());
-    ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last();
-    handler.context = context;
-    return handler.stackFrame;
-}
-
-void __qmljs_delete_exception_handler(ExecutionContext *context)
-{
-    assert(!context->engine->unwindStack.isEmpty());
-
-    context->engine->unwindStack.pop_back();
 }
 
 void __qmljs_get_exception(ExecutionContext *context, Value *result)
index 4da9015..f504e6d 100644 (file)
@@ -89,7 +89,16 @@ struct ArrayObject;
 struct ErrorObject;
 struct ExecutionEngine;
 
-struct Exception {
+struct Q_V4_EXPORT Exception {
+    explicit Exception(ExecutionContext *throwingContext);
+    ~Exception();
+
+    void accept(ExecutionContext *catchingContext);
+
+    void partiallyUnwindContext(ExecutionContext *catchingContext);
+private:
+    ExecutionContext *throwingContext;
+    bool accepted;
 };
 
 extern "C" {
@@ -196,8 +205,7 @@ void __qmljs_delete_name(ExecutionContext *ctx, Value *result, String *name);
 
 void Q_NORETURN __qmljs_throw(ExecutionContext*, const Value &value);
 // actually returns a jmp_buf *
-Q_V4_EXPORT void *__qmljs_create_exception_handler(ExecutionContext *context);
-void __qmljs_delete_exception_handler(ExecutionContext *context);
+Q_V4_EXPORT void __qmljs_create_exception_handler(ExecutionContext *context);
 void __qmljs_get_exception(ExecutionContext *context, Value *result);
 
 // binary operators
index 437ea70..7990514 100644 (file)
@@ -373,7 +373,13 @@ Value ScriptFunction::construct(Managed *that, ExecutionContext *context, Value
     ctx->argumentCount = argc;
 
     ctx->initCallContext(context);
-    Value result = f->function->code(ctx, f->function->codeData);
+    Value result = Value::undefinedValue();
+    try {
+        result = f->function->code(ctx, f->function->codeData);
+    } catch (Exception &ex) {
+        ex.partiallyUnwindContext(ctx->parent);
+        throw;
+    }
     ctx->leaveCallContext();
 
     if (result.isObject())
@@ -402,7 +408,13 @@ Value ScriptFunction::call(Managed *that, ExecutionContext *context, const Value
     ctx->argumentCount = argc;
 
     ctx->initCallContext(context);
-    Value result = f->function->code(ctx, f->function->codeData);
+    Value result = Value::undefinedValue();
+    try {
+        result = f->function->code(ctx, f->function->codeData);
+    } catch (Exception &ex) {
+        ex.partiallyUnwindContext(ctx->parent);
+        throw;
+    }
     ctx->leaveCallContext();
     return result;
 }
@@ -446,7 +458,14 @@ Value BuiltinFunctionOld::call(Managed *that, ExecutionContext *context, const V
     ctx->argumentCount = argc;
 
     ctx->initCallContext(context);
-    Value result = f->code(ctx);
+    Value result = Value::undefinedValue();
+    try {
+        result = f->code(ctx);
+    } catch (Exception &ex) {
+        ex.partiallyUnwindContext(ctx->parent);
+        throw;
+    }
+
     ctx->leaveCallContext();
     return result;
 }
index 2c921b2..2454126 100644 (file)
@@ -393,7 +393,14 @@ Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Va
     bool cstrict = ctx->strictMode;
     ctx->strictMode = strict;
 
-    Value result = f->code(ctx, f->codeData);
+    Value result = Value::undefinedValue();
+    try {
+        result = f->code(ctx, f->codeData);
+    } catch (Exception &ex) {
+        if (strict)
+            ex.partiallyUnwindContext(ctx->parent);
+        throw;
+    }
 
     ctx->strictMode = cstrict;
 
index 1659276..26307e8 100644 (file)
@@ -646,10 +646,12 @@ static void *tryWrapper(ExecutionContext *context, void *localsPtr, void *(*exce
     void *addressToContinueAt = 0;
     try {
         addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 0);
-    } catch (const Exception&) {
+    } catch (Exception& ex) {
+        ex.accept(context);
         try {
             addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 1);
-        } catch (const Exception&) {
+        } catch (Exception& ex) {
+            ex.accept(context);
             addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 1);
         }
     }
@@ -694,7 +696,6 @@ void InstructionSelection::callBuiltinDeleteExceptionHandler()
 {
     // 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.
-    generateFunctionCall(Assembler::Void, __qmljs_delete_exception_handler, Assembler::ContextRegister);
     Assembler::DataLabelPtr continuation = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ReturnValueRegister);
     _as->leaveStandardStackFrame(/*locals*/0);
     _as->ret();
index d73fa48..89a2597 100644 (file)
@@ -388,7 +388,8 @@ int main(int argc, char *argv[])
                         if (! qgetenv("SHOW_EXIT_VALUE").isEmpty())
                             std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl;
                     }
-                } catch (const QQmlJS::VM::Exception&) {
+                } catch (QQmlJS::VM::Exception& ex) {
+                    ex.accept(ctx);
                     showException(ctx);
                     return EXIT_FAILURE;
                 }