aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2013-10-16 14:03:48 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-10-16 16:03:57 +0200
commit12c3579136b6925e75cca4f3a9b8bae2e4665db7 (patch)
tree17f82a28141856af0954ffe4eff20ebc679a3265
parentc1c526aafb2fc70ac6155eb775b3784f1e2e6504 (diff)
Speed up exception propagation
Avoid catch (...) with re-throw as it turns that this is very slow because it throws a new exception and the unwinder starts from scratch. Instead use stack allocated objects and cleaning destructors to restore state before continuing with the propagation of exceptions. Change-Id: I6d95026bcd60b58cb6258a9dae28623a46739532 Reviewed-by: Lars Knoll <lars.knoll@digia.com>
-rw-r--r--src/imports/localstorage/plugin.cpp55
-rw-r--r--src/qml/jsruntime/qv4context.cpp5
-rw-r--r--src/qml/jsruntime/qv4context_p.h1
-rw-r--r--src/qml/jsruntime/qv4engine.cpp17
-rw-r--r--src/qml/jsruntime/qv4engine_cxxabi.cpp16
-rw-r--r--src/qml/jsruntime/qv4engine_p.h19
-rw-r--r--src/qml/jsruntime/qv4function_p.h25
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp72
-rw-r--r--src/qml/jsruntime/qv4globalobject.cpp46
-rw-r--r--src/qml/jsruntime/qv4script.cpp43
10 files changed, 136 insertions, 163 deletions
diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp
index 84bcb756e8..9a0528a28f 100644
--- a/src/imports/localstorage/plugin.cpp
+++ b/src/imports/localstorage/plugin.cpp
@@ -322,6 +322,35 @@ static ReturnedValue qmlsqldatabase_executeSql(SimpleCallContext *ctx)
return result.asReturnedValue();
}
+struct TransactionRollback {
+ QSqlDatabase *db;
+ bool *inTransactionFlag;
+
+ TransactionRollback(QSqlDatabase *database, bool *transactionFlag)
+ : db(database)
+ , inTransactionFlag(transactionFlag)
+ {
+ if (inTransactionFlag)
+ *inTransactionFlag = true;
+ }
+
+ ~TransactionRollback()
+ {
+ if (inTransactionFlag)
+ *inTransactionFlag = false;
+ if (db)
+ db->rollback();
+ }
+
+ void clear() {
+ db = 0;
+ if (inTransactionFlag)
+ *inTransactionFlag = false;
+ inTransactionFlag = 0;
+ }
+};
+
+
static ReturnedValue qmlsqldatabase_changeVersion(SimpleCallContext *ctx)
{
if (ctx->callData->argc < 2)
@@ -349,7 +378,6 @@ static ReturnedValue qmlsqldatabase_changeVersion(SimpleCallContext *ctx)
w->type = QQmlSqlDatabaseWrapper::Query;
w->database = db;
w->version = r->version;
- w->inTransaction = true;
bool ok = true;
if (!!callback) {
@@ -359,12 +387,10 @@ static ReturnedValue qmlsqldatabase_changeVersion(SimpleCallContext *ctx)
ScopedCallData callData(scope, 1);
callData->thisObject = engine->global();
callData->args[0] = w;
- try {
- callback->call(callData);
- } catch (...) {
- db.rollback();
- ctx->rethrowException();
- }
+
+ TransactionRollback rollbackOnException(&db, &w->inTransaction);
+ callback->call(callData);
+ rollbackOnException.clear();
if (!db.commit()) {
db.rollback();
V4THROW_SQL(SQLEXCEPTION_UNKNOWN_ERR,QQmlEngine::tr("SQL transaction failed"));
@@ -373,8 +399,6 @@ static ReturnedValue qmlsqldatabase_changeVersion(SimpleCallContext *ctx)
}
}
- w->inTransaction = false;
-
if (ok) {
w->version = to_version;
#ifndef QT_NO_SETTINGS
@@ -408,22 +432,15 @@ static ReturnedValue qmlsqldatabase_transaction_shared(SimpleCallContext *ctx, b
w->database = db;
w->version = r->version;
w->readonly = readOnly;
- w->inTransaction = true;
db.transaction();
if (callback) {
ScopedCallData callData(scope, 1);
callData->thisObject = engine->global();
callData->args[0] = w;
- try {
- callback->call(callData);
- } catch (...) {
- w->inTransaction = false;
- db.rollback();
- ctx->rethrowException();
- }
-
- w->inTransaction = false;
+ TransactionRollback rollbackOnException(&db, &w->inTransaction);
+ callback->call(callData);
+ rollbackOnException.clear();
if (!db.commit())
db.rollback();
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index be6e1649cf..f3d2ab66ed 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -649,11 +649,6 @@ ReturnedValue ExecutionContext::catchException(StackTrace *trace)
return engine->catchException(this, trace);
}
-void ExecutionContext::rethrowException()
-{
- engine->rethrowException(this);
-}
-
void ExecutionContext::throwReferenceError(const ValueRef value)
{
Scope scope(this);
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index d44c7d25f9..e5b0c431ca 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -147,7 +147,6 @@ struct Q_QML_EXPORT ExecutionContext
// Can only be called from within catch(...), rethrows if no JS exception.
ReturnedValue catchException(StackTrace *trace = 0);
- void Q_NORETURN rethrowException();
void mark();
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 23faa43ac7..d312db272c 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -826,19 +826,9 @@ void ExecutionEngine::throwException(const ValueRef value)
throwInternal();
}
-void ExecutionEngine::rethrowException(ExecutionContext *intermediateCatchingContext)
-{
- if (hasException) {
- while (current != intermediateCatchingContext)
- popContext();
- }
- rethrowInternal();
-}
-
ReturnedValue ExecutionEngine::catchException(ExecutionContext *catchingContext, StackTrace *trace)
{
- if (!hasException)
- rethrowInternal();
+ Q_ASSERT(hasException);
while (current != catchingContext)
popContext();
if (trace)
@@ -858,11 +848,6 @@ void ExecutionEngine::throwInternal()
{
throw DummyException();
}
-
-void ExecutionEngine::rethrowInternal()
-{
- throw;
-}
#endif
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4engine_cxxabi.cpp b/src/qml/jsruntime/qv4engine_cxxabi.cpp
index dfe1db8fe3..f29af5a284 100644
--- a/src/qml/jsruntime/qv4engine_cxxabi.cpp
+++ b/src/qml/jsruntime/qv4engine_cxxabi.cpp
@@ -118,22 +118,6 @@ void ExecutionEngine::throwInternal()
std::terminate();
}
-void ExecutionEngine::rethrowInternal()
-{
- cxa_eh_globals *globals = __cxa_get_globals();
- cxa_exception *exception = globals->caughtExceptions;
-
- // Make sure we only re-throw our foreign exceptions. For general re-throw
- // we'd need different code.
-#ifndef __ARM_EABI_UNWINDER__
- Q_ASSERT(exception->unwindHeader.exception_class == 0x514d4c4a53563400); // QMLJSV40
-#endif
-
- globals->caughtExceptions = 0;
- _Unwind_RaiseException(&exception->unwindHeader);
- std::terminate();
-}
-
QT_END_NAMESPACE
/*
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 5da556452e..e49b6a4ebf 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -326,11 +326,9 @@ struct Q_QML_EXPORT ExecutionEngine
StackTrace exceptionStackTrace;
void Q_NORETURN throwException(const ValueRef value);
- void Q_NORETURN rethrowException(ExecutionContext *intermediateCatchingContext);
ReturnedValue catchException(ExecutionContext *catchingContext, StackTrace *trace);
void Q_NORETURN throwInternal();
- void Q_NORETURN rethrowInternal();
// ----
@@ -351,6 +349,23 @@ inline ExecutionContext *ExecutionEngine::popContext()
return current;
}
+struct ExecutionContextSaver
+{
+ ExecutionEngine *engine;
+ ExecutionContext *savedContext;
+
+ ExecutionContextSaver(ExecutionContext *context)
+ : engine(context->engine)
+ , savedContext(context)
+ {
+ }
+ ~ExecutionContextSaver()
+ {
+ while (engine->current != savedContext)
+ engine->popContext();
+ }
+};
+
} // namespace QV4
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index 34ab5df73a..35c98897a0 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -86,13 +86,24 @@ struct Function {
const CompiledData::Function *compiledFunction;
CompiledData::CompilationUnit *compilationUnit;
inline ReturnedValue code(ExecutionContext *ctx, const uchar *data) {
- SafeValue *stack = ctx->engine->jsStackTop;
- try {
- return codePtr(ctx, data);
- } catch (...) {
- ctx->engine->jsStackTop = stack;
- ctx->rethrowException();
- }
+
+ struct StackSaver {
+ ExecutionEngine *engine;
+ SafeValue *stack;
+
+ StackSaver(ExecutionEngine *engine)
+ : engine(engine)
+ , stack(engine->jsStackTop)
+ {}
+
+ ~StackSaver()
+ {
+ engine->jsStackTop = stack;
+ }
+ };
+
+ StackSaver(ctx->engine);
+ return codePtr(ctx, data);
}
ReturnedValue (*codePtr)(ExecutionContext *, const uchar *);
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 8faf2a74b6..55baef06db 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -443,16 +443,8 @@ ReturnedValue ScriptFunction::construct(Managed *that, CallData *callData)
callData->thisObject = obj.asReturnedValue();
ExecutionContext *ctx = context->newCallContext(f.getPointer(), callData);
- ScopedValue result(scope);
- SAVE_JS_STACK(f->scope);
- try {
- result = f->function->code(ctx, f->function->codeData);
- } catch (...) {
- context->rethrowException();
- }
- CHECK_JS_STACK(f->scope);
- ctx->engine->popContext();
-
+ ExecutionContextSaver ctxSaver(context);
+ ScopedValue result(scope, f->function->code(ctx, f->function->codeData));
if (result->isObject())
return result.asReturnedValue();
return obj.asReturnedValue();
@@ -474,16 +466,8 @@ ReturnedValue ScriptFunction::call(Managed *that, CallData *callData)
}
}
- ScopedValue result(scope);
- SAVE_JS_STACK(f->scope);
- try {
- result = f->function->code(ctx, f->function->codeData);
- } catch (...) {
- context->rethrowException();
- }
- CHECK_JS_STACK(f->scope);
- ctx->engine->popContext();
- return result.asReturnedValue();
+ ExecutionContextSaver ctxSaver(context);
+ return f->function->code(ctx, f->function->codeData);
}
DEFINE_MANAGED_VTABLE(SimpleScriptFunction);
@@ -540,16 +524,12 @@ ReturnedValue SimpleScriptFunction::construct(Managed *that, CallData *callData)
callData->thisObject = obj;
ExecutionContext *ctx = context->newCallContext(stackSpace, scope.alloc(f->varCount), f.getPointer(), callData);
- try {
- Scoped<Object> result(scope, f->function->code(ctx, f->function->codeData));
- ctx->engine->popContext();
+ ExecutionContextSaver ctxSaver(context);
+ Scoped<Object> result(scope, f->function->code(ctx, f->function->codeData));
- if (!result)
- return obj.asReturnedValue();
- return result.asReturnedValue();
- } catch (...) {
- context->rethrowException();
- }
+ if (!result)
+ return obj.asReturnedValue();
+ return result.asReturnedValue();
}
ReturnedValue SimpleScriptFunction::call(Managed *that, CallData *callData)
@@ -570,16 +550,8 @@ ReturnedValue SimpleScriptFunction::call(Managed *that, CallData *callData)
}
}
- ScopedValue result(scope);
- SAVE_JS_STACK(f->scope);
- try {
- result = f->function->code(ctx, f->function->codeData);
- } catch (...) {
- context->rethrowException();
- }
- CHECK_JS_STACK(f->scope);
- ctx->engine->popContext();
- return result.asReturnedValue();
+ ExecutionContextSaver ctxSaver(context);
+ return f->function->code(ctx, f->function->codeData);
}
@@ -613,15 +585,8 @@ ReturnedValue BuiltinFunction::call(Managed *that, CallData *callData)
ctx.callData = callData;
v4->pushContext(&ctx);
- ScopedValue result(scope);
- try {
- result = f->code(&ctx);
- } catch (...) {
- context->rethrowException();
- }
-
- context->engine->popContext();
- return result.asReturnedValue();
+ ExecutionContextSaver ctxSaver(context);
+ return f->code(&ctx);
}
ReturnedValue IndexedBuiltinFunction::call(Managed *that, CallData *callData)
@@ -637,15 +602,8 @@ ReturnedValue IndexedBuiltinFunction::call(Managed *that, CallData *callData)
ctx.callData = callData;
v4->pushContext(&ctx);
- ScopedValue result(scope);
- try {
- result = f->code(&ctx, f->index);
- } catch (...) {
- context->rethrowException();
- }
-
- context->engine->popContext();
- return result.asReturnedValue();
+ ExecutionContextSaver ctxSaver(context);
+ return f->code(&ctx, f->index);
}
DEFINE_MANAGED_VTABLE(IndexedBuiltinFunction);
diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp
index 3b1e0b1bae..4dcdb08415 100644
--- a/src/qml/jsruntime/qv4globalobject.cpp
+++ b/src/qml/jsruntime/qv4globalobject.cpp
@@ -355,6 +355,27 @@ EvalFunction::EvalFunction(ExecutionContext *scope)
ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall)
{
+ struct ContextStateSaver {
+ ExecutionContext *savedContext;
+ bool strictMode;
+ ExecutionContext::EvalCode *evalCode;
+ CompiledData::CompilationUnit *compilationUnit;
+
+ ContextStateSaver(ExecutionContext *context)
+ : savedContext(context)
+ , strictMode(context->strictMode)
+ , evalCode(context->currentEvalCode)
+ , compilationUnit(context->compilationUnit)
+ {}
+
+ ~ContextStateSaver()
+ {
+ savedContext->strictMode = strictMode;
+ savedContext->currentEvalCode = evalCode;
+ savedContext->compilationUnit = compilationUnit;
+ }
+ };
+
if (callData->argc < 1)
return Encode::undefined();
@@ -395,36 +416,19 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall)
return e->call(callData);
}
+ ExecutionContextSaver ctxSaver(parentContext);
+ ContextStateSaver stateSaver(ctx);
+
ExecutionContext::EvalCode evalCode;
evalCode.function = function;
evalCode.next = ctx->currentEvalCode;
ctx->currentEvalCode = &evalCode;
// set the correct strict mode flag on the context
- bool cstrict = ctx->strictMode;
ctx->strictMode = strictMode;
-
- CompiledData::CompilationUnit * const oldCompilationUnit = ctx->compilationUnit;
ctx->compilationUnit = function->compilationUnit;
- ScopedValue result(scope);
- try {
- result = function->code(ctx, function->codeData);
- } catch (...) {
- ctx->strictMode = cstrict;
- ctx->currentEvalCode = evalCode.next;
- ctx->compilationUnit = oldCompilationUnit;
- ctx->rethrowException();
- }
-
- ctx->strictMode = cstrict;
- ctx->currentEvalCode = evalCode.next;
- ctx->compilationUnit = oldCompilationUnit;
-
- while (engine->current != parentContext)
- engine->popContext();
-
- return result.asReturnedValue();
+ return function->code(ctx, function->codeData);
}
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index 885ea8ea76..d30132140b 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -227,6 +227,27 @@ void Script::parse()
ReturnedValue Script::run()
{
+ struct ContextStateSaver {
+ ExecutionContext *savedContext;
+ bool strictMode;
+ Lookup *lookups;
+ CompiledData::CompilationUnit *compilationUnit;
+
+ ContextStateSaver(ExecutionContext *context)
+ : savedContext(context)
+ , strictMode(context->strictMode)
+ , lookups(context->lookups)
+ , compilationUnit(context->compilationUnit)
+ {}
+
+ ~ContextStateSaver()
+ {
+ savedContext->strictMode = strictMode;
+ savedContext->lookups = lookups;
+ savedContext->compilationUnit = compilationUnit;
+ }
+ };
+
if (!parsed)
parse();
if (!vmFunction)
@@ -238,29 +259,13 @@ ReturnedValue Script::run()
if (qml.isUndefined()) {
TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction);
- bool strict = scope->strictMode;
- Lookup *oldLookups = scope->lookups;
- CompiledData::CompilationUnit * const oldCompilationUnit = scope->compilationUnit;
-
+ ExecutionContextSaver ctxSaver(scope);
+ ContextStateSaver stateSaver(scope);
scope->strictMode = vmFunction->isStrict();
scope->lookups = vmFunction->compilationUnit->runtimeLookups;
scope->compilationUnit = vmFunction->compilationUnit;
- QV4::ScopedValue result(valueScope);
- try {
- result = vmFunction->code(scope, vmFunction->codeData);
- } catch (...) {
- scope->strictMode = strict;
- scope->lookups = oldLookups;
- scope->compilationUnit = oldCompilationUnit;
- scope->rethrowException();
- }
-
- scope->lookups = oldLookups;
- scope->compilationUnit = oldCompilationUnit;
-
- return result.asReturnedValue();
-
+ return vmFunction->code(scope, vmFunction->codeData);
} else {
ScopedObject qmlObj(valueScope, qml.value());
FunctionObject *f = new (engine->memoryManager) QmlBindingWrapper(scope, vmFunction, qmlObj);