diff options
-rw-r--r-- | src/v4/moth/qv4isel_moth.cpp | 2 | ||||
-rw-r--r-- | src/v4/moth/qv4isel_moth_p.h | 2 | ||||
-rw-r--r-- | src/v4/moth/qv4vme_moth.cpp | 47 | ||||
-rw-r--r-- | src/v4/moth/qv4vme_moth_p.h | 11 | ||||
-rw-r--r-- | src/v4/qmljs_runtime.cpp | 2 | ||||
-rw-r--r-- | src/v4/qmljs_runtime.h | 3 | ||||
-rw-r--r-- | src/v4/qv4codegen.cpp | 5 | ||||
-rw-r--r-- | src/v4/qv4isel_llvm.cpp | 2 | ||||
-rw-r--r-- | src/v4/qv4isel_llvm_p.h | 2 | ||||
-rw-r--r-- | src/v4/qv4isel_masm.cpp | 65 | ||||
-rw-r--r-- | src/v4/qv4isel_masm_p.h | 9 | ||||
-rw-r--r-- | src/v4/qv4isel_p.cpp | 3 | ||||
-rw-r--r-- | src/v4/qv4isel_p.h | 2 | ||||
-rw-r--r-- | tools/v4/main.cpp | 40 |
14 files changed, 129 insertions, 66 deletions
diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index 125014a1..d8d70401 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -829,7 +829,7 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) addInstruction(call); } -void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *) +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) { Instruction::CallBuiltinCreateExceptionHandler call; call.result = getResultParam(result); diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h index af60ca4f..6eaee6dd 100644 --- a/src/v4/moth/qv4isel_moth_p.h +++ b/src/v4/moth/qv4isel_moth_p.h @@ -43,7 +43,7 @@ protected: virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); virtual void callBuiltinDeleteExceptionHandler(); virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index ffade960..aea4fce8 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -139,7 +139,8 @@ static inline VM::Value *getValueRef(QQmlJS::VM::ExecutionContext *context, # define VALUEPTR(param) getValueRef(context, stack, param, stackSize) #endif -VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code +VM::Value VME::run(QQmlJS::VM::ExecutionContext *context, const uchar *&code, + VM::Value *stack, unsigned stackSize #ifdef MOTH_THREADED_INTERPRETER , void ***storeJumpTable #endif @@ -161,8 +162,6 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co } #endif - VM::Value *stack = 0; - unsigned stackSize = 0; FunctionState state(context, &code); #ifdef MOTH_THREADED_INTERPRETER @@ -260,31 +259,35 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinThrow) MOTH_BEGIN_INSTR(CallBuiltinCreateExceptionHandler) - void *buf = __qmljs_create_exception_handler(context); - // The result is the only value we need from the instr to - // continue execution when an exception is caught. + __qmljs_create_exception_handler(context); VM::Value *result = getValueRef(context, stack, instr.result #if !defined(QT_NO_DEBUG) , stackSize #endif ); - VM::ExecutionContext *oldContext = context; - int didThrow = setjmp(* static_cast<jmp_buf *>(buf)); - context = oldContext; - // 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. - restoreState(context, result, 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, result, code); - *result = VM::Value::fromInt32(didThrow); + try { + *result = VM::Value::fromInt32(0); + const uchar *tryCode = code; + run(context, tryCode, stack, stackSize); + code = tryCode; + } catch (const VM::Exception &) { + try { + *result = VM::Value::fromInt32(1); + const uchar *catchCode = code; + run(context, catchCode, stack, stackSize); + code = catchCode; + } catch (const VM::Exception &) { + *result = VM::Value::fromInt32(1); + const uchar *catchCode = code; + run(context, catchCode, stack, stackSize); + code = catchCode; + } + } MOTH_END_INSTR(CallBuiltinCreateExceptionHandler) MOTH_BEGIN_INSTR(CallBuiltinDeleteExceptionHandler) __qmljs_delete_exception_handler(context); + return VM::Value(); MOTH_END_INSTR(CallBuiltinDeleteExceptionHandler) MOTH_BEGIN_INSTR(CallBuiltinGetException) @@ -473,8 +476,8 @@ void **VME::instructionJumpTable() { static void **jumpTable = 0; if (!jumpTable) { - VME dummy; - dummy(0, 0, &jumpTable); + const uchar *code = 0; + VME().run(0, code, 0, 0, &jumpTable); } return jumpTable; } @@ -483,7 +486,7 @@ void **VME::instructionJumpTable() VM::Value VME::exec(VM::ExecutionContext *ctxt, const uchar *code) { VME vme; - return vme(ctxt, code); + return vme.run(ctxt, code); } void VME::restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code) diff --git a/src/v4/moth/qv4vme_moth_p.h b/src/v4/moth/qv4vme_moth_p.h index 2fd877f7..0b7fc9b7 100644 --- a/src/v4/moth/qv4vme_moth_p.h +++ b/src/v4/moth/qv4vme_moth_p.h @@ -16,17 +16,18 @@ class VME public: static VM::Value exec(VM::ExecutionContext *, const uchar *); - VM::Value operator()(QQmlJS::VM::ExecutionContext *, const uchar *code #ifdef MOTH_THREADED_INTERPRETER - , void ***storeJumpTable = 0 + static void **instructionJumpTable(); #endif - ); +private: + VM::Value run(QQmlJS::VM::ExecutionContext *, const uchar *&code, + VM::Value *stack = 0, unsigned stackSize = 0 #ifdef MOTH_THREADED_INTERPRETER - static void **instructionJumpTable(); + , void ***storeJumpTable = 0 #endif + ); -private: static void restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code); static void saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code); }; diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 76361c0c..43940e68 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -964,7 +964,7 @@ void __qmljs_throw(ExecutionContext *context, const Value &value) context->engine->exception = value; - longjmp(handler.stackFrame, 1); + throw Exception(); } Q_V4_EXPORT void * __qmljs_create_exception_handler(ExecutionContext *context) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index e649debe..4da9015a 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -89,6 +89,9 @@ struct ArrayObject; struct ErrorObject; struct ExecutionEngine; +struct Exception { +}; + extern "C" { // context diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 7ad09ca3..ad390dfb 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -2454,10 +2454,7 @@ bool Codegen::visit(TryStatement *ast) } int hasException = _block->newTemp(); - int contextTemp = _block->newTemp(); - IR::ExprList *createExceptionArgs = _function->New<IR::ExprList>(); - createExceptionArgs->init(_block->TEMP(contextTemp)); - move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), createExceptionArgs)); + move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), 0)); // Pass the hidden "inCatch" and "hasException" TEMPs to the // builtin_delete_exception_handler, in order to have those TEMPs alive for diff --git a/src/v4/qv4isel_llvm.cpp b/src/v4/qv4isel_llvm.cpp index 57175320..e6010cfd 100644 --- a/src/v4/qv4isel_llvm.cpp +++ b/src/v4/qv4isel_llvm.cpp @@ -427,7 +427,7 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) Q_UNREACHABLE(); } -void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp) +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) { // TODO assert(!"TODO!"); diff --git a/src/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h index 43e3dfc0..fb28456b 100644 --- a/src/v4/qv4isel_llvm_p.h +++ b/src/v4/qv4isel_llvm_p.h @@ -88,7 +88,7 @@ public: // methods from InstructionSelection: virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); virtual void callBuiltinDeleteExceptionHandler(); virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 82b0634a..16592760 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -123,6 +123,14 @@ void Assembler::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) _patches[targetBlock].append(targetJump); } +void Assembler::addPatch(DataLabelPtr patch, Label target) +{ + DataLabelPatch p; + p.dataLabel = patch; + p.target = target; + _dataLabelPatches.append(p); +} + Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, IR::Temp *t) { int32_t offset = 0; @@ -420,6 +428,9 @@ void Assembler::link(VM::Function *vmFunc) functions[ctl.externalFunction.value()] = ctl.functionName; } + foreach (const DataLabelPatch &p, _dataLabelPatches) + linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); + static bool showCode = !qgetenv("SHOW_CODE").isNull(); if (showCode) { #if OS(LINUX) @@ -630,22 +641,64 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, Assembler::ContextRegister, Assembler::Reference(arg)); } -void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp) +static void *tryWrapper(ExecutionContext *context, void *localsPtr, void *(*exceptionEntryPointInCallingFunction)(ExecutionContext*, void*, int)) +{ + void *addressToContinueAt = 0; + try { + addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 0); + } catch (const Exception&) { + try { + addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 1); + } catch (const Exception&) { + addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 1); + } + } + return addressToContinueAt; +} + +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) { - Address contextAddr = _as->loadTempAddress(Assembler::ScratchRegister, contextTemp); - _as->storePtr(Assembler::ContextRegister, contextAddr); - generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_create_exception_handler, Assembler::ContextRegister); - generateFunctionCall(Assembler::ReturnValueRegister, setjmp, Assembler::ReturnValueRegister); - _as->loadPtr(contextAddr, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_create_exception_handler, Assembler::ContextRegister); + + // Call tryWrapper, which is going to re-enter the same function again below. + // When tryWrapper returns, it returns the with address of where to continue. + Assembler::DataLabelPtr movePatch = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ScratchRegister); + generateFunctionCall(Assembler::ReturnValueRegister, tryWrapper, Assembler::ContextRegister, Assembler::LocalsRegister, Assembler::ScratchRegister); + _as->jump(Assembler::ReturnValueRegister); + + // tryWrapper calls us at this place with arg3 == 0 if we're supposed to execute the try block + // and arg == 1 if we caught an exception. The generated IR takes care of returning from this + // call when deleteExceptionHandler is called. + _as->addPatch(movePatch, _as->label()); + _as->enterStandardStackFrame(/*locals*/0); +#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 + Address addr = _as->loadTempAddress(Assembler::ScratchRegister, result); +#ifdef ARGUMENTS_IN_REGISTERS + _as->store32(Assembler::registerForArgument(2), addr); +#else + _as->load32(addressForArgument(2), Assembler::ReturnValueRegister); _as->store32(Assembler::ReturnValueRegister, addr); +#endif addr.offset += 4; _as->store32(Assembler::TrustedImm32(Value::Boolean_Type), addr); } 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(); + _as->addPatch(continuation, _as->label()); } void InstructionSelection::callBuiltinGetException(IR::Temp *result) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 4a12351b..d850246a 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -216,6 +216,7 @@ public: void registerBlock(IR::BasicBlock*); void jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target); void addPatch(IR::BasicBlock* targetBlock, Jump targetJump); + void addPatch(DataLabelPtr patch, Label target); Pointer loadTempAddress(RegisterID reg, IR::Temp *t); @@ -723,6 +724,12 @@ private: QHash<IR::BasicBlock *, Label> _addrs; QHash<IR::BasicBlock *, QVector<Jump> > _patches; QList<CallToLink> _callsToLink; + + struct DataLabelPatch { + DataLabelPtr dataLabel; + Label target; + }; + QList<DataLabelPatch> _dataLabelPatches; }; class Q_V4_EXPORT InstructionSelection: @@ -754,7 +761,7 @@ protected: virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); virtual void callBuiltinDeleteExceptionHandler(); virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index b4360153..03fa0745 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -311,8 +311,7 @@ void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) } return; case IR::Name::builtin_create_exception_handler: { - IR::Temp *arg = call->args->expr->asTemp(); - callBuiltinCreateExceptionHandler(result, arg); + callBuiltinCreateExceptionHandler(result); return; } diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index 9c15829d..40cf86ed 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -103,7 +103,7 @@ public: // to implement by subclasses: virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result) = 0; virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) = 0; virtual void callBuiltinThrow(IR::Temp *arg) = 0; - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp) = 0; + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result) = 0; virtual void callBuiltinDeleteExceptionHandler() = 0; virtual void callBuiltinGetException(IR::Temp *result) = 0; virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) = 0; diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp index f6997eb7..d73fa480 100644 --- a/tools/v4/main.cpp +++ b/tools/v4/main.cpp @@ -369,30 +369,30 @@ int main(int argc, char *argv[]) const QString code = QString::fromUtf8(file.readAll()); file.close(); - void * buf = __qmljs_create_exception_handler(ctx); - if (setjmp(*(jmp_buf *)buf)) { + __qmljs_create_exception_handler(ctx); + try { + QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode, + /*strictMode =*/ false, /*inheritContext =*/ false); + if (!f) + continue; + vm.globalCode = f; + + ctx->strictMode = f->isStrict; + ctx->lookups = f->lookups; + if (debugger) + debugger->aboutToCall(0, ctx); + QQmlJS::VM::Value result = f->code(ctx, f->codeData); + if (debugger) + debugger->justLeft(ctx); + if (!result.isUndefined()) { + if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) + std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; + } + } catch (const QQmlJS::VM::Exception&) { showException(ctx); return EXIT_FAILURE; } - QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode, - /*strictMode =*/ false, /*inheritContext =*/ false); - if (!f) - continue; - vm.globalCode = f; - - ctx->strictMode = f->isStrict; - ctx->lookups = f->lookups; - if (debugger) - debugger->aboutToCall(0, ctx); - QQmlJS::VM::Value result = f->code(ctx, f->codeData); - if (debugger) - debugger->justLeft(ctx); - if (!result.isUndefined()) { - if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) - std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; - } - } else { std::cerr << "Error: cannot open file " << fn.toUtf8().constData() << std::endl; return EXIT_FAILURE; |