diff options
author | Lars Knoll <lars.knoll@digia.com> | 2013-05-23 22:56:38 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@digia.com> | 2013-05-24 12:39:34 +0200 |
commit | 1d62cefbaa23ef96fa4dc36d950cc2d9a2d4b5c3 (patch) | |
tree | 114eb77af818b37b05801498f9e4e3e4c482d7e1 /src/qml/qml | |
parent | fa2d572d5d202b05ed1908ea1119a1995960ce1f (diff) |
Add support for direct binding evaluation in QV4Script
This way there is no need to rewrite the binding expressions
anymore, instead we can directly compile them into a
binding function.
Change-Id: I91a0c540d066976e363590fe9ccde6a81ee92b1d
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Diffstat (limited to 'src/qml/qml')
-rw-r--r-- | src/qml/qml/qqmlbinding.cpp | 33 | ||||
-rw-r--r-- | src/qml/qml/qqmlexpression.cpp | 86 | ||||
-rw-r--r-- | src/qml/qml/qqmlexpression.h | 8 | ||||
-rw-r--r-- | src/qml/qml/qqmlexpression_p.h | 5 | ||||
-rw-r--r-- | src/qml/qml/qqmljavascriptexpression.cpp | 37 | ||||
-rw-r--r-- | src/qml/qml/qqmljavascriptexpression_p.h | 5 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4script.cpp | 173 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4script_p.h | 10 |
8 files changed, 159 insertions, 198 deletions
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index b66f053b0f..936c63f35e 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -108,11 +108,8 @@ QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt) QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt)); setScopeObject(obj); - QQmlRewrite::RewriteBinding rewriteBinding; - QString code = rewriteBinding(str); - m_expression = str.toUtf8(); - v4function = evalFunction(context(), obj, code, QString(), 0); + v4function = qmlBinding(context(), obj, str, QString(), 0); } QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt) @@ -146,11 +143,6 @@ QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlConte } } - if (needRewrite) { - QQmlRewrite::RewriteBinding rewriteBinding; - code = rewriteBinding(scriptPrivate->script); - } - setNotifyOnValueChanged(true); QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context)); setScopeObject(obj ? obj : scriptPrivate->scope); @@ -159,7 +151,10 @@ QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlConte m_lineNumber = scriptPrivate->lineNumber; m_columnNumber = scriptPrivate->columnNumber; - v4function = evalFunction(context(), scopeObject(), code, QString(), m_lineNumber); + if (needRewrite) + v4function = qmlBinding(context(), scopeObject(), code, QString(), m_lineNumber); + else + v4function = evalFunction(context(), scopeObject(), code, QString(), m_lineNumber); } QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt) @@ -170,11 +165,8 @@ QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt QQmlAbstractExpression::setContext(ctxt); setScopeObject(obj); - QQmlRewrite::RewriteBinding rewriteBinding; - QString code = rewriteBinding(str); - m_expression = str.toUtf8(); - v4function = evalFunction(ctxt, obj, code, QString(), 0); + v4function = qmlBinding(ctxt, obj, str, QString(), 0); } QQmlBinding::QQmlBinding(const QString &str, bool isRewritten, QObject *obj, @@ -187,17 +179,12 @@ QQmlBinding::QQmlBinding(const QString &str, bool isRewritten, QObject *obj, QQmlAbstractExpression::setContext(ctxt); setScopeObject(obj); - QString code; - if (isRewritten) { - code = str; - } else { - QQmlRewrite::RewriteBinding rewriteBinding; - code = rewriteBinding(str); - } - m_expression = str.toUtf8(); - v4function = evalFunction(ctxt, obj, code, url, m_lineNumber); + if (isRewritten) + v4function = evalFunction(ctxt, obj, str, url, m_lineNumber); + else + v4function = qmlBinding(ctxt, obj, str, url, m_lineNumber); } /*! diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index 32dbbb0e6c..419ea86ad9 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -96,42 +96,6 @@ void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, setScopeObject(me); } -void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QByteArray &expr, - bool isRewritten, QObject *me, const QString &srcUrl, - quint16 lineNumber, quint16 columnNumber) -{ - url = srcUrl; - line = lineNumber; - column = columnNumber; - - if (isRewritten) { - expressionFunctionValid = true; - expressionFunctionRewritten = true; - v8function = evalFunction(ctxt, me, expr.constData(), expr.length(), - srcUrl, lineNumber, &v8qmlscope); - setUseSharedContext(false); - - expressionUtf8 = expr; - } else { - expression = QString::fromUtf8(expr); - - expressionFunctionValid = false; - expressionFunctionRewritten = isRewritten; - } - - QQmlAbstractExpression::setContext(ctxt); - setScopeObject(me); -} - -QQmlExpression * -QQmlExpressionPrivate::create(QQmlContextData *ctxt, QObject *object, - const QString &expr, bool isRewritten, - const QString &url, quint16 lineNumber, quint16 columnNumber) -{ - return new QQmlExpression(ctxt, object, expr, isRewritten, url, lineNumber, columnNumber, - *new QQmlExpressionPrivate); -} - /*! \class QQmlExpression \since 5.0 @@ -174,29 +138,6 @@ QQmlExpression::QQmlExpression() { } -/*! \internal */ -QQmlExpression::QQmlExpression(QQmlContextData *ctxt, - QObject *object, const QString &expr, bool isRewritten, - const QString &url, int lineNumber, int columnNumber, - QQmlExpressionPrivate &dd) -: QObject(dd, 0) -{ - Q_D(QQmlExpression); - d->init(ctxt, expr, isRewritten, object, url, qmlSourceCoordinate(lineNumber), qmlSourceCoordinate(columnNumber)); -} - -/*! \internal */ -QQmlExpression::QQmlExpression(QQmlContextData *ctxt, - QObject *object, const QByteArray &expr, - bool isRewritten, - const QString &url, int lineNumber, int columnNumber, - QQmlExpressionPrivate &dd) -: QObject(dd, 0) -{ - Q_D(QQmlExpression); - d->init(ctxt, expr, isRewritten, object, url, qmlSourceCoordinate(lineNumber), qmlSourceCoordinate(columnNumber)); -} - /*! Create a QQmlExpression object that is a child of \a parent. @@ -271,15 +212,6 @@ QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope, d->init(ctxt, expression, scope); } -/*! \internal */ -QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope, - const QString &expression, QQmlExpressionPrivate &dd) -: QObject(dd, 0) -{ - Q_D(QQmlExpression); - d->init(ctxt, expression, scope); -} - /*! Destroy the QQmlExpression instance. */ @@ -314,11 +246,7 @@ QQmlContext *QQmlExpression::context() const QString QQmlExpression::expression() const { Q_D(const QQmlExpression); - if (!d->expressionUtf8.isEmpty()) { - return QString::fromUtf8(d->expressionUtf8); - } else { - return d->expression; - } + return d->expression; } /*! @@ -330,7 +258,6 @@ void QQmlExpression::setExpression(const QString &expression) d->resetNotifyOnValueChanged(); d->expression = expression; - d->expressionUtf8.clear(); d->expressionFunctionValid = false; d->expressionFunctionRewritten = false; } @@ -339,17 +266,10 @@ void QQmlExpression::setExpression(const QString &expression) v8::Handle<v8::Value> QQmlExpressionPrivate::v8value(bool *isUndefined) { if (!expressionFunctionValid) { - bool ok = true; - - QQmlRewrite::RewriteBinding rewriteBinding; - rewriteBinding.setName(name); - QString code; if (expressionFunctionRewritten) - code = expression; + v8function = evalFunction(context(), scopeObject(), expression, url, line, &v8qmlscope); else - code = rewriteBinding(expression, &ok); - - if (ok) v8function = evalFunction(context(), scopeObject(), code, url, line, &v8qmlscope); + v8function = qmlBinding(context(), scopeObject(), expression, url, line, &v8qmlscope); setUseSharedContext(false); expressionFunctionValid = true; } diff --git a/src/qml/qml/qqmlexpression.h b/src/qml/qml/qqmlexpression.h index b04abc1b98..b702a37ee0 100644 --- a/src/qml/qml/qqmlexpression.h +++ b/src/qml/qml/qqmlexpression.h @@ -91,14 +91,6 @@ public: Q_SIGNALS: void valueChanged(); -protected: - QQmlExpression(QQmlContextData *, QObject *, const QString &, - QQmlExpressionPrivate &dd); - QQmlExpression(QQmlContextData *, QObject *, const QString &, bool, - const QString &, int, int, QQmlExpressionPrivate &dd); - QQmlExpression(QQmlContextData *, QObject *, const QByteArray &, bool, - const QString &, int, int, QQmlExpressionPrivate &dd); - private: QQmlExpression(QQmlContextData *, QObject *, const QString &); diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h index 4577878290..0c407a2367 100644 --- a/src/qml/qml/qqmlexpression_p.h +++ b/src/qml/qml/qqmlexpression_p.h @@ -80,7 +80,6 @@ public: void init(QQmlContextData *, const QString &, QObject *); void init(QQmlContextData *, const QString &, bool, QObject *, const QString &, quint16, quint16); - void init(QQmlContextData *, const QByteArray &, bool, QObject *, const QString &, quint16, quint16); QVariant value(bool *isUndefined = 0); @@ -91,9 +90,6 @@ public: void _q_notify(); - static QQmlExpression *create(QQmlContextData *, QObject *, const QString &, bool, - const QString &, quint16, quint16); - bool expressionFunctionValid:1; bool expressionFunctionRewritten:1; @@ -103,7 +99,6 @@ public: virtual void expressionChanged(); QString expression; - QByteArray expressionUtf8; QV4::PersistentValue v8qmlscope; QV4::PersistentValue v8function; diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index b64035b66d..0e733cb1d2 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -306,7 +306,6 @@ void QQmlJavaScriptExpression::exceptionToError(const QV4::Exception &e, QQmlErr error.setDescription(e.value().toQString()); } -// Callee owns the persistent handle QV4::PersistentValue QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope, const char *code, int codeLength, @@ -316,7 +315,6 @@ QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope, return evalFunction(ctxt, scope, QString::fromUtf8(code, codeLength), filename, line, qmlscope); } -// Callee owns the persistent handle QV4::PersistentValue QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line, @@ -352,6 +350,41 @@ QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope, return result; } +QV4::PersistentValue QQmlJavaScriptExpression::qmlBinding(QQmlContextData *ctxt, QObject *scope, + const QString &code, const QString &filename, quint16 line, + QV4::PersistentValue *qmlscope) +{ + QQmlEngine *engine = ctxt->engine; + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + + QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); + QV4::ExecutionContext *ctx = v4->current; + + QV4::Value scopeObject = ep->v8engine()->qmlScope(ctxt, scope)->v4Value(); + QV4::Script script(v4, scopeObject.asObject(), code, filename, line); + QV4::Value result; + try { + script.parse(); + result = script.qmlBinding(); + } catch (QV4::Exception &e) { + e.accept(ctx); + QQmlError error; + QQmlExpressionPrivate::exceptionToError(e, error); + if (error.description().isEmpty()) + error.setDescription(QLatin1String("Exception occurred during function evaluation")); + if (error.line() == -1) + error.setLine(line); + if (error.url().isEmpty()) + error.setUrl(QUrl::fromLocalFile(filename)); + ep->warning(error); + return QV4::PersistentValue(); + } + if (qmlscope) + *qmlscope = scopeObject; + return result; +} + + void QQmlJavaScriptExpression::clearGuards() { while (Guard *g = activeGuards.takeFirst()) diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index d7db55d4f6..caf1266415 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -155,6 +155,11 @@ public: const char *code, int codeLength, const QString &filename, quint16 line, QV4::PersistentValue *qmlscope = 0); + // doesn't require rewriting the expression + static QV4::PersistentValue qmlBinding(QQmlContextData *ctxt, QObject *scope, + const QString &code, + const QString &filename, quint16 line, + QV4::PersistentValue *qmlscope = 0); protected: ~QQmlJavaScriptExpression(); diff --git a/src/qml/qml/v4/qv4script.cpp b/src/qml/qml/v4/qv4script.cpp index fa63ed33cb..610e80246e 100644 --- a/src/qml/qml/v4/qv4script.cpp +++ b/src/qml/qml/v4/qv4script.cpp @@ -58,119 +58,140 @@ using namespace QV4; -struct FunctionWrapper : FunctionObject +struct QmlBindingWrapper : FunctionObject { - FunctionWrapper(ExecutionContext *scope, Function *f) + QmlBindingWrapper(ExecutionContext *scope, Function *f, Object *qml) : FunctionObject(scope, scope->engine->id_eval) + , qml(qml) { + vtbl = &static_vtbl; function = f; usesArgumentsObject = function->usesArgumentsObject; needsActivation = function->needsActivation(); defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); } + + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; + +private: + Object *qml; }; +DEFINE_MANAGED_VTABLE(QmlBindingWrapper); + +Value QmlBindingWrapper::call(Managed *that, ExecutionContext *ctx, const Value &, Value *, int) +{ + ExecutionEngine *engine = ctx->engine; + QmlBindingWrapper *This = static_cast<QmlBindingWrapper *>(that); + + ExecutionContext *qmlScope = engine->newQmlContext(This, This->qml); + + Value result = This->function->code(qmlScope, This->function->codeData); + + engine->popContext(); + + return result; + +} + void Script::parse() { using namespace QQmlJS; + parsed = true; + ExecutionEngine *v4 = scope->engine; MemoryManager::GCBlocker gcBlocker(v4->memoryManager); V4IR::Module module; - Function *globalCode = 0; - { - QQmlJS::Engine ee, *engine = ⅇ - Lexer lexer(engine); - lexer.setCode(sourceCode, 1, false); - Parser parser(engine); - - const bool parsed = parser.parseProgram(); - - DiagnosticMessage *error = 0, **errIt = &error; - foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { - if (m.isError()) { - *errIt = new DiagnosticMessage; - (*errIt)->fileName = sourceFile; - (*errIt)->offset = m.loc.offset; - (*errIt)->length = m.loc.length; - (*errIt)->startLine = m.loc.startLine; - (*errIt)->startColumn = m.loc.startColumn; - (*errIt)->type = DiagnosticMessage::Error; - (*errIt)->message = m.message; - errIt = &(*errIt)->next; - } else { - qWarning() << sourceFile << ':' << m.loc.startLine << ':' << m.loc.startColumn - << ": warning: " << m.message; - } + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(sourceCode, line, false); + Parser parser(engine); + + const bool parsed = parser.parseProgram(); + + DiagnosticMessage *error = 0, **errIt = &error; + foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { + if (m.isError()) { + *errIt = new DiagnosticMessage; + (*errIt)->fileName = sourceFile; + (*errIt)->offset = m.loc.offset; + (*errIt)->length = m.loc.length; + (*errIt)->startLine = m.loc.startLine; + (*errIt)->startColumn = m.loc.startColumn; + (*errIt)->type = DiagnosticMessage::Error; + (*errIt)->message = m.message; + errIt = &(*errIt)->next; + } else { + qWarning() << sourceFile << ':' << m.loc.startLine << ':' << m.loc.startColumn + << ": warning: " << m.message; } - if (error) - scope->throwSyntaxError(error); - - if (parsed) { - using namespace AST; - Program *program = AST::cast<Program *>(parser.rootNode()); - if (!program) { - // if parsing was successful, and we have no program, then - // we're done...: - functionWrapper = Value::nullValue(); - return; - } - - QStringList inheritedLocals; - if (inheritContext) - for (String * const *i = scope->variables(), * const *ei = i + scope->variableCount(); i < ei; ++i) - inheritedLocals.append(*i ? (*i)->toQString() : QString()); - - Codegen cg(scope, strictMode); - V4IR::Function *globalIRCode = cg(sourceFile, sourceCode, program, &module, QQmlJS::Codegen::EvalCode, inheritedLocals); - QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(v4, &module)); - if (inheritContext) - isel->setUseFastLookups(false); - if (globalIRCode) - globalCode = isel->vmFunction(globalIRCode); + } + if (error) + scope->throwSyntaxError(error); + + if (parsed) { + using namespace AST; + Program *program = AST::cast<Program *>(parser.rootNode()); + if (!program) { + // if parsing was successful, and we have no program, then + // we're done...: + return; } + + QStringList inheritedLocals; + if (inheritContext) + for (String * const *i = scope->variables(), * const *ei = i + scope->variableCount(); i < ei; ++i) + inheritedLocals.append(*i ? (*i)->toQString() : QString()); + + Codegen cg(scope, strictMode); + V4IR::Function *globalIRCode = cg(sourceFile, sourceCode, program, &module, QQmlJS::Codegen::EvalCode, inheritedLocals); + QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(v4, &module)); + if (inheritContext) + isel->setUseFastLookups(false); + if (globalIRCode) + vmFunction = isel->vmFunction(globalIRCode); } - if (!globalCode) + + if (!vmFunction) // ### FIX file/line number __qmljs_throw(v4->current, QV4::Value::fromObject(v4->newSyntaxErrorObject(v4->current, 0)), -1); - - functionWrapper = Value::fromObject(new (v4->memoryManager) FunctionWrapper(scope, globalCode)); } Value Script::run() { - if (functionWrapper.value().isNull()) - return Value::undefinedValue(); - - if (functionWrapper.isEmpty()) + if (!parsed) parse(); + if (!vmFunction) + return Value::undefinedValue(); QV4::ExecutionEngine *engine = scope->engine; - QV4::FunctionObject *f = functionWrapper.value().asFunctionObject(); if (engine->debugger) engine->debugger->aboutToCall(0, scope); if (!qml) { - QV4::Function *function = this->function(); - TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, function); + TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction); bool strict = scope->strictMode; Lookup *lookups = scope->lookups; - scope->strictMode = function->isStrict; - scope->lookups = function->lookups; + scope->strictMode = vmFunction->isStrict; + scope->lookups = vmFunction->lookups; if (engine->debugger) engine->debugger->aboutToCall(0, scope); QV4::Value result; try { - result = function->code(scope, function->codeData); + result = vmFunction->code(scope, vmFunction->codeData); } catch (Exception &e) { scope->strictMode = strict; scope->lookups = lookups; @@ -182,20 +203,24 @@ Value Script::run() return result; } else { - ExecutionContext *ctx = engine->newQmlContext(f, qml); - - Value result = f->function->code(ctx, f->function->codeData); - - engine->popContext(); - - return result; + FunctionObject *f = new (engine->memoryManager) QmlBindingWrapper(scope, vmFunction, qml); + return f->call(Value::undefinedValue(), 0, 0); } } Function *Script::function() { - FunctionObject *f = functionWrapper.value().asFunctionObject(); - return f ? f->function : 0; + if (!parsed) + parse(); + return vmFunction; +} + +Value Script::qmlBinding() +{ + if (!parsed) + parse(); + QV4::ExecutionEngine *v4 = scope->engine; + return Value::fromObject(new (v4->memoryManager) QmlBindingWrapper(scope, vmFunction, qml)); } QV4::Value Script::evaluate(ExecutionEngine *engine, const QString &script, Object *scopeObject) diff --git a/src/qml/qml/v4/qv4script_p.h b/src/qml/qml/v4/qv4script_p.h index 17b98c213d..b9d27a6be5 100644 --- a/src/qml/qml/v4/qv4script_p.h +++ b/src/qml/qml/v4/qv4script_p.h @@ -53,10 +53,10 @@ struct ExecutionContext; struct Q_QML_EXPORT Script { Script(ExecutionContext *scope, const QString &sourceCode, const QString &source = QString(), int line = 0, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) - , scope(scope), strictMode(false), inheritContext(false), qml(0) {} + , scope(scope), strictMode(false), inheritContext(false), parsed(false), qml(0) {} Script(ExecutionEngine *engine, Object *qml, const QString &sourceCode, const QString &source = QString(), int line = 0, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) - , scope(engine->rootContext), strictMode(true), inheritContext(true), qml(qml) {} + , scope(engine->rootContext), strictMode(true), inheritContext(true), parsed(false), qml(qml) {} QString sourceFile; int line; int column; @@ -64,13 +64,17 @@ struct Q_QML_EXPORT Script { ExecutionContext *scope; bool strictMode; bool inheritContext; + bool parsed; Object *qml; - PersistentValue functionWrapper; + Function *vmFunction; void parse(); Value run(); + Value qmlBinding(); + Function *function(); + static Value evaluate(ExecutionEngine *engine, const QString &script, Object *scopeObject); }; |