Пример #1
0
QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt)
: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding)
{
    if (ctxt && !ctxt->isValid())
        return;

    const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
    if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid()))
        return;

    QString url;
    QV4::Function *runtimeFunction = 0;

    QQmlContextData *ctxtdata = QQmlContextData::get(scriptPrivate->context);
    QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine());
    if (engine && ctxtdata && !ctxtdata->url.isEmpty() && ctxtdata->typeCompilationUnit) {
        url = ctxtdata->url.toString();
        if (scriptPrivate->bindingId != QQmlBinding::Invalid)
            runtimeFunction = ctxtdata->typeCompilationUnit->runtimeFunctions.at(scriptPrivate->bindingId);
    }

    setNotifyOnValueChanged(true);
    QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context));
    setScopeObject(obj ? obj : scriptPrivate->scope);

    if (runtimeFunction) {
        v4function = QV4::QmlBindingWrapper::createQmlCallableForFunction(ctxtdata, scopeObject(), runtimeFunction);
    } else {
        QString code = scriptPrivate->script;
        v4function = qmlBinding(context(), scopeObject(), code, url, scriptPrivate->lineNumber);
    }
}
Пример #2
0
QT_BEGIN_NAMESPACE

QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index,
                                                     QQmlContextData *ctxt, QObject *scope, const QString &expression,
                                                     const QString &fileName, quint16 line, quint16 column,
                                                     const QString &handlerName,
                                                     const QString &parameterString)
    : QQmlJavaScriptExpression(),
      m_index(index),
      m_target(target)
{
    init(ctxt, scope);

    QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine());
    QV4::ExecutionEngine *v4 = ep->v4engine();

    QString function;

    // Add some leading whitespace to account for the binding's column offset.
    // It's 2 off because a, we start counting at 1 and b, the '(' below is not counted.
    function.fill(QChar(QChar::Space), qMax(column, (quint16)2) - 2);
    function += QStringLiteral("(function ");
    function += handlerName;
    function += QLatin1Char('(');

    if (parameterString.isEmpty()) {
        QString error;
        //TODO: look at using the property cache here (as in the compiler)
        //      for further optimization
        QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index);
        function += QQmlPropertyCache::signalParameterStringForJS(v4, signal.parameterNames(), &error);

        if (!error.isEmpty()) {
            qmlInfo(scopeObject()) << error;
            return;
        }
    } else
        function += parameterString;

    function += QStringLiteral(") { ");
    function += expression;
    function += QStringLiteral(" })");

    m_function.set(v4, evalFunction(context(), scopeObject(), function, fileName, line));

    if (m_function.isNullOrUndefined())
        return; // could not evaluate function.  Not valid.

}
Пример #3
0
// Must be called with a valid handle scope
QV4::ReturnedValue QQmlExpressionPrivate::v4value(bool *isUndefined)
{
    Q_Q(QQmlExpression);

    if (!expressionFunctionValid) {
        function = qmlBinding(context(), scopeObject(), expression, url, line, &qmlscope);
        expressionFunctionValid = true;
    }

    QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(q->engine())->v4engine();
    QV4::Scope scope(v4);
    QV4::ScopedValue f(scope, function.value());
    return evaluate(context(), f, isUndefined);
}
Пример #4
0
QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, QV4::Function *runtimeFunction)
    : QQmlJavaScriptExpression(),
      m_index(index),
      m_target(target)
{
    // It's important to call init first, because m_index gets remapped in case of cloned signals.
    init(ctxt, scope);

    QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index);
    QString error;
    QV4::ExecutionEngine *engine = QQmlEnginePrivate::getV4Engine(ctxt->engine);
    m_function.set(engine, QV4::FunctionObject::createQmlFunction(ctxt, scope, runtimeFunction, signal.parameterNames(), &error));
    if (!error.isEmpty()) {
        qmlInfo(scopeObject()) << error;
        m_function.clear();
    }
}
Пример #5
0
QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt)
: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding)
{
    if (ctxt && !ctxt->isValid())
        return;

    const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
    if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid()))
        return;

    bool needRewrite = true;
    QString code;

    int id = scriptPrivate->bindingId;
    if (id >= 0) {
        QQmlContextData *ctxtdata = QQmlContextData::get(scriptPrivate->context);
        QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine());
        if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
            QQmlTypeData *typeData = engine->typeLoader.getType(ctxtdata->url);
            Q_ASSERT(typeData);

            if (QQmlCompiledData *cdata = typeData->compiledData()) {
                needRewrite = false;
                code = cdata->primitives.at(id);
                m_url = cdata->name;
            }

            typeData->release();
        }
    }

    if (needRewrite) {
        QQmlRewrite::RewriteBinding rewriteBinding;
        code = rewriteBinding(scriptPrivate->script);
    }

    setNotifyOnValueChanged(true);
    QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context));
    setScopeObject(obj ? obj : scriptPrivate->scope);

    m_expression = scriptPrivate->script.toUtf8();
    m_lineNumber = scriptPrivate->lineNumber;
    m_columnNumber = scriptPrivate->columnNumber;

    v8function = evalFunction(context(), scopeObject(), code, QString(), m_lineNumber);
}
QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, QV4::Function *runtimeFunction)
    : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable),
      m_target(target),
      m_index(index),
      m_extra(0)
{
    setExpressionFunctionValid(true);
    setInvalidParameterName(false);

    // It's important to call init first, because m_index gets remapped in case of cloned signals.
    init(ctxt, scope);

    QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index);
    QString error;
    m_v8function = QV4::QmlBindingWrapper::createQmlCallableForFunction(ctxt, scope, runtimeFunction, signal.parameterNames(), &error);
    if (!error.isEmpty()) {
        qmlInfo(scopeObject()) << error;
        setInvalidParameterName(true);
    } else
        setInvalidParameterName(false);
}
QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QQmlContextData *context,
                                   const QV4::ValueRef function,
                                   QV4::CallData *callData,
                                   bool *isUndefined)
{
    Q_ASSERT(context && context->engine);

    if (function->isUndefined()) {
        if (isUndefined)
            *isUndefined = true;
        return QV4::Encode::undefined();
    }

    QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);

    // All code that follows must check with watcher before it accesses data members
    // incase we have been deleted.
    DeleteWatcher watcher(this);

    Q_ASSERT(notifyOnValueChanged() || activeGuards.isEmpty());
    GuardCapture capture(context->engine, this, &watcher);

    QQmlEnginePrivate::PropertyCapture *lastPropertyCapture = ep->propertyCapture;
    ep->propertyCapture = notifyOnValueChanged()?&capture:0;


    if (notifyOnValueChanged())
        capture.guards.copyAndClearPrepend(activeGuards);

    QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine());
    QV4::Scope scope(v4);
    QV4::ScopedValue result(scope, QV4::Primitive::undefinedValue());
    QV4::ExecutionContext *ctx = v4->currentContext();
    callData->thisObject = v4->globalObject;
    if (scopeObject()) {
        QV4::ScopedValue value(scope, QV4::QObjectWrapper::wrap(ctx->d()->engine, scopeObject()));
        if (value->isObject())
            callData->thisObject = value;
    }

    result = function->asFunctionObject()->call(callData);
    if (scope.hasException()) {
        if (watcher.wasDeleted())
            ctx->catchException(); // ignore exception
        else
            delayedError()->catchJavaScriptException(ctx);
        if (isUndefined)
            *isUndefined = true;
    } else {
        if (isUndefined)
            *isUndefined = result->isUndefined();

        if (!watcher.wasDeleted() && hasDelayedError())
            delayedError()->clearError();
    }

    if (capture.errorString) {
        for (int ii = 0; ii < capture.errorString->count(); ++ii)
            qWarning("%s", qPrintable(capture.errorString->at(ii)));
        delete capture.errorString;
        capture.errorString = 0;
    }

    while (Guard *g = capture.guards.takeFirst())
        g->Delete();

    ep->propertyCapture = lastPropertyCapture;

    return result.asReturnedValue();
}
// Parts of this function mirror code in QQmlExpressionPrivate::value() and v8value().
// Changes made here may need to be made there and vice versa.
void QQmlBoundSignalExpression::evaluate(void **a)
{
    Q_ASSERT (context() && engine());

    if (invalidParameterName())
        return;

    QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine());
    QV4::Scope scope(ep->v4engine());

    ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
    {
        if (!expressionFunctionValid()) {
            Q_ASSERT(!m_extra.isNull());
            QString expression;

            // Add some leading whitespace to account for the binding's column offset.
            // It's 2 off because a, we start counting at 1 and b, the '(' below is not counted.
            expression.fill(QChar(QChar::Space), qMax(m_extra->m_sourceLocation.column, (quint16)2) - 2);
            expression += QStringLiteral("(function ");
            expression += m_extra->m_handlerName;
            expression += QLatin1Char('(');

            if (m_extra->m_parameterString.isEmpty()) {
                QString error;
                //TODO: look at using the property cache here (as in the compiler)
                //      for further optimization
                QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index);
                expression += QQmlPropertyCache::signalParameterStringForJS(engine(), signal.parameterNames(), &error);

                if (!error.isEmpty()) {
                    qmlInfo(scopeObject()) << error;
                    setInvalidParameterName(true);
                    ep->dereferenceScarceResources();
                    return;
                }
            } else
                expression += m_extra->m_parameterString;

            expression += QStringLiteral(") { ");
            expression += m_extra->m_expression;
            expression += QStringLiteral(" })");

            m_extra->m_expression.clear();
            m_extra->m_handlerName.clear();
            m_extra->m_parameterString.clear();

            m_v8function = evalFunction(context(), scopeObject(), expression,
                                        m_extra->m_sourceLocation.sourceFile, m_extra->m_sourceLocation.line, &m_extra->m_v8qmlscope);

            if (m_v8function.isNullOrUndefined()) {
                ep->dereferenceScarceResources();
                return; // could not evaluate function.  Not valid.
            }

            setExpressionFunctionValid(true);
        }

        QV8Engine *engine = ep->v8engine();
        QVarLengthArray<int, 9> dummy;
        //TODO: lookup via signal index rather than method index as an optimization
        int methodIndex = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).methodIndex();
        int *argsTypes = QQmlPropertyCache::methodParameterTypes(m_target, methodIndex, dummy, 0);
        int argCount = argsTypes ? *argsTypes : 0;

        QV4::ScopedValue f(scope, m_v8function.value());
        QV4::ScopedCallData callData(scope, argCount);
        for (int ii = 0; ii < argCount; ++ii) {
            int type = argsTypes[ii + 1];
            //### ideally we would use metaTypeToJS, however it currently gives different results
            //    for several cases (such as QVariant type and QObject-derived types)
            //args[ii] = engine->metaTypeToJS(type, a[ii + 1]);
            if (type == QMetaType::QVariant) {
                callData->args[ii] = engine->fromVariant(*((QVariant *)a[ii + 1]));
            } else if (type == QMetaType::Int) {
                //### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise
                callData->args[ii] = QV4::Primitive::fromInt32(*reinterpret_cast<const int*>(a[ii + 1]));
            } else if (type == qMetaTypeId<QQmlV4Handle>()) {
                callData->args[ii] = *reinterpret_cast<QQmlV4Handle *>(a[ii + 1]);
            } else if (ep->isQObject(type)) {
                if (!*reinterpret_cast<void* const *>(a[ii + 1]))
                    callData->args[ii] = QV4::Primitive::nullValue();
                else
                    callData->args[ii] = QV4::QObjectWrapper::wrap(ep->v4engine(), *reinterpret_cast<QObject* const *>(a[ii + 1]));
            } else {
                callData->args[ii] = engine->fromVariant(QVariant(type, a[ii + 1]));
            }
        }

        QQmlJavaScriptExpression::evaluate(context(), f, callData, 0);
    }
    ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
}
Пример #9
0
// Parts of this function mirror code in QQmlExpressionPrivate::value() and v8value().
// Changes made here may need to be made there and vice versa.
void QQmlBoundSignalExpression::evaluate(void **a)
{
    Q_ASSERT (context() && engine());

    if (m_invalidParameterName)
        return;

    QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine());

    ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
    {
        v8::HandleScope handle_scope;
        v8::Context::Scope context_scope(ep->v8engine()->context());
        if (!m_expressionFunctionValid) {

            //TODO: look at using the property cache here (as in the compiler)
            //      for further optimization
            QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index);
            QQmlRewrite::RewriteSignalHandler rewriter;

            QString expression;
            bool ok = true;

            if (m_expressionFunctionRewritten) {
                expression = QString::fromUtf8(m_expressionUtf8);

                //if we need parameters, and the rewrite doesn't include them,
                //create and insert the parameter string now
                if (m_parameterCountForJS == -1 && signal.parameterCount()) {
                    const QString &parameters = rewriter.createParameterString(signal.parameterNames(),
                                                ep->v8engine()->illegalNames());
                    int index = expression.indexOf(QLatin1Char('('), 1);
                    Q_ASSERT(index > -1);
                    expression.insert(index + 1, parameters);

                    setParameterCountForJS(rewriter.parameterCountForJS());
                }

                m_expressionUtf8.clear();
            } else {
                //expression is still in its original form, so perform a full rewrite
                expression = rewriter(m_expression, QString()/*no name hint available*/, &ok,
                                      signal.parameterNames(),
                                      ep->v8engine()->illegalNames());
                setParameterCountForJS(rewriter.parameterCountForJS());
                m_expression.clear();
            }

            if (rewriter.hasParameterError()) {
                qmlInfo(scopeObject()) << rewriter.parameterError();
                m_invalidParameterName = true;
                ep->dereferenceScarceResources();
                return;
            }

            if (ok) {
                m_v8function = evalFunction(context(), scopeObject(), expression,
                                            m_fileName, m_line, &m_v8qmlscope);
            }

            if (m_v8function.IsEmpty() || m_v8function->IsNull()) {
                ep->dereferenceScarceResources();
                return; // could not evaluate function.  Not valid.
            }

            setUseSharedContext(false);
            m_expressionFunctionValid = true;
        }

        if (!hasParameterInfo()) {
            QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0);
        } else {
            QV8Engine *engine = ep->v8engine();
            QVarLengthArray<int, 9> dummy;
            //TODO: lookup via signal index rather than method index as an optimization
            int methodIndex = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).methodIndex();
            int *argsTypes = QQmlPropertyCache::methodParameterTypes(m_target, methodIndex, dummy, 0);
            int argCount = argsTypes ? m_parameterCountForJS : 0;

            QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);

            for (int ii = 0; ii < argCount; ++ii) {
                int type = argsTypes[ii + 1];
                //### ideally we would use metaTypeToJS, however it currently gives different results
                //    for several cases (such as QVariant type and QObject-derived types)
                //args[ii] = engine->metaTypeToJS(type, a[ii + 1]);
                if (type == QMetaType::QVariant) {
                    args[ii] = engine->fromVariant(*((QVariant *)a[ii + 1]));
                } else if (type == QMetaType::Int) {
                    //### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise
                    args[ii] = v8::Integer::New(*reinterpret_cast<const int*>(a[ii + 1]));
                } else if (type == qMetaTypeId<QQmlV8Handle>()) {
                    args[ii] = reinterpret_cast<QQmlV8Handle *>(a[ii + 1])->toHandle();
                } else if (ep->isQObject(type)) {
                    if (!*reinterpret_cast<void* const *>(a[ii + 1]))
                        args[ii] = v8::Null();
                    else
                        args[ii] = engine->newQObject(*reinterpret_cast<QObject* const *>(a[ii + 1]));
                } else {
                    args[ii] = engine->fromVariant(QVariant(type, a[ii + 1]));
                }
            }

            QQmlJavaScriptExpression::evaluate(context(), m_v8function, argCount, args.data(), 0);
        }
    }
    ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
}
v8::Local<v8::Value>
QQmlJavaScriptExpression::evaluate(QQmlContextData *context,
                                   v8::Handle<v8::Function> function,
                                   int argc, v8::Handle<v8::Value> args[],
                                   bool *isUndefined)
{
    Q_ASSERT(context && context->engine);

    if (function.IsEmpty() || function->IsUndefined()) {
        if (isUndefined) *isUndefined = true;
        return v8::Local<v8::Value>();
    }

    QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);

    Q_ASSERT(notifyOnValueChanged() || activeGuards.isEmpty());
    GuardCapture capture(context->engine, this);

    QQmlEnginePrivate::PropertyCapture *lastPropertyCapture = ep->propertyCapture;
    ep->propertyCapture = notifyOnValueChanged()?&capture:0;


    if (notifyOnValueChanged())
        capture.guards.copyAndClearPrepend(activeGuards);

    QQmlContextData *lastSharedContext = 0;
    QObject *lastSharedScope = 0;

    bool sharedContext = useSharedContext();

    // All code that follows must check with watcher before it accesses data members
    // incase we have been deleted.
    DeleteWatcher watcher(this);

    if (sharedContext) {
        lastSharedContext = ep->sharedContext;
        lastSharedScope = ep->sharedScope;
        ep->sharedContext = context;
        ep->sharedScope = scopeObject();
    }

    v8::Local<v8::Value> result;
    {
        v8::TryCatch try_catch;
        v8::Handle<v8::Object> This = ep->v8engine()->global();
        if (scopeObject() && requiresThisObject()) {
            v8::Handle<v8::Value> value = ep->v8engine()->newQObject(scopeObject());
            if (value->IsObject()) This = v8::Handle<v8::Object>::Cast(value);
        }

        result = function->Call(This, argc, args);

        if (isUndefined)
            *isUndefined = try_catch.HasCaught() || result->IsUndefined();

        if (watcher.wasDeleted()) {
        } else if (try_catch.HasCaught()) {
            v8::Context::Scope scope(ep->v8engine()->context());
            v8::Local<v8::Message> message = try_catch.Message();
            if (!message.IsEmpty()) {
                delayedError()->setMessage(message);
            } else {
                if (hasDelayedError()) delayedError()->clearError();
            }
        } else {
            if (hasDelayedError()) delayedError()->clearError();
        }
    }

    if (sharedContext) {
        ep->sharedContext = lastSharedContext;
        ep->sharedScope = lastSharedScope;
    }

    if (capture.errorString) {
        for (int ii = 0; ii < capture.errorString->count(); ++ii)
            qWarning("%s", qPrintable(capture.errorString->at(ii)));
        delete capture.errorString;
        capture.errorString = 0;
    }

    while (Guard *g = capture.guards.takeFirst())
        g->Delete();

    ep->propertyCapture = lastPropertyCapture;

    return result;
}