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(); }
void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) { if (!enabledFlag() || !context() || !context()->isValid()) return; // Check that the target has not been deleted if (QQmlData::wasDeleted(object())) return; QString url; quint16 lineNumber; quint16 columnNumber; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); QV4::Scope scope(ep->v4engine()); QV4::ScopedFunctionObject f(scope, v4function.value()); Q_ASSERT(f); if (f->bindingKeyFlag()) { Q_ASSERT(f->as<QV4::QQmlBindingFunction>()); QQmlSourceLocation loc = static_cast<QV4::QQmlBindingFunction *>(f.getPointer())->d()->bindingLocation; url = loc.sourceFile; lineNumber = loc.line; columnNumber = loc.column; } else { QV4::Function *function = f->asFunctionObject()->function(); Q_ASSERT(function); url = function->sourceFile(); lineNumber = function->compiledFunction->location.line; columnNumber = function->compiledFunction->location.column; } int lineNo = qmlSourceCoordinate(lineNumber); int columnNo = qmlSourceCoordinate(columnNumber); QQmlTrace trace("General Binding Update"); trace.addDetail("URL", url); trace.addDetail("Line", lineNo); trace.addDetail("Column", columnNo); if (!updatingFlag()) { QQmlBindingProfiler prof(ep->profiler, url, lineNo, columnNo); setUpdatingFlag(true); QQmlAbstractExpression::DeleteWatcher watcher(this); if (m_core.propType == qMetaTypeId<QQmlBinding *>()) { int idx = m_core.coreIndex; Q_ASSERT(idx != -1); QQmlBinding *t = this; int status = -1; void *a[] = { &t, 0, &status, &flags }; QMetaObject::metacall(*m_coreObject, QMetaObject::WriteProperty, idx, a); } else { ep->referenceScarceResources(); bool isUndefined = false; QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(context(), f, &isUndefined)); trace.event("writing binding result"); bool needsErrorLocationData = false; if (!watcher.wasDeleted() && !hasError()) needsErrorLocationData = !QQmlPropertyPrivate::writeBinding(*m_coreObject, m_core, context(), this, result, isUndefined, flags); if (!watcher.wasDeleted()) { if (needsErrorLocationData) delayedError()->setErrorLocation(QUrl(url), lineNumber, columnNumber); if (hasError()) { if (!delayedError()->addError(ep)) ep->warning(this->error(context()->engine)); } else { clearError(); } } ep->dereferenceScarceResources(); } if (!watcher.wasDeleted()) setUpdatingFlag(false); } else { QQmlProperty p = property(); QQmlAbstractBinding::printBindingLoopError(p); } }
// 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. }
void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) { if (!enabledFlag() || !context() || !context()->isValid()) return; // Check that the target has not been deleted if (QQmlData::wasDeleted(object())) return; int lineNo = qmlSourceCoordinate(m_lineNumber); int columnNo = qmlSourceCoordinate(m_columnNumber); QQmlTrace trace("General Binding Update"); trace.addDetail("URL", m_url); trace.addDetail("Line", lineNo); trace.addDetail("Column", columnNo); if (!updatingFlag()) { QQmlBindingProfiler prof(m_url, lineNo, columnNo, QQmlProfilerService::QmlBinding); setUpdatingFlag(true); QQmlAbstractExpression::DeleteWatcher watcher(this); if (m_core.propType == qMetaTypeId<QQmlBinding *>()) { int idx = m_core.coreIndex; Q_ASSERT(idx != -1); QQmlBinding *t = this; int status = -1; void *a[] = { &t, 0, &status, &flags }; QMetaObject::metacall(*m_coreObject, QMetaObject::WriteProperty, idx, a); } else { QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); ep->referenceScarceResources(); bool isUndefined = false; v8::HandleScope handle_scope; v8::Context::Scope scope(ep->v8engine()->context()); v8::Local<v8::Value> result = QQmlJavaScriptExpression::evaluate(context(), v8function, &isUndefined); trace.event("writing binding result"); bool needsErrorLocationData = false; if (!watcher.wasDeleted() && !hasError()) needsErrorLocationData = !QQmlPropertyPrivate::writeBinding(*m_coreObject, m_core, context(), this, result, isUndefined, flags); if (!watcher.wasDeleted()) { if (needsErrorLocationData) delayedError()->setErrorLocation(QUrl(m_url), m_lineNumber, m_columnNumber); if (hasError()) { if (!delayedError()->addError(ep)) ep->warning(this->error(context()->engine)); } else { clearError(); } } ep->dereferenceScarceResources(); } if (!watcher.wasDeleted()) setUpdatingFlag(false); } else { QQmlProperty p = property(); QQmlAbstractBinding::printBindingLoopError(p); } }
void QQuickAnimationController::classBegin() { QQmlEnginePrivate *engPriv = QQmlEnginePrivate::get(qmlEngine(this)); engPriv->registerFinalizeCallback(this, this->metaObject()->indexOfSlot("componentFinalized()")); }
void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) { if (!compiledData) return; QML_MEMORY_SCOPE_URL(compiledData->url()); QExplicitlySharedDataPointer<QQmlIncubatorPrivate> protectThis(this); QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(this); QQmlEngine *engine = compiledData->engine; QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); if (!vmeGuard.isOK()) { QQmlError error; error.setUrl(compiledData->url()); error.setDescription(QQmlComponent::tr("Object destroyed during incubation")); errors << error; progress = QQmlIncubatorPrivate::Completed; goto finishIncubate; } vmeGuard.clear(); if (progress == QQmlIncubatorPrivate::Execute) { enginePriv->referenceScarceResources(); QObject *tresult = 0; tresult = creator->create(subComponentToCreate, /*parent*/0, &i); if (!tresult) errors = creator->errors; enginePriv->dereferenceScarceResources(); if (watcher.hasRecursed()) return; result = tresult; if (errors.isEmpty() && result == 0) goto finishIncubate; if (result) { QQmlData *ddata = QQmlData::get(result); Q_ASSERT(ddata); //see QQmlComponent::beginCreate for explanation of indestructible ddata->indestructible = true; ddata->explicitIndestructibleSet = true; ddata->rootObjectInCreation = false; if (q) q->setInitialState(result); } if (watcher.hasRecursed()) return; if (errors.isEmpty()) progress = QQmlIncubatorPrivate::Completing; else progress = QQmlIncubatorPrivate::Completed; changeStatus(calculateStatus()); if (watcher.hasRecursed()) return; if (i.shouldInterrupt()) goto finishIncubate; } if (progress == QQmlIncubatorPrivate::Completing) { do { if (watcher.hasRecursed()) return; QQmlContextData *ctxt = 0; ctxt = creator->finalize(i); if (ctxt) { rootContext = ctxt; progress = QQmlIncubatorPrivate::Completed; goto finishIncubate; } } while (!i.shouldInterrupt()); } finishIncubate: if (progress == QQmlIncubatorPrivate::Completed && waitingFor.isEmpty()) { QExplicitlySharedDataPointer<QQmlIncubatorPrivate> isWaiting = waitingOnMe; clear(); if (isWaiting) { QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(isWaiting.data()); changeStatus(calculateStatus()); if (!watcher.hasRecursed()) isWaiting->incubate(i); } else { changeStatus(calculateStatus()); } enginePriv->inProgressCreations--; if (0 == enginePriv->inProgressCreations) { while (enginePriv->erroredBindings) { enginePriv->warning(enginePriv->erroredBindings); enginePriv->erroredBindings->removeError(); } } } else if (!creator.isNull()) { vmeGuard.guard(creator.data()); } }
v8::Handle<v8::Value> QV8ContextWrapper::Getter(v8::Local<v8::String> property, const v8::AccessorInfo &info) { QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This()); // Its possible we could delay the calculation of the "actual" context (in the case // of sub contexts) until it is definately needed. QQmlContextData *context = resource->getContext(); QQmlContextData *expressionContext = context; if (!context) return v8::Undefined(); if (v8::Context::GetCallingQmlGlobal() != info.This()) return v8::Handle<v8::Value>(); // Search type (attached property/enum/imported scripts) names // while (context) { // Search context properties // Search scope object // Search context object // context = context->parent // } QV8Engine *engine = resource->engine; QObject *scopeObject = resource->getScopeObject(); QHashedV8String propertystring(property); if (context->imports && QV8Engine::startsWithUpper(property)) { // Search for attached properties, enums and imported scripts QQmlTypeNameCache::Result r = context->imports->query(propertystring); if (r.isValid()) { if (r.scriptIndex != -1) { int index = r.scriptIndex; if (index < context->importedScripts.count()) return context->importedScripts.at(index); else return v8::Undefined(); } else if (r.type) { return engine->typeWrapper()->newObject(scopeObject, r.type); } else if (r.importNamespace) { return engine->typeWrapper()->newObject(scopeObject, context->imports, r.importNamespace); } Q_ASSERT(!"Unreachable"); } // Fall through } QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine()); QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper(); while (context) { // Search context properties if (context->propertyNames) { int propertyIdx = context->propertyNames->value(propertystring); if (propertyIdx != -1) { if (propertyIdx < context->idValueCount) { ep->captureProperty(&context->idValues[propertyIdx].bindings); return engine->newQObject(context->idValues[propertyIdx]); } else { QQmlContextPrivate *cp = context->asQQmlContextPrivate(); ep->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex); const QVariant &value = cp->propertyValues.at(propertyIdx); if (value.userType() == qMetaTypeId<QList<QObject*> >()) { QQmlListProperty<QObject> prop(context->asQQmlContext(), (void*) qintptr(propertyIdx), QQmlContextPrivate::context_count, QQmlContextPrivate::context_at); return engine->listWrapper()->newList(prop, qMetaTypeId<QQmlListProperty<QObject> >()); } else { return engine->fromVariant(cp->propertyValues.at(propertyIdx)); } } } } // Search scope object if (scopeObject) { v8::Handle<v8::Value> result = qobjectWrapper->getProperty(scopeObject, propertystring, context, QV8QObjectWrapper::CheckRevision); if (!result.IsEmpty()) return result; } scopeObject = 0; // Search context object if (context->contextObject) { v8::Handle<v8::Value> result = qobjectWrapper->getProperty(context->contextObject, propertystring, context, QV8QObjectWrapper::CheckRevision); if (!result.IsEmpty()) return result; } context = context->parent; } expressionContext->unresolvedNames = true; // V8 will throw a ReferenceError if appropriate ("typeof" should not throw) return v8::Handle<v8::Value>(); }
bool QmcTypeCompiler::precompile() { // QQmlTypeCompiler::compile // qqmltypecompiler.cpp:68 compiledData->importCache = new QQmlTypeNameCache; // namespaces // qqmltypecompiler.cpp:72 // qqmltypeloader.cpp:2356 foreach (const QString &ns, compilation->namespaces) { compiledData->importCache->add(ns); } // TBD: qqmltypecompiler.cpp:72 namespaces, copy from QmlCompilation->namespaces // qqmltypecompiler.cpp:76 composite singletons // Add any Composite Singletons that were used to the import cache foreach (const QmlCompilation::TypeReference &singleton, compilation->m_compositeSingletons) { compiledData->importCache->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); } compilation->importCache->populateCache(compiledData->importCache); // create typemap qqmltypecompiler.cpp:81 if (!createTypeMap()) return false; // qqmltypecompiler.cpp:134 // Build property caches and VME meta object data if (!createPropertyCacheVmeMetaData()) { return false; } // default property merger mergeDefaultProperties(); // convert signal handlers to functions if (!convertSignalHandlersToFunctions()) return false; // resolve enums if (!resolveEnums()) return false; // index custom parser scripts indexCustomParserScripts(); // annotate aliases annotateAliases(); // collect imported scripts // qqmltypecompiler.cpp:180 // script data collection // 1. QQmlTypeData::continueLoadFromIR qqmltypeloader.cpp:2271 from QmlIR::Document -> QQmlTypeData::m_scripts // 2. QQmlTypeData::resolveTypes qqmltypeloader.cpp:2356 from QQmlImports -> QQmlTypeData::m_scripts // TBD: are actual script blobs needed ? #if 0 compiledData->scripts.reserve(compilation->scripts.count()); #endif for (int scriptIndex = 0; scriptIndex < compilation->scripts.count(); ++scriptIndex) { const QmlCompilation::ScriptReference &script = compilation->scripts.at(scriptIndex); QString qualifier = script.qualifier; QString enclosingNamespace; const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.')); if (lastDotIndex != -1) { enclosingNamespace = qualifier.left(lastDotIndex); qualifier = qualifier.mid(lastDotIndex+1); } compiledData->importCache->add(qualifier, scriptIndex, enclosingNamespace); #if 0 QQmlScriptData *scriptData = script.script->scriptData(); scriptData->addref(); compiledData->scripts << scriptData; #endif } // resolve component boundaries and aliases if (!resolveComponentBoundariesAndAliases()) return false; // Compile JS binding expressions and signal handlers if (!compilation->document->javaScriptCompilationUnit) { // We can compile script strings ahead of time, but they must be compiled // without type optimizations as their scope is always entirely dynamic. scanScriptStrings(); // TBD: check how it uses importCache // TBD: check how links with other scripts QmlIR::JSCodeGen v4CodeGenerator(compilation->urlString, compilation->document->code, &compilation->document->jsModule, &compilation->document->jsParserEngine, compilation->document->program, compiledData->importCache, &compilation->document->jsGenerator.stringTable); JSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) return false; simplifyJavaScriptBindingExpressions(); QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(compilation->engine); QV4::ExecutionEngine *v4 = enginePrivate->v4engine(); // TBD: store unlinked binaries #if 0 QScopedPointer<QV4::EvalInstructionSelection> isel( v4->iselFactory->create(enginePrivate, v4->executableAllocator, &compilation->document->jsModule, &compilation->document->jsGenerator)); #endif QScopedPointer<QmcInstructionSelection> isel( new QmcInstructionSelection(enginePrivate, v4->executableAllocator, &compilation->document->jsModule, &compilation->document->jsGenerator)); isel->setUseFastLookups(false); compilation->document->javaScriptCompilationUnit = isel->compile(/*generated unit data*/false); compilation->linkData = isel->linkData(); compilation->exceptionReturnLabels = isel->exceptionReturnLabelsData(); compilation->exceptionPropagationJumps = isel->exceptionPropagationJumpsData(); #if CPU(ARM_THUMB2) compilation->jumpsToLinkData = isel->linkRecordData(); compilation->unlinkedCodeData = isel->unlinkedCodeData(); #endif } // Generate QML compiled type data structures QmlIR::QmlUnitGenerator qmlGenerator; QV4::CompiledData::QmlUnit *qmlUnit = qmlGenerator.generate(*compilation->document); Q_ASSERT(compilation->document->javaScriptCompilationUnit); Q_ASSERT((void*)qmlUnit == (void*)&qmlUnit->header); // The js unit owns the data and will free the qml unit. compilation->document->javaScriptCompilationUnit->data = &qmlUnit->header; compiledData->compilationUnit = compilation->document->javaScriptCompilationUnit; if (compiledData->compilationUnit) compiledData->compilationUnit->ref(); compiledData->qmlUnit = qmlUnit; // ownership transferred to m_compiledData // add to type registry // qqmltypecompiler.cpp:248 if (compiledData->isCompositeType()) { if (compilation->singleton) qmlUnit->header.flags |= QV4::CompiledData::Unit::IsSingleton; QQmlEnginePrivate::get(compilation->engine)->registerInternalCompositeType(compiledData); } else { if (compilation->singleton) { qmlUnit->header.flags = qmlUnit->header.flags | QV4::CompiledData::Unit::IsSingleton; } const QV4::CompiledData::Object *obj = qmlUnit->objectAt(qmlUnit->indexOfRootObject); QQmlCompiledData::TypeReference *typeRef = compiledData->resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); if (typeRef->component) { compiledData->metaTypeId = typeRef->component->metaTypeId; compiledData->listMetaTypeId = typeRef->component->listMetaTypeId; } else { compiledData->metaTypeId = typeRef->type->typeId(); compiledData->listMetaTypeId = typeRef->type->qListTypeId(); } } // Sanity check property bindings and compile custom parsers if (!validateProperties()) return false; return true; }
// 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 ¶meters = 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. }
int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) { int id = _id; if (c == QMetaObject::WriteProperty && interceptors && !(*reinterpret_cast<int*>(a[3]) & QQmlPropertyPrivate::BypassInterceptor)) { for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) { if (vi->m_coreIndex != id) continue; int valueIndex = vi->m_valueTypeCoreIndex; int type = QQmlData::get(object)->propertyCache->property(id)->propType; if (type != QVariant::Invalid) { if (valueIndex != -1) { QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type); Q_ASSERT(valueType); // // Consider the following case: // color c = { 0.1, 0.2, 0.3 } // interceptor exists on c.r // write { 0.2, 0.4, 0.6 } // // The interceptor may choose not to update the r component at this // point (for example, a behavior that creates an animation). But we // need to ensure that the g and b components are updated correctly. // // So we need to perform a full write where the value type is: // r = old value, g = new value, b = new value // // And then call the interceptor which may or may not write the // new value to the r component. // // This will ensure that the other components don't contain stale data // and any relevant signals are emitted. // // To achieve this: // (1) Store the new value type as a whole (needed due to // aliasing between a[0] and static storage in value type). // (2) Read the entire existing value type from object -> valueType temp. // (3) Read the previous value of the component being changed // from the valueType temp. // (4) Write the entire new value type into the temp. // (5) Overwrite the component being changed with the old value. // (6) Perform a full write to the value type (which may emit signals etc). // (7) Issue the interceptor call with the new component value. // QMetaProperty valueProp = valueType->metaObject()->property(valueIndex); QVariant newValue(type, a[0]); valueType->read(object, id); QVariant prevComponentValue = valueProp.read(valueType); valueType->setValue(newValue); QVariant newComponentValue = valueProp.read(valueType); // Don't apply the interceptor if the intercepted value has not changed bool updated = false; if (newComponentValue != prevComponentValue) { valueProp.write(valueType, prevComponentValue); valueType->write(object, id, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); vi->write(newComponentValue); updated = true; } if (updated) return -1; } else { vi->write(QVariant(type, a[0])); return -1; } } } } if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty) { if (id >= propOffset()) { id -= propOffset(); if (id < metaData->propertyCount) { int t = (metaData->propertyData() + id)->propertyType; bool needActivate = false; if (id >= firstVarPropertyIndex) { Q_ASSERT(t == QMetaType::QVariant); // the context can be null if accessing var properties from cpp after re-parenting an item. QQmlEnginePrivate *ep = (ctxt == 0 || ctxt->engine == 0) ? 0 : QQmlEnginePrivate::get(ctxt->engine); QV8Engine *v8e = (ep == 0) ? 0 : ep->v8engine(); if (v8e) { if (c == QMetaObject::ReadProperty) { *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id); } else if (c == QMetaObject::WriteProperty) { writeProperty(id, *reinterpret_cast<QVariant *>(a[0])); } } else if (c == QMetaObject::ReadProperty) { // if the context was disposed, we just return an invalid variant from read. *reinterpret_cast<QVariant *>(a[0]) = QVariant(); } } else { if (c == QMetaObject::ReadProperty) { switch(t) { case QVariant::Int: *reinterpret_cast<int *>(a[0]) = data[id].asInt(); break; case QVariant::Bool: *reinterpret_cast<bool *>(a[0]) = data[id].asBool(); break; case QVariant::Double: *reinterpret_cast<double *>(a[0]) = data[id].asDouble(); break; case QVariant::String: *reinterpret_cast<QString *>(a[0]) = data[id].asQString(); break; case QVariant::Url: *reinterpret_cast<QUrl *>(a[0]) = data[id].asQUrl(); break; case QVariant::Date: *reinterpret_cast<QDate *>(a[0]) = data[id].asQDate(); break; case QVariant::DateTime: *reinterpret_cast<QDateTime *>(a[0]) = data[id].asQDateTime(); break; case QVariant::RectF: *reinterpret_cast<QRectF *>(a[0]) = data[id].asQRectF(); break; case QVariant::SizeF: *reinterpret_cast<QSizeF *>(a[0]) = data[id].asQSizeF(); break; case QVariant::PointF: *reinterpret_cast<QPointF *>(a[0]) = data[id].asQPointF(); break; case QMetaType::QObjectStar: *reinterpret_cast<QObject **>(a[0]) = data[id].asQObject(); break; case QMetaType::QVariant: *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id); break; default: QQml_valueTypeProvider()->readValueType(data[id].dataType(), data[id].dataPtr(), data->dataSize(), t, a[0]); break; } if (t == qMetaTypeId<QQmlListProperty<QObject> >()) { int listIndex = data[id].asInt(); const List *list = &listProperties.at(listIndex); *reinterpret_cast<QQmlListProperty<QObject> *>(a[0]) = QQmlListProperty<QObject>(object, (void *)list, list_append, list_count, list_at, list_clear); } } else if (c == QMetaObject::WriteProperty) { switch(t) { case QVariant::Int: needActivate = *reinterpret_cast<int *>(a[0]) != data[id].asInt(); data[id].setValue(*reinterpret_cast<int *>(a[0])); break; case QVariant::Bool: needActivate = *reinterpret_cast<bool *>(a[0]) != data[id].asBool(); data[id].setValue(*reinterpret_cast<bool *>(a[0])); break; case QVariant::Double: needActivate = *reinterpret_cast<double *>(a[0]) != data[id].asDouble(); data[id].setValue(*reinterpret_cast<double *>(a[0])); break; case QVariant::String: needActivate = *reinterpret_cast<QString *>(a[0]) != data[id].asQString(); data[id].setValue(*reinterpret_cast<QString *>(a[0])); break; case QVariant::Url: needActivate = *reinterpret_cast<QUrl *>(a[0]) != data[id].asQUrl(); data[id].setValue(*reinterpret_cast<QUrl *>(a[0])); break; case QVariant::Date: needActivate = *reinterpret_cast<QDate *>(a[0]) != data[id].asQDate(); data[id].setValue(*reinterpret_cast<QDate *>(a[0])); break; case QVariant::DateTime: needActivate = *reinterpret_cast<QDateTime *>(a[0]) != data[id].asQDateTime(); data[id].setValue(*reinterpret_cast<QDateTime *>(a[0])); break; case QVariant::RectF: needActivate = *reinterpret_cast<QRectF *>(a[0]) != data[id].asQRectF(); data[id].setValue(*reinterpret_cast<QRectF *>(a[0])); break; case QVariant::SizeF: needActivate = *reinterpret_cast<QSizeF *>(a[0]) != data[id].asQSizeF(); data[id].setValue(*reinterpret_cast<QSizeF *>(a[0])); break; case QVariant::PointF: needActivate = *reinterpret_cast<QPointF *>(a[0]) != data[id].asQPointF(); data[id].setValue(*reinterpret_cast<QPointF *>(a[0])); break; case QMetaType::QObjectStar: needActivate = *reinterpret_cast<QObject **>(a[0]) != data[id].asQObject(); data[id].setValue(*reinterpret_cast<QObject **>(a[0]), this, id); break; case QMetaType::QVariant: writeProperty(id, *reinterpret_cast<QVariant *>(a[0])); break; default: data[id].ensureValueType(t); needActivate = !QQml_valueTypeProvider()->equalValueType(t, a[0], data[id].dataPtr(), data[id].dataSize()); QQml_valueTypeProvider()->writeValueType(t, a[0], data[id].dataPtr(), data[id].dataSize()); break; } } } if (c == QMetaObject::WriteProperty && needActivate) { activate(object, methodOffset() + id, 0); } return -1; } id -= metaData->propertyCount; if (id < metaData->aliasCount) { QQmlVMEMetaData::AliasData *d = metaData->aliasData() + id; if (d->flags & QML_ALIAS_FLAG_PTR && c == QMetaObject::ReadProperty) *reinterpret_cast<void **>(a[0]) = 0; if (!ctxt) return -1; QQmlContext *context = ctxt->asQQmlContext(); QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context); QObject *target = ctxtPriv->data->idValues[d->contextIdx].data(); if (!target) return -1; connectAlias(id); if (d->isObjectAlias()) { *reinterpret_cast<QObject **>(a[0]) = target; return -1; } // Remove binding (if any) on write if(c == QMetaObject::WriteProperty) { int flags = *reinterpret_cast<int*>(a[3]); if (flags & QQmlPropertyPrivate::RemoveBindingOnAliasWrite) { QQmlData *targetData = QQmlData::get(target); if (targetData && targetData->hasBindingBit(d->propertyIndex())) { QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(target, d->propertyIndex(), d->isValueTypeAlias()?d->valueTypeIndex():-1, 0); if (binding) binding->destroy(); } } } if (d->isValueTypeAlias()) { // Value type property QQmlValueType *valueType = QQmlValueTypeFactory::valueType(d->valueType()); Q_ASSERT(valueType); valueType->read(target, d->propertyIndex()); int rv = QMetaObject::metacall(valueType, c, d->valueTypeIndex(), a); if (c == QMetaObject::WriteProperty) valueType->write(target, d->propertyIndex(), 0x00); return rv; } else { return QMetaObject::metacall(target, c, d->propertyIndex(), a); } } return -1; } } else if(c == QMetaObject::InvokeMetaMethod) { if (id >= methodOffset()) { id -= methodOffset(); int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; if (id < plainSignals) { activate(object, _id, a); return -1; } id -= plainSignals; if (id < metaData->methodCount) { if (!ctxt->engine) return -1; // We can't run the method QQmlEnginePrivate *ep = QQmlEnginePrivate::get(ctxt->engine); ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. QV4::Scope scope(ep->v4engine()); QV4::Scoped<QV4::FunctionObject> function(scope, method(id)); if (!function) { // The function was not compiled. There are some exceptional cases which the // expression rewriter does not rewrite properly (e.g., \r-terminated lines // are not rewritten correctly but this bug is deemed out-of-scope to fix for // performance reasons; see QTBUG-24064) and thus compilation will have failed. QQmlError e; e.setDescription(QString(QLatin1String("Exception occurred during compilation of function: %1")). arg(QLatin1String(QMetaObject::method(_id).methodSignature().constData()))); ep->warning(e); return -1; // The dynamic method with that id is not available. } QQmlVMEMetaData::MethodData *data = metaData->methodData() + id; QV4::ScopedCallData callData(scope, data->parameterCount); callData->thisObject = ep->v8engine()->global(); for (int ii = 0; ii < data->parameterCount; ++ii) callData->args[ii] = ep->v8engine()->fromVariant(*(QVariant *)a[ii + 1]); QV4::ScopedValue result(scope); QV4::ExecutionContext *ctx = function->engine()->currentContext(); result = function->call(callData); if (scope.hasException()) { QQmlError error = QV4::ExecutionEngine::catchExceptionAsQmlError(ctx); if (error.isValid()) ep->warning(error); if (a[0]) *(QVariant *)a[0] = QVariant(); } else { if (a[0]) *(QVariant *)a[0] = ep->v8engine()->toVariant(result, 0); } ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. return -1; } return -1; } } if (parent.isT1()) return parent.asT1()->metaCall(object, c, _id, a); else return object->qt_metacall(c, _id, a); }
void QQmlVMEMetaObject::ensureQObjectWrapper() { QQmlEnginePrivate *ep = (ctxt == 0 || ctxt->engine == 0) ? 0 : QQmlEnginePrivate::get(ctxt->engine); QV4::ExecutionEngine *v4 = (ep == 0) ? 0 : ep->v4engine(); QV4::QObjectWrapper::wrap(v4, object); }
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; }