From 2cf8be29bc31f557af47c143048b5bb70d6000cc Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 4 Mar 2013 12:12:21 +0100 Subject: [PATCH] Cleanup: Get rid of manual unwind stack 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 --- src/v4/llvm_runtime.cpp | 7 +++--- src/v4/moth/qv4vme_moth.cpp | 21 +++------------- src/v4/moth/qv4vme_moth_p.h | 3 --- src/v4/qmljs_engine.h | 8 ------ src/v4/qmljs_runtime.cpp | 59 +++++++++++++++++++++++++------------------- src/v4/qmljs_runtime.h | 14 ++++++++--- src/v4/qv4functionobject.cpp | 25 ++++++++++++++++--- src/v4/qv4globalobject.cpp | 9 ++++++- src/v4/qv4isel_masm.cpp | 7 +++--- tools/v4/main.cpp | 3 ++- 10 files changed, 89 insertions(+), 67 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 0322ecc..c3aede7 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -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(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) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index caa5810..1238b36 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -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; -} diff --git a/src/v4/moth/qv4vme_moth_p.h b/src/v4/moth/qv4vme_moth_p.h index 0b7fc9b7..7c09fc0 100644 --- a/src/v4/moth/qv4vme_moth_p.h +++ b/src/v4/moth/qv4vme_moth_p.h @@ -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 diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h index cc978c6..2289e73 100644 --- a/src/v4/qmljs_engine.h +++ b/src/v4/qmljs_engine.h @@ -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 unwindStack; Value exception; QVector functions; diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 4047a5c..1f73753 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -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) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 4da9015..f504e6d 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -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 diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 437ea70..7990514 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -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; } diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp index 2c921b2..2454126 100644 --- a/src/v4/qv4globalobject.cpp +++ b/src/v4/qv4globalobject.cpp @@ -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; diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 1659276..26307e8 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -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(); diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp index d73fa48..89a2597 100644 --- a/tools/v4/main.cpp +++ b/tools/v4/main.cpp @@ -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; } -- 2.7.4