diff options
Diffstat (limited to 'src/qml')
308 files changed, 10285 insertions, 14646 deletions
diff --git a/src/qml/Qt5QmlConfigExtras.cmake.in b/src/qml/Qt5QmlConfigExtras.cmake.in new file mode 100644 index 0000000000..9ddb9885cd --- /dev/null +++ b/src/qml/Qt5QmlConfigExtras.cmake.in @@ -0,0 +1,5 @@ +file(GLOB _qt5qml_other_plugins "${CMAKE_CURRENT_LIST_DIR}/Qt5Qml_*Factory.cmake") + +foreach(_other_plugin ${_qt5qml_other_plugins}) + include(${_other_plugin} OPTIONAL) +endforeach() diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h index 5a3e9d2025..14dbf85a3d 100644 --- a/src/qml/animations/qabstractanimationjob_p.h +++ b/src/qml/animations/qabstractanimationjob_p.h @@ -34,6 +34,17 @@ #ifndef QABSTRACTANIMATIONJOB_P_H #define QABSTRACTANIMATIONJOB_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <private/qtqmlglobal_p.h> #include <QtCore/QObject> #include <QtCore/private/qabstractanimation_p.h> diff --git a/src/qml/animations/qanimationgroupjob_p.h b/src/qml/animations/qanimationgroupjob_p.h index fa8fc08bfe..c97a18f089 100644 --- a/src/qml/animations/qanimationgroupjob_p.h +++ b/src/qml/animations/qanimationgroupjob_p.h @@ -34,6 +34,17 @@ #ifndef QANIMATIONGROUPJOB_P_H #define QANIMATIONGROUPJOB_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "private/qabstractanimationjob_p.h" QT_BEGIN_NAMESPACE diff --git a/src/qml/animations/qanimationjobutil_p.h b/src/qml/animations/qanimationjobutil_p.h index 3c38fc1599..d2aceb72d0 100644 --- a/src/qml/animations/qanimationjobutil_p.h +++ b/src/qml/animations/qanimationjobutil_p.h @@ -34,6 +34,17 @@ #ifndef QANIMATIONJOBUTIL_P_H #define QANIMATIONJOBUTIL_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #define RETURN_IF_DELETED(func) \ { \ bool *prevWasDeleted = m_wasDeleted; \ diff --git a/src/qml/animations/qcontinuinganimationgroupjob_p.h b/src/qml/animations/qcontinuinganimationgroupjob_p.h index 3df0e676a7..b9827ab936 100644 --- a/src/qml/animations/qcontinuinganimationgroupjob_p.h +++ b/src/qml/animations/qcontinuinganimationgroupjob_p.h @@ -34,6 +34,17 @@ #ifndef QCONTINUINGANIMATIONGROUPJOB_P_H #define QCONTINUINGANIMATIONGROUPJOB_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "private/qanimationgroupjob_p.h" QT_BEGIN_NAMESPACE diff --git a/src/qml/animations/qparallelanimationgroupjob_p.h b/src/qml/animations/qparallelanimationgroupjob_p.h index 1ac7709f21..83e5457cdd 100644 --- a/src/qml/animations/qparallelanimationgroupjob_p.h +++ b/src/qml/animations/qparallelanimationgroupjob_p.h @@ -34,6 +34,17 @@ #ifndef QPARALLELANIMATIONGROUPJOB_P_H #define QPARALLELANIMATIONGROUPJOB_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "private/qanimationgroupjob_p.h" QT_BEGIN_NAMESPACE diff --git a/src/qml/animations/qpauseanimationjob_p.h b/src/qml/animations/qpauseanimationjob_p.h index b909c71f49..725e9b62e9 100644 --- a/src/qml/animations/qpauseanimationjob_p.h +++ b/src/qml/animations/qpauseanimationjob_p.h @@ -34,6 +34,17 @@ #ifndef QPAUSEANIMATIONJOB_P_H #define QPAUSEANIMATIONJOB_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <private/qanimationgroupjob_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/animations/qsequentialanimationgroupjob_p.h b/src/qml/animations/qsequentialanimationgroupjob_p.h index 5286fda28a..dab086e851 100644 --- a/src/qml/animations/qsequentialanimationgroupjob_p.h +++ b/src/qml/animations/qsequentialanimationgroupjob_p.h @@ -34,6 +34,17 @@ #ifndef QSEQUENTIALANIMATIONGROUPJOB_P_H #define QSEQUENTIALANIMATIONGROUPJOB_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <private/qanimationgroupjob_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 63833504f1..16c4cb28ed 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -33,7 +33,7 @@ #include "qqmlirbuilder_p.h" -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4compileddata_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljslexer_p.h> @@ -1456,10 +1456,8 @@ JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR , _disableAcceleratedLookups(false) , _contextObject(0) , _scopeObject(0) - , _contextObjectTemp(-1) - , _scopeObjectTemp(-1) + , _qmlContextTemp(-1) , _importedScriptsTemp(-1) - , _idArrayTemp(-1) { _module = jsModule; _module->setFileName(fileName); @@ -1592,7 +1590,7 @@ static QV4::IR::Type resolveQmlType(QQmlEnginePrivate *qmlEngine, QV4::IR::Membe if (member->name->constData()->isUpper()) { bool ok = false; - int value = type->enumValue(*member->name, &ok); + int value = type->enumValue(qmlEngine, *member->name, &ok); if (ok) { member->setEnumValue(value); resolver->clear(); @@ -1617,10 +1615,10 @@ static QV4::IR::Type resolveQmlType(QQmlEnginePrivate *qmlEngine, QV4::IR::Membe member->kind = QV4::IR::Member::MemberOfSingletonObject; return resolver->resolveMember(qmlEngine, resolver, member); } - } else if (const QMetaObject *attachedMeta = type->attachedPropertiesType()) { + } else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) { QQmlPropertyCache *cache = qmlEngine->cache(attachedMeta); initMetaObjectResolver(resolver, cache); - member->setAttachedPropertiesId(type->attachedPropertiesId()); + member->setAttachedPropertiesId(type->attachedPropertiesId(qmlEngine)); return resolver->resolveMember(qmlEngine, resolver, member); } @@ -1695,7 +1693,8 @@ static QV4::IR::Type resolveMetaObjectProperty(QQmlEnginePrivate *qmlEngine, QV4 } } - if (qmlEngine && !(resolver->flags & LookupsExcludeProperties)) { + if (member->kind != QV4::IR::Member::MemberOfIdObjectsArray && member->kind != QV4::IR::Member::MemberOfSingletonObject && + qmlEngine && !(resolver->flags & LookupsExcludeProperties)) { QQmlPropertyData *property = member->property; if (!property && metaObject) { if (QQmlPropertyData *candidate = metaObject->property(*member->name, /*object*/0, /*context*/0)) { @@ -1764,24 +1763,14 @@ static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, void JSCodeGen::beginFunctionBodyHook() { - _contextObjectTemp = _block->newTemp(); - _scopeObjectTemp = _block->newTemp(); + _qmlContextTemp = _block->newTemp(); _importedScriptsTemp = _block->newTemp(); - _idArrayTemp = _block->newTemp(); #ifndef V4_BOOTSTRAP - QV4::IR::Temp *temp = _block->TEMP(_contextObjectTemp); - temp->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - initMetaObjectResolver(temp->memberResolver, _contextObject); - move(temp, _block->NAME(QV4::IR::Name::builtin_qml_context_object, 0, 0)); - - temp = _block->TEMP(_scopeObjectTemp); - temp->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - initMetaObjectResolver(temp->memberResolver, _scopeObject); - move(temp, _block->NAME(QV4::IR::Name::builtin_qml_scope_object, 0, 0)); + QV4::IR::Temp *temp = _block->TEMP(_qmlContextTemp); + move(temp, _block->NAME(QV4::IR::Name::builtin_qml_context, 0, 0)); move(_block->TEMP(_importedScriptsTemp), _block->NAME(QV4::IR::Name::builtin_qml_imported_scripts_object, 0, 0)); - move(_block->TEMP(_idArrayTemp), _block->NAME(QV4::IR::Name::builtin_qml_id_array, 0, 0)); #endif } @@ -1807,7 +1796,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int foreach (const IdMapping &mapping, _idObjects) if (name == mapping.name) { _function->idObjectDependencies.insert(mapping.idIndex); - QV4::IR::Expr *s = subscript(_block->TEMP(_idArrayTemp), _block->CONST(QV4::IR::SInt32Type, mapping.idIndex)); + QV4::IR::Expr *s = _block->MEMBER(_block->TEMP(_qmlContextTemp), _function->newString(name), 0, QV4::IR::Member::MemberOfIdObjectsArray, mapping.idIndex); QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); _block->MOVE(result, s); result = _block->TEMP(result->index); @@ -1857,7 +1846,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int if (propertyExistsButForceNameLookup) return 0; if (pd) { - QV4::IR::Temp *base = _block->TEMP(_scopeObjectTemp); + QV4::IR::Temp *base = _block->TEMP(_qmlContextTemp); base->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); initMetaObjectResolver(base->memberResolver, _scopeObject); return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlScopeObject); @@ -1870,7 +1859,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int if (propertyExistsButForceNameLookup) return 0; if (pd) { - QV4::IR::Temp *base = _block->TEMP(_contextObjectTemp); + QV4::IR::Temp *base = _block->TEMP(_qmlContextTemp); base->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); initMetaObjectResolver(base->memberResolver, _contextObject); return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlContextObject); diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 120de91321..9a659f4d72 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -33,8 +33,18 @@ #ifndef QQMLIRBUILDER_P_H #define QQMLIRBUILDER_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <private/qqmljsast_p.h> -#include <private/qqmlpool_p.h> #include <private/qqmljsengine_p.h> #include <private/qv4compiler_p.h> #include <private/qv4compileddata_p.h> @@ -496,10 +506,8 @@ private: ObjectIdMapping _idObjects; QQmlPropertyCache *_contextObject; QQmlPropertyCache *_scopeObject; - int _contextObjectTemp; - int _scopeObjectTemp; + int _qmlContextTemp; int _importedScriptsTemp; - int _idArrayTemp; }; } // namespace QmlIR diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 4e9817aa0d..cde7a2acb4 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -104,11 +104,10 @@ bool QQmlTypeCompiler::compile() } if (ref->type->containsRevisionedAttributes()) { - QQmlError cacheError; ref->typePropertyCache = engine->cache(ref->type, - resolvedType->minorVersion, - cacheError); + resolvedType->minorVersion); if (!ref->typePropertyCache) { + QQmlError cacheError; cacheError.setColumn(resolvedType->location.column); cacheError.setLine(resolvedType->location.line); recordError(cacheError); @@ -521,7 +520,24 @@ bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int r } else if (instantiatingBinding && instantiatingBinding->isAttachedProperty()) { QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(instantiatingBinding->propertyNameIndex); Q_ASSERT(typeRef); - const QMetaObject *attachedMo = typeRef->type ? typeRef->type->attachedPropertiesType() : 0; + QQmlType *qmltype = typeRef->type; + if (!qmltype) { + QString propertyName = stringAt(instantiatingBinding->propertyNameIndex); + if (imports->resolveType(propertyName, &qmltype, 0, 0, 0)) { + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + QQmlCompiledData *data = tdata->compiledData(); + qmltype = QQmlMetaType::qmlType(data->metaTypeId); + + tdata->release(); + } + } + } + + const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0; if (!attachedMo) { recordError(instantiatingBinding->location, tr("Non-existent attached object")); return false; @@ -668,17 +684,14 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob } // First set up notify signals for properties - first normal, then var, then alias - enum { NSS_Normal = 0, NSS_Var = 1, NSS_Alias = 2 }; + enum { NSS_Normal = 0, NSS_Alias = 1 }; for (int ii = NSS_Normal; ii <= NSS_Alias; ++ii) { // 0 == normal, 1 == var, 2 == alias - if (ii == NSS_Var && varPropCount == 0) continue; - else if (ii == NSS_Alias && aliasCount == 0) continue; + if (ii == NSS_Alias && aliasCount == 0) continue; for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) { - if ((ii == NSS_Normal && (p->type == QV4::CompiledData::Property::Alias || - p->type == QV4::CompiledData::Property::Var)) || - ((ii == NSS_Var) && (p->type != QV4::CompiledData::Property::Var)) || - ((ii == NSS_Alias) && (p->type != QV4::CompiledData::Property::Alias))) + if ((ii == NSS_Normal && p->type == QV4::CompiledData::Property::Alias) || + (ii == NSS_Alias && p->type != QV4::CompiledData::Property::Alias)) continue; quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | @@ -696,6 +709,7 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob const int paramCount = s->parameters->count; QList<QByteArray> names; + names.reserve(paramCount); QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0); if (paramCount) { @@ -780,15 +794,18 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob int propertyIdx = 0; for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next, ++propertyIdx) { - if (p->type == QV4::CompiledData::Property::Alias || - p->type == QV4::CompiledData::Property::Var) + if (p->type == QV4::CompiledData::Property::Alias) continue; int propertyType = 0; int vmePropertyType = 0; quint32 propertyFlags = 0; - if (p->type < builtinTypeCount) { + if (p->type == QV4::CompiledData::Property::Var) { + propertyType = QMetaType::QVariant; + vmePropertyType = QQmlVMEMetaData::VarPropertyType; + propertyFlags = QQmlPropertyData::IsVarProperty; + } else if (p->type < builtinTypeCount) { propertyType = builtinTypes[p->type].metaType; vmePropertyType = propertyType; @@ -852,30 +869,6 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob vmd->propertyCount++; } - // Now do var properties - propertyIdx = 0; - for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next, ++propertyIdx) { - - if (p->type != QV4::CompiledData::Property::Var) - continue; - - quint32 propertyFlags = QQmlPropertyData::IsVarProperty; - if (!(p->flags & QV4::CompiledData::Property::IsReadOnly)) - propertyFlags |= QQmlPropertyData::IsWritable; - - VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); - (vmd->propertyData() + vmd->propertyCount)->propertyType = QMetaType::QVariant; - vmd->propertyCount++; - ((QQmlVMEMetaData *)dynamicData.data())->varPropertyCount++; - - QString propertyName = stringAt(p->nameIndex); - if (propertyIdx == obj->indexOfDefaultProperty) cache->_defaultPropertyName = propertyName; - cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - QMetaType::QVariant, effectiveSignalIndex); - - effectiveSignalIndex++; - } - // Alias property count. Actual data is setup in buildDynamicMetaAliases ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount; @@ -904,7 +897,9 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler) : QQmlCompilePass(typeCompiler) + , enginePrivate(typeCompiler->enginePrivate()) , qmlObjects(*typeCompiler->qmlObjects()) + , imports(typeCompiler->imports()) , customParsers(typeCompiler->customParserCache()) , resolvedTypes(*typeCompiler->resolvedTypes()) , illegalNames(QV8Engine::get(QQmlEnginePrivate::get(typeCompiler->enginePrivate()))->illegalNames()) @@ -942,7 +937,22 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex); QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(binding->propertyNameIndex); QQmlType *type = typeRef ? typeRef->type : 0; - const QMetaObject *attachedType = type ? type->attachedPropertiesType() : 0; + if (!type) { + if (imports->resolveType(propertyName, &type, 0, 0, 0)) { + if (type->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(type->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + QQmlCompiledData *data = tdata->compiledData(); + type = QQmlMetaType::qmlType(data->metaTypeId); + + tdata->release(); + } + } + } + + const QMetaObject *attachedType = type ? type->attachedPropertiesType(enginePrivate) : 0; if (!attachedType) COMPILE_EXCEPTION(binding, tr("Non-existent attached object")); QQmlPropertyCache *cache = compiler->enginePrivate()->cache(attachedType); @@ -1022,10 +1032,10 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio } } - QHash<QString, QStringList>::ConstIterator entry = customSignals.find(propertyName); + QHash<QString, QStringList>::ConstIterator entry = customSignals.constFind(propertyName); if (entry == customSignals.constEnd() && propertyName.endsWith(QStringLiteral("Changed"))) { QString alternateName = propertyName.mid(0, propertyName.length() - static_cast<int>(strlen("Changed"))); - entry = customSignals.find(alternateName); + entry = customSignals.constFind(alternateName); } if (entry == customSignals.constEnd()) { @@ -1067,16 +1077,29 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio paramList = paramList->finish(); QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex); - QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node); - QQmlJS::AST::SourceElement *sourceElement = new (pool) QQmlJS::AST::StatementSourceElement(statement); - QQmlJS::AST::SourceElements *elements = new (pool) QQmlJS::AST::SourceElements(sourceElement); - elements = elements->finish(); - - QQmlJS::AST::FunctionBody *body = new (pool) QQmlJS::AST::FunctionBody(elements); + QQmlJS::AST::FunctionDeclaration *functionDeclaration = 0; + if (QQmlJS::AST::ExpressionStatement *es = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(foe->node)) { + if (QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression*>(es->expression)) { + functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(fe->name, fe->formals, fe->body); + functionDeclaration->functionToken = fe->functionToken; + functionDeclaration->identifierToken = fe->identifierToken; + functionDeclaration->lparenToken = fe->lparenToken; + functionDeclaration->rparenToken = fe->rparenToken; + functionDeclaration->lbraceToken = fe->lbraceToken; + functionDeclaration->rbraceToken = fe->rbraceToken; + } + } + if (!functionDeclaration) { + QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node); + QQmlJS::AST::SourceElement *sourceElement = new (pool) QQmlJS::AST::StatementSourceElement(statement); + QQmlJS::AST::SourceElements *elements = new (pool) QQmlJS::AST::SourceElements(sourceElement); + elements = elements->finish(); - QQmlJS::AST::FunctionDeclaration *functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body); - functionDeclaration->functionToken = foe->node->firstSourceLocation(); + QQmlJS::AST::FunctionBody *body = new (pool) QQmlJS::AST::FunctionBody(elements); + functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body); + functionDeclaration->functionToken = foe->node->firstSourceLocation(); + } foe->node = functionDeclaration; binding->propertyNameIndex = compiler->registerString(propertyName); binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression; @@ -1174,8 +1197,6 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, if (!type && typeName != QLatin1String("Qt")) return true; - if (type && type->isComposite()) //No enums on composite (or composite singleton) types - return true; int value = 0; bool ok = false; @@ -1193,7 +1214,7 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, } else { // Otherwise we have to search the whole type if (type) { - value = type->enumValue(QHashedStringRef(enumValue), &ok); + value = type->enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok); } else { QByteArray enumName = enumValue.toUtf8(); const QMetaObject *metaObject = StaticQtMetaObject::get(); @@ -1221,7 +1242,9 @@ int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QByteArray &e if (scope != QLatin1String("Qt")) { QQmlType *type = 0; imports->resolveType(scope, &type, 0, 0, 0); - return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; + if (!type) + return -1; + return type ? type->enumValue(compiler->enginePrivate(), QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; } const QMetaObject *mo = StaticQtMetaObject::get(); @@ -1730,17 +1753,6 @@ const QQmlImports &QQmlPropertyValidator::imports() const return *compiler->imports(); } -QString QQmlPropertyValidator::bindingAsString(int objectIndex, const QV4::CompiledData::Binding *binding) const -{ - const QmlIR::Object *object = compiler->qmlObjects()->value(objectIndex); - if (!object) - return QString(); - int reverseIndex = object->runtimeFunctionIndices->indexOf(binding->value.compiledScriptIndex); - if (reverseIndex == -1) - return QString(); - return compiler->bindingAsString(object, reverseIndex); -} - typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector; struct BindingFinder @@ -2009,15 +2021,17 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD if (customParser && !customBindings.isEmpty()) { customParser->clearErrors(); - customParser->compiler = this; + customParser->validator = this; + customParser->engine = enginePrivate; customParser->imports = compiler->imports(); customParser->verifyBindings(qmlUnit, customBindings); - customParser->compiler = 0; + customParser->validator = 0; + customParser->engine = 0; customParser->imports = (QQmlImports*)0; customParserBindingsPerObject->insert(objectIndex, customParserBindings); const QList<QQmlError> parserErrors = customParser->errors(); if (!parserErrors.isEmpty()) { - foreach (QQmlError error, parserErrors) + foreach (const QQmlError &error, parserErrors) compiler->recordError(error); return false; } @@ -2623,10 +2637,8 @@ void QQmlJavaScriptBindingExpressionSimplificationPass::visitMove(QV4::IR::Move } if (QV4::IR::Name *n = move->source->asName()) { - if (n->builtin == QV4::IR::Name::builtin_qml_id_array - || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object - || n->builtin == QV4::IR::Name::builtin_qml_context_object - || n->builtin == QV4::IR::Name::builtin_qml_scope_object) { + if (n->builtin == QV4::IR::Name::builtin_qml_context + || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object) { // these are free of side-effects return; } diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 75987af656..c5be92d256 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -33,6 +33,17 @@ #ifndef QQMLTYPECOMPILER_P_H #define QQMLTYPECOMPILER_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <qglobal.h> #include <qqmlerror.h> #include <qhash.h> @@ -158,7 +169,9 @@ public: private: bool convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache); + QQmlEnginePrivate *enginePrivate; const QList<QmlIR::Object*> &qmlObjects; + const QQmlImports *imports; const QHash<int, QQmlCustomParser*> &customParsers; const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes; const QSet<QString> &illegalNames; @@ -264,7 +277,7 @@ protected: QHash<int, QHash<int, int> > *objectIndexToIdPerComponent; }; -class QQmlPropertyValidator : public QQmlCompilePass, public QQmlCustomParserCompilerBackend +class QQmlPropertyValidator : public QQmlCompilePass { Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) public: @@ -272,9 +285,8 @@ public: bool validate(); - // Re-implemented for QQmlCustomParser - virtual const QQmlImports &imports() const; - virtual QString bindingAsString(int objectIndex, const QV4::CompiledData::Binding *binding) const; + const QQmlImports &imports() const; + QQmlEnginePrivate *engine() const { return enginePrivate; } private: bool validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const; diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 9168889c8c..ea82d07e69 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -33,6 +33,7 @@ #include "qv4codegen_p.h" #include "qv4util_p.h" +#include "qv4engine_p.h" #include <QtCore/QCoreApplication> #include <QtCore/QStringList> @@ -43,7 +44,7 @@ #include <QtCore/QStack> #include <private/qqmljsast_p.h> #include <private/qv4string_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #ifndef V4_BOOTSTRAP #include <qv4context_p.h> diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index aec51cc19c..a7b0b06fe2 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -33,6 +33,17 @@ #ifndef QV4CODEGEN_P_H #define QV4CODEGEN_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "private/qv4global_p.h" #include "qv4jsir_p.h" #include <private/qqmljsastvisitor_p.h> diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 5d954eb4fc..20db5edaa3 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -33,7 +33,7 @@ #include "qv4compileddata_p.h" #include "qv4jsir_p.h" -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #ifndef V4_BOOTSTRAP #include <private/qv4engine_p.h> #include <private/qv4function_p.h> diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 48324fbbc4..0d6e4b15a7 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -33,6 +33,17 @@ #ifndef QV4COMPILEDDATA_P_H #define QV4COMPILEDDATA_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QtCore/qstring.h> #include <QVector> #include <QStringList> diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 450889c275..ba4bde7a31 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -35,7 +35,7 @@ #include <qv4compileddata_p.h> #include <qv4isel_p.h> #include <private/qv4string_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> QV4::Compiler::StringTableGenerator::StringTableGenerator() { @@ -44,8 +44,8 @@ QV4::Compiler::StringTableGenerator::StringTableGenerator() int QV4::Compiler::StringTableGenerator::registerString(const QString &str) { - QHash<QString, int>::ConstIterator it = stringToId.find(str); - if (it != stringToId.end()) + QHash<QString, int>::ConstIterator it = stringToId.constFind(str); + if (it != stringToId.cend()) return *it; stringToId.insert(str, strings.size()); strings.append(str); @@ -169,6 +169,7 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, IR::ExprList *arg // ### re-use existing class definitions. QList<CompiledData::JSClassMember> members; + members.reserve(count); IR::ExprList *it = args; for (int i = 0; i < count; ++i, it = it->next) { diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 3cf80a9791..d999a93f4f 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -33,6 +33,17 @@ #ifndef QV4COMPILER_P_H #define QV4COMPILER_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QtCore/qstring.h> #include "qv4jsir_p.h" diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 5c2ad45da2..97aee80e91 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -34,6 +34,17 @@ #ifndef QV4INSTR_MOTH_P_H #define QV4INSTR_MOTH_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QtCore/qglobal.h> #include <private/qv4value_p.h> #include <private/qv4function_p.h> @@ -64,12 +75,19 @@ QT_BEGIN_NAMESPACE F(SetLookup, setLookup) \ F(StoreQObjectProperty, storeQObjectProperty) \ F(LoadQObjectProperty, loadQObjectProperty) \ + F(StoreScopeObjectProperty, storeScopeObjectProperty) \ + F(StoreContextObjectProperty, storeContextObjectProperty) \ + F(LoadScopeObjectProperty, loadScopeObjectProperty) \ + F(LoadContextObjectProperty, loadContextObjectProperty) \ + F(LoadIdObject, loadIdObject) \ F(LoadAttachedQObjectProperty, loadAttachedQObjectProperty) \ F(LoadSingletonQObjectProperty, loadQObjectProperty) \ F(Push, push) \ F(CallValue, callValue) \ F(CallProperty, callProperty) \ F(CallPropertyLookup, callPropertyLookup) \ + F(CallScopeObjectProperty, callScopeObjectProperty) \ + F(CallContextObjectProperty, callContextObjectProperty) \ F(CallElement, callElement) \ F(CallActivationProperty, callActivationProperty) \ F(CallGlobalLookup, callGlobalLookup) \ @@ -84,6 +102,8 @@ QT_BEGIN_NAMESPACE F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ F(CallBuiltinDeleteName, callBuiltinDeleteName) \ + F(CallBuiltinTypeofScopeObjectProperty, callBuiltinTypeofScopeObjectProperty) \ + F(CallBuiltinTypeofContextObjectProperty, callBuiltinTypeofContextObjectProperty) \ F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \ F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ F(CallBuiltinTypeofName, callBuiltinTypeofName) \ @@ -125,10 +145,8 @@ QT_BEGIN_NAMESPACE F(Sub, sub) \ F(BinopContext, binopContext) \ F(LoadThis, loadThis) \ - F(LoadQmlIdArray, loadQmlIdArray) \ + F(LoadQmlContext, loadQmlContext) \ F(LoadQmlImportedScripts, loadQmlImportedScripts) \ - F(LoadQmlContextObject, loadQmlContextObject) \ - F(LoadQmlScopeObject, loadQmlScopeObject) \ F(LoadQmlSingleton, loadQmlSingleton) #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) @@ -293,6 +311,24 @@ union Instr Param base; Param result; }; + struct instr_loadScopeObjectProperty { + MOTH_INSTR_HEADER + int propertyIndex; + Param base; + Param result; + }; + struct instr_loadContextObjectProperty { + MOTH_INSTR_HEADER + int propertyIndex; + Param base; + Param result; + }; + struct instr_loadIdObject { + MOTH_INSTR_HEADER + int index; + Param base; + Param result; + }; struct instr_loadQObjectProperty { MOTH_INSTR_HEADER int propertyIndex; @@ -318,6 +354,18 @@ union Instr Param base; Param source; }; + struct instr_storeScopeObjectProperty { + MOTH_INSTR_HEADER + Param base; + int propertyIndex; + Param source; + }; + struct instr_storeContextObjectProperty { + MOTH_INSTR_HEADER + Param base; + int propertyIndex; + Param source; + }; struct instr_storeQObjectProperty { MOTH_INSTR_HEADER Param base; @@ -377,6 +425,22 @@ union Instr Param base; Param result; }; + struct instr_callScopeObjectProperty { + MOTH_INSTR_HEADER + int index; + quint32 argc; + quint32 callData; + Param base; + Param result; + }; + struct instr_callContextObjectProperty { + MOTH_INSTR_HEADER + int index; + quint32 argc; + quint32 callData; + Param base; + Param result; + }; struct instr_callElement { MOTH_INSTR_HEADER Param base; @@ -449,6 +513,18 @@ union Instr int name; Param result; }; + struct instr_callBuiltinTypeofScopeObjectProperty { + MOTH_INSTR_HEADER + int index; + Param base; + Param result; + }; + struct instr_callBuiltinTypeofContextObjectProperty { + MOTH_INSTR_HEADER + int index; + Param base; + Param result; + }; struct instr_callBuiltinTypeofMember { MOTH_INSTR_HEADER int member; @@ -684,7 +760,7 @@ union Instr MOTH_INSTR_HEADER Param result; }; - struct instr_loadQmlIdArray { + struct instr_loadQmlContext { MOTH_INSTR_HEADER Param result; }; @@ -692,14 +768,6 @@ union Instr MOTH_INSTR_HEADER Param result; }; - struct instr_loadQmlContextObject { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_loadQmlScopeObject { - MOTH_INSTR_HEADER - Param result; - }; struct instr_loadQmlSingleton { MOTH_INSTR_HEADER Param result; @@ -725,15 +793,22 @@ union Instr instr_storeElementLookup storeElementLookup; instr_loadProperty loadProperty; instr_getLookup getLookup; + instr_loadScopeObjectProperty loadScopeObjectProperty; + instr_loadContextObjectProperty loadContextObjectProperty; + instr_loadIdObject loadIdObject; instr_loadQObjectProperty loadQObjectProperty; instr_loadAttachedQObjectProperty loadAttachedQObjectProperty; instr_storeProperty storeProperty; instr_setLookup setLookup; + instr_storeScopeObjectProperty storeScopeObjectProperty; + instr_storeContextObjectProperty storeContextObjectProperty; instr_storeQObjectProperty storeQObjectProperty; instr_push push; instr_callValue callValue; instr_callProperty callProperty; instr_callPropertyLookup callPropertyLookup; + instr_callScopeObjectProperty callScopeObjectProperty; + instr_callContextObjectProperty callContextObjectProperty; instr_callElement callElement; instr_callActivationProperty callActivationProperty; instr_callGlobalLookup callGlobalLookup; @@ -748,6 +823,8 @@ union Instr instr_callBuiltinDeleteMember callBuiltinDeleteMember; instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; instr_callBuiltinDeleteName callBuiltinDeleteName; + instr_callBuiltinTypeofScopeObjectProperty callBuiltinTypeofScopeObjectProperty; + instr_callBuiltinTypeofContextObjectProperty callBuiltinTypeofContextObjectProperty; instr_callBuiltinTypeofMember callBuiltinTypeofMember; instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; instr_callBuiltinTypeofName callBuiltinTypeofName; @@ -789,10 +866,8 @@ union Instr instr_sub sub; instr_binopContext binopContext; instr_loadThis loadThis; - instr_loadQmlIdArray loadQmlIdArray; + instr_loadQmlContext loadQmlContext; instr_loadQmlImportedScripts loadQmlImportedScripts; - instr_loadQmlContextObject loadQmlContextObject; - instr_loadQmlScopeObject loadQmlScopeObject; instr_loadQmlSingleton loadQmlSingleton; static int size(Type type); diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index eb78a0c054..afb36c5f14 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -350,7 +350,7 @@ void InstructionSelection::run(int functionIndex) opt.run(qmlEngine, useTypeInference, /*peelLoops =*/ false); if (opt.isInSSA()) { static const bool doStackSlotAllocation = - qgetenv("QV4_NO_INTERPRETER_STACK_SLOT_ALLOCATION").isEmpty(); + qEnvironmentVariableIsEmpty("QV4_NO_INTERPRETER_STACK_SLOT_ALLOCATION"); if (doStackSlotAllocation) { AllocateStackSlots(opt.lifeTimeIntervals()).forFunction(_function); @@ -447,7 +447,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backend foreach (IR::Function *irFunction, irModule->functions) compilationUnit->codeRefs[i++] = codeRefs[irFunction]; QQmlRefPointer<QV4::CompiledData::CompilationUnit> result; - result.take(compilationUnit.take()); + result.adopt(compilationUnit.take()); return result; } @@ -461,6 +461,29 @@ void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Ex addInstruction(call); } +void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) +{ + if (kind == IR::Member::MemberOfQmlScopeObject) { + Instruction::CallScopeObjectProperty call; + call.base = getParam(base); + call.index = propertyIndex; + prepareCallArgs(args, call.argc); + call.callData = callDataStart(); + call.result = getResultParam(result); + addInstruction(call); + } else if (kind == IR::Member::MemberOfQmlContextObject) { + Instruction::CallContextObjectProperty call; + call.base = getParam(base); + call.index = propertyIndex; + prepareCallArgs(args, call.argc); + call.callData = callDataStart(); + call.result = getResultParam(result); + addInstruction(call); + } else { + Q_ASSERT(false); + } +} + void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) { @@ -565,9 +588,9 @@ void InstructionSelection::loadThisObject(IR::Expr *e) addInstruction(load); } -void InstructionSelection::loadQmlIdArray(IR::Expr *e) +void InstructionSelection::loadQmlContext(IR::Expr *e) { - Instruction::LoadQmlIdArray load; + Instruction::LoadQmlContext load; load.result = getResultParam(e); addInstruction(load); } @@ -579,20 +602,6 @@ void InstructionSelection::loadQmlImportedScripts(IR::Expr *e) addInstruction(load); } -void InstructionSelection::loadQmlContextObject(IR::Expr *e) -{ - Instruction::LoadQmlContextObject load; - load.result = getResultParam(e); - addInstruction(load); -} - -void InstructionSelection::loadQmlScopeObject(IR::Expr *e) -{ - Instruction::LoadQmlScopeObject load; - load.result = getResultParam(e); - addInstruction(load); -} - void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *e) { Instruction::LoadQmlSingleton load; @@ -694,6 +703,25 @@ void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase, addInstruction(store); } +void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) +{ + if (kind == IR::Member::MemberOfQmlScopeObject) { + Instruction::StoreScopeObjectProperty store; + store.base = getParam(targetBase); + store.propertyIndex = propertyIndex; + store.source = getParam(source); + addInstruction(store); + } else if (kind == IR::Member::MemberOfQmlContextObject) { + Instruction::StoreContextObjectProperty store; + store.base = getParam(targetBase); + store.propertyIndex = propertyIndex; + store.source = getParam(source); + addInstruction(store); + } else { + Q_ASSERT(false); + } +} + void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) { Instruction::StoreQObjectProperty store; @@ -703,6 +731,31 @@ void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *target addInstruction(store); } +void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target) +{ + if (kind == IR::Member::MemberOfQmlScopeObject) { + Instruction::LoadScopeObjectProperty load; + load.base = getParam(source); + load.propertyIndex = index; + load.result = getResultParam(target); + addInstruction(load); + } else if (kind == IR::Member::MemberOfQmlContextObject) { + Instruction::LoadContextObjectProperty load; + load.base = getParam(source); + load.propertyIndex = index; + load.result = getResultParam(target); + addInstruction(load); + } else if (kind == IR::Member::MemberOfIdObjectsArray) { + Instruction::LoadIdObject load; + load.base = getParam(source); + load.index = index; + load.result = getResultParam(target); + addInstruction(load); + } else { + Q_ASSERT(false); + } +} + void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) { if (attachedPropertiesId != 0) { @@ -1124,6 +1177,25 @@ void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args addInstruction(call); } +void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) +{ + if (kind == IR::Member::MemberOfQmlScopeObject) { + Instruction::CallBuiltinTypeofScopeObjectProperty call; + call.base = getParam(base); + call.index = propertyIndex; + call.result = getResultParam(result); + addInstruction(call); + } else if (kind == IR::Member::MemberOfQmlContextObject) { + Instruction::CallBuiltinTypeofContextObjectProperty call; + call.base = getParam(base); + call.index = propertyIndex; + call.result = getResultParam(result); + addInstruction(call); + } else { + Q_UNREACHABLE(); + } +} + void InstructionSelection::callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) { @@ -1444,7 +1516,7 @@ ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &in void InstructionSelection::patchJumpAddresses() { typedef QHash<IR::BasicBlock *, QVector<ptrdiff_t> >::ConstIterator PatchIt; - for (PatchIt i = _patches.begin(), ei = _patches.end(); i != ei; ++i) { + for (PatchIt i = _patches.cbegin(), ei = _patches.cend(); i != ei; ++i) { Q_ASSERT(_addrs.contains(i.key())); ptrdiff_t target = _addrs.value(i.key()); diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index 4ea0f1d07f..e2385aad6d 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -34,6 +34,17 @@ #ifndef QV4ISEL_MOTH_P_H #define QV4ISEL_MOTH_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <private/qv4global_p.h> #include <private/qv4isel_p.h> #include <private/qv4isel_util_p.h> @@ -73,6 +84,7 @@ protected: virtual void visitRet(IR::Ret *); virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result); + virtual void callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result); virtual void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result); virtual void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result); virtual void callBuiltinTypeofName(const QString &name, IR::Expr *result); @@ -95,6 +107,7 @@ protected: virtual void callBuiltinSetupArgumentObject(IR::Expr *result); virtual void callBuiltinConvertThisToObject(); virtual void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result); + virtual void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result); virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result); virtual void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result); virtual void convertType(IR::Expr *source, IR::Expr *target); @@ -102,10 +115,8 @@ protected: virtual void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result); virtual void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result); virtual void loadThisObject(IR::Expr *e); - virtual void loadQmlIdArray(IR::Expr *e); + virtual void loadQmlContext(IR::Expr *e); virtual void loadQmlImportedScripts(IR::Expr *e); - virtual void loadQmlContextObject(IR::Expr *e); - virtual void loadQmlScopeObject(IR::Expr *e); virtual void loadQmlSingleton(const QString &name, IR::Expr *e); virtual void loadConst(IR::Const *sourceConst, IR::Expr *e); virtual void loadString(const QString &str, IR::Expr *target); @@ -115,7 +126,9 @@ protected: virtual void initClosure(IR::Closure *closure, IR::Expr *target); virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target); virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName); + virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex); virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex); + virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target); virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target); virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target); virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex); diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 54b184b4eb..184cff43e6 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -36,7 +36,7 @@ #include "qv4jsir_p.h" #include "qv4isel_p.h" #include "qv4isel_util_p.h" -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #ifndef V4_BOOTSTRAP #include <private/qqmlpropertycache_p.h> #endif @@ -91,12 +91,8 @@ void IRDecoder::visitMove(IR::Move *s) if (IR::Name *n = s->source->asName()) { if (n->id && *n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. loadThisObject(s->target); - else if (n->builtin == IR::Name::builtin_qml_id_array) - loadQmlIdArray(s->target); - else if (n->builtin == IR::Name::builtin_qml_context_object) - loadQmlContextObject(s->target); - else if (n->builtin == IR::Name::builtin_qml_scope_object) - loadQmlScopeObject(s->target); + else if (n->builtin == IR::Name::builtin_qml_context) + loadQmlContext(s->target); else if (n->builtin == IR::Name::builtin_qml_imported_scripts_object) loadQmlImportedScripts(s->target); else if (n->qmlSingleton) @@ -140,8 +136,8 @@ void IRDecoder::visitMove(IR::Move *s) #else bool captureRequired = true; - Q_ASSERT(m->kind != IR::Member::MemberOfEnum); - const int attachedPropertiesId = m->attachedPropertiesIdOrEnumValue; + Q_ASSERT(m->kind != IR::Member::MemberOfEnum && m->kind != IR::Member::MemberOfIdObjectsArray); + const int attachedPropertiesId = m->attachedPropertiesId; const bool isSingletonProperty = m->kind == IR::Member::MemberOfSingletonObject; if (_function && attachedPropertiesId == 0 && !m->property->isConstant()) { @@ -153,9 +149,16 @@ void IRDecoder::visitMove(IR::Move *s) captureRequired = false; } } + if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) { + getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex, s->target); + return; + } getQObjectProperty(m->base, m->property->coreIndex, captureRequired, isSingletonProperty, attachedPropertiesId, s->target); #endif // V4_BOOTSTRAP return; + } else if (m->kind == IR::Member::MemberOfIdObjectsArray) { + getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->idIndex, s->target); + return; } else if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) { getProperty(m->base, *m->name, s->target); return; @@ -174,6 +177,13 @@ void IRDecoder::visitMove(IR::Move *s) callBuiltin(c, s->target); return; } else if (Member *member = c->base->asMember()) { +#ifndef V4_BOOTSTRAP + Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray); + if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { + callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex, c->args, s->target); + return; + } +#endif callProperty(member->base, *member->name, c->args, s->target); return; } else if (Subscript *ss = c->base->asSubscript()) { @@ -192,11 +202,16 @@ void IRDecoder::visitMove(IR::Move *s) if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) { if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) { Q_ASSERT(m->kind != IR::Member::MemberOfEnum); - const int attachedPropertiesId = m->attachedPropertiesIdOrEnumValue; + Q_ASSERT(m->kind != IR::Member::MemberOfIdObjectsArray); + const int attachedPropertiesId = m->attachedPropertiesId; if (m->property && attachedPropertiesId == 0) { #ifdef V4_BOOTSTRAP Q_UNIMPLEMENTED(); #else + if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) { + setQmlContextProperty(s->source, m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex); + return; + } setQObjectProperty(s->source, m->base, m->property->coreIndex); #endif return; @@ -238,6 +253,13 @@ void IRDecoder::visitExp(IR::Exp *s) callValue(c->base, c->args, 0); } else if (Member *member = c->base->asMember()) { Q_ASSERT(member->base->asTemp() || member->base->asArgLocal()); +#ifndef V4_BOOTSTRAP + Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray); + if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { + callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex, c->args, 0); + return; + } +#endif callProperty(member->base, *member->name, c->args, 0); } else if (Subscript *s = c->base->asSubscript()) { callSubscript(s->base, s->index, c->args, 0); @@ -260,8 +282,17 @@ void IRDecoder::callBuiltin(IR::Call *call, Expr *result) return; case IR::Name::builtin_typeof: { - if (IR::Member *m = call->args->expr->asMember()) { - callBuiltinTypeofMember(m->base, *m->name, result); + if (IR::Member *member = call->args->expr->asMember()) { +#ifndef V4_BOOTSTRAP + Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray); + if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { + callBuiltinTypeofQmlContextProperty(member->base, + IR::Member::MemberKind(member->kind), + member->property->coreIndex, result); + return; + } +#endif + callBuiltinTypeofMember(member->base, *member->name, result); return; } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { callBuiltinTypeofSubscript(ss->base, ss->index, result); diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index 1e273df93e..b78d323e7d 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -34,6 +34,17 @@ #ifndef QV4ISEL_P_H #define QV4ISEL_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "private/qv4global_p.h" #include "qv4jsir_p.h" #include <private/qv4compileddata_p.h> @@ -107,6 +118,7 @@ public: // visitor methods for StmtVisitor: public: // to implement by subclasses: virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) = 0; + virtual void callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) = 0; virtual void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) = 0; virtual void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) = 0; virtual void callBuiltinTypeofName(const QString &name, IR::Expr *result) = 0; @@ -129,6 +141,7 @@ public: // to implement by subclasses: virtual void callBuiltinSetupArgumentObject(IR::Expr *result) = 0; virtual void callBuiltinConvertThisToObject() = 0; virtual void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) = 0; + virtual void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) = 0; virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) = 0; virtual void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) = 0; virtual void convertType(IR::Expr *source, IR::Expr *target) = 0; @@ -136,10 +149,8 @@ public: // to implement by subclasses: virtual void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) = 0; virtual void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) = 0; virtual void loadThisObject(IR::Expr *target) = 0; - virtual void loadQmlIdArray(IR::Expr *target) = 0; + virtual void loadQmlContext(IR::Expr *target) = 0; virtual void loadQmlImportedScripts(IR::Expr *target) = 0; - virtual void loadQmlContextObject(IR::Expr *target) = 0; - virtual void loadQmlScopeObject(IR::Expr *target) = 0; virtual void loadQmlSingleton(const QString &name, IR::Expr *target) = 0; virtual void loadConst(IR::Const *sourceConst, IR::Expr *target) = 0; virtual void loadString(const QString &str, IR::Expr *target) = 0; @@ -149,7 +160,9 @@ public: // to implement by subclasses: virtual void initClosure(IR::Closure *closure, IR::Expr *target) = 0; virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) = 0; virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) = 0; + virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target) = 0; virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) = 0; + virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) = 0; virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) = 0; virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) = 0; virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) = 0; diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h index 521c345228..9c4ab63ba6 100644 --- a/src/qml/compiler/qv4isel_util_p.h +++ b/src/qml/compiler/qv4isel_util_p.h @@ -34,6 +34,17 @@ #ifndef QV4ISEL_UTIL_P_H #define QV4ISEL_UTIL_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "private/qv4value_p.h" #include "qv4jsir_p.h" diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 5c9cc98ade..685825e8ea 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -175,8 +175,8 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor } } - template <typename _Expr> - _Expr *cleanup(_Expr *expr) + template <typename Expr_> + Expr_ *cleanup(Expr_ *expr) { std::vector<Expr *>::iterator it = std::lower_bound(subexpressions.begin(), subexpressions.end(), expr); if (it == subexpressions.end() || *it != expr) { @@ -185,7 +185,7 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor qSwap(uniqueExpr, e); expr->accept(this); qSwap(uniqueExpr, e); - return static_cast<_Expr *>(e); + return static_cast<Expr_ *>(e); } // the cloned expression is unique by definition @@ -341,14 +341,10 @@ const char *builtin_to_string(Name::Builtin b) return "builtin_setup_argument_object"; case IR::Name::builtin_convert_this_to_object: return "builtin_convert_this_to_object"; - case IR::Name::builtin_qml_id_array: - return "builtin_qml_id_array"; + case IR::Name::builtin_qml_context: + return "builtin_qml_context"; case IR::Name::builtin_qml_imported_scripts_object: return "builtin_qml_imported_scripts_object"; - case IR::Name::builtin_qml_scope_object: - return "builtin_qml_scope_object"; - case IR::Name::builtin_qml_context_object: - return "builtin_qml_context_object"; } return "builtin_(###FIXME)"; }; @@ -935,7 +931,7 @@ void CloneExpr::visitSubscript(Subscript *e) void CloneExpr::visitMember(Member *e) { Expr *clonedBase = clone(e->base); - cloned = block->MEMBER(clonedBase, e->name, e->property, e->kind, e->attachedPropertiesIdOrEnumValue); + cloned = block->MEMBER(clonedBase, e->name, e->property, e->kind, e->idIndex); } IRPrinter::IRPrinter(QTextStream *out) @@ -1239,9 +1235,9 @@ void IRPrinter::visitSubscript(Subscript *e) void IRPrinter::visitMember(Member *e) { - if (e->kind != Member::MemberOfEnum - && e->attachedPropertiesIdOrEnumValue != 0 && !e->base->asTemp()) - *out << "[[attached property from " << e->attachedPropertiesIdOrEnumValue << "]]"; + if (e->kind != Member::MemberOfEnum && e->kind != Member::MemberOfIdObjectsArray + && e->attachedPropertiesId != 0 && !e->base->asTemp()) + *out << "[[attached property from " << e->attachedPropertiesId << "]]"; else e->base->accept(this); *out << '.' << *e->name; @@ -1250,6 +1246,8 @@ void IRPrinter::visitMember(Member *e) *out << " (meta-property " << e->property->coreIndex << " <" << QMetaType::typeName(e->property->propType) << ">)"; + else if (e->kind == Member::MemberOfIdObjectsArray) + *out << "(id object " << e->idIndex << ")"; #endif } diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 8daad97e8b..80869dd3e3 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -53,6 +53,7 @@ #include <QtCore/QString> #include <QtCore/QBitArray> #include <QtCore/qurl.h> +#include <QtCore/QVarLengthArray> #include <qglobal.h> #if defined(CONST) && defined(Q_OS_WIN) @@ -114,6 +115,23 @@ struct CJump; struct Ret; struct Phi; +template<class T, int Prealloc> +class VarLengthArray: public QVarLengthArray<T, Prealloc> +{ +public: + bool removeOne(const T &element) + { + for (int i = 0; i < this->size(); ++i) { + if (this->at(i) == element) { + this->remove(i); + return true; + } + } + + return false; + } +}; + // Flag pointer: // * The first flag indicates whether the meta object is final. // If final, then none of its properties themselves need to @@ -337,10 +355,8 @@ struct Name: Expr { builtin_define_object_literal, builtin_setup_argument_object, builtin_convert_this_to_object, - builtin_qml_id_array, - builtin_qml_imported_scripts_object, - builtin_qml_context_object, - builtin_qml_scope_object + builtin_qml_context, + builtin_qml_imported_scripts_object }; const QString *id; @@ -368,18 +384,18 @@ struct Q_AUTOTEST_EXPORT Temp: Expr { StackSlot }; - // Used when temp is used as base in member expression - MemberExpressionResolver *memberResolver; - unsigned index : 28; unsigned isReadOnly : 1; unsigned kind : 3; + // Used when temp is used as base in member expression + MemberExpressionResolver *memberResolver; + Temp() - : memberResolver(0) - , index((1 << 28) - 1) + : index((1 << 28) - 1) , isReadOnly(0) , kind(Invalid) + , memberResolver(0) {} void init(unsigned kind, unsigned index) @@ -558,13 +574,18 @@ struct Member: Expr { MemberOfEnum, MemberOfQmlScopeObject, MemberOfQmlContextObject, - MemberOfSingletonObject + MemberOfIdObjectsArray, + MemberOfSingletonObject, }; Expr *base; const QString *name; QQmlPropertyData *property; - int attachedPropertiesIdOrEnumValue; // depending on kind + union { // depending on kind + int attachedPropertiesId; + int enumValue; + int idIndex; + }; uchar freeOfSideEffects : 1; // This is set for example for for QObject properties. All sorts of extra behavior @@ -577,20 +598,20 @@ struct Member: Expr { void setEnumValue(int value) { kind = MemberOfEnum; - attachedPropertiesIdOrEnumValue = value; + enumValue = value; } void setAttachedPropertiesId(int id) { - Q_ASSERT(kind != MemberOfEnum); - attachedPropertiesIdOrEnumValue = id; + Q_ASSERT(kind != MemberOfEnum && kind != MemberOfIdObjectsArray); + attachedPropertiesId = id; } - void init(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = UnspecifiedMember, int attachedPropertiesIdOrEnumValue = 0) + void init(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = UnspecifiedMember, int index = 0) { this->base = base; this->name = name; this->property = property; - this->attachedPropertiesIdOrEnumValue = attachedPropertiesIdOrEnumValue; + this->idIndex = index; this->freeOfSideEffects = false; this->inhibitTypeConversionOnWrite = property != 0; this->kind = kind; @@ -768,10 +789,13 @@ private: Q_DISABLE_COPY(BasicBlock) public: + typedef VarLengthArray<BasicBlock *, 4> IncomingEdges; + typedef VarLengthArray<BasicBlock *, 2> OutgoingEdges; + Function *function; BasicBlock *catchBlock; - QVector<BasicBlock *> in; - QVector<BasicBlock *> out; + IncomingEdges in; + OutgoingEdges out; QQmlJS::AST::SourceLocation nextLocation; BasicBlock(Function *function, BasicBlock *catcher) @@ -782,10 +806,7 @@ public: , _isExceptionHandler(false) , _groupStart(false) , _isRemoved(false) - { - in.reserve(2); - out.reserve(2); - } + {} ~BasicBlock(); const QVector<Stmt *> &statements() const diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index c0669d3e47..f20dbbf4fe 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -69,7 +69,7 @@ enum { DoVerification = 1 }; static void showMeTheCode(IR::Function *function, const char *marker) { - static bool showCode = !qgetenv("QV4_SHOW_IR").isNull(); + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR"); if (showCode) { qDebug() << marker; QBuffer buf; @@ -477,7 +477,7 @@ class DominatorTree d->vertex[d->N] = n; d->parent[n] = todo.parent; ++d->N; - const QVector<BasicBlock *> &out = function->basicBlock(n)->out; + const BasicBlock::OutgoingEdges &out = function->basicBlock(n)->out; for (int i = out.size() - 1; i > 0; --i) worklist.push_back(DFSTodo(out[i]->index(), n)); @@ -2028,32 +2028,16 @@ private: } }; -class EliminateDeadCode: public ExprVisitor { - DefUses &_defUses; - StatementWorklist &_worklist; +class SideEffectsChecker: public ExprVisitor +{ bool _sideEffect; - QVector<Temp *> _collectedTemps; public: - EliminateDeadCode(DefUses &defUses, StatementWorklist &worklist) - : _defUses(defUses) - , _worklist(worklist) - { - _collectedTemps.reserve(8); - } - - void run(Expr *&expr, Stmt *stmt) { - if (!checkForSideEffects(expr)) { - expr = 0; - foreach (Temp *t, _collectedTemps) { - _defUses.removeUse(stmt, *t); - _worklist += _defUses.defStmt(*t); - } - } - } + SideEffectsChecker() + : _sideEffect(false) + {} -private: - bool checkForSideEffects(Expr *expr) + bool hasSideEffects(Expr *expr) { bool sideEffect = false; qSwap(_sideEffect, sideEffect); @@ -2062,19 +2046,20 @@ private: return sideEffect; } +protected: void markAsSideEffect() { _sideEffect = true; - _collectedTemps.clear(); } + bool seenSideEffects() const { return _sideEffect; } + protected: - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} + void visitConst(Const *) Q_DECL_OVERRIDE {} + void visitString(IR::String *) Q_DECL_OVERRIDE {} + void visitRegExp(IR::RegExp *) Q_DECL_OVERRIDE {} - virtual void visitName(Name *e) - { + void visitName(Name *e) Q_DECL_OVERRIDE { if (e->freeOfSideEffects) return; // TODO: maybe we can distinguish between built-ins of which we know that they do not have @@ -2083,19 +2068,14 @@ protected: markAsSideEffect(); } - virtual void visitTemp(Temp *e) - { - _collectedTemps.append(e); - } - - virtual void visitArgLocal(ArgLocal *) {} + void visitTemp(Temp *) Q_DECL_OVERRIDE {} + void visitArgLocal(ArgLocal *) Q_DECL_OVERRIDE {} - virtual void visitClosure(Closure *) - { + void visitClosure(Closure *) Q_DECL_OVERRIDE { markAsSideEffect(); } - virtual void visitConvert(Convert *e) { + void visitConvert(Convert *e) Q_DECL_OVERRIDE { e->expr->accept(this); switch (e->expr->type) { @@ -2109,7 +2089,7 @@ protected: } } - virtual void visitUnop(Unop *e) { + void visitUnop(Unop *e) Q_DECL_OVERRIDE { e->expr->accept(this); switch (e->op) { @@ -2127,39 +2107,39 @@ protected: } } - virtual void visitBinop(Binop *e) { + void visitBinop(Binop *e) Q_DECL_OVERRIDE { // TODO: prune parts that don't have a side-effect. For example, in: // function f(x) { +x+1; return 0; } // we can prune the binop and leave the unop/conversion. - _sideEffect = checkForSideEffects(e->left); - _sideEffect |= checkForSideEffects(e->right); + _sideEffect = hasSideEffects(e->left); + _sideEffect |= hasSideEffects(e->right); if (e->left->type == VarType || e->left->type == StringType || e->left->type == QObjectType || e->right->type == VarType || e->right->type == StringType || e->right->type == QObjectType) markAsSideEffect(); } - virtual void visitSubscript(Subscript *e) { + void visitSubscript(Subscript *e) Q_DECL_OVERRIDE { e->base->accept(this); e->index->accept(this); markAsSideEffect(); } - virtual void visitMember(Member *e) { + void visitMember(Member *e) Q_DECL_OVERRIDE { e->base->accept(this); if (e->freeOfSideEffects) return; markAsSideEffect(); } - virtual void visitCall(Call *e) { + void visitCall(Call *e) Q_DECL_OVERRIDE { e->base->accept(this); for (ExprList *args = e->args; args; args = args->next) args->expr->accept(this); markAsSideEffect(); // TODO: there are built-in functions that have no side effect. } - virtual void visitNew(New *e) { + void visitNew(New *e) Q_DECL_OVERRIDE { e->base->accept(this); for (ExprList *args = e->args; args; args = args->next) args->expr->accept(this); @@ -2167,6 +2147,38 @@ protected: } }; +class EliminateDeadCode: public SideEffectsChecker +{ + DefUses &_defUses; + StatementWorklist &_worklist; + QVector<Temp *> _collectedTemps; + +public: + EliminateDeadCode(DefUses &defUses, StatementWorklist &worklist) + : _defUses(defUses) + , _worklist(worklist) + { + _collectedTemps.reserve(8); + } + + void run(Expr *&expr, Stmt *stmt) { + _collectedTemps.clear(); + if (!hasSideEffects(expr)) { + expr = 0; + foreach (Temp *t, _collectedTemps) { + _defUses.removeUse(stmt, *t); + _worklist += _defUses.defStmt(*t); + } + } + } + +protected: + void visitTemp(Temp *e) Q_DECL_OVERRIDE + { + _collectedTemps.append(e); + } +}; + struct DiscoveredType { int type; MemberExpressionResolver *memberResolver; @@ -3504,7 +3516,7 @@ void checkCriticalEdges(QVector<BasicBlock *> basicBlocks) { } #endif -void cleanupBasicBlocks(IR::Function *function) +static void cleanupBasicBlocks(IR::Function *function) { showMeTheCode(function, "Before basic block cleanup"); @@ -3887,7 +3899,7 @@ bool tryOptimizingComparison(Expr *&expr) void cfg2dot(IR::Function *f, const QVector<LoopDetection::LoopInfo *> &loops = QVector<LoopDetection::LoopInfo *>()) { - static bool showCode = !qgetenv("QV4_SHOW_IR").isNull(); + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR"); if (!showCode) return; @@ -3912,7 +3924,7 @@ void cfg2dot(IR::Function *f, const QVector<LoopDetection::LoopInfo *> &loops = QString name; if (f->name) name = *f->name; - else name = QString::fromLatin1("%1").arg((unsigned long long)f); + else name = QStringLiteral("%1").arg((unsigned long long)f); qout << "digraph \"" << name << "\" { ordering=out;\n"; foreach (LoopDetection::LoopInfo *l, loops) { @@ -4020,14 +4032,14 @@ void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df) if (Member *member = m->source->asMember()) { if (member->kind == Member::MemberOfEnum) { Const *c = function->New<Const>(); - const int enumValue = member->attachedPropertiesIdOrEnumValue; + const int enumValue = member->enumValue; c->init(SInt32Type, enumValue); replaceUses(targetTemp, c, W); defUses.removeDef(*targetTemp); W.remove(s); defUses.removeUse(s, *member->base->asTemp()); continue; - } else if (member->attachedPropertiesIdOrEnumValue != 0 && member->property && member->base->asTemp()) { + } else if (member->kind != IR::Member::MemberOfIdObjectsArray && member->attachedPropertiesId != 0 && member->property && member->base->asTemp()) { // Attached properties have no dependency on their base. Isel doesn't // need it and we can eliminate the temp used to initialize it. defUses.removeUse(s, *member->base->asTemp()); @@ -4927,6 +4939,39 @@ static void verifyNoPointerSharing(IR::Function *function) V(function); } +class RemoveLineNumbers: public SideEffectsChecker, public StmtVisitor +{ +public: + static void run(IR::Function *function) + { + foreach (BasicBlock *bb, function->basicBlocks()) { + if (bb->isRemoved()) + continue; + + foreach (Stmt *s, bb->statements()) { + if (!hasSideEffects(s)) { + s->location = QQmlJS::AST::SourceLocation(); + } + } + } + } + +private: + static bool hasSideEffects(Stmt *stmt) + { + RemoveLineNumbers checker; + stmt->accept(&checker); + return checker.seenSideEffects(); + } + + void visitExp(Exp *s) Q_DECL_OVERRIDE { s->expr->accept(this); } + void visitMove(Move *s) Q_DECL_OVERRIDE { s->source->accept(this); s->target->accept(this); } + void visitJump(Jump *) Q_DECL_OVERRIDE {} + void visitCJump(CJump *s) Q_DECL_OVERRIDE { s->cond->accept(this); } + void visitRet(Ret *s) Q_DECL_OVERRIDE { s->expr->accept(this); } + void visitPhi(Phi *) Q_DECL_OVERRIDE {} +}; + } // anonymous namespace void LifeTimeInterval::setFrom(int from) { @@ -5150,12 +5195,15 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee cleanupBasicBlocks(function); function->removeSharedExpressions(); - + int statementCount = 0; + foreach (BasicBlock *bb, function->basicBlocks()) + if (!bb->isRemoved()) + statementCount += bb->statementCount(); // showMeTheCode(function); - static bool doSSA = qgetenv("QV4_NO_SSA").isEmpty(); + static bool doSSA = qEnvironmentVariableIsEmpty("QV4_NO_SSA"); - if (!function->hasTry && !function->hasWith && !function->module->debugMode && doSSA) { + if (!function->hasTry && !function->hasWith && !function->module->debugMode && doSSA && statementCount <= 300) { // qout << "SSA for " << (function->name ? qPrintable(*function->name) : "<anonymous>") << endl; ConvertArgLocals(function).toTemps(); @@ -5221,7 +5269,7 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee verifyNoPointerSharing(function); } - static bool doOpt = qgetenv("QV4_NO_OPT").isEmpty(); + static const bool doOpt = qEnvironmentVariableIsEmpty("QV4_NO_OPT"); if (doOpt) { // qout << "Running SSA optimization..." << endl; worklist.reset(); @@ -5259,6 +5307,11 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee checkCriticalEdges(function->basicBlocks()); #endif + if (!function->module->debugMode) { + RemoveLineNumbers::run(function); + showMeTheCode(function, "After line number removal"); + } + // qout << "Finished SSA." << endl; inSSA = true; } else { diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h index 3cfacaee27..d06774e803 100644 --- a/src/qml/compiler/qv4ssa_p.h +++ b/src/qml/compiler/qv4ssa_p.h @@ -34,6 +34,17 @@ #ifndef QV4SSA_P_H #define QV4SSA_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4jsir_p.h" #include <QtCore/QSharedPointer> diff --git a/src/qml/debugger/debugger.pri b/src/qml/debugger/debugger.pri index 77a3ba6490..30a44eedd1 100644 --- a/src/qml/debugger/debugger.pri +++ b/src/qml/debugger/debugger.pri @@ -1,36 +1,23 @@ +contains(QT_CONFIG, no-qml-debug):DEFINES += QT_NO_QML_DEBUGGER + SOURCES += \ + $$PWD/qqmldebug.cpp \ + $$PWD/qqmldebugconnector.cpp \ $$PWD/qqmldebugservice.cpp \ - $$PWD/qqmlprofilerservice.cpp \ - $$PWD/qqmldebugserver.cpp \ - $$PWD/qqmlinspectorservice.cpp \ - $$PWD/qqmlenginedebugservice.cpp \ - $$PWD/qdebugmessageservice.cpp \ - $$PWD/qv4debugservice.cpp \ - $$PWD/qqmlconfigurabledebugservice.cpp \ - $$PWD/qqmlenginecontrolservice.cpp \ + $$PWD/qqmldebugserviceinterfaces.cpp \ $$PWD/qqmlabstractprofileradapter.cpp \ - $$PWD/qv4profileradapter.cpp \ $$PWD/qqmlprofiler.cpp HEADERS += \ + $$PWD/qqmldebugconnector_p.h \ + $$PWD/qqmldebugpluginmanager_p.h \ $$PWD/qqmldebugservice_p.h \ - $$PWD/qqmldebugservice_p_p.h \ - $$PWD/qqmlprofilerservice_p.h \ - $$PWD/qqmldebugserver_p.h \ - $$PWD/qqmldebugserverconnection_p.h \ + $$PWD/qqmldebugservicefactory_p.h \ + $$PWD/qqmldebugserviceinterfaces_p.h \ $$PWD/qqmldebugstatesdelegate_p.h \ - $$PWD/qqmlinspectorservice_p.h \ - $$PWD/qqmlinspectorinterface_p.h \ - $$PWD/qqmlenginedebugservice_p.h \ $$PWD/qqmldebug.h \ - $$PWD/qdebugmessageservice_p.h \ - $$PWD/qv4debugservice_p.h \ - $$PWD/qqmlconfigurabledebugservice_p.h \ - $$PWD/qqmlconfigurabledebugservice_p_p.h \ - $$PWD/qqmlenginecontrolservice_p.h \ $$PWD/qqmlprofilerdefinitions_p.h \ $$PWD/qqmlabstractprofileradapter_p.h \ - $$PWD/qv4profileradapter_p.h \ $$PWD/qqmlprofiler_p.h INCLUDEPATH += $$PWD diff --git a/src/qml/debugger/qdebugmessageservice.cpp b/src/qml/debugger/qdebugmessageservice.cpp deleted file mode 100644 index deaa472ce0..0000000000 --- a/src/qml/debugger/qdebugmessageservice.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qdebugmessageservice_p.h" -#include "qqmldebugservice_p_p.h" - -#include <QDataStream> -#include <QMutex> - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(QDebugMessageService, qmlDebugMessageService) - -void DebugMessageHandler(QtMsgType type, const QMessageLogContext &ctxt, - const QString &buf) -{ - QDebugMessageService::instance()->sendDebugMessage(type, ctxt, buf); -} - -class QDebugMessageServicePrivate : public QQmlDebugServicePrivate -{ -public: - QDebugMessageServicePrivate() - : oldMsgHandler(0) - , prevState(QQmlDebugService::NotConnected) - { - } - - QtMessageHandler oldMsgHandler; - QQmlDebugService::State prevState; - QMutex initMutex; -}; - -QDebugMessageService::QDebugMessageService(QObject *parent) : - QQmlDebugService(*(new QDebugMessageServicePrivate()), - QStringLiteral("DebugMessages"), 2, parent) -{ - Q_D(QDebugMessageService); - - // don't execute stateChanged() in parallel - QMutexLocker lock(&d->initMutex); - registerService(); - if (state() == Enabled) { - d->oldMsgHandler = qInstallMessageHandler(DebugMessageHandler); - d->prevState = Enabled; - } -} - -QDebugMessageService *QDebugMessageService::instance() -{ - return qmlDebugMessageService(); -} - -void QDebugMessageService::sendDebugMessage(QtMsgType type, - const QMessageLogContext &ctxt, - const QString &buf) -{ - Q_D(QDebugMessageService); - - //We do not want to alter the message handling mechanism - //We just eavesdrop and forward the messages to a port - //only if a client is connected to it. - QByteArray message; - QQmlDebugStream ws(&message, QIODevice::WriteOnly); - ws << QByteArray("MESSAGE") << type << buf.toUtf8(); - ws << QString::fromLatin1(ctxt.file).toUtf8(); - ws << ctxt.line << QString::fromLatin1(ctxt.function).toUtf8(); - - sendMessage(message); - if (d->oldMsgHandler) - (*d->oldMsgHandler)(type, ctxt, buf); -} - -void QDebugMessageService::stateChanged(State state) -{ - Q_D(QDebugMessageService); - QMutexLocker lock(&d->initMutex); - - if (state != Enabled && d->prevState == Enabled) { - QtMessageHandler handler = qInstallMessageHandler(d->oldMsgHandler); - // has our handler been overwritten in between? - if (handler != DebugMessageHandler) - qInstallMessageHandler(handler); - - } else if (state == Enabled && d->prevState != Enabled) { - d->oldMsgHandler = qInstallMessageHandler(DebugMessageHandler); - } - - d->prevState = state; -} - -QT_END_NAMESPACE diff --git a/src/qml/debugger/qdebugmessageservice_p.h b/src/qml/debugger/qdebugmessageservice_p.h deleted file mode 100644 index 694cd0e64c..0000000000 --- a/src/qml/debugger/qdebugmessageservice_p.h +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QDEBUGMESSAGESERVICE_P_H -#define QDEBUGMESSAGESERVICE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqmldebugservice_p.h" - -#include <QtCore/qlogging.h> - -QT_BEGIN_NAMESPACE - -class QDebugMessageServicePrivate; - -class QDebugMessageService : public QQmlDebugService -{ - Q_OBJECT -public: - QDebugMessageService(QObject *parent = 0); - - static QDebugMessageService *instance(); - - void sendDebugMessage(QtMsgType type, const QMessageLogContext &ctxt, - const QString &buf); - -protected: - void stateChanged(State); - -private: - Q_DISABLE_COPY(QDebugMessageService) - Q_DECLARE_PRIVATE(QDebugMessageService) -}; - -QT_END_NAMESPACE - -#endif // QDEBUGMESSAGESERVICE_P_H diff --git a/src/qml/debugger/qqmlconfigurabledebugservice.cpp b/src/qml/debugger/qqmlconfigurabledebugservice.cpp deleted file mode 100644 index e3693e00a4..0000000000 --- a/src/qml/debugger/qqmlconfigurabledebugservice.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlconfigurabledebugservice_p.h" -#include "qqmlconfigurabledebugservice_p_p.h" - -QT_BEGIN_NAMESPACE - -QQmlConfigurableDebugService::QQmlConfigurableDebugService(const QString &name, float version, - QObject *parent) : - QQmlDebugService((*new QQmlConfigurableDebugServicePrivate), name, version, parent) { init(); } - -QQmlConfigurableDebugService::QQmlConfigurableDebugService(QQmlDebugServicePrivate &dd, - const QString &name, float version, - QObject *parent) : - QQmlDebugService(dd, name, version, parent) { init(); } - -QMutex *QQmlConfigurableDebugService::configMutex() -{ - Q_D(QQmlConfigurableDebugService); - return &d->configMutex; -} - -void QQmlConfigurableDebugService::init() -{ - Q_D(QQmlConfigurableDebugService); - QMutexLocker lock(&d->configMutex); - // If we're not enabled or not blocking, don't wait for configuration - d->waitingForConfiguration = (registerService() == Enabled && blockingMode()); -} - -void QQmlConfigurableDebugService::stopWaiting() -{ - Q_D(QQmlConfigurableDebugService); - QMutexLocker lock(&d->configMutex); - d->waitingForConfiguration = false; - foreach (QQmlEngine *engine, d->waitingEngines) - emit attachedToEngine(engine); - d->waitingEngines.clear(); -} - -void QQmlConfigurableDebugService::stateChanged(QQmlDebugService::State newState) -{ - if (newState != Enabled) - stopWaiting(); -} - -void QQmlConfigurableDebugService::engineAboutToBeAdded(QQmlEngine *engine) -{ - Q_D(QQmlConfigurableDebugService); - QMutexLocker lock(&d->configMutex); - if (d->waitingForConfiguration) - d->waitingEngines.append(engine); - else - emit attachedToEngine(engine); -} - -QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlconfigurabledebugservice_p.h b/src/qml/debugger/qqmlconfigurabledebugservice_p.h deleted file mode 100644 index cf69c3a1f6..0000000000 --- a/src/qml/debugger/qqmlconfigurabledebugservice_p.h +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef QQMLCONFIGURABLEDEBUGSEVICE_H -#define QQMLCONFIGURABLEDEBUGSEVICE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqmldebugservice_p.h" - -QT_BEGIN_NAMESPACE - -class QMutex; -class QQmlConfigurableDebugServicePrivate; -class QQmlConfigurableDebugService : public QQmlDebugService -{ - Q_OBJECT -public: - QQmlConfigurableDebugService(const QString &name, float version, QObject *parent = 0); - -protected: - QQmlConfigurableDebugService(QQmlDebugServicePrivate &dd, const QString &name, float version, QObject *parent = 0); - - QMutex *configMutex(); - void stopWaiting(); - void init(); - - void stateChanged(State); - void engineAboutToBeAdded(QQmlEngine *); - - virtual ~QQmlConfigurableDebugService() {} -private: - Q_DISABLE_COPY(QQmlConfigurableDebugService) - Q_DECLARE_PRIVATE(QQmlConfigurableDebugService) -}; - -QT_END_NAMESPACE - -#endif // QQMLCONFIGURABLEDEBUGSEVICE_H diff --git a/src/qml/debugger/qqmlconfigurabledebugservice_p_p.h b/src/qml/debugger/qqmlconfigurabledebugservice_p_p.h deleted file mode 100644 index 6d693b6352..0000000000 --- a/src/qml/debugger/qqmlconfigurabledebugservice_p_p.h +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLCONFIGURABLEDEBUGSERVICE_P_H -#define QQMLCONFIGURABLEDEBUGSERVICE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqmldebugservice_p.h" -#include "qqmldebugservice_p_p.h" - -#include <QMutex> - -QT_BEGIN_NAMESPACE - -class QQmlEngine; - -class QQmlConfigurableDebugServicePrivate : public QQmlDebugServicePrivate -{ - Q_DECLARE_PUBLIC(QQmlConfigurableDebugService) -public: - QQmlConfigurableDebugServicePrivate() : configMutex(QMutex::Recursive) {} - - QMutex configMutex; - QList<QQmlEngine *> waitingEngines; - bool waitingForConfiguration; -}; - -QT_END_NAMESPACE - -#endif // QQMLCONFIGURABLEDEBUGSERVICE_P_H diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp new file mode 100644 index 0000000000..35dc110e9a --- /dev/null +++ b/src/qml/debugger/qqmldebug.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldebug.h" +#include "qqmldebugconnector_p.h" + +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool printWarning) +{ +#ifndef QQML_NO_DEBUG_PROTOCOL + if (!QQmlEnginePrivate::qml_debugging_enabled + && printWarning) { + qDebug("QML debugging is enabled. Only use this in a safe environment."); + } + QQmlEnginePrivate::qml_debugging_enabled = true; +#else + Q_UNUSED(printWarning); +#endif +} + +/*! + * \enum QQmlDebuggingEnabler::StartMode + * + * Defines the debug server's start behavior. You can interrupt QML engines starting while a debug + * client is connecting, in order to set breakpoints in or profile startup code. + * + * \value DoNotWaitForClient Run any QML engines as usual while the debug services are connecting. + * \value WaitForClient If a QML engine starts while the debug services are connecting, + * interrupt it until they are done. + */ + +/*! + * Enables debugging for QML engines created after calling this function. The debug server will + * listen on \a port at \a hostName and block the QML engine until it receives a connection if + * \a mode is \c WaitForClient. If \a mode is not specified it won't block and if \a hostName is not + * specified it will listen on all available interfaces. You can only start one debug server at a + * time. A debug server may have already been started if the -qmljsdebugger= command line argument + * was given. This method returns \c true if a new debug server was successfully started, or + * \c false otherwise. + */ +bool QQmlDebuggingEnabler::startTcpDebugServer(int port, StartMode mode, const QString &hostName) +{ +#ifndef QQML_NO_DEBUG_PROTOCOL + QQmlDebugConnector::setPluginKey(QLatin1String("QQmlDebugServer")); + QQmlDebugConnector *connector = QQmlDebugConnector::instance(); + if (connector) { + QVariantHash configuration; + configuration[QLatin1String("portFrom")] = configuration[QLatin1String("portTo")] = port; + configuration[QLatin1String("block")] = (mode == WaitForClient); + configuration[QLatin1String("hostAddress")] = hostName; + return connector->open(configuration); + } +#else + Q_UNUSED(port); + Q_UNUSED(block); + Q_UNUSED(hostName); +#endif + return false; +} + +/*! + * \since 5.6 + * + * Enables debugging for QML engines created after calling this function. The debug server will + * connect to a debugger waiting on a local socket at the given \a socketFileName and block the QML + * engine until the connection is established if \a mode is \c WaitForClient. If \a mode is not + * specified it will not block. You can only start one debug server at a time. A debug server may + * have already been started if the -qmljsdebugger= command line argument was given. This method + * returns \c true if a new debug server was successfully started, or \c false otherwise. + */ +bool QQmlDebuggingEnabler::connectToLocalDebugger(const QString &socketFileName, StartMode mode) +{ +#ifndef QQML_NO_DEBUG_PROTOCOL + QQmlDebugConnector::setPluginKey(QLatin1String("QQmlDebugServer")); + QQmlDebugConnector *connector = QQmlDebugConnector::instance(); + if (connector) { + QVariantHash configuration; + configuration[QLatin1String("fileName")] = socketFileName; + configuration[QLatin1String("block")] = (mode == WaitForClient); + return connector->open(configuration); + } +#else + Q_UNUSED(fileName); + Q_UNUSED(block); +#endif + return false; +} + +QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmldebug.h b/src/qml/debugger/qqmldebug.h index 559c492dfd..5d65982a49 100644 --- a/src/qml/debugger/qqmldebug.h +++ b/src/qml/debugger/qqmldebug.h @@ -50,6 +50,8 @@ struct Q_QML_EXPORT QQmlDebuggingEnabler QQmlDebuggingEnabler(bool printWarning = true); static bool startTcpDebugServer(int port, StartMode mode = DoNotWaitForClient, const QString &hostName = QString()); + static bool connectToLocalDebugger(const QString &socketFileName, + StartMode mode = DoNotWaitForClient); }; // Execute code in constructor before first QQmlEngine is instantiated diff --git a/src/qml/debugger/qqmldebugconnector.cpp b/src/qml/debugger/qqmldebugconnector.cpp new file mode 100644 index 0000000000..64a8a49bb9 --- /dev/null +++ b/src/qml/debugger/qqmldebugconnector.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldebugpluginmanager_p.h" +#include "qqmldebugconnector_p.h" +#include "qqmldebugservicefactory_p.h" +#include <QtCore/QPluginLoader> +#include <QtCore/QCoreApplication> +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtCore/QJsonArray> + +#include <private/qcoreapplication_p.h> +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +// Connectors. We could add more plugins here, and distinguish by arguments to instance() +Q_QML_DEBUG_PLUGIN_LOADER(QQmlDebugConnector) +Q_QML_IMPORT_DEBUG_PLUGIN(QQmlDebugServerFactory) +Q_QML_IMPORT_DEBUG_PLUGIN(QQmlNativeDebugConnectorFactory) + +// Services +Q_QML_DEBUG_PLUGIN_LOADER(QQmlDebugService) +Q_QML_IMPORT_DEBUG_PLUGIN(QQmlInspectorServiceFactory) +Q_QML_IMPORT_DEBUG_PLUGIN(QQmlProfilerServiceFactory) +Q_QML_IMPORT_DEBUG_PLUGIN(QQmlDebuggerServiceFactory) + +struct QQmlDebugConnectorParams { + QString pluginKey; + QStringList services; + QString arguments; + QQmlDebugConnector *instance; + + QQmlDebugConnectorParams() : instance(0) + { + if (qApp) { + QCoreApplicationPrivate *appD = + static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(qApp)); + if (appD) + arguments = appD->qmljsDebugArgumentsString(); + } + } +}; + +Q_GLOBAL_STATIC(QQmlDebugConnectorParams, qmlDebugConnectorParams) + +void QQmlDebugConnector::setPluginKey(const QString &key) +{ + QQmlDebugConnectorParams *params = qmlDebugConnectorParams(); + if (params) { + if (params->instance) + qWarning() << "QML debugger: Cannot set plugin key after loading the plugin."; + else + params->pluginKey = key; + } +} + +void QQmlDebugConnector::setServices(const QStringList &services) +{ + QQmlDebugConnectorParams *params = qmlDebugConnectorParams(); + if (params) + params->services = services; +} + +QString QQmlDebugConnector::commandLineArguments() +{ + QQmlDebugConnectorParams *params = qmlDebugConnectorParams(); + if (!params) + return QString(); + return params->arguments; +} + +QQmlDebugConnector *QQmlDebugConnector::instance() +{ + QQmlDebugConnectorParams *params = qmlDebugConnectorParams(); + if (!params) + return 0; + + if (!QQmlEnginePrivate::qml_debugging_enabled) { + if (!params->arguments.isEmpty()) { + qWarning().noquote() << QString::fromLatin1( + "QML Debugger: Ignoring \"-qmljsdebugger=%1\". Debugging " + "has not been enabled.").arg(params->arguments); + params->arguments.clear(); + } + return 0; + } + + if (!params->instance) { + const QString serverConnector = QStringLiteral("QQmlDebugServer"); + const QString nativeConnector = QStringLiteral("QQmlNativeDebugConnector"); + const bool isNative = params->arguments.startsWith(QStringLiteral("native")); + if (!params->pluginKey.isEmpty()) { + if (params->pluginKey == serverConnector || params->pluginKey == nativeConnector) + params->instance = loadQQmlDebugConnector(params->pluginKey); + else + return 0; // We cannot load anything else, yet + } else if (params->arguments.isEmpty()) { + return 0; // no explicit class name given and no command line arguments + } else { + params->instance = loadQQmlDebugConnector(isNative ? nativeConnector : serverConnector); + } + + if (params->instance) { + foreach (const QJsonObject &object, metaDataForQQmlDebugService()) { + foreach (const QJsonValue &key, object.value(QLatin1String("MetaData")).toObject() + .value(QLatin1String("Keys")).toArray()) { + QString keyString = key.toString(); + if (params->services.isEmpty() || params->services.contains(keyString)) + loadQQmlDebugService(keyString); + } + } + } + } + + return params->instance; +} + +QQmlDebugConnectorFactory::~QQmlDebugConnectorFactory() +{ + // This is triggered when the plugin is unloaded. + QQmlDebugConnectorParams *params = qmlDebugConnectorParams(); + if (params && params->instance) { + delete params->instance; + params->instance = 0; + } +} + +QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlinspectorservice_p.h b/src/qml/debugger/qqmldebugconnector_p.h index f49c36aacb..f5f5a87b56 100644 --- a/src/qml/debugger/qqmlinspectorservice_p.h +++ b/src/qml/debugger/qqmldebugconnector_p.h @@ -31,8 +31,13 @@ ** ****************************************************************************/ -#ifndef QQMLINSPECTORSERVICE_H -#define QQMLINSPECTORSERVICE_H +#ifndef QQMLDEBUGCONNECTOR_H +#define QQMLDEBUGCONNECTOR_H + +#include <QtQml/qtqmlglobal.h> +#include <QtCore/QVariantList> + +#include <private/qqmldebugservice_p.h> // // W A R N I N G @@ -45,45 +50,49 @@ // We mean it. // -#include "qqmldebugservice_p.h" - -#include <QtQml/qtqmlglobal.h> -#include <QtCore/QList> - QT_BEGIN_NAMESPACE - -class QQmlInspectorInterface; - -class Q_QML_PRIVATE_EXPORT QQmlInspectorService : public QQmlDebugService +class QQmlDebugService; +class Q_QML_PRIVATE_EXPORT QQmlDebugConnector : public QObject { Q_OBJECT - public: - QQmlInspectorService(); - static QQmlInspectorService *instance(); + static void setPluginKey(const QString &key); + static void setServices(const QStringList &services); + static QQmlDebugConnector *instance(); - void addView(QObject *); - void removeView(QObject *); + virtual bool blockingMode() const = 0; - void sendMessage(const QByteArray &message); + virtual QQmlDebugService *service(const QString &name) const = 0; -protected: - virtual void stateChanged(State state); - virtual void messageReceived(const QByteArray &); + virtual void addEngine(QQmlEngine *engine) = 0; + virtual void removeEngine(QQmlEngine *engine) = 0; + + virtual bool addService(const QString &name, QQmlDebugService *service) = 0; + virtual bool removeService(const QString &name) = 0; -private Q_SLOTS: - void processMessage(const QByteArray &message); - void updateState(); + virtual bool open(const QVariantHash &configuration = QVariantHash()) = 0; -private: - void loadInspectorPlugins(); + template<class Service> + static Service *service() + { + QQmlDebugConnector *inst = instance(); + return inst ? static_cast<Service *>(inst->service(Service::s_key)) : 0; + } + +protected: + static QString commandLineArguments(); +}; - QList<QObject*> m_views; - QQmlInspectorInterface *m_currentInspectorPlugin; - QList<QQmlInspectorInterface*> m_inspectorPlugins; +class Q_QML_PRIVATE_EXPORT QQmlDebugConnectorFactory : public QObject { + Q_OBJECT +public: + virtual QQmlDebugConnector *create(const QString &key) = 0; + ~QQmlDebugConnectorFactory(); }; +#define QQmlDebugConnectorFactory_iid "org.qt-project.Qt.QQmlDebugConnectorFactory" + QT_END_NAMESPACE -#endif // QQMLINSPECTORSERVICE_H +#endif // QQMLDEBUGCONNECTOR_H diff --git a/src/qml/debugger/qqmlenginecontrolservice_p.h b/src/qml/debugger/qqmldebugpluginmanager_p.h index 2171937efe..6fffa67d7b 100644 --- a/src/qml/debugger/qqmlenginecontrolservice_p.h +++ b/src/qml/debugger/qqmldebugpluginmanager_p.h @@ -31,11 +31,8 @@ ** ****************************************************************************/ -#ifndef QQMLENGINECONTROLSERVICE_H -#define QQMLENGINECONTROLSERVICE_H - -#include <QMutex> -#include "qqmldebugservice_p.h" +#ifndef QQMLDEBUGPLUGINMANAGER_P_H +#define QQMLDEBUGPLUGINMANAGER_P_H // // W A R N I N G @@ -48,43 +45,51 @@ // We mean it. // -QT_BEGIN_NAMESPACE - -class QQmlEngineControlService : public QQmlDebugService -{ -public: - enum MessageType { - EngineAboutToBeAdded, - EngineAdded, - EngineAboutToBeRemoved, - EngineRemoved - }; +#include <QDebug> +#include <private/qtqmlglobal_p.h> +#include <private/qfactoryloader_p.h> - enum CommandType { - StartWaitingEngine, - StopWaitingEngine - }; +QT_BEGIN_NAMESPACE - QQmlEngineControlService(); +#if defined(QT_NO_QML_DEBUGGER) - static QQmlEngineControlService *instance(); +#define Q_QML_DEBUG_PLUGIN_LOADER(interfaceName)\ + interfaceName *load##interfaceName(const QString &key)\ + {\ + qWarning() << "Qml Debugger: QtQml is not configured for debugging. Ignoring request for"\ + << "debug plugin" << key;\ + return 0;\ + }\ + QList<QJsonObject> metaDataFor##interfaceName()\ + {\ + return QList<QJsonObject>();\ + } +#define Q_QML_IMPORT_DEBUG_PLUGIN(className) -protected: - QMutex dataMutex; - QList<QQmlEngine *> startingEngines; - QList<QQmlEngine *> stoppingEngines; +#else // QT_NO_QML_DEBUGGER - void messageReceived(const QByteArray &); - void engineAboutToBeAdded(QQmlEngine *); - void engineAboutToBeRemoved(QQmlEngine *); - void engineAdded(QQmlEngine *); - void engineRemoved(QQmlEngine *); +#ifdef QT_STATIC +#define Q_QML_IMPORT_DEBUG_PLUGIN(className)\ + QT_END_NAMESPACE\ + Q_IMPORT_PLUGIN(className)\ + QT_BEGIN_NAMESPACE +#else +#define Q_QML_IMPORT_DEBUG_PLUGIN(className) +#endif // QT_STATIC - void sendMessage(MessageType type, QQmlEngine *engine); +#define Q_QML_DEBUG_PLUGIN_LOADER(interfaceName)\ + Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, interfaceName##Loader,\ + (interfaceName##Factory_iid, QLatin1String("/qmltooling")))\ + interfaceName *load##interfaceName(const QString &key)\ + {\ + return qLoadPlugin<interfaceName, interfaceName##Factory>(interfaceName##Loader(), key);\ + }\ + QList<QJsonObject> metaDataFor##interfaceName()\ + {\ + return interfaceName##Loader()->metaData();\ + } - void stateChanged(State); -}; +#endif // QT_NO_QML_DEBUGGER QT_END_NAMESPACE - -#endif // QQMLENGINECONTROLSERVICE_H +#endif // QQMLDEBUGPLUGINMANAGER_P_H diff --git a/src/qml/debugger/qqmldebugserver.cpp b/src/qml/debugger/qqmldebugserver.cpp deleted file mode 100644 index b0302181ee..0000000000 --- a/src/qml/debugger/qqmldebugserver.cpp +++ /dev/null @@ -1,787 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmldebugserver_p.h" -#include "qqmldebugservice_p.h" -#include "qqmldebugservice_p_p.h" -#include "qqmlenginedebugservice_p.h" -#include "qv4debugservice_p.h" -#include "qdebugmessageservice_p.h" -#include "qqmlprofilerservice_p.h" - -#include <private/qqmlengine_p.h> -#include <private/qqmlglobal_p.h> - -#include <QtCore/QAtomicInt> -#include <QtCore/QDir> -#include <QtCore/QPluginLoader> -#include <QtCore/QStringList> -#include <QtCore/qwaitcondition.h> - -#include <private/qobject_p.h> -#include <private/qcoreapplication_p.h> - -#if defined(QT_STATIC) && ! defined(QT_NO_QML_DEBUGGER) -#include "../../plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.h" -#endif - -QT_BEGIN_NAMESPACE - -// We can't friend the Q_GLOBAL_STATIC to have the constructor available so we need a little -// workaround here. Using this wrapper we can also make QQmlEnginePrivate's cleanup() available to -// qAddPostRoutine(). We can't do the cleanup in the destructor because we need a QApplication to -// be available when stopping the plugins. -struct QQmlDebugServerInstanceWrapper { - QQmlDebugServer m_instance; - void cleanup(); -}; - -Q_GLOBAL_STATIC(QQmlDebugServerInstanceWrapper, debugServerInstance) - -/* - QQmlDebug Protocol (Version 1): - - handshake: - 1. Client sends - "QDeclarativeDebugServer" 0 version pluginNames [QDataStream version] - version: an int representing the highest protocol version the client knows - pluginNames: plugins available on client side - 2. Server sends - "QDeclarativeDebugClient" 0 version pluginNames pluginVersions [QDataStream version] - version: an int representing the highest protocol version the client & server know - pluginNames: plugins available on server side. plugins both in the client and server message are enabled. - client plugin advertisement - 1. Client sends - "QDeclarativeDebugServer" 1 pluginNames - server plugin advertisement - 1. Server sends - "QDeclarativeDebugClient" 1 pluginNames pluginVersions - plugin communication: - Everything send with a header different to "QDeclarativeDebugServer" is sent to the appropriate plugin. - */ - -const int protocolVersion = 1; -int QQmlDebugServer::s_dataStreamVersion = QDataStream::Qt_4_7; - -// print detailed information about loading of plugins -DEFINE_BOOL_CONFIG_OPTION(qmlDebugVerbose, QML_DEBUGGER_VERBOSE) - -class QQmlDebugServerThread; - -class QQmlDebugServerPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQmlDebugServer) -public: - QQmlDebugServerPrivate(); - - bool start(int portFrom, int portTo, bool block, const QString &hostAddress, - const QString &pluginName); - void advertisePlugins(); - void cleanup(); - QQmlDebugServerConnection *loadConnectionPlugin(const QString &pluginName); - - QQmlDebugServerConnection *connection; - QHash<QString, QQmlDebugService *> plugins; - mutable QReadWriteLock pluginsLock; - QStringList clientPlugins; - bool gotHello; - bool blockingMode; - - class EngineCondition { - public: - EngineCondition() : numServices(0), condition(new QWaitCondition) {} - - bool waitForServices(QReadWriteLock *locked, int numEngines); - - void wake(); - private: - int numServices; - - // shared pointer to allow for QHash-inflicted copying. - QSharedPointer<QWaitCondition> condition; - }; - - QHash<QQmlEngine *, EngineCondition> engineConditions; - - QMutex helloMutex; - QWaitCondition helloCondition; - QQmlDebugServerThread *thread; - QPluginLoader loader; - QAtomicInt changeServiceStateCalls; - -private: - // private slots - void _q_changeServiceState(const QString &serviceName, - QQmlDebugService::State newState); - void _q_sendMessages(const QList<QByteArray> &messages); - void _q_removeThread(); -}; - -void QQmlDebugServerInstanceWrapper::cleanup() -{ m_instance.d_func()->cleanup(); } - -class QQmlDebugServerThread : public QThread -{ -public: - void setPluginName(const QString &pluginName) { - m_pluginName = pluginName; - } - - void setPortRange(int portFrom, int portTo, bool block, const QString &hostAddress) { - m_portFrom = portFrom; - m_portTo = portTo; - m_block = block; - m_hostAddress = hostAddress; - } - - void run(); - -private: - QString m_pluginName; - int m_portFrom; - int m_portTo; - bool m_block; - QString m_hostAddress; -}; - -QQmlDebugServerPrivate::QQmlDebugServerPrivate() : - connection(0), - pluginsLock(QReadWriteLock::Recursive), - gotHello(false), - blockingMode(false), - thread(0) -{ - // used in _q_sendMessages - qRegisterMetaType<QList<QByteArray> >("QList<QByteArray>"); - // used in _q_changeServiceState - qRegisterMetaType<QQmlDebugService::State>("QQmlDebugService::State"); -} - -void QQmlDebugServerPrivate::advertisePlugins() -{ - Q_Q(QQmlDebugServer); - - if (!gotHello) - return; - - QByteArray message; - { - QQmlDebugStream out(&message, QIODevice::WriteOnly); - QStringList pluginNames; - QList<float> pluginVersions; - foreach (QQmlDebugService *service, plugins.values()) { - pluginNames << service->name(); - pluginVersions << service->version(); - } - out << QString(QStringLiteral("QDeclarativeDebugClient")) << 1 << pluginNames << pluginVersions; - } - - QMetaObject::invokeMethod(q, "_q_sendMessages", Qt::QueuedConnection, Q_ARG(QList<QByteArray>, QList<QByteArray>() << message)); -} - -void QQmlDebugServerPrivate::cleanup() -{ - Q_Q(QQmlDebugServer); - { - QReadLocker lock(&pluginsLock); - foreach (QQmlDebugService *service, plugins.values()) { - changeServiceStateCalls.ref(); - QMetaObject::invokeMethod(q, "_q_changeServiceState", Qt::QueuedConnection, - Q_ARG(QString, service->name()), - Q_ARG(QQmlDebugService::State, QQmlDebugService::NotConnected)); - } - } - - // Wait for changeServiceState calls to finish - // (while running an event loop because some services - // might again use slots to execute stuff in the GUI thread) - QEventLoop loop; - while (!changeServiceStateCalls.testAndSetOrdered(0, 0)) - loop.processEvents(); - - // Stop the thread while the application is still there. Copy here as the thread will set itself - // to 0 when it stops. It will also do deleteLater, but as long as we don't allow the GUI - // thread's event loop to run we're safe from that. - QThread *threadCopy = thread; - if (threadCopy) { - threadCopy->exit(); - threadCopy->wait(); - } -} - -QQmlDebugServerConnection *QQmlDebugServerPrivate::loadConnectionPlugin( - const QString &pluginName) -{ -#ifndef QT_NO_LIBRARY - QStringList pluginCandidates; - const QStringList paths = QCoreApplication::libraryPaths(); - foreach (const QString &libPath, paths) { - const QDir dir(libPath + QLatin1String("/qmltooling")); - if (dir.exists()) { - QStringList plugins(dir.entryList(QDir::Files)); - foreach (const QString &pluginPath, plugins) { - if (QFileInfo(pluginPath).fileName().contains(pluginName)) - pluginCandidates << dir.absoluteFilePath(pluginPath); - } - } - } - - QQmlDebugServerConnection *loadedConnection = 0; - foreach (const QString &pluginPath, pluginCandidates) { - if (qmlDebugVerbose()) - qDebug() << "QML Debugger: Trying to load plugin " << pluginPath << "..."; - - loader.setFileName(pluginPath); - if (!loader.load()) { - if (qmlDebugVerbose()) - qDebug() << "QML Debugger: Error while loading: " << loader.errorString(); - continue; - } - if (QObject *instance = loader.instance()) - loadedConnection = qobject_cast<QQmlDebugServerConnection*>(instance); - - if (loadedConnection) { - if (qmlDebugVerbose()) - qDebug() << "QML Debugger: Plugin successfully loaded."; - - return loadedConnection; - } - - if (qmlDebugVerbose()) - qDebug() << "QML Debugger: Plugin does not implement interface QQmlDebugServerConnection."; - - loader.unload(); - } -#endif - return 0; -} - -void QQmlDebugServerThread::run() -{ - QQmlDebugServerInstanceWrapper *wrapper = debugServerInstance(); - Q_ASSERT_X(wrapper != 0, Q_FUNC_INFO, "There should always be a debug server available here."); - QQmlDebugServer *server = &wrapper->m_instance; -#if defined(QT_STATIC) && ! defined(QT_NO_QML_DEBUGGER) - QQmlDebugServerConnection *connection - = new QTcpServerConnection; -#else - QQmlDebugServerConnection *connection - = server->d_func()->loadConnectionPlugin(m_pluginName); -#endif - if (connection) { - connection->setServer(server); - if (!connection->setPortRange(m_portFrom, m_portTo, m_block, m_hostAddress)) { - delete connection; - return; - } - server->d_func()->connection = connection; - if (m_block) - connection->waitForConnection(); - } else { - qWarning() << "QML Debugger: Couldn't load plugin" << m_pluginName; - return; - } - - exec(); - - // make sure events still waiting are processed - QEventLoop eventLoop; - eventLoop.processEvents(QEventLoop::AllEvents); -} - -bool QQmlDebugServer::hasDebuggingClient() const -{ - Q_D(const QQmlDebugServer); - return d->connection - && d->connection->isConnected() - && d->gotHello; -} - -bool QQmlDebugServer::hasThread() const -{ - Q_D(const QQmlDebugServer); - return d->thread != 0; -} - -bool QQmlDebugServer::hasConnection() const -{ - Q_D(const QQmlDebugServer); - return d->connection != 0; -} - -bool QQmlDebugServer::blockingMode() const -{ - Q_D(const QQmlDebugServer); - return d->blockingMode; -} - -QQmlDebugServer *QQmlDebugServer::instance() -{ - QQmlDebugServerInstanceWrapper *wrapper = debugServerInstance(); - if (wrapper && wrapper->m_instance.d_func()->thread) { - QQmlDebugServer *ret = &(wrapper->m_instance); - QQmlDebugServerPrivate *d = ret->d_func(); - QMutexLocker locker(&d->helloMutex); - if (d->blockingMode && !d->gotHello) - d->helloCondition.wait(&d->helloMutex); - return ret; - } else { - return 0; - } -} - -static void cleanupOnShutdown() -{ - QQmlDebugServerInstanceWrapper *wrapper = debugServerInstance(); - if (wrapper) - wrapper->cleanup(); -} - -bool QQmlDebugServerPrivate::start(int portFrom, int portTo, bool block, const QString &hostAddress, - const QString &pluginName) -{ - if (!QQmlEnginePrivate::qml_debugging_enabled) - return false; - if (thread) - return false; - static bool postRoutineAdded = false; - if (!postRoutineAdded) { - qAddPostRoutine(cleanupOnShutdown); - postRoutineAdded = true; - } - Q_Q(QQmlDebugServer); - thread = new QQmlDebugServerThread; - q->moveToThread(thread); - - // Remove the thread immmediately when it finishes, so that we don't have to wait for the event - // loop to signal that. - QObject::connect(thread, SIGNAL(finished()), q, SLOT(_q_removeThread()), Qt::DirectConnection); - - thread->setObjectName(QStringLiteral("QQmlDebugServerThread")); - thread->setPluginName(pluginName); - thread->setPortRange(portFrom, portTo == -1 ? portFrom : portTo, block, hostAddress); - blockingMode = block; - thread->start(); - return true; -} - -QQmlDebugServer::QQmlDebugServer() - : QObject(*(new QQmlDebugServerPrivate)) -{ - if (qApp == 0) - return; - QCoreApplicationPrivate *appD = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(qApp)); -#ifndef QT_NO_QML_DEBUGGER - // ### remove port definition when protocol is changed - int portFrom = 0; - int portTo = 0; - bool block = false; - bool ok = false; - QString hostAddress; - - // format: qmljsdebugger=port:<port_from>[,port_to],host:<ip address>][,block] - if (!appD->qmljsDebugArgumentsString().isEmpty()) { - if (!QQmlEnginePrivate::qml_debugging_enabled) { - qWarning() << QString(QLatin1String( - "QML Debugger: Ignoring \"-qmljsdebugger=%1\". " - "Debugging has not been enabled.")).arg( - appD->qmljsDebugArgumentsString()); - return; - } - - QString pluginName; - QStringList lstjsDebugArguments = appD->qmljsDebugArgumentsString() - .split(QLatin1Char(',')); - QStringList::const_iterator argsItEnd = lstjsDebugArguments.end(); - QStringList::const_iterator argsIt = lstjsDebugArguments.begin(); - for (; argsIt != argsItEnd; ++argsIt) { - const QString strArgument = *argsIt; - if (strArgument.startsWith(QLatin1String("port:"))) { - pluginName = QLatin1String("qmldbg_tcp"); - portFrom = strArgument.mid(5).toInt(&ok); - portTo = portFrom; - QStringList::const_iterator argsNext = argsIt + 1; - if (argsNext == argsItEnd) - break; - const QString nextArgument = *argsNext; - if (ok && nextArgument.contains(QRegExp(QStringLiteral("^\\s*\\d+\\s*$")))) { - portTo = nextArgument.toInt(&ok); - ++argsIt; - } - } else if (strArgument.startsWith(QLatin1String("host:"))) { - hostAddress = strArgument.mid(5); - } else if (strArgument == QLatin1String("block")) { - block = true; - } else { - qWarning() << QString::fromLatin1("QML Debugger: Invalid argument '%1' " - "detected. Ignoring the same.") - .arg(strArgument); - } - } - - if (ok) { - Q_D(QQmlDebugServer); - d->start(portFrom, portTo, block, hostAddress, pluginName); - } else { - qWarning() << QString(QLatin1String( - "QML Debugger: Ignoring \"-qmljsdebugger=%1\". " - "Format is qmljsdebugger=port:<port_from>[,port_to],host:" - "<ip address>][,block]")).arg(appD->qmljsDebugArgumentsString()); - } - } -#else - if (!appD->qmljsDebugArgumentsString().isEmpty()) { - qWarning() << QString(QLatin1String( - "QML Debugger: Ignoring \"-qmljsdebugger=%1\". " - "QtQml is not configured for debugging.")).arg( - appD->qmljsDebugArgumentsString()); - } -#endif -} - -void QQmlDebugServer::receiveMessage(const QByteArray &message) -{ - typedef QHash<QString, QQmlDebugService*>::const_iterator DebugServiceConstIt; - - // to be executed in debugger thread - Q_ASSERT(QThread::currentThread() == thread()); - - Q_D(QQmlDebugServer); - - QQmlDebugStream in(message); - - QString name; - - in >> name; - if (name == QLatin1String("QDeclarativeDebugServer")) { - int op = -1; - in >> op; - if (op == 0) { - QWriteLocker lock(&d->pluginsLock); - int version; - in >> version >> d->clientPlugins; - - //Get the supported QDataStream version - if (!in.atEnd()) { - in >> s_dataStreamVersion; - if (s_dataStreamVersion > QDataStream().version()) - s_dataStreamVersion = QDataStream().version(); - } - - // Send the hello answer immediately, since it needs to arrive before - // the plugins below start sending messages. - - QByteArray helloAnswer; - QQmlDebugStream out(&helloAnswer, QIODevice::WriteOnly); - QStringList pluginNames; - QList<float> pluginVersions; - foreach (QQmlDebugService *service, d->plugins.values()) { - pluginNames << service->name(); - pluginVersions << service->version(); - } - - out << QString(QStringLiteral("QDeclarativeDebugClient")) << 0 << protocolVersion - << pluginNames << pluginVersions << s_dataStreamVersion; - - d->connection->send(QList<QByteArray>() << helloAnswer); - - QMutexLocker helloLock(&d->helloMutex); - d->gotHello = true; - - for (DebugServiceConstIt iter = d->plugins.constBegin(), cend = d->plugins.constEnd(); iter != cend; ++iter) { - QQmlDebugService::State newState = QQmlDebugService::Unavailable; - if (d->clientPlugins.contains(iter.key())) - newState = QQmlDebugService::Enabled; - d->changeServiceStateCalls.ref(); - d->_q_changeServiceState(iter.value()->name(), newState); - } - - d->helloCondition.wakeAll(); - - } else if (op == 1) { - QWriteLocker lock(&d->pluginsLock); - - // Service Discovery - QStringList oldClientPlugins = d->clientPlugins; - in >> d->clientPlugins; - - for (DebugServiceConstIt iter = d->plugins.constBegin(), cend = d->plugins.constEnd(); iter != cend; ++iter) { - const QString pluginName = iter.key(); - QQmlDebugService::State newState = QQmlDebugService::Unavailable; - if (d->clientPlugins.contains(pluginName)) - newState = QQmlDebugService::Enabled; - - if (oldClientPlugins.contains(pluginName) - != d->clientPlugins.contains(pluginName)) { - d->changeServiceStateCalls.ref(); - d->_q_changeServiceState(iter.value()->name(), newState); - } - } - - } else { - qWarning("QML Debugger: Invalid control message %d.", op); - d->connection->disconnect(); - return; - } - - } else { - if (d->gotHello) { - QByteArray message; - in >> message; - - QReadLocker lock(&d->pluginsLock); - QHash<QString, QQmlDebugService *>::Iterator iter = d->plugins.find(name); - if (iter == d->plugins.end()) { - qWarning() << "QML Debugger: Message received for missing plugin" << name << '.'; - } else { - (*iter)->messageReceived(message); - } - } else { - qWarning("QML Debugger: Invalid hello message."); - } - - } -} - -void QQmlDebugServerPrivate::_q_changeServiceState(const QString &serviceName, - QQmlDebugService::State newState) -{ - // to be executed in debugger thread - Q_ASSERT(QThread::currentThread() == q_func()->thread()); - - QQmlDebugService *service = 0; - { - // Write lock here, because this can be called from receiveMessage which already has a write - // lock. We cannot downgrade it. We also don't want to give up the write lock and later get - // a read lock as that technique has great potential for deadlocks. - QWriteLocker lock(&pluginsLock); - service = plugins.value(serviceName); - } - - if (service && (service->d_func()->state != newState)) { - service->stateAboutToBeChanged(newState); - service->d_func()->state = newState; - service->stateChanged(newState); - } - - changeServiceStateCalls.deref(); -} - -void QQmlDebugServerPrivate::_q_sendMessages(const QList<QByteArray> &messages) -{ - // to be executed in debugger thread - Q_ASSERT(QThread::currentThread() == q_func()->thread()); - - if (connection) - connection->send(messages); -} - -void QQmlDebugServerPrivate::_q_removeThread() -{ - Q_ASSERT(thread->isFinished()); - Q_ASSERT(QThread::currentThread() == thread); - - QThread *parentThread = thread->thread(); - - // We cannot delete it right away as it will access its data after the finished() signal. - thread->deleteLater(); - thread = 0; - - delete connection; - connection = 0; - - // Move it back to the parent thread so that we can potentially restart it on a new thread. - q_func()->moveToThread(parentThread); -} - -QList<QQmlDebugService*> QQmlDebugServer::services() const -{ - Q_D(const QQmlDebugServer); - QReadLocker lock(&d->pluginsLock); - return d->plugins.values(); -} - -QStringList QQmlDebugServer::serviceNames() const -{ - Q_D(const QQmlDebugServer); - QReadLocker lock(&d->pluginsLock); - return d->plugins.keys(); -} - -void QQmlDebugServer::addEngine(QQmlEngine *engine) -{ - Q_D(QQmlDebugServer); - QWriteLocker lock(&d->pluginsLock); - - foreach (QQmlDebugService *service, d->plugins) - service->engineAboutToBeAdded(engine); - - d->engineConditions[engine].waitForServices(&d->pluginsLock, d->plugins.count()); - - foreach (QQmlDebugService *service, d->plugins) - service->engineAdded(engine); -} - -void QQmlDebugServer::removeEngine(QQmlEngine *engine) -{ - Q_D(QQmlDebugServer); - QWriteLocker lock(&d->pluginsLock); - - foreach (QQmlDebugService *service, d->plugins) - service->engineAboutToBeRemoved(engine); - - d->engineConditions[engine].waitForServices(&d->pluginsLock, d->plugins.count()); - - foreach (QQmlDebugService *service, d->plugins) - service->engineRemoved(engine); -} - -bool QQmlDebugServer::addService(QQmlDebugService *service) -{ - Q_D(QQmlDebugServer); - - // to be executed outside of debugger thread - Q_ASSERT(QThread::currentThread() != thread()); - - connect(service, SIGNAL(attachedToEngine(QQmlEngine*)), - this, SLOT(wakeEngine(QQmlEngine*)), Qt::QueuedConnection); - connect(service, SIGNAL(detachedFromEngine(QQmlEngine*)), - this, SLOT(wakeEngine(QQmlEngine*)), Qt::QueuedConnection); - - - QWriteLocker lock(&d->pluginsLock); - if (!service || d->plugins.contains(service->name())) - return false; - d->plugins.insert(service->name(), service); - d->advertisePlugins(); - QQmlDebugService::State newState = QQmlDebugService::Unavailable; - if (d->clientPlugins.contains(service->name())) - newState = QQmlDebugService::Enabled; - service->d_func()->state = newState; - return true; -} - -bool QQmlDebugServer::removeService(QQmlDebugService *service) -{ - Q_D(QQmlDebugServer); - - // to be executed outside of debugger thread - Q_ASSERT(QThread::currentThread() != thread()); - - QWriteLocker lock(&d->pluginsLock); - QQmlDebugService::State newState = QQmlDebugService::NotConnected; - - d->changeServiceStateCalls.ref(); - QMetaObject::invokeMethod(this, "_q_changeServiceState", Qt::QueuedConnection, - Q_ARG(QString, service->name()), - Q_ARG(QQmlDebugService::State, newState)); - - if (!service || !d->plugins.contains(service->name())) - return false; - d->plugins.remove(service->name()); - - d->advertisePlugins(); - - return true; -} - -void QQmlDebugServer::sendMessages(QQmlDebugService *service, - const QList<QByteArray> &messages) -{ - QList<QByteArray> prefixedMessages; - foreach (const QByteArray &message, messages) { - QByteArray prefixed; - QQmlDebugStream out(&prefixed, QIODevice::WriteOnly); - out << service->name() << message; - prefixedMessages << prefixed; - } - - QMetaObject::invokeMethod(this, "_q_sendMessages", Qt::QueuedConnection, - Q_ARG(QList<QByteArray>, prefixedMessages)); -} - -bool QQmlDebugServer::enable(int portFrom, int portTo, bool block, const QString &hostAddress) -{ -#ifndef QT_NO_QML_DEBUGGER - QQmlDebugServerInstanceWrapper *wrapper = debugServerInstance(); - if (!wrapper) - return false; - QQmlDebugServerPrivate *d = wrapper->m_instance.d_func(); - if (d->thread) - return false; - if (!d->start(portFrom, portTo, block, hostAddress, QLatin1String("qmldbg_tcp"))) - return false; - while (!wrapper->m_instance.hasConnection()) { - if (!wrapper->m_instance.hasThread()) - return false; - } - return true; -#else - Q_UNUSED(portFrom); - Q_UNUSED(portTo); - Q_UNUSED(block); - Q_UNUSED(hostAddress); - return false; -#endif -} - -void QQmlDebugServer::wakeEngine(QQmlEngine *engine) -{ - // to be executed in debugger thread - Q_ASSERT(QThread::currentThread() == thread()); - - Q_D(QQmlDebugServer); - QWriteLocker lock(&d->pluginsLock); - d->engineConditions[engine].wake(); -} - -bool QQmlDebugServerPrivate::EngineCondition::waitForServices(QReadWriteLock *locked, int num) -{ - // to be executed outside of debugger thread - Q_ASSERT(QThread::currentThread() != QQmlDebugServer::instance()->thread()); - - Q_ASSERT_X(numServices == 0, Q_FUNC_INFO, "Request to wait again before previous wait finished"); - numServices = num; - return condition->wait(locked); -} - -void QQmlDebugServerPrivate::EngineCondition::wake() -{ - if (--numServices == 0) - condition->wakeAll(); - Q_ASSERT_X(numServices >=0, Q_FUNC_INFO, "Woken more often than #services."); -} - -QT_END_NAMESPACE - -#include "moc_qqmldebugserver_p.cpp" diff --git a/src/qml/debugger/qqmldebugserver_p.h b/src/qml/debugger/qqmldebugserver_p.h deleted file mode 100644 index b2bd0b2463..0000000000 --- a/src/qml/debugger/qqmldebugserver_p.h +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLDEBUGSERVER_H -#define QQMLDEBUGSERVER_H - -#include <QtQml/qtqmlglobal.h> -#include <private/qqmldebugserverconnection_p.h> -#include <private/qqmldebugservice_p.h> - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -QT_BEGIN_NAMESPACE - - -class QQmlDebugServerPrivate; -class Q_QML_PRIVATE_EXPORT QQmlDebugServer : public QObject -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlDebugServer) - Q_DISABLE_COPY(QQmlDebugServer) -public: - - static QQmlDebugServer *instance(); - - bool hasThread() const; - bool hasConnection() const; - bool hasDebuggingClient() const; - bool blockingMode() const; - - QList<QQmlDebugService*> services() const; - QStringList serviceNames() const; - - void addEngine(QQmlEngine *engine); - void removeEngine(QQmlEngine *engine); - - bool addService(QQmlDebugService *service); - bool removeService(QQmlDebugService *service); - - void receiveMessage(const QByteArray &message); - - void sendMessages(QQmlDebugService *service, const QList<QByteArray> &messages); - static bool enable(int portFrom, int portTo, bool block, const QString &hostAddress); - -private slots: - void wakeEngine(QQmlEngine *engine); - -private: - friend class QQmlDebugService; - friend class QQmlDebugServicePrivate; - friend class QQmlDebugServerThread; - friend struct QQmlDebugServerInstanceWrapper; - QQmlDebugServer(); - Q_PRIVATE_SLOT(d_func(), void _q_changeServiceState(const QString &serviceName, - QQmlDebugService::State state)) - Q_PRIVATE_SLOT(d_func(), void _q_sendMessages(QList<QByteArray>)) - Q_PRIVATE_SLOT(d_func(), void _q_removeThread()) - -public: - static int s_dataStreamVersion; -}; - -QT_END_NAMESPACE - -#endif // QQMLDEBUGSERVICE_H diff --git a/src/qml/debugger/qqmldebugserverconnection_p.h b/src/qml/debugger/qqmldebugserverconnection_p.h deleted file mode 100644 index 9170238b46..0000000000 --- a/src/qml/debugger/qqmldebugserverconnection_p.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLDEBUGSERVERCONNECTION_H -#define QQMLDEBUGSERVERCONNECTION_H - -#include <QtQml/qtqmlglobal.h> -#include <private/qqmlglobal_p.h> - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -QT_BEGIN_NAMESPACE - - -class QQmlDebugServer; -class Q_QML_PRIVATE_EXPORT QQmlDebugServerConnection -{ -public: - QQmlDebugServerConnection() {} - virtual ~QQmlDebugServerConnection() {} - - virtual void setServer(QQmlDebugServer *server) = 0; - virtual bool setPortRange(int portFrom, int portTo, bool bock, const QString &hostaddress) = 0; - virtual bool isConnected() const = 0; - virtual void send(const QList<QByteArray> &messages) = 0; - virtual void disconnect() = 0; - virtual void waitForConnection() = 0; - virtual bool waitForMessage() = 0; -}; - -#define QQmlDebugServerConnection_iid "org.qt-project.Qt.QQmlDebugServerConnection" - -Q_DECLARE_INTERFACE(QQmlDebugServerConnection, QQmlDebugServerConnection_iid) - -QT_END_NAMESPACE - -#endif // QQMLDEBUGSERVERCONNECTION_H diff --git a/src/qml/debugger/qqmldebugservice.cpp b/src/qml/debugger/qqmldebugservice.cpp index b37a7335a0..0b07f320ec 100644 --- a/src/qml/debugger/qqmldebugservice.cpp +++ b/src/qml/debugger/qqmldebugservice.cpp @@ -32,8 +32,7 @@ ****************************************************************************/ #include "qqmldebugservice_p.h" -#include "qqmldebugservice_p_p.h" -#include "qqmldebugserver_p.h" +#include "qqmldebugconnector_p.h" #include <private/qqmldata_p.h> #include <private/qqmlcontext_p.h> @@ -43,58 +42,55 @@ QT_BEGIN_NAMESPACE -QQmlDebugServicePrivate::QQmlDebugServicePrivate() -{ -} +class QQmlDebugServer; -QQmlDebugService::QQmlDebugService(const QString &name, float version, QObject *parent) - : QObject(*(new QQmlDebugServicePrivate), parent) +class QQmlDebugServicePrivate : public QObjectPrivate { - QQmlDebugServer::instance(); // create it when it isn't there yet. + Q_DECLARE_PUBLIC(QQmlDebugService) +public: + QQmlDebugServicePrivate(const QString &name, float version); - Q_D(QQmlDebugService); - d->name = name; - d->version = version; - d->state = QQmlDebugService::NotConnected; -} + const QString name; + const float version; + QQmlDebugService::State state; +}; -QQmlDebugService::QQmlDebugService(QQmlDebugServicePrivate &dd, - const QString &name, float version, QObject *parent) - : QObject(dd, parent) +QQmlDebugServicePrivate::QQmlDebugServicePrivate(const QString &name, float version) : + name(name), version(version), state(QQmlDebugService::NotConnected) { - Q_D(QQmlDebugService); - d->name = name; - d->version = version; - d->state = QQmlDebugService::NotConnected; } -/** - Registers the service. This should be called in the constructor of the inherited class. From - then on the service might get asynchronous calls to messageReceived(). - */ -QQmlDebugService::State QQmlDebugService::registerService() +QQmlDebugService::QQmlDebugService(const QString &name, float version, QObject *parent) + : QObject(*(new QQmlDebugServicePrivate(name, version)), parent) { Q_D(QQmlDebugService); - QQmlDebugServer *server = QQmlDebugServer::instance(); + QQmlDebugConnector *server = QQmlDebugConnector::instance(); if (!server) - return NotConnected; + return; - if (server->serviceNames().contains(d->name)) { + if (server->service(d->name)) { qWarning() << "QQmlDebugService: Conflicting plugin name" << d->name; } else { - server->addService(this); + server->addService(d->name, this); } - return state(); } QQmlDebugService::~QQmlDebugService() { - if (QQmlDebugServer *inst = QQmlDebugServer::instance()) - inst->removeService(this); + Q_D(QQmlDebugService); + QQmlDebugConnector *server = QQmlDebugConnector::instance(); + + if (!server) + return; + + if (server->service(d->name) != this) + qWarning() << "QQmlDebugService: Plugin" << d->name << "is not registered."; + else + server->removeService(d->name); } -QString QQmlDebugService::name() const +const QString &QQmlDebugService::name() const { Q_D(const QQmlDebugService); return d->name; @@ -112,27 +108,38 @@ QQmlDebugService::State QQmlDebugService::state() const return d->state; } -namespace { - -struct ObjectReference +void QQmlDebugService::setState(QQmlDebugService::State newState) { - QPointer<QObject> object; - int id; -}; + Q_D(QQmlDebugService); + d->state = newState; +} -struct ObjectReferenceHash +namespace { +class ObjectReferenceHash : public QObject { + Q_OBJECT +public: ObjectReferenceHash() : nextId(0) {} - QHash<QObject *, ObjectReference> objects; + QHash<QObject *, int> objects; QHash<int, QObject *> ids; int nextId; -}; +private slots: + void remove(QObject *obj); +}; } Q_GLOBAL_STATIC(ObjectReferenceHash, objectReferenceHash) +void ObjectReferenceHash::remove(QObject *obj) +{ + QHash<QObject *, int>::Iterator iter = objects.find(obj); + if (iter != objects.end()) { + ids.remove(iter.value()); + objects.erase(iter); + } +} /*! Returns a unique id for \a object. Calling this method multiple times @@ -144,158 +151,23 @@ int QQmlDebugService::idForObject(QObject *object) return -1; ObjectReferenceHash *hash = objectReferenceHash(); - QHash<QObject *, ObjectReference>::Iterator iter = - hash->objects.find(object); + QHash<QObject *, int>::Iterator iter = hash->objects.find(object); if (iter == hash->objects.end()) { int id = hash->nextId++; - - hash->ids.insert(id, object); - iter = hash->objects.insert(object, ObjectReference()); - iter->object = object; - iter->id = id; - } else if (iter->object != object) { - int id = hash->nextId++; - - hash->ids.remove(iter->id); - hash->ids.insert(id, object); - iter->object = object; - iter->id = id; - } - return iter->id; -} - -/*! - Returns the object for unique \a id. If the object has not previously been - assigned an id, through idForObject(), then 0 is returned. If the object - has been destroyed, 0 is returned. -*/ -QObject *QQmlDebugService::objectForId(int id) -{ - ObjectReferenceHash *hash = objectReferenceHash(); - - QHash<int, QObject *>::Iterator iter = hash->ids.find(id); - if (iter == hash->ids.end()) - return 0; - - - QHash<QObject *, ObjectReference>::Iterator objIter = - hash->objects.find(*iter); - Q_ASSERT(objIter != hash->objects.end()); - - if (objIter->object == 0) { - hash->ids.erase(iter); - hash->objects.erase(objIter); - // run a loop to remove other invalid objects - removeInvalidObjectsFromHash(); - return 0; - } else { - return *iter; + iter = hash->objects.insert(object, id); + connect(object, SIGNAL(destroyed(QObject*)), hash, SLOT(remove(QObject*))); } + return iter.value(); } /*! - Returns a list of objects matching the given filename, line and column. + Returns the mapping of objects to unique \a ids, created through calls to idForObject(). */ -QList<QObject*> QQmlDebugService::objectForLocationInfo(const QString &filename, - int lineNumber, int columnNumber) -{ - ObjectReferenceHash *hash = objectReferenceHash(); - QList<QObject*> objects; - QHash<int, QObject *>::Iterator iter = hash->ids.begin(); - while (iter != hash->ids.end()) { - QHash<QObject *, ObjectReference>::Iterator objIter = - hash->objects.find(*iter); - Q_ASSERT(objIter != hash->objects.end()); - - if (objIter->object == 0) { - iter = hash->ids.erase(iter); - hash->objects.erase(objIter); - } else { - QQmlData *ddata = QQmlData::get(iter.value()); - if (ddata && ddata->outerContext) { - if (QFileInfo(ddata->outerContext->urlString()).fileName() == filename && - ddata->lineNumber == lineNumber && - ddata->columnNumber >= columnNumber) { - objects << *iter; - } - } - ++iter; - } - } - return objects; -} - -void QQmlDebugService::removeInvalidObjectsFromHash() -{ - ObjectReferenceHash *hash = objectReferenceHash(); - QHash<int, QObject *>::Iterator iter = hash->ids.begin(); - while (iter != hash->ids.end()) { - QHash<QObject *, ObjectReference>::Iterator objIter = - hash->objects.find(*iter); - Q_ASSERT(objIter != hash->objects.end()); - - if (objIter->object == 0) { - iter = hash->ids.erase(iter); - hash->objects.erase(objIter); - } else { - ++iter; - } - } -} - -void QQmlDebugService::clearObjectsFromHash() -{ - ObjectReferenceHash *hash = objectReferenceHash(); - hash->ids.clear(); - hash->objects.clear(); -} - -bool QQmlDebugService::isDebuggingEnabled() -{ - return QQmlDebugServer::instance() != 0; -} - -bool QQmlDebugService::hasDebuggingClient() +const QHash<int, QObject *> &QQmlDebugService::objectsForIds() { - return QQmlDebugServer::instance() != 0 - && QQmlDebugServer::instance()->hasDebuggingClient(); -} - -bool QQmlDebugService::blockingMode() -{ - return QQmlDebugServer::instance() != 0 - && QQmlDebugServer::instance()->blockingMode(); -} - -QString QQmlDebugService::objectToString(QObject *obj) -{ - if(!obj) - return QStringLiteral("NULL"); - - QString objectName = obj->objectName(); - if(objectName.isEmpty()) - objectName = QStringLiteral("<unnamed>"); - - QString rv = QString::fromUtf8(obj->metaObject()->className()) + - QLatin1String(": ") + objectName; - - return rv; -} - -void QQmlDebugService::sendMessage(const QByteArray &message) -{ - sendMessages(QList<QByteArray>() << message); -} - -void QQmlDebugService::sendMessages(const QList<QByteArray> &messages) -{ - if (state() != Enabled) - return; - - if (QQmlDebugServer *inst = QQmlDebugServer::instance()) - inst->sendMessages(this, messages); + return objectReferenceHash()->ids; } void QQmlDebugService::stateAboutToBeChanged(State) @@ -328,28 +200,32 @@ void QQmlDebugService::engineRemoved(QQmlEngine *) { } +int QQmlDebugStream::s_dataStreamVersion = QDataStream::Qt_4_7; + QQmlDebugStream::QQmlDebugStream() : QDataStream() { - setVersion(QQmlDebugServer::s_dataStreamVersion); + setVersion(s_dataStreamVersion); } QQmlDebugStream::QQmlDebugStream(QIODevice *d) : QDataStream(d) { - setVersion(QQmlDebugServer::s_dataStreamVersion); + setVersion(s_dataStreamVersion); } QQmlDebugStream::QQmlDebugStream(QByteArray *ba, QIODevice::OpenMode flags) : QDataStream(ba, flags) { - setVersion(QQmlDebugServer::s_dataStreamVersion); + setVersion(s_dataStreamVersion); } QQmlDebugStream::QQmlDebugStream(const QByteArray &ba) : QDataStream(ba) { - setVersion(QQmlDebugServer::s_dataStreamVersion); + setVersion(s_dataStreamVersion); } QT_END_NAMESPACE + +#include "qqmldebugservice.moc" diff --git a/src/qml/debugger/qqmldebugservice_p.h b/src/qml/debugger/qqmldebugservice_p.h index 9cdfc34ff1..3d692133cc 100644 --- a/src/qml/debugger/qqmldebugservice_p.h +++ b/src/qml/debugger/qqmldebugservice_p.h @@ -35,7 +35,8 @@ #define QQMLDEBUGSERVICE_H #include <QtCore/qobject.h> -#include <QtCore/QDataStream> +#include <QtCore/qdatastream.h> +#include <QtCore/qhash.h> #include <private/qtqmlglobal_p.h> @@ -62,35 +63,14 @@ class Q_QML_PRIVATE_EXPORT QQmlDebugService : public QObject Q_DISABLE_COPY(QQmlDebugService) public: - explicit QQmlDebugService(const QString &, float version, QObject *parent = 0); ~QQmlDebugService(); - QString name() const; + const QString &name() const; float version() const; enum State { NotConnected, Unavailable, Enabled }; State state() const; - - void sendMessage(const QByteArray &); - void sendMessages(const QList<QByteArray> &); - - static int idForObject(QObject *); - static QObject *objectForId(int); - static QList<QObject*> objectForLocationInfo(const QString &filename, - int lineNumber, int columnNumber); - static void removeInvalidObjectsFromHash(); - static void clearObjectsFromHash(); - - static QString objectToString(QObject *obj); - - static bool isDebuggingEnabled(); - static bool hasDebuggingClient(); - static bool blockingMode(); - -protected: - QQmlDebugService(QQmlDebugServicePrivate &dd, const QString &name, float version, QObject *parent = 0); - - State registerService(); + void setState(State newState); virtual void stateAboutToBeChanged(State); virtual void stateChanged(State); @@ -101,18 +81,26 @@ protected: virtual void engineAdded(QQmlEngine *); virtual void engineRemoved(QQmlEngine *); + static const QHash<int, QObject *> &objectsForIds(); + static int idForObject(QObject *); + static QObject *objectForId(int id) { return objectsForIds().value(id); } + +protected: + explicit QQmlDebugService(const QString &, float version, QObject *parent = 0); + signals: void attachedToEngine(QQmlEngine *); void detachedFromEngine(QQmlEngine *); -private: - friend class QQmlDebugServer; - friend class QQmlDebugServerPrivate; + void messageToClient(const QString &name, const QByteArray &message); + void messagesToClient(const QString &name, const QList<QByteArray> &messages); }; class Q_QML_PRIVATE_EXPORT QQmlDebugStream : public QDataStream { public: + static int s_dataStreamVersion; + QQmlDebugStream(); explicit QQmlDebugStream(QIODevice *d); QQmlDebugStream(QByteArray *ba, QIODevice::OpenMode flags); diff --git a/src/qml/debugger/qqmldebugservice_p_p.h b/src/qml/debugger/qqmldebugservicefactory_p.h index 7d60739f3a..af50cd4635 100644 --- a/src/qml/debugger/qqmldebugservice_p_p.h +++ b/src/qml/debugger/qqmldebugservicefactory_p.h @@ -31,8 +31,8 @@ ** ****************************************************************************/ -#ifndef QQMLDEBUGSERVICE_P_H -#define QQMLDEBUGSERVICE_P_H +#ifndef QQMLDEBUGSERVICEFACTORY_P_H +#define QQMLDEBUGSERVICEFACTORY_P_H // // W A R N I N G @@ -45,25 +45,19 @@ // We mean it. // -#include <QtCore/qglobal.h> -#include <private/qobject_p.h> +#include "qqmldebugservice_p.h" QT_BEGIN_NAMESPACE - -class QQmlDebugServer; - -class QQmlDebugServicePrivate : public QObjectPrivate +class Q_QML_PRIVATE_EXPORT QQmlDebugServiceFactory : public QObject { - Q_DECLARE_PUBLIC(QQmlDebugService) + Q_OBJECT public: - QQmlDebugServicePrivate(); - - QString name; - float version; - QQmlDebugService::State state; + virtual QQmlDebugService *create(const QString &key) = 0; }; +#define QQmlDebugServiceFactory_iid "org.qt-project.Qt.QQmlDebugServiceFactory" + QT_END_NAMESPACE -#endif // QQMLDEBUGSERVICE_P_H +#endif // QQMLDEBUGSERVICEFACTORY_P_H diff --git a/src/qml/jsruntime/qv4qmlextensions_p.h b/src/qml/debugger/qqmldebugserviceinterfaces.cpp index e4b9f75298..199c682748 100644 --- a/src/qml/jsruntime/qv4qmlextensions_p.h +++ b/src/qml/debugger/qqmldebugserviceinterfaces.cpp @@ -30,29 +30,15 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#ifndef QV4QMLEXTENSIONS_P_H -#define QV4QMLEXTENSIONS_P_H -#include <qtqmlglobal.h> -#include <qv4global_p.h> +#include "qqmldebugserviceinterfaces_p.h" QT_BEGIN_NAMESPACE -namespace QV4 { - -struct Q_QML_EXPORT QmlExtensions -{ - QmlExtensions() - : valueTypeWrapperPrototype(0) - {} - - Heap::Object *valueTypeWrapperPrototype; - - void markObjects(ExecutionEngine *e); -}; - -} // namespace QV4 +const QString QV4DebugService::s_key = QStringLiteral("V8Debugger"); +const QString QQmlEngineDebugService::s_key = QStringLiteral("QmlDebugger"); +const QString QQmlInspectorService::s_key = QStringLiteral("QmlInspector"); +const QString QQmlProfilerService::s_key = QStringLiteral("CanvasFrameRate"); +const QString QQmlNativeDebugService::s_key = QStringLiteral("NativeQmlDebugger"); QT_END_NAMESPACE - -#endif diff --git a/src/qml/debugger/qqmldebugserviceinterfaces_p.h b/src/qml/debugger/qqmldebugserviceinterfaces_p.h new file mode 100644 index 0000000000..6391bc6218 --- /dev/null +++ b/src/qml/debugger/qqmldebugserviceinterfaces_p.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDEBUGSERVICEINTERFACES_P_H +#define QQMLDEBUGSERVICEINTERFACES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qstring.h> +#include <private/qtqmlglobal_p.h> +#include <private/qqmldebugservice_p.h> +#include <private/qqmldebugstatesdelegate_p.h> +#include <private/qqmlabstractprofileradapter_p.h> +#include <private/qqmlboundsignal_p.h> + +#include <limits> + +QT_BEGIN_NAMESPACE + +class Q_QML_PRIVATE_EXPORT QV4DebugService : protected QQmlDebugService +{ + Q_OBJECT +public: + virtual void signalEmitted(const QString &signal) = 0; + +protected: + friend class QQmlDebugConnector; + + QV4DebugService(float version, QObject *parent = 0) : + QQmlDebugService(s_key, version, parent) {} + + static const QString s_key; +}; + +class Q_QML_PRIVATE_EXPORT QQmlProfilerService : protected QQmlDebugService +{ + Q_OBJECT +public: + virtual void addGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) = 0; + virtual void removeGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) = 0; + + virtual void startProfiling(QQmlEngine *engine, + quint64 features = std::numeric_limits<quint64>::max()) = 0; + virtual void stopProfiling(QQmlEngine *engine) = 0; + + virtual void dataReady(QQmlAbstractProfilerAdapter *profiler) = 0; + +protected: + friend class QQmlDebugConnector; + + QQmlProfilerService(float version, QObject *parent = 0) : + QQmlDebugService(s_key, version, parent) {} + + static const QString s_key; +}; + +class Q_QML_PRIVATE_EXPORT QQmlEngineDebugService : protected QQmlDebugService +{ + Q_OBJECT +public: + virtual void objectCreated(QQmlEngine *engine, QObject *object) = 0; + virtual void setStatesDelegate(QQmlDebugStatesDelegate *) = 0; + +protected: + friend class QQmlDebugConnector; + + QQmlEngineDebugService(float version, QObject *parent = 0) : + QQmlDebugService(s_key, version, parent) {} + + QQmlBoundSignal *nextSignal(QQmlBoundSignal *prev) { return prev->m_nextSignal; } + + static const QString s_key; +}; + +class Q_QML_PRIVATE_EXPORT QQmlInspectorService : protected QQmlDebugService +{ + Q_OBJECT +public: + virtual void addView(QObject *) = 0; + virtual void removeView(QObject *) = 0; + +protected: + friend class QQmlDebugConnector; + + QQmlInspectorService(float version, QObject *parent = 0) : + QQmlDebugService(s_key, version, parent) {} + + static const QString s_key; +}; + +class Q_QML_PRIVATE_EXPORT QQmlNativeDebugService : protected QQmlDebugService +{ + Q_OBJECT + +protected: + friend class QQmlDebugConnector; + + QQmlNativeDebugService(float version, QObject *parent = 0) + : QQmlDebugService(s_key, version, parent) {} + + static const QString s_key; +}; + +QT_END_NAMESPACE + +#endif // QQMLDEBUGSERVICEINTERFACES_P_H + diff --git a/src/qml/debugger/qqmlenginecontrolservice.cpp b/src/qml/debugger/qqmlenginecontrolservice.cpp deleted file mode 100644 index 07c6715524..0000000000 --- a/src/qml/debugger/qqmlenginecontrolservice.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QQmlEngine> -#include "qqmldebug.h" -#include "qqmlenginecontrolservice_p.h" - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(QQmlEngineControlService, qmlEngineControlService) - -QQmlEngineControlService::QQmlEngineControlService() : - QQmlDebugService(QStringLiteral("EngineControl"), 1) -{ - QMutexLocker lock(&dataMutex); - registerService(); -} - -QQmlEngineControlService *QQmlEngineControlService::instance() -{ - return qmlEngineControlService(); -} - -void QQmlEngineControlService::messageReceived(const QByteArray &message) -{ - QMutexLocker lock(&dataMutex); - QQmlDebugStream d(message); - int command; - int engineId; - d >> command >> engineId; - QQmlEngine *engine = qobject_cast<QQmlEngine *>(objectForId(engineId)); - if (command == StartWaitingEngine && startingEngines.contains(engine)) { - startingEngines.removeOne(engine); - emit attachedToEngine(engine); - } else if (command == StopWaitingEngine && stoppingEngines.contains(engine)) { - stoppingEngines.removeOne(engine); - emit detachedFromEngine(engine); - } -} - -void QQmlEngineControlService::engineAboutToBeAdded(QQmlEngine *engine) -{ - QMutexLocker lock(&dataMutex); - if (state() == Enabled) { - Q_ASSERT(!stoppingEngines.contains(engine)); - Q_ASSERT(!startingEngines.contains(engine)); - startingEngines.append(engine); - sendMessage(EngineAboutToBeAdded, engine); - } else { - emit attachedToEngine(engine); - } -} - -void QQmlEngineControlService::engineAboutToBeRemoved(QQmlEngine *engine) -{ - QMutexLocker lock(&dataMutex); - if (state() == Enabled) { - Q_ASSERT(!stoppingEngines.contains(engine)); - Q_ASSERT(!startingEngines.contains(engine)); - stoppingEngines.append(engine); - sendMessage(EngineAboutToBeRemoved, engine); - } else { - emit detachedFromEngine(engine); - } -} - -void QQmlEngineControlService::engineAdded(QQmlEngine *engine) -{ - if (state() == Enabled) { - QMutexLocker lock(&dataMutex); - Q_ASSERT(!startingEngines.contains(engine)); - Q_ASSERT(!stoppingEngines.contains(engine)); - sendMessage(EngineAdded, engine); - } -} - -void QQmlEngineControlService::engineRemoved(QQmlEngine *engine) -{ - if (state() == Enabled) { - QMutexLocker lock(&dataMutex); - Q_ASSERT(!startingEngines.contains(engine)); - Q_ASSERT(!stoppingEngines.contains(engine)); - sendMessage(EngineRemoved, engine); - } -} - -void QQmlEngineControlService::sendMessage(QQmlEngineControlService::MessageType type, QQmlEngine *engine) -{ - QByteArray message; - QQmlDebugStream d(&message, QIODevice::WriteOnly); - d << type << idForObject(engine); - QQmlDebugService::sendMessage(message); -} - -void QQmlEngineControlService::stateChanged(State) -{ - // We flush everything for any kind of state change, to avoid complicated timing issues. - QMutexLocker lock(&dataMutex); - foreach (QQmlEngine *engine, startingEngines) - emit attachedToEngine(engine); - startingEngines.clear(); - foreach (QQmlEngine *engine, stoppingEngines) - emit detachedFromEngine(engine); - stoppingEngines.clear(); -} - -QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlenginedebugservice.cpp b/src/qml/debugger/qqmlenginedebugservice.cpp deleted file mode 100644 index da01d00f17..0000000000 --- a/src/qml/debugger/qqmlenginedebugservice.cpp +++ /dev/null @@ -1,820 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlenginedebugservice_p.h" - -#include "qqmldebugstatesdelegate_p.h" -#include <private/qqmlboundsignal_p.h> -#include <qqmlengine.h> -#include <private/qqmlmetatype_p.h> -#include <qqmlproperty.h> -#include <private/qqmlproperty_p.h> -#include <private/qqmlbinding_p.h> -#include <private/qqmlcontext_p.h> -#include <private/qqmlwatcher_p.h> -#include <private/qqmlvaluetype_p.h> -#include <private/qqmlvmemetaobject_p.h> -#include <private/qqmlexpression_p.h> - -#include <QtCore/qdebug.h> -#include <QtCore/qmetaobject.h> -#include <private/qmetaobject_p.h> - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(QQmlEngineDebugService, qmlEngineDebugService) - -QQmlEngineDebugService *QQmlEngineDebugService::instance() -{ - return qmlEngineDebugService(); -} - -QQmlEngineDebugService::QQmlEngineDebugService(QObject *parent) - : QQmlDebugService(QStringLiteral("QmlDebugger"), 2, parent), - m_watch(new QQmlWatcher(this)), - m_statesDelegate(0) -{ - QObject::connect(m_watch, SIGNAL(propertyChanged(int,int,QMetaProperty,QVariant)), - this, SLOT(propertyChanged(int,int,QMetaProperty,QVariant))); - - registerService(); -} - -QQmlEngineDebugService::~QQmlEngineDebugService() -{ - delete m_statesDelegate; -} - -QDataStream &operator<<(QDataStream &ds, - const QQmlEngineDebugService::QQmlObjectData &data) -{ - ds << data.url << data.lineNumber << data.columnNumber << data.idString - << data.objectName << data.objectType << data.objectId << data.contextId - << data.parentId; - return ds; -} - -QDataStream &operator>>(QDataStream &ds, - QQmlEngineDebugService::QQmlObjectData &data) -{ - ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString - >> data.objectName >> data.objectType >> data.objectId >> data.contextId - >> data.parentId; - return ds; -} - -QDataStream &operator<<(QDataStream &ds, - const QQmlEngineDebugService::QQmlObjectProperty &data) -{ - ds << (int)data.type << data.name; - // check first whether the data can be saved - // (otherwise we assert in QVariant::operator<<) - QByteArray buffer; - QDataStream fakeStream(&buffer, QIODevice::WriteOnly); - if (QMetaType::save(fakeStream, data.value.type(), data.value.constData())) - ds << data.value; - else - ds << QVariant(); - ds << data.valueTypeName << data.binding << data.hasNotifySignal; - return ds; -} - -QDataStream &operator>>(QDataStream &ds, - QQmlEngineDebugService::QQmlObjectProperty &data) -{ - int type; - ds >> type >> data.name >> data.value >> data.valueTypeName - >> data.binding >> data.hasNotifySignal; - data.type = (QQmlEngineDebugService::QQmlObjectProperty::Type)type; - return ds; -} - -static inline bool isSignalPropertyName(const QString &signalName) -{ - // see QmlCompiler::isSignalPropertyName - return signalName.length() >= 3 && signalName.startsWith(QLatin1String("on")) && - signalName.at(2).isLetter() && signalName.at(2).isUpper(); -} - -static bool hasValidSignal(QObject *object, const QString &propertyName) -{ - if (!isSignalPropertyName(propertyName)) - return false; - - QString signalName = propertyName.mid(2); - signalName[0] = signalName.at(0).toLower(); - - int sigIdx = QQmlPropertyPrivate::findSignalByName(object->metaObject(), signalName.toLatin1()).methodIndex(); - - if (sigIdx == -1) - return false; - - return true; -} - -QQmlEngineDebugService::QQmlObjectProperty -QQmlEngineDebugService::propertyData(QObject *obj, int propIdx) -{ - QQmlObjectProperty rv; - - QMetaProperty prop = obj->metaObject()->property(propIdx); - - rv.type = QQmlObjectProperty::Unknown; - rv.valueTypeName = QString::fromUtf8(prop.typeName()); - rv.name = QString::fromUtf8(prop.name()); - rv.hasNotifySignal = prop.hasNotifySignal(); - QQmlAbstractBinding *binding = - QQmlPropertyPrivate::binding(QQmlProperty(obj, rv.name)); - if (binding) - rv.binding = binding->expression(); - - if (QQmlValueTypeFactory::isValueType(prop.userType())) { - rv.type = QQmlObjectProperty::Basic; - } else if (QQmlMetaType::isQObject(prop.userType())) { - rv.type = QQmlObjectProperty::Object; - } else if (QQmlMetaType::isList(prop.userType())) { - rv.type = QQmlObjectProperty::List; - } else if (prop.userType() == QMetaType::QVariant) { - rv.type = QQmlObjectProperty::Variant; - } - - QVariant value; - if (rv.type != QQmlObjectProperty::Unknown && prop.userType() != 0) { - value = prop.read(obj); - } - rv.value = valueContents(value); - - return rv; -} - -QVariant QQmlEngineDebugService::valueContents(QVariant value) const -{ - // We can't send JS objects across the wire, so transform them to variant - // maps for serialization. - if (value.userType() == qMetaTypeId<QJSValue>()) - value = value.value<QJSValue>().toVariant(); - const int userType = value.userType(); - - //QObject * is not streamable. - //Convert all such instances to a String value - - if (value.type() == QVariant::List) { - QVariantList contents; - QVariantList list = value.toList(); - int count = list.size(); - for (int i = 0; i < count; i++) - contents << valueContents(list.at(i)); - return contents; - } - - if (value.type() == QVariant::Map) { - QVariantMap contents; - QMapIterator<QString, QVariant> i(value.toMap()); - while (i.hasNext()) { - i.next(); - contents.insert(i.key(), valueContents(i.value())); - } - return contents; - } - - if (QQmlValueTypeFactory::isValueType(userType)) { - const QMetaObject *mo = QQmlValueTypeFactory::metaObjectForMetaType(userType); - if (mo) { - int toStringIndex = mo->indexOfMethod("toString"); - if (toStringIndex != -1) { - QMetaMethod mm = mo->method(toStringIndex); - QMetaType info(userType); - QString s; - if (info.flags() & QMetaType::IsGadget - && mm.invokeOnGadget(value.data(), Q_RETURN_ARG(QString, s))) - return s; - } - } - - return value; - } - - if (QQmlMetaType::isQObject(userType)) { - QObject *o = QQmlMetaType::toQObject(value); - if (o) { - QString name = o->objectName(); - if (name.isEmpty()) - name = QStringLiteral("<unnamed object>"); - return name; - } - } - - return QString(QStringLiteral("<unknown value>")); -} - -void QQmlEngineDebugService::buildObjectDump(QDataStream &message, - QObject *object, bool recur, bool dumpProperties) -{ - message << objectData(object); - - QObjectList children = object->children(); - - int childrenCount = children.count(); - for (int ii = 0; ii < children.count(); ++ii) { - if (qobject_cast<QQmlContext*>(children[ii])) - --childrenCount; - } - - message << childrenCount << recur; - - QList<QQmlObjectProperty> fakeProperties; - - for (int ii = 0; ii < children.count(); ++ii) { - QObject *child = children.at(ii); - if (qobject_cast<QQmlContext*>(child)) - continue; - if (recur) - buildObjectDump(message, child, recur, dumpProperties); - else - message << objectData(child); - } - - if (!dumpProperties) { - message << 0; - return; - } - - QList<int> propertyIndexes; - for (int ii = 0; ii < object->metaObject()->propertyCount(); ++ii) { - if (object->metaObject()->property(ii).isScriptable()) - propertyIndexes << ii; - } - - QQmlData *ddata = QQmlData::get(object); - if (ddata && ddata->signalHandlers) { - QQmlAbstractBoundSignal *signalHandler = ddata->signalHandlers; - - while (signalHandler) { - if (!dumpProperties) { - signalHandler = signalHandler->m_nextSignal; - continue; - } - QQmlObjectProperty prop; - prop.type = QQmlObjectProperty::SignalProperty; - prop.hasNotifySignal = false; - QQmlBoundSignalExpression *expr = signalHandler->expression(); - if (expr) { - prop.value = expr->expression(); - QObject *scope = expr->scopeObject(); - if (scope) { - QString methodName = QString::fromLatin1(QMetaObjectPrivate::signal(scope->metaObject(), signalHandler->index()).name()); - if (!methodName.isEmpty()) { - prop.name = QLatin1String("on") + methodName[0].toUpper() - + methodName.mid(1); - } - } - } - fakeProperties << prop; - - signalHandler = signalHandler->m_nextSignal; - } - } - - message << propertyIndexes.size() + fakeProperties.count(); - - for (int ii = 0; ii < propertyIndexes.size(); ++ii) - message << propertyData(object, propertyIndexes.at(ii)); - - for (int ii = 0; ii < fakeProperties.count(); ++ii) - message << fakeProperties[ii]; -} - -void QQmlEngineDebugService::prepareDeferredObjects(QObject *obj) -{ - qmlExecuteDeferred(obj); - - QObjectList children = obj->children(); - for (int ii = 0; ii < children.count(); ++ii) { - QObject *child = children.at(ii); - prepareDeferredObjects(child); - } - -} - -void QQmlEngineDebugService::storeObjectIds(QObject *co) -{ - QQmlDebugService::idForObject(co); - QObjectList children = co->children(); - for (int ii = 0; ii < children.count(); ++ii) - storeObjectIds(children.at(ii)); -} - -void QQmlEngineDebugService::buildObjectList(QDataStream &message, - QQmlContext *ctxt, - const QList<QPointer<QObject> > &instances) -{ - QQmlContextData *p = QQmlContextData::get(ctxt); - - QString ctxtName = ctxt->objectName(); - int ctxtId = QQmlDebugService::idForObject(ctxt); - if (ctxt->contextObject()) - storeObjectIds(ctxt->contextObject()); - - message << ctxtName << ctxtId; - - int count = 0; - - QQmlContextData *child = p->childContexts; - while (child) { - ++count; - child = child->nextChild; - } - - message << count; - - child = p->childContexts; - while (child) { - buildObjectList(message, child->asQQmlContext(), instances); - child = child->nextChild; - } - - count = 0; - for (int ii = 0; ii < instances.count(); ++ii) { - QQmlData *data = QQmlData::get(instances.at(ii)); - if (data->context == p) - count ++; - } - message << count; - - for (int ii = 0; ii < instances.count(); ++ii) { - QQmlData *data = QQmlData::get(instances.at(ii)); - if (data->context == p) - message << objectData(instances.at(ii)); - } -} - -void QQmlEngineDebugService::buildStatesList(bool cleanList, - const QList<QPointer<QObject> > &instances) -{ - if (m_statesDelegate) - m_statesDelegate->buildStatesList(cleanList, instances); -} - -QQmlEngineDebugService::QQmlObjectData -QQmlEngineDebugService::objectData(QObject *object) -{ - QQmlData *ddata = QQmlData::get(object); - QQmlObjectData rv; - if (ddata && ddata->outerContext) { - rv.url = ddata->outerContext->url(); - rv.lineNumber = ddata->lineNumber; - rv.columnNumber = ddata->columnNumber; - } else { - rv.lineNumber = -1; - rv.columnNumber = -1; - } - - QQmlContext *context = qmlContext(object); - if (context) { - QQmlContextData *cdata = QQmlContextData::get(context); - if (cdata) - rv.idString = cdata->findObjectId(object); - } - - rv.objectName = object->objectName(); - rv.objectId = QQmlDebugService::idForObject(object); - rv.contextId = QQmlDebugService::idForObject(qmlContext(object)); - rv.parentId = QQmlDebugService::idForObject(object->parent()); - QQmlType *type = QQmlMetaType::qmlType(object->metaObject()); - if (type) { - QString typeName = type->qmlTypeName(); - int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); - rv.objectType = lastSlash < 0 ? typeName : typeName.mid(lastSlash+1); - } else { - rv.objectType = QString::fromUtf8(object->metaObject()->className()); - int marker = rv.objectType.indexOf(QLatin1String("_QMLTYPE_")); - if (marker != -1) - rv.objectType = rv.objectType.left(marker); - } - - return rv; -} - -void QQmlEngineDebugService::messageReceived(const QByteArray &message) -{ - QMetaObject::invokeMethod(this, "processMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message)); -} - -void QQmlEngineDebugService::processMessage(const QByteArray &message) -{ - QQmlDebugStream ds(message); - - QByteArray type; - int queryId; - ds >> type >> queryId; - - QByteArray reply; - QQmlDebugStream rs(&reply, QIODevice::WriteOnly); - - if (type == "LIST_ENGINES") { - rs << QByteArray("LIST_ENGINES_R"); - rs << queryId << m_engines.count(); - - for (int ii = 0; ii < m_engines.count(); ++ii) { - QQmlEngine *engine = m_engines.at(ii); - - QString engineName = engine->objectName(); - int engineId = QQmlDebugService::idForObject(engine); - - rs << engineName << engineId; - } - - } else if (type == "LIST_OBJECTS") { - int engineId = -1; - ds >> engineId; - - QQmlEngine *engine = - qobject_cast<QQmlEngine *>(QQmlDebugService::objectForId(engineId)); - - rs << QByteArray("LIST_OBJECTS_R") << queryId; - - if (engine) { - QQmlContext *rootContext = engine->rootContext(); - // Clean deleted objects - QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(rootContext); - for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) { - if (!ctxtPriv->instances.at(ii)) { - ctxtPriv->instances.removeAt(ii); - --ii; - } - } - buildObjectList(rs, rootContext, ctxtPriv->instances); - buildStatesList(true, ctxtPriv->instances); - } - - } else if (type == "FETCH_OBJECT") { - int objectId; - bool recurse; - bool dumpProperties = true; - - ds >> objectId >> recurse >> dumpProperties; - - QObject *object = QQmlDebugService::objectForId(objectId); - - rs << QByteArray("FETCH_OBJECT_R") << queryId; - - if (object) { - if (recurse) - prepareDeferredObjects(object); - buildObjectDump(rs, object, recurse, dumpProperties); - } - - } else if (type == "FETCH_OBJECTS_FOR_LOCATION") { - QString file; - int lineNumber; - int columnNumber; - bool recurse; - bool dumpProperties = true; - - ds >> file >> lineNumber >> columnNumber >> recurse >> dumpProperties; - - QList<QObject*> objects = QQmlDebugService::objectForLocationInfo( - file, lineNumber, columnNumber); - - rs << QByteArray("FETCH_OBJECTS_FOR_LOCATION_R") << queryId - << objects.count(); - - foreach (QObject *object, objects) { - if (recurse) - prepareDeferredObjects(object); - buildObjectDump(rs, object, recurse, dumpProperties); - } - - } else if (type == "WATCH_OBJECT") { - int objectId; - - ds >> objectId; - bool ok = m_watch->addWatch(queryId, objectId); - - rs << QByteArray("WATCH_OBJECT_R") << queryId << ok; - - } else if (type == "WATCH_PROPERTY") { - int objectId; - QByteArray property; - - ds >> objectId >> property; - bool ok = m_watch->addWatch(queryId, objectId, property); - - rs << QByteArray("WATCH_PROPERTY_R") << queryId << ok; - - } else if (type == "WATCH_EXPR_OBJECT") { - int debugId; - QString expr; - - ds >> debugId >> expr; - bool ok = m_watch->addWatch(queryId, debugId, expr); - - rs << QByteArray("WATCH_EXPR_OBJECT_R") << queryId << ok; - - } else if (type == "NO_WATCH") { - bool ok = m_watch->removeWatch(queryId); - - rs << QByteArray("NO_WATCH_R") << queryId << ok; - - } else if (type == "EVAL_EXPRESSION") { - int objectId; - QString expr; - - ds >> objectId >> expr; - int engineId = -1; - if (!ds.atEnd()) - ds >> engineId; - - QObject *object = QQmlDebugService::objectForId(objectId); - QQmlContext *context = qmlContext(object); - if (!context) { - QQmlEngine *engine = qobject_cast<QQmlEngine *>( - QQmlDebugService::objectForId(engineId)); - if (engine && m_engines.contains(engine)) - context = engine->rootContext(); - } - QVariant result; - if (context) { - QQmlExpression exprObj(context, object, expr); - bool undefined = false; - QVariant value = exprObj.evaluate(&undefined); - if (undefined) - result = QString(QStringLiteral("<undefined>")); - else - result = valueContents(value); - } else { - result = QString(QStringLiteral("<unknown context>")); - } - - rs << QByteArray("EVAL_EXPRESSION_R") << queryId << result; - - } else if (type == "SET_BINDING") { - int objectId; - QString propertyName; - QVariant expr; - bool isLiteralValue; - QString filename; - int line; - ds >> objectId >> propertyName >> expr >> isLiteralValue >> - filename >> line; - bool ok = setBinding(objectId, propertyName, expr, isLiteralValue, - filename, line); - - rs << QByteArray("SET_BINDING_R") << queryId << ok; - - } else if (type == "RESET_BINDING") { - int objectId; - QString propertyName; - ds >> objectId >> propertyName; - bool ok = resetBinding(objectId, propertyName); - - rs << QByteArray("RESET_BINDING_R") << queryId << ok; - - } else if (type == "SET_METHOD_BODY") { - int objectId; - QString methodName; - QString methodBody; - ds >> objectId >> methodName >> methodBody; - bool ok = setMethodBody(objectId, methodName, methodBody); - - rs << QByteArray("SET_METHOD_BODY_R") << queryId << ok; - - } - sendMessage(reply); -} - -bool QQmlEngineDebugService::setBinding(int objectId, - const QString &propertyName, - const QVariant &expression, - bool isLiteralValue, - QString filename, - int line, - int column) -{ - bool ok = true; - QObject *object = objectForId(objectId); - QQmlContext *context = qmlContext(object); - - if (object && context) { - QQmlProperty property(object, propertyName, context); - if (property.isValid()) { - - bool inBaseState = true; - if (m_statesDelegate) { - m_statesDelegate->updateBinding(context, property, expression, isLiteralValue, - filename, line, column, &inBaseState); - } - - if (inBaseState) { - if (isLiteralValue) { - property.write(expression); - } else if (hasValidSignal(object, propertyName)) { - QQmlBoundSignalExpression *qmlExpression = new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(property)->signalIndex(), - QQmlContextData::get(context), object, expression.toString(), - filename, line, column); - QQmlPropertyPrivate::takeSignalExpression(property, qmlExpression); - } else if (property.isProperty()) { - QQmlBinding *binding = new QQmlBinding(expression.toString(), object, QQmlContextData::get(context), filename, line, column);; - binding->setTarget(property); - QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::setBinding(property, binding); - if (oldBinding) - oldBinding->destroy(); - binding->update(); - } else { - ok = false; - qWarning() << "QQmlEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object; - } - } - - } else { - // not a valid property - if (m_statesDelegate) - ok = m_statesDelegate->setBindingForInvalidProperty(object, propertyName, expression, isLiteralValue); - if (!ok) - qWarning() << "QQmlEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object; - } - } - return ok; -} - -bool QQmlEngineDebugService::resetBinding(int objectId, const QString &propertyName) -{ - QObject *object = objectForId(objectId); - QQmlContext *context = qmlContext(object); - - if (object && context) { - QString parentProperty = propertyName; - if (propertyName.indexOf(QLatin1Char('.')) != -1) - parentProperty = propertyName.left(propertyName.indexOf(QLatin1Char('.'))); - - if (object->property(parentProperty.toLatin1()).isValid()) { - QQmlProperty property(object, propertyName); - QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(property); - if (oldBinding) { - QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::setBinding(property, 0); - if (oldBinding) - oldBinding->destroy(); - } - if (property.isResettable()) { - // Note: this will reset the property in any case, without regard to states - // Right now almost no QQuickItem has reset methods for its properties (with the - // notable exception of QQuickAnchors), so this is not a big issue - // later on, setBinding does take states into account - property.reset(); - } else { - // overwrite with default value - if (QQmlType *objType = QQmlMetaType::qmlType(object->metaObject())) { - if (QObject *emptyObject = objType->create()) { - if (emptyObject->property(parentProperty.toLatin1()).isValid()) { - QVariant defaultValue = QQmlProperty(emptyObject, propertyName).read(); - if (defaultValue.isValid()) { - setBinding(objectId, propertyName, defaultValue, true); - } - } - delete emptyObject; - } - } - } - return true; - } - - if (hasValidSignal(object, propertyName)) { - QQmlProperty property(object, propertyName, context); - QQmlPropertyPrivate::setSignalExpression(property, 0); - return true; - } - - if (m_statesDelegate) { - m_statesDelegate->resetBindingForInvalidProperty(object, propertyName); - return true; - } - - return false; - } - // object or context null. - return false; -} - -bool QQmlEngineDebugService::setMethodBody(int objectId, const QString &method, const QString &body) -{ - QObject *object = objectForId(objectId); - QQmlContext *context = qmlContext(object); - if (!object || !context || !context->engine()) - return false; - QQmlContextData *contextData = QQmlContextData::get(context); - if (!contextData) - return false; - - QQmlPropertyData dummy; - QQmlPropertyData *prop = - QQmlPropertyCache::property(context->engine(), object, method, contextData, dummy); - - if (!prop || !prop->isVMEFunction()) - return false; - - QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex); - QList<QByteArray> paramNames = metaMethod.parameterNames(); - - QString paramStr; - for (int ii = 0; ii < paramNames.count(); ++ii) { - if (ii != 0) paramStr.append(QLatin1Char(',')); - paramStr.append(QString::fromUtf8(paramNames.at(ii))); - } - - QString jsfunction = QLatin1String("(function ") + method + QLatin1Char('(') + paramStr + - QLatin1String(") {"); - jsfunction += body; - jsfunction += QLatin1String("\n})"); - - QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(object); - Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this - - int lineNumber = vmeMetaObject->vmeMethodLineNumber(prop->coreIndex); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(qmlEngine(object)->handle()); - QV4::Scope scope(v4); - QV4::ScopedValue v(scope, QQmlExpressionPrivate::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber)); - vmeMetaObject->setVmeMethod(prop->coreIndex, v); - return true; -} - -void QQmlEngineDebugService::propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value) -{ - QByteArray reply; - QQmlDebugStream rs(&reply, QIODevice::WriteOnly); - - rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value); - - sendMessage(reply); -} - -void QQmlEngineDebugService::engineAboutToBeAdded(QQmlEngine *engine) -{ - Q_ASSERT(engine); - Q_ASSERT(!m_engines.contains(engine)); - - m_engines.append(engine); - emit attachedToEngine(engine); -} - -void QQmlEngineDebugService::engineAboutToBeRemoved(QQmlEngine *engine) -{ - Q_ASSERT(engine); - Q_ASSERT(m_engines.contains(engine)); - - m_engines.removeAll(engine); - emit detachedFromEngine(engine); -} - -void QQmlEngineDebugService::objectCreated(QQmlEngine *engine, QObject *object) -{ - Q_ASSERT(engine); - Q_ASSERT(m_engines.contains(engine)); - - int engineId = QQmlDebugService::idForObject(engine); - int objectId = QQmlDebugService::idForObject(object); - int parentId = QQmlDebugService::idForObject(object->parent()); - - QByteArray reply; - QQmlDebugStream rs(&reply, QIODevice::WriteOnly); - - //unique queryId -1 - rs << QByteArray("OBJECT_CREATED") << -1 << engineId << objectId << parentId; - sendMessage(reply); -} - -void QQmlEngineDebugService::setStatesDelegate(QQmlDebugStatesDelegate *delegate) -{ - m_statesDelegate = delegate; -} - -QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlenginedebugservice_p.h b/src/qml/debugger/qqmlenginedebugservice_p.h deleted file mode 100644 index 0a824b132f..0000000000 --- a/src/qml/debugger/qqmlenginedebugservice_p.h +++ /dev/null @@ -1,132 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLENGINEDEBUGSERVICE_P_H -#define QQMLENGINEDEBUGSERVICE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmldebugservice_p.h> - -#include <QtCore/qurl.h> -#include <QtCore/qvariant.h> -#include <QtCore/QPointer> - -QT_BEGIN_NAMESPACE - -class QQmlEngine; -class QQmlContext; -class QQmlWatcher; -class QDataStream; -class QQmlDebugStatesDelegate; - -class Q_QML_PRIVATE_EXPORT QQmlEngineDebugService : public QQmlDebugService -{ - Q_OBJECT -public: - QQmlEngineDebugService(QObject * = 0); - ~QQmlEngineDebugService(); - - struct QQmlObjectData { - QUrl url; - int lineNumber; - int columnNumber; - QString idString; - QString objectName; - QString objectType; - int objectId; - int contextId; - int parentId; - }; - - struct QQmlObjectProperty { - enum Type { Unknown, Basic, Object, List, SignalProperty, Variant }; - Type type; - QString name; - QVariant value; - QString valueTypeName; - QString binding; - bool hasNotifySignal; - }; - - void engineAboutToBeAdded(QQmlEngine *); - void engineAboutToBeRemoved(QQmlEngine *); - void objectCreated(QQmlEngine *, QObject *); - - void setStatesDelegate(QQmlDebugStatesDelegate *); - - static QQmlEngineDebugService *instance(); - -protected: - virtual void messageReceived(const QByteArray &); - -private Q_SLOTS: - void processMessage(const QByteArray &msg); - void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value); - -private: - void prepareDeferredObjects(QObject *); - void buildObjectList(QDataStream &, QQmlContext *, - const QList<QPointer<QObject> > &instances); - void buildObjectDump(QDataStream &, QObject *, bool, bool); - void buildStatesList(bool cleanList, const QList<QPointer<QObject> > &instances); - QQmlObjectData objectData(QObject *); - QQmlObjectProperty propertyData(QObject *, int); - QVariant valueContents(QVariant defaultValue) const; - bool setBinding(int objectId, const QString &propertyName, const QVariant &expression, bool isLiteralValue, QString filename = QString(), int line = -1, int column = 0); - bool resetBinding(int objectId, const QString &propertyName); - bool setMethodBody(int objectId, const QString &method, const QString &body); - void storeObjectIds(QObject *co); - - QList<QQmlEngine *> m_engines; - QQmlWatcher *m_watch; - QQmlDebugStatesDelegate *m_statesDelegate; -}; -Q_QML_PRIVATE_EXPORT QDataStream &operator<<(QDataStream &, const QQmlEngineDebugService::QQmlObjectData &); -Q_QML_PRIVATE_EXPORT QDataStream &operator>>(QDataStream &, QQmlEngineDebugService::QQmlObjectData &); -Q_QML_PRIVATE_EXPORT QDataStream &operator<<(QDataStream &, const QQmlEngineDebugService::QQmlObjectProperty &); -Q_QML_PRIVATE_EXPORT QDataStream &operator>>(QDataStream &, QQmlEngineDebugService::QQmlObjectProperty &); - -QT_END_NAMESPACE - -#endif // QQMLENGINEDEBUGSERVICE_P_H - diff --git a/src/qml/debugger/qqmlinspectorinterface_p.h b/src/qml/debugger/qqmlinspectorinterface_p.h deleted file mode 100644 index 9b29d383c7..0000000000 --- a/src/qml/debugger/qqmlinspectorinterface_p.h +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLINSPECTORINTERFACE_H -#define QQMLINSPECTORINTERFACE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtQml/qtqmlglobal.h> -#include <private/qqmlglobal_p.h> - -QT_BEGIN_NAMESPACE - - -class Q_QML_PRIVATE_EXPORT QQmlInspectorInterface -{ -public: - QQmlInspectorInterface() {} - virtual ~QQmlInspectorInterface() {} - - virtual bool canHandleView(QObject *view) = 0; - - virtual void activate(QObject *view) = 0; - virtual void deactivate() = 0; - - virtual void clientMessage(const QByteArray &message) = 0; -}; - -#define QQmlInspectorInterface_iid "org.qt-project.Qt.QQmlInspectorInterface" - -Q_DECLARE_INTERFACE(QQmlInspectorInterface, QQmlInspectorInterface_iid) - -QT_END_NAMESPACE - -#endif // QQMLINSPECTORINTERFACE_H diff --git a/src/qml/debugger/qqmlinspectorservice.cpp b/src/qml/debugger/qqmlinspectorservice.cpp deleted file mode 100644 index 5a8c4487d9..0000000000 --- a/src/qml/debugger/qqmlinspectorservice.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlinspectorservice_p.h" -#include "qqmlinspectorinterface_p.h" -#include "qqmldebugserver_p.h" - -#include <private/qqmlglobal_p.h> - -#include <QtCore/QCoreApplication> -#include <QtCore/QDebug> -#include <QtCore/QDir> -#include <QtCore/QPluginLoader> - -// print detailed information about loading of plugins -DEFINE_BOOL_CONFIG_OPTION(qmlDebugVerbose, QML_DEBUGGER_VERBOSE) - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(QQmlInspectorService, serviceInstance) - -QQmlInspectorService::QQmlInspectorService() - : QQmlDebugService(QStringLiteral("QmlInspector"), 1) - , m_currentInspectorPlugin(0) -{ - registerService(); -} - -QQmlInspectorService *QQmlInspectorService::instance() -{ - return serviceInstance(); -} - -void QQmlInspectorService::addView(QObject *view) -{ - m_views.append(view); - updateState(); -} - -void QQmlInspectorService::removeView(QObject *view) -{ - m_views.removeAll(view); - updateState(); -} - -void QQmlInspectorService::sendMessage(const QByteArray &message) -{ - if (state() != Enabled) - return; - - QQmlDebugService::sendMessage(message); -} - -void QQmlInspectorService::stateChanged(State /*state*/) -{ - QMetaObject::invokeMethod(this, "updateState", Qt::QueuedConnection); -} - -void QQmlInspectorService::updateState() -{ - if (m_views.isEmpty()) { - if (m_currentInspectorPlugin) { - m_currentInspectorPlugin->deactivate(); - m_currentInspectorPlugin = 0; - } - return; - } - - if (state() == Enabled) { - if (m_inspectorPlugins.isEmpty()) - loadInspectorPlugins(); - - if (m_inspectorPlugins.isEmpty()) { - qWarning() << "QQmlInspector: No plugins found."; - QQmlDebugServer::instance()->removeService(this); - return; - } - - m_currentInspectorPlugin = 0; - foreach (QQmlInspectorInterface *inspector, m_inspectorPlugins) { - if (inspector->canHandleView(m_views.first())) { - m_currentInspectorPlugin = inspector; - break; - } - } - - if (!m_currentInspectorPlugin) { - qWarning() << "QQmlInspector: No plugin available for view '" << m_views.first()->metaObject()->className() << "'."; - return; - } - m_currentInspectorPlugin->activate(m_views.first()); - } else { - if (m_currentInspectorPlugin) { - m_currentInspectorPlugin->deactivate(); - m_currentInspectorPlugin = 0; - } - } -} - -void QQmlInspectorService::messageReceived(const QByteArray &message) -{ - QMetaObject::invokeMethod(this, "processMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message)); -} - -void QQmlInspectorService::processMessage(const QByteArray &message) -{ - if (m_currentInspectorPlugin) - m_currentInspectorPlugin->clientMessage(message); -} - -void QQmlInspectorService::loadInspectorPlugins() -{ - QStringList pluginCandidates; - const QStringList paths = QCoreApplication::libraryPaths(); - foreach (const QString &libPath, paths) { - const QDir dir(libPath + QLatin1String("/qmltooling")); - if (dir.exists()) - foreach (const QString &pluginPath, dir.entryList(QDir::Files)) - pluginCandidates << dir.absoluteFilePath(pluginPath); - } - - foreach (const QString &pluginPath, pluginCandidates) { - if (pluginPath.contains(QLatin1String("qmldbg_tcp"))) - continue; - if (qmlDebugVerbose()) - qDebug() << "QQmlInspector: Trying to load plugin " << pluginPath << "..."; - - QPluginLoader loader(pluginPath); - if (!loader.load()) { - if (qmlDebugVerbose()) - qDebug() << "QQmlInspector: Error while loading: " << loader.errorString(); - - continue; - } - - QQmlInspectorInterface *inspector = - qobject_cast<QQmlInspectorInterface*>(loader.instance()); - if (inspector) { - if (qmlDebugVerbose()) - qDebug() << "QQmlInspector: Plugin successfully loaded."; - m_inspectorPlugins << inspector; - } else { - if (qmlDebugVerbose()) - qDebug() << "QQmlInspector: Plugin does not implement interface QQmlInspectorInterface."; - - loader.unload(); - } - } -} - -QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlprofiler.cpp b/src/qml/debugger/qqmlprofiler.cpp index b35da8a714..a6423d769a 100644 --- a/src/qml/debugger/qqmlprofiler.cpp +++ b/src/qml/debugger/qqmlprofiler.cpp @@ -32,88 +32,13 @@ ****************************************************************************/ #include "qqmlprofiler_p.h" -#include "qqmlprofilerservice_p.h" #include "qqmldebugservice_p.h" QT_BEGIN_NAMESPACE -// convert to QByteArrays that can be sent to the debug client -// use of QDataStream can skew results -// (see tst_qqmldebugtrace::trace() benchmark) -void QQmlProfilerData::toByteArrays(QList<QByteArray> &messages) const -{ - QByteArray data; - Q_ASSERT_X(((messageType | detailType) & (1 << 31)) == 0, Q_FUNC_INFO, "You can use at most 31 message types and 31 detail types."); - for (uint decodedMessageType = 0; (messageType >> decodedMessageType) != 0; ++decodedMessageType) { - if ((messageType & (1 << decodedMessageType)) == 0) - continue; - - for (uint decodedDetailType = 0; (detailType >> decodedDetailType) != 0; ++decodedDetailType) { - if ((detailType & (1 << decodedDetailType)) == 0) - continue; - - //### using QDataStream is relatively expensive - QQmlDebugStream ds(&data, QIODevice::WriteOnly); - ds << time << decodedMessageType << decodedDetailType; - - switch (decodedMessageType) { - case QQmlProfilerDefinitions::RangeStart: - if (decodedDetailType == (int)QQmlProfilerDefinitions::Binding) - ds << QQmlProfilerDefinitions::QmlBinding; - break; - case QQmlProfilerDefinitions::RangeData: - ds << detailString; - break; - case QQmlProfilerDefinitions::RangeLocation: - ds << (detailUrl.isEmpty() ? detailString : detailUrl.toString()) << x << y; - break; - case QQmlProfilerDefinitions::RangeEnd: break; - default: - Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message type."); - break; - } - messages << data; - data.clear(); - } - } -} - -QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine) : - QQmlAbstractProfilerAdapter(service) -{ - engine->enableProfiler(); - connect(this, SIGNAL(profilingEnabled(quint64)), engine->profiler, SLOT(startProfiling(quint64))); - connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)), - engine->profiler, SLOT(startProfiling(quint64)), Qt::DirectConnection); - connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling())); - connect(this, SIGNAL(profilingDisabledWhileWaiting()), - engine->profiler, SLOT(stopProfiling()), Qt::DirectConnection); - connect(this, SIGNAL(dataRequested()), engine->profiler, SLOT(reportData())); - connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)), - engine->profiler, SLOT(setTimer(QElapsedTimer))); - connect(engine->profiler, SIGNAL(dataReady(QList<QQmlProfilerData>)), - this, SLOT(receiveData(QList<QQmlProfilerData>))); -} - -qint64 QQmlProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages) -{ - while (!data.empty() && data.front().time <= until) { - data.front().toByteArrays(messages); - data.pop_front(); - } - return data.empty() ? -1 : data.front().time; -} - -void QQmlProfilerAdapter::receiveData(const QList<QQmlProfilerData> &new_data) -{ - data = new_data; - service->dataReady(this); -} - - QQmlProfiler::QQmlProfiler() : featuresEnabled(0) { - static int metatype = qRegisterMetaType<QList<QQmlProfilerData> >(); + static int metatype = qRegisterMetaType<QVector<QQmlProfilerData> >(); Q_UNUSED(metatype); m_timer.start(); } @@ -127,16 +52,12 @@ void QQmlProfiler::stopProfiling() { featuresEnabled = false; reportData(); - m_data.clear(); } void QQmlProfiler::reportData() { - QList<QQmlProfilerData> result; - result.reserve(m_data.size()); - for (int i = 0; i < m_data.size(); ++i) - result.append(m_data[i]); - emit dataReady(result); + emit dataReady(m_data); + m_data.clear(); } QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 5c994b112f..7e29c3ede6 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -68,8 +68,9 @@ QT_BEGIN_NAMESPACE // This struct is somewhat dangerous to use: // The messageType is a bit field. You can pack multiple messages into // one object, e.g. RangeStart and RangeLocation. Each one will be read -// independently by toByteArrays. Thus you can only pack messages if their data -// doesn't overlap. It's up to you to figure that out. +// independently when converting to QByteArrays. Thus you can only pack +// messages if their data doesn't overlap. It's up to you to figure that +// out. struct Q_AUTOTEST_EXPORT QQmlProfilerData { QQmlProfilerData() {} @@ -98,13 +99,12 @@ struct Q_AUTOTEST_EXPORT QQmlProfilerData int messageType; //bit field of QQmlProfilerService::Message int detailType; + // RangeData prefers detailString; RangeLocation prefers detailUrl. QString detailString; //used by RangeData and possibly by RangeLocation - QUrl detailUrl; //used by RangeLocation, overrides detailString + QUrl detailUrl; //used by RangeLocation and possibly by RangeData int x; //used by RangeLocation int y; //used by RangeLocation - - void toByteArrays(QList<QByteArray> &messages) const; }; Q_DECLARE_TYPEINFO(QQmlProfilerData, Q_MOVABLE_TYPE); @@ -121,11 +121,11 @@ public: // Have toByteArrays() construct another RangeData event from the same QString later. // This is somewhat pointless but important for backwards compatibility. - void startCompiling(const QString &name) + void startCompiling(const QUrl &url) { m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(), (1 << RangeStart | 1 << RangeLocation | 1 << RangeData), - 1 << Compiling, name, 1, 1)); + 1 << Compiling, url, 1, 1)); } void startHandlingSignal(const QQmlSourceLocation &location) @@ -171,24 +171,11 @@ public slots: void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: - void dataReady(const QList<QQmlProfilerData> &); + void dataReady(const QVector<QQmlProfilerData> &); protected: QElapsedTimer m_timer; - QVarLengthArray<QQmlProfilerData> m_data; -}; - -class QQmlProfilerAdapter : public QQmlAbstractProfilerAdapter { - Q_OBJECT -public: - QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine); - qint64 sendMessages(qint64 until, QList<QByteArray> &messages); - -public slots: - void receiveData(const QList<QQmlProfilerData> &new_data); - -private: - QList<QQmlProfilerData> data; + QVector<QQmlProfilerData> m_data; }; // @@ -231,10 +218,10 @@ struct QQmlHandlingSignalProfiler : public QQmlProfilerHelper { }; struct QQmlCompilingProfiler : public QQmlProfilerHelper { - QQmlCompilingProfiler(QQmlProfiler *profiler, const QString &name) : + QQmlCompilingProfiler(QQmlProfiler *profiler, const QUrl &url) : QQmlProfilerHelper(profiler) { - Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCompiling, profiler, startCompiling(name)); + Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCompiling, profiler, startCompiling(url)); } ~QQmlCompilingProfiler() @@ -332,6 +319,6 @@ private: }; QT_END_NAMESPACE -Q_DECLARE_METATYPE(QList<QQmlProfilerData>) +Q_DECLARE_METATYPE(QVector<QQmlProfilerData>) #endif // QQMLPROFILER_P_H diff --git a/src/qml/debugger/qqmlprofilerservice.cpp b/src/qml/debugger/qqmlprofilerservice.cpp deleted file mode 100644 index 85556836e6..0000000000 --- a/src/qml/debugger/qqmlprofilerservice.cpp +++ /dev/null @@ -1,379 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlprofilerservice_p.h" -#include "qqmldebugserver_p.h" -#include "qv4profileradapter_p.h" -#include "qqmlprofiler_p.h" -#include <private/qqmlengine_p.h> - -#include <QtCore/qdatastream.h> -#include <QtCore/qurl.h> -#include <QtCore/qtimer.h> -#include <QtCore/qthread.h> -#include <QtCore/qcoreapplication.h> - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(QQmlProfilerService, profilerInstance) - -QQmlProfilerService::QQmlProfilerService() - : QQmlConfigurableDebugService(QStringLiteral("CanvasFrameRate"), 1) -{ - m_timer.start(); - - QMutexLocker lock(configMutex()); - // If there is no debug server it doesn't matter as we'll never get enabled anyway. - if (QQmlDebugServer::instance() != 0) - moveToThread(QQmlDebugServer::instance()->thread()); -} - -QQmlProfilerService::~QQmlProfilerService() -{ - // No need to lock here. If any engine or global profiler is still trying to register at this - // point we have a nasty bug anyway. - qDeleteAll(m_engineProfilers.values()); - qDeleteAll(m_globalProfilers); -} - -void QQmlProfilerService::dataReady(QQmlAbstractProfilerAdapter *profiler) -{ - QMutexLocker lock(configMutex()); - bool dataComplete = true; - for (QMultiMap<qint64, QQmlAbstractProfilerAdapter *>::iterator i(m_startTimes.begin()); i != m_startTimes.end();) { - if (i.value() == profiler) { - m_startTimes.erase(i++); - } else { - if (i.key() == -1) - dataComplete = false; - ++i; - } - } - m_startTimes.insert(0, profiler); - if (dataComplete) { - QList<QQmlEngine *> enginesToRelease; - foreach (QQmlEngine *engine, m_stoppingEngines) { - foreach (QQmlAbstractProfilerAdapter *engineProfiler, m_engineProfilers.values(engine)) { - if (m_startTimes.values().contains(engineProfiler)) { - enginesToRelease.append(engine); - break; - } - } - } - sendMessages(); - foreach (QQmlEngine *engine, enginesToRelease) { - m_stoppingEngines.removeOne(engine); - emit detachedFromEngine(engine); - } - } -} - -QQmlProfilerService *QQmlProfilerService::instance() -{ - // just make sure that the service is properly registered - return profilerInstance(); -} - -void QQmlProfilerService::engineAboutToBeAdded(QQmlEngine *engine) -{ - Q_ASSERT_X(QThread::currentThread() != thread(), Q_FUNC_INFO, "QML profilers have to be added from the engine thread"); - - QMutexLocker lock(configMutex()); - QQmlProfilerAdapter *qmlAdapter = new QQmlProfilerAdapter(this, QQmlEnginePrivate::get(engine)); - QV4ProfilerAdapter *v4Adapter = new QV4ProfilerAdapter(this, QV8Engine::getV4(engine->handle())); - addEngineProfiler(qmlAdapter, engine); - addEngineProfiler(v4Adapter, engine); - QQmlConfigurableDebugService::engineAboutToBeAdded(engine); -} - -void QQmlProfilerService::engineAdded(QQmlEngine *engine) -{ - Q_ASSERT_X(QThread::currentThread() != thread(), Q_FUNC_INFO, "QML profilers have to be added from the engine thread"); - - QMutexLocker lock(configMutex()); - foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) - profiler->stopWaiting(); -} - -void QQmlProfilerService::engineAboutToBeRemoved(QQmlEngine *engine) -{ - Q_ASSERT_X(QThread::currentThread() != thread(), Q_FUNC_INFO, "QML profilers have to be removed from the engine thread"); - - QMutexLocker lock(configMutex()); - bool isRunning = false; - foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) { - if (profiler->isRunning()) - isRunning = true; - profiler->startWaiting(); - } - if (isRunning) { - m_stoppingEngines.append(engine); - stopProfiling(engine); - } else { - emit detachedFromEngine(engine); - } -} - -void QQmlProfilerService::engineRemoved(QQmlEngine *engine) -{ - Q_ASSERT_X(QThread::currentThread() != thread(), Q_FUNC_INFO, "QML profilers have to be removed from the engine thread"); - - QMutexLocker lock(configMutex()); - foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) { - removeProfilerFromStartTimes(profiler); - delete profiler; - } - m_engineProfilers.remove(engine); -} - -void QQmlProfilerService::addEngineProfiler(QQmlAbstractProfilerAdapter *profiler, QQmlEngine *engine) -{ - profiler->moveToThread(thread()); - profiler->synchronize(m_timer); - m_engineProfilers.insert(engine, profiler); -} - -void QQmlProfilerService::addGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) -{ - QMutexLocker lock(configMutex()); - profiler->synchronize(m_timer); - m_globalProfilers.append(profiler); - // Global profiler, not connected to a specific engine. - // Global profilers are started whenever any engine profiler is started and stopped when - // all engine profilers are stopped. - quint64 features = 0; - foreach (QQmlAbstractProfilerAdapter *engineProfiler, m_engineProfilers) - features |= engineProfiler->features(); - - if (features != 0) - profiler->startProfiling(features); -} - -void QQmlProfilerService::removeGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) -{ - QMutexLocker lock(configMutex()); - removeProfilerFromStartTimes(profiler); - m_globalProfilers.removeOne(profiler); - delete profiler; -} - -void QQmlProfilerService::removeProfilerFromStartTimes(const QQmlAbstractProfilerAdapter *profiler) -{ - for (QMultiMap<qint64, QQmlAbstractProfilerAdapter *>::iterator i(m_startTimes.begin()); - i != m_startTimes.end();) { - if (i.value() == profiler) { - m_startTimes.erase(i++); - break; - } else { - ++i; - } - } -} - -/*! - * Start profiling the given \a engine. If \a engine is 0, start all engine profilers that aren't - * currently running. - * - * If any engine profiler is started like that also start all global profilers. - */ -void QQmlProfilerService::startProfiling(QQmlEngine *engine, quint64 features) -{ - QMutexLocker lock(configMutex()); - - QByteArray message; - QQmlDebugStream d(&message, QIODevice::WriteOnly); - - d << m_timer.nsecsElapsed() << (int)Event << (int)StartTrace; - bool startedAny = false; - if (engine != 0) { - foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) { - if (!profiler->isRunning()) { - profiler->startProfiling(features); - startedAny = true; - } - } - if (startedAny) - d << idForObject(engine); - } else { - QSet<QQmlEngine *> engines; - for (QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin()); - i != m_engineProfilers.end(); ++i) { - if (!i.value()->isRunning()) { - engines << i.key(); - i.value()->startProfiling(features); - startedAny = true; - } - } - foreach (QQmlEngine *profiledEngine, engines) - d << idForObject(profiledEngine); - } - - if (startedAny) { - foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) { - if (!profiler->isRunning()) - profiler->startProfiling(features); - } - } - - QQmlDebugService::sendMessage(message); -} - -/*! - * Stop profiling the given \a engine. If \a engine is 0, stop all currently running engine - * profilers. - * - * If afterwards no more engine profilers are running, also stop all global profilers. Otherwise - * only make them report their data. - */ -void QQmlProfilerService::stopProfiling(QQmlEngine *engine) -{ - QMutexLocker lock(configMutex()); - QList<QQmlAbstractProfilerAdapter *> stopping; - QList<QQmlAbstractProfilerAdapter *> reporting; - - bool stillRunning = false; - for (QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin()); - i != m_engineProfilers.end(); ++i) { - if (i.value()->isRunning()) { - if (engine == 0 || i.key() == engine) { - m_startTimes.insert(-1, i.value()); - stopping << i.value(); - } else { - stillRunning = true; - } - } - } - - foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) { - if (!profiler->isRunning()) - continue; - m_startTimes.insert(-1, profiler); - if (stillRunning) { - reporting << profiler; - } else { - stopping << profiler; - } - } - - foreach (QQmlAbstractProfilerAdapter *profiler, reporting) - profiler->reportData(); - - foreach (QQmlAbstractProfilerAdapter *profiler, stopping) - profiler->stopProfiling(); -} - -/* - Send the queued up messages. -*/ -void QQmlProfilerService::sendMessages() -{ - QList<QByteArray> messages; - - QByteArray data; - QQmlDebugStream traceEnd(&data, QIODevice::WriteOnly); - traceEnd << m_timer.nsecsElapsed() << (int)Event << (int)EndTrace; - - QSet<QQmlEngine *> seen; - foreach (QQmlAbstractProfilerAdapter *profiler, m_startTimes) { - for (QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin()); - i != m_engineProfilers.end(); ++i) { - if (i.value() == profiler && !seen.contains(i.key())) { - seen << i.key(); - traceEnd << idForObject(i.key()); - } - } - } - - while (!m_startTimes.empty()) { - QQmlAbstractProfilerAdapter *first = m_startTimes.begin().value(); - m_startTimes.erase(m_startTimes.begin()); - if (!m_startTimes.empty()) { - qint64 next = first->sendMessages(m_startTimes.begin().key(), messages); - if (next != -1) - m_startTimes.insert(next, first); - } else { - first->sendMessages(std::numeric_limits<qint64>::max(), messages); - } - } - - //indicate completion - messages << data; - data.clear(); - - QQmlDebugStream ds(&data, QIODevice::WriteOnly); - ds << (qint64)-1 << (int)Complete; - messages << data; - - QQmlDebugService::sendMessages(messages); -} - -void QQmlProfilerService::stateAboutToBeChanged(QQmlDebugService::State newState) -{ - QMutexLocker lock(configMutex()); - - if (state() == newState) - return; - - // Stop all profiling and send the data before we get disabled. - if (newState != Enabled) { - foreach (QQmlEngine *engine, m_engineProfilers.keys()) - stopProfiling(engine); - } -} - -void QQmlProfilerService::messageReceived(const QByteArray &message) -{ - QMutexLocker lock(configMutex()); - - QByteArray rwData = message; - QQmlDebugStream stream(&rwData, QIODevice::ReadOnly); - - int engineId = -1; - quint64 features = std::numeric_limits<quint64>::max(); - bool enabled; - stream >> enabled; - if (!stream.atEnd()) - stream >> engineId; - if (!stream.atEnd()) - stream >> features; - - // If engineId == -1 objectForId() and then the cast will return 0. - if (enabled) - startProfiling(qobject_cast<QQmlEngine *>(objectForId(engineId)), features); - else - stopProfiling(qobject_cast<QQmlEngine *>(objectForId(engineId))); - - stopWaiting(); -} - -QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlprofilerservice_p.h b/src/qml/debugger/qqmlprofilerservice_p.h deleted file mode 100644 index 978d8413a7..0000000000 --- a/src/qml/debugger/qqmlprofilerservice_p.h +++ /dev/null @@ -1,112 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLPROFILERSERVICE_P_H -#define QQMLPROFILERSERVICE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqmlconfigurabledebugservice_p.h" -#include "qqmlprofilerdefinitions_p.h" -#include "qqmlabstractprofileradapter_p.h" - -#include <private/qqmlboundsignal_p.h> - -#include <QtCore/qelapsedtimer.h> -#include <QtCore/qmetaobject.h> -#include <QtCore/qmutex.h> -#include <QtCore/qvector.h> -#include <QtCore/qstringbuilder.h> -#include <QtCore/qwaitcondition.h> - -#include <limits> - -QT_BEGIN_NAMESPACE - -class QUrl; -class QQmlEngine; - - -class Q_QML_PRIVATE_EXPORT QQmlProfilerService : public QQmlConfigurableDebugService, public QQmlProfilerDefinitions -{ - Q_OBJECT -public: - - static QQmlProfilerService *instance(); - void engineAboutToBeAdded(QQmlEngine *engine); - void engineAboutToBeRemoved(QQmlEngine *engine); - void engineAdded(QQmlEngine *engine); - void engineRemoved(QQmlEngine *engine); - - void addGlobalProfiler(QQmlAbstractProfilerAdapter *profiler); - void removeGlobalProfiler(QQmlAbstractProfilerAdapter *profiler); - - void startProfiling(QQmlEngine *engine, quint64 features = std::numeric_limits<quint64>::max()); - void stopProfiling(QQmlEngine *engine); - - QQmlProfilerService(); - ~QQmlProfilerService(); - - void dataReady(QQmlAbstractProfilerAdapter *profiler); - -protected: - virtual void stateAboutToBeChanged(State state); - virtual void messageReceived(const QByteArray &); - -private: - - void sendMessages(); - void addEngineProfiler(QQmlAbstractProfilerAdapter *profiler, QQmlEngine *engine); - void removeProfilerFromStartTimes(const QQmlAbstractProfilerAdapter *profiler); - - QElapsedTimer m_timer; - - QList<QQmlAbstractProfilerAdapter *> m_globalProfilers; - QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *> m_engineProfilers; - QList<QQmlEngine *> m_stoppingEngines; - QMultiMap<qint64, QQmlAbstractProfilerAdapter *> m_startTimes; -}; - -QT_END_NAMESPACE - -#endif // QQMLPROFILERSERVICE_P_H - diff --git a/src/qml/debugger/qv4debugservice.cpp b/src/qml/debugger/qv4debugservice.cpp deleted file mode 100644 index cefb29e031..0000000000 --- a/src/qml/debugger/qv4debugservice.cpp +++ /dev/null @@ -1,1265 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4debugservice_p.h" -#include "qqmlconfigurabledebugservice_p_p.h" -#include "qqmlengine.h" -#include "qv4debugging_p.h" -#include "qv4engine_p.h" -#include "qv4function_p.h" -#include "qqmldebugserver_p.h" - -#include <private/qv8engine_p.h> - -#include <QtCore/QJsonArray> -#include <QtCore/QJsonDocument> -#include <QtCore/QJsonObject> -#include <QtCore/QJsonValue> - -const char *V4_CONNECT = "connect"; -const char *V4_DISCONNECT = "disconnect"; -const char *V4_BREAK_ON_SIGNAL = "breakonsignal"; -const char *V4_ADD_BREAKPOINT = "addBreakpoint"; -const char *V4_REMOVE_BREAKPOINT = "removeBreakpoint"; -const char *V4_PAUSE = "interrupt"; -const char *V4_ALL = "all"; -const char *V4_BREAK = "break"; - -const char *V4_FILENAME = "filename"; -const char *V4_LINENUMBER = "linenumber"; - -#define NO_PROTOCOL_TRACING -#ifdef NO_PROTOCOL_TRACING -# define TRACE_PROTOCOL(x) -#else -#include <QtCore/QDebug> -# define TRACE_PROTOCOL(x) x -#endif - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(QV4DebugService, v4ServiceInstance) - -class QV4DebugServicePrivate; - -class QV4DebuggerAgent : public QV4::Debugging::DebuggerAgent -{ -public: - QV4DebuggerAgent(QV4DebugServicePrivate *debugServicePrivate) - : debugServicePrivate(debugServicePrivate) - {} - - QV4::Debugging::Debugger *firstDebugger() const - { - // Currently only 1 single engine is supported, so: - if (m_debuggers.isEmpty()) - return 0; - else - return m_debuggers.first(); - } - - bool isRunning() const - { - // Currently only 1 single engine is supported, so: - if (QV4::Debugging::Debugger *debugger = firstDebugger()) - return debugger->state() == QV4::Debugging::Debugger::Running; - else - return false; - } - -public slots: - virtual void debuggerPaused(QV4::Debugging::Debugger *debugger, QV4::Debugging::PauseReason reason); - virtual void sourcesCollected(QV4::Debugging::Debugger *debugger, QStringList sources, int requestSequenceNr); - -private: - QV4DebugServicePrivate *debugServicePrivate; -}; - -class V8CommandHandler; -class UnknownV8CommandHandler; - -class VariableCollector: public QV4::Debugging::Debugger::Collector -{ -public: - VariableCollector(QV4::ExecutionEngine *engine) - : Collector(engine) - , destination(0) - {} - - virtual ~VariableCollector() {} - - void collectScope(QJsonArray *dest, QV4::Debugging::Debugger *debugger, int frameNr, int scopeNr) - { - qSwap(destination, dest); - bool oldIsProp = isProperty(); - setIsProperty(true); - debugger->collectArgumentsInContext(this, frameNr, scopeNr); - debugger->collectLocalsInContext(this, frameNr, scopeNr); - setIsProperty(oldIsProp); - qSwap(destination, dest); - } - - void setDestination(QJsonArray *dest) - { destination = dest; } - - QJsonArray retrieveRefsToInclude() - { - QJsonArray result; - qSwap(refsToInclude, result); - return result; - } - - QJsonValue lookup(int handle, bool addRefs = true) - { - if (handle < 0) - handle = -handle; - - if (addRefs) - foreach (int ref, refsByHandle[handle]) - refsToInclude.append(lookup(ref, false)); - return refs[handle]; - } - - QJsonObject makeRef(int refId) - { - QJsonObject ref; - ref[QLatin1String("ref")] = refId; - return ref; - } - - QJsonObject addFunctionRef(const QString &name) - { - const int refId = newRefId(); - - QJsonObject func; - func[QLatin1String("handle")] = refId; - func[QLatin1String("type")] = QStringLiteral("function"); - func[QLatin1String("className")] = QStringLiteral("Function"); - func[QLatin1String("name")] = name; - insertRef(func, refId); - - return makeRef(refId); - } - - QJsonObject addScriptRef(const QString &name) - { - const int refId = newRefId(); - - QJsonObject func; - func[QLatin1String("handle")] = refId; - func[QLatin1String("type")] = QStringLiteral("script"); - func[QLatin1String("name")] = name; - insertRef(func, refId); - - return makeRef(refId); - } - - QJsonObject addObjectRef(QJsonObject obj, bool anonymous) - { - int ref = newRefId(); - - if (anonymous) - ref = -ref; - obj[QLatin1String("handle")] = ref; - obj[QLatin1String("type")] = QStringLiteral("object"); - insertRef(obj, ref); - QSet<int> used; - qSwap(usedRefs, used); - refsByHandle.insert(ref, used); - - return makeRef(ref); - } - -protected: - virtual void addUndefined(const QString &name) - { - QJsonObject o; - addHandle(name, o, QStringLiteral("undefined")); - } - - virtual void addNull(const QString &name) - { - QJsonObject o; - addHandle(name, o, QStringLiteral("null")); - } - - virtual void addBoolean(const QString &name, bool value) - { - QJsonObject o; - o[QLatin1String("value")] = value; - addHandle(name, o, QStringLiteral("boolean")); - } - - virtual void addString(const QString &name, const QString &value) - { - QJsonObject o; - o[QLatin1String("value")] = value; - addHandle(name, o, QStringLiteral("string")); - } - - virtual void addObject(const QString &name, const QV4::Value &value) - { - QV4::Scope scope(engine()); - QV4::ScopedObject obj(scope, value.asObject()); - - int ref = cachedObjectRef(obj); - if (ref != -1) { - addNameRefPair(name, ref); - } else { - int ref = newRefId(); - cacheObjectRef(obj, ref); - - QJsonArray properties, *prev = &properties; - QSet<int> used; - qSwap(usedRefs, used); - qSwap(destination, prev); - collect(obj); - qSwap(destination, prev); - qSwap(usedRefs, used); - - QJsonObject o; - o[QLatin1String("properties")] = properties; - addHandle(name, o, QStringLiteral("object"), ref); - refsByHandle.insert(ref, used); - } - } - - virtual void addInteger(const QString &name, int value) - { - QJsonObject o; - o[QLatin1String("value")] = value; - addHandle(name, o, QStringLiteral("number")); - } - - virtual void addDouble(const QString &name, double value) - { - QJsonObject o; - o[QLatin1String("value")] = value; - addHandle(name, o, QStringLiteral("number")); - } - -private: - int addHandle(const QString &name, QJsonObject object, const QString &type, int suppliedRef = -1) - { - Q_ASSERT(destination); - - object[QLatin1String("type")] = type; - - QJsonDocument tmp; - tmp.setObject(object); - QByteArray key = tmp.toJson(QJsonDocument::Compact); - - int ref; - if (suppliedRef == -1) { - ref = refCache.value(key, -1); - if (ref == -1) { - ref = newRefId(); - object[QLatin1String("handle")] = ref; - insertRef(object, ref); - refCache.insert(key, ref); - } - } else { - ref = suppliedRef; - object[QLatin1String("handle")] = ref; - insertRef(object, ref); - refCache.insert(key, ref); - } - - addNameRefPair(name, ref); - return ref; - } - - void addNameRefPair(const QString &name, int ref) - { - QJsonObject nameValuePair; - nameValuePair[QLatin1String("name")] = name; - if (isProperty()) { - nameValuePair[QLatin1String("ref")] = ref; - } else { - QJsonObject refObj; - refObj[QLatin1String("ref")] = ref; - nameValuePair[QLatin1String("value")] = refObj; - } - destination->append(nameValuePair); - usedRefs.insert(ref); - } - - int newRefId() - { - int ref = refs.count(); - refs.insert(ref, QJsonValue()); - return ref; - } - - void insertRef(const QJsonValue &value, int refId) - { - if (refId < 0) - refId = -refId; - - refs.insert(refId, value); - refsToInclude.append(value); - } - - void cacheObjectRef(QV4::Value obj, int ref) - { - objectRefs.insert(obj.val, ref); - } - - int cachedObjectRef(QV4::Value obj) const - { - return objectRefs.value(obj.val, -1); - } - -private: - QJsonArray refsToInclude; - QHash<int, QJsonValue> refs; - QHash<QByteArray, int> refCache; - QJsonArray *destination; - QSet<int> usedRefs; - QHash<int, QSet<int> > refsByHandle; - QHash<quint64, int> objectRefs; -}; - -class QV4DebugServicePrivate : public QQmlConfigurableDebugServicePrivate -{ - Q_DECLARE_PUBLIC(QV4DebugService) - -public: - QV4DebugServicePrivate(); - ~QV4DebugServicePrivate() { qDeleteAll(handlers.values()); } - - static QByteArray packMessage(const QByteArray &command, const QByteArray &message = QByteArray()) - { - QByteArray reply; - QQmlDebugStream rs(&reply, QIODevice::WriteOnly); - static const QByteArray cmd("V8DEBUG"); - rs << cmd << command << message; - return reply; - } - - void send(QJsonObject v8Payload) - { - v8Payload[QLatin1String("seq")] = sequence++; - QJsonDocument doc; - doc.setObject(v8Payload); -#ifdef NO_PROTOCOL_TRACING - QByteArray responseData = doc.toJson(QJsonDocument::Compact); -#else - QByteArray responseData = doc.toJson(QJsonDocument::Indented); -#endif - - TRACE_PROTOCOL(qDebug() << "sending response for:" << responseData << endl); - - q_func()->sendMessage(packMessage("v8message", responseData)); - } - - void processCommand(const QByteArray &command, const QByteArray &data); - - QV4DebuggerAgent debuggerAgent; - - QStringList breakOnSignals; - QMap<int, QV4::Debugging::Debugger *> debuggerMap; - static int debuggerIndex; - static int sequence; - const int version; - - V8CommandHandler *v8CommandHandler(const QString &command) const; - - void clearHandles(QV4::ExecutionEngine *engine) - { - theCollector.reset(new VariableCollector(engine)); - } - - QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr, - QV4::Debugging::Debugger *debugger) - { - QJsonObject frame; - frame[QLatin1String("index")] = frameNr; - frame[QLatin1String("debuggerFrame")] = false; - frame[QLatin1String("func")] = theCollector->addFunctionRef(stackFrame.function); - frame[QLatin1String("script")] = theCollector->addScriptRef(stackFrame.source); - frame[QLatin1String("line")] = stackFrame.line - 1; - if (stackFrame.column >= 0) - frame[QLatin1String("column")] = stackFrame.column; - - QJsonArray properties; - theCollector->setDestination(&properties); - if (debugger->collectThisInContext(theCollector.data(), frameNr)) { - QJsonObject obj; - obj[QLatin1String("properties")] = properties; - frame[QLatin1String("receiver")] = theCollector->addObjectRef(obj, false); - } - - QJsonArray scopes; - // Only type and index are used by Qt Creator, so we keep it easy: - QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes = debugger->getScopeTypes(frameNr); - for (int i = 0, ei = scopeTypes.count(); i != ei; ++i) { - int type = encodeScopeType(scopeTypes[i]); - if (type == -1) - continue; - - QJsonObject scope; - scope[QLatin1String("index")] = i; - scope[QLatin1String("type")] = type; - scopes.push_back(scope); - } - frame[QLatin1String("scopes")] = scopes; - - return frame; - } - - int encodeScopeType(QV4::Heap::ExecutionContext::ContextType scopeType) - { - switch (scopeType) { - case QV4::Heap::ExecutionContext::Type_GlobalContext: - return 0; - break; - case QV4::Heap::ExecutionContext::Type_CatchContext: - return 4; - break; - case QV4::Heap::ExecutionContext::Type_WithContext: - return 2; - break; - case QV4::Heap::ExecutionContext::Type_SimpleCallContext: - case QV4::Heap::ExecutionContext::Type_CallContext: - return 1; - break; - case QV4::Heap::ExecutionContext::Type_QmlContext: - default: - return -1; - } - } - - QJsonObject buildScope(int frameNr, int scopeNr, QV4::Debugging::Debugger *debugger) - { - QJsonObject scope; - - QJsonArray properties; - theCollector->collectScope(&properties, debugger, frameNr, scopeNr); - - QJsonObject anonymous; - anonymous[QLatin1String("properties")] = properties; - - QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes = debugger->getScopeTypes(frameNr); - scope[QLatin1String("type")] = encodeScopeType(scopeTypes[scopeNr]); - scope[QLatin1String("index")] = scopeNr; - scope[QLatin1String("frameIndex")] = frameNr; - scope[QLatin1String("object")] = theCollector->addObjectRef(anonymous, true); - - return scope; - } - - QJsonValue lookup(int refId) const { return theCollector->lookup(refId); } - - QJsonArray buildRefs() - { - return theCollector->retrieveRefsToInclude(); - } - - VariableCollector *collector() const - { - return theCollector.data(); - } - - void selectFrame(int frameNr) - { theSelectedFrame = frameNr; } - - int selectedFrame() const - { return theSelectedFrame; } - -private: - QScopedPointer<VariableCollector> theCollector; - int theSelectedFrame; - - void addHandler(V8CommandHandler* handler); - QHash<QString, V8CommandHandler*> handlers; - QScopedPointer<UnknownV8CommandHandler> unknownV8CommandHandler; -}; - -int QV4DebugServicePrivate::debuggerIndex = 0; -int QV4DebugServicePrivate::sequence = 0; - -class V8CommandHandler -{ -public: - V8CommandHandler(const QString &command) - : cmd(command) - {} - - virtual ~V8CommandHandler() - {} - - QString command() const { return cmd; } - - void handle(const QJsonObject &request, QQmlDebugService *s, QV4DebugServicePrivate *p) - { - TRACE_PROTOCOL(qDebug() << "handling command" << command() << "..."); - - req = request; - seq = req.value(QStringLiteral("seq")); - debugService = s; - debugServicePrivate = p; - - handleRequest(); - if (!response.isEmpty()) { - response[QLatin1String("type")] = QStringLiteral("response"); - debugServicePrivate->send(response); - } - - debugServicePrivate = 0; - debugService = 0; - seq = QJsonValue(); - req = QJsonObject(); - response = QJsonObject(); - } - - virtual void handleRequest() = 0; - -protected: - void addCommand() { response.insert(QStringLiteral("command"), cmd); } - void addRequestSequence() { response.insert(QStringLiteral("request_seq"), seq); } - void addSuccess(bool success) { response.insert(QStringLiteral("success"), success); } - void addBody(const QJsonObject &body) - { - response.insert(QStringLiteral("body"), body); - } - - void addRunning() - { - response.insert(QStringLiteral("running"), debugServicePrivate->debuggerAgent.isRunning()); - } - - void addRefs() - { - response.insert(QStringLiteral("refs"), debugServicePrivate->buildRefs()); - } - - void createErrorResponse(const QString &msg) - { - QJsonValue command = req.value(QStringLiteral("command")); - response.insert(QStringLiteral("command"), command); - addRequestSequence(); - addSuccess(false); - addRunning(); - response.insert(QStringLiteral("message"), msg); - } - - int requestSequenceNr() const - { return seq.toInt(-1); } - -protected: - QString cmd; - QJsonObject req; - QJsonValue seq; - QQmlDebugService *debugService; - QV4DebugServicePrivate *debugServicePrivate; - QJsonObject response; -}; - -class UnknownV8CommandHandler: public V8CommandHandler -{ -public: - UnknownV8CommandHandler(): V8CommandHandler(QString()) {} - - virtual void handleRequest() - { - QString msg = QStringLiteral("unimplemented command \""); - msg += req.value(QStringLiteral("command")).toString(); - msg += QStringLiteral("\""); - createErrorResponse(msg); - } -}; - -namespace { -class V8VersionRequest: public V8CommandHandler -{ -public: - V8VersionRequest(): V8CommandHandler(QStringLiteral("version")) {} - - virtual void handleRequest() - { - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - QJsonObject body; - body.insert(QStringLiteral("V8Version"), - QLatin1String("this is not V8, this is V4 in Qt " QT_VERSION_STR)); - addBody(body); - } -}; - -class V8SetBreakPointRequest: public V8CommandHandler -{ -public: - V8SetBreakPointRequest(): V8CommandHandler(QStringLiteral("setbreakpoint")) {} - - virtual void handleRequest() - { - // decypher the payload: - QJsonObject args = req.value(QStringLiteral("arguments")).toObject(); - if (args.isEmpty()) - return; - - QString type = args.value(QStringLiteral("type")).toString(); - if (type != QStringLiteral("scriptRegExp")) { - createErrorResponse(QStringLiteral("breakpoint type \"%1\" is not implemented").arg(type)); - return; - } - - QString fileName = args.value(QStringLiteral("target")).toString(); - if (fileName.isEmpty()) { - createErrorResponse(QStringLiteral("breakpoint has no file name")); - return; - } - - int line = args.value(QStringLiteral("line")).toInt(-1); - if (line < 0) { - createErrorResponse(QStringLiteral("breakpoint has an invalid line number")); - return; - } - - bool enabled = args.value(QStringLiteral("enabled")).toBool(true); - QString condition = args.value(QStringLiteral("condition")).toString(); - - // set the break point: - int id = debugServicePrivate->debuggerAgent.addBreakPoint(fileName, line + 1, enabled, condition); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - QJsonObject body; - body.insert(QStringLiteral("type"), type); - body.insert(QStringLiteral("breakpoint"), id); - // It's undocumented, but V8 sends back an actual_locations array too. However, our - // Debugger currently doesn't tell us when it resolved a breakpoint, so we'll leave them - // pending until the breakpoint is hit for the first time. - addBody(body); - } -}; - -class V8ClearBreakPointRequest: public V8CommandHandler -{ -public: - V8ClearBreakPointRequest(): V8CommandHandler(QStringLiteral("clearbreakpoint")) {} - - virtual void handleRequest() - { - // decypher the payload: - QJsonObject args = req.value(QStringLiteral("arguments")).toObject(); - if (args.isEmpty()) - return; - - int id = args.value(QStringLiteral("breakpoint")).toInt(-1); - if (id < 0) { - createErrorResponse(QStringLiteral("breakpoint has an invalid number")); - return; - } - - // remove the break point: - debugServicePrivate->debuggerAgent.removeBreakPoint(id); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - QJsonObject body; - body.insert(QStringLiteral("type"), QStringLiteral("scriptRegExp")); - body.insert(QStringLiteral("breakpoint"), id); - addBody(body); - } -}; - -class V8BacktraceRequest: public V8CommandHandler -{ -public: - V8BacktraceRequest(): V8CommandHandler(QStringLiteral("backtrace")) {} - - virtual void handleRequest() - { - // decypher the payload: - - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - int fromFrame = arguments.value(QStringLiteral("fromFrame")).toInt(0); - int toFrame = arguments.value(QStringLiteral("toFrame")).toInt(fromFrame + 10); - // no idea what the bottom property is for, so we'll ignore it. - - QV4::Debugging::Debugger *debugger = debugServicePrivate->debuggerAgent.firstDebugger(); - - QJsonArray frameArray; - QVector<QV4::StackFrame> frames = debugger->stackTrace(toFrame); - for (int i = fromFrame; i < toFrame && i < frames.size(); ++i) - frameArray.push_back(debugServicePrivate->buildFrame(frames[i], i, debugger)); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - QJsonObject body; - if (frameArray.isEmpty()) { - body.insert(QStringLiteral("totalFrames"), 0); - } else { - body.insert(QStringLiteral("fromFrame"), fromFrame); - body.insert(QStringLiteral("toFrame"), fromFrame + frameArray.size()); - body.insert(QStringLiteral("frames"), frameArray); - } - addBody(body); - addRefs(); - } -}; - -class V8FrameRequest: public V8CommandHandler -{ -public: - V8FrameRequest(): V8CommandHandler(QStringLiteral("frame")) {} - - virtual void handleRequest() - { - // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - const int frameNr = arguments.value(QStringLiteral("number")).toInt(debugServicePrivate->selectedFrame()); - - QV4::Debugging::Debugger *debugger = debugServicePrivate->debuggerAgent.firstDebugger(); - QVector<QV4::StackFrame> frames = debugger->stackTrace(frameNr + 1); - if (frameNr < 0 || frameNr >= frames.size()) { - createErrorResponse(QStringLiteral("frame command has invalid frame number")); - return; - } - - debugServicePrivate->selectFrame(frameNr); - QJsonObject frame = debugServicePrivate->buildFrame(frames[frameNr], frameNr, debugger); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - addBody(frame); - addRefs(); - } -}; - -class V8ScopeRequest: public V8CommandHandler -{ -public: - V8ScopeRequest(): V8CommandHandler(QStringLiteral("scope")) {} - - virtual void handleRequest() - { - // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - const int frameNr = arguments.value(QStringLiteral("frameNumber")).toInt(debugServicePrivate->selectedFrame()); - const int scopeNr = arguments.value(QStringLiteral("number")).toInt(0); - - QV4::Debugging::Debugger *debugger = debugServicePrivate->debuggerAgent.firstDebugger(); - QVector<QV4::StackFrame> frames = debugger->stackTrace(frameNr + 1); - if (frameNr < 0 || frameNr >= frames.size()) { - createErrorResponse(QStringLiteral("scope command has invalid frame number")); - return; - } - if (scopeNr < 0) { - createErrorResponse(QStringLiteral("scope command has invalid scope number")); - return; - } - - QJsonObject scope = debugServicePrivate->buildScope(frameNr, scopeNr, debugger); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - addBody(scope); - addRefs(); - } -}; - -class V8LookupRequest: public V8CommandHandler -{ -public: - V8LookupRequest(): V8CommandHandler(QStringLiteral("lookup")) {} - - virtual void handleRequest() - { - // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QJsonArray handles = arguments.value(QStringLiteral("handles")).toArray(); - - QJsonObject body; - foreach (QJsonValue handle, handles) - body[QString::number(handle.toInt())] = debugServicePrivate->lookup(handle.toInt()); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - addBody(body); - addRefs(); - } -}; - -class V8ContinueRequest: public V8CommandHandler -{ -public: - V8ContinueRequest(): V8CommandHandler(QStringLiteral("continue")) {} - - virtual void handleRequest() - { - // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - - QV4::Debugging::Debugger *debugger = debugServicePrivate->debuggerAgent.firstDebugger(); - - if (arguments.empty()) { - debugger->resume(QV4::Debugging::Debugger::FullThrottle); - } else { - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QString stepAction = arguments.value(QStringLiteral("stepaction")).toString(); - const int stepcount = arguments.value(QStringLiteral("stepcount")).toInt(1); - if (stepcount != 1) - qWarning() << "Step count other than 1 is not supported."; - - if (stepAction == QStringLiteral("in")) { - debugger->resume(QV4::Debugging::Debugger::StepIn); - } else if (stepAction == QStringLiteral("out")) { - debugger->resume(QV4::Debugging::Debugger::StepOut); - } else if (stepAction == QStringLiteral("next")) { - debugger->resume(QV4::Debugging::Debugger::StepOver); - } else { - createErrorResponse(QStringLiteral("continue command has invalid stepaction")); - return; - } - } - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - } -}; - -class V8DisconnectRequest: public V8CommandHandler -{ -public: - V8DisconnectRequest(): V8CommandHandler(QStringLiteral("disconnect")) {} - - virtual void handleRequest() - { - debugServicePrivate->debuggerAgent.removeAllBreakPoints(); - debugServicePrivate->debuggerAgent.resumeAll(); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - } -}; - -class V8SetExceptionBreakRequest: public V8CommandHandler -{ -public: - V8SetExceptionBreakRequest(): V8CommandHandler(QStringLiteral("setexceptionbreak")) {} - - virtual void handleRequest() - { - bool wasEnabled = debugServicePrivate->debuggerAgent.breakOnThrow(); - - //decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QString type = arguments.value(QStringLiteral("type")).toString(); - bool enabled = arguments.value(QStringLiteral("number")).toBool(!wasEnabled); - - if (type == QStringLiteral("all")) { - // that's fine - } else if (type == QStringLiteral("uncaught")) { - createErrorResponse(QStringLiteral("breaking only on uncaught exceptions is not supported yet")); - return; - } else { - createErrorResponse(QStringLiteral("invalid type for break on exception")); - return; - } - - // do it: - debugServicePrivate->debuggerAgent.setBreakOnThrow(enabled); - - QJsonObject body; - body[QLatin1String("type")] = type; - body[QLatin1String("enabled")] = debugServicePrivate->debuggerAgent.breakOnThrow(); - - // response: - addBody(body); - addRunning(); - addSuccess(true); - addRequestSequence(); - addCommand(); - } -}; - -class V8ScriptsRequest: public V8CommandHandler -{ -public: - V8ScriptsRequest(): V8CommandHandler(QStringLiteral("scripts")) {} - - virtual void handleRequest() - { - //decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - int types = arguments.value(QStringLiteral("types")).toInt(-1); - if (types < 0 || types > 7) { - createErrorResponse(QStringLiteral("invalid types value in scripts command")); - return; - } else if (types != 4) { - createErrorResponse(QStringLiteral("unsupported types value in scripts command")); - return; - } - - // do it: - debugServicePrivate->debuggerAgent.firstDebugger()->gatherSources(requestSequenceNr()); - - // response will be send by - } -}; - -// Request: -// { -// "seq": 4, -// "type": "request", -// "command": "evaluate", -// "arguments": { -// "expression": "a", -// "frame": 0 -// } -// } -// -// Response: -// { -// "body": { -// "handle": 3, -// "type": "number", -// "value": 1 -// }, -// "command": "evaluate", -// "refs": [], -// "request_seq": 4, -// "running": false, -// "seq": 5, -// "success": true, -// "type": "response" -// } -// -// The "value" key in "body" is the result of evaluating the expression in the request. -class V8EvaluateRequest: public V8CommandHandler -{ -public: - V8EvaluateRequest(): V8CommandHandler(QStringLiteral("evaluate")) {} - - virtual void handleRequest() - { - //decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QString expression = arguments.value(QStringLiteral("expression")).toString(); - const int frame = arguments.value(QStringLiteral("frame")).toInt(0); - - QV4::Debugging::Debugger *debugger = debugServicePrivate->debuggerAgent.firstDebugger(); - Q_ASSERT(debugger->state() == QV4::Debugging::Debugger::Paused); - - VariableCollector *collector = debugServicePrivate->collector(); - QJsonArray dest; - collector->setDestination(&dest); - debugger->evaluateExpression(frame, expression, collector); - - const int ref = dest.at(0).toObject().value(QStringLiteral("value")).toObject() - .value(QStringLiteral("ref")).toInt(); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - addBody(collector->lookup(ref).toObject()); - addRefs(); - } -}; -} // anonymous namespace - -QV4DebugServicePrivate::QV4DebugServicePrivate() - : debuggerAgent(this) - , version(1) - , theSelectedFrame(0) - , unknownV8CommandHandler(new UnknownV8CommandHandler) -{ - addHandler(new V8VersionRequest); - addHandler(new V8SetBreakPointRequest); - addHandler(new V8ClearBreakPointRequest); - addHandler(new V8BacktraceRequest); - addHandler(new V8FrameRequest); - addHandler(new V8ScopeRequest); - addHandler(new V8LookupRequest); - addHandler(new V8ContinueRequest); - addHandler(new V8DisconnectRequest); - addHandler(new V8SetExceptionBreakRequest); - addHandler(new V8ScriptsRequest); - addHandler(new V8EvaluateRequest); -} - -void QV4DebugServicePrivate::addHandler(V8CommandHandler* handler) -{ - handlers[handler->command()] = handler; -} - -V8CommandHandler *QV4DebugServicePrivate::v8CommandHandler(const QString &command) const -{ - V8CommandHandler *handler = handlers.value(command, 0); - if (handler) - return handler; - else - return unknownV8CommandHandler.data(); -} - -QV4DebugService::QV4DebugService(QObject *parent) - : QQmlConfigurableDebugService(*(new QV4DebugServicePrivate()), - QStringLiteral("V8Debugger"), 1, parent) -{} - -QV4DebugService::~QV4DebugService() -{ -} - -QV4DebugService *QV4DebugService::instance() -{ - return v4ServiceInstance(); -} - -void QV4DebugService::engineAboutToBeAdded(QQmlEngine *engine) -{ - Q_D(QV4DebugService); - QMutexLocker lock(configMutex()); - if (engine) { - QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); - if (QQmlDebugServer *server = QQmlDebugServer::instance()) { - if (ee) { - ee->enableDebugger(); - QV4::Debugging::Debugger *debugger = ee->debugger; - d->debuggerMap.insert(d->debuggerIndex++, debugger); - d->debuggerAgent.addDebugger(debugger); - d->debuggerAgent.moveToThread(server->thread()); - moveToThread(server->thread()); - } - } - } - QQmlConfigurableDebugService::engineAboutToBeAdded(engine); -} - -void QV4DebugService::engineAboutToBeRemoved(QQmlEngine *engine) -{ - Q_D(QV4DebugService); - QMutexLocker lock(configMutex()); - if (engine){ - const QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); - if (ee) { - QV4::Debugging::Debugger *debugger = ee->debugger; - typedef QMap<int, QV4::Debugging::Debugger *>::const_iterator DebuggerMapIterator; - const DebuggerMapIterator end = d->debuggerMap.constEnd(); - for (DebuggerMapIterator i = d->debuggerMap.constBegin(); i != end; ++i) { - if (i.value() == debugger) { - d->debuggerMap.remove(i.key()); - break; - } - } - d->debuggerAgent.removeDebugger(debugger); - } - } - QQmlConfigurableDebugService::engineAboutToBeRemoved(engine); -} - -void QV4DebugService::signalEmitted(const QString &signal) -{ - //This function is only called by QQmlBoundSignal - //only if there is a slot connected to the signal. Hence, there - //is no need for additional check. - Q_D(QV4DebugService); - - //Parse just the name and remove the class info - //Normalize to Lower case. - QString signalName = signal.left(signal.indexOf(QLatin1Char('('))).toLower(); - - foreach (const QString &signal, d->breakOnSignals) { - if (signal == signalName) { - // TODO: pause debugger - break; - } - } -} - -void QV4DebugService::messageReceived(const QByteArray &message) -{ - Q_D(QV4DebugService); - QMutexLocker lock(configMutex()); - - QQmlDebugStream ms(message); - QByteArray header; - ms >> header; - - TRACE_PROTOCOL(qDebug() << "received message with header" << header); - - if (header == "V8DEBUG") { - QByteArray type; - QByteArray payload; - ms >> type >> payload; - TRACE_PROTOCOL(qDebug() << "... type:" << type); - - if (type == V4_CONNECT) { - sendMessage(d->packMessage(type)); - stopWaiting(); - } else if (type == V4_PAUSE) { - d->debuggerAgent.pauseAll(); - sendSomethingToSomebody(type); - } else if (type == V4_BREAK_ON_SIGNAL) { - QByteArray signal; - bool enabled; - ms >> signal >> enabled; - //Normalize to lower case. - QString signalName(QString::fromUtf8(signal).toLower()); - if (enabled) - d->breakOnSignals.append(signalName); - else - d->breakOnSignals.removeOne(signalName); - } else if (type == "v8request") { - handleV8Request(payload); - } else if (type == V4_DISCONNECT) { - TRACE_PROTOCOL(qDebug() << "... payload:" << payload); - handleV8Request(payload); - } else { - sendSomethingToSomebody(type, 0); - } - } -} - -void QV4DebugService::sendSomethingToSomebody(const char *type, int magicNumber) -{ - Q_D(QV4DebugService); - - QByteArray response; - QQmlDebugStream rs(&response, QIODevice::WriteOnly); - rs << QByteArray(type) - << QByteArray::number(d->version) << QByteArray::number(magicNumber); - sendMessage(d->packMessage(type, response)); -} - -void QV4DebuggerAgent::debuggerPaused(QV4::Debugging::Debugger *debugger, QV4::Debugging::PauseReason reason) -{ - Q_UNUSED(reason); - - debugServicePrivate->clearHandles(debugger->engine()); - - QJsonObject event, body, script; - event.insert(QStringLiteral("type"), QStringLiteral("event")); - - switch (reason) { - case QV4::Debugging::Step: - case QV4::Debugging::PauseRequest: - case QV4::Debugging::BreakPoint: { - event.insert(QStringLiteral("event"), QStringLiteral("break")); - QVector<QV4::StackFrame> frames = debugger->stackTrace(1); - if (frames.isEmpty()) - break; - - const QV4::StackFrame &topFrame = frames.first(); - body.insert(QStringLiteral("invocationText"), topFrame.function); - body.insert(QStringLiteral("sourceLine"), topFrame.line - 1); - if (topFrame.column > 0) - body.insert(QStringLiteral("sourceColumn"), topFrame.column); - QJsonArray breakPoints; - foreach (int breakPointId, breakPointIds(topFrame.source, topFrame.line)) - breakPoints.push_back(breakPointId); - body.insert(QStringLiteral("breakpoints"), breakPoints); - script.insert(QStringLiteral("name"), topFrame.source); - } break; - case QV4::Debugging::Throwing: - // TODO: complete this! - event.insert(QStringLiteral("event"), QStringLiteral("exception")); - break; - } - - if (!script.isEmpty()) - body.insert(QStringLiteral("script"), script); - if (!body.isEmpty()) - event.insert(QStringLiteral("body"), body); - debugServicePrivate->send(event); -} - -void QV4DebuggerAgent::sourcesCollected(QV4::Debugging::Debugger *debugger, QStringList sources, int requestSequenceNr) -{ - QJsonArray body; - foreach (const QString source, sources) { - QJsonObject src; - src[QLatin1String("name")] = source; - src[QLatin1String("scriptType")] = 4; - body.append(src); - } - - QJsonObject response; - response[QLatin1String("success")] = true; - response[QLatin1String("running")] = debugger->state() == QV4::Debugging::Debugger::Running; - response[QLatin1String("body")] = body; - response[QLatin1String("command")] = QStringLiteral("scripts"); - response[QLatin1String("request_seq")] = requestSequenceNr; - response[QLatin1String("type")] = QStringLiteral("response"); - debugServicePrivate->send(response); -} - -void QV4DebugService::handleV8Request(const QByteArray &payload) -{ - Q_D(QV4DebugService); - - TRACE_PROTOCOL(qDebug() << "v8request, payload:" << payload); - - QJsonDocument request = QJsonDocument::fromJson(payload); - QJsonObject o = request.object(); - QJsonValue type = o.value(QStringLiteral("type")); - if (type.toString() == QStringLiteral("request")) { - QJsonValue command = o.value(QStringLiteral("command")); - V8CommandHandler *h = d->v8CommandHandler(command.toString()); - if (h) - h->handle(o, this, d); - } -} - -QT_END_NAMESPACE diff --git a/src/qml/debugger/qv4debugservice_p.h b/src/qml/debugger/qv4debugservice_p.h deleted file mode 100644 index f7b38b11b6..0000000000 --- a/src/qml/debugger/qv4debugservice_p.h +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4DEBUGSERVICE_P_H -#define QV4DEBUGSERVICE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqmlconfigurabledebugservice_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { struct ExecutionEngine; } -class QQmlEngine; -class QV4DebugServicePrivate; - -class QV4DebugService : public QQmlConfigurableDebugService -{ - Q_OBJECT -public: - explicit QV4DebugService(QObject *parent = 0); - ~QV4DebugService(); - - static QV4DebugService *instance(); - void engineAboutToBeAdded(QQmlEngine *engine); - void engineAboutToBeRemoved(QQmlEngine *engine); - - void signalEmitted(const QString &signal); - -protected: - void messageReceived(const QByteArray &); - void sendSomethingToSomebody(const char *type, int magicNumber = 1); - -private: - void handleV8Request(const QByteArray &payload); - -private: - Q_DISABLE_COPY(QV4DebugService) - Q_DECLARE_PRIVATE(QV4DebugService) -}; - -QT_END_NAMESPACE - -#endif // QV4DEBUGSERVICE_P_H diff --git a/src/qml/debugger/qv4profileradapter.cpp b/src/qml/debugger/qv4profileradapter.cpp deleted file mode 100644 index 2b8183dc69..0000000000 --- a/src/qml/debugger/qv4profileradapter.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4profileradapter_p.h" -#include "qqmlprofilerservice_p.h" -#include "qqmldebugservice_p.h" - -QT_BEGIN_NAMESPACE - -QV4ProfilerAdapter::QV4ProfilerAdapter(QQmlProfilerService *service, QV4::ExecutionEngine *engine) : - QQmlAbstractProfilerAdapter(service) -{ - engine->enableProfiler(); - connect(this, SIGNAL(profilingEnabled(quint64)), - engine->profiler, SLOT(startProfiling(quint64))); - connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)), - engine->profiler, SLOT(startProfiling(quint64)), Qt::DirectConnection); - connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling())); - connect(this, SIGNAL(profilingDisabledWhileWaiting()), engine->profiler, SLOT(stopProfiling()), - Qt::DirectConnection); - connect(this, SIGNAL(dataRequested()), engine->profiler, SLOT(reportData())); - connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)), - engine->profiler, SLOT(setTimer(QElapsedTimer))); - connect(engine->profiler, SIGNAL(dataReady(QList<QV4::Profiling::FunctionCallProperties>, - QList<QV4::Profiling::MemoryAllocationProperties>)), - this, SLOT(receiveData(QList<QV4::Profiling::FunctionCallProperties>, - QList<QV4::Profiling::MemoryAllocationProperties>))); -} - -qint64 QV4ProfilerAdapter::appendMemoryEvents(qint64 until, QList<QByteArray> &messages) -{ - QByteArray message; - while (!memory_data.empty() && memory_data.front().timestamp <= until) { - QQmlDebugStream d(&message, QIODevice::WriteOnly); - QV4::Profiling::MemoryAllocationProperties &props = memory_data.front(); - d << props.timestamp << MemoryAllocation << props.type << props.size; - memory_data.pop_front(); - messages.append(message); - } - return memory_data.empty() ? -1 : memory_data.front().timestamp; -} - -qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages) -{ - QByteArray message; - while (true) { - while (!stack.empty() && (data.empty() || stack.top() <= data.front().start)) { - if (stack.top() > until) { - qint64 memory_next = appendMemoryEvents(until, messages); - return memory_next == -1 ? stack.top() : qMin(stack.top(), memory_next); - } - appendMemoryEvents(stack.top(), messages); - QQmlDebugStream d(&message, QIODevice::WriteOnly); - d << stack.pop() << RangeEnd << Javascript; - messages.append(message); - } - while (!data.empty() && (stack.empty() || data.front().start < stack.top())) { - const QV4::Profiling::FunctionCallProperties &props = data.front(); - if (props.start > until) { - qint64 memory_next = appendMemoryEvents(until, messages); - return memory_next == -1 ? props.start : qMin(props.start, memory_next); - } - appendMemoryEvents(props.start, messages); - - QQmlDebugStream d_start(&message, QIODevice::WriteOnly); - d_start << props.start << RangeStart << Javascript; - messages.push_back(message); - message.clear(); - QQmlDebugStream d_location(&message, QIODevice::WriteOnly); - d_location << props.start << RangeLocation << Javascript << props.file << props.line - << props.column; - messages.push_back(message); - message.clear(); - QQmlDebugStream d_data(&message, QIODevice::WriteOnly); - d_data << props.start << RangeData << Javascript << props.name; - messages.push_back(message); - message.clear(); - stack.push(props.end); - data.pop_front(); - } - if (stack.empty() && data.empty()) - return appendMemoryEvents(until, messages); - } -} - -void QV4ProfilerAdapter::receiveData(const QList<QV4::Profiling::FunctionCallProperties> &new_data, - const QList<QV4::Profiling::MemoryAllocationProperties> &new_memory_data) -{ - data = new_data; - memory_data = new_memory_data; - stack.clear(); - service->dataReady(this); -} - -QT_END_NAMESPACE diff --git a/src/qml/debugger/qv4profileradapter_p.h b/src/qml/debugger/qv4profileradapter_p.h deleted file mode 100644 index 2f467f4beb..0000000000 --- a/src/qml/debugger/qv4profileradapter_p.h +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4PROFILERADAPTER_P_H -#define QV4PROFILERADAPTER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qv4profiling_p.h" -#include "qqmlabstractprofileradapter_p.h" - -#include <QStack> -#include <QList> - -QT_BEGIN_NAMESPACE - -class QQmlProfilerService; -class QV4ProfilerAdapter : public QQmlAbstractProfilerAdapter { - Q_OBJECT - -public: - QV4ProfilerAdapter(QQmlProfilerService *service, QV4::ExecutionEngine *engine); - - virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages); - -public slots: - void receiveData(const QList<QV4::Profiling::FunctionCallProperties> &, - const QList<QV4::Profiling::MemoryAllocationProperties> &); - -private: - QList<QV4::Profiling::FunctionCallProperties> data; - QList<QV4::Profiling::MemoryAllocationProperties> memory_data; - QStack<qint64> stack; - qint64 appendMemoryEvents(qint64 until, QList<QByteArray> &messages); -}; - -QT_END_NAMESPACE - -#endif // QV4PROFILERADAPTER_P_H diff --git a/src/qml/doc/qtqml.qdocconf b/src/qml/doc/qtqml.qdocconf index 74b61fd6e1..500754ead4 100644 --- a/src/qml/doc/qtqml.qdocconf +++ b/src/qml/doc/qtqml.qdocconf @@ -4,7 +4,7 @@ project = QtQml description = Qt QML Reference Documentation version = $QT_VERSION -examplesinstallpath = qml +examplesinstallpath = qtdeclarative/qml qhp.projects = QtQml diff --git a/src/qml/doc/snippets/qml/qtBinding.2.qml b/src/qml/doc/snippets/qml/qtBinding.2.qml index 6a9a2de750..6159b31748 100644 --- a/src/qml/doc/snippets/qml/qtBinding.2.qml +++ b/src/qml/doc/snippets/qml/qtBinding.2.qml @@ -52,7 +52,7 @@ Item { root.dynamicText = "Modified root text" var obj2 = c.createObject(root, { 'text': Qt.binding(function() { return this.dynamicText + ' extra text' }) }) - obj2.dynamicText = "Modified text element text" + obj2.dynamicText = "Modified dynamic text" } } //![0] diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc index 54ed3f1e6e..593feb49e7 100644 --- a/src/qml/doc/src/cppintegration/definetypes.qdoc +++ b/src/qml/doc/src/cppintegration/definetypes.qdoc @@ -195,6 +195,7 @@ be aware that properties of such a singleton type cannot be bound to. See \l{qmlRegisterSingletonType()} for more information on how implement and register a new singleton type, and how to use an existing singleton type. +\note Enum values for registered types in QML should start with a capital. \section2 Type Revisions and Versions @@ -314,7 +315,7 @@ merged with the original target class when used from within QML. For example: The \c leftMargin property is a new property added to an existing C++ type, \l QLineEdit, without modifying its source code. -The \l qmlRegisterExtendedType() function is for registering extended types. +The \l {QQmlEngine::}{qmlRegisterExtendedType()} function is for registering extended types. Note that it has two forms. \code diff --git a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc index e1db5c9d57..f4f688520a 100644 --- a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc +++ b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc @@ -328,11 +328,11 @@ public: : QObject(parent), m_author(new MessageAuthor(this)) { } - Message *author() const { + MessageAuthor *author() const { return m_author; } private: - Message *m_author; + MessageAuthor *m_author; }; \endcode diff --git a/src/qml/doc/src/external-resources.qdoc b/src/qml/doc/src/external-resources.qdoc index 202d1e4ae2..63f59bf3be 100644 --- a/src/qml/doc/src/external-resources.qdoc +++ b/src/qml/doc/src/external-resources.qdoc @@ -31,6 +31,11 @@ */ /*! + \externalpage http://qmlbook.github.io/ + \title Qt5 Cadaques +*/ + +/*! \externalpage http://www.w3schools.com/jsref/default.asp \title W3Schools JavaScript Reference */ @@ -38,4 +43,4 @@ /*! \externalpage https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date \title Mozilla Developer Network Date Reference -*/
\ No newline at end of file +*/ diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc index 3b1763bd50..e613c4fcfb 100644 --- a/src/qml/doc/src/javascript/hostenvironment.qdoc +++ b/src/qml/doc/src/javascript/hostenvironment.qdoc @@ -71,7 +71,7 @@ Note that QML makes the following modifications to native objects: \list \li An \l {String::arg}{arg()} function is added to the \l String prototype. -\li Locale-aware coversion functions are added to the \l Date and \l Number prototypes. +\li Locale-aware conversion functions are added to the \l Date and \l Number prototypes. \endlist diff --git a/src/qml/doc/src/javascript/qmlglobalobject.qdoc b/src/qml/doc/src/javascript/qmlglobalobject.qdoc index 50374f4583..b3d8a2b2a5 100644 --- a/src/qml/doc/src/javascript/qmlglobalobject.qdoc +++ b/src/qml/doc/src/javascript/qmlglobalobject.qdoc @@ -49,7 +49,7 @@ additional imports: the global object of a \l QQmlEngine. For more information about this, see \l {JavaScript Environment Restrictions}. -\keyword XMLHttpRequest +\target XMLHttpRequest \section1 XMLHttpRequest The XMLHttpRequest object, which can be used to asynchronously obtain diff --git a/src/qml/doc/src/javascript/resources.qdoc b/src/qml/doc/src/javascript/resources.qdoc index 51354e9bf0..b831e2ba70 100644 --- a/src/qml/doc/src/javascript/resources.qdoc +++ b/src/qml/doc/src/javascript/resources.qdoc @@ -57,7 +57,7 @@ parameters if they are required. An example of a code-behind implementation resource follows: -\qml +\code // MyButton.qml import QtQuick 2.0 import "my_button_impl.js" as Logic // a new instance of this JavaScript resource is loaded for each instance of Button.qml @@ -74,11 +74,11 @@ Rectangle { onClicked: Logic.onClicked(rect) } } -\endqml +\endcode -\qml +\code // my_button_impl.js -var clickCount = 0; // this state is separate for each instance of MyButton +property var clickCount = 0; // this state is separate for each instance of MyButton function onClicked(btn) { clickCount += 1; if ((clickCount % 5) == 0) { @@ -87,7 +87,7 @@ function onClicked(btn) { obj.color = Qt.rgba(0,1,0,1); } } -\endqml +\endcode In general, simple logic should be defined in-line in the QML file, but more complex logic should be separated into code-behind implementation resources @@ -138,7 +138,7 @@ within a QML document which never calls the factorial function. For example: -\qml +\code // Calculator.qml import QtQuick 2.0 import "factorial.js" as FactorialCalculator // this JavaScript resource is only ever loaded once by the engine, even if multiple instances of Calculator.qml are created @@ -149,7 +149,7 @@ Text { property int input: 17 text: "The factorial of " + input + " is: " + FactorialCalculator.factorial(input) } -\endqml +\endcode As they are shared, .pragma library files cannot access QML component instance objects or properties directly, although QML values can be passed as function diff --git a/src/qml/doc/src/qmllanguageref/syntax/basics.qdoc b/src/qml/doc/src/qmllanguageref/syntax/basics.qdoc index ddb307cf75..50767bfc8f 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/basics.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/basics.qdoc @@ -73,7 +73,7 @@ Please see the \l{qtqml-syntax-imports.html}{QML Syntax - Import Statements} documentation for in-depth information about QML imports. -\keyword qml-object-declarations +\target qml-object-declarations \section1 Object Declarations Syntactically, a block of QML code defines a tree of QML objects to be created. Objects are diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc index a8177d29d8..c0b74c4fc6 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc @@ -564,8 +564,7 @@ list of \l {Item::children}{children}. Default properties can be useful for reassigning the children of an item. See the \l{TabWidget Example}, which uses a default property to automatically reassign children of the TabWidget as children of an inner -ListView. - +ListView. See also \l {Extending QML}. \section3 Read-Only Properties diff --git a/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc b/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc index 3aa228c8eb..f28ff5082a 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc @@ -130,7 +130,7 @@ and maintainability. It may be a good idea to redesign components that have complex bindings, or at least factor the binding out into a separate function. -\keyword qml-javascript-assignment +\target qml-javascript-assignment \section1 Creating Property Bindings from JavaScript A property with a binding is automatically updated as necessary. However, if the diff --git a/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc b/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc index e7d75a89bc..602b202ed4 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc @@ -42,7 +42,7 @@ and the signal is responded to through a \e {signal handler}. When a signal is emitted, the corresponding signal handler is invoked. Placing logic such as scripts or other operations in the handler allows the component to respond to the event. -\keyword qml-signals-and-handlers +\target qml-signals-and-handlers \section1 Receiving Signals with Signal Handlers To receive a notification when a particular signal is emitted for a particular object, the object definition should declare a signal handler named \e on<Signal> where \e <Signal> is the name of the signal, with the first letter capitalized. The signal handler should contain the JavaScript code to be executed when the signal handler is invoked. @@ -208,7 +208,7 @@ SquareButton { See \l {Signal Attributes} for more details on writing signals for custom QML types. -\keyword qml-connect-signals-to-method +\target qml-connect-signals-to-method \section1 Connecting Signals to Methods and Signals Signal objects have a \c connect() method to a connect a signal either to a diff --git a/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc index 780086cfc7..ffbf2282a6 100644 --- a/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc +++ b/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc @@ -80,7 +80,7 @@ must import that module in their QML documents. \section1 Property Change Behavior for Basic Types Some basic types have properties: for example, the \l font type has -\c pixelSize, \c family and \c b properties. Unlike properties of +\c pixelSize, \c family and \c bold properties. Unlike properties of \l{qtqml-typesystem-topic.html#qml-object-types}{object types}, properties of basic types do not provide their own property change signals. It is only possible to create a property change signal handler for the basic type property itself: diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 09e5b14c97..929726f4b7 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -241,16 +241,16 @@ void Assembler::enterStandardStackFrame(const RegisterInformation ®ularRegist subPtr(TrustedImm32(frameSize), StackPointerRegister); Address slotAddr(StackFrameRegister, 0); - for (int i = 0, ei = regularRegistersToSave.size(); i < ei; ++i) { - Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister()); - slotAddr.offset -= RegisterSize; - storePtr(regularRegistersToSave.at(i).reg<RegisterID>(), slotAddr); - } for (int i = 0, ei = fpRegistersToSave.size(); i < ei; ++i) { Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); slotAddr.offset -= sizeof(double); JSC::MacroAssembler::storeDouble(fpRegistersToSave.at(i).reg<FPRegisterID>(), slotAddr); } + for (int i = 0, ei = regularRegistersToSave.size(); i < ei; ++i) { + Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister()); + slotAddr.offset -= RegisterSize; + storePtr(regularRegistersToSave.at(i).reg<RegisterID>(), slotAddr); + } } void Assembler::leaveStandardStackFrame(const RegisterInformation ®ularRegistersToSave, @@ -259,16 +259,16 @@ void Assembler::leaveStandardStackFrame(const RegisterInformation ®ularRegist Address slotAddr(StackFrameRegister, -regularRegistersToSave.size() * RegisterSize - fpRegistersToSave.size() * sizeof(double)); // restore the callee saved registers - for (int i = fpRegistersToSave.size() - 1; i >= 0; --i) { - Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); - JSC::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>()); - slotAddr.offset += sizeof(double); - } for (int i = regularRegistersToSave.size() - 1; i >= 0; --i) { Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister()); loadPtr(slotAddr, regularRegistersToSave.at(i).reg<RegisterID>()); slotAddr.offset += RegisterSize; } + for (int i = fpRegistersToSave.size() - 1; i >= 0; --i) { + Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); + JSC::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>()); + slotAddr.offset += sizeof(double); + } Q_ASSERT(slotAddr.offset == 0); @@ -327,13 +327,13 @@ Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRe // check if it's an int32: Assembler::Jump isNoInt = branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::_Integer_Type)); + Assembler::TrustedImm32(Value::Integer_Type_Internal)); convertInt32ToDouble(toInt32Register(src, Assembler::ScratchRegister), dest); Assembler::Jump intDone = jump(); // not an int, check if it's a double: isNoInt.link(this); -#if QT_POINTER_SIZE == 8 +#ifdef QV4_USE_64_BIT_VALUE_ENCODING and32(Assembler::TrustedImm32(Value::IsDouble_Mask), Assembler::ScratchRegister); Assembler::Jump isNoDbl = branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(0)); diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 3b65acb26c..532a3114f2 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -33,6 +33,17 @@ #ifndef QV4ASSEMBLER_P_H #define QV4ASSEMBLER_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "private/qv4global_p.h" #include "private/qv4jsir_p.h" #include "private/qv4isel_p.h" @@ -478,7 +489,7 @@ public: load64(addr, dest); } else { QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.val), dest); + move(TrustedImm64(undefined.rawValue()), dest); } } @@ -491,7 +502,7 @@ public: load64(addr, dest); } else { QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.val), dest); + move(TrustedImm64(undefined.rawValue()), dest); } } @@ -500,7 +511,7 @@ public: Q_UNUSED(argumentNumber); QV4::Value v = convertToValue(c); - move(TrustedImm64(v.val), dest); + move(TrustedImm64(v.rawValue()), dest); } void loadArgumentInRegister(IR::Expr* expr, RegisterID dest, int argumentNumber) @@ -509,7 +520,7 @@ public: if (!expr) { QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.val), dest); + move(TrustedImm64(undefined.rawValue()), dest); } else if (IR::Temp *t = expr->asTemp()){ loadArgumentInRegister(t, dest, argumentNumber); } else if (IR::ArgLocal *al = expr->asArgLocal()) { @@ -565,6 +576,8 @@ public: moveIntsToDouble(JSC::ARMRegisters::r0, JSC::ARMRegisters::r1, dest, FPGpr0); #elif defined(Q_PROCESSOR_X86) moveIntsToDouble(JSC::X86Registers::eax, JSC::X86Registers::edx, dest, FPGpr0); +#elif defined(Q_PROCESSOR_MIPS) + moveIntsToDouble(JSC::MIPSRegisters::v0, JSC::MIPSRegisters::v1, dest, FPGpr0); #else subPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister); Pointer tmp(StackPointerRegister, 0); @@ -595,6 +608,14 @@ public: destination.offset += 4; store32(JSC::ARMRegisters::r1, destination); } +#elif defined(Q_PROCESSOR_MIPS) + void storeReturnValue(const Pointer &dest) + { + Pointer destination = dest; + store32(JSC::MIPSRegisters::v0, destination); + destination.offset += 4; + store32(JSC::MIPSRegisters::v1, destination); + } #endif void storeReturnValue(IR::Expr *target) @@ -724,7 +745,7 @@ public: moveDouble(source, (FPRegisterID) targetTemp->index); return; } -#if QT_POINTER_SIZE == 8 +#ifdef QV4_USE_64_BIT_VALUE_ENCODING moveDoubleTo64(source, ReturnValueRegister); move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); xor64(ScratchRegister, ReturnValueRegister); @@ -735,7 +756,7 @@ public: storeDouble(source, ptr); #endif } -#if QT_POINTER_SIZE == 8 +#ifdef QV4_USE_64_BIT_VALUE_ENCODING // We need to (de)mangle the double void loadDouble(Address addr, FPRegisterID dest) { @@ -781,11 +802,11 @@ public: void storeValue(QV4::Primitive value, Address destination) { #ifdef VALUE_FITS_IN_REGISTER - store64(TrustedImm64(value.val), destination); + store64(TrustedImm64(value.rawValue()), destination); #else - store32(TrustedImm32(value.int_32), destination); + store32(TrustedImm32(value.int_32()), destination); destination.offset += 4; - store32(TrustedImm32(value.tag), destination); + store32(TrustedImm32(value.tag()), destination); #endif } @@ -820,6 +841,8 @@ public: else #if OS(WINDOWS) && CPU(X86_64) loadArgumentOnStack<argumentNumber>(value, argumentNumber); +#elif CPU(MIPS) // Stack space for 4 arguments needs to be allocated for MIPS platforms. + loadArgumentOnStack<argumentNumber>(value, argumentNumber + 4); #else // Sanity: loadArgumentOnStack<argumentNumber - RegisterArgumentCount>(value, argumentNumber); #endif @@ -846,7 +869,7 @@ public: template <int ArgumentIndex, typename Parameter> struct SizeOnStack { - enum { Size = Select<ArgumentIndex >= RegisterArgumentCount, QT_POINTER_SIZE, 0>::Chosen }; + enum { Size = Select<ArgumentIndex >= RegisterArgumentCount, sizeof(void*), 0>::Chosen }; }; template <int ArgumentIndex> @@ -945,8 +968,8 @@ public: tagAddr.offset += 4; QV4::Primitive v = convertToValue(c); - store32(TrustedImm32(v.int_32), addr); - store32(TrustedImm32(v.tag), tagAddr); + store32(TrustedImm32(v.int_32()), addr); + store32(TrustedImm32(v.tag()), tagAddr); return Pointer(addr); } @@ -961,7 +984,7 @@ public: { store32(reg, addr); addr.offset += 4; - store32(TrustedImm32(QV4::Primitive::fromBoolean(0).tag), addr); + store32(TrustedImm32(QV4::Primitive::fromBoolean(0).tag()), addr); } void storeBool(RegisterID src, RegisterID dest) @@ -1005,7 +1028,7 @@ public: { store32(reg, addr); addr.offset += 4; - store32(TrustedImm32(QV4::Primitive::fromInt32(0).tag), addr); + store32(TrustedImm32(QV4::Primitive::fromInt32(0).tag()), addr); } void storeInt32(RegisterID reg, IR::Expr *target) @@ -1054,7 +1077,7 @@ public: FPRegisterID toDoubleRegister(IR::Expr *e, FPRegisterID target = FPGpr0) { if (IR::Const *c = e->asConst()) { -#if QT_POINTER_SIZE == 8 +#ifdef QV4_USE_64_BIT_VALUE_ENCODING union { double d; int64_t i; @@ -1084,7 +1107,7 @@ public: RegisterID toInt32Register(IR::Expr *e, RegisterID scratchReg) { if (IR::Const *c = e->asConst()) { - move(TrustedImm32(convertToValue(c).int_32), scratchReg); + move(TrustedImm32(convertToValue(c).int_32()), scratchReg); return scratchReg; } @@ -1123,7 +1146,7 @@ public: Pointer tagAddr = addr; tagAddr.offset += 4; load32(tagAddr, scratchReg); - Jump inIntRange = branch32(Equal, scratchReg, TrustedImm32(QV4::Value::_Integer_Type)); + Jump inIntRange = branch32(Equal, scratchReg, TrustedImm32(QV4::Value::Integer_Type_Internal)); // it's not in signed int range, so load it as a double, and truncate it down loadDouble(addr, FPGpr0); diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp index c6c8023cd7..8b051fcb3d 100644 --- a/src/qml/jit/qv4binop.cpp +++ b/src/qml/jit/qv4binop.cpp @@ -352,7 +352,9 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta } } - if (op == IR::OpSub) { + // Special cases: + switch (op) { + case IR::OpSub: if (rightSource->asTemp() && rightSource->asTemp()->kind == IR::Temp::PhysicalRegister && targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister @@ -368,11 +370,27 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta } as->storeInt32(targetReg, target); return true; + + case IR::OpLShift: + case IR::OpRShift: + case IR::OpURShift: + if (IR::Const *c = rightSource->asConst()) { + if ((QV4::Primitive::toUInt32(c->value) & 0x1f) == 0) { + Assembler::RegisterID r = as->toInt32Register(leftSource, targetReg); + as->storeInt32(r, target); + return true; + } + } + break; + + default: + break; } Assembler::RegisterID l = as->toInt32Register(leftSource, targetReg); if (IR::Const *c = rightSource->asConst()) { // All cases of Y = X op Const Assembler::TrustedImm32 r(int(c->value)); + Assembler::TrustedImm32 ur(QV4::Primitive::toUInt32(c->value) & 0x1f); switch (op) { case IR::OpBitAnd: as->and32(r, l, targetReg); break; @@ -381,9 +399,9 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta case IR::OpAdd: as->add32(r, l, targetReg); break; case IR::OpMul: as->mul32(r, l, targetReg); break; - case IR::OpLShift: r.m_value &= 0x1f; as->lshift32(l, r, targetReg); break; - case IR::OpRShift: r.m_value &= 0x1f; as->rshift32(l, r, targetReg); break; - case IR::OpURShift: r.m_value &= 0x1f; as->urshift32(l, r, targetReg); + case IR::OpLShift: as->lshift32(l, ur, targetReg); break; + case IR::OpRShift: as->rshift32(l, ur, targetReg); break; + case IR::OpURShift: as->urshift32(l, ur, targetReg); as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations! return true; @@ -415,32 +433,33 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta case IR::OpAdd: as->add32(l, r, targetReg); break; case IR::OpMul: as->mul32(l, r, targetReg); break; -#if CPU(ARM) || CPU(X86) || CPU(X86_64) - // The ARM assembler will generate an and with 0x1f for us, and Intel will do it on the CPU. - +#if CPU(X86) || CPU(X86_64) + // Intel does the & 0x1f on the CPU, so: case IR::OpLShift: as->lshift32(l, r, targetReg); break; case IR::OpRShift: as->rshift32(l, r, targetReg); break; case IR::OpURShift: as->urshift32(l, r, targetReg); as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations! return true; #else - case IR::OpLShift: - as->move(r, Assembler::ScratchRegister); - as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister); - as->lshift32(l, Assembler::ScratchRegister, targetReg); - break; - - case IR::OpRShift: - as->move(r, Assembler::ScratchRegister); - as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister); - as->rshift32(l, Assembler::ScratchRegister, targetReg); - break; - + // Not all CPUs accept shifts over more than 31 bits, and some CPUs (like ARM) will do + // surprising stuff when shifting over 0 bits. +#define CHECK_RHS(op) { \ + as->and32(Assembler::TrustedImm32(0x1f), r, Assembler::ScratchRegister); \ + Assembler::Jump notZero = as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, Assembler::TrustedImm32(0)); \ + as->move(l, targetReg); \ + Assembler::Jump done = as->jump(); \ + notZero.link(as); \ + op; \ + done.link(as); \ +} + case IR::OpLShift: CHECK_RHS(as->lshift32(l, Assembler::ScratchRegister, targetReg)); break; + case IR::OpRShift: CHECK_RHS(as->rshift32(l, Assembler::ScratchRegister, targetReg)); break; case IR::OpURShift: - as->move(r, Assembler::ScratchRegister); - as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister); - as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations! + CHECK_RHS(as->urshift32(l, Assembler::ScratchRegister, targetReg)); + as->storeUInt32(targetReg, target); + // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations! return true; +#undef CHECK_RHS #endif case IR::OpSub: // already handled before diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h index 4fa2369312..96c8281d57 100644 --- a/src/qml/jit/qv4binop_p.h +++ b/src/qml/jit/qv4binop_p.h @@ -33,6 +33,17 @@ #ifndef QV4BINOP_P_H #define QV4BINOP_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <qv4jsir_p.h> #include <qv4isel_masm_p.h> #include <qv4assembler_p.h> diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index da511cd1eb..b6df5fb08c 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -45,6 +45,7 @@ #include "qv4binop_p.h" #include <QtCore/QBuffer> +#include <QtCore/QCoreApplication> #include <assembler/LinkBuffer.h> #include <WTFStubs.h> @@ -120,6 +121,19 @@ static void printDisassembledOutputWithCalls(QByteArray processedOutput, const Q qDebug("%s", processedOutput.constData()); } +#if defined(Q_OS_LINUX) +static FILE *pmap; + +static void qt_closePmap() +{ + if (pmap) { + fclose(pmap); + pmap = 0; + } +} + +#endif + JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) { Label endOfCode = label(); @@ -167,19 +181,21 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) *codeSize = linkBuffer.offsetOf(endOfCode); + QByteArray name; + JSC::MacroAssemblerCodeRef codeRef; - static bool showCode = !qgetenv("QV4_SHOW_ASM").isNull(); + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM"); if (showCode) { QBuffer buf; buf.open(QIODevice::WriteOnly); WTF::setDataFile(new QIODevicePrintStream(&buf)); - QByteArray name = _function->name->toUtf8(); + name = _function->name->toUtf8(); if (name.isEmpty()) { name = QByteArray::number(quintptr(_function), 16); name.prepend("IR::Function(0x"); - name.append(")"); + name.append(')'); } codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); @@ -189,6 +205,50 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); } +#if defined(Q_OS_LINUX) + // This implements writing of JIT'd addresses so that perf can find the + // symbol names. + // + // Perf expects the mapping to be in a certain place and have certain + // content, for more information, see: + // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt + static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP"); + static bool profileInitialized = false; + if (doProfile && !profileInitialized) { + profileInitialized = true; + + char pname[PATH_MAX]; + snprintf(pname, PATH_MAX - 1, "/tmp/perf-%lu.map", + (unsigned long)QCoreApplication::applicationPid()); + + pmap = fopen(pname, "w"); + if (!pmap) + qWarning("QV4: Can't write %s, call stacks will not contain JavaScript function names", pname); + + // make sure we clean up nicely + std::atexit(qt_closePmap); + } + + if (pmap) { + // this may have been pre-populated, if QV4_SHOW_ASM was on + if (name.isEmpty()) { + name = _function->name->toUtf8(); + if (name.isEmpty()) { + name = QByteArray::number(quintptr(_function), 16); + name.prepend("IR::Function(0x"); + name.append(')'); + } + } + + fprintf(pmap, "%llx %x %.*s\n", + (long long unsigned int)codeRef.code().executableAddress(), + *codeSize, + name.length(), + name.constData()); + fflush(pmap); + } +#endif + return codeRef; } @@ -215,7 +275,7 @@ void InstructionSelection::run(int functionIndex) IR::Optimizer opt(_function); opt.run(qmlEngine); - static const bool withRegisterAllocator = qgetenv("QV4_NO_REGALLOC").isEmpty(); + static const bool withRegisterAllocator = qEnvironmentVariableIsEmpty("QV4_NO_REGALLOC"); if (Assembler::RegAllocIsSupported && opt.isInSSA() && withRegisterAllocator) { RegisterAllocator regalloc(Assembler::getRegisterInfo()); regalloc.run(_function, opt); @@ -317,7 +377,7 @@ const void *InstructionSelection::addConstantTable(QVector<Primitive> *values) QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backendCompileStep() { QQmlRefPointer<QV4::CompiledData::CompilationUnit> result; - result.take(compilationUnit.take()); + result.adopt(compilationUnit.take()); return result; } @@ -339,6 +399,23 @@ void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args } } +void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base, + IR::Member::MemberKind kind, + int propertyIndex, IR::Expr *result) +{ + if (kind == IR::Member::MemberOfQmlScopeObject) { + generateFunctionCall(result, Runtime::typeofScopeObjectProperty, Assembler::EngineRegister, + Assembler::PointerToValue(base), + Assembler::TrustedImm32(propertyIndex)); + } else if (kind == IR::Member::MemberOfQmlContextObject) { + generateFunctionCall(result, Runtime::typeofContextObjectProperty, + Assembler::EngineRegister, Assembler::PointerToValue(base), + Assembler::TrustedImm32(propertyIndex)); + } else { + Q_UNREACHABLE(); + } +} + void InstructionSelection::callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) { @@ -574,9 +651,9 @@ void InstructionSelection::loadThisObject(IR::Expr *temp) #endif } -void InstructionSelection::loadQmlIdArray(IR::Expr *temp) +void InstructionSelection::loadQmlContext(IR::Expr *temp) { - generateFunctionCall(temp, Runtime::getQmlIdArray, Assembler::EngineRegister); + generateFunctionCall(temp, Runtime::getQmlContext, Assembler::EngineRegister); } void InstructionSelection::loadQmlImportedScripts(IR::Expr *temp) @@ -584,16 +661,6 @@ void InstructionSelection::loadQmlImportedScripts(IR::Expr *temp) generateFunctionCall(temp, Runtime::getQmlImportedScripts, Assembler::EngineRegister); } -void InstructionSelection::loadQmlContextObject(IR::Expr *temp) -{ - generateFunctionCall(temp, Runtime::getQmlContextObject, Assembler::EngineRegister); -} - -void InstructionSelection::loadQmlScopeObject(IR::Expr *temp) -{ - generateFunctionCall(temp, Runtime::getQmlScopeObject, Assembler::EngineRegister); -} - void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *temp) { generateFunctionCall(temp, Runtime::getQmlSingleton, Assembler::EngineRegister, Assembler::StringToIndex(name)); @@ -614,7 +681,7 @@ void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *target) _as->toUInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index); } else if (targetTemp->type == IR::BoolType) { Q_ASSERT(sourceConst->type == IR::BoolType); - _as->move(Assembler::TrustedImm32(convertToValue(sourceConst).int_32), + _as->move(Assembler::TrustedImm32(convertToValue(sourceConst).int_32()), (Assembler::RegisterID) targetTemp->index); } else { Q_UNREACHABLE(); @@ -631,7 +698,7 @@ void InstructionSelection::loadString(const QString &str, IR::Expr *target) Pointer srcAddr = _as->loadStringAddress(Assembler::ReturnValueRegister, str); _as->loadPtr(srcAddr, Assembler::ReturnValueRegister); Pointer destAddr = _as->loadAddress(Assembler::ScratchRegister, target); -#if QT_POINTER_SIZE == 8 +#ifdef QV4_USE_64_BIT_VALUE_ENCODING _as->store64(Assembler::ReturnValueRegister, destAddr); #else _as->store32(Assembler::ReturnValueRegister, destAddr); @@ -680,6 +747,18 @@ void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR:: } } +void InstructionSelection::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, IR::Expr *target) +{ + if (kind == IR::Member::MemberOfQmlScopeObject) + generateFunctionCall(target, Runtime::getQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index)); + else if (kind == IR::Member::MemberOfQmlContextObject) + generateFunctionCall(target, Runtime::getQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index)); + else if (kind == IR::Member::MemberOfIdObjectsArray) + generateFunctionCall(target, Runtime::getQmlIdObject, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index)); + else + Q_ASSERT(false); +} + void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) { if (attachedPropertiesId != 0) @@ -708,6 +787,18 @@ void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase, } } +void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) +{ + if (kind == IR::Member::MemberOfQmlScopeObject) + generateFunctionCall(Assembler::Void, Runtime::setQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), + Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); + else if (kind == IR::Member::MemberOfQmlContextObject) + generateFunctionCall(Assembler::Void, Runtime::setQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), + Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); + else + Q_ASSERT(false); +} + void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) { generateFunctionCall(Assembler::Void, Runtime::setQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), @@ -868,10 +959,10 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) quint32 tag; switch (regTemp->type) { case IR::BoolType: - tag = QV4::Value::_Boolean_Type; + tag = QV4::Value::Boolean_Type_Internal; break; case IR::SInt32Type: - tag = QV4::Value::_Integer_Type; + tag = QV4::Value::Integer_Type_Internal; break; default: tag = QV4::Value::Undefined_Type; @@ -901,6 +992,24 @@ void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr binop.generate(leftSource, rightSource, target); } +void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) +{ + prepareCallData(args, base); + + if (kind == IR::Member::MemberOfQmlScopeObject) + generateFunctionCall(result, Runtime::callQmlScopeObjectProperty, + Assembler::EngineRegister, + Assembler::TrustedImm32(propertyIndex), + baseAddressForCallData()); + else if (kind == IR::Member::MemberOfQmlContextObject) + generateFunctionCall(result, Runtime::callQmlContextObjectProperty, + Assembler::EngineRegister, + Assembler::TrustedImm32(propertyIndex), + baseAddressForCallData()); + else + Q_ASSERT(false); +} + void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) { @@ -987,13 +1096,13 @@ void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *targe // check if it's an int32: Assembler::Jump isNoInt = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::_Integer_Type)); + Assembler::TrustedImm32(Value::Integer_Type_Internal)); convertIntToDouble(source, target); Assembler::Jump intDone = _as->jump(); // not an int, check if it's NOT a double: isNoInt.link(_as); -#if QT_POINTER_SIZE == 8 +#ifdef QV4_USE_64_BIT_VALUE_ENCODING _as->and32(Assembler::TrustedImm32(Value::IsDouble_Mask), Assembler::ScratchRegister); Assembler::Jump isDbl = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, Assembler::TrustedImm32(0)); @@ -1011,7 +1120,7 @@ void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *targe Assembler::Pointer addr2 = _as->loadAddress(Assembler::ScratchRegister, source); IR::Temp *targetTemp = target->asTemp(); if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { -#if QT_POINTER_SIZE == 8 +#if Q_PROCESSOR_WORDSIZE == 8 _as->load64(addr2, Assembler::ScratchRegister); _as->store64(Assembler::ScratchRegister, _as->loadAddress(Assembler::ReturnValueRegister, target)); #else @@ -1080,7 +1189,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe switch (source->type) { case IR::VarType: { -#if QT_POINTER_SIZE == 8 +#ifdef QV4_USE_64_BIT_VALUE_ENCODING Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); _as->load64(addr, Assembler::ScratchRegister); _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister); @@ -1110,7 +1219,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe Assembler::Pointer targetAddr = _as->loadAddress(Assembler::ScratchRegister, target); _as->store32(Assembler::ReturnValueRegister, targetAddr); targetAddr.offset += 4; - _as->store32(Assembler::TrustedImm32(Value::_Integer_Type), targetAddr); + _as->store32(Assembler::TrustedImm32(Value::Integer_Type_Internal), targetAddr); } else { _as->storeInt32(Assembler::ReturnValueRegister, target); } @@ -1123,14 +1232,14 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe // check if it's an int32: Assembler::Jump fallback = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, - Assembler::TrustedImm32(Value::_Integer_Type)); + Assembler::TrustedImm32(Value::Integer_Type_Internal)); IR::Temp *targetTemp = target->asTemp(); if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { _as->load32(addr, Assembler::ReturnValueRegister); Assembler::Pointer targetAddr = _as->loadAddress(Assembler::ScratchRegister, target); _as->store32(Assembler::ReturnValueRegister, targetAddr); targetAddr.offset += 4; - _as->store32(Assembler::TrustedImm32(Value::_Integer_Type), targetAddr); + _as->store32(Assembler::TrustedImm32(Value::Integer_Type_Internal), targetAddr); } else { _as->load32(addr, (Assembler::RegisterID) targetTemp->index); } @@ -1187,7 +1296,7 @@ void InstructionSelection::convertTypeToUInt32(IR::Expr *source, IR::Expr *targe // check if it's an int32: Assembler::Jump isNoInt = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::_Integer_Type)); + Assembler::TrustedImm32(Value::Integer_Type_Internal)); Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); _as->storeUInt32(_as->toInt32Register(addr, Assembler::ScratchRegister), target); Assembler::Jump intDone = _as->jump(); @@ -1297,11 +1406,11 @@ void InstructionSelection::visitCJump(IR::CJump *s) } else { Address temp = _as->loadAddress(Assembler::ScratchRegister, s->cond); Address tag = temp; - tag.offset += qOffsetOf(QV4::Value, tag); + tag.offset += QV4::Value::tagOffset(); Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(QV4::Value::Boolean_Type)); Address data = temp; - data.offset += qOffsetOf(QV4::Value, int_32); + data.offset += QV4::Value::valueOffset(); _as->load32(data, Assembler::ReturnValueRegister); Assembler::Jump testBoolean = _as->jump(); @@ -1383,11 +1492,14 @@ void InstructionSelection::visitRet(IR::Ret *s) // this only happens if the method doesn't have a return statement and can // only exit through an exception } else if (IR::Temp *t = s->expr->asTemp()) { -#if CPU(X86) || CPU(ARM) +#if CPU(X86) || CPU(ARM) || CPU(MIPS) # if CPU(X86) Assembler::RegisterID lowReg = JSC::X86Registers::eax; Assembler::RegisterID highReg = JSC::X86Registers::edx; +# elif CPU(MIPS) + Assembler::RegisterID lowReg = JSC::MIPSRegisters::v0; + Assembler::RegisterID highReg = JSC::MIPSRegisters::v1; # else // CPU(ARM) Assembler::RegisterID lowReg = JSC::ARMRegisters::r0; Assembler::RegisterID highReg = JSC::ARMRegisters::r1; @@ -1406,16 +1518,16 @@ void InstructionSelection::visitRet(IR::Ret *s) Assembler::Jump done = _as->jump(); intRange.link(_as); _as->move(srcReg, lowReg); - _as->move(Assembler::TrustedImm32(QV4::Value::_Integer_Type), highReg); + _as->move(Assembler::TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); done.link(_as); } break; case IR::SInt32Type: _as->move((Assembler::RegisterID) t->index, lowReg); - _as->move(Assembler::TrustedImm32(QV4::Value::_Integer_Type), highReg); + _as->move(Assembler::TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); break; case IR::BoolType: _as->move((Assembler::RegisterID) t->index, lowReg); - _as->move(Assembler::TrustedImm32(QV4::Value::_Boolean_Type), highReg); + _as->move(Assembler::TrustedImm32(QV4::Value::Boolean_Type_Internal), highReg); break; default: Q_UNREACHABLE(); @@ -1444,7 +1556,7 @@ void InstructionSelection::visitRet(IR::Ret *s) Assembler::Jump done = _as->jump(); intRange.link(_as); _as->zeroExtend32ToPtr(srcReg, Assembler::ReturnValueRegister); - quint64 tag = QV4::Value::_Integer_Type; + quint64 tag = QV4::Value::Integer_Type_Internal; _as->or64(Assembler::TrustedImm64(tag << 32), Assembler::ReturnValueRegister); done.link(_as); @@ -1453,10 +1565,10 @@ void InstructionSelection::visitRet(IR::Ret *s) quint64 tag; switch (t->type) { case IR::SInt32Type: - tag = QV4::Value::_Integer_Type; + tag = QV4::Value::Integer_Type_Internal; break; case IR::BoolType: - tag = QV4::Value::_Boolean_Type; + tag = QV4::Value::Boolean_Type_Internal; break; default: tag = QV4::Value::Undefined_Type; @@ -1472,13 +1584,16 @@ void InstructionSelection::visitRet(IR::Ret *s) } else if (IR::Const *c = s->expr->asConst()) { QV4::Primitive retVal = convertToValue(c); #if CPU(X86) - _as->move(Assembler::TrustedImm32(retVal.int_32), JSC::X86Registers::eax); - _as->move(Assembler::TrustedImm32(retVal.tag), JSC::X86Registers::edx); + _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::X86Registers::eax); + _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::X86Registers::edx); #elif CPU(ARM) - _as->move(Assembler::TrustedImm32(retVal.int_32), JSC::ARMRegisters::r0); - _as->move(Assembler::TrustedImm32(retVal.tag), JSC::ARMRegisters::r1); + _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::ARMRegisters::r0); + _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::ARMRegisters::r1); +#elif CPU(MIPS) + _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::MIPSRegisters::v0); + _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::MIPSRegisters::v1); #else - _as->move(Assembler::TrustedImm64(retVal.val), Assembler::ReturnValueRegister); + _as->move(Assembler::TrustedImm64(retVal.rawValue()), Assembler::ReturnValueRegister); #endif } else { Q_UNREACHABLE(); @@ -1499,13 +1614,16 @@ void InstructionSelection::visitRet(IR::Ret *s) _as->exceptionReturnLabel = _as->label(); QV4::Primitive retVal = Primitive::undefinedValue(); #if CPU(X86) - _as->move(Assembler::TrustedImm32(retVal.int_32), JSC::X86Registers::eax); - _as->move(Assembler::TrustedImm32(retVal.tag), JSC::X86Registers::edx); + _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::X86Registers::eax); + _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::X86Registers::edx); #elif CPU(ARM) - _as->move(Assembler::TrustedImm32(retVal.int_32), JSC::ARMRegisters::r0); - _as->move(Assembler::TrustedImm32(retVal.tag), JSC::ARMRegisters::r1); + _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::ARMRegisters::r0); + _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::ARMRegisters::r1); +#elif CPU(MIPS) + _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::MIPSRegisters::v0); + _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::MIPSRegisters::v1); #else - _as->move(Assembler::TrustedImm64(retVal.val), Assembler::ReturnValueRegister); + _as->move(Assembler::TrustedImm64(retVal.rawValue()), Assembler::ReturnValueRegister); #endif _as->jump(leaveStackFrame); } @@ -1539,7 +1657,7 @@ int InstructionSelection::prepareCallData(IR::ExprList* args, IR::Expr *thisObje } Pointer p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, tag)); - _as->store32(Assembler::TrustedImm32(QV4::Value::_Integer_Type), p); + _as->store32(Assembler::TrustedImm32(QV4::Value::Integer_Type_Internal), p); p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, argc)); _as->store32(Assembler::TrustedImm32(argc), p); p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, thisObject)); @@ -1718,7 +1836,7 @@ bool InstructionSelection::visitCJumpStrictNullUndefined(IR::Type nullOrUndef, I Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal : Assembler::NotEqual; - const Assembler::TrustedImm32 tag(nullOrUndef == IR::NullType ? int(QV4::Value::_Null_Type) + const Assembler::TrustedImm32 tag(nullOrUndef == IR::NullType ? int(QV4::Value::Null_Type_Internal) : int(QV4::Value::Undefined_Type)); _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock); return true; @@ -1760,7 +1878,7 @@ bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock // check if the tag of the var operand is indicates 'boolean' _as->load32(otherAddr, Assembler::ScratchRegister); Assembler::Jump noBool = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(QV4::Value::_Boolean_Type)); + Assembler::TrustedImm32(QV4::Value::Boolean_Type_Internal)); if (binop->op == IR::OpStrictEqual) _as->addPatch(falseBlock, noBool); else @@ -1809,7 +1927,7 @@ bool InstructionSelection::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Bin if (binop->op == IR::OpNotEqual) qSwap(trueBlock, falseBlock); - Assembler::Jump isNull = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::_Null_Type))); + Assembler::Jump isNull = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::Null_Type_Internal))); Assembler::Jump isUndefined = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::Undefined_Type))); _as->addPatch(trueBlock, isNull); _as->addPatch(trueBlock, isUndefined); diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 87b4a20bfc..6e9b02b034 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -33,6 +33,17 @@ #ifndef QV4ISEL_MASM_P_H #define QV4ISEL_MASM_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "private/qv4global_p.h" #include "private/qv4jsir_p.h" #include "private/qv4isel_p.h" @@ -69,6 +80,7 @@ protected: virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> backendCompileStep(); virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result); + virtual void callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result); virtual void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result); virtual void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result); virtual void callBuiltinTypeofName(const QString &name, IR::Expr *result); @@ -91,14 +103,13 @@ protected: virtual void callBuiltinSetupArgumentObject(IR::Expr *result); virtual void callBuiltinConvertThisToObject(); virtual void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result); + virtual void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result); virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result); virtual void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result); virtual void convertType(IR::Expr *source, IR::Expr *target); virtual void loadThisObject(IR::Expr *temp); - virtual void loadQmlIdArray(IR::Expr *target); + virtual void loadQmlContext(IR::Expr *target); virtual void loadQmlImportedScripts(IR::Expr *target); - virtual void loadQmlContextObject(IR::Expr *target); - virtual void loadQmlScopeObject(IR::Expr *target); virtual void loadQmlSingleton(const QString &name, IR::Expr *target); virtual void loadConst(IR::Const *sourceConst, IR::Expr *target); virtual void loadString(const QString &str, IR::Expr *target); @@ -107,8 +118,10 @@ protected: virtual void setActivationProperty(IR::Expr *source, const QString &targetName); virtual void initClosure(IR::Closure *closure, IR::Expr *target); virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target); + virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target); virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target); virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName); + virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex); virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex); virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target); virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex); diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index ae06a99d2a..d1d97c8f84 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -35,7 +35,7 @@ #include <QtCore/QDebug> #include "qv4regalloc_p.h" #include "qv4alloca_p.h" -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <algorithm> #if defined(Q_CC_MINGW) @@ -267,6 +267,7 @@ public: protected: // IRDecoder virtual void callBuiltinInvalid(IR::Name *, IR::ExprList *, IR::Expr *) {} + virtual void callBuiltinTypeofQmlContextProperty(IR::Expr *, IR::Member::MemberKind, int, IR::Expr *) {} virtual void callBuiltinTypeofMember(IR::Expr *, const QString &, IR::Expr *) {} virtual void callBuiltinTypeofSubscript(IR::Expr *, IR::Expr *, IR::Expr *) {} virtual void callBuiltinTypeofName(const QString &, IR::Expr *) {} @@ -299,6 +300,16 @@ protected: // IRDecoder addCall(); } + virtual void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int propertyIndex, IR::ExprList *args, IR::Expr *result) + { + Q_UNUSED(propertyIndex) + + addDef(result); + addUses(base->asTemp(), Use::CouldHaveRegister); + addUses(args, Use::CouldHaveRegister); + addCall(); + } + virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) { @@ -421,7 +432,7 @@ protected: // IRDecoder addDef(temp); } - virtual void loadQmlIdArray(IR::Expr *temp) + virtual void loadQmlContext(IR::Expr *temp) { addDef(temp); addCall(); @@ -433,20 +444,6 @@ protected: // IRDecoder addCall(); } - virtual void loadQmlContextObject(Expr *temp) - { - addDef(temp); - addCall(); - } - - virtual void loadQmlScopeObject(Expr *temp) - { - Q_UNUSED(temp); - - addDef(temp); - addCall(); - } - virtual void loadQmlSingleton(const QString &/*name*/, Expr *temp) { Q_UNUSED(temp); @@ -511,6 +508,13 @@ protected: // IRDecoder addCall(); } + virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind /*kind*/, int /*propertyIndex*/) + { + addUses(source->asTemp(), Use::CouldHaveRegister); + addUses(targetBase->asTemp(), Use::CouldHaveRegister); + addCall(); + } + virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int /*propertyIndex*/) { addUses(source->asTemp(), Use::CouldHaveRegister); @@ -518,6 +522,13 @@ protected: // IRDecoder addCall(); } + virtual void getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int /*index*/, IR::Expr *target) + { + addDef(target); + addUses(base->asTemp(), Use::CouldHaveRegister); + addCall(); + } + virtual void getQObjectProperty(IR::Expr *base, int /*propertyIndex*/, bool /*captureRequired*/, bool /*isSingleton*/, int /*attachedPropertiesId*/, IR::Expr *target) { addDef(target); @@ -935,7 +946,7 @@ private: return; while (!_unprocessed.isEmpty()) { - const LifeTimeInterval *i = _unprocessed.first(); + const LifeTimeInterval *i = _unprocessed.constFirst(); if (i->start() > position) break; @@ -1313,7 +1324,7 @@ void RegisterAllocator::run(IR::Function *function, const Optimizer &opt) if (DebugRegAlloc) qDebug() << "*** Finished regalloc , result:"; - static bool showCode = !qgetenv("QV4_SHOW_IR").isNull(); + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR"); if (showCode) { QBuffer buf; buf.open(QIODevice::WriteOnly); diff --git a/src/qml/jit/qv4regalloc_p.h b/src/qml/jit/qv4regalloc_p.h index f0d78cf0d3..1b6eb34e0b 100644 --- a/src/qml/jit/qv4regalloc_p.h +++ b/src/qml/jit/qv4regalloc_p.h @@ -33,6 +33,17 @@ #ifndef QV4REGALLOC_P_H #define QV4REGALLOC_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include "qv4isel_p.h" #include "qv4ssa_p.h" diff --git a/src/qml/jit/qv4registerinfo_p.h b/src/qml/jit/qv4registerinfo_p.h index ebd8537a85..cfd7bcb071 100644 --- a/src/qml/jit/qv4registerinfo_p.h +++ b/src/qml/jit/qv4registerinfo_p.h @@ -34,6 +34,17 @@ #ifndef QV4REGISTERINFO_P_H #define QV4REGISTERINFO_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QtCore/QString> QT_BEGIN_NAMESPACE diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h index 05741f0ae5..1e62b23fe4 100644 --- a/src/qml/jit/qv4targetplatform_p.h +++ b/src/qml/jit/qv4targetplatform_p.h @@ -34,6 +34,17 @@ #ifndef QV4TARGETPLATFORM_P_H #define QV4TARGETPLATFORM_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <config.h> #if ENABLE(ASSEMBLER) @@ -346,6 +357,71 @@ public: static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(JSC::ARMRegisters::lr); } #endif // Linux on ARM (32 bit) +#if defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) + enum { RegAllocIsSupported = 1 }; + + static const JSC::MacroAssembler::RegisterID StackFrameRegister = JSC::MIPSRegisters::fp; + static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::MIPSRegisters::sp; + static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::MIPSRegisters::s0; + static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::MIPSRegisters::s1; + static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::MIPSRegisters::v0; + static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::MIPSRegisters::s2; + static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::MIPSRegisters::f0; + static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::MIPSRegisters::f2; + + static RegisterInformation getPlatformRegisterInfo() + { + typedef RegisterInfo RI; + return RegisterInformation() + // Note: t0, t1, t2, t3 and f16 are already used by MacroAssemblerMIPS. + << RI(JSC::MIPSRegisters::t4, QStringLiteral("t4"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::t5, QStringLiteral("t5"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::t6, QStringLiteral("t6"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::t7, QStringLiteral("t7"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::t8, QStringLiteral("t8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::s0, QStringLiteral("s0"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::MIPSRegisters::s1, QStringLiteral("s1"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::MIPSRegisters::s2, QStringLiteral("s2"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::MIPSRegisters::s3, QStringLiteral("s3"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f4, QStringLiteral("f4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f6, QStringLiteral("f6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f8, QStringLiteral("f8"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f10, QStringLiteral("f10"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f18, QStringLiteral("f18"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f20, QStringLiteral("f20"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f22, QStringLiteral("f22"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f24, QStringLiteral("f24"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f26, QStringLiteral("f26"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f28, QStringLiteral("f28"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) + ; + } + +#undef HAVE_ALU_OPS_WITH_MEM_OPERAND +#undef VALUE_FITS_IN_REGISTER + static const int RegisterSize = 4; + +#define ARGUMENTS_IN_REGISTERS + static const int RegisterArgumentCount = 4; + static JSC::MacroAssembler::RegisterID registerForArgument(int index) + { + static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + JSC::MIPSRegisters::a0, + JSC::MIPSRegisters::a1, + JSC::MIPSRegisters::a2, + JSC::MIPSRegisters::a3 + }; + + Q_ASSERT(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + + static const int StackAlignment = 8; + static const int StackShadowSpace = 4 * RegisterSize; // Stack space for 4 argument registers. + static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. + static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) { as->push(JSC::MIPSRegisters::ra); } + static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(JSC::MIPSRegisters::ra); } +#endif // Linux on MIPS (32 bit) + public: // utility functions static RegisterInformation getRegisterInfo() { diff --git a/src/qml/jit/qv4unop_p.h b/src/qml/jit/qv4unop_p.h index f96898ce1b..69a70062b8 100644 --- a/src/qml/jit/qv4unop_p.h +++ b/src/qml/jit/qv4unop_p.h @@ -33,6 +33,17 @@ #ifndef QV4UNOP_P_H #define QV4UNOP_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <qv4jsir_p.h> #include <qv4isel_masm_p.h> diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index ee27c21aed..5ccbccebad 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -77,7 +77,6 @@ Q_DECLARE_METATYPE(QList<int>) \ingroup qtjavascript \inmodule QtQml - \mainclass \section1 Evaluating Scripts @@ -161,8 +160,82 @@ Q_DECLARE_METATYPE(QList<int>) \snippet code/src_script_qjsengine.cpp 5 - \sa QJSValue, {Making Applications Scriptable} + \section1 Extensions + QJSEngine provides a compliant ECMAScript implementation. By default, + familiar utilities like logging are not available, but they can can be + installed via the \l installExtensions() function. + + \sa QJSValue, {Making Applications Scriptable}, + {List of JavaScript Objects and Functions} + +*/ + +/*! + \enum QJSEngine::Extension + + This enum is used to specify extensions to be installed via + \l installExtensions(). + + \value TranslationExtension Indicates that translation functions (\c qsTr(), + for example) should be installed. + + \value ConsoleExtension Indicates that console functions (\c console.log(), + for example) should be installed. + + \value GarbageCollectionExtension Indicates that garbage collection + functions (\c gc(), for example) should be installed. + + \value AllExtensions Indicates that all extension should be installed. + + \b TranslationExtension + + The relation between script translation functions and C++ translation + functions is described in the following table: + + \table + \header \li Script Function \li Corresponding C++ Function + \row \li qsTr() \li QObject::tr() + \row \li QT_TR_NOOP() \li QT_TR_NOOP() + \row \li qsTranslate() \li QCoreApplication::translate() + \row \li QT_TRANSLATE_NOOP() \li QT_TRANSLATE_NOOP() + \row \li qsTrId() \li qtTrId() + \row \li QT_TRID_NOOP() \li QT_TRID_NOOP() + \endtable + + This flag also adds an \c arg() function to the string prototype. + + For more information, see the \l {Internationalization with Qt} + documentation. + + \b ConsoleExtension + + The \l {Console API}{console} object implements a subset of the + \l {https://developer.mozilla.org/en-US/docs/Web/API/Console}{Console API}, + which provides familiar logging functions, such as \c console.log(). + + The list of functions added is as follows: + + \list + \li \c console.assert() + \li \c console.debug() + \li \c console.exception() + \li \c console.info() + \li \c console.log() (equivalent to \c console.debug()) + \li \c console.error() + \li \c console.time() + \li \c console.timeEnd() + \li \c console.trace() + \li \c console.count() + \li \c console.warn() + \li \c {print()} (equivalent to \c console.debug()) + \endlist + + For more information, see the \l {Console API} documentation. + + \b GarbageCollectionExtension + + The \c gc() function is equivalent to calling \l collectGarbage(). */ QT_BEGIN_NAMESPACE @@ -235,8 +308,11 @@ void QJSEngine::collectGarbage() d->m_v4Engine->memoryManager->runGC(); } +#if QT_DEPRECATED_SINCE(5, 6) + /*! \since 5.4 + \obsolete Installs translator functions on the given \a object, or on the Global Object if no object is specified. @@ -260,30 +336,46 @@ void QJSEngine::collectGarbage() */ void QJSEngine::installTranslatorFunctions(const QJSValue &object) { + installExtensions(TranslationExtension, object); +} + +#endif // QT_DEPRECATED_SINCE(5, 6) + + +/*! + \since 5.6 + + Installs JavaScript \a extensions to add functionality that is not + available in a standard ECMAScript implementation. + + The extensions are installed on the given \a object, or on the + \l {globalObject()}{Global Object} if no object is specified. + + Several extensions can be installed at once by \c {OR}-ing the enum values: + + \code + installExtensions(QJSEngine::TranslationExtension | QJSEngine::ConsoleExtension); + \endcode + + \sa Extension +*/ +void QJSEngine::installExtensions(QJSEngine::Extensions extensions, const QJSValue &object) +{ QV4::ExecutionEngine *otherEngine = QJSValuePrivate::engine(&object); if (otherEngine && otherEngine != d->m_v4Engine) { - qWarning("QJSEngine: Trying to install a translator function from a different engine"); + qWarning("QJSEngine: Trying to install extensions from a different engine"); return; } + QV4::Scope scope(d->m_v4Engine); QV4::ScopedObject obj(scope); QV4::Value *val = QJSValuePrivate::getValue(&object); if (val) obj = val; if (!obj) - obj = scope.engine->globalObject(); -#ifndef QT_NO_TRANSLATION - obj->defineDefaultProperty(QStringLiteral("qsTranslate"), QV4::GlobalExtensions::method_qsTranslate); - obj->defineDefaultProperty(QStringLiteral("QT_TRANSLATE_NOOP"), QV4::GlobalExtensions::method_qsTranslateNoOp); - obj->defineDefaultProperty(QStringLiteral("qsTr"), QV4::GlobalExtensions::method_qsTr); - obj->defineDefaultProperty(QStringLiteral("QT_TR_NOOP"), QV4::GlobalExtensions::method_qsTrNoOp); - obj->defineDefaultProperty(QStringLiteral("qsTrId"), QV4::GlobalExtensions::method_qsTrId); - obj->defineDefaultProperty(QStringLiteral("QT_TRID_NOOP"), QV4::GlobalExtensions::method_qsTrIdNoOp); - - // string prototype extension - scope.engine->stringPrototype.asObject()->defineDefaultProperty(QStringLiteral("arg"), - QV4::GlobalExtensions::method_string_arg); -#endif + obj = scope.engine->globalObject; + + QV4::GlobalExtensions::init(obj, extensions); } /*! @@ -318,8 +410,10 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in { QV4::ExecutionEngine *v4 = d->m_v4Engine; QV4::Scope scope(v4); - QV4::ScopedContext ctx(scope, v4->currentContext()); - if (ctx->d() != v4->rootContext()) + QV4::ExecutionContextSaver saver(scope); + + QV4::ExecutionContext *ctx = v4->currentContext; + if (ctx->d() != v4->rootContext()->d()) ctx = v4->pushGlobalContext(); QV4::ScopedValue result(scope); @@ -331,8 +425,7 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in result = script.run(); if (scope.engine->hasException) result = v4->catchException(); - if (ctx->d() != v4->rootContext()) - v4->popContext(); + return QJSValue(v4, result->asReturnedValue()); } @@ -414,7 +507,7 @@ QJSValue QJSEngine::globalObject() const { Q_D(const QJSEngine); QV4::Scope scope(d->m_v4Engine); - QV4::ScopedValue v(scope, d->m_v4Engine->globalObject()); + QV4::ScopedValue v(scope, d->m_v4Engine->globalObject); return QJSValue(d->m_v4Engine, v->asReturnedValue()); } @@ -554,7 +647,7 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) Creates a QJSValue with the given \a value. - \sa fromScriptValue(), newVariant() + \sa fromScriptValue() */ /*! \fn T QJSEngine::fromScriptValue(const QJSValue &value) @@ -581,7 +674,7 @@ QJSEnginePrivate::~QJSEnginePrivate() QQmlPropertyCache *QJSEnginePrivate::createCache(const QMetaObject *mo) { if (!mo->superClass()) { - QQmlPropertyCache *rv = new QQmlPropertyCache(q_func(), mo); + QQmlPropertyCache *rv = new QQmlPropertyCache(QV8Engine::getV4(q_func()), mo); propertyCache.insert(mo, rv); return rv; } else { diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h index 123eb727df..40b0a60369 100644 --- a/src/qml/jsapi/qjsengine.h +++ b/src/qml/jsapi/qjsengine.h @@ -81,7 +81,19 @@ public: void collectGarbage(); - void installTranslatorFunctions(const QJSValue &object = QJSValue()); +#if QT_DEPRECATED_SINCE(5, 6) + QT_DEPRECATED void installTranslatorFunctions(const QJSValue &object = QJSValue()); +#endif + + enum Extension { + TranslationExtension = 0x1, + ConsoleExtension = 0x2, + GarbageCollectionExtension = 0x4, + AllExtensions = 0xffffffff + }; + Q_DECLARE_FLAGS(Extensions, Extension) + + void installExtensions(Extensions extensions, const QJSValue &object = QJSValue()); QV8Engine *handle() const { return d; } @@ -102,6 +114,8 @@ private: friend class QV8Engine; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QJSEngine::Extensions) + inline bool qjsvalue_cast_helper(const QJSValue &value, int type, void *ptr) { return QJSEngine::convertV2(value, type, ptr); diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 1d9dc2b6db..a49b98c921 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -37,13 +37,14 @@ #include "qjsengine.h" #include "qjsvalue.h" #include "qjsvalue_p.h" -#include "qv4value_inl_p.h" +#include "qv4value_p.h" #include "qv4object_p.h" #include "qv4functionobject_p.h" #include "qv4dateobject_p.h" #include "qv4runtime_p.h" #include "qv4variantobject_p.h" #include "qv4regexpobject_p.h" +#include "qv4errorobject_p.h" #include "private/qv8engine_p.h" #include <private/qv4mm_p.h> #include <private/qv4scopedvalue_p.h> @@ -57,7 +58,6 @@ \ingroup qtjavascript \inmodule QtQml - \mainclass QJSValue supports the types defined in the \l{ECMA-262} standard: The primitive types, which are Undefined, Null, Boolean, @@ -329,8 +329,7 @@ bool QJSValue::isError() const QV4::Value *val = QJSValuePrivate::getValue(this); if (!val) return false; - Object *o = val->asObject(); - return o && o->asErrorObject(); + return val->as<ErrorObject>(); } /*! @@ -344,7 +343,7 @@ bool QJSValue::isArray() const QV4::Value *val = QJSValuePrivate::getValue(this); if (!val) return false; - return val->asArrayObject(); + return val->as<ArrayObject>(); } /*! @@ -361,7 +360,7 @@ bool QJSValue::isObject() const QV4::Value *val = QJSValuePrivate::getValue(this); if (!val) return false; - return val->asObject(); + return val->as<Object>(); } /*! @@ -375,7 +374,7 @@ bool QJSValue::isCallable() const QV4::Value *val = QJSValuePrivate::getValue(this); if (!val) return false; - return val->asFunctionObject(); + return val->as<FunctionObject>(); } /*! @@ -601,7 +600,7 @@ QVariant QJSValue::toVariant() const QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch); Q_ASSERT(val); - if (Object *o = val->asObject()) + if (Object *o = val->as<Object>()) return o->engine()->toVariant(*val, /*typeHint*/ -1, /*createJSValueForObjects*/ false); if (val->isString()) @@ -640,7 +639,7 @@ QJSValue QJSValue::call(const QJSValueList &args) if (!val) return QJSValue(); - FunctionObject *f = val->asFunctionObject(); + FunctionObject *f = val->as<FunctionObject>(); if (!f) return QJSValue(); @@ -649,7 +648,7 @@ QJSValue QJSValue::call(const QJSValueList &args) Scope scope(engine); ScopedCallData callData(scope, args.length()); - callData->thisObject = engine->globalObject()->asReturnedValue(); + callData->thisObject = engine->globalObject; for (int i = 0; i < args.size(); ++i) { if (!QJSValuePrivate::checkEngine(engine, args.at(i))) { qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine"); @@ -691,7 +690,7 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList if (!val) return QJSValue(); - FunctionObject *f = val->asFunctionObject(); + FunctionObject *f = val->as<FunctionObject>(); if (!f) return QJSValue(); @@ -745,7 +744,7 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) if (!val) return QJSValue(); - FunctionObject *f = val->asFunctionObject(); + FunctionObject *f = val->as<FunctionObject>(); if (!f) return QJSValue(); @@ -801,7 +800,7 @@ QJSValue QJSValue::prototype() const if (!engine) return QJSValue(); QV4::Scope scope(engine); - ScopedObject o(scope, QJSValuePrivate::getValue(this)->asObject()); + ScopedObject o(scope, QJSValuePrivate::getValue(this)->as<Object>()); if (!o) return QJSValue(); ScopedObject p(scope, o->prototype()); @@ -1041,7 +1040,7 @@ QJSValue QJSValue::property(quint32 arrayIndex) const if (!o) return QJSValue(); - QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(engine->id_uintMax) : o->getIndexed(arrayIndex)); + QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(engine->id_uintMax()) : o->getIndexed(arrayIndex)); if (engine->hasException) engine->catchException(); return QJSValue(engine, result->asReturnedValue()); @@ -1120,7 +1119,7 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) if (arrayIndex != UINT_MAX) o->putIndexed(arrayIndex, v); else - o->put(engine->id_uintMax, v); + o->put(engine->id_uintMax(), v); if (engine->hasException) engine->catchException(); } @@ -1236,7 +1235,7 @@ QDateTime QJSValue::toDateTime() const { QV4::Value *val = QJSValuePrivate::getValue(this); if (val) { - QV4::DateObject *date = val->asDateObject(); + QV4::DateObject *date = val->as<DateObject>(); if (date) return date->toQDateTime(); } @@ -1250,7 +1249,7 @@ QDateTime QJSValue::toDateTime() const bool QJSValue::isDate() const { QV4::Value *val = QJSValuePrivate::getValue(this); - return val && val->asDateObject(); + return val && val->as<DateObject>(); } /*! diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h index 93a28a4a5f..08dc184412 100644 --- a/src/qml/jsapi/qjsvalue_p.h +++ b/src/qml/jsapi/qjsvalue_p.h @@ -47,7 +47,7 @@ #include <qjsvalue.h> #include <private/qtqmlglobal_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4string_p.h> #include <private/qv4engine_p.h> #include <private/qv4object_p.h> diff --git a/src/qml/jsapi/qjsvalueiterator.cpp b/src/qml/jsapi/qjsvalueiterator.cpp index 1ee4121f5c..a24953ae3f 100644 --- a/src/qml/jsapi/qjsvalueiterator.cpp +++ b/src/qml/jsapi/qjsvalueiterator.cpp @@ -52,9 +52,6 @@ QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValue &v) QV4::Scope scope(e); QV4::ScopedObject o(scope, QJSValuePrivate::getValue(&v)); iterator.set(e, e->newForEachIteratorObject(o)); - - currentName = (QV4::String *)0; - nextName = (QV4::String *)0; } @@ -102,8 +99,10 @@ QJSValueIterator::QJSValueIterator(const QJSValue& object) QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value()); it->d()->it.flags = QV4::ObjectIterator::NoFlags; QV4::ScopedString nm(scope); - it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &d_ptr->nextProperty, &d_ptr->nextAttributes); - d_ptr->nextName = nm; + QV4::Property nextProperty; + QV4::PropertyAttributes nextAttributes; + it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes); + d_ptr->nextName.set(v4, nm.asReturnedValue()); } /*! @@ -125,7 +124,7 @@ bool QJSValueIterator::hasNext() const QV4::Value *val = QJSValuePrivate::getValue(&d_ptr->value); if (!val || !val->isObject()) return false; - return !!d_ptr->nextName || d_ptr->nextIndex != UINT_MAX; + return d_ptr->nextName.as<QV4::String>() || d_ptr->nextIndex != UINT_MAX; } /*! @@ -143,8 +142,6 @@ bool QJSValueIterator::next() return false; d_ptr->currentName = d_ptr->nextName; d_ptr->currentIndex = d_ptr->nextIndex; - d_ptr->currentProperty.copy(&d_ptr->nextProperty, d_ptr->nextAttributes); - d_ptr->currentAttributes = d_ptr->nextAttributes; QV4::ExecutionEngine *v4 = d_ptr->iterator.engine(); if (!v4) @@ -152,9 +149,11 @@ bool QJSValueIterator::next() QV4::Scope scope(v4); QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value()); QV4::ScopedString nm(scope); - it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &d_ptr->nextProperty, &d_ptr->nextAttributes); - d_ptr->nextName = nm; - return !!d_ptr->currentName || d_ptr->currentIndex != UINT_MAX; + QV4::Property nextProperty; + QV4::PropertyAttributes nextAttributes; + it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes); + d_ptr->nextName.set(v4, nm.asReturnedValue()); + return d_ptr->currentName.as<QV4::String>() || d_ptr->currentIndex != UINT_MAX; } /*! @@ -168,8 +167,8 @@ QString QJSValueIterator::name() const QV4::Value *val = QJSValuePrivate::getValue(&d_ptr->value); if (!val || !val->isObject()) return QString(); - if (!!d_ptr->currentName) - return d_ptr->currentName->toQString(); + if (QV4::String *s = d_ptr->currentName.as<QV4::String>()) + return s->toQString(); if (d_ptr->currentIndex < UINT_MAX) return QString::number(d_ptr->currentIndex); return QString(); @@ -192,10 +191,10 @@ QJSValue QJSValueIterator::value() const if (!obj) return QJSValue(); - if (!d_ptr->currentName && d_ptr->currentIndex == UINT_MAX) + if (!d_ptr->currentName.as<QV4::String>() && d_ptr->currentIndex == UINT_MAX) return QJSValue(); - QV4::ScopedValue v(scope, obj->getValue(*obj, &d_ptr->currentProperty, d_ptr->currentAttributes)); + QV4::ScopedValue v(scope, d_ptr->currentIndex == UINT_MAX ? obj->get(d_ptr->currentName.as<QV4::String>()) : obj->getIndexed(d_ptr->currentIndex)); if (scope.hasException()) { engine->catchException(); return QJSValue(); @@ -214,8 +213,8 @@ QJSValueIterator& QJSValueIterator::operator=(QJSValue& object) d_ptr->value = object; d_ptr->currentIndex = UINT_MAX; d_ptr->nextIndex = UINT_MAX; - d_ptr->currentName = (QV4::String *)0; - d_ptr->nextName = (QV4::String *)0; + d_ptr->currentName.clear(); + d_ptr->nextName.clear(); QV4::ExecutionEngine *v4 = d_ptr->iterator.engine(); if (!v4) { d_ptr->iterator.clear(); @@ -228,8 +227,10 @@ QJSValueIterator& QJSValueIterator::operator=(QJSValue& object) QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value()); it->d()->it.flags = QV4::ObjectIterator::NoFlags; QV4::ScopedString nm(scope); - it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &d_ptr->nextProperty, &d_ptr->nextAttributes); - d_ptr->nextName = nm; + QV4::Property nextProperty; + QV4::PropertyAttributes nextAttributes; + it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes); + d_ptr->nextName.set(v4, nm.asReturnedValue()); return *this; } diff --git a/src/qml/jsapi/qjsvalueiterator_p.h b/src/qml/jsapi/qjsvalueiterator_p.h index c17fedf73e..dfc5e18cd6 100644 --- a/src/qml/jsapi/qjsvalueiterator_p.h +++ b/src/qml/jsapi/qjsvalueiterator_p.h @@ -34,6 +34,17 @@ #ifndef QJSVALUEITERATOR_P_H #define QJSVALUEITERATOR_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qjsvalue.h" #include "private/qv4objectiterator_p.h" @@ -48,15 +59,9 @@ public: QJSValue value; QV4::PersistentValue iterator; - // ### GC - QV4::Property currentProperty; - QV4::PropertyAttributes currentAttributes; - QV4::StringValue currentName; + QV4::PersistentValue currentName; uint currentIndex; - // ### GC - QV4::Property nextProperty; - QV4::PropertyAttributes nextAttributes; - QV4::StringValue nextName; + QV4::PersistentValue nextName; uint nextIndex; }; diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index ef44ca6f4d..5ffdebe328 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -10,7 +10,6 @@ SOURCES += \ $$PWD/qv4lookup.cpp \ $$PWD/qv4identifier.cpp \ $$PWD/qv4identifiertable.cpp \ - $$PWD/qv4mm.cpp \ $$PWD/qv4managed.cpp \ $$PWD/qv4internalclass.cpp \ $$PWD/qv4sparsearray.cpp \ @@ -40,7 +39,6 @@ SOURCES += \ $$PWD/qv4sequenceobject.cpp \ $$PWD/qv4include.cpp \ $$PWD/qv4qobjectwrapper.cpp \ - $$PWD/qv4qmlextensions.cpp \ $$PWD/qv4vme_moth.cpp \ $$PWD/qv4profiling.cpp \ $$PWD/qv4arraybuffer.cpp \ @@ -57,7 +55,6 @@ HEADERS += \ $$PWD/qv4lookup_p.h \ $$PWD/qv4identifier_p.h \ $$PWD/qv4identifiertable_p.h \ - $$PWD/qv4mm_p.h \ $$PWD/qv4managed_p.h \ $$PWD/qv4internalclass_p.h \ $$PWD/qv4sparsearray_p.h \ @@ -90,7 +87,6 @@ HEADERS += \ $$PWD/qv4sequenceobject_p.h \ $$PWD/qv4include_p.h \ $$PWD/qv4qobjectwrapper_p.h \ - $$PWD/qv4qmlextensions_p.h \ $$PWD/qv4vme_moth_p.h \ $$PWD/qv4profiling_p.h \ $$PWD/qv4arraybuffer_p.h \ @@ -102,7 +98,7 @@ HEADERS += \ HEADERS += \ $$PWD/qv4runtime_p.h \ - $$PWD/qv4value_inl_p.h \ + $$PWD/qv4value_p.h \ $$PWD/qv4string_p.h \ $$PWD/qv4value_p.h diff --git a/src/qml/jsruntime/qv4alloca_p.h b/src/qml/jsruntime/qv4alloca_p.h index a4537868e2..df40a018ba 100644 --- a/src/qml/jsruntime/qv4alloca_p.h +++ b/src/qml/jsruntime/qv4alloca_p.h @@ -34,6 +34,17 @@ #ifndef QV4_ALLOCA_H #define QV4_ALLOCA_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <qglobal.h> #if defined(Q_OS_WIN) diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 92c77570af..698b4c325c 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -33,18 +33,17 @@ #include <qv4argumentsobject_p.h> #include <qv4alloca_p.h> #include <qv4scopedvalue_p.h> +#include "qv4string_p.h" using namespace QV4; DEFINE_OBJECT_VTABLE(ArgumentsObject); Heap::ArgumentsObject::ArgumentsObject(QV4::CallContext *context) - : Heap::Object(context->d()->strictMode ? context->d()->engine->strictArgumentsObjectClass : context->d()->engine->argumentsObjectClass, - context->d()->engine->objectPrototype.asObject()) - , context(context->d()) + : context(context->d()) , fullyCreated(false) { - Q_ASSERT(vtable == QV4::ArgumentsObject::staticVTable()); + Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable()); ExecutionEngine *v4 = context->d()->engine; Scope scope(v4); @@ -53,22 +52,22 @@ Heap::ArgumentsObject::ArgumentsObject(QV4::CallContext *context) args->setArrayType(Heap::ArrayData::Complex); if (context->d()->strictMode) { - Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee)); - Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(context->d()->engine->id_caller)); - args->propertyAt(CalleePropertyIndex)->value = v4->thrower; - args->propertyAt(CalleePropertyIndex)->set = v4->thrower; - args->propertyAt(CallerPropertyIndex)->value = v4->thrower; - args->propertyAt(CallerPropertyIndex)->set = v4->thrower; + Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee())); + Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(context->d()->engine->id_caller())); + *args->propertyData(CalleePropertyIndex + QV4::Object::GetterOffset) = v4->thrower(); + *args->propertyData(CalleePropertyIndex + QV4::Object::SetterOffset) = v4->thrower(); + *args->propertyData(CallerPropertyIndex + QV4::Object::GetterOffset) = v4->thrower(); + *args->propertyData(CallerPropertyIndex + QV4::Object::SetterOffset) = v4->thrower(); args->arrayReserve(context->argc()); args->arrayPut(0, context->args(), context->argc()); args->d()->fullyCreated = true; } else { - Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee)); - args->memberData()->data[CalleePropertyIndex] = context->d()->function->asReturnedValue(); + Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee())); + *args->propertyData(CalleePropertyIndex) = context->d()->function->asReturnedValue(); } - Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(context->d()->engine->id_length)); - args->memberData()->data[LengthPropertyIndex] = Primitive::fromInt32(context->d()->callData->argc); + Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(context->d()->engine->id_length())); + *args->propertyData(LengthPropertyIndex) = Primitive::fromInt32(context->d()->callData->argc); } void ArgumentsObject::fullyCreate() @@ -76,17 +75,16 @@ void ArgumentsObject::fullyCreate() if (fullyCreated()) return; - uint numAccessors = qMin((int)context()->function->formalParameterCount(), context()->callData->argc); uint argCount = context()->callData->argc; + uint numAccessors = qMin(context()->function->formalParameterCount(), argCount); ArrayData::realloc(this, Heap::ArrayData::Sparse, argCount, true); context()->engine->requireArgumentsAccessors(numAccessors); Scope scope(engine()); Scoped<MemberData> md(scope, d()->mappedArguments); - if (!md || md->size() < numAccessors) - d()->mappedArguments = md->reallocate(engine(), d()->mappedArguments, numAccessors); - for (uint i = 0; i < (uint)numAccessors; ++i) { - mappedArguments()->data[i] = context()->callData->args[i]; + d()->mappedArguments = md->allocate(engine(), numAccessors); + for (uint i = 0; i < numAccessors; ++i) { + d()->mappedArguments->data[i] = context()->callData->args[i]; arraySet(i, context()->engine->argumentsAccessors + i, Attr_Accessor); } arrayPut(numAccessors, context()->callData->args + numAccessors, argCount - numAccessors); @@ -116,13 +114,13 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con map->copy(pd, mapAttrs); setArrayAttributes(index, Attr_Data); pd = arrayData()->getProperty(index); - pd->value = mappedArguments()->data[index]; + pd->value = d()->mappedArguments->data[index]; } - bool strict = engine->currentContext()->strictMode; - engine->currentContext()->strictMode = false; + bool strict = engine->current->strictMode; + engine->current->strictMode = false; bool result = Object::defineOwnProperty2(scope.engine, index, desc, attrs); - engine->currentContext()->strictMode = strict; + engine->current->strictMode = strict; if (isMapped && attrs.isData()) { Q_ASSERT(arrayData()); @@ -139,14 +137,14 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con } } - if (engine->currentContext()->strictMode && !result) + if (engine->current->strictMode && !result) return engine->throwTypeError(); return result; } -ReturnedValue ArgumentsObject::getIndexed(Managed *m, uint index, bool *hasProperty) +ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *hasProperty) { - ArgumentsObject *args = static_cast<ArgumentsObject *>(m); + const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); if (args->fullyCreated()) return Object::getIndexed(m, index, hasProperty); @@ -199,11 +197,11 @@ PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) DEFINE_OBJECT_VTABLE(ArgumentsGetterFunction); -ReturnedValue ArgumentsGetterFunction::call(Managed *getter, CallData *callData) +ReturnedValue ArgumentsGetterFunction::call(const Managed *getter, CallData *callData) { - ExecutionEngine *v4 = static_cast<ArgumentsGetterFunction *>(getter)->engine(); + ExecutionEngine *v4 = static_cast<const ArgumentsGetterFunction *>(getter)->engine(); Scope scope(v4); - Scoped<ArgumentsGetterFunction> g(scope, static_cast<ArgumentsGetterFunction *>(getter)); + Scoped<ArgumentsGetterFunction> g(scope, static_cast<const ArgumentsGetterFunction *>(getter)); Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>()); if (!o) return v4->throwTypeError(); @@ -214,11 +212,11 @@ ReturnedValue ArgumentsGetterFunction::call(Managed *getter, CallData *callData) DEFINE_OBJECT_VTABLE(ArgumentsSetterFunction); -ReturnedValue ArgumentsSetterFunction::call(Managed *setter, CallData *callData) +ReturnedValue ArgumentsSetterFunction::call(const Managed *setter, CallData *callData) { - ExecutionEngine *v4 = static_cast<ArgumentsSetterFunction *>(setter)->engine(); + ExecutionEngine *v4 = static_cast<const ArgumentsSetterFunction *>(setter)->engine(); Scope scope(v4); - Scoped<ArgumentsSetterFunction> s(scope, static_cast<ArgumentsSetterFunction *>(setter)); + Scoped<ArgumentsSetterFunction> s(scope, static_cast<const ArgumentsSetterFunction *>(setter)); Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>()); if (!o) return v4->throwTypeError(); diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index 43cd6d1dee..7a9c4b1d51 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -33,6 +33,17 @@ #ifndef QV4ARGUMENTSOBJECTS_H #define QV4ARGUMENTSOBJECTS_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" #include "qv4functionobject_p.h" @@ -59,9 +70,9 @@ struct ArgumentsObject : Object { CallerPropertyIndex = 3 }; ArgumentsObject(QV4::CallContext *context); - CallContext *context; + Pointer<CallContext> context; bool fullyCreated; - MemberData *mappedArguments; + Pointer<MemberData> mappedArguments; }; } @@ -71,7 +82,7 @@ struct ArgumentsGetterFunction: FunctionObject V4_OBJECT2(ArgumentsGetterFunction, FunctionObject) uint index() const { return d()->index; } - static ReturnedValue call(Managed *that, CallData *d); + static ReturnedValue call(const Managed *that, CallData *d); }; inline @@ -86,7 +97,7 @@ struct ArgumentsSetterFunction: FunctionObject V4_OBJECT2(ArgumentsSetterFunction, FunctionObject) uint index() const { return d()->index; } - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; inline @@ -103,15 +114,14 @@ struct ArgumentsObject: Object { Heap::CallContext *context() const { return d()->context; } bool fullyCreated() const { return d()->fullyCreated; } - Heap::MemberData *mappedArguments() { return d()->mappedArguments; } static bool isNonStrictArgumentsObject(Managed *m) { - return m->d()->vtable->type == Type_ArgumentsObject && + return m->d()->vtable()->type == Type_ArgumentsObject && !static_cast<ArgumentsObject *>(m)->context()->strictMode; } bool defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs); - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); static void putIndexed(Managed *m, uint index, const Value &value); static bool deleteIndexedProperty(Managed *m, uint index); static PropertyAttributes queryIndexed(const Managed *m, uint index); diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index dc65b5d21a..0a3aa414de 100644 --- a/src/qml/jsruntime/qv4arraybuffer.cpp +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -33,6 +33,7 @@ #include "qv4arraybuffer_p.h" #include "qv4typedarray_p.h" #include "qv4dataview_p.h" +#include "qv4string_p.h" using namespace QV4; @@ -44,9 +45,9 @@ Heap::ArrayBufferCtor::ArrayBufferCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ArrayBufferCtor::construct(Managed *m, CallData *callData) +ReturnedValue ArrayBufferCtor::construct(const Managed *m, CallData *callData) { - ExecutionEngine *v4 = static_cast<Object *>(m)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(m)->engine(); Scope scope(v4); ScopedValue l(scope, callData->argument(0)); @@ -57,14 +58,14 @@ ReturnedValue ArrayBufferCtor::construct(Managed *m, CallData *callData) if (len != dl) return v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length")); - Scoped<ArrayBuffer> a(scope, v4->memoryManager->alloc<ArrayBuffer>(v4, len)); + Scoped<ArrayBuffer> a(scope, v4->newArrayBuffer(len)); if (scope.engine->hasException) return Encode::undefined(); return a.asReturnedValue(); } -ReturnedValue ArrayBufferCtor::call(Managed *that, CallData *callData) +ReturnedValue ArrayBufferCtor::call(const Managed *that, CallData *callData) { return construct(that, callData); } @@ -82,22 +83,20 @@ ReturnedValue ArrayBufferCtor::method_isView(CallContext *ctx) } -Heap::ArrayBuffer::ArrayBuffer(ExecutionEngine *e, size_t length) - : Heap::Object(e->emptyClass, e->arrayBufferPrototype.asObject()) +Heap::ArrayBuffer::ArrayBuffer(size_t length) { data = QTypedArrayData<char>::allocate(length + 1); if (!data) { data = 0; - e->throwRangeError(QStringLiteral("ArrayBuffer: out of memory")); + internalClass->engine->throwRangeError(QStringLiteral("ArrayBuffer: out of memory")); return; } data->size = int(length); memset(data->data(), 0, length + 1); } -Heap::ArrayBuffer::ArrayBuffer(ExecutionEngine *e, const QByteArray& array) - : Heap::Object(e->emptyClass, e->arrayBufferPrototype.asObject()) - , data(const_cast<QByteArray&>(array).data_ptr()) +Heap::ArrayBuffer::ArrayBuffer(const QByteArray& array) + : data(const_cast<QByteArray&>(array).data_ptr()) { data->ref.ref(); } @@ -138,10 +137,10 @@ void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1)); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); ctor->defineDefaultProperty(QStringLiteral("isView"), ArrayBufferCtor::method_isView, 1); - defineDefaultProperty(engine->id_constructor, (o = ctor)); + defineDefaultProperty(engine->id_constructor(), (o = ctor)); defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, 0); defineDefaultProperty(QStringLiteral("slice"), method_slice, 2); } @@ -172,7 +171,7 @@ ReturnedValue ArrayBufferPrototype::method_slice(CallContext *ctx) double first = (start < 0) ? qMax(a->d()->data->size + start, 0.) : qMin(start, (double)a->d()->data->size); double final = (end < 0) ? qMax(a->d()->data->size + end, 0.) : qMin(end, (double)a->d()->data->size); - ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor)); + ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor())); if (!constructor) return scope.engine->throwTypeError(); diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h index fe3150618d..56f45b5a72 100644 --- a/src/qml/jsruntime/qv4arraybuffer_p.h +++ b/src/qml/jsruntime/qv4arraybuffer_p.h @@ -33,6 +33,17 @@ #ifndef QV4ARRAYBUFFER_H #define QV4ARRAYBUFFER_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" #include "qv4functionobject_p.h" @@ -47,8 +58,8 @@ struct ArrayBufferCtor : FunctionObject { }; struct Q_QML_PRIVATE_EXPORT ArrayBuffer : Object { - ArrayBuffer(ExecutionEngine *e, size_t length); - ArrayBuffer(ExecutionEngine *e, const QByteArray& array); + ArrayBuffer(size_t length); + ArrayBuffer(const QByteArray& array); ~ArrayBuffer(); QTypedArrayData<char> *data; @@ -61,8 +72,8 @@ struct ArrayBufferCtor: FunctionObject { V4_OBJECT2(ArrayBufferCtor, FunctionObject) - static ReturnedValue construct(Managed *m, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); static ReturnedValue method_isView(CallContext *ctx); @@ -72,6 +83,7 @@ struct Q_QML_PRIVATE_EXPORT ArrayBuffer : Object { V4_OBJECT2(ArrayBuffer, Object) V4_NEEDS_DESTROY + V4_PROTOTYPE(arrayBufferPrototype) QByteArray asByteArray() const; uint byteLength() const { return d()->byteLength(); } diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index afcfa00905..ec0185de64 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -33,13 +33,14 @@ #include "qv4arraydata_p.h" #include "qv4object_p.h" #include "qv4functionobject_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4runtime_p.h" #include "qv4argumentsobject_p.h" +#include "qv4string_p.h" using namespace QV4; -const QV4::ManagedVTable QV4::ArrayData::static_vtbl = { +const QV4::VTable QV4::ArrayData::static_vtbl = { 0, QV4::ArrayData::IsExecutionContext, QV4::ArrayData::IsString, @@ -90,6 +91,13 @@ const ArrayVTable SparseArrayData::static_vtbl = Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SimpleArrayData)); Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SparseArrayData)); +static Q_ALWAYS_INLINE void storeValue(ReturnedValue *target, uint value) +{ + Value v = Value::fromReturnedValue(*target); + v.setValue(value); + *target = v.asReturnedValue(); +} + void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAttributes) { Scope scope(o->engine()); @@ -165,7 +173,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt Heap::SparseArrayData *sparse = static_cast<Heap::SparseArrayData *>(newData->d()); - uint *lastFree; + ReturnedValue *lastFree; if (d && d->type() == Heap::ArrayData::Sparse) { Heap::SparseArrayData *old = static_cast<Heap::SparseArrayData *>(d->d()); sparse->sparse = old->sparse; @@ -180,20 +188,20 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt SparseArrayNode *n = sparse->sparse->insert(i); n->value = i; } else { - *lastFree = i; - sparse->arrayData[i].tag = Value::Empty_Type; - lastFree = &sparse->arrayData[i].uint_32; + storeValue(lastFree, i); + sparse->arrayData[i].setTag(Value::Empty_Type); + lastFree = &sparse->arrayData[i].rawValueRef(); } } } if (toCopy < sparse->alloc) { for (uint i = toCopy; i < sparse->alloc; ++i) { - *lastFree = i; - sparse->arrayData[i].tag = Value::Empty_Type; - lastFree = &sparse->arrayData[i].uint_32; + storeValue(lastFree, i); + sparse->arrayData[i].setTag(Value::Empty_Type); + lastFree = &sparse->arrayData[i].rawValueRef(); } - *lastFree = UINT_MAX; + storeValue(lastFree, UINT_MAX); } // ### Could explicitly free the old data } @@ -230,7 +238,7 @@ ReturnedValue SimpleArrayData::get(const Heap::ArrayData *d, uint index) bool SimpleArrayData::put(Object *o, uint index, const Value &value) { - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); Q_ASSERT(index >= dd->len || !dd->attrs || !dd->attrs[index].isAccessor()); // ### honour attributes dd->data(index) = value; @@ -244,7 +252,7 @@ bool SimpleArrayData::put(Object *o, uint index, const Value &value) bool SimpleArrayData::del(Object *o, uint index) { - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (index >= dd->len) return true; @@ -266,12 +274,12 @@ void SimpleArrayData::setAttribute(Object *o, uint index, PropertyAttributes att void SimpleArrayData::push_front(Object *o, const Value *values, uint n) { - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); Q_ASSERT(!dd->attrs); if (dd->len + n > dd->alloc) { realloc(o, Heap::ArrayData::Simple, dd->len + n, false); Q_ASSERT(o->d()->arrayData->type == Heap::ArrayData::Simple); - dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } dd->offset = (dd->offset - n) % dd->alloc; dd->len += n; @@ -281,7 +289,7 @@ void SimpleArrayData::push_front(Object *o, const Value *values, uint n) ReturnedValue SimpleArrayData::pop_front(Object *o) { - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); Q_ASSERT(!dd->attrs); if (!dd->len) return Encode::undefined(); @@ -294,7 +302,7 @@ ReturnedValue SimpleArrayData::pop_front(Object *o) uint SimpleArrayData::truncate(Object *o, uint newLen) { - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (dd->len < newLen) return newLen; @@ -318,10 +326,10 @@ uint SimpleArrayData::length(const Heap::ArrayData *d) bool SimpleArrayData::putArray(Object *o, uint index, const Value *values, uint n) { - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (index + n > dd->alloc) { reallocate(o, index + n + 1, false); - dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } for (uint i = dd->len; i < index; ++i) dd->data(i) = Primitive::emptyValue(); @@ -337,13 +345,10 @@ void SparseArrayData::free(Heap::ArrayData *d, uint idx) Value *v = d->arrayData + idx; if (d->attrs && d->attrs[idx].isAccessor()) { // double slot, free both. Order is important, so we have a double slot for allocation again afterwards. - v[1].tag = Value::Empty_Type; - v[1].uint_32 = d->freeList; - v[0].tag = Value::Empty_Type; - v[0].uint_32 = idx + 1; + v[1].setTagValue(Value::Empty_Type, Value::fromReturnedValue(d->freeList).value()); + v[0].setTagValue(Value::Empty_Type, idx + 1); } else { - v->tag = Value::Empty_Type; - v->uint_32 = d->freeList; + v->setTagValue(Value::Empty_Type, Value::fromReturnedValue(d->freeList).value()); } d->freeList = idx; if (d->attrs) @@ -369,35 +374,37 @@ Heap::ArrayData *SparseArrayData::reallocate(Object *o, uint n, bool enforceAttr uint SparseArrayData::allocate(Object *o, bool doubleSlot) { Q_ASSERT(o->d()->arrayData->type == Heap::ArrayData::Sparse); - Heap::SparseArrayData *dd = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (doubleSlot) { - uint *last = &dd->freeList; + ReturnedValue *last = &dd->freeList; while (1) { - if (*last == UINT_MAX) { + if (Value::fromReturnedValue(*last).value() == UINT_MAX) { reallocate(o, dd->alloc + 2, true); - dd = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); last = &dd->freeList; - Q_ASSERT(*last != UINT_MAX); + Q_ASSERT(Value::fromReturnedValue(*last).value() != UINT_MAX); } - Q_ASSERT(dd->arrayData[*last].uint_32 != *last); - if (dd->arrayData[*last].uint_32 == (*last + 1)) { + Q_ASSERT(dd->arrayData[Value::fromReturnedValue(*last).value()].value() != Value::fromReturnedValue(*last).value()); + if (dd->arrayData[Value::fromReturnedValue(*last).value()].value() == (Value::fromReturnedValue(*last).value() + 1)) { // found two slots in a row - uint idx = *last; - *last = dd->arrayData[*last + 1].uint_32; + uint idx = Value::fromReturnedValue(*last).uint_32(); + Value lastV = Value::fromReturnedValue(*last); + lastV.setValue(dd->arrayData[lastV.value() + 1].value()); + *last = lastV.rawValue(); dd->attrs[idx] = Attr_Accessor; return idx; } - last = &dd->arrayData[*last].uint_32; + last = &dd->arrayData[Value::fromReturnedValue(*last).value()].rawValueRef(); } } else { - if (dd->freeList == UINT_MAX) { + if (Value::fromReturnedValue(dd->freeList).value() == UINT_MAX) { reallocate(o, dd->alloc + 1, false); - dd = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } - uint idx = dd->freeList; + uint idx = Value::fromReturnedValue(dd->freeList).value(); Q_ASSERT(idx != UINT_MAX); - dd->freeList = dd->arrayData[idx].uint_32; + dd->freeList = dd->arrayData[idx].uint_32(); if (dd->attrs) dd->attrs[idx] = Attr_Data; return idx; @@ -418,12 +425,12 @@ bool SparseArrayData::put(Object *o, uint index, const Value &value) if (value.isEmpty()) return true; - Heap::SparseArrayData *s = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *s = o->d()->arrayData.cast<Heap::SparseArrayData>(); SparseArrayNode *n = s->sparse->insert(index); Q_ASSERT(n->value == UINT_MAX || !s->attrs || !s->attrs[n->value].isAccessor()); if (n->value == UINT_MAX) n->value = allocate(o); - s = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + s = o->d()->arrayData.cast<Heap::SparseArrayData>(); s->arrayData[n->value] = value; if (s->attrs) s->attrs[n->value] = Attr_Data; @@ -432,7 +439,7 @@ bool SparseArrayData::put(Object *o, uint index, const Value &value) bool SparseArrayData::del(Object *o, uint index) { - Heap::SparseArrayData *dd = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *dd = o->d()->arrayData.cast<Heap::SparseArrayData>(); SparseArrayNode *n = dd->sparse->findNode(index); if (!n) @@ -452,13 +459,10 @@ bool SparseArrayData::del(Object *o, uint index) if (isAccessor) { // free up both indices - dd->arrayData[pidx + 1].tag = Value::Empty_Type; - dd->arrayData[pidx + 1].uint_32 = dd->freeList; - dd->arrayData[pidx].tag = Value::Undefined_Type; - dd->arrayData[pidx].uint_32 = pidx + 1; + dd->arrayData[pidx + 1].setTagValue(Value::Empty_Type, Value::fromReturnedValue(dd->freeList).value()); + dd->arrayData[pidx].setTagValue(Value::Undefined_Type, pidx + 1); } else { - dd->arrayData[pidx].tag = Value::Empty_Type; - dd->arrayData[pidx].uint_32 = dd->freeList; + dd->arrayData[pidx].setTagValue(Value::Empty_Type, Value::fromReturnedValue(dd->freeList).value()); } dd->freeList = pidx; @@ -468,28 +472,28 @@ bool SparseArrayData::del(Object *o, uint index) void SparseArrayData::setAttribute(Object *o, uint index, PropertyAttributes attrs) { - Heap::SparseArrayData *d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *d = o->d()->arrayData.cast<Heap::SparseArrayData>(); SparseArrayNode *n = d->sparse->insert(index); if (n->value == UINT_MAX) { n->value = allocate(o, attrs.isAccessor()); - d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + d = o->d()->arrayData.cast<Heap::SparseArrayData>(); } else if (attrs.isAccessor() != d->attrs[n->value].isAccessor()) { // need to convert the slot free(o->arrayData(), n->value); n->value = allocate(o, attrs.isAccessor()); - d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + d = o->d()->arrayData.cast<Heap::SparseArrayData>(); } d->attrs[n->value] = attrs; } void SparseArrayData::push_front(Object *o, const Value *values, uint n) { - Heap::SparseArrayData *d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *d = o->d()->arrayData.cast<Heap::SparseArrayData>(); Q_ASSERT(!d->attrs); for (int i = n - 1; i >= 0; --i) { uint idx = allocate(o); - d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + d = o->d()->arrayData.cast<Heap::SparseArrayData>(); d->arrayData[idx] = values[i]; d->sparse->push_front(idx); } @@ -497,7 +501,7 @@ void SparseArrayData::push_front(Object *o, const Value *values, uint n) ReturnedValue SparseArrayData::pop_front(Object *o) { - Heap::SparseArrayData *d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *d = o->d()->arrayData.cast<Heap::SparseArrayData>(); Q_ASSERT(!d->attrs); uint idx = d->sparse->pop_front(); ReturnedValue v; @@ -512,7 +516,7 @@ ReturnedValue SparseArrayData::pop_front(Object *o) uint SparseArrayData::truncate(Object *o, uint newLen) { - Heap::SparseArrayData *d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *d = o->d()->arrayData.cast<Heap::SparseArrayData>(); SparseArrayNode *begin = d->sparse->lowerBound(newLen); if (begin != d->sparse->end()) { SparseArrayNode *it = d->sparse->end()->previousNode(); @@ -580,7 +584,7 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n) ScopedValue v(scope); for (const SparseArrayNode *it = os->sparse->begin(); it != os->sparse->end(); it = it->nextNode()) { - v = otherObj->getValue(reinterpret_cast<Property *>(os->arrayData + it->value), other->d()->attrs[it->value]); + v = otherObj->getValue(os->arrayData[it->value], other->d()->attrs[it->value]); obj->arraySet(oldSize + it->key(), v); } } else { @@ -603,14 +607,14 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n) return oldSize + n; } -Property *ArrayData::insert(Object *o, uint index, bool isAccessor) +void ArrayData::insert(Object *o, uint index, const Value *v, bool isAccessor) { if (!isAccessor && o->d()->arrayData->type != Heap::ArrayData::Sparse) { - Heap::SimpleArrayData *d = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *d = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (index < 0x1000 || index < d->len + (d->len >> 2)) { if (index >= d->alloc) { o->arrayReserve(index + 1); - d = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + d = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } if (index >= d->len) { // mark possible hole in the array @@ -618,17 +622,20 @@ Property *ArrayData::insert(Object *o, uint index, bool isAccessor) d->data(i) = Primitive::emptyValue(); d->len = index + 1; } - return reinterpret_cast<Property *>(d->arrayData + d->mappedIndex(index)); + d->arrayData[d->mappedIndex(index)] = *v; + return; } } o->initSparseArray(); - Heap::SparseArrayData *s = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *s = o->d()->arrayData.cast<Heap::SparseArrayData>(); SparseArrayNode *n = s->sparse->insert(index); if (n->value == UINT_MAX) n->value = SparseArrayData::allocate(o, isAccessor); - s = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); - return reinterpret_cast<Property *>(s->arrayData + n->value); + s = o->d()->arrayData.cast<Heap::SparseArrayData>(); + s->arrayData[n->value] = *v; + if (isAccessor) + s->arrayData[n->value + Object::SetterOffset] = v[Object::SetterOffset]; } @@ -737,7 +744,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c if (!arrayData || !arrayData->length()) return; - if (!(comparefn.isUndefined() || comparefn.asObject())) { + if (!(comparefn.isUndefined() || comparefn.as<Object>())) { engine->throwTypeError(); return; } @@ -755,7 +762,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c thisObject->setArrayData(0); ArrayData::realloc(thisObject, Heap::ArrayData::Simple, sparse->sparse()->nEntries(), sparse->attrs() ? true : false); - Heap::SimpleArrayData *d = static_cast<Heap::SimpleArrayData *>(thisObject->d()->arrayData); + Heap::SimpleArrayData *d = thisObject->d()->arrayData.cast<Heap::SimpleArrayData>(); SparseArrayNode *n = sparse->sparse()->begin(); uint i = 0; @@ -765,7 +772,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c break; PropertyAttributes a = sparse->attrs() ? sparse->attrs()[n->value] : Attr_Data; - d->data(i) = thisObject->getValue(reinterpret_cast<Property *>(sparse->arrayData() + n->value), a); + d->data(i) = thisObject->getValue(sparse->arrayData()[n->value], a); d->attrs[i] = a.isAccessor() ? Attr_Data : a; n = n->nextNode(); @@ -795,7 +802,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c } } else { - Heap::SimpleArrayData *d = static_cast<Heap::SimpleArrayData *>(thisObject->d()->arrayData); + Heap::SimpleArrayData *d = thisObject->d()->arrayData.cast<Heap::SimpleArrayData>(); if (len > d->len) len = d->len; diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index 915e862bbb..48d2b9dbbf 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -33,6 +33,17 @@ #ifndef QV4ARRAYDATA_H #define QV4ARRAYDATA_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include "qv4managed_p.h" #include "qv4property_p.h" @@ -47,17 +58,17 @@ namespace QV4 { Q_MANAGED_CHECK \ typedef QV4::Heap::DataClass Data; \ static const QV4::ArrayVTable static_vtbl; \ - static inline const QV4::ManagedVTable *staticVTable() { return &static_vtbl.managedVTable; } \ + static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \ V4_MANAGED_SIZE_TEST \ - const Data *d() const { return static_cast<const Data *>(m); } \ - Data *d() { return static_cast<Data *>(m); } + const Data *d() const { return static_cast<const Data *>(m()); } \ + Data *d() { return static_cast<Data *>(m()); } struct ArrayData; struct ArrayVTable { - ManagedVTable managedVTable; + VTable vTable; uint type; Heap::ArrayData *(*reallocate)(Object *o, uint n, bool enforceAttributes); ReturnedValue (*get)(const Heap::ArrayData *d, uint index); @@ -86,7 +97,7 @@ struct ArrayData : public Base { PropertyAttributes *attrs; union { uint len; - uint freeList; + ReturnedValue freeList; }; union { uint offset; @@ -96,12 +107,15 @@ struct ArrayData : public Base { bool isSparse() const { return type == Sparse; } - const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(Base::vtable); } + const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(Base::vtable()); } inline ReturnedValue get(uint i) const { return vtable()->get(this, i); } + inline void getProperty(uint index, Property *p, PropertyAttributes *attrs); + inline void setProperty(uint index, const Property *p); inline Property *getProperty(uint index); + inline Value *getValueOrSetter(uint index, PropertyAttributes *attrs); inline PropertyAttributes attributes(uint i) const; bool isEmpty(uint i) const { @@ -205,7 +219,7 @@ struct Q_QML_EXPORT ArrayData : public Managed static void sort(ExecutionEngine *engine, Object *thisObject, const Value &comparefn, uint dataLen); static uint append(Object *obj, ArrayObject *otherObj, uint n); - static Property *insert(Object *o, uint index, bool isAccessor = false); + static void insert(Object *o, uint index, const Value *v, bool isAccessor = false); }; struct Q_QML_EXPORT SimpleArrayData : public ArrayData @@ -239,8 +253,8 @@ struct Q_QML_EXPORT SparseArrayData : public ArrayData V4_ARRAYDATA(SparseArrayData) V4_NEEDS_DESTROY - uint &freeList() { return d()->freeList; } - uint freeList() const { return d()->freeList; } + ReturnedValue &freeList() { return d()->freeList; } + ReturnedValue freeList() const { return d()->freeList; } SparseArray *sparse() const { return d()->sparse; } void setSparse(SparseArray *s) { d()->sparse = s; } @@ -270,6 +284,25 @@ inline SparseArrayData::~SparseArrayData() delete sparse; } +void ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs) +{ + Property *pd = getProperty(index); + Q_ASSERT(pd); + *attrs = attributes(index); + p->value = pd->value; + if (attrs->isAccessor()) + p->set = pd->set; +} + +void ArrayData::setProperty(uint index, const Property *p) +{ + Property *pd = getProperty(index); + Q_ASSERT(pd); + pd->value = p->value; + if (attributes(index).isAccessor()) + pd->set = p->set; +} + inline Property *ArrayData::getProperty(uint index) { if (isSparse()) @@ -284,6 +317,19 @@ inline PropertyAttributes ArrayData::attributes(uint i) const return static_cast<const SimpleArrayData *>(this)->attributes(i); } +Value *ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs) +{ + Property *p = getProperty(index); + if (!p) { + *attrs = Attr_Invalid; + return 0; + } + + *attrs = attributes(index); + return attrs->isAccessor() ? &p->set : &p->value; +} + + } diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 231eb93dd5..25d3d9329b 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -37,6 +37,7 @@ #include "qv4scopedvalue_p.h" #include "qv4argumentsobject_p.h" #include "qv4runtime_p.h" +#include "qv4string_p.h" using namespace QV4; @@ -47,9 +48,9 @@ Heap::ArrayCtor::ArrayCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ArrayCtor::construct(Managed *m, CallData *callData) +ReturnedValue ArrayCtor::construct(const Managed *m, CallData *callData) { - ExecutionEngine *v4 = static_cast<ArrayCtor *>(m)->engine(); + ExecutionEngine *v4 = static_cast<const ArrayCtor *>(m)->engine(); Scope scope(v4); ScopedArrayObject a(scope, v4->newArrayObject()); uint len; @@ -72,7 +73,7 @@ ReturnedValue ArrayCtor::construct(Managed *m, CallData *callData) return a.asReturnedValue(); } -ReturnedValue ArrayCtor::call(Managed *that, CallData *callData) +ReturnedValue ArrayCtor::call(const Managed *that, CallData *callData) { return construct(that, callData); } @@ -81,11 +82,11 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1)); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); ctor->defineDefaultProperty(QStringLiteral("isArray"), method_isArray, 1); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString, method_toString, 0); + defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0); defineDefaultProperty(QStringLiteral("concat"), method_concat, 1); defineDefaultProperty(QStringLiteral("join"), method_join, 1); @@ -110,7 +111,7 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) ReturnedValue ArrayPrototype::method_isArray(CallContext *ctx) { - bool isArray = ctx->argc() && ctx->args()[0].asArrayObject(); + bool isArray = ctx->argc() && ctx->args()[0].as<ArrayObject>(); return Encode(isArray); } @@ -185,7 +186,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) r4 = arg->toQString(); ScopedObject self(scope, ctx->thisObject()); - ScopedValue length(scope, self->get(ctx->d()->engine->id_length)); + ScopedValue length(scope, self->get(ctx->d()->engine->id_length())); const quint32 r2 = length->isUndefined() ? 0 : length->toUInt32(); if (!r2) @@ -194,7 +195,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) QString R; // ### FIXME - if (ArrayObject *a = self->asArrayObject()) { + if (ArrayObject *a = self->as<ArrayObject>()) { ScopedValue e(scope); for (uint i = 0; i < a->getLength(); ++i) { if (i) @@ -242,7 +243,7 @@ ReturnedValue ArrayPrototype::method_pop(CallContext *ctx) if (!len) { if (!instance->isArrayObject()) - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromInt32(0))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0))); return Encode::undefined(); } @@ -256,7 +257,7 @@ ReturnedValue ArrayPrototype::method_pop(CallContext *ctx) if (instance->isArrayObject()) instance->setArrayLength(len - 1); else - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromDouble(len - 1))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); return result->asReturnedValue(); } @@ -282,7 +283,7 @@ ReturnedValue ArrayPrototype::method_push(CallContext *ctx) } double newLen = l + ctx->argc(); if (!instance->isArrayObject()) - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromDouble(newLen))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))); else { ScopedString str(scope, ctx->d()->engine->newString(QStringLiteral("Array.prototype.push: Overflow"))); return ctx->engine()->throwRangeError(str); @@ -303,7 +304,7 @@ ReturnedValue ArrayPrototype::method_push(CallContext *ctx) if (instance->isArrayObject()) instance->setArrayLengthUnchecked(len); else - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromDouble(len))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len))); return Encode(len); } @@ -354,7 +355,7 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx) if (!len) { if (!instance->isArrayObject()) - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromInt32(0))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0))); return Encode::undefined(); } @@ -388,7 +389,7 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx) if (instance->isArrayObject()) instance->setArrayLengthUnchecked(len - 1); else - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromDouble(len - 1))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); return result->asReturnedValue(); } @@ -523,7 +524,7 @@ ReturnedValue ArrayPrototype::method_splice(CallContext *ctx) } ctx->d()->strictMode = true; - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount))); return newArray.asReturnedValue(); } @@ -561,7 +562,7 @@ ReturnedValue ArrayPrototype::method_unshift(CallContext *ctx) if (instance->isArrayObject()) instance->setArrayLengthUnchecked(newLen); else - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromDouble(newLen))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))); return Encode(newLen); } @@ -619,7 +620,7 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx) return Encode(-1); } else { Q_ASSERT(instance->arrayType() == Heap::ArrayData::Simple || instance->arrayType() == Heap::ArrayData::Complex); - Heap::SimpleArrayData *sa = static_cast<Heap::SimpleArrayData *>(instance->d()->arrayData); + Heap::SimpleArrayData *sa = instance->d()->arrayData.cast<Heap::SimpleArrayData>(); if (len > sa->len) len = sa->len; uint idx = fromIndex; diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index 4e67eb2e31..afd8080fa3 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -33,6 +33,17 @@ #ifndef QV4ARRAYOBJECT_H #define QV4ARRAYOBJECT_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" #include "qv4functionobject_p.h" #include <QtCore/qnumeric.h> @@ -53,8 +64,8 @@ struct ArrayCtor: FunctionObject { V4_OBJECT2(ArrayCtor, FunctionObject) - static ReturnedValue construct(Managed *m, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct ArrayPrototype: ArrayObject diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index 9c293e783b..53f8abf3f2 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -32,6 +32,7 @@ ****************************************************************************/ #include "qv4booleanobject_p.h" +#include "qv4string_p.h" using namespace QV4; @@ -43,14 +44,14 @@ Heap::BooleanCtor::BooleanCtor(QV4::ExecutionContext *scope) { } -ReturnedValue BooleanCtor::construct(Managed *m, CallData *callData) +ReturnedValue BooleanCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<BooleanCtor *>(m)->engine()); + Scope scope(static_cast<const BooleanCtor *>(m)->engine()); bool n = callData->argc ? callData->args[0].toBoolean() : false; return Encode(scope.engine->newBooleanObject(n)); } -ReturnedValue BooleanCtor::call(Managed *, CallData *callData) +ReturnedValue BooleanCtor::call(const Managed *, CallData *callData) { bool value = callData->argc ? callData->args[0].toBoolean() : 0; return Encode(value); @@ -60,11 +61,11 @@ void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1)); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString, method_toString); - defineDefaultProperty(engine->id_valueOf, method_valueOf); + defineDefaultProperty(engine->id_toString(), method_toString); + defineDefaultProperty(engine->id_valueOf(), method_valueOf); } ReturnedValue BooleanPrototype::method_toString(CallContext *ctx) @@ -73,7 +74,7 @@ ReturnedValue BooleanPrototype::method_toString(CallContext *ctx) if (ctx->thisObject().isBoolean()) { result = ctx->thisObject().booleanValue(); } else { - BooleanObject *thisObject = ctx->thisObject().as<BooleanObject>(); + const BooleanObject *thisObject = ctx->thisObject().as<BooleanObject>(); if (!thisObject) return ctx->engine()->throwTypeError(); result = thisObject->value(); @@ -87,7 +88,7 @@ ReturnedValue BooleanPrototype::method_valueOf(CallContext *ctx) if (ctx->thisObject().isBoolean()) return ctx->thisObject().asReturnedValue(); - BooleanObject *thisObject = ctx->thisObject().as<BooleanObject>(); + const BooleanObject *thisObject = ctx->thisObject().as<BooleanObject>(); if (!thisObject) return ctx->engine()->throwTypeError(); diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h index 903261bdce..203c8ba4ea 100644 --- a/src/qml/jsruntime/qv4booleanobject_p.h +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -33,6 +33,17 @@ #ifndef QV4BOOLEANOBJECT_H #define QV4BOOLEANOBJECT_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" #include "qv4functionobject_p.h" #include <QtCore/qnumeric.h> @@ -53,8 +64,8 @@ struct BooleanCtor: FunctionObject { V4_OBJECT2(BooleanCtor, FunctionObject) - static ReturnedValue construct(Managed *, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct BooleanPrototype: BooleanObject diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 9330f10780..007bf92639 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -36,10 +36,12 @@ #include <qv4context_p.h> #include <qv4object_p.h> #include <qv4objectproto_p.h> -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include <qv4argumentsobject_p.h> #include "qv4function_p.h" #include "qv4errorobject_p.h" +#include "qv4string_p.h" +#include "private/qqmlcontextwrapper_p.h" using namespace QV4; @@ -48,8 +50,9 @@ DEFINE_MANAGED_VTABLE(CallContext); DEFINE_MANAGED_VTABLE(WithContext); DEFINE_MANAGED_VTABLE(CatchContext); DEFINE_MANAGED_VTABLE(GlobalContext); +DEFINE_MANAGED_VTABLE(QmlContext); -Heap::CallContext *ExecutionContext::newCallContext(FunctionObject *function, CallData *callData) +Heap::CallContext *ExecutionContext::newCallContext(const FunctionObject *function, CallData *callData) { Q_ASSERT(function->function()); @@ -80,39 +83,64 @@ Heap::CallContext *ExecutionContext::newCallContext(FunctionObject *function, Ca return c; } -Heap::WithContext *ExecutionContext::newWithContext(Object *with) +Heap::WithContext *ExecutionContext::newWithContext(Heap::Object *with) { - return d()->engine->memoryManager->alloc<WithContext>(d()->engine, with); + return d()->engine->memoryManager->alloc<WithContext>(d(), with); } -Heap::CatchContext *ExecutionContext::newCatchContext(String *exceptionVarName, const Value &exceptionValue) +Heap::CatchContext *ExecutionContext::newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue) { - return d()->engine->memoryManager->alloc<CatchContext>(d()->engine, exceptionVarName, exceptionValue); + Scope scope(this); + ScopedValue e(scope, exceptionValue); + return d()->engine->memoryManager->alloc<CatchContext>(d(), exceptionVarName, e); } -Heap::CallContext *ExecutionContext::newQmlContext(FunctionObject *f, Object *qml) +Heap::QmlContext *ExecutionContext::newQmlContext(QmlContextWrapper *qml) { - Scope scope(this); - Scoped<CallContext> c(scope, d()->engine->memoryManager->allocManaged<CallContext>(requiredMemoryForExecutionContect(f, 0))); - new (c->d()) Heap::CallContext(d()->engine, qml, f); - return c->d(); + Heap::QmlContext *c = d()->engine->memoryManager->alloc<QmlContext>(this, qml); + return c; } - +Heap::QmlContext *ExecutionContext::newQmlContext(QQmlContextData *context, QObject *scopeObject) +{ + Scope scope(this); + Scoped<QmlContextWrapper> qml(scope, QmlContextWrapper::qmlScope(scope.engine, context, scopeObject)); + Heap::QmlContext *c = d()->engine->memoryManager->alloc<QmlContext>(this, qml); + return c; +} void ExecutionContext::createMutableBinding(String *name, bool deletable) { Scope scope(this); // find the right context to create the binding on - ScopedObject activation(scope, d()->engine->globalObject()); + ScopedObject activation(scope); ScopedContext ctx(scope, this); while (ctx) { - if (ctx->d()->type >= Heap::ExecutionContext::Type_CallContext) { + switch (ctx->d()->type) { + case Heap::ExecutionContext::Type_CallContext: + case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); - if (!c->activation) - c->activation = scope.engine->newObject(); - activation = c->activation; + if (!activation) { + if (!c->activation) + c->activation = scope.engine->newObject(); + activation = c->activation; + } + break; + } + case Heap::ExecutionContext::Type_QmlContext: { + // this is ugly, as it overrides the inner callcontext, but has to stay as long + // as bindings still get their own callcontext + Heap::QmlContext *qml = static_cast<Heap::QmlContext *>(ctx->d()); + activation = qml->qml; + break; + } + case Heap::ExecutionContext::Type_GlobalContext: { + if (!activation) + activation = scope.engine->globalObject; + break; + } + default: break; } ctx = ctx->d()->outer; @@ -130,57 +158,46 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) Heap::GlobalContext::GlobalContext(ExecutionEngine *eng) : Heap::ExecutionContext(eng, Heap::ExecutionContext::Type_GlobalContext) { - global = eng->globalObject()->d(); + global = eng->globalObject->d(); } -Heap::WithContext::WithContext(ExecutionEngine *engine, QV4::Object *with) - : Heap::ExecutionContext(engine, Heap::ExecutionContext::Type_WithContext) +Heap::WithContext::WithContext(ExecutionContext *outerContext, Object *with) + : Heap::ExecutionContext(outerContext->engine, Heap::ExecutionContext::Type_WithContext) { - callData = parent->callData; - outer = parent; - lookups = parent->lookups; - compilationUnit = parent->compilationUnit; + outer = outerContext; + callData = outer->callData; + lookups = outer->lookups; + compilationUnit = outer->compilationUnit; - withObject = with ? with->d() : 0; + withObject = with; } -Heap::CatchContext::CatchContext(ExecutionEngine *engine, QV4::String *exceptionVarName, const Value &exceptionValue) - : Heap::ExecutionContext(engine, Heap::ExecutionContext::Type_CatchContext) +Heap::CatchContext::CatchContext(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue) + : Heap::ExecutionContext(outerContext->engine, Heap::ExecutionContext::Type_CatchContext) { - strictMode = parent->strictMode; - callData = parent->callData; - outer = parent; - lookups = parent->lookups; - compilationUnit = parent->compilationUnit; + outer = outerContext; + strictMode = outer->strictMode; + callData = outer->callData; + lookups = outer->lookups; + compilationUnit = outer->compilationUnit; this->exceptionVarName = exceptionVarName; this->exceptionValue = exceptionValue; } -Heap::CallContext::CallContext(ExecutionEngine *engine, QV4::Object *qml, QV4::FunctionObject *function) - : Heap::ExecutionContext(engine, Heap::ExecutionContext::Type_QmlContext) +Heap::QmlContext::QmlContext(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml) + : Heap::ExecutionContext(outerContext->engine(), Heap::ExecutionContext::Type_QmlContext) { - this->function = function->d(); - callData = reinterpret_cast<CallData *>(this + 1); - callData->tag = QV4::Value::_Integer_Type; - callData->argc = 0; - callData->thisObject = Primitive::undefinedValue(); - + outer = outerContext->d(); strictMode = false; - outer = function->scope(); + callData = outer->callData; + lookups = outer->lookups; + compilationUnit = outer->compilationUnit; - activation = qml->d(); - - if (function->function()) { - compilationUnit = function->function()->compilationUnit; - lookups = compilationUnit->runtimeLookups; - } - - locals = (Value *)(this + 1); - if (function->varCount()) - std::fill(locals, locals + function->varCount(), Primitive::undefinedValue()); + this->qml = qml->d(); } + Identifier * const *CallContext::formals() const { return (d()->function && d()->function->function) ? d()->function->function->internalClass->nameMap.constData() : 0; @@ -209,16 +226,28 @@ bool ExecutionContext::deleteProperty(String *name) bool hasWith = false; ScopedContext ctx(scope, this); for (; ctx; ctx = ctx->d()->outer) { - if (ctx->d()->type == Heap::ExecutionContext::Type_WithContext) { + switch (ctx->d()->type) { + case Heap::ExecutionContext::Type_CatchContext: { + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); + if (c->exceptionVarName->isEqualTo(name->d())) + return false; + break; + } + case Heap::ExecutionContext::Type_WithContext: { hasWith = true; ScopedObject withObject(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); if (withObject->hasProperty(name)) return withObject->deleteProperty(name); - } else if (ctx->d()->type == Heap::ExecutionContext::Type_CatchContext) { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); - if (c->exceptionVarName->isEqualTo(name)) - return false; - } else if (ctx->d()->type >= Heap::ExecutionContext::Type_CallContext) { + break; + } + case Heap::ExecutionContext::Type_GlobalContext: { + ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); + if (global->hasProperty(name)) + return global->deleteProperty(name); + break; + } + case Heap::ExecutionContext::Type_CallContext: + case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); ScopedFunctionObject f(scope, c->function); if (f->needsActivation() || hasWith) { @@ -227,13 +256,14 @@ bool ExecutionContext::deleteProperty(String *name) // ### throw in strict mode? return false; } - ScopedObject activation(scope, c->activation); - if (activation && activation->hasProperty(name)) - return activation->deleteProperty(name); - } else if (ctx->d()->type == Heap::ExecutionContext::Type_GlobalContext) { - ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); - if (global->hasProperty(name)) - return global->deleteProperty(name); + ScopedObject qml(scope, c->activation); + if (qml && qml->hasProperty(name)) + return qml->deleteProperty(name); + break; + } + case Heap::ExecutionContext::Type_QmlContext: + // can't delete properties on qml objects + break; } } @@ -254,7 +284,27 @@ void ExecutionContext::markObjects(Heap::Base *m, ExecutionEngine *engine) if (ctx->outer) ctx->outer->mark(engine); - if (ctx->type >= Heap::ExecutionContext::Type_CallContext) { + switch (ctx->type) { + case Heap::ExecutionContext::Type_CatchContext: { + CatchContext::Data *c = static_cast<CatchContext::Data *>(ctx); + c->exceptionVarName->mark(engine); + c->exceptionValue.mark(engine); + break; + } + case Heap::ExecutionContext::Type_WithContext: { + WithContext::Data *w = static_cast<WithContext::Data *>(ctx); + if (w->withObject) + w->withObject->mark(engine); + break; + } + case Heap::ExecutionContext::Type_GlobalContext: { + GlobalContext::Data *g = static_cast<GlobalContext::Data *>(ctx); + g->global->mark(engine); + break; + } + case Heap::ExecutionContext::Type_SimpleCallContext: + break; + case Heap::ExecutionContext::Type_CallContext: { QV4::Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); ctx->callData->thisObject.mark(engine); for (int arg = 0; arg < qMax(ctx->callData->argc, (int)c->function->formalParameterCount()); ++arg) @@ -264,17 +314,13 @@ void ExecutionContext::markObjects(Heap::Base *m, ExecutionEngine *engine) if (c->activation) c->activation->mark(engine); c->function->mark(engine); - } else if (ctx->type == Heap::ExecutionContext::Type_WithContext) { - WithContext::Data *w = static_cast<WithContext::Data *>(ctx); - if (w->withObject) - w->withObject->mark(engine); - } else if (ctx->type == Heap::ExecutionContext::Type_CatchContext) { - CatchContext::Data *c = static_cast<CatchContext::Data *>(ctx); - c->exceptionVarName->mark(engine); - c->exceptionValue.mark(engine); - } else if (ctx->type == Heap::ExecutionContext::Type_GlobalContext) { - GlobalContext::Data *g = static_cast<GlobalContext::Data *>(ctx); - g->global->mark(engine); + break; + } + case Heap::ExecutionContext::Type_QmlContext: { + QmlContext::Data *g = static_cast<QmlContext::Data *>(ctx); + g->qml->mark(engine); + break; + } } } @@ -282,57 +328,71 @@ void ExecutionContext::setProperty(String *name, const Value &value) { Scope scope(this); ScopedContext ctx(scope, this); + ScopedObject activation(scope); + for (; ctx; ctx = ctx->d()->outer) { - if (ctx->d()->type == Heap::ExecutionContext::Type_WithContext) { + activation = (Object *)0; + switch (ctx->d()->type) { + case Heap::ExecutionContext::Type_CatchContext: { + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); + if (c->exceptionVarName->isEqualTo(name->d())) { + c->exceptionValue = value; + return; + } + break; + } + case Heap::ExecutionContext::Type_WithContext: { ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); if (w->hasProperty(name)) { w->put(name, value); return; } - } else if (ctx->d()->type == Heap::ExecutionContext::Type_CatchContext && static_cast<Heap::CatchContext *>(ctx->d())->exceptionVarName->isEqualTo(name)) { - static_cast<Heap::CatchContext *>(ctx->d())->exceptionValue = value; - return; - } else { - ScopedObject activation(scope, (Object *)0); - if (ctx->d()->type >= Heap::ExecutionContext::Type_CallContext) { - Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); - if (c->function->function) { - uint index = c->function->function->internalClass->find(name); - if (index < UINT_MAX) { - if (index < c->function->formalParameterCount()) { - c->callData->args[c->function->formalParameterCount() - index - 1] = value; - } else { - index -= c->function->formalParameterCount(); - c->locals[index] = value; - } - return; + break; + } + case Heap::ExecutionContext::Type_GlobalContext: { + activation = static_cast<Heap::GlobalContext *>(ctx->d())->global; + break; + } + case Heap::ExecutionContext::Type_CallContext: + case Heap::ExecutionContext::Type_SimpleCallContext: { + Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); + if (c->function->function) { + uint index = c->function->function->internalClass->find(name); + if (index < UINT_MAX) { + if (index < c->function->formalParameterCount()) { + c->callData->args[c->function->formalParameterCount() - index - 1] = value; + } else { + index -= c->function->formalParameterCount(); + c->locals[index] = value; } + return; } - activation = c->activation; - } else if (ctx->d()->type == Heap::ExecutionContext::Type_GlobalContext) { - activation = static_cast<Heap::GlobalContext *>(ctx->d())->global; } + activation = c->activation; + break; + } + case Heap::ExecutionContext::Type_QmlContext: { + activation = static_cast<Heap::QmlContext *>(ctx->d())->qml; + activation->put(name, value); + return; + } + } - if (activation) { - if (ctx->d()->type == Heap::ExecutionContext::Type_QmlContext) { - activation->put(name, value); - return; - } else { - uint member = activation->internalClass()->find(name); - if (member < UINT_MAX) { - activation->putValue(activation->propertyAt(member), activation->internalClass()->propertyData[member], value); - return; - } - } + if (activation) { + uint member = activation->internalClass()->find(name); + if (member < UINT_MAX) { + activation->putValue(member, value); + return; } } } - if (d()->strictMode || name->equals(d()->engine->id_this)) { + + if (d()->strictMode || name->equals(d()->engine->id_this())) { ScopedValue n(scope, name->asReturnedValue()); engine()->throwReferenceError(n); return; } - d()->engine->globalObject()->put(name, value); + d()->engine->globalObject->put(name, value); } ReturnedValue ExecutionContext::getProperty(String *name) @@ -341,14 +401,22 @@ ReturnedValue ExecutionContext::getProperty(String *name) ScopedValue v(scope); name->makeIdentifier(scope.engine); - if (name->equals(d()->engine->id_this)) + if (name->equals(d()->engine->id_this())) return thisObject().asReturnedValue(); bool hasWith = false; bool hasCatchScope = false; ScopedContext ctx(scope, this); for (; ctx; ctx = ctx->d()->outer) { - if (ctx->d()->type == Heap::ExecutionContext::Type_WithContext) { + switch (ctx->d()->type) { + case Heap::ExecutionContext::Type_CatchContext: { + hasCatchScope = true; + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); + if (c->exceptionVarName->isEqualTo(name->d())) + return c->exceptionValue.asReturnedValue(); + break; + } + case Heap::ExecutionContext::Type_WithContext: { ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); hasWith = true; bool hasProperty = false; @@ -356,17 +424,18 @@ ReturnedValue ExecutionContext::getProperty(String *name) if (hasProperty) { return v->asReturnedValue(); } - continue; + break; } - - else if (ctx->d()->type == Heap::ExecutionContext::Type_CatchContext) { - hasCatchScope = true; - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); - if (c->exceptionVarName->isEqualTo(name)) - return c->exceptionValue.asReturnedValue(); + case Heap::ExecutionContext::Type_GlobalContext: { + ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); + bool hasProperty = false; + v = global->get(name, &hasProperty); + if (hasProperty) + return v->asReturnedValue(); + break; } - - else if (ctx->d()->type >= Heap::ExecutionContext::Type_CallContext) { + case Heap::ExecutionContext::Type_CallContext: + case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); ScopedFunctionObject f(scope, c->function); if (f->function() && (f->needsActivation() || hasWith || hasCatchScope)) { @@ -387,85 +456,97 @@ ReturnedValue ExecutionContext::getProperty(String *name) if (f->function() && f->function()->isNamedExpression() && name->equals(ScopedString(scope, f->function()->name()))) return f.asReturnedValue(); + break; } - - else if (ctx->d()->type == Heap::ExecutionContext::Type_GlobalContext) { - ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); + case Heap::ExecutionContext::Type_QmlContext: { + ScopedObject qml(scope, static_cast<Heap::QmlContext *>(ctx->d())->qml); bool hasProperty = false; - v = global->get(name, &hasProperty); + v = qml->get(name, &hasProperty); if (hasProperty) return v->asReturnedValue(); + break; + } } } ScopedValue n(scope, name); return engine()->throwReferenceError(n); } -ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Heap::Object **base) +ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) { Scope scope(this); ScopedValue v(scope); - *base = (Heap::Object *)0; + base->setM(0); name->makeIdentifier(scope.engine); - if (name->equals(d()->engine->id_this)) + if (name->equals(d()->engine->id_this())) return thisObject().asReturnedValue(); bool hasWith = false; bool hasCatchScope = false; ScopedContext ctx(scope, this); for (; ctx; ctx = ctx->d()->outer) { - if (ctx->d()->type == Heap::ExecutionContext::Type_WithContext) { + switch (ctx->d()->type) { + case Heap::ExecutionContext::Type_CatchContext: { + hasCatchScope = true; + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); + if (c->exceptionVarName->isEqualTo(name->d())) + return c->exceptionValue.asReturnedValue(); + break; + } + case Heap::ExecutionContext::Type_WithContext: { ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); hasWith = true; bool hasProperty = false; v = w->get(name, &hasProperty); if (hasProperty) { - *base = w->d(); + base->setM(w->d()); return v->asReturnedValue(); } - continue; + break; } - - else if (ctx->d()->type == Heap::ExecutionContext::Type_CatchContext) { - hasCatchScope = true; - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); - if (c->exceptionVarName->isEqualTo(name)) - return c->exceptionValue.asReturnedValue(); + case Heap::ExecutionContext::Type_GlobalContext: { + ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); + bool hasProperty = false; + v = global->get(name, &hasProperty); + if (hasProperty) + return v->asReturnedValue(); + break; } - - else if (ctx->d()->type >= Heap::ExecutionContext::Type_CallContext) { + case Heap::ExecutionContext::Type_CallContext: + case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); ScopedFunctionObject f(scope, c->function); if (f->function() && (f->needsActivation() || hasWith || hasCatchScope)) { uint index = f->function()->internalClass->find(name); if (index < UINT_MAX) { - if (index < f->formalParameterCount()) - return c->callData->args[f->formalParameterCount() - index - 1].asReturnedValue(); - return c->locals[index - f->formalParameterCount()].asReturnedValue(); + if (index < c->function->formalParameterCount()) + return c->callData->args[c->function->formalParameterCount() - index - 1].asReturnedValue(); + return c->locals[index - c->function->formalParameterCount()].asReturnedValue(); } } ScopedObject activation(scope, c->activation); if (activation) { bool hasProperty = false; v = activation->get(name, &hasProperty); - if (hasProperty) { - if (ctx->d()->type == Heap::ExecutionContext::Type_QmlContext) - *base = activation->d(); + if (hasProperty) return v->asReturnedValue(); - } } if (f->function() && f->function()->isNamedExpression() && name->equals(ScopedString(scope, f->function()->name()))) - return c->function->asReturnedValue(); + return f.asReturnedValue(); + break; } - - else if (ctx->d()->type == Heap::ExecutionContext::Type_GlobalContext) { - ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); + case Heap::ExecutionContext::Type_QmlContext: { + ScopedObject qml(scope, static_cast<Heap::QmlContext *>(ctx->d())->qml); bool hasProperty = false; - v = global->get(name, &hasProperty); - if (hasProperty) + v = qml->get(name, &hasProperty); + if (hasProperty) { + base->setM(qml->d()); return v->asReturnedValue(); + } + break; + } } } ScopedValue n(scope, name); @@ -476,7 +557,7 @@ Heap::FunctionObject *ExecutionContext::getFunctionObject() const { Scope scope(d()->engine); ScopedContext it(scope, this->d()); - for (; it; it = it->d()->parent) { + for (; it; it = it->d()->outer) { if (const CallContext *callCtx = it->asCallContext()) return callCtx->d()->function; else if (it->asCatchContext() || it->asWithContext()) @@ -487,3 +568,19 @@ Heap::FunctionObject *ExecutionContext::getFunctionObject() const return 0; } + + +QObject *QmlContext::qmlScope() const +{ + return d()->qml->scopeObject; +} + +QQmlContextData *QmlContext::qmlContext() const +{ + return d()->qml->context; +} + +void QmlContext::takeContextOwnership() { + d()->qml->ownsContext = true; +} + diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 8392dd836d..6c360e7dda 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -33,11 +33,25 @@ #ifndef QMLJS_ENVIRONMENT_H #define QMLJS_ENVIRONMENT_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include "qv4managed_p.h" QT_BEGIN_NAMESPACE +class QQmlContextData; +class QObject; + namespace QV4 { namespace CompiledData { @@ -45,6 +59,8 @@ struct CompilationUnit; struct Function; } +struct QmlContextWrapper; +struct Identifier; struct CallContext; struct CatchContext; struct WithContext; @@ -74,9 +90,9 @@ struct ExecutionContext : Base { Type_GlobalContext = 0x1, Type_CatchContext = 0x2, Type_WithContext = 0x3, - Type_SimpleCallContext = 0x4, - Type_CallContext = 0x5, - Type_QmlContext = 0x6 + Type_QmlContext = 0x4, + Type_SimpleCallContext = 0x5, + Type_CallContext = 0x6 }; inline ExecutionContext(ExecutionEngine *engine, ContextType t); @@ -84,8 +100,7 @@ struct ExecutionContext : Base { CallData *callData; ExecutionEngine *engine; - ExecutionContext *parent; - ExecutionContext *outer; + Pointer<ExecutionContext> outer; Lookup *lookups; CompiledData::CompilationUnit *compilationUnit; @@ -94,6 +109,18 @@ struct ExecutionContext : Base { int lineNumber; }; +inline +ExecutionContext::ExecutionContext(ExecutionEngine *engine, ContextType t) + : engine(engine) + , outer(0) + , lookups(0) + , compilationUnit(0) + , type(t) + , strictMode(false) + , lineNumber(-1) +{} + + struct CallContext : ExecutionContext { CallContext(ExecutionEngine *engine, ContextType t = Type_SimpleCallContext) : ExecutionContext(engine, t) @@ -102,29 +129,34 @@ struct CallContext : ExecutionContext { locals = 0; activation = 0; } - CallContext(ExecutionEngine *engine, QV4::Object *qml, QV4::FunctionObject *function); - FunctionObject *function; + Pointer<FunctionObject> function; Value *locals; - Object *activation; + Pointer<Object> activation; }; struct GlobalContext : ExecutionContext { GlobalContext(ExecutionEngine *engine); - Object *global; + Pointer<Object> global; }; struct CatchContext : ExecutionContext { - CatchContext(ExecutionEngine *engine, QV4::String *exceptionVarName, const Value &exceptionValue); - StringValue exceptionVarName; + CatchContext(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue); + Pointer<String> exceptionVarName; Value exceptionValue; }; struct WithContext : ExecutionContext { - WithContext(ExecutionEngine *engine, QV4::Object *with); - Object *withObject; + WithContext(ExecutionContext *outerContext, Object *with); + Pointer<Object> withObject; }; +struct QmlContextWrapper; + +struct QmlContext : ExecutionContext { + QmlContext(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml); + Pointer<QmlContextWrapper> qml; +}; } @@ -139,16 +171,17 @@ struct Q_QML_EXPORT ExecutionContext : public Managed ExecutionEngine *engine() const { return d()->engine; } - Heap::CallContext *newCallContext(FunctionObject *f, CallData *callData); - Heap::WithContext *newWithContext(Object *with); - Heap::CatchContext *newCatchContext(String *exceptionVarName, const Value &exceptionValue); - Heap::CallContext *newQmlContext(FunctionObject *f, Object *qml); + Heap::CallContext *newCallContext(const FunctionObject *f, CallData *callData); + Heap::WithContext *newWithContext(Heap::Object *with); + Heap::CatchContext *newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue); + Heap::QmlContext *newQmlContext(QmlContextWrapper *qml); + Heap::QmlContext *newQmlContext(QQmlContextData *context, QObject *scopeObject); void createMutableBinding(String *name, bool deletable); void setProperty(String *name, const Value &value); ReturnedValue getProperty(String *name); - ReturnedValue getPropertyAndBase(String *name, Heap::Object **base); + ReturnedValue getPropertyAndBase(String *name, Value *base); bool deleteProperty(String *name); inline CallContext *asCallContext(); @@ -160,7 +193,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed static void markObjects(Heap::Base *m, ExecutionEngine *e); - const Value &thisObject() const { + Value &thisObject() const { return d()->callData->thisObject; } int argc() const { @@ -174,7 +207,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed } }; -struct CallContext : public ExecutionContext +struct Q_QML_EXPORT CallContext : public ExecutionContext { V4_MANAGED(CallContext, ExecutionContext) @@ -208,6 +241,16 @@ struct WithContext : public ExecutionContext V4_MANAGED(WithContext, ExecutionContext) }; +struct QmlContext : public ExecutionContext +{ + V4_MANAGED(QmlContext, ExecutionContext) + + QObject *qmlScope() const; + QQmlContextData *qmlContext() const; + + void takeContextOwnership(); +}; + inline CallContext *ExecutionContext::asCallContext() { return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<CallContext *>(this) : 0; diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index 8a66c2cbfc..8901834e76 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -33,6 +33,7 @@ #include "qv4dataview_p.h" #include "qv4arraybuffer_p.h" +#include "qv4string_p.h" #include "qendian.h" @@ -46,9 +47,9 @@ Heap::DataViewCtor::DataViewCtor(QV4::ExecutionContext *scope) { } -ReturnedValue DataViewCtor::construct(Managed *m, CallData *callData) +ReturnedValue DataViewCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<Object *>(m)->engine()); + Scope scope(static_cast<const Object *>(m)->engine()); Scoped<ArrayBuffer> buffer(scope, callData->argument(0)); if (!buffer) return scope.engine->throwTypeError(); @@ -61,7 +62,7 @@ ReturnedValue DataViewCtor::construct(Managed *m, CallData *callData) if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); - Scoped<DataView> a(scope, scope.engine->memoryManager->alloc<DataView>(scope.engine)); + Scoped<DataView> a(scope, scope.engine->memoryManager->allocObject<DataView>()); a->d()->buffer = buffer->d(); a->d()->byteLength = byteLength; a->d()->byteOffset = byteOffset; @@ -69,21 +70,12 @@ ReturnedValue DataViewCtor::construct(Managed *m, CallData *callData) } -ReturnedValue DataViewCtor::call(Managed *that, CallData *callData) +ReturnedValue DataViewCtor::call(const Managed *that, CallData *callData) { return construct(that, callData); } -Heap::DataView::DataView(ExecutionEngine *e) - : Heap::Object(e->emptyClass, e->dataViewPrototype.asObject()), - buffer(0), - byteLength(0), - byteOffset(0) -{ -} - - void DataView::markObjects(Heap::Base *that, ExecutionEngine *e) { DataView::Data *v = static_cast<DataView::Data *>(that); @@ -94,30 +86,38 @@ void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(3)); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); - defineDefaultProperty(engine->id_constructor, (o = ctor)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + defineDefaultProperty(engine->id_constructor(), (o = ctor)); defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, 0); defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, 0); defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, 0); defineDefaultProperty(QStringLiteral("getInt8"), method_getChar<signed char>, 0); - defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 0); + defineDefaultProperty(QStringLiteral("getUint8"), method_getChar<unsigned char>, 0); defineDefaultProperty(QStringLiteral("getInt16"), method_get<short>, 0); - defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 0); + defineDefaultProperty(QStringLiteral("getUint16"), method_get<unsigned short>, 0); defineDefaultProperty(QStringLiteral("getInt32"), method_get<int>, 0); - defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 0); + defineDefaultProperty(QStringLiteral("getUint32"), method_get<unsigned int>, 0); defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat<float>, 0); defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat<double>, 0); defineDefaultProperty(QStringLiteral("setInt8"), method_setChar<signed char>, 0); - defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 0); + defineDefaultProperty(QStringLiteral("setUint8"), method_setChar<unsigned char>, 0); defineDefaultProperty(QStringLiteral("setInt16"), method_set<short>, 0); - defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 0); + defineDefaultProperty(QStringLiteral("setUint16"), method_set<unsigned short>, 0); defineDefaultProperty(QStringLiteral("setInt32"), method_set<int>, 0); - defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 0); + defineDefaultProperty(QStringLiteral("setUint32"), method_set<unsigned int>, 0); defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat<float>, 0); defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat<double>, 0); + + // For backword compatibility + defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 0); + defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 0); + defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 0); + defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 0); + defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 0); + defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 0); } ReturnedValue DataViewPrototype::method_get_buffer(CallContext *ctx) diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 3f0c1e9e23..26347766d3 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -33,6 +33,17 @@ #ifndef QV4DATAVIEW_H #define QV4DATAVIEW_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" #include "qv4functionobject_p.h" @@ -47,8 +58,8 @@ struct DataViewCtor : FunctionObject { }; struct DataView : Object { - DataView(ExecutionEngine *e); - ArrayBuffer *buffer; + DataView() {} + Pointer<ArrayBuffer> buffer; uint byteLength; uint byteOffset; }; @@ -59,13 +70,14 @@ struct DataViewCtor: FunctionObject { V4_OBJECT2(DataViewCtor, FunctionObject) - static ReturnedValue construct(Managed *m, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct DataView : Object { V4_OBJECT2(DataView, Object) + V4_PROTOTYPE(dataViewPrototype) static void markObjects(Heap::Base *that, ExecutionEngine *e); }; diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 451ef2486d..a6e1f47d91 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -36,6 +36,7 @@ #include "qv4objectproto_p.h" #include "qv4scopedvalue_p.h" #include "qv4runtime_p.h" +#include "qv4string_p.h" #include <QtCore/QDebug> #include <QtCore/QDateTime> @@ -627,15 +628,14 @@ static double getLocalTZA() DEFINE_OBJECT_VTABLE(DateObject); -Heap::DateObject::DateObject(QV4::ExecutionEngine *engine, const QDateTime &date) - : Heap::Object(engine->emptyClass, engine->datePrototype.asObject()) +Heap::DateObject::DateObject(const QDateTime &date) { - value.setDouble(date.isValid() ? date.toMSecsSinceEpoch() : qSNaN()); + this->date = date.isValid() ? date.toMSecsSinceEpoch() : qSNaN(); } QDateTime DateObject::toQDateTime() const { - return ToDateTime(date().asDouble(), Qt::LocalTime); + return ToDateTime(date(), Qt::LocalTime); } DEFINE_OBJECT_VTABLE(DateCtor); @@ -645,9 +645,9 @@ Heap::DateCtor::DateCtor(QV4::ExecutionContext *scope) { } -ReturnedValue DateCtor::construct(Managed *m, CallData *callData) +ReturnedValue DateCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<DateCtor *>(m)->engine()); + Scope scope(static_cast<const DateCtor *>(m)->engine()); double t = 0; if (callData->argc == 0) @@ -655,15 +655,16 @@ ReturnedValue DateCtor::construct(Managed *m, CallData *callData) else if (callData->argc == 1) { ScopedValue arg(scope, callData->args[0]); - if (DateObject *d = arg->asDateObject()) - arg = d->date(); - else + if (DateObject *d = arg->as<DateObject>()) { + t = d->date(); + } else { arg = RuntimeHelpers::toPrimitive(arg, PREFERREDTYPE_HINT); - if (arg->isString()) - t = ParseString(arg->stringValue()->toQString()); - else - t = TimeClip(arg->toNumber()); + if (arg->isString()) + t = ParseString(arg->stringValue()->toQString()); + else + t = TimeClip(arg->toNumber()); + } } else { // d.argc > 1 @@ -683,18 +684,18 @@ ReturnedValue DateCtor::construct(Managed *m, CallData *callData) return Encode(scope.engine->newDateObject(Primitive::fromDouble(t))); } -ReturnedValue DateCtor::call(Managed *m, CallData *) +ReturnedValue DateCtor::call(const Managed *m, CallData *) { double t = currentTime(); - return static_cast<DateCtor *>(m)->engine()->newString(ToString(t))->asReturnedValue(); + return static_cast<const DateCtor *>(m)->engine()->newString(ToString(t))->asReturnedValue(); } void DatePrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(7)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(7)); LocalTZA = getLocalTZA(); ctor->defineDefaultProperty(QStringLiteral("parse"), method_parse, 1); @@ -702,13 +703,13 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) ctor->defineDefaultProperty(QStringLiteral("now"), method_now, 0); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString, method_toString, 0); + defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("toDateString"), method_toDateString, 0); defineDefaultProperty(QStringLiteral("toTimeString"), method_toTimeString, 0); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0); defineDefaultProperty(QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); defineDefaultProperty(QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); - defineDefaultProperty(engine->id_valueOf, method_valueOf, 0); + defineDefaultProperty(engine->id_valueOf(), method_valueOf, 0); defineDefaultProperty(QStringLiteral("getTime"), method_getTime, 0); defineDefaultProperty(QStringLiteral("getYear"), method_getYear, 0); defineDefaultProperty(QStringLiteral("getFullYear"), method_getFullYear, 0); @@ -752,8 +753,8 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) double DatePrototype::getThisDate(ExecutionContext *ctx) { - if (DateObject *thisObject = ctx->thisObject().asDateObject()) - return thisObject->date().asDouble(); + if (DateObject *thisObject = ctx->thisObject().as<DateObject>()) + return thisObject->date(); else { ctx->engine()->throwTypeError(); return 0; @@ -994,8 +995,8 @@ ReturnedValue DatePrototype::method_setTime(CallContext *ctx) return ctx->engine()->throwTypeError(); double t = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); - self->date().setDouble(TimeClip(t)); - return self->date().asReturnedValue(); + self->setDate(TimeClip(t)); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setMilliseconds(CallContext *ctx) @@ -1005,175 +1006,175 @@ ReturnedValue DatePrototype::method_setMilliseconds(CallContext *ctx) if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); double ms = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); - self->date().setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); - return self->date().asReturnedValue(); + self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCMilliseconds(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double ms = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); - self->date().setDouble(TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); - return self->date().asReturnedValue(); + self->setDate(TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setSeconds(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); double sec = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double ms = (ctx->argc() < 2) ? msFromTime(t) : ctx->args()[1].toNumber(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCSeconds(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double sec = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double ms = (ctx->argc() < 2) ? msFromTime(t) : ctx->args()[1].toNumber(); t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setMinutes(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); double min = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double sec = (ctx->argc() < 2) ? SecFromTime(t) : ctx->args()[1].toNumber(); double ms = (ctx->argc() < 3) ? msFromTime(t) : ctx->args()[2].toNumber(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCMinutes(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double min = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double sec = (ctx->argc() < 2) ? SecFromTime(t) : ctx->args()[1].toNumber(); double ms = (ctx->argc() < 3) ? msFromTime(t) : ctx->args()[2].toNumber(); t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setHours(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); double hour = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double min = (ctx->argc() < 2) ? MinFromTime(t) : ctx->args()[1].toNumber(); double sec = (ctx->argc() < 3) ? SecFromTime(t) : ctx->args()[2].toNumber(); double ms = (ctx->argc() < 4) ? msFromTime(t) : ctx->args()[3].toNumber(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCHours(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double hour = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double min = (ctx->argc() < 2) ? MinFromTime(t) : ctx->args()[1].toNumber(); double sec = (ctx->argc() < 3) ? SecFromTime(t) : ctx->args()[2].toNumber(); double ms = (ctx->argc() < 4) ? msFromTime(t) : ctx->args()[3].toNumber(); t = TimeClip(MakeDate(Day(t), MakeTime(hour, min, sec, ms))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setDate(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); double date = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCDate(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double date = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); t = TimeClip(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setMonth(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); double month = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double date = (ctx->argc() < 2) ? DateFromTime(t) : ctx->args()[1].toNumber(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCMonth(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double month = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double date = (ctx->argc() < 2) ? DateFromTime(t) : ctx->args()[1].toNumber(); t = TimeClip(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setYear(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); if (std::isnan(t)) t = 0; else @@ -1189,49 +1190,49 @@ ReturnedValue DatePrototype::method_setYear(CallContext *ctx) r = UTC(MakeDate(r, TimeWithinDay(t))); r = TimeClip(r); } - self->date().setDouble(r); - return self->date().asReturnedValue(); + self->setDate(r); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCFullYear(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double year = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double month = (ctx->argc() < 2) ? MonthFromTime(t) : ctx->args()[1].toNumber(); double date = (ctx->argc() < 3) ? DateFromTime(t) : ctx->args()[2].toNumber(); t = TimeClip(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setFullYear(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); if (std::isnan(t)) t = 0; double year = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double month = (ctx->argc() < 2) ? MonthFromTime(t) : ctx->args()[1].toNumber(); double date = (ctx->argc() < 3) ? DateFromTime(t) : ctx->args()[2].toNumber(); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_toUTCString(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); return ctx->d()->engine->newString(ToUTCString(t))->asReturnedValue(); } @@ -1250,11 +1251,11 @@ static void addZeroPrefixedInt(QString &str, int num, int nDigits) ReturnedValue DatePrototype::method_toISOString(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); if (!std::isfinite(t)) return ctx->engine()->throwRangeError(ctx->thisObject()); @@ -1297,7 +1298,7 @@ ReturnedValue DatePrototype::method_toJSON(CallContext *ctx) ScopedString s(scope, ctx->d()->engine->newString(QStringLiteral("toISOString"))); ScopedValue v(scope, O->objectValue()->get(s)); - FunctionObject *toIso = v->asFunctionObject(); + FunctionObject *toIso = v->as<FunctionObject>(); if (!toIso) return ctx->engine()->throwTypeError(); diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index dad3689054..2eaa837666 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -33,6 +33,17 @@ #ifndef QV4DATEOBJECT_P_H #define QV4DATEOBJECT_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" #include "qv4functionobject_p.h" #include <QtCore/qnumeric.h> @@ -46,21 +57,20 @@ namespace QV4 { namespace Heap { struct DateObject : Object { - DateObject(InternalClass *ic, QV4::Object *prototype) - : Object(ic, prototype) + DateObject() { - value = Encode(qSNaN()); + date = qSNaN(); } - DateObject(QV4::ExecutionEngine *engine, const Value &date) - : Object(engine->emptyClass, engine->datePrototype.asObject()) + DateObject(const Value &date) { - value = date; + this->date = date.toNumber(); } - DateObject(QV4::ExecutionEngine *engine, const QDateTime &date); - Value value; + DateObject(const QDateTime &date); + double date; }; + struct DateCtor : FunctionObject { DateCtor(QV4::ExecutionContext *scope); }; @@ -70,21 +80,26 @@ struct DateCtor : FunctionObject { struct DateObject: Object { V4_OBJECT2(DateObject, Object) Q_MANAGED_TYPE(DateObject) + V4_PROTOTYPE(datePrototype) - Value date() const { return d()->value; } - Value &date() { return d()->value; } - void setDate(const Value &date) { d()->value = date; } + double date() const { return d()->date; } + void setDate(double date) { d()->date = date; } QDateTime toQDateTime() const; }; +template<> +inline const DateObject *Value::as() const { + return isManaged() && m() && m()->vtable()->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : 0; +} + struct DateCtor: FunctionObject { V4_OBJECT2(DateCtor, FunctionObject) - static ReturnedValue construct(Managed *, CallData *callData); - static ReturnedValue call(Managed *that, CallData *); + static ReturnedValue construct(const Managed *, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *); }; struct DatePrototype: DateObject diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp index 36e7a3558c..7706a40da6 100644 --- a/src/qml/jsruntime/qv4debugging.cpp +++ b/src/qml/jsruntime/qv4debugging.cpp @@ -38,69 +38,72 @@ #include "qv4instr_moth_p.h" #include "qv4runtime_p.h" #include "qv4script_p.h" -#include "qv4objectiterator_p.h" #include "qv4identifier_p.h" -#include <iostream> +#include "qv4string_p.h" +#include "qv4objectiterator_p.h" +#include <iostream> #include <algorithm> +#include <QtCore/QJsonArray> +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonValue> + +QT_BEGIN_NAMESPACE + using namespace QV4; using namespace QV4::Debugging; -namespace { -class JavaScriptJob: public Debugger::Job -{ - QV4::ExecutionEngine *engine; - int frameNr; - const QString &script; +V4Debugger::JavaScriptJob::JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, + const QString &script) + : engine(engine) + , frameNr(frameNr) + , script(script) + , resultIsException(false) +{} -public: - JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, const QString &script) - : engine(engine) - , frameNr(frameNr) - , script(script) - {} - - void run() - { - Scope scope(engine); +void V4Debugger::JavaScriptJob::run() +{ + Scope scope(engine); - ExecutionContextSaver saver(scope, engine->currentContext()); + ExecutionContextSaver saver(scope); - if (frameNr > 0) { - Value *savedContexts = scope.alloc(frameNr); - for (int i = 0; i < frameNr; ++i) { - savedContexts[i] = engine->currentContext(); - engine->popContext(); - } + ExecutionContext *ctx = engine->currentContext; + if (frameNr > 0) { + for (int i = 0; i < frameNr; ++i) { + ctx = engine->parentContext(ctx); } + engine->pushContext(ctx); + } - ScopedContext ctx(scope, engine->currentContext()); - QV4::Script script(ctx, this->script); - script.strictMode = ctx->d()->strictMode; - // In order for property lookups in QML to work, we need to disable fast v4 lookups. That - // is a side-effect of inheritContext. - script.inheritContext = true; - script.parse(); - QV4::ScopedValue result(scope); - if (!scope.engine->hasException) - result = script.run(); - if (scope.engine->hasException) - result = scope.engine->catchException(); - handleResult(result); + QV4::Script script(ctx, this->script); + script.strictMode = ctx->d()->strictMode; + // In order for property lookups in QML to work, we need to disable fast v4 lookups. That + // is a side-effect of inheritContext. + script.inheritContext = true; + script.parse(); + QV4::ScopedValue result(scope); + if (!scope.engine->hasException) + result = script.run(); + if (scope.engine->hasException) { + result = scope.engine->catchException(); + resultIsException = true; } + handleResult(result); +} -protected: - virtual void handleResult(QV4::ScopedValue &result) = 0; -}; +bool V4Debugger::JavaScriptJob::hasExeption() const +{ + return resultIsException; +} -class EvalJob: public JavaScriptJob +class EvalJob: public V4Debugger::JavaScriptJob { bool result; public: EvalJob(QV4::ExecutionEngine *engine, const QString &script) - : JavaScriptJob(engine, /*frameNr*/-1, script) + : V4Debugger::JavaScriptJob(engine, /*frameNr*/-1, script) , result(false) {} @@ -115,58 +118,8 @@ public: } }; -class ExpressionEvalJob: public JavaScriptJob -{ - Debugger::Collector *collector; - -public: - ExpressionEvalJob(ExecutionEngine *engine, int frameNr, const QString &expression, Debugger::Collector *collector) - : JavaScriptJob(engine, frameNr, expression) - , collector(collector) - { - } - - virtual void handleResult(QV4::ScopedValue &result) - { - collector->collect(QStringLiteral("body"), result); - } -}; - -class GatherSourcesJob: public Debugger::Job -{ - QV4::ExecutionEngine *engine; - const int seq; - -public: - GatherSourcesJob(QV4::ExecutionEngine *engine, int seq) - : engine(engine) - , seq(seq) - {} - - ~GatherSourcesJob() {} - - void run() - { - QStringList sources; - - foreach (QV4::CompiledData::CompilationUnit *unit, engine->compilationUnits) { - QString fileName = unit->fileName(); - if (!fileName.isEmpty()) - sources.append(fileName); - } - - Debugger *debugger = engine->debugger; - QMetaObject::invokeMethod(debugger->agent(), "sourcesCollected", Qt::QueuedConnection, - Q_ARG(QV4::Debugging::Debugger*, debugger), - Q_ARG(QStringList, sources), - Q_ARG(int, seq)); - } -}; -} - -Debugger::Debugger(QV4::ExecutionEngine *engine) +V4Debugger::V4Debugger(QV4::ExecutionEngine *engine) : m_engine(engine) - , m_agent(0) , m_state(Running) , m_stepping(NotStepping) , m_pauseRequested(false) @@ -176,46 +129,11 @@ Debugger::Debugger(QV4::ExecutionEngine *engine) , m_gatherSources(0) , m_runningJob(0) { - qMetaTypeId<Debugger*>(); + qMetaTypeId<V4Debugger*>(); qMetaTypeId<PauseReason>(); } -Debugger::~Debugger() -{ - detachFromAgent(); -} - -void Debugger::attachToAgent(DebuggerAgent *agent) -{ - Q_ASSERT(!m_agent); - m_agent = agent; -} - -void Debugger::detachFromAgent() -{ - DebuggerAgent *agent = 0; - { - QMutexLocker locker(&m_lock); - agent = m_agent; - m_agent = 0; - } - if (agent) - agent->removeDebugger(this); -} - -void Debugger::gatherSources(int requestSequenceNr) -{ - QMutexLocker locker(&m_lock); - - m_gatherSources = new GatherSourcesJob(m_engine, requestSequenceNr); - if (m_state == Paused) { - runInEngine_havingLock(m_gatherSources); - delete m_gatherSources; - m_gatherSources = 0; - } -} - -void Debugger::pause() +void V4Debugger::pause() { QMutexLocker locker(&m_lock); if (m_state == Paused) @@ -223,7 +141,7 @@ void Debugger::pause() m_pauseRequested = true; } -void Debugger::resume(Speed speed) +void V4Debugger::resume(Speed speed) { QMutexLocker locker(&m_lock); if (m_state != Paused) @@ -232,292 +150,47 @@ void Debugger::resume(Speed speed) if (!m_returnedValue.isUndefined()) m_returnedValue.set(m_engine, Encode::undefined()); - m_currentContext.set(m_engine, m_engine->currentContext()); + m_currentContext.set(m_engine, *m_engine->currentContext); m_stepping = speed; m_runningCondition.wakeAll(); } -void Debugger::addBreakPoint(const QString &fileName, int lineNumber, const QString &condition) +void V4Debugger::addBreakPoint(const QString &fileName, int lineNumber, const QString &condition) { QMutexLocker locker(&m_lock); m_breakPoints.insert(DebuggerBreakPoint(fileName.mid(fileName.lastIndexOf('/') + 1), lineNumber), condition); m_haveBreakPoints = true; } -void Debugger::removeBreakPoint(const QString &fileName, int lineNumber) +void V4Debugger::removeBreakPoint(const QString &fileName, int lineNumber) { QMutexLocker locker(&m_lock); m_breakPoints.remove(DebuggerBreakPoint(fileName.mid(fileName.lastIndexOf('/') + 1), lineNumber)); m_haveBreakPoints = !m_breakPoints.isEmpty(); } -void Debugger::setBreakOnThrow(bool onoff) +void V4Debugger::setBreakOnThrow(bool onoff) { QMutexLocker locker(&m_lock); m_breakOnThrow = onoff; } -Debugger::ExecutionState Debugger::currentExecutionState() const +V4Debugger::ExecutionState V4Debugger::currentExecutionState() const { ExecutionState state; state.fileName = getFunction()->sourceFile(); - state.lineNumber = engine()->currentContext()->lineNumber; + state.lineNumber = engine()->current->lineNumber; return state; } -QVector<StackFrame> Debugger::stackTrace(int frameLimit) const +QVector<StackFrame> V4Debugger::stackTrace(int frameLimit) const { return m_engine->stackTrace(frameLimit); } -static inline Heap::CallContext *findContext(Heap::ExecutionContext *ctxt, int frame) -{ - if (!ctxt) - return 0; - - Scope scope(ctxt->engine); - ScopedContext ctx(scope, ctxt); - while (ctx) { - CallContext *cCtxt = ctx->asCallContext(); - if (cCtxt && cCtxt->d()->function) { - if (frame < 1) - return cCtxt->d(); - --frame; - } - ctx = ctx->d()->parent; - } - - return 0; -} - -static inline Heap::CallContext *findScope(Heap::ExecutionContext *ctxt, int scope) -{ - if (!ctxt) - return 0; - - Scope s(ctxt->engine); - ScopedContext ctx(s, ctxt); - for (; scope > 0 && ctx; --scope) - ctx = ctx->d()->outer; - - return (ctx && ctx->d()) ? ctx->asCallContext()->d() : 0; -} - -void Debugger::collectArgumentsInContext(Collector *collector, int frameNr, int scopeNr) -{ - if (state() != Paused) - return; - - class ArgumentCollectJob: public Job - { - QV4::ExecutionEngine *engine; - Collector *collector; - int frameNr; - int scopeNr; - - public: - ArgumentCollectJob(QV4::ExecutionEngine *engine, Collector *collector, int frameNr, int scopeNr) - : engine(engine) - , collector(collector) - , frameNr(frameNr) - , scopeNr(scopeNr) - {} - - ~ArgumentCollectJob() {} - - void run() - { - if (frameNr < 0) - return; - - Scope scope(engine); - Scoped<CallContext> ctxt(scope, findScope(findContext(engine->currentContext(), frameNr), scopeNr)); - if (!ctxt) - return; - - ScopedValue v(scope); - int nFormals = ctxt->formalCount(); - for (unsigned i = 0, ei = nFormals; i != ei; ++i) { - QString qName; - if (Identifier *name = ctxt->formals()[nFormals - i - 1]) - qName = name->string; - v = ctxt->argument(i); - collector->collect(qName, v); - } - } - }; - - ArgumentCollectJob job(m_engine, collector, frameNr, scopeNr); - runInEngine(&job); -} - -/// Same as \c retrieveArgumentsFromContext, but now for locals. -void Debugger::collectLocalsInContext(Collector *collector, int frameNr, int scopeNr) -{ - if (state() != Paused) - return; - - class LocalCollectJob: public Job - { - QV4::ExecutionEngine *engine; - Collector *collector; - int frameNr; - int scopeNr; - - public: - LocalCollectJob(QV4::ExecutionEngine *engine, Collector *collector, int frameNr, int scopeNr) - : engine(engine) - , collector(collector) - , frameNr(frameNr) - , scopeNr(scopeNr) - {} - - void run() - { - if (frameNr < 0) - return; - - Scope scope(engine); - Scoped<CallContext> ctxt(scope, findScope(findContext(engine->currentContext(), frameNr), scopeNr)); - if (!ctxt) - return; - - ScopedValue v(scope); - for (unsigned i = 0, ei = ctxt->variableCount(); i != ei; ++i) { - QString qName; - if (Identifier *name = ctxt->variables()[i]) - qName = name->string; - v = ctxt->d()->locals[i]; - collector->collect(qName, v); - } - } - }; - - LocalCollectJob job(m_engine, collector, frameNr, scopeNr); - runInEngine(&job); -} - -bool Debugger::collectThisInContext(Debugger::Collector *collector, int frame) -{ - if (state() != Paused) - return false; - - class ThisCollectJob: public Job - { - QV4::ExecutionEngine *engine; - Collector *collector; - int frameNr; - bool *foundThis; - - public: - ThisCollectJob(QV4::ExecutionEngine *engine, Collector *collector, int frameNr, bool *foundThis) - : engine(engine) - , collector(collector) - , frameNr(frameNr) - , foundThis(foundThis) - {} - - void run() - { - *foundThis = myRun(); - } - - bool myRun() - { - Scope scope(engine); - ScopedContext ctxt(scope, findContext(engine->currentContext(), frameNr)); - while (ctxt) { - if (CallContext *cCtxt = ctxt->asCallContext()) - if (cCtxt->d()->activation) - break; - ctxt = ctxt->d()->outer; - } - - if (!ctxt) - return false; - - ScopedObject o(scope, ctxt->asCallContext()->d()->activation); - collector->collect(o); - return true; - } - }; - - bool foundThis = false; - ThisCollectJob job(m_engine, collector, frame, &foundThis); - runInEngine(&job); - return foundThis; -} - -void Debugger::collectThrownValue(Collector *collector) -{ - if (state() != Paused || !m_engine->hasException) - return; - - class ThisCollectJob: public Job - { - QV4::ExecutionEngine *engine; - Collector *collector; - - public: - ThisCollectJob(QV4::ExecutionEngine *engine, Collector *collector) - : engine(engine) - , collector(collector) - {} - - void run() - { - Scope scope(engine); - ScopedValue v(scope, engine->exceptionValue); - collector->collect(QStringLiteral("exception"), v); - } - }; - - ThisCollectJob job(m_engine, collector); - runInEngine(&job); -} - -void Debugger::collectReturnedValue(Collector *collector) const -{ - if (state() != Paused) - return; - - Scope scope(m_engine); - ScopedObject o(scope, m_returnedValue.valueRef()); - collector->collect(o); -} - -QVector<Heap::ExecutionContext::ContextType> Debugger::getScopeTypes(int frame) const -{ - QVector<Heap::ExecutionContext::ContextType> types; - - if (state() != Paused) - return types; - - Scope scope(m_engine); - Scoped<CallContext> sctxt(scope, findContext(m_engine->currentContext(), frame)); - if (!sctxt || sctxt->d()->type < Heap::ExecutionContext::Type_SimpleCallContext) - return types; - - ScopedContext it(scope, sctxt->d()); - for (; it; it = it->d()->outer) - types.append(it->d()->type); - - return types; -} - - -void Debugger::evaluateExpression(int frameNr, const QString &expression, Debugger::Collector *resultsCollector) -{ - Q_ASSERT(state() == Paused); - - Q_ASSERT(m_runningJob == 0); - ExpressionEvalJob job(m_engine, frameNr, expression, resultsCollector); - runInEngine(&job); -} - -void Debugger::maybeBreakAtInstruction() +void V4Debugger::maybeBreakAtInstruction() { if (m_runningJob) // do not re-enter when we're doing a job for the debugger. return; @@ -532,7 +205,7 @@ void Debugger::maybeBreakAtInstruction() switch (m_stepping) { case StepOver: - if (m_currentContext.asManaged()->d() != m_engine->currentContext()) + if (m_currentContext.asManaged()->d() != m_engine->current) break; // fall through case StepIn: @@ -548,25 +221,25 @@ void Debugger::maybeBreakAtInstruction() pauseAndWait(PauseRequest); } else if (m_haveBreakPoints) { if (Function *f = getFunction()) { - const int lineNumber = engine()->currentContext()->lineNumber; + const int lineNumber = engine()->current->lineNumber; if (reallyHitTheBreakPoint(f->sourceFile(), lineNumber)) pauseAndWait(BreakPoint); } } } -void Debugger::enteringFunction() +void V4Debugger::enteringFunction() { if (m_runningJob) return; QMutexLocker locker(&m_lock); if (m_stepping == StepIn) { - m_currentContext.set(m_engine, m_engine->currentContext()); + m_currentContext.set(m_engine, *m_engine->currentContext); } } -void Debugger::leavingFunction(const ReturnedValue &retVal) +void V4Debugger::leavingFunction(const ReturnedValue &retVal) { if (m_runningJob) return; @@ -574,14 +247,14 @@ void Debugger::leavingFunction(const ReturnedValue &retVal) QMutexLocker locker(&m_lock); - if (m_stepping != NotStepping && m_currentContext.asManaged()->d() == m_engine->currentContext()) { - m_currentContext.set(m_engine, m_engine->currentContext()->parent); + if (m_stepping != NotStepping && m_currentContext.asManaged()->d() == m_engine->current) { + m_currentContext.set(m_engine, *m_engine->parentContext(m_engine->currentContext)); m_stepping = StepOver; m_returnedValue.set(m_engine, retVal); } } -void Debugger::aboutToThrow() +void V4Debugger::aboutToThrow() { if (!m_breakOnThrow) return; @@ -593,10 +266,10 @@ void Debugger::aboutToThrow() pauseAndWait(Throwing); } -Function *Debugger::getFunction() const +Function *V4Debugger::getFunction() const { Scope scope(m_engine); - ScopedContext context(scope, m_engine->currentContext()); + ExecutionContext *context = m_engine->currentContext; ScopedFunctionObject function(scope, context->getFunctionObject()); if (function) return function->function(); @@ -604,15 +277,13 @@ Function *Debugger::getFunction() const return context->d()->engine->globalCode; } -void Debugger::pauseAndWait(PauseReason reason) +void V4Debugger::pauseAndWait(PauseReason reason) { if (m_runningJob) return; m_state = Paused; - QMetaObject::invokeMethod(m_agent, "debuggerPaused", Qt::QueuedConnection, - Q_ARG(QV4::Debugging::Debugger*, this), - Q_ARG(QV4::Debugging::PauseReason, reason)); + emit debuggerPaused(this, reason); while (true) { m_runningCondition.wait(&m_lock); @@ -627,7 +298,7 @@ void Debugger::pauseAndWait(PauseReason reason) m_state = Running; } -bool Debugger::reallyHitTheBreakPoint(const QString &filename, int linenr) +bool V4Debugger::reallyHitTheBreakPoint(const QString &filename, int linenr) { BreakPoints::iterator it = m_breakPoints.find(DebuggerBreakPoint(filename.mid(filename.lastIndexOf('/') + 1), linenr)); if (it == m_breakPoints.end()) @@ -645,13 +316,13 @@ bool Debugger::reallyHitTheBreakPoint(const QString &filename, int linenr) return evilJob.resultAsBoolean(); } -void Debugger::runInEngine(Debugger::Job *job) +void V4Debugger::runInEngine(V4Debugger::Job *job) { QMutexLocker locker(&m_lock); runInEngine_havingLock(job); } -void Debugger::runInEngine_havingLock(Debugger::Job *job) +void V4Debugger::runInEngine_havingLock(V4Debugger::Job *job) { Q_ASSERT(job); Q_ASSERT(m_runningJob == 0); @@ -662,174 +333,8 @@ void Debugger::runInEngine_havingLock(Debugger::Job *job) m_runningJob = 0; } -void DebuggerAgent::addDebugger(Debugger *debugger) -{ - Q_ASSERT(!m_debuggers.contains(debugger)); - m_debuggers << debugger; - debugger->attachToAgent(this); - - debugger->setBreakOnThrow(m_breakOnThrow); - - foreach (const BreakPoint &breakPoint, m_breakPoints.values()) - if (breakPoint.enabled) - debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition); -} - -void DebuggerAgent::removeDebugger(Debugger *debugger) -{ - m_debuggers.removeAll(debugger); - debugger->detachFromAgent(); -} - -void DebuggerAgent::pause(Debugger *debugger) const -{ - debugger->pause(); -} - -void DebuggerAgent::pauseAll() const +V4Debugger::Job::~Job() { - foreach (Debugger *debugger, m_debuggers) - pause(debugger); } -void DebuggerAgent::resumeAll() const -{ - foreach (Debugger *debugger, m_debuggers) - if (debugger->state() == Debugger::Paused) - debugger->resume(Debugger::FullThrottle); -} - -int DebuggerAgent::addBreakPoint(const QString &fileName, int lineNumber, bool enabled, const QString &condition) -{ - if (enabled) - foreach (Debugger *debugger, m_debuggers) - debugger->addBreakPoint(fileName, lineNumber, condition); - - int id = m_breakPoints.size(); - m_breakPoints.insert(id, BreakPoint(fileName, lineNumber, enabled, condition)); - return id; -} - -void DebuggerAgent::removeBreakPoint(int id) -{ - BreakPoint breakPoint = m_breakPoints.value(id); - if (!breakPoint.isValid()) - return; - - m_breakPoints.remove(id); - - if (breakPoint.enabled) - foreach (Debugger *debugger, m_debuggers) - debugger->removeBreakPoint(breakPoint.fileName, breakPoint.lineNr); -} - -void DebuggerAgent::removeAllBreakPoints() -{ - QList<int> ids = m_breakPoints.keys(); - foreach (int id, ids) - removeBreakPoint(id); -} - -void DebuggerAgent::enableBreakPoint(int id, bool onoff) -{ - BreakPoint &breakPoint = m_breakPoints[id]; - if (!breakPoint.isValid() || breakPoint.enabled == onoff) - return; - breakPoint.enabled = onoff; - - foreach (Debugger *debugger, m_debuggers) { - if (onoff) - debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition); - else - debugger->removeBreakPoint(breakPoint.fileName, breakPoint.lineNr); - } -} - -QList<int> DebuggerAgent::breakPointIds(const QString &fileName, int lineNumber) const -{ - QList<int> ids; - - for (QHash<int, BreakPoint>::const_iterator i = m_breakPoints.begin(), ei = m_breakPoints.end(); i != ei; ++i) - if (i->lineNr == lineNumber && fileName.endsWith(i->fileName)) - ids.push_back(i.key()); - - return ids; -} - -void DebuggerAgent::setBreakOnThrow(bool onoff) -{ - if (onoff != m_breakOnThrow) { - m_breakOnThrow = onoff; - foreach (Debugger *debugger, m_debuggers) - debugger->setBreakOnThrow(onoff); - } -} - -DebuggerAgent::~DebuggerAgent() -{ - foreach (Debugger *debugger, m_debuggers) - debugger->detachFromAgent(); - - Q_ASSERT(m_debuggers.isEmpty()); -} - -Debugger::Collector::~Collector() -{ -} - -void Debugger::Collector::collect(const QString &name, const ScopedValue &value) -{ - switch (value->type()) { - case Value::Empty_Type: - Q_ASSERT(!"empty Value encountered"); - break; - case Value::Undefined_Type: - addUndefined(name); - break; - case Value::Null_Type: - addNull(name); - break; - case Value::Boolean_Type: - addBoolean(name, value->booleanValue()); - break; - case Value::Managed_Type: - if (String *s = value->asString()) - addString(name, s->toQString()); - else - addObject(name, value); - break; - case Value::Integer_Type: - addInteger(name, value->int_32); - break; - default: // double - addDouble(name, value->doubleValue()); - break; - } -} - -void Debugger::Collector::collect(Object *object) -{ - bool property = true; - qSwap(property, m_isProperty); - - Scope scope(m_engine); - ObjectIterator it(scope, object, ObjectIterator::EnumerableOnly); - ScopedValue name(scope); - ScopedValue value(scope); - while (true) { - Value v; - name = it.nextPropertyNameAsString(&v); - if (name->isNull()) - break; - QString key = name->toQStringNoThrow(); - value = v; - collect(key, value); - } - - qSwap(property, m_isProperty); -} - - -Debugger::Job::~Job() -{ -} +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h index e6a9750351..fdc9cac24f 100644 --- a/src/qml/jsruntime/qv4debugging_p.h +++ b/src/qml/jsruntime/qv4debugging_p.h @@ -34,6 +34,17 @@ #ifndef DEBUGGING_H #define DEBUGGING_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include "qv4engine_p.h" #include "qv4context_p.h" @@ -44,6 +55,8 @@ #include <QMutex> #include <QWaitCondition> +#include <QtCore/QJsonObject> + QT_BEGIN_NAMESPACE namespace QV4 { @@ -59,8 +72,6 @@ enum PauseReason { Step }; -class DebuggerAgent; - struct DebuggerBreakPoint { DebuggerBreakPoint(const QString &fileName, int line) : fileName(fileName), lineNumber(line) @@ -79,43 +90,44 @@ inline bool operator==(const DebuggerBreakPoint &a, const DebuggerBreakPoint &b) typedef QHash<DebuggerBreakPoint, QString> BreakPoints; +class Q_QML_EXPORT Debugger : public QObject +{ + Q_OBJECT -class Q_QML_EXPORT Debugger +public: + virtual ~Debugger() {} + virtual bool pauseAtNextOpportunity() const = 0; + virtual void maybeBreakAtInstruction() = 0; + virtual void enteringFunction() = 0; + virtual void leavingFunction(const ReturnedValue &retVal) = 0; + virtual void aboutToThrow() = 0; +}; + +class Q_QML_EXPORT V4Debugger : public Debugger { + Q_OBJECT public: - class Job + class Q_QML_EXPORT Job { public: virtual ~Job() = 0; virtual void run() = 0; }; - class Q_QML_EXPORT Collector + class Q_QML_EXPORT JavaScriptJob: public Job { - public: - Collector(ExecutionEngine *engine): m_engine(engine), m_isProperty(false) {} - virtual ~Collector(); + QV4::ExecutionEngine *engine; + int frameNr; + const QString &script; + bool resultIsException; - void collect(const QString &name, const ScopedValue &value); - void collect(Object *object); + public: + JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, const QString &script); + void run(); + bool hasExeption() const; protected: - virtual void addUndefined(const QString &name) = 0; - virtual void addNull(const QString &name) = 0; - virtual void addBoolean(const QString &name, bool value) = 0; - virtual void addString(const QString &name, const QString &value) = 0; - virtual void addObject(const QString &name, const Value &value) = 0; - virtual void addInteger(const QString &name, int value) = 0; - virtual void addDouble(const QString &name, double value) = 0; - - QV4::ExecutionEngine *engine() const { return m_engine; } - - bool isProperty() const { return m_isProperty; } - void setIsProperty(bool onoff) { m_isProperty = onoff; } - - private: - QV4::ExecutionEngine *m_engine; - bool m_isProperty; + virtual void handleResult(QV4::ScopedValue &result) = 0; }; enum State { @@ -132,17 +144,11 @@ public: NotStepping = FullThrottle }; - Debugger(ExecutionEngine *engine); - ~Debugger(); + V4Debugger(ExecutionEngine *engine); ExecutionEngine *engine() const { return m_engine; } - void attachToAgent(DebuggerAgent *agent); - void detachFromAgent(); - DebuggerAgent *agent() const { return m_agent; } - - void gatherSources(int requestSequenceNr); void pause(); void resume(Speed speed); @@ -166,14 +172,10 @@ public: } QVector<StackFrame> stackTrace(int frameLimit = -1) const; - void collectArgumentsInContext(Collector *collector, int frameNr = 0, int scopeNr = 0); - void collectLocalsInContext(Collector *collector, int frameNr = 0, int scopeNr = 0); - bool collectThisInContext(Collector *collector, int frame = 0); - void collectThrownValue(Collector *collector); - void collectReturnedValue(Collector *collector) const; QVector<Heap::ExecutionContext::ContextType> getScopeTypes(int frame = 0) const; - void evaluateExpression(int frameNr, const QString &expression, Collector *resultsCollector); + Function *getFunction() const; + void runInEngine(Job *job); public: // compile-time interface void maybeBreakAtInstruction(); @@ -183,21 +185,19 @@ public: // execution hooks void leavingFunction(const ReturnedValue &retVal); void aboutToThrow(); -private: - Function *getFunction() const; +signals: + void sourcesCollected(QV4::Debugging::V4Debugger *self, const QStringList &sources, int seq); + void debuggerPaused(QV4::Debugging::V4Debugger *self, QV4::Debugging::PauseReason reason); +private: // requires lock to be held void pauseAndWait(PauseReason reason); - bool reallyHitTheBreakPoint(const QString &filename, int linenr); - - void runInEngine(Job *job); - void runInEngine_havingLock(Debugger::Job *job); + void runInEngine_havingLock(V4Debugger::Job *job); private: QV4::ExecutionEngine *m_engine; QV4::PersistentValue m_currentContext; - DebuggerAgent *m_agent; QMutex m_lock; QWaitCondition m_runningCondition; State m_state; @@ -214,54 +214,6 @@ private: QWaitCondition m_jobIsRunning; }; -class Q_QML_EXPORT DebuggerAgent : public QObject -{ - Q_OBJECT -public: - DebuggerAgent(): m_breakOnThrow(false) {} - ~DebuggerAgent(); - - void addDebugger(Debugger *debugger); - void removeDebugger(Debugger *debugger); - - void pause(Debugger *debugger) const; - void pauseAll() const; - void resumeAll() const; - int addBreakPoint(const QString &fileName, int lineNumber, bool enabled = true, const QString &condition = QString()); - void removeBreakPoint(int id); - void removeAllBreakPoints(); - void enableBreakPoint(int id, bool onoff); - QList<int> breakPointIds(const QString &fileName, int lineNumber) const; - - bool breakOnThrow() const { return m_breakOnThrow; } - void setBreakOnThrow(bool onoff); - - Q_INVOKABLE virtual void debuggerPaused(QV4::Debugging::Debugger *debugger, - QV4::Debugging::PauseReason reason) = 0; - Q_INVOKABLE virtual void sourcesCollected(QV4::Debugging::Debugger *debugger, - QStringList sources, int requestSequenceNr) = 0; - -protected: - QList<Debugger *> m_debuggers; - - struct BreakPoint { - QString fileName; - int lineNr; - bool enabled; - QString condition; - - BreakPoint(): lineNr(-1), enabled(false) {} - BreakPoint(const QString &fileName, int lineNr, bool enabled, const QString &condition) - : fileName(fileName), lineNr(lineNr), enabled(enabled), condition(condition) - {} - - bool isValid() const { return lineNr >= 0 && !fileName.isEmpty(); } - }; - - QHash<int, BreakPoint> m_breakPoints; - bool m_breakOnThrow; -}; - } // namespace Debugging } // namespace QV4 diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index e1282eeca9..2560f065cf 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -32,7 +32,7 @@ ****************************************************************************/ #include <qv4engine_p.h> #include <qv4context_p.h> -#include <qv4value_inl_p.h> +#include <qv4value_p.h> #include <qv4object_p.h> #include <qv4objectproto_p.h> #include <qv4objectiterator_p.h> @@ -48,7 +48,7 @@ #include <qv4regexp_p.h> #include <qv4variantobject_p.h> #include <qv4runtime_p.h> -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include <qv4argumentsobject_p.h> #include <qv4dateobject_p.h> #include <qv4jsonobject_p.h> @@ -59,7 +59,6 @@ #include "qv4executableallocator_p.h" #include "qv4sequenceobject_p.h" #include "qv4qobjectwrapper_p.h" -#include "qv4qmlextensions_p.h" #include "qv4memberdata_p.h" #include "qv4arraybuffer_p.h" #include "qv4dataview_p.h" @@ -197,9 +196,11 @@ QQmlEngine *ExecutionEngine::qmlEngine() const ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) : current(0) + , hasException(false) , memoryManager(new QV4::MemoryManager(this)) , executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) + , currentContext(0) , bumperPointerAllocator(new WTF::BumpPointerAllocator) , jsStack(new WTF::PageAllocation) , debugger(0) @@ -211,17 +212,13 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , m_engineId(engineSerial.fetchAndAddOrdered(1)) , regExpCache(0) , m_multiplyWrappedQObjects(0) - , m_qmlExtensions(0) { MemoryManager::GCBlocker gcBlocker(memoryManager); - exceptionValue = Encode::undefined(); - hasException = false; - if (!factory) { #ifdef V4_ENABLE_JIT - static const bool forceMoth = !qgetenv("QV4_FORCE_INTERPRETER").isEmpty(); + static const bool forceMoth = !qEnvironmentVariableIsEmpty("QV4_FORCE_INTERPRETER"); if (forceMoth) factory = new Moth::ISelFactory; else @@ -241,6 +238,13 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) jsStackBase = (Value *)jsStack->base(); jsStackTop = jsStackBase; + exceptionValue = jsAlloca(1); + globalObject = static_cast<Object *>(jsAlloca(1)); + jsObjects = jsAlloca(NJSObjects); + typedArrayPrototype = static_cast<Object *>(jsAlloca(NTypedArrayTypes)); + typedArrayCtors = static_cast<FunctionObject *>(jsAlloca(NTypedArrayTypes)); + jsStrings = jsAlloca(NJSStrings); + #ifdef V4_USE_VALGRIND VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, 2*JSStackLimit); #endif @@ -251,208 +255,240 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) if (!recheckCStackLimits()) qFatal("Fatal: Not enough stack space available for QML. Please increase the process stack size to more than %d KBytes.", MinimumStackSize); - Scope scope(this); - identifierTable = new IdentifierTable(this); classPool = new InternalClassPool; emptyClass = new (classPool) InternalClass(this); - id_empty = newIdentifier(QString()); - id_undefined = newIdentifier(QStringLiteral("undefined")); - id_null = newIdentifier(QStringLiteral("null")); - id_true = newIdentifier(QStringLiteral("true")); - id_false = newIdentifier(QStringLiteral("false")); - id_boolean = newIdentifier(QStringLiteral("boolean")); - id_number = newIdentifier(QStringLiteral("number")); - id_string = newIdentifier(QStringLiteral("string")); - id_object = newIdentifier(QStringLiteral("object")); - id_function = newIdentifier(QStringLiteral("function")); - id_length = newIdentifier(QStringLiteral("length")); - id_prototype = newIdentifier(QStringLiteral("prototype")); - id_constructor = newIdentifier(QStringLiteral("constructor")); - id_arguments = newIdentifier(QStringLiteral("arguments")); - id_caller = newIdentifier(QStringLiteral("caller")); - id_callee = newIdentifier(QStringLiteral("callee")); - id_this = newIdentifier(QStringLiteral("this")); - id___proto__ = newIdentifier(QStringLiteral("__proto__")); - id_enumerable = newIdentifier(QStringLiteral("enumerable")); - id_configurable = newIdentifier(QStringLiteral("configurable")); - id_writable = newIdentifier(QStringLiteral("writable")); - id_value = newIdentifier(QStringLiteral("value")); - id_get = newIdentifier(QStringLiteral("get")); - id_set = newIdentifier(QStringLiteral("set")); - id_eval = newIdentifier(QStringLiteral("eval")); - id_uintMax = newIdentifier(QStringLiteral("4294967295")); - id_name = newIdentifier(QStringLiteral("name")); - id_index = newIdentifier(QStringLiteral("index")); - id_input = newIdentifier(QStringLiteral("input")); - id_toString = newIdentifier(QStringLiteral("toString")); - id_destroy = newIdentifier(QStringLiteral("destroy")); - id_valueOf = newIdentifier(QStringLiteral("valueOf")); - id_byteLength = newIdentifier(QStringLiteral("byteLength")); - id_byteOffset = newIdentifier(QStringLiteral("byteOffset")); - id_buffer = newIdentifier(QStringLiteral("buffer")); - id_lastIndex = newIdentifier(QStringLiteral("lastIndex")); - - objectPrototype = memoryManager->alloc<ObjectPrototype>(emptyClass, (QV4::Object *)0); - - arrayClass = emptyClass->addMember(id_length, Attr_NotConfigurable|Attr_NotEnumerable); - arrayPrototype = memoryManager->alloc<ArrayPrototype>(arrayClass, objectPrototype.asObject()); - - InternalClass *argsClass = emptyClass->addMember(id_length, Attr_NotEnumerable); - argumentsObjectClass = argsClass->addMember(id_callee, Attr_Data|Attr_NotEnumerable); - strictArgumentsObjectClass = argsClass->addMember(id_callee, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - strictArgumentsObjectClass = strictArgumentsObjectClass->addMember(id_caller, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - - m_globalObject = newObject(); - Q_ASSERT(globalObject()->d()->vtable); + jsStrings[String_Empty] = newIdentifier(QString()); + jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined")); + jsStrings[String_null] = newIdentifier(QStringLiteral("null")); + jsStrings[String_true] = newIdentifier(QStringLiteral("true")); + jsStrings[String_false] = newIdentifier(QStringLiteral("false")); + jsStrings[String_boolean] = newIdentifier(QStringLiteral("boolean")); + jsStrings[String_number] = newIdentifier(QStringLiteral("number")); + jsStrings[String_string] = newIdentifier(QStringLiteral("string")); + jsStrings[String_object] = newIdentifier(QStringLiteral("object")); + jsStrings[String_function] = newIdentifier(QStringLiteral("function")); + jsStrings[String_length] = newIdentifier(QStringLiteral("length")); + jsStrings[String_prototype] = newIdentifier(QStringLiteral("prototype")); + jsStrings[String_constructor] = newIdentifier(QStringLiteral("constructor")); + jsStrings[String_arguments] = newIdentifier(QStringLiteral("arguments")); + jsStrings[String_caller] = newIdentifier(QStringLiteral("caller")); + jsStrings[String_callee] = newIdentifier(QStringLiteral("callee")); + jsStrings[String_this] = newIdentifier(QStringLiteral("this")); + jsStrings[String___proto__] = newIdentifier(QStringLiteral("__proto__")); + jsStrings[String_enumerable] = newIdentifier(QStringLiteral("enumerable")); + jsStrings[String_configurable] = newIdentifier(QStringLiteral("configurable")); + jsStrings[String_writable] = newIdentifier(QStringLiteral("writable")); + jsStrings[String_value] = newIdentifier(QStringLiteral("value")); + jsStrings[String_get] = newIdentifier(QStringLiteral("get")); + jsStrings[String_set] = newIdentifier(QStringLiteral("set")); + jsStrings[String_eval] = newIdentifier(QStringLiteral("eval")); + jsStrings[String_uintMax] = newIdentifier(QStringLiteral("4294967295")); + jsStrings[String_name] = newIdentifier(QStringLiteral("name")); + jsStrings[String_index] = newIdentifier(QStringLiteral("index")); + jsStrings[String_input] = newIdentifier(QStringLiteral("input")); + jsStrings[String_toString] = newIdentifier(QStringLiteral("toString")); + jsStrings[String_destroy] = newIdentifier(QStringLiteral("destroy")); + jsStrings[String_valueOf] = newIdentifier(QStringLiteral("valueOf")); + jsStrings[String_byteLength] = newIdentifier(QStringLiteral("byteLength")); + jsStrings[String_byteOffset] = newIdentifier(QStringLiteral("byteOffset")); + jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer")); + jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex")); + + jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(emptyClass); + + arrayClass = emptyClass->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); + jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(arrayClass, objectPrototype()); + + InternalClass *argsClass = emptyClass->addMember(id_length(), Attr_NotEnumerable); + argumentsObjectClass = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable); + strictArgumentsObjectClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + strictArgumentsObjectClass = strictArgumentsObjectClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + + *static_cast<Value *>(globalObject) = newObject(); + Q_ASSERT(globalObject->d()->vtable()); initRootContext(); - stringPrototype = memoryManager->alloc<StringPrototype>(emptyClass, objectPrototype.asObject()); - numberPrototype = memoryManager->alloc<NumberPrototype>(emptyClass, objectPrototype.asObject()); - booleanPrototype = memoryManager->alloc<BooleanPrototype>(emptyClass, objectPrototype.asObject()); - datePrototype = memoryManager->alloc<DatePrototype>(emptyClass, objectPrototype.asObject()); + stringClass = emptyClass->addMember(id_length(), Attr_ReadOnly); + Q_ASSERT(stringClass->find(id_length()) == Heap::StringObject::LengthPropertyIndex); + jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(stringClass, objectPrototype()); + jsObjects[NumberProto] = memoryManager->allocObject<NumberPrototype>(emptyClass, objectPrototype()); + jsObjects[BooleanProto] = memoryManager->allocObject<BooleanPrototype>(emptyClass, objectPrototype()); + jsObjects[DateProto] = memoryManager->allocObject<DatePrototype>(emptyClass, objectPrototype()); uint index; - InternalClass *functionProtoClass = emptyClass->addMember(id_prototype, Attr_NotEnumerable, &index); + InternalClass *functionProtoClass = emptyClass->addMember(id_prototype(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); - functionPrototype = memoryManager->alloc<FunctionPrototype>(functionProtoClass, objectPrototype.asObject()); - functionClass = emptyClass->addMember(id_prototype, Attr_NotEnumerable|Attr_NotConfigurable, &index); + jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(functionProtoClass, objectPrototype()); + functionClass = emptyClass->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); - simpleScriptFunctionClass = functionClass->addMember(id_name, Attr_ReadOnly, &index); + simpleScriptFunctionClass = functionClass->addMember(id_name(), Attr_ReadOnly, &index); Q_ASSERT(index == Heap::SimpleScriptFunction::Index_Name); - simpleScriptFunctionClass = simpleScriptFunctionClass->addMember(id_length, Attr_ReadOnly, &index); + simpleScriptFunctionClass = simpleScriptFunctionClass->addMember(id_length(), Attr_ReadOnly, &index); Q_ASSERT(index == Heap::SimpleScriptFunction::Index_Length); - protoClass = emptyClass->addMember(id_constructor, Attr_NotEnumerable, &index); + protoClass = emptyClass->addMember(id_constructor(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_ProtoConstructor); - regExpPrototype = memoryManager->alloc<RegExpPrototype>(this); - regExpExecArrayClass = arrayClass->addMember(id_index, Attr_Data, &index); + Scope scope(this); + ScopedString str(scope); + regExpObjectClass = emptyClass->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index); + Q_ASSERT(index == RegExpObject::Index_LastIndex); + regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index); + Q_ASSERT(index == RegExpObject::Index_Source); + regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index); + Q_ASSERT(index == RegExpObject::Index_Global); + regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index); + Q_ASSERT(index == RegExpObject::Index_IgnoreCase); + regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index); + Q_ASSERT(index == RegExpObject::Index_Multiline); + + jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(regExpObjectClass, objectPrototype()); + regExpExecArrayClass = arrayClass->addMember(id_index(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayIndex); - regExpExecArrayClass = regExpExecArrayClass->addMember(id_input, Attr_Data, &index); + regExpExecArrayClass = regExpExecArrayClass->addMember(id_input(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayInput); - errorPrototype = memoryManager->alloc<ErrorPrototype>(emptyClass, objectPrototype.asObject()); - evalErrorPrototype = memoryManager->alloc<EvalErrorPrototype>(emptyClass, errorPrototype.asObject()); - rangeErrorPrototype = memoryManager->alloc<RangeErrorPrototype>(emptyClass, errorPrototype.asObject()); - referenceErrorPrototype = memoryManager->alloc<ReferenceErrorPrototype>(emptyClass, errorPrototype.asObject()); - syntaxErrorPrototype = memoryManager->alloc<SyntaxErrorPrototype>(emptyClass, errorPrototype.asObject()); - typeErrorPrototype = memoryManager->alloc<TypeErrorPrototype>(emptyClass, errorPrototype.asObject()); - uRIErrorPrototype = memoryManager->alloc<URIErrorPrototype>(emptyClass, errorPrototype.asObject()); - - variantPrototype = memoryManager->alloc<VariantPrototype>(emptyClass, objectPrototype.asObject()); - Q_ASSERT(variantPrototype.asObject()->prototype() == objectPrototype.asObject()->d()); - - sequencePrototype = ScopedValue(scope, memoryManager->alloc<SequencePrototype>(arrayClass, arrayPrototype.asObject())); - - ScopedContext global(scope, rootContext()); - objectCtor = memoryManager->alloc<ObjectCtor>(global); - stringCtor = memoryManager->alloc<StringCtor>(global); - numberCtor = memoryManager->alloc<NumberCtor>(global); - booleanCtor = memoryManager->alloc<BooleanCtor>(global); - arrayCtor = memoryManager->alloc<ArrayCtor>(global); - functionCtor = memoryManager->alloc<FunctionCtor>(global); - dateCtor = memoryManager->alloc<DateCtor>(global); - regExpCtor = memoryManager->alloc<RegExpCtor>(global); - errorCtor = memoryManager->alloc<ErrorCtor>(global); - evalErrorCtor = memoryManager->alloc<EvalErrorCtor>(global); - rangeErrorCtor = memoryManager->alloc<RangeErrorCtor>(global); - referenceErrorCtor = memoryManager->alloc<ReferenceErrorCtor>(global); - syntaxErrorCtor = memoryManager->alloc<SyntaxErrorCtor>(global); - typeErrorCtor = memoryManager->alloc<TypeErrorCtor>(global); - uRIErrorCtor = memoryManager->alloc<URIErrorCtor>(global); - - static_cast<ObjectPrototype *>(objectPrototype.asObject())->init(this, objectCtor.asObject()); - static_cast<StringPrototype *>(stringPrototype.asObject())->init(this, stringCtor.asObject()); - static_cast<NumberPrototype *>(numberPrototype.asObject())->init(this, numberCtor.asObject()); - static_cast<BooleanPrototype *>(booleanPrototype.asObject())->init(this, booleanCtor.asObject()); - static_cast<ArrayPrototype *>(arrayPrototype.asObject())->init(this, arrayCtor.asObject()); - static_cast<DatePrototype *>(datePrototype.asObject())->init(this, dateCtor.asObject()); - static_cast<FunctionPrototype *>(functionPrototype.asObject())->init(this, functionCtor.asObject()); - static_cast<RegExpPrototype *>(regExpPrototype.asObject())->init(this, regExpCtor.asObject()); - static_cast<ErrorPrototype *>(errorPrototype.asObject())->init(this, errorCtor.asObject()); - static_cast<EvalErrorPrototype *>(evalErrorPrototype.asObject())->init(this, evalErrorCtor.asObject()); - static_cast<RangeErrorPrototype *>(rangeErrorPrototype.asObject())->init(this, rangeErrorCtor.asObject()); - static_cast<ReferenceErrorPrototype *>(referenceErrorPrototype.asObject())->init(this, referenceErrorCtor.asObject()); - static_cast<SyntaxErrorPrototype *>(syntaxErrorPrototype.asObject())->init(this, syntaxErrorCtor.asObject()); - static_cast<TypeErrorPrototype *>(typeErrorPrototype.asObject())->init(this, typeErrorCtor.asObject()); - static_cast<URIErrorPrototype *>(uRIErrorPrototype.asObject())->init(this, uRIErrorCtor.asObject()); - - static_cast<VariantPrototype *>(variantPrototype.asObject())->init(); - sequencePrototype.cast<SequencePrototype>()->init(); + errorClass = emptyClass->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index); + Q_ASSERT(index == ErrorObject::Index_Stack); + errorClass = errorClass->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index); + Q_ASSERT(index == ErrorObject::Index_FileName); + errorClass = errorClass->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index); + Q_ASSERT(index == ErrorObject::Index_LineNumber); + errorClassWithMessage = errorClass->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); + Q_ASSERT(index == ErrorObject::Index_Message); + errorProtoClass = emptyClass->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index); + Q_ASSERT(index == ErrorPrototype::Index_Constructor); + errorProtoClass = errorProtoClass->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); + Q_ASSERT(index == ErrorPrototype::Index_Message); + errorProtoClass = errorProtoClass->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index); + Q_ASSERT(index == ErrorPrototype::Index_Name); + + jsObjects[GetStack_Function] = BuiltinFunction::create(rootContext(), str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack); + getStackFunction()->defineReadonlyProperty(id_length(), Primitive::fromInt32(0)); + + jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(errorProtoClass, objectPrototype()); + jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(errorProtoClass, errorPrototype()); + jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(errorProtoClass, errorPrototype()); + jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(errorProtoClass, errorPrototype()); + jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(errorProtoClass, errorPrototype()); + jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(errorProtoClass, errorPrototype()); + jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(errorProtoClass, errorPrototype()); + + jsObjects[VariantProto] = memoryManager->allocObject<VariantPrototype>(emptyClass, objectPrototype()); + Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d()); + + jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(arrayClass, arrayPrototype())); + + ExecutionContext *global = rootContext(); + jsObjects[Object_Ctor] = memoryManager->allocObject<ObjectCtor>(global); + jsObjects[String_Ctor] = memoryManager->allocObject<StringCtor>(global); + jsObjects[Number_Ctor] = memoryManager->allocObject<NumberCtor>(global); + jsObjects[Boolean_Ctor] = memoryManager->allocObject<BooleanCtor>(global); + jsObjects[Array_Ctor] = memoryManager->allocObject<ArrayCtor>(global); + jsObjects[Function_Ctor] = memoryManager->allocObject<FunctionCtor>(global); + jsObjects[Date_Ctor] = memoryManager->allocObject<DateCtor>(global); + jsObjects[RegExp_Ctor] = memoryManager->allocObject<RegExpCtor>(global); + jsObjects[Error_Ctor] = memoryManager->allocObject<ErrorCtor>(global); + jsObjects[EvalError_Ctor] = memoryManager->allocObject<EvalErrorCtor>(global); + jsObjects[RangeError_Ctor] = memoryManager->allocObject<RangeErrorCtor>(global); + jsObjects[ReferenceError_Ctor] = memoryManager->allocObject<ReferenceErrorCtor>(global); + jsObjects[SyntaxError_Ctor] = memoryManager->allocObject<SyntaxErrorCtor>(global); + jsObjects[TypeError_Ctor] = memoryManager->allocObject<TypeErrorCtor>(global); + jsObjects[URIError_Ctor] = memoryManager->allocObject<URIErrorCtor>(global); + + static_cast<ObjectPrototype *>(objectPrototype())->init(this, objectCtor()); + static_cast<StringPrototype *>(stringPrototype())->init(this, stringCtor()); + static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor()); + static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor()); + static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor()); + static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor()); + static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor()); + static_cast<RegExpPrototype *>(regExpPrototype())->init(this, regExpCtor()); + static_cast<ErrorPrototype *>(errorPrototype())->init(this, errorCtor()); + static_cast<EvalErrorPrototype *>(evalErrorPrototype())->init(this, evalErrorCtor()); + static_cast<RangeErrorPrototype *>(rangeErrorPrototype())->init(this, rangeErrorCtor()); + static_cast<ReferenceErrorPrototype *>(referenceErrorPrototype())->init(this, referenceErrorCtor()); + static_cast<SyntaxErrorPrototype *>(syntaxErrorPrototype())->init(this, syntaxErrorCtor()); + static_cast<TypeErrorPrototype *>(typeErrorPrototype())->init(this, typeErrorCtor()); + static_cast<URIErrorPrototype *>(uRIErrorPrototype())->init(this, uRIErrorCtor()); + + static_cast<VariantPrototype *>(variantPrototype())->init(); + sequencePrototype()->cast<SequencePrototype>()->init(); // typed arrays - arrayBufferCtor = memoryManager->alloc<ArrayBufferCtor>(global); - arrayBufferPrototype = memoryManager->alloc<ArrayBufferPrototype>(emptyClass, objectPrototype.asObject()); - static_cast<ArrayBufferPrototype *>(arrayBufferPrototype.asObject())->init(this, arrayBufferCtor.asObject()); + jsObjects[ArrayBuffer_Ctor] = memoryManager->allocObject<ArrayBufferCtor>(global); + jsObjects[ArrayBufferProto] = memoryManager->allocObject<ArrayBufferPrototype>(); + static_cast<ArrayBufferPrototype *>(arrayBufferPrototype())->init(this, arrayBufferCtor()); - dataViewCtor = memoryManager->alloc<DataViewCtor>(global); - dataViewPrototype = memoryManager->alloc<DataViewPrototype>(emptyClass, objectPrototype.asObject()); - static_cast<DataViewPrototype *>(dataViewPrototype.asObject())->init(this, dataViewCtor.asObject()); + jsObjects[DataView_Ctor] = memoryManager->allocObject<DataViewCtor>(global); + jsObjects[DataViewProto] = memoryManager->allocObject<DataViewPrototype>(); + static_cast<DataViewPrototype *>(dataViewPrototype())->init(this, dataViewCtor()); + jsObjects[ValueTypeProto] = (Heap::Base *) 0; + jsObjects[SignalHandlerProto] = (Heap::Base *) 0; for (int i = 0; i < Heap::TypedArray::NTypes; ++i) { - typedArrayCtors[i] = memoryManager->alloc<TypedArrayCtor>(global, Heap::TypedArray::Type(i)); - typedArrayPrototype[i] = memoryManager->alloc<TypedArrayPrototype>(this, Heap::TypedArray::Type(i)); - typedArrayPrototype[i].as<TypedArrayPrototype>()->init(this, static_cast<TypedArrayCtor *>(typedArrayCtors[i].asObject())); + static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocObject<TypedArrayCtor>(global, Heap::TypedArray::Type(i)); + static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocObject<TypedArrayPrototype>(Heap::TypedArray::Type(i)); + typedArrayPrototype[i].as<TypedArrayPrototype>()->init(this, static_cast<TypedArrayCtor *>(typedArrayCtors[i].as<Object>())); } // // set up the global object // - rootContext()->global = globalObject()->d(); - rootContext()->callData->thisObject = globalObject(); - Q_ASSERT(globalObject()->d()->vtable); - - globalObject()->defineDefaultProperty(QStringLiteral("Object"), objectCtor); - globalObject()->defineDefaultProperty(QStringLiteral("String"), stringCtor); - globalObject()->defineDefaultProperty(QStringLiteral("Number"), numberCtor); - globalObject()->defineDefaultProperty(QStringLiteral("Boolean"), booleanCtor); - globalObject()->defineDefaultProperty(QStringLiteral("Array"), arrayCtor); - globalObject()->defineDefaultProperty(QStringLiteral("Function"), functionCtor); - globalObject()->defineDefaultProperty(QStringLiteral("Date"), dateCtor); - globalObject()->defineDefaultProperty(QStringLiteral("RegExp"), regExpCtor); - globalObject()->defineDefaultProperty(QStringLiteral("Error"), errorCtor); - globalObject()->defineDefaultProperty(QStringLiteral("EvalError"), evalErrorCtor); - globalObject()->defineDefaultProperty(QStringLiteral("RangeError"), rangeErrorCtor); - globalObject()->defineDefaultProperty(QStringLiteral("ReferenceError"), referenceErrorCtor); - globalObject()->defineDefaultProperty(QStringLiteral("SyntaxError"), syntaxErrorCtor); - globalObject()->defineDefaultProperty(QStringLiteral("TypeError"), typeErrorCtor); - globalObject()->defineDefaultProperty(QStringLiteral("URIError"), uRIErrorCtor); - - globalObject()->defineDefaultProperty(QStringLiteral("ArrayBuffer"), arrayBufferCtor); - globalObject()->defineDefaultProperty(QStringLiteral("DataView"), dataViewCtor); - ScopedString str(scope); + rootContext()->d()->global = globalObject->d(); + rootContext()->d()->callData->thisObject = globalObject; + Q_ASSERT(globalObject->d()->vtable()); + + globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor()); + globalObject->defineDefaultProperty(QStringLiteral("String"), *stringCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Boolean"), *booleanCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Array"), *arrayCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Function"), *functionCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Date"), *dateCtor()); + globalObject->defineDefaultProperty(QStringLiteral("RegExp"), *regExpCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Error"), *errorCtor()); + globalObject->defineDefaultProperty(QStringLiteral("EvalError"), *evalErrorCtor()); + globalObject->defineDefaultProperty(QStringLiteral("RangeError"), *rangeErrorCtor()); + globalObject->defineDefaultProperty(QStringLiteral("ReferenceError"), *referenceErrorCtor()); + globalObject->defineDefaultProperty(QStringLiteral("SyntaxError"), *syntaxErrorCtor()); + globalObject->defineDefaultProperty(QStringLiteral("TypeError"), *typeErrorCtor()); + globalObject->defineDefaultProperty(QStringLiteral("URIError"), *uRIErrorCtor()); + + globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor()); + globalObject->defineDefaultProperty(QStringLiteral("DataView"), *dataViewCtor()); for (int i = 0; i < Heap::TypedArray::NTypes; ++i) - globalObject()->defineDefaultProperty((str = typedArrayCtors[i].asFunctionObject()->name())->toQString(), typedArrayCtors[i]); + globalObject->defineDefaultProperty((str = typedArrayCtors[i].as<FunctionObject>()->name())->toQString(), typedArrayCtors[i]); ScopedObject o(scope); - globalObject()->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->alloc<MathObject>(this))); - globalObject()->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->alloc<JsonObject>(this))); + globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocObject<MathObject>())); + globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocObject<JsonObject>())); - globalObject()->defineReadonlyProperty(QStringLiteral("undefined"), Primitive::undefinedValue()); - globalObject()->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(std::numeric_limits<double>::quiet_NaN())); - globalObject()->defineReadonlyProperty(QStringLiteral("Infinity"), Primitive::fromDouble(Q_INFINITY)); + globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Primitive::undefinedValue()); + globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(std::numeric_limits<double>::quiet_NaN())); + globalObject->defineReadonlyProperty(QStringLiteral("Infinity"), Primitive::fromDouble(Q_INFINITY)); - evalFunction = memoryManager->alloc<EvalFunction>(global); - globalObject()->defineDefaultProperty(QStringLiteral("eval"), (o = evalFunction)); + jsObjects[Eval_Function] = memoryManager->allocObject<EvalFunction>(global); + globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction()); - globalObject()->defineDefaultProperty(QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); - globalObject()->defineDefaultProperty(QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); - globalObject()->defineDefaultProperty(QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); - globalObject()->defineDefaultProperty(QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); - globalObject()->defineDefaultProperty(QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); - globalObject()->defineDefaultProperty(QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1); - globalObject()->defineDefaultProperty(QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1); - globalObject()->defineDefaultProperty(QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1); - globalObject()->defineDefaultProperty(QStringLiteral("escape"), GlobalFunctions::method_escape, 1); - globalObject()->defineDefaultProperty(QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); + globalObject->defineDefaultProperty(QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); + globalObject->defineDefaultProperty(QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); + globalObject->defineDefaultProperty(QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); + globalObject->defineDefaultProperty(QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); + globalObject->defineDefaultProperty(QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); + globalObject->defineDefaultProperty(QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1); + globalObject->defineDefaultProperty(QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1); + globalObject->defineDefaultProperty(QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1); + globalObject->defineDefaultProperty(QStringLiteral("escape"), GlobalFunctions::method_escape, 1); + globalObject->defineDefaultProperty(QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); ScopedString name(scope, newString(QStringLiteral("thrower"))); - thrower = BuiltinFunction::create(global, name, ::throwTypeError); + jsObjects[ThrowerObject] = BuiltinFunction::create(global, name, ::throwTypeError); } ExecutionEngine::~ExecutionEngine() @@ -471,7 +507,6 @@ ExecutionEngine::~ExecutionEngine() foreach (QV4::CompiledData::CompilationUnit *unit, remainingUnits) unit->unlink(); - delete m_qmlExtensions; emptyClass->destroy(); delete classPool; delete bumperPointerAllocator; @@ -483,11 +518,10 @@ ExecutionEngine::~ExecutionEngine() delete [] argumentsAccessors; } -void ExecutionEngine::enableDebugger() +void ExecutionEngine::setDebugger(Debugging::Debugger *debugger_) { Q_ASSERT(!debugger); - debugger = new Debugging::Debugger(this); - iselFactory.reset(new Moth::ISelFactory); + debugger = debugger_; } void ExecutionEngine::enableProfiler() @@ -502,12 +536,15 @@ void ExecutionEngine::initRootContext() Scoped<GlobalContext> r(scope, memoryManager->allocManaged<GlobalContext>(sizeof(GlobalContext::Data) + sizeof(CallData))); new (r->d()) GlobalContext::Data(this); r->d()->callData = reinterpret_cast<CallData *>(r->d() + 1); - r->d()->callData->tag = QV4::Value::_Integer_Type; + r->d()->callData->tag = QV4::Value::Integer_Type_Internal; r->d()->callData->argc = 0; - r->d()->callData->thisObject = globalObject(); + r->d()->callData->thisObject = globalObject; r->d()->callData->args[0] = Encode::undefined(); + jsObjects[RootContext] = r; + jsObjects[IntegerNull] = Encode((int)0); - m_rootContext = r->d(); + currentContext = static_cast<ExecutionContext *>(jsObjects + RootContext); + current = currentContext->d(); } InternalClass *ExecutionEngine::newClass(const InternalClass &other) @@ -515,29 +552,31 @@ InternalClass *ExecutionEngine::newClass(const InternalClass &other) return new (classPool) InternalClass(other); } -Heap::ExecutionContext *ExecutionEngine::pushGlobalContext() +ExecutionContext *ExecutionEngine::pushGlobalContext() { - Scope scope(this); - Scoped<GlobalContext> g(scope, memoryManager->alloc<GlobalContext>(this)); - g->d()->callData = rootContext()->callData; + pushContext(rootContext()->d()); + + Q_ASSERT(current == rootContext()->d()); + return currentContext; +} - Q_ASSERT(currentContext() == g->d()); - return g->d(); +ExecutionContext *ExecutionEngine::parentContext(ExecutionContext *context) const +{ + Value *offset = static_cast<Value *>(context) + 1; + Q_ASSERT(offset->isInteger()); + int o = offset->integerValue(); + return o ? context - o : 0; } Heap::Object *ExecutionEngine::newObject() { - Scope scope(this); - ScopedObject object(scope, memoryManager->alloc<Object>(this)); - return object->d(); + return memoryManager->allocObject<Object>(); } Heap::Object *ExecutionEngine::newObject(InternalClass *internalClass, QV4::Object *prototype) { - Scope scope(this); - ScopedObject object(scope, memoryManager->alloc<Object>(internalClass, prototype)); - return object->d(); + return memoryManager->allocObject<Object>(internalClass, prototype); } Heap::String *ExecutionEngine::newString(const QString &s) @@ -551,31 +590,25 @@ Heap::String *ExecutionEngine::newIdentifier(const QString &text) return identifierTable->insertString(text); } -Heap::Object *ExecutionEngine::newStringObject(const Value &value) +Heap::Object *ExecutionEngine::newStringObject(const String *string) { - Scope scope(this); - Scoped<StringObject> object(scope, memoryManager->alloc<StringObject>(this, value)); - return object->d(); + return memoryManager->allocObject<StringObject>(string); } Heap::Object *ExecutionEngine::newNumberObject(double value) { - Scope scope(this); - Scoped<NumberObject> object(scope, memoryManager->alloc<NumberObject>(this, value)); - return object->d(); + return memoryManager->allocObject<NumberObject>(value); } Heap::Object *ExecutionEngine::newBooleanObject(bool b) { - Scope scope(this); - ScopedObject object(scope, memoryManager->alloc<BooleanObject>(this, b)); - return object->d(); + return memoryManager->allocObject<BooleanObject>(b); } Heap::ArrayObject *ExecutionEngine::newArrayObject(int count) { Scope scope(this); - ScopedArrayObject object(scope, memoryManager->alloc<ArrayObject>(this)); + ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>()); if (count) { if (count < 0x1000) @@ -585,39 +618,60 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(int count) return object->d(); } +Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int length) +{ + Scope scope(this); + ScopedArrayObject a(scope, memoryManager->allocObject<ArrayObject>()); + + if (length) { + size_t size = sizeof(Heap::ArrayData) + (length-1)*sizeof(Value); + Heap::SimpleArrayData *d = scope.engine->memoryManager->allocManaged<SimpleArrayData>(size); + new (d) Heap::SimpleArrayData; + d->alloc = length; + d->type = Heap::ArrayData::Simple; + d->offset = 0; + d->len = length; + memcpy(&d->arrayData, values, length*sizeof(Value)); + a->d()->arrayData = d; + a->setArrayLengthUnchecked(length); + } + return a->d(); +} + Heap::ArrayObject *ExecutionEngine::newArrayObject(const QStringList &list) { Scope scope(this); - ScopedArrayObject object(scope, memoryManager->alloc<ArrayObject>(this, list)); + ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>(list)); return object->d(); } -Heap::ArrayObject *ExecutionEngine::newArrayObject(InternalClass *ic, Object *prototype) +Heap::ArrayObject *ExecutionEngine::newArrayObject(InternalClass *internalClass, Object *prototype) { Scope scope(this); - ScopedArrayObject object(scope, memoryManager->alloc<ArrayObject>(ic, prototype)); + ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>(internalClass, prototype)); return object->d(); } Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(const QByteArray &array) { - Scope scope(this); - Scoped<ArrayBuffer> object(scope, memoryManager->alloc<ArrayBuffer>(this, array)); - return object->d(); + return memoryManager->allocObject<ArrayBuffer>(array); +} + +Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(size_t length) +{ + return memoryManager->allocObject<ArrayBuffer>(length); } Heap::DateObject *ExecutionEngine::newDateObject(const Value &value) { - Scope scope(this); - Scoped<DateObject> object(scope, memoryManager->alloc<DateObject>(this, value)); - return object->d(); + return memoryManager->allocObject<DateObject>(value); } Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dt) { Scope scope(this); - Scoped<DateObject> object(scope, memoryManager->alloc<DateObject>(this, dt)); + Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(dt)); return object->d(); } @@ -638,97 +692,75 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re, bool global) { - Scope scope(this); - Scoped<RegExpObject> object(scope, memoryManager->alloc<RegExpObject>(this, re, global)); - return object->d(); + return memoryManager->allocObject<RegExpObject>(re, global); } Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re) { - Scope scope(this); - Scoped<RegExpObject> object(scope, memoryManager->alloc<RegExpObject>(this, re)); - return object->d(); + return memoryManager->allocObject<RegExpObject>(re); } Heap::Object *ExecutionEngine::newErrorObject(const Value &value) { - Scope scope(this); - ScopedObject object(scope, memoryManager->alloc<ErrorObject>(emptyClass, errorPrototype.asObject(), value)); - return object->d(); + return ErrorObject::create<ErrorObject>(this, value); } Heap::Object *ExecutionEngine::newSyntaxErrorObject(const QString &message) { - Scope scope(this); - ScopedString s(scope, newString(message)); - ScopedObject error(scope, memoryManager->alloc<SyntaxErrorObject>(this, s)); - return error->d(); + return ErrorObject::create<SyntaxErrorObject>(this, message); } Heap::Object *ExecutionEngine::newSyntaxErrorObject(const QString &message, const QString &fileName, int line, int column) { - Scope scope(this); - ScopedObject error(scope, memoryManager->alloc<SyntaxErrorObject>(this, message, fileName, line, column)); - return error->d(); + return ErrorObject::create<SyntaxErrorObject>(this, message, fileName, line, column); } Heap::Object *ExecutionEngine::newReferenceErrorObject(const QString &message) { - Scope scope(this); - ScopedObject o(scope, memoryManager->alloc<ReferenceErrorObject>(this, message)); - return o->d(); + return ErrorObject::create<ReferenceErrorObject>(this, message); } -Heap::Object *ExecutionEngine::newReferenceErrorObject(const QString &message, const QString &fileName, int lineNumber, int columnNumber) +Heap::Object *ExecutionEngine::newReferenceErrorObject(const QString &message, const QString &fileName, int line, int column) { - Scope scope(this); - ScopedObject o(scope, memoryManager->alloc<ReferenceErrorObject>(this, message, fileName, lineNumber, columnNumber)); - return o->d(); + return ErrorObject::create<ReferenceErrorObject>(this, message, fileName, line, column); } Heap::Object *ExecutionEngine::newTypeErrorObject(const QString &message) { - Scope scope(this); - ScopedObject o(scope, memoryManager->alloc<TypeErrorObject>(this, message)); - return o->d(); + return ErrorObject::create<TypeErrorObject>(this, message); } Heap::Object *ExecutionEngine::newRangeErrorObject(const QString &message) { - Scope scope(this); - ScopedObject o(scope, memoryManager->alloc<RangeErrorObject>(this, message)); - return o->d(); + return ErrorObject::create<RangeErrorObject>(this, message); } Heap::Object *ExecutionEngine::newURIErrorObject(const Value &message) { - Scope scope(this); - ScopedObject o(scope, memoryManager->alloc<URIErrorObject>(this, message)); - return o->d(); + return ErrorObject::create<URIErrorObject>(this, message); } Heap::Object *ExecutionEngine::newVariantObject(const QVariant &v) { - Scope scope(this); - ScopedObject o(scope, memoryManager->alloc<VariantObject>(this, v)); - return o->d(); + return memoryManager->allocObject<VariantObject>(v); } Heap::Object *ExecutionEngine::newForEachIteratorObject(Object *o) { Scope scope(this); - ScopedObject obj(scope, memoryManager->alloc<ForEachIteratorObject>(this, o)); + ScopedObject obj(scope, memoryManager->allocObject<ForEachIteratorObject>(o)); return obj->d(); } -Heap::Object *ExecutionEngine::qmlContextObject() const +Heap::QmlContext *ExecutionEngine::qmlContext() const { - Heap::ExecutionContext *ctx = currentContext(); + Heap::ExecutionContext *ctx = current; + // get the correct context when we're within a builtin function if (ctx->type == Heap::ExecutionContext::Type_SimpleCallContext && !ctx->outer) - ctx = ctx->parent; + ctx = parentContext(currentContext)->d(); if (!ctx->outer) return 0; @@ -740,8 +772,46 @@ Heap::Object *ExecutionEngine::qmlContextObject() const if (ctx->type != Heap::ExecutionContext::Type_QmlContext) return 0; - Q_ASSERT(static_cast<Heap::CallContext *>(ctx)->activation); - return static_cast<Heap::CallContext *>(ctx)->activation; + return static_cast<Heap::QmlContext *>(ctx); +} + +QObject *ExecutionEngine::qmlScopeObject() const +{ + Heap::QmlContext *ctx = qmlContext(); + if (!ctx) + return 0; + + return ctx->qml->scopeObject; +} + +ReturnedValue ExecutionEngine::qmlSingletonWrapper(String *name) +{ + QQmlContextData *ctx = callingQmlContext(); + if (!ctx->imports) + return Encode::undefined(); + // Search for attached properties, enums and imported scripts + QQmlTypeNameCache::Result r = ctx->imports->query(name); + + Q_ASSERT(r.isValid()); + Q_ASSERT(r.type); + Q_ASSERT(r.type->isSingleton()); + + QQmlType::SingletonInstanceInfo *siinfo = r.type->singletonInstanceInfo(); + QQmlEngine *e = qmlEngine(); + siinfo->init(e); + + if (QObject *qobjectSingleton = siinfo->qobjectApi(e)) + return QV4::QObjectWrapper::wrap(this, qobjectSingleton); + return QJSValuePrivate::convertedToValue(this, siinfo->scriptApi(e)); +} + +QQmlContextData *ExecutionEngine::callingQmlContext() const +{ + Heap::QmlContext *ctx = qmlContext(); + if (!ctx) + return 0; + + return ctx->qml->context.contextData(); } QVector<StackFrame> ExecutionEngine::stackTrace(int frameLimit) const @@ -750,7 +820,7 @@ QVector<StackFrame> ExecutionEngine::stackTrace(int frameLimit) const ScopedString name(scope); QVector<StackFrame> stack; - ScopedContext c(scope, currentContext()); + ExecutionContext *c = currentContext; ScopedFunctionObject function(scope); while (c && frameLimit) { function = c->getFunctionObject(); @@ -770,14 +840,14 @@ QVector<StackFrame> ExecutionEngine::stackTrace(int frameLimit) const stack.append(frame); --frameLimit; } - c = c->d()->parent; + c = parentContext(c); } if (frameLimit && globalCode) { StackFrame frame; frame.source = globalCode->sourceFile(); frame.function = globalCode->name()->toQString(); - frame.line = rootContext()->lineNumber; + frame.line = rootContext()->d()->lineNumber; frame.column = -1; stack.append(frame); @@ -838,8 +908,7 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file) return src; QUrl base; - Scope scope(this); - ScopedContext c(scope, currentContext()); + ExecutionContext *c = currentContext; while (c) { CallContext *callCtx = c->asCallContext(); if (callCtx && callCtx->d()->function) { @@ -847,7 +916,7 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file) base.setUrl(callCtx->d()->function->function->sourceFile()); break; } - c = c->d()->parent; + c = parentContext(c); } if (base.isEmpty() && globalCode) @@ -877,10 +946,10 @@ void ExecutionEngine::requireArgumentsAccessors(int n) memcpy(argumentsAccessors, oldAccessors, oldSize*sizeof(Property)); delete [] oldAccessors; } - ScopedContext global(scope, scope.engine->rootContext()); + ExecutionContext *global = rootContext(); for (int i = oldSize; i < nArgumentsAccessors; ++i) { - argumentsAccessors[i].value = ScopedValue(scope, memoryManager->alloc<ArgumentsGetterFunction>(global, i)); - argumentsAccessors[i].set = ScopedValue(scope, memoryManager->alloc<ArgumentsSetterFunction>(global, i)); + argumentsAccessors[i].value = ScopedValue(scope, memoryManager->allocObject<ArgumentsGetterFunction>(global, i)); + argumentsAccessors[i].set = ScopedValue(scope, memoryManager->allocObject<ArgumentsSetterFunction>(global, i)); } } } @@ -889,8 +958,6 @@ void ExecutionEngine::markObjects() { identifierTable->mark(this); - globalObject()->mark(this); - for (int i = 0; i < nArgumentsAccessors; ++i) { const Property &pd = argumentsAccessors[i]; if (Heap::FunctionObject *getter = pd.getter()) @@ -899,103 +966,6 @@ void ExecutionEngine::markObjects() setter->mark(this); } - Heap::ExecutionContext *c = currentContext(); - while (c) { - Q_ASSERT(c->inUse()); - if (!c->isMarked()) { - c->setMarkBit(); - c->gcGetVtable()->markObjects(c, this); - } - c = c->parent; - } - - id_empty->mark(this); - id_undefined->mark(this); - id_null->mark(this); - id_true->mark(this); - id_false->mark(this); - id_boolean->mark(this); - id_number->mark(this); - id_string->mark(this); - id_object->mark(this); - id_function->mark(this); - id_length->mark(this); - id_prototype->mark(this); - id_constructor->mark(this); - id_arguments->mark(this); - id_caller->mark(this); - id_callee->mark(this); - id_this->mark(this); - id___proto__->mark(this); - id_enumerable->mark(this); - id_configurable->mark(this); - id_writable->mark(this); - id_value->mark(this); - id_get->mark(this); - id_set->mark(this); - id_eval->mark(this); - id_uintMax->mark(this); - id_name->mark(this); - id_index->mark(this); - id_input->mark(this); - id_toString->mark(this); - id_destroy->mark(this); - id_valueOf->mark(this); - id_byteLength->mark(this); - id_byteOffset->mark(this); - id_buffer->mark(this); - id_lastIndex->mark(this); - - objectCtor.mark(this); - stringCtor.mark(this); - numberCtor.mark(this); - booleanCtor.mark(this); - arrayCtor.mark(this); - functionCtor.mark(this); - dateCtor.mark(this); - regExpCtor.mark(this); - errorCtor.mark(this); - evalErrorCtor.mark(this); - rangeErrorCtor.mark(this); - referenceErrorCtor.mark(this); - syntaxErrorCtor.mark(this); - typeErrorCtor.mark(this); - uRIErrorCtor.mark(this); - arrayBufferCtor.mark(this); - dataViewCtor.mark(this); - for (int i = 0; i < Heap::TypedArray::NTypes; ++i) - typedArrayCtors[i].mark(this); - - objectPrototype.mark(this); - arrayPrototype.mark(this); - stringPrototype.mark(this); - numberPrototype.mark(this); - booleanPrototype.mark(this); - datePrototype.mark(this); - functionPrototype.mark(this); - regExpPrototype.mark(this); - errorPrototype.mark(this); - evalErrorPrototype.mark(this); - rangeErrorPrototype.mark(this); - referenceErrorPrototype.mark(this); - syntaxErrorPrototype.mark(this); - typeErrorPrototype.mark(this); - uRIErrorPrototype.mark(this); - variantPrototype.mark(this); - sequencePrototype.mark(this); - - arrayBufferPrototype.mark(this); - dataViewPrototype.mark(this); - for (int i = 0; i < Heap::TypedArray::NTypes; ++i) - typedArrayPrototype[i].mark(this); - - exceptionValue.mark(this); - - thrower->mark(this); - - if (m_qmlExtensions) - m_qmlExtensions->markObjects(this); - classPool->markObjects(this); for (QSet<CompiledData::CompilationUnit*>::ConstIterator it = compilationUnits.constBegin(), end = compilationUnits.constEnd(); @@ -1003,13 +973,6 @@ void ExecutionEngine::markObjects() (*it)->markObjects(this); } -QmlExtensions *ExecutionEngine::qmlExtensions() -{ - if (!m_qmlExtensions) - m_qmlExtensions = new QmlExtensions; - return m_qmlExtensions; -} - ReturnedValue ExecutionEngine::throwError(const Value &value) { // we can get in here with an exception already set, as the runtime @@ -1020,7 +983,7 @@ ReturnedValue ExecutionEngine::throwError(const Value &value) return Encode::undefined(); hasException = true; - exceptionValue = value; + *exceptionValue = value; QV4::Scope scope(this); QV4::Scoped<ErrorObject> error(scope, value); if (!!error) @@ -1041,8 +1004,8 @@ ReturnedValue ExecutionEngine::catchException(StackTrace *trace) *trace = exceptionStackTrace; exceptionStackTrace.clear(); hasException = false; - ReturnedValue res = exceptionValue.asReturnedValue(); - exceptionValue = Primitive::emptyValue(); + ReturnedValue res = exceptionValue->asReturnedValue(); + *exceptionValue = Primitive::emptyValue(); return res; } @@ -1139,7 +1102,7 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError() QV4::ScopedValue exception(scope, catchException(&trace)); QQmlError error; if (!trace.isEmpty()) { - QV4::StackFrame frame = trace.first(); + QV4::StackFrame frame = trace.constFirst(); error.setUrl(QUrl(frame.source)); error.setLine(frame.line); error.setColumn(frame.column); @@ -1175,7 +1138,7 @@ bool ExecutionEngine::recheckCStackLimits() typedef QSet<QV4::Heap::Object *> V4ObjectSet; static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int typeHint, bool createJSValueForObjects, V4ObjectSet *visitedObjects); static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &value); -static QVariant objectToVariant(QV4::ExecutionEngine *e, QV4::Object *o, V4ObjectSet *visitedObjects = 0); +static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects = 0); static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &value, const QByteArray &targetType, void **result); @@ -1198,7 +1161,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int Q_ASSERT (!value.isEmpty()); QV4::Scope scope(e); - if (QV4::VariantObject *v = value.as<QV4::VariantObject>()) + if (const QV4::VariantObject *v = value.as<QV4::VariantObject>()) return v->d()->data; if (typeHint == QVariant::Bool) @@ -1210,10 +1173,10 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int if (typeHint == qMetaTypeId<QJSValue>()) return QVariant::fromValue(QJSValue(e, value.asReturnedValue())); - if (value.asObject()) { + if (value.as<Object>()) { QV4::ScopedObject object(scope, value); if (typeHint == QMetaType::QJsonObject - && !value.asArrayObject() && !value.asFunctionObject()) { + && !value.as<ArrayObject>() && !value.as<FunctionObject>()) { return QVariant::fromValue(QV4::JsonObject::toJsonObject(object)); } else if (QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>()) { return qVariantFromValue<QObject *>(wrapper->object()); @@ -1229,7 +1192,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return QV4::SequencePrototype::toVariant(object); } - if (value.asArrayObject()) { + if (value.as<ArrayObject>()) { QV4::ScopedArrayObject a(scope, value); if (typeHint == qMetaTypeId<QList<QObject *> >()) { QList<QObject *> list; @@ -1267,11 +1230,11 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return value.asDouble(); if (value.isString()) return value.stringValue()->toQString(); - if (QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>()) + if (const QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>()) return ld->d()->locale; - if (QV4::DateObject *d = value.asDateObject()) + if (const QV4::DateObject *d = value.as<DateObject>()) return d->toQDateTime(); - if (QV4::ArrayBuffer *d = value.as<ArrayBuffer>()) + if (const QV4::ArrayBuffer *d = value.as<ArrayBuffer>()) return d->asByteArray(); // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)! @@ -1287,7 +1250,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return objectToVariant(e, o, visitedObjects); } -static QVariant objectToVariant(QV4::ExecutionEngine *e, QV4::Object *o, V4ObjectSet *visitedObjects) +static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects) { Q_ASSERT(o); @@ -1298,7 +1261,7 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, QV4::Object *o, V4Objec // Avoid recursion. // For compatibility with QVariant{List,Map} conversion, we return an // empty object (and no error is thrown). - if (o->asArrayObject()) + if (o->as<ArrayObject>()) return QVariantList(); return QVariantMap(); } @@ -1306,7 +1269,7 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, QV4::Object *o, V4Objec QVariant result; - if (o->asArrayObject()) { + if (o->as<ArrayObject>()) { QV4::Scope scope(e); QV4::ScopedArrayObject a(scope, o->asReturnedValue()); QV4::ScopedValue v(scope); @@ -1319,7 +1282,7 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, QV4::Object *o, V4Objec } result = list; - } else if (!o->asFunctionObject()) { + } else if (!o->as<FunctionObject>()) { QVariantMap map; QV4::Scope scope(e); QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly); @@ -1497,7 +1460,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::Encode(newVariantObject(variant)); } -QVariantMap ExecutionEngine::variantMapFromJS(Object *o) +QVariantMap ExecutionEngine::variantMapFromJS(const Object *o) { return objectToVariant(this, o).toMap(); } @@ -1630,93 +1593,90 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) return 0; } -void ExecutionEngine::assertObjectBelongsToEngine(const Value &v) +void ExecutionEngine::assertObjectBelongsToEngine(const Heap::Base &baseObject) { - Q_UNUSED(v); - Q_ASSERT(!v.isObject() || v.objectValue()->engine() == this); - Q_UNUSED(v); + Q_ASSERT(!baseObject.vtable()->isObject || static_cast<const Heap::Object&>(baseObject).internalClass->engine == this); + Q_UNUSED(baseObject); } // Converts a JS value to a meta-type. // data must point to a place that can store a value of the given type. // Returns true if conversion succeeded, false otherwise. -bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *data) +bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) { - QV4::Scope scope(this); - // check if it's one of the types we know switch (QMetaType::Type(type)) { case QMetaType::Bool: - *reinterpret_cast<bool*>(data) = value.toBoolean(); + *reinterpret_cast<bool*>(data) = value->toBoolean(); return true; case QMetaType::Int: - *reinterpret_cast<int*>(data) = value.toInt32(); + *reinterpret_cast<int*>(data) = value->toInt32(); return true; case QMetaType::UInt: - *reinterpret_cast<uint*>(data) = value.toUInt32(); + *reinterpret_cast<uint*>(data) = value->toUInt32(); return true; case QMetaType::LongLong: - *reinterpret_cast<qlonglong*>(data) = qlonglong(value.toInteger()); + *reinterpret_cast<qlonglong*>(data) = qlonglong(value->toInteger()); return true; case QMetaType::ULongLong: - *reinterpret_cast<qulonglong*>(data) = qulonglong(value.toInteger()); + *reinterpret_cast<qulonglong*>(data) = qulonglong(value->toInteger()); return true; case QMetaType::Double: - *reinterpret_cast<double*>(data) = value.toNumber(); + *reinterpret_cast<double*>(data) = value->toNumber(); return true; case QMetaType::QString: - if (value.isUndefined() || value.isNull()) + if (value->isUndefined() || value->isNull()) *reinterpret_cast<QString*>(data) = QString(); else - *reinterpret_cast<QString*>(data) = value.toQString(); + *reinterpret_cast<QString*>(data) = value->toQString(); return true; case QMetaType::Float: - *reinterpret_cast<float*>(data) = value.toNumber(); + *reinterpret_cast<float*>(data) = value->toNumber(); return true; case QMetaType::Short: - *reinterpret_cast<short*>(data) = short(value.toInt32()); + *reinterpret_cast<short*>(data) = short(value->toInt32()); return true; case QMetaType::UShort: - *reinterpret_cast<unsigned short*>(data) = value.toUInt16(); + *reinterpret_cast<unsigned short*>(data) = value->toUInt16(); return true; case QMetaType::Char: - *reinterpret_cast<char*>(data) = char(value.toInt32()); + *reinterpret_cast<char*>(data) = char(value->toInt32()); return true; case QMetaType::UChar: - *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value.toInt32()); + *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value->toInt32()); return true; case QMetaType::QChar: - if (value.isString()) { - QString str = value.stringValue()->toQString(); + if (value->isString()) { + QString str = value->stringValue()->toQString(); *reinterpret_cast<QChar*>(data) = str.isEmpty() ? QChar() : str.at(0); } else { - *reinterpret_cast<QChar*>(data) = QChar(ushort(value.toUInt16())); + *reinterpret_cast<QChar*>(data) = QChar(ushort(value->toUInt16())); } return true; case QMetaType::QDateTime: - if (QV4::DateObject *d = value.asDateObject()) { + if (const QV4::DateObject *d = value->as<DateObject>()) { *reinterpret_cast<QDateTime *>(data) = d->toQDateTime(); return true; } break; case QMetaType::QDate: - if (QV4::DateObject *d = value.asDateObject()) { + if (const QV4::DateObject *d = value->as<DateObject>()) { *reinterpret_cast<QDate *>(data) = d->toQDateTime().date(); return true; } break; case QMetaType::QRegExp: - if (QV4::RegExpObject *r = value.as<QV4::RegExpObject>()) { + if (const QV4::RegExpObject *r = value->as<QV4::RegExpObject>()) { *reinterpret_cast<QRegExp *>(data) = r->toQRegExp(); return true; } break; case QMetaType::QObjectStar: { - QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>(); - if (qobjectWrapper || value.isNull()) { - *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(scope.engine, value); + const QV4::QObjectWrapper *qobjectWrapper = value->as<QV4::QObjectWrapper>(); + if (qobjectWrapper || value->isNull()) { + *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(this, *value); return true; } break; } case QMetaType::QStringList: { - QV4::ScopedArrayObject a(scope, value); + const QV4::ArrayObject *a = value->as<QV4::ArrayObject>(); if (a) { *reinterpret_cast<QStringList *>(data) = a->toQStringList(); return true; @@ -1724,15 +1684,15 @@ bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *da break; } case QMetaType::QVariantList: { - QV4::ScopedArrayObject a(scope, value); + const QV4::ArrayObject *a = value->as<QV4::ArrayObject>(); if (a) { - *reinterpret_cast<QVariantList *>(data) = scope.engine->toVariant(a, /*typeHint*/-1, /*createJSValueForObjects*/false).toList(); + *reinterpret_cast<QVariantList *>(data) = toVariant(*a, /*typeHint*/-1, /*createJSValueForObjects*/false).toList(); return true; } break; } case QMetaType::QVariantMap: { - QV4::ScopedObject o(scope, value); + const QV4::Object *o = value->as<QV4::Object>(); if (o) { *reinterpret_cast<QVariantMap *>(data) = variantMapFromJS(o); return true; @@ -1740,20 +1700,19 @@ bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *da break; } case QMetaType::QVariant: - *reinterpret_cast<QVariant*>(data) = scope.engine->toVariant(value, /*typeHint*/-1, /*createJSValueForObjects*/false); + *reinterpret_cast<QVariant*>(data) = toVariant(*value, /*typeHint*/-1, /*createJSValueForObjects*/false); return true; case QMetaType::QJsonValue: - *reinterpret_cast<QJsonValue *>(data) = QV4::JsonObject::toJsonValue(value); + *reinterpret_cast<QJsonValue *>(data) = QV4::JsonObject::toJsonValue(*value); return true; case QMetaType::QJsonObject: { - QV4::ScopedObject o(scope, value); - *reinterpret_cast<QJsonObject *>(data) = QV4::JsonObject::toJsonObject(o); + *reinterpret_cast<QJsonObject *>(data) = QV4::JsonObject::toJsonObject(value->as<Object>()); return true; } case QMetaType::QJsonArray: { - QV4::ScopedArrayObject a(scope, value); + const QV4::ArrayObject *a = value->as<ArrayObject>(); if (a) { - *reinterpret_cast<QJsonArray *>(data) = QV4::JsonObject::toJsonArray(a); + *reinterpret_cast<QJsonArray *>(data) = JsonObject::toJsonArray(a); return true; } break; @@ -1763,7 +1722,7 @@ bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *da } { - QV4::Scoped<QV4::QQmlValueTypeWrapper> vtw(scope, value); + const QQmlValueTypeWrapper *vtw = value->as<QQmlValueTypeWrapper>(); if (vtw && vtw->typeId() == type) { return vtw->toGadget(data); } @@ -1788,21 +1747,22 @@ bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *da } #endif - // Try to use magic; for compatibility with qscriptvalue_cast. + // Try to use magic; for compatibility with qjsvalue_cast. QByteArray name = QMetaType::typeName(type); - if (convertToNativeQObject(this, value, name, reinterpret_cast<void* *>(data))) + if (convertToNativeQObject(this, *value, name, reinterpret_cast<void* *>(data))) return true; - if (value.as<QV4::VariantObject>() && name.endsWith('*')) { + if (value->as<QV4::VariantObject>() && name.endsWith('*')) { int valueType = QMetaType::type(name.left(name.size()-1)); - QVariant &var = value.as<QV4::VariantObject>()->d()->data; + QVariant &var = value->as<QV4::VariantObject>()->d()->data; if (valueType == var.userType()) { // We have T t, T* is requested, so return &t. *reinterpret_cast<void* *>(data) = var.data(); return true; - } else if (value.isObject()) { + } else if (value->isObject()) { // Look in the prototype chain. - QV4::ScopedObject proto(scope, value.objectValue()->prototype()); + QV4::Scope scope(this); + QV4::ScopedObject proto(scope, value->objectValue()->prototype()); while (proto) { bool canCast = false; if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) { @@ -1812,7 +1772,7 @@ bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *da else if (proto->as<QV4::QObjectWrapper>()) { QByteArray className = name.left(name.size()-1); QV4::ScopedObject p(scope, proto.getPointer()); - if (QObject *qobject = qtObjectFromJS(scope.engine, p)) + if (QObject *qobject = qtObjectFromJS(this, p)) canCast = qobject->qt_metacast(className) != 0; } if (canCast) { @@ -1826,11 +1786,11 @@ bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *da proto = proto->prototype(); } } - } else if (value.isNull() && name.endsWith('*')) { + } else if (value->isNull() && name.endsWith('*')) { *reinterpret_cast<void* *>(data) = 0; return true; } else if (type == qMetaTypeId<QJSValue>()) { - *reinterpret_cast<QJSValue*>(data) = QJSValue(this, value.asReturnedValue()); + *reinterpret_cast<QJSValue*>(data) = QJSValue(this, value->asReturnedValue()); return true; } diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index c0c88abaa5..4640f3f4cc 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -33,10 +33,22 @@ #ifndef QV4ENGINE_H #define QV4ENGINE_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include "private/qv4isel_p.h" #include "qv4managed_p.h" #include "qv4context_p.h" +#include "qv4internalclass_p.h" #include <private/qintrusivelist_p.h> namespace WTF { @@ -50,6 +62,7 @@ class QV8Engine; class QQmlError; class QJSEngine; class QQmlEngine; +class QQmlContextData; namespace QV4 { namespace Debugging { @@ -76,18 +89,16 @@ private: friend struct Heap::ExecutionContext; public: Heap::ExecutionContext *current; - Heap::ExecutionContext *currentContext() const { return current; } Value *jsStackTop; quint32 hasException; - Heap::GlobalContext *m_rootContext; - Heap::GlobalContext *rootContext() const { return m_rootContext; } MemoryManager *memoryManager; ExecutableAllocator *executableAllocator; ExecutableAllocator *regExpAllocator; QScopedPointer<EvalISelFactory> iselFactory; + ExecutionContext *currentContext; Value *jsStackLimit; quintptr cStackLimit; @@ -106,14 +117,19 @@ public: --jsStackTop; return jsStackTop->heapObject(); } + Value *jsAlloca(int nValues) { + Value *ptr = jsStackTop; + jsStackTop = ptr + nValues; + memset(ptr, 0, nValues*sizeof(Value)); + return ptr; + } IdentifierTable *identifierTable; QV4::Debugging::Debugger *debugger; QV4::Profiling::Profiler *profiler; - Value m_globalObject; - Object *globalObject() { return reinterpret_cast<Object *>(&m_globalObject); } + Object *globalObject; Function *globalCode; @@ -121,104 +137,206 @@ public: QQmlEngine *qmlEngine() const; QV8Engine *v8Engine; - Value objectCtor; - Value stringCtor; - Value numberCtor; - Value booleanCtor; - Value arrayCtor; - Value functionCtor; - Value dateCtor; - Value regExpCtor; - Value errorCtor; - Value evalErrorCtor; - Value rangeErrorCtor; - Value referenceErrorCtor; - Value syntaxErrorCtor; - Value typeErrorCtor; - Value uRIErrorCtor; - Value arrayBufferCtor; - Value dataViewCtor; - enum { NTypedArrayTypes = 9 }; // avoid header dependency - Value typedArrayCtors[NTypedArrayTypes]; - - Value objectPrototype; - Value arrayPrototype; - Value stringPrototype; - Value numberPrototype; - Value booleanPrototype; - Value datePrototype; - Value functionPrototype; - Value regExpPrototype; - Value errorPrototype; - Value evalErrorPrototype; - Value rangeErrorPrototype; - Value referenceErrorPrototype; - Value syntaxErrorPrototype; - Value typeErrorPrototype; - Value uRIErrorPrototype; - Value variantPrototype; - Value sequencePrototype; - - Value arrayBufferPrototype; - Value dataViewPrototype; - Value typedArrayPrototype[NTypedArrayTypes]; // TypedArray::NValues, avoid including the header here + enum JSObjects { + RootContext, + IntegerNull, // Has to come after the RootContext to make the context stack safe + ObjectProto, + ArrayProto, + StringProto, + NumberProto, + BooleanProto, + DateProto, + FunctionProto, + RegExpProto, + ErrorProto, + EvalErrorProto, + RangeErrorProto, + ReferenceErrorProto, + SyntaxErrorProto, + TypeErrorProto, + URIErrorProto, + VariantProto, + SequenceProto, + ArrayBufferProto, + DataViewProto, + ValueTypeProto, + SignalHandlerProto, + + Object_Ctor, + String_Ctor, + Number_Ctor, + Boolean_Ctor, + Array_Ctor, + Function_Ctor, + Date_Ctor, + RegExp_Ctor, + Error_Ctor, + EvalError_Ctor, + RangeError_Ctor, + ReferenceError_Ctor, + SyntaxError_Ctor, + TypeError_Ctor, + URIError_Ctor, + ArrayBuffer_Ctor, + DataView_Ctor, + + Eval_Function, + GetStack_Function, + ThrowerObject, + NJSObjects + }; + Value *jsObjects; + enum { NTypedArrayTypes = 9 }; // == TypedArray::NValues, avoid header dependency + + GlobalContext *rootContext() const { return reinterpret_cast<GlobalContext *>(jsObjects + RootContext); } + FunctionObject *objectCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Object_Ctor); } + FunctionObject *stringCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + String_Ctor); } + FunctionObject *numberCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Number_Ctor); } + FunctionObject *booleanCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Boolean_Ctor); } + FunctionObject *arrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Array_Ctor); } + FunctionObject *functionCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Function_Ctor); } + FunctionObject *dateCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Date_Ctor); } + FunctionObject *regExpCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + RegExp_Ctor); } + FunctionObject *errorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Error_Ctor); } + FunctionObject *evalErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + EvalError_Ctor); } + FunctionObject *rangeErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + RangeError_Ctor); } + FunctionObject *referenceErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + ReferenceError_Ctor); } + FunctionObject *syntaxErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + SyntaxError_Ctor); } + FunctionObject *typeErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + TypeError_Ctor); } + FunctionObject *uRIErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + URIError_Ctor); } + FunctionObject *arrayBufferCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + ArrayBuffer_Ctor); } + FunctionObject *dataViewCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + DataView_Ctor); } + FunctionObject *typedArrayCtors; + + Object *objectPrototype() const { return reinterpret_cast<Object *>(jsObjects + ObjectProto); } + Object *arrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayProto); } + Object *stringPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringProto); } + Object *numberPrototype() const { return reinterpret_cast<Object *>(jsObjects + NumberProto); } + Object *booleanPrototype() const { return reinterpret_cast<Object *>(jsObjects + BooleanProto); } + Object *datePrototype() const { return reinterpret_cast<Object *>(jsObjects + DateProto); } + Object *functionPrototype() const { return reinterpret_cast<Object *>(jsObjects + FunctionProto); } + Object *regExpPrototype() const { return reinterpret_cast<Object *>(jsObjects + RegExpProto); } + Object *errorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ErrorProto); } + Object *evalErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + EvalErrorProto); } + Object *rangeErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + RangeErrorProto); } + Object *referenceErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ReferenceErrorProto); } + Object *syntaxErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + SyntaxErrorProto); } + Object *typeErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + TypeErrorProto); } + Object *uRIErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + URIErrorProto); } + Object *variantPrototype() const { return reinterpret_cast<Object *>(jsObjects + VariantProto); } + Object *sequencePrototype() const { return reinterpret_cast<Object *>(jsObjects + SequenceProto); } + + Object *arrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayBufferProto); } + Object *dataViewPrototype() const { return reinterpret_cast<Object *>(jsObjects + DataViewProto); } + Object *typedArrayPrototype; + + Object *valueTypeWrapperPrototype() const { return reinterpret_cast<Object *>(jsObjects + ValueTypeProto); } + Object *signalHandlerPrototype() const { return reinterpret_cast<Object *>(jsObjects + SignalHandlerProto); } InternalClassPool *classPool; InternalClass *emptyClass; InternalClass *arrayClass; + InternalClass *stringClass; InternalClass *functionClass; InternalClass *simpleScriptFunctionClass; InternalClass *protoClass; InternalClass *regExpExecArrayClass; + InternalClass *regExpObjectClass; InternalClass *argumentsObjectClass; InternalClass *strictArgumentsObjectClass; - Heap::EvalFunction *evalFunction; - Heap::FunctionObject *thrower; + InternalClass *errorClass; + InternalClass *errorClassWithMessage; + InternalClass *errorProtoClass; + + EvalFunction *evalFunction() const { return reinterpret_cast<EvalFunction *>(jsObjects + Eval_Function); } + FunctionObject *getStackFunction() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetStack_Function); } + FunctionObject *thrower() const { return reinterpret_cast<FunctionObject *>(jsObjects + ThrowerObject); } Property *argumentsAccessors; int nArgumentsAccessors; - StringValue id_empty; - StringValue id_undefined; - StringValue id_null; - StringValue id_true; - StringValue id_false; - StringValue id_boolean; - StringValue id_number; - StringValue id_string; - StringValue id_object; - StringValue id_function; - StringValue id_length; - StringValue id_prototype; - StringValue id_constructor; - StringValue id_arguments; - StringValue id_caller; - StringValue id_callee; - StringValue id_this; - StringValue id___proto__; - StringValue id_enumerable; - StringValue id_configurable; - StringValue id_writable; - StringValue id_value; - StringValue id_get; - StringValue id_set; - StringValue id_eval; - StringValue id_uintMax; - StringValue id_name; - StringValue id_index; - StringValue id_input; - StringValue id_toString; - StringValue id_destroy; - StringValue id_valueOf; - StringValue id_byteLength; - StringValue id_byteOffset; - StringValue id_buffer; - StringValue id_lastIndex; + enum JSStrings { + String_Empty, + String_undefined, + String_null, + String_true, + String_false, + String_boolean, + String_number, + String_string, + String_object, + String_function, + String_length, + String_prototype, + String_constructor, + String_arguments, + String_caller, + String_callee, + String_this, + String___proto__, + String_enumerable, + String_configurable, + String_writable, + String_value, + String_get, + String_set, + String_eval, + String_uintMax, + String_name, + String_index, + String_input, + String_toString, + String_destroy, + String_valueOf, + String_byteLength, + String_byteOffset, + String_buffer, + String_lastIndex, + NJSStrings + }; + Value *jsStrings; + + String *id_empty() const { return reinterpret_cast<String *>(jsStrings + String_Empty); } + String *id_undefined() const { return reinterpret_cast<String *>(jsStrings + String_undefined); } + String *id_null() const { return reinterpret_cast<String *>(jsStrings + String_null); } + String *id_true() const { return reinterpret_cast<String *>(jsStrings + String_true); } + String *id_false() const { return reinterpret_cast<String *>(jsStrings + String_false); } + String *id_boolean() const { return reinterpret_cast<String *>(jsStrings + String_boolean); } + String *id_number() const { return reinterpret_cast<String *>(jsStrings + String_number); } + String *id_string() const { return reinterpret_cast<String *>(jsStrings + String_string); } + String *id_object() const { return reinterpret_cast<String *>(jsStrings + String_object); } + String *id_function() const { return reinterpret_cast<String *>(jsStrings + String_function); } + String *id_length() const { return reinterpret_cast<String *>(jsStrings + String_length); } + String *id_prototype() const { return reinterpret_cast<String *>(jsStrings + String_prototype); } + String *id_constructor() const { return reinterpret_cast<String *>(jsStrings + String_constructor); } + String *id_arguments() const { return reinterpret_cast<String *>(jsStrings + String_arguments); } + String *id_caller() const { return reinterpret_cast<String *>(jsStrings + String_caller); } + String *id_callee() const { return reinterpret_cast<String *>(jsStrings + String_callee); } + String *id_this() const { return reinterpret_cast<String *>(jsStrings + String_this); } + String *id___proto__() const { return reinterpret_cast<String *>(jsStrings + String___proto__); } + String *id_enumerable() const { return reinterpret_cast<String *>(jsStrings + String_enumerable); } + String *id_configurable() const { return reinterpret_cast<String *>(jsStrings + String_configurable); } + String *id_writable() const { return reinterpret_cast<String *>(jsStrings + String_writable); } + String *id_value() const { return reinterpret_cast<String *>(jsStrings + String_value); } + String *id_get() const { return reinterpret_cast<String *>(jsStrings + String_get); } + String *id_set() const { return reinterpret_cast<String *>(jsStrings + String_set); } + String *id_eval() const { return reinterpret_cast<String *>(jsStrings + String_eval); } + String *id_uintMax() const { return reinterpret_cast<String *>(jsStrings + String_uintMax); } + String *id_name() const { return reinterpret_cast<String *>(jsStrings + String_name); } + String *id_index() const { return reinterpret_cast<String *>(jsStrings + String_index); } + String *id_input() const { return reinterpret_cast<String *>(jsStrings + String_input); } + String *id_toString() const { return reinterpret_cast<String *>(jsStrings + String_toString); } + String *id_destroy() const { return reinterpret_cast<String *>(jsStrings + String_destroy); } + String *id_valueOf() const { return reinterpret_cast<String *>(jsStrings + String_valueOf); } + String *id_byteLength() const { return reinterpret_cast<String *>(jsStrings + String_byteLength); } + String *id_byteOffset() const { return reinterpret_cast<String *>(jsStrings + String_byteOffset); } + String *id_buffer() const { return reinterpret_cast<String *>(jsStrings + String_buffer); } + String *id_lastIndex() const { return reinterpret_cast<String *>(jsStrings + String_lastIndex); } QSet<CompiledData::CompilationUnit*> compilationUnits; @@ -248,12 +366,14 @@ public: ExecutionEngine(EvalISelFactory *iselFactory = 0); ~ExecutionEngine(); - void enableDebugger(); + void setDebugger(Debugging::Debugger *debugger); void enableProfiler(); - Heap::ExecutionContext *pushGlobalContext(); - void pushContext(CallContext *context); - Heap::ExecutionContext *popContext(); + ExecutionContext *pushGlobalContext(); + void pushContext(Heap::ExecutionContext *context); + void pushContext(ExecutionContext *context); + void popContext(); + ExecutionContext *parentContext(ExecutionContext *context) const; Heap::Object *newObject(); Heap::Object *newObject(InternalClass *internalClass, Object *prototype); @@ -261,15 +381,17 @@ public: Heap::String *newString(const QString &s = QString()); Heap::String *newIdentifier(const QString &text); - Heap::Object *newStringObject(const Value &value); + Heap::Object *newStringObject(const String *string); Heap::Object *newNumberObject(double value); Heap::Object *newBooleanObject(bool b); Heap::ArrayObject *newArrayObject(int count = 0); + Heap::ArrayObject *newArrayObject(const Value *values, int length); Heap::ArrayObject *newArrayObject(const QStringList &list); Heap::ArrayObject *newArrayObject(InternalClass *ic, Object *prototype); Heap::ArrayBuffer *newArrayBuffer(const QByteArray &array); + Heap::ArrayBuffer *newArrayBuffer(size_t length); Heap::DateObject *newDateObject(const Value &value); Heap::DateObject *newDateObject(const QDateTime &dt); @@ -282,7 +404,7 @@ public: Heap::Object *newSyntaxErrorObject(const QString &message, const QString &fileName, int line, int column); Heap::Object *newSyntaxErrorObject(const QString &message); Heap::Object *newReferenceErrorObject(const QString &message); - Heap::Object *newReferenceErrorObject(const QString &message, const QString &fileName, int lineNumber, int columnNumber); + Heap::Object *newReferenceErrorObject(const QString &message, const QString &fileName, int line, int column); Heap::Object *newTypeErrorObject(const QString &message); Heap::Object *newRangeErrorObject(const QString &message); Heap::Object *newURIErrorObject(const Value &message); @@ -291,7 +413,11 @@ public: Heap::Object *newForEachIteratorObject(Object *o); - Heap::Object *qmlContextObject() const; + Heap::QmlContext *qmlContext() const; + QObject *qmlScopeObject() const; + ReturnedValue qmlSingletonWrapper(String *name); + QQmlContextData *callingQmlContext() const; + StackTrace stackTrace(int frameLimit = -1) const; StackFrame currentStackFrame() const; @@ -305,12 +431,10 @@ public: InternalClass *newClass(const InternalClass &other); - QmlExtensions *qmlExtensions(); - bool recheckCStackLimits(); // Exception handling - Value exceptionValue; + Value *exceptionValue; StackTrace exceptionStackTrace; ReturnedValue throwError(const Value &value); @@ -335,70 +459,62 @@ public: QVariant toVariant(const QV4::Value &value, int typeHint, bool createJSValueForObjects = true); QV4::ReturnedValue fromVariant(const QVariant &); - QVariantMap variantMapFromJS(QV4::Object *o); + QVariantMap variantMapFromJS(const QV4::Object *o); - bool metaTypeFromJS(const Value &value, int type, void *data); + bool metaTypeFromJS(const Value *value, int type, void *data); QV4::ReturnedValue metaTypeToJS(int type, const void *data); - void assertObjectBelongsToEngine(const Value &v); - -private: - QmlExtensions *m_qmlExtensions; + void assertObjectBelongsToEngine(const Heap::Base &baseObject); }; -inline void ExecutionEngine::pushContext(CallContext *context) +inline void ExecutionEngine::pushContext(Heap::ExecutionContext *context) { - Q_ASSERT(current && context && context->d()); - context->d()->parent = current; - current = context->d(); + Q_ASSERT(currentContext && context); + Value *v = jsAlloca(2); + v[0] = Encode(context); + v[1] = Encode((int)(v - static_cast<Value *>(currentContext))); + currentContext = static_cast<ExecutionContext *>(v); + current = currentContext->d(); } -inline Heap::ExecutionContext *ExecutionEngine::popContext() +inline void ExecutionEngine::pushContext(ExecutionContext *context) { - Q_ASSERT(current->parent); - current = current->parent; - Q_ASSERT(current); - return current; + pushContext(context->d()); } -inline -Heap::ExecutionContext::ExecutionContext(ExecutionEngine *engine, ContextType t) - : engine(engine) - , parent(engine->currentContext()) - , outer(0) - , lookups(0) - , compilationUnit(0) - , type(t) - , strictMode(false) - , lineNumber(-1) + +inline void ExecutionEngine::popContext() { - engine->current = this; + Q_ASSERT(jsStackTop > currentContext); + QV4::Value *offset = (currentContext + 1); + Q_ASSERT(offset->isInteger()); + int o = offset->integerValue(); + Q_ASSERT(o); + currentContext -= o; + current = currentContext->d(); } - -// ### Remove me inline -void Managed::mark(QV4::ExecutionEngine *engine) +void Heap::Base::mark(QV4::ExecutionEngine *engine) { Q_ASSERT(inUse()); - if (markBit()) + if (isMarked()) return; #ifndef QT_NO_DEBUG engine->assertObjectBelongsToEngine(*this); #endif - d()->setMarkBit(); - engine->pushForGC(d()); + setMarkBit(); + engine->pushForGC(this); } - -inline -void Heap::Base::mark(QV4::ExecutionEngine *engine) +inline void Value::mark(ExecutionEngine *e) { - Q_ASSERT(inUse()); - if (isMarked()) + if (!isManaged()) return; - setMarkBit(); - engine->pushForGC(this); + + Heap::Base *o = heapObject(); + if (o) + o->mark(e); } diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index 9034dee6a0..87b7a88a2b 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -33,11 +33,14 @@ #include "qv4errorobject_p.h" -#include "qv4mm_p.h" +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> #include <QtCore/QDateTime> #include <QtCore/QStringList> #include <QtCore/QDebug> +#include "qv4string_p.h" +#include <private/qv4mm_p.h> #include <private/qqmljsengine_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> @@ -58,72 +61,49 @@ using namespace QV4; -Heap::ErrorObject::ErrorObject(InternalClass *ic, QV4::Object *prototype) - : Heap::Object(ic, prototype) - , stack(Q_NULLPTR) +Heap::ErrorObject::ErrorObject() { - Scope scope(ic->engine); + Scope scope(internalClass->engine); Scoped<QV4::ErrorObject> e(scope, this); - ScopedString s(scope, scope.engine->newString(QStringLiteral("Error"))); - e->defineDefaultProperty(QStringLiteral("name"), s); -} - -Heap::ErrorObject::ErrorObject(InternalClass *ic, QV4::Object *prototype, const Value &message, ErrorType t) - : Heap::Object(ic, prototype) -{ - errorType = t; + if (internalClass == scope.engine->errorProtoClass) + return; - Scope scope(ic->engine); - Scoped<QV4::ErrorObject> e(scope, this); - - e->defineAccessorProperty(QStringLiteral("stack"), QV4::ErrorObject::method_get_stack, 0); - - if (!message.isUndefined()) - e->defineDefaultProperty(QStringLiteral("message"), message); - ScopedString s(scope); - e->defineDefaultProperty(QStringLiteral("name"), (s = scope.engine->newString(e->className()))); - - e->d()->stackTrace = scope.engine->stackTrace(); - if (!e->d()->stackTrace.isEmpty()) { - e->defineDefaultProperty(QStringLiteral("fileName"), (s = scope.engine->newString(e->d()->stackTrace.at(0).source))); - e->defineDefaultProperty(QStringLiteral("lineNumber"), Primitive::fromInt32(e->d()->stackTrace.at(0).line)); - } + *propertyData(QV4::ErrorObject::Index_Stack) = scope.engine->getStackFunction(); + *propertyData(QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset) = Encode::undefined(); + *propertyData(QV4::ErrorObject::Index_FileName) = Encode::undefined(); + *propertyData(QV4::ErrorObject::Index_LineNumber) = Encode::undefined(); } -Heap::ErrorObject::ErrorObject(InternalClass *ic, QV4::Object *prototype, const QString &message, ErrorObject::ErrorType t) - : Heap::Object(ic, prototype) +Heap::ErrorObject::ErrorObject(const Value &message, ErrorType t) { errorType = t; - Scope scope(ic->engine); + Scope scope(internalClass->engine); Scoped<QV4::ErrorObject> e(scope, this); - ScopedString s(scope); - - e->defineAccessorProperty(QStringLiteral("stack"), QV4::ErrorObject::method_get_stack, 0); - ScopedValue v(scope, scope.engine->newString(message)); - e->defineDefaultProperty(QStringLiteral("message"), v); - e->defineDefaultProperty(QStringLiteral("name"), (s = scope.engine->newString(e->className()))); + *propertyData(QV4::ErrorObject::Index_Stack) = scope.engine->getStackFunction(); + *propertyData(QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset) = Encode::undefined(); e->d()->stackTrace = scope.engine->stackTrace(); if (!e->d()->stackTrace.isEmpty()) { - e->defineDefaultProperty(QStringLiteral("fileName"), (s = scope.engine->newString(e->d()->stackTrace.at(0).source))); - e->defineDefaultProperty(QStringLiteral("lineNumber"), Primitive::fromInt32(e->d()->stackTrace.at(0).line)); + *propertyData(QV4::ErrorObject::Index_FileName) = scope.engine->newString(e->d()->stackTrace.at(0).source); + *propertyData(QV4::ErrorObject::Index_LineNumber) = Primitive::fromInt32(e->d()->stackTrace.at(0).line); } + + if (!message.isUndefined()) + *propertyData(QV4::ErrorObject::Index_Message) = message; } -Heap::ErrorObject::ErrorObject(InternalClass *ic, QV4::Object *prototype, const QString &message, const QString &fileName, int line, int column, ErrorObject::ErrorType t) - : Heap::Object(ic, prototype) +Heap::ErrorObject::ErrorObject(const Value &message, const QString &fileName, int line, int column, ErrorObject::ErrorType t) { errorType = t; - Scope scope(ic->engine); + Scope scope(internalClass->engine); Scoped<QV4::ErrorObject> e(scope, this); - ScopedString s(scope); - e->defineAccessorProperty(QStringLiteral("stack"), QV4::ErrorObject::method_get_stack, 0); - e->defineDefaultProperty(QStringLiteral("name"), (s = scope.engine->newString(e->className()))); + *propertyData(QV4::ErrorObject::Index_Stack) = scope.engine->getStackFunction(); + *propertyData(QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset) = Encode::undefined(); e->d()->stackTrace = scope.engine->stackTrace(); StackFrame frame; @@ -133,12 +113,33 @@ Heap::ErrorObject::ErrorObject(InternalClass *ic, QV4::Object *prototype, const e->d()->stackTrace.prepend(frame); if (!e->d()->stackTrace.isEmpty()) { - e->defineDefaultProperty(QStringLiteral("fileName"), (s = scope.engine->newString(e->d()->stackTrace.at(0).source))); - e->defineDefaultProperty(QStringLiteral("lineNumber"), Primitive::fromInt32(e->d()->stackTrace.at(0).line)); + *propertyData(QV4::ErrorObject::Index_FileName) = scope.engine->newString(e->d()->stackTrace.at(0).source); + *propertyData(QV4::ErrorObject::Index_LineNumber) = Primitive::fromInt32(e->d()->stackTrace.at(0).line); } - ScopedValue v(scope, scope.engine->newString(message)); - e->defineDefaultProperty(QStringLiteral("message"), v); + if (!message.isUndefined()) + *propertyData(QV4::ErrorObject::Index_Message) = message; +} + +const char *ErrorObject::className(Heap::ErrorObject::ErrorType t) +{ + switch (t) { + case Heap::ErrorObject::Error: + return "Error"; + case Heap::ErrorObject::EvalError: + return "EvalError"; + case Heap::ErrorObject::RangeError: + return "RangeError"; + case Heap::ErrorObject::ReferenceError: + return "ReferenceError"; + case Heap::ErrorObject::SyntaxError: + return "SyntaxError"; + case Heap::ErrorObject::TypeError: + return "TypeError"; + case Heap::ErrorObject::URIError: + return "URIError"; + } + Q_UNREACHABLE(); } ReturnedValue ErrorObject::method_get_stack(CallContext *ctx) @@ -178,58 +179,43 @@ DEFINE_OBJECT_VTABLE(ErrorObject); DEFINE_OBJECT_VTABLE(SyntaxErrorObject); -Heap::SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const Value &msg) - : Heap::ErrorObject(engine->emptyClass, engine->syntaxErrorPrototype.asObject(), msg, SyntaxError) -{ -} - -Heap::SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber, int columnNumber) - : Heap::ErrorObject(engine->emptyClass, engine->syntaxErrorPrototype.asObject(), msg, fileName, lineNumber, columnNumber, SyntaxError) -{ -} - -Heap::EvalErrorObject::EvalErrorObject(ExecutionEngine *engine, const Value &message) - : Heap::ErrorObject(engine->emptyClass, engine->evalErrorPrototype.asObject(), message, EvalError) -{ -} - -Heap::RangeErrorObject::RangeErrorObject(ExecutionEngine *engine, const Value &message) - : Heap::ErrorObject(engine->emptyClass, engine->rangeErrorPrototype.asObject(), message, RangeError) +Heap::SyntaxErrorObject::SyntaxErrorObject(const Value &msg) + : Heap::ErrorObject(msg, SyntaxError) { } -Heap::RangeErrorObject::RangeErrorObject(ExecutionEngine *engine, const QString &message) - : Heap::ErrorObject(engine->emptyClass, engine->rangeErrorPrototype.asObject(), message, RangeError) +Heap::SyntaxErrorObject::SyntaxErrorObject(const Value &msg, const QString &fileName, int lineNumber, int columnNumber) + : Heap::ErrorObject(msg, fileName, lineNumber, columnNumber, SyntaxError) { } -Heap::ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const Value &message) - : Heap::ErrorObject(engine->emptyClass, engine->referenceErrorPrototype.asObject(), message, ReferenceError) +Heap::EvalErrorObject::EvalErrorObject(const Value &message) + : Heap::ErrorObject(message, EvalError) { } -Heap::ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const QString &message) - : Heap::ErrorObject(engine->emptyClass, engine->referenceErrorPrototype.asObject(), message, ReferenceError) +Heap::RangeErrorObject::RangeErrorObject(const Value &message) + : Heap::ErrorObject(message, RangeError) { } -Heap::ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber, int columnNumber) - : Heap::ErrorObject(engine->emptyClass, engine->referenceErrorPrototype.asObject(), msg, fileName, lineNumber, columnNumber, ReferenceError) +Heap::ReferenceErrorObject::ReferenceErrorObject(const Value &message) + : Heap::ErrorObject(message, ReferenceError) { } -Heap::TypeErrorObject::TypeErrorObject(ExecutionEngine *engine, const Value &message) - : Heap::ErrorObject(engine->emptyClass, engine->typeErrorPrototype.asObject(), message, TypeError) +Heap::ReferenceErrorObject::ReferenceErrorObject(const Value &msg, const QString &fileName, int lineNumber, int columnNumber) + : Heap::ErrorObject(msg, fileName, lineNumber, columnNumber, ReferenceError) { } -Heap::TypeErrorObject::TypeErrorObject(ExecutionEngine *engine, const QString &message) - : Heap::ErrorObject(engine->emptyClass, engine->typeErrorPrototype.asObject(), message, TypeError) +Heap::TypeErrorObject::TypeErrorObject(const Value &message) + : Heap::ErrorObject(message, TypeError) { } -Heap::URIErrorObject::URIErrorObject(ExecutionEngine *engine, const Value &message) - : Heap::ErrorObject(engine->emptyClass, engine->uRIErrorPrototype.asObject(), message, URIError) +Heap::URIErrorObject::URIErrorObject(const Value &message) + : Heap::ErrorObject(message, URIError) { } @@ -251,16 +237,16 @@ Heap::ErrorCtor::ErrorCtor(QV4::ExecutionContext *scope, const QString &name) { } -ReturnedValue ErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue ErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<ErrorCtor *>(m)->engine()); + Scope scope(static_cast<const ErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return Encode(scope.engine->newErrorObject(v)); + return ErrorObject::create<ErrorObject>(scope.engine, v)->asReturnedValue(); } -ReturnedValue ErrorCtor::call(Managed *that, CallData *callData) +ReturnedValue ErrorCtor::call(const Managed *that, CallData *callData) { - return static_cast<Object *>(that)->construct(callData); + return static_cast<const Object *>(that)->construct(callData); } Heap::EvalErrorCtor::EvalErrorCtor(QV4::ExecutionContext *scope) @@ -268,11 +254,11 @@ Heap::EvalErrorCtor::EvalErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue EvalErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue EvalErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<EvalErrorCtor *>(m)->engine()); + Scope scope(static_cast<const EvalErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return (scope.engine->memoryManager->alloc<EvalErrorObject>(scope.engine, v))->asReturnedValue(); + return ErrorObject::create<EvalErrorObject>(scope.engine, v)->asReturnedValue(); } Heap::RangeErrorCtor::RangeErrorCtor(QV4::ExecutionContext *scope) @@ -280,11 +266,11 @@ Heap::RangeErrorCtor::RangeErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue RangeErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue RangeErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<RangeErrorCtor *>(m)->engine()); + Scope scope(static_cast<const RangeErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return (scope.engine->memoryManager->alloc<RangeErrorObject>(scope.engine, v))->asReturnedValue(); + return ErrorObject::create<RangeErrorObject>(scope.engine, v)->asReturnedValue(); } Heap::ReferenceErrorCtor::ReferenceErrorCtor(QV4::ExecutionContext *scope) @@ -292,11 +278,11 @@ Heap::ReferenceErrorCtor::ReferenceErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ReferenceErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue ReferenceErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<ReferenceErrorCtor *>(m)->engine()); + Scope scope(static_cast<const ReferenceErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return (scope.engine->memoryManager->alloc<ReferenceErrorObject>(scope.engine, v))->asReturnedValue(); + return ErrorObject::create<ReferenceErrorObject>(scope.engine, v)->asReturnedValue(); } Heap::SyntaxErrorCtor::SyntaxErrorCtor(QV4::ExecutionContext *scope) @@ -304,11 +290,11 @@ Heap::SyntaxErrorCtor::SyntaxErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue SyntaxErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue SyntaxErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<SyntaxErrorCtor *>(m)->engine()); + Scope scope(static_cast<const SyntaxErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return (scope.engine->memoryManager->alloc<SyntaxErrorObject>(scope.engine, v))->asReturnedValue(); + return ErrorObject::create<SyntaxErrorObject>(scope.engine, v)->asReturnedValue(); } Heap::TypeErrorCtor::TypeErrorCtor(QV4::ExecutionContext *scope) @@ -316,11 +302,11 @@ Heap::TypeErrorCtor::TypeErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue TypeErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue TypeErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<TypeErrorCtor *>(m)->engine()); + Scope scope(static_cast<const TypeErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return (scope.engine->memoryManager->alloc<TypeErrorObject>(scope.engine, v))->asReturnedValue(); + return ErrorObject::create<TypeErrorObject>(scope.engine, v)->asReturnedValue(); } Heap::URIErrorCtor::URIErrorCtor(QV4::ExecutionContext *scope) @@ -328,41 +314,43 @@ Heap::URIErrorCtor::URIErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue URIErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue URIErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<URIErrorCtor *>(m)->engine()); + Scope scope(static_cast<const URIErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return (scope.engine->memoryManager->alloc<URIErrorObject>(scope.engine, v))->asReturnedValue(); + return ErrorObject::create<URIErrorObject>(scope.engine, v)->asReturnedValue(); } -void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj) +void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, Heap::ErrorObject::ErrorType t) { Scope scope(engine); ScopedString s(scope); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_prototype, (o = obj)); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1)); - obj->defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - obj->defineDefaultProperty(engine->id_toString, method_toString, 0); - obj->defineDefaultProperty(QStringLiteral("message"), (s = engine->newString())); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = obj)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + *obj->propertyData(Index_Constructor) = ctor; + *obj->propertyData(Index_Message) = engine->id_empty(); + *obj->propertyData(Index_Name) = engine->newString(QString::fromLatin1(ErrorObject::className(t))); + if (t == Heap::ErrorObject::Error) + obj->defineDefaultProperty(engine->id_toString(), method_toString, 0); } ReturnedValue ErrorPrototype::method_toString(CallContext *ctx) { Scope scope(ctx); - Object *o = ctx->thisObject().asObject(); + Object *o = ctx->thisObject().as<Object>(); if (!o) return ctx->engine()->throwTypeError(); - ScopedValue name(scope, o->get(ctx->d()->engine->id_name)); + ScopedValue name(scope, o->get(ctx->d()->engine->id_name())); QString qname; if (name->isUndefined()) - qname = QString::fromLatin1("Error"); + qname = QStringLiteral("Error"); else qname = name->toQString(); - ScopedString s(scope, ctx->d()->engine->newString(QString::fromLatin1("message"))); + ScopedString s(scope, ctx->d()->engine->newString(QStringLiteral("message"))); ScopedValue message(scope, o->get(s)); QString qmessage; if (!message->isUndefined()) diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 071f5b8c9a..336050ab2b 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -33,8 +33,20 @@ #ifndef QV4ERROROBJECT_H #define QV4ERROROBJECT_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" #include "qv4functionobject_p.h" +#include "qv4string_p.h" QT_BEGIN_NAMESPACE @@ -55,43 +67,39 @@ struct ErrorObject : Object { URIError }; - ErrorObject(InternalClass *ic, QV4::Object *prototype); - ErrorObject(InternalClass *ic, QV4::Object *prototype, const Value &message, ErrorType t = Error); - ErrorObject(InternalClass *ic, QV4::Object *prototype, const QString &message, ErrorType t = Error); - ErrorObject(InternalClass *ic, QV4::Object *prototype, const QString &message, const QString &fileName, int line, int column, ErrorType t = Error); + ErrorObject(); + ErrorObject(const Value &message, ErrorType t = Error); + ErrorObject(const Value &message, const QString &fileName, int line, int column, ErrorType t = Error); ErrorType errorType; StackTrace stackTrace; - String *stack; + Pointer<String> stack; }; struct EvalErrorObject : ErrorObject { - EvalErrorObject(ExecutionEngine *engine, const Value &message); + EvalErrorObject(const Value &message); }; struct RangeErrorObject : ErrorObject { - RangeErrorObject(ExecutionEngine *engine, const Value &message); - RangeErrorObject(ExecutionEngine *engine, const QString &msg); + RangeErrorObject(const Value &message); }; struct ReferenceErrorObject : ErrorObject { - ReferenceErrorObject(ExecutionEngine *engine, const Value &message); - ReferenceErrorObject(ExecutionEngine *engine, const QString &msg); - ReferenceErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber, int columnNumber); + ReferenceErrorObject(const Value &message); + ReferenceErrorObject(const Value &msg, const QString &fileName, int lineNumber, int columnNumber); }; struct SyntaxErrorObject : ErrorObject { - SyntaxErrorObject(ExecutionEngine *engine, const Value &message); - SyntaxErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber, int columnNumber); + SyntaxErrorObject(const Value &message); + SyntaxErrorObject(const Value &msg, const QString &fileName, int lineNumber, int columnNumber); }; struct TypeErrorObject : ErrorObject { - TypeErrorObject(ExecutionEngine *engine, const Value &message); - TypeErrorObject(ExecutionEngine *engine, const QString &msg); + TypeErrorObject(const Value &message); }; struct URIErrorObject : ErrorObject { - URIErrorObject(ExecutionEngine *engine, const Value &message); + URIErrorObject(const Value &message); }; struct ErrorCtor : Heap::FunctionObject { @@ -130,51 +138,75 @@ struct ErrorObject: Object { IsErrorObject = true }; + enum { + Index_Stack = 0, // Accessor Property + Index_FileName = 2, + Index_LineNumber = 3, + Index_Message = 4 + }; + V4_OBJECT2(ErrorObject, Object) Q_MANAGED_TYPE(ErrorObject) + V4_INTERNALCLASS(errorClass) + V4_PROTOTYPE(errorPrototype) V4_NEEDS_DESTROY + template <typename T> + static Heap::Object *create(ExecutionEngine *e, const Value &message); + template <typename T> + static Heap::Object *create(ExecutionEngine *e, const QString &message); + template <typename T> + static Heap::Object *create(ExecutionEngine *e, const QString &message, const QString &filename, int line, int column); + SyntaxErrorObject *asSyntaxError(); + static const char *className(Heap::ErrorObject::ErrorType t); + static ReturnedValue method_get_stack(CallContext *ctx); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; template<> -inline ErrorObject *value_cast(const Value &v) { - return v.asErrorObject(); +inline const ErrorObject *Value::as() const { + return isManaged() && m() && m()->vtable()->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : 0; } struct EvalErrorObject: ErrorObject { typedef Heap::EvalErrorObject Data; + V4_PROTOTYPE(evalErrorPrototype) const Data *d() const { return static_cast<const Data *>(ErrorObject::d()); } Data *d() { return static_cast<Data *>(ErrorObject::d()); } }; struct RangeErrorObject: ErrorObject { typedef Heap::RangeErrorObject Data; + V4_PROTOTYPE(rangeErrorPrototype) const Data *d() const { return static_cast<const Data *>(ErrorObject::d()); } Data *d() { return static_cast<Data *>(ErrorObject::d()); } }; struct ReferenceErrorObject: ErrorObject { typedef Heap::ReferenceErrorObject Data; + V4_PROTOTYPE(referenceErrorPrototype) const Data *d() const { return static_cast<const Data *>(ErrorObject::d()); } Data *d() { return static_cast<Data *>(ErrorObject::d()); } }; struct SyntaxErrorObject: ErrorObject { V4_OBJECT2(SyntaxErrorObject, ErrorObject) + V4_PROTOTYPE(syntaxErrorPrototype) }; struct TypeErrorObject: ErrorObject { typedef Heap::TypeErrorObject Data; + V4_PROTOTYPE(typeErrorPrototype) const Data *d() const { return static_cast<const Data *>(ErrorObject::d()); } Data *d() { return static_cast<Data *>(ErrorObject::d()); } }; struct URIErrorObject: ErrorObject { typedef Heap::URIErrorObject Data; + V4_PROTOTYPE(uRIErrorPrototype) const Data *d() const { return static_cast<const Data *>(ErrorObject::d()); } Data *d() { return static_cast<Data *>(ErrorObject::d()); } }; @@ -183,89 +215,94 @@ struct ErrorCtor: FunctionObject { V4_OBJECT2(ErrorCtor, FunctionObject) - static ReturnedValue construct(Managed *, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct EvalErrorCtor: ErrorCtor { V4_OBJECT2(EvalErrorCtor, ErrorCtor) - static ReturnedValue construct(Managed *m, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); }; struct RangeErrorCtor: ErrorCtor { V4_OBJECT2(RangeErrorCtor, ErrorCtor) - static ReturnedValue construct(Managed *m, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); }; struct ReferenceErrorCtor: ErrorCtor { V4_OBJECT2(ReferenceErrorCtor, ErrorCtor) - static ReturnedValue construct(Managed *m, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); }; struct SyntaxErrorCtor: ErrorCtor { V4_OBJECT2(SyntaxErrorCtor, ErrorCtor) - static ReturnedValue construct(Managed *m, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); }; struct TypeErrorCtor: ErrorCtor { V4_OBJECT2(TypeErrorCtor, ErrorCtor) - static ReturnedValue construct(Managed *m, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); }; struct URIErrorCtor: ErrorCtor { V4_OBJECT2(URIErrorCtor, ErrorCtor) - static ReturnedValue construct(Managed *m, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); }; struct ErrorPrototype : ErrorObject { - void init(ExecutionEngine *engine, Object *ctor) { init(engine, ctor, this); } + enum { + Index_Constructor = 0, + Index_Message = 1, + Index_Name = 2 + }; + void init(ExecutionEngine *engine, Object *ctor) { init(engine, ctor, this, Heap::ErrorObject::Error); } - static void init(ExecutionEngine *engine, Object *ctor, Object *obj); + static void init(ExecutionEngine *engine, Object *ctor, Object *obj, Heap::ErrorObject::ErrorType t); static ReturnedValue method_toString(CallContext *ctx); }; struct EvalErrorPrototype : ErrorObject { - void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this); } + void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this, Heap::ErrorObject::EvalError); } }; struct RangeErrorPrototype : ErrorObject { - void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this); } + void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this, Heap::ErrorObject::RangeError); } }; struct ReferenceErrorPrototype : ErrorObject { - void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this); } + void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this, Heap::ErrorObject::ReferenceError); } }; struct SyntaxErrorPrototype : ErrorObject { - void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this); } + void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this, Heap::ErrorObject::SyntaxError); } }; struct TypeErrorPrototype : ErrorObject { - void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this); } + void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this, Heap::ErrorObject::TypeError); } }; struct URIErrorPrototype : ErrorObject { - void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this); } + void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this, Heap::ErrorObject::URIError); } }; @@ -274,6 +311,25 @@ inline SyntaxErrorObject *ErrorObject::asSyntaxError() return d()->errorType == QV4::Heap::ErrorObject::SyntaxError ? static_cast<SyntaxErrorObject *>(this) : 0; } + +template <typename T> +Heap::Object *ErrorObject::create(ExecutionEngine *e, const Value &message) { + return e->memoryManager->allocObject<T>(message.isUndefined() ? e->errorClass : e->errorClassWithMessage, T::defaultPrototype(e), message); +} +template <typename T> +Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message) { + Scope scope(e); + ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue()); + return e->memoryManager->allocObject<T>(v->isUndefined() ? e->errorClass : e->errorClassWithMessage, T::defaultPrototype(e), v); +} +template <typename T> +Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message, const QString &filename, int line, int column) { + Scope scope(e); + ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue()); + return e->memoryManager->allocObject<T>(v->isUndefined() ? e->errorClass : e->errorClassWithMessage, T::defaultPrototype(e), v, filename, line, column); +} + + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h index 0d07e3c1bd..bb63d423a6 100644 --- a/src/qml/jsruntime/qv4executableallocator_p.h +++ b/src/qml/jsruntime/qv4executableallocator_p.h @@ -34,6 +34,17 @@ #ifndef QV4EXECUTABLEALLOCATOR_H #define QV4EXECUTABLEALLOCATOR_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include <QMultiMap> diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index be63b31e1e..66b2125a4f 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -34,10 +34,10 @@ #include "qv4function_p.h" #include "qv4managed_p.h" #include "qv4string_p.h" -#include "qv4value_inl_p.h" +#include "qv4value_p.h" #include "qv4engine_p.h" #include "qv4lookup_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> QT_BEGIN_NAMESPACE @@ -70,14 +70,45 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, arg = mm->alloc<String>(mm, arg->d(), engine->newString(QString(0xfffe))); } } + nFormals = compiledFunction->nFormals; const quint32 *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable); + + activationRequired = compiledFunction->nInnerFunctions > 0 || (compiledFunction->flags & (CompiledData::Function::HasDirectEval | CompiledData::Function::UsesArgumentsObject)); } Function::~Function() { } +void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters) +{ + internalClass = engine->emptyClass; + + // iterate backwards, so we get the right ordering for duplicate names + Scope scope(engine); + ScopedString arg(scope); + for (int i = parameters.size() - 1; i >= 0; --i) { + arg = engine->newString(QString::fromUtf8(parameters.at(i))); + while (1) { + InternalClass *newClass = internalClass->addMember(arg, Attr_NotConfigurable); + if (newClass != internalClass) { + internalClass = newClass; + break; + } + // duplicate arguments, need some trick to store them + arg = engine->memoryManager->alloc<String>(engine->memoryManager, arg->d(), engine->newString(QString(0xfffe))); + } + } + nFormals = parameters.size(); + + const quint32 *localsIndices = compiledFunction->localsTable(); + for (quint32 i = 0; i < compiledFunction->nLocals; ++i) + internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable); + + activationRequired = true; +} + QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 10a03bca94..0e1216a45b 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -33,6 +33,17 @@ #ifndef QV4FUNCTION_H #define QV4FUNCTION_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include <private/qv4compileddata_p.h> @@ -49,11 +60,16 @@ struct Q_QML_EXPORT Function { // first nArguments names in internalClass are the actual arguments InternalClass *internalClass; + uint nFormals; + bool activationRequired; Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, ReturnedValue (*codePtr)(ExecutionEngine *, const uchar *)); ~Function(); + // used when dynamically assigning signal handlers (QQmlConnection) + void updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters); + inline Heap::String *name() { return compilationUnit->runtimeStrings[compiledFunction->nameIndex]; } @@ -64,7 +80,7 @@ struct Q_QML_EXPORT Function { inline bool isNamedExpression() const { return compiledFunction->flags & CompiledData::Function::IsNamedExpression; } inline bool needsActivation() const - { return compiledFunction->nInnerFunctions > 0 || (compiledFunction->flags & (CompiledData::Function::HasDirectEval | CompiledData::Function::UsesArgumentsObject)); } + { return activationRequired; } }; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 0a12d013ac..1194033872 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -37,7 +37,7 @@ #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" #include "qv4function_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4arrayobject_p.h" #include "qv4scopedvalue_p.h" @@ -46,7 +46,7 @@ #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> -#include <private/qqmlcontextwrapper_p.h> +#include <private/qqmljavascriptexpression_p.h> #include <private/qqmlengine_p.h> #include <qv4codegen_p.h> #include "private/qlocale_tools_p.h" @@ -63,8 +63,7 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(FunctionObject); Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, QV4::String *name, bool createProto) - : Heap::Object(scope->d()->engine->functionClass, scope->d()->engine->functionPrototype.asObject()) - , scope(scope->d()) + : scope(scope->d()) , function(Q_NULLPTR) { Scope s(scope->engine()); @@ -73,8 +72,7 @@ Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, QV4::String * } Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, Function *function, bool createProto) - : Heap::Object(scope->d()->engine->functionClass, scope->d()->engine->functionPrototype.asObject()) - , scope(scope->d()) + : scope(scope->d()) , function(Q_NULLPTR) { Scope s(scope->engine()); @@ -84,8 +82,7 @@ Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, Function *fun } Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, const QString &name, bool createProto) - : Heap::Object(scope->d()->engine->functionClass, scope->d()->engine->functionPrototype.asObject()) - , scope(scope->d()) + : scope(scope->d()) , function(Q_NULLPTR) { Scope s(scope->engine()); @@ -95,8 +92,7 @@ Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, const QString } Heap::FunctionObject::FunctionObject(ExecutionContext *scope, const QString &name, bool createProto) - : Heap::Object(scope->engine->functionClass, scope->engine->functionPrototype.asObject()) - , scope(scope) + : scope(scope) , function(Q_NULLPTR) { Scope s(scope->engine); @@ -106,8 +102,7 @@ Heap::FunctionObject::FunctionObject(ExecutionContext *scope, const QString &nam } Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, const ReturnedValue name) - : Heap::Object(scope->d()->engine->functionClass, scope->d()->engine->functionPrototype.asObject()) - , scope(scope->d()) + : scope(scope->d()) , function(Q_NULLPTR) { Scope s(scope); @@ -117,8 +112,7 @@ Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, const Returne } Heap::FunctionObject::FunctionObject(ExecutionContext *scope, const ReturnedValue name) - : Heap::Object(scope->engine->functionClass, scope->engine->functionPrototype.asObject()) - , scope(scope) + : scope(scope) , function(Q_NULLPTR) { Scope s(scope->engine); @@ -127,15 +121,12 @@ Heap::FunctionObject::FunctionObject(ExecutionContext *scope, const ReturnedValu f->init(n, false); } -Heap::FunctionObject::FunctionObject(InternalClass *ic, QV4::Object *prototype) - : Heap::Object(ic, prototype) - , scope(ic->engine->rootContext()) +Heap::FunctionObject::FunctionObject() + : scope(internalClass->engine->rootContext()->d()) , function(Q_NULLPTR) { - Scope scope(ic->engine); - ScopedObject o(scope, this); - o->ensureMemberIndex(ic->engine, Index_Prototype); - memberData->data[Index_Prototype] = Encode::undefined(); + Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()) == Index_Prototype); + *propertyData(Index_Prototype) = Encode::undefined(); } @@ -150,23 +141,23 @@ void FunctionObject::init(String *n, bool createProto) Scope s(internalClass()->engine); ScopedValue protectThis(s, this); - ensureMemberIndex(s.engine, Heap::FunctionObject::Index_Prototype); + Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()) == Heap::FunctionObject::Index_Prototype); if (createProto) { - ScopedObject proto(s, scope()->engine->newObject(s.engine->protoClass, s.engine->objectPrototype.asObject())); - proto->ensureMemberIndex(s.engine, Heap::FunctionObject::Index_ProtoConstructor); - proto->memberData()->data[Heap::FunctionObject::Index_ProtoConstructor] = this->asReturnedValue(); - memberData()->data[Heap::FunctionObject::Index_Prototype] = proto.asReturnedValue(); + ScopedObject proto(s, scope()->engine->newObject(s.engine->protoClass, s.engine->objectPrototype())); + Q_ASSERT(s.engine->protoClass->find(s.engine->id_constructor()) == Heap::FunctionObject::Index_ProtoConstructor); + *proto->propertyData(Heap::FunctionObject::Index_ProtoConstructor) = this->asReturnedValue(); + *propertyData(Heap::FunctionObject::Index_Prototype) = proto.asReturnedValue(); } else { - memberData()->data[Heap::FunctionObject::Index_Prototype] = Encode::undefined(); + *propertyData(Heap::FunctionObject::Index_Prototype) = Encode::undefined(); } ScopedValue v(s, n); - defineReadonlyProperty(s.engine->id_name, v); + defineReadonlyProperty(s.engine->id_name(), v); } -ReturnedValue FunctionObject::name() +ReturnedValue FunctionObject::name() const { - return get(scope()->engine->id_name); + return get(scope()->engine->id_name()); } @@ -177,13 +168,12 @@ ReturnedValue FunctionObject::newInstance() return construct(callData); } -ReturnedValue FunctionObject::construct(Managed *that, CallData *) +ReturnedValue FunctionObject::construct(const Managed *that, CallData *) { - static_cast<FunctionObject *>(that)->internalClass()->engine->throwTypeError(); - return Encode::undefined(); + return static_cast<const FunctionObject *>(that)->engine()->throwTypeError(); } -ReturnedValue FunctionObject::call(Managed *, CallData *) +ReturnedValue FunctionObject::call(const Managed *, CallData *) { return Encode::undefined(); } @@ -203,18 +193,36 @@ Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *sco function->compiledFunction->flags & CompiledData::Function::HasCatchOrWith || function->compiledFunction->nFormals > QV4::Global::ReservedArgumentCount || function->isNamedExpression()) - return scope->d()->engine->memoryManager->alloc<ScriptFunction>(scope, function); - return scope->d()->engine->memoryManager->alloc<SimpleScriptFunction>(scope, function, createProto); + return scope->d()->engine->memoryManager->allocObject<ScriptFunction>(scope, function); + return scope->d()->engine->memoryManager->allocObject<SimpleScriptFunction>(scope, function, createProto); +} + +Heap::FunctionObject *FunctionObject::createQmlFunction(QQmlContextData *qmlContext, QObject *scopeObject, Function *runtimeFunction, const QList<QByteArray> &signalParameters, QString *error) +{ + ExecutionEngine *engine = QQmlEnginePrivate::getV4Engine(qmlContext->engine); + QV4::Scope valueScope(engine); + ExecutionContext *global = valueScope.engine->rootContext(); + QV4::Scoped<QmlContext> wrapperContext(valueScope, global->newQmlContext(qmlContext, scopeObject)); + + if (!signalParameters.isEmpty()) { + if (error) + QQmlPropertyCache::signalParameterStringForJS(engine, signalParameters, error); + runtimeFunction->updateInternalClass(engine, signalParameters); + } + + QV4::ScopedFunctionObject function(valueScope, QV4::FunctionObject::createScriptFunction(wrapperContext, runtimeFunction)); + return function->d(); } + bool FunctionObject::isBinding() const { - return d()->vtable == QQmlBindingFunction::staticVTable(); + return d()->vtable() == QQmlBindingFunction::staticVTable(); } bool FunctionObject::isBoundFunction() const { - return d()->vtable == BoundFunction::staticVTable(); + return d()->vtable() == BoundFunction::staticVTable(); } QQmlSourceLocation FunctionObject::sourceLocation() const @@ -237,11 +245,11 @@ Heap::FunctionCtor::FunctionCtor(QV4::ExecutionContext *scope) } // 15.3.2 -ReturnedValue FunctionCtor::construct(Managed *that, CallData *callData) +ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData) { - Scope scope(static_cast<Object *>(that)->engine()); - Scoped<FunctionCtor> f(scope, static_cast<FunctionCtor *>(that)); - ScopedContext ctx(scope, scope.engine->currentContext()); + Scope scope(static_cast<const Object *>(that)->engine()); + Scoped<FunctionCtor> f(scope, static_cast<const FunctionCtor *>(that)); + QString arguments; QString body; if (callData->argc > 0) { @@ -252,10 +260,10 @@ ReturnedValue FunctionCtor::construct(Managed *that, CallData *callData) } body = callData->args[callData->argc - 1].toQString(); } - if (ctx->d()->engine->hasException) + if (scope.engine->hasException) return Encode::undefined(); - QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1String("}"); + QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1Char('}'); QQmlJS::Engine ee, *engine = ⅇ QQmlJS::Lexer lexer(engine); @@ -282,20 +290,19 @@ ReturnedValue FunctionCtor::construct(Managed *that, CallData *callData) QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = isel->compile(); Function *vmf = compilationUnit->linkToEngine(scope.engine); - ScopedContext global(scope, scope.engine->rootContext()); + ExecutionContext *global = scope.engine->rootContext(); return FunctionObject::createScriptFunction(global, vmf)->asReturnedValue(); } // 15.3.1: This is equivalent to new Function(...) -ReturnedValue FunctionCtor::call(Managed *that, CallData *callData) +ReturnedValue FunctionCtor::call(const Managed *that, CallData *callData) { return construct(that, callData); } DEFINE_OBJECT_VTABLE(FunctionPrototype); -Heap::FunctionPrototype::FunctionPrototype(InternalClass *ic, QV4::Object *prototype) - : Heap::FunctionObject(ic, prototype) +Heap::FunctionPrototype::FunctionPrototype() { } @@ -304,12 +311,12 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1)); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - defineReadonlyProperty(engine->id_length, Primitive::fromInt32(0)); + defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(0)); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString, method_toString, 0); + defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("apply"), method_apply, 2); defineDefaultProperty(QStringLiteral("call"), method_call, 1); defineDefaultProperty(QStringLiteral("bind"), method_bind, 1); @@ -318,7 +325,7 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) ReturnedValue FunctionPrototype::method_toString(CallContext *ctx) { - FunctionObject *fun = ctx->thisObject().asFunctionObject(); + FunctionObject *fun = ctx->thisObject().as<FunctionObject>(); if (!fun) return ctx->engine()->throwTypeError(); @@ -328,7 +335,7 @@ ReturnedValue FunctionPrototype::method_toString(CallContext *ctx) ReturnedValue FunctionPrototype::method_apply(CallContext *ctx) { Scope scope(ctx); - ScopedFunctionObject o(scope, ctx->thisObject().asFunctionObject()); + ScopedFunctionObject o(scope, ctx->thisObject().as<FunctionObject>()); if (!o) return ctx->engine()->throwTypeError(); @@ -370,7 +377,7 @@ ReturnedValue FunctionPrototype::method_call(CallContext *ctx) { Scope scope(ctx); - ScopedFunctionObject o(scope, ctx->thisObject().asFunctionObject()); + ScopedFunctionObject o(scope, ctx->thisObject().as<FunctionObject>()); if (!o) return ctx->engine()->throwTypeError(); @@ -393,12 +400,12 @@ ReturnedValue FunctionPrototype::method_bind(CallContext *ctx) ScopedValue boundThis(scope, ctx->argument(0)); Scoped<MemberData> boundArgs(scope, (Heap::MemberData *)0); if (ctx->argc() > 1) { - boundArgs = MemberData::reallocate(scope.engine, 0, ctx->argc() - 1); + boundArgs = MemberData::allocate(scope.engine, ctx->argc() - 1); boundArgs->d()->size = ctx->argc() - 1; memcpy(boundArgs->data(), ctx->args() + 1, (ctx->argc() - 1)*sizeof(Value)); } - ScopedContext global(scope, scope.engine->rootContext()); + ExecutionContext *global = scope.engine->rootContext(); return BoundFunction::create(global, target, boundThis, boundArgs)->asReturnedValue(); } @@ -409,29 +416,30 @@ Heap::ScriptFunction::ScriptFunction(QV4::ExecutionContext *scope, Function *fun { } -ReturnedValue ScriptFunction::construct(Managed *that, CallData *callData) +ReturnedValue ScriptFunction::construct(const Managed *that, CallData *callData) { - ExecutionEngine *v4 = static_cast<Object *>(that)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(that)->engine(); if (v4->hasException) return Encode::undefined(); CHECK_STACK_LIMITS(v4); Scope scope(v4); - Scoped<ScriptFunction> f(scope, static_cast<ScriptFunction *>(that)); + ExecutionContextSaver ctxSaver(scope); + + Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that)); InternalClass *ic = scope.engine->emptyClass; ScopedObject proto(scope, f->protoForConstructor()); ScopedObject obj(scope, v4->newObject(ic, proto)); - ScopedContext context(scope, v4->currentContext()); callData->thisObject = obj.asReturnedValue(); - Scoped<CallContext> ctx(scope, context->newCallContext(f, callData)); + Scoped<CallContext> ctx(scope, v4->currentContext->newCallContext(f, callData)); + v4->pushContext(ctx); - ExecutionContextSaver ctxSaver(scope, context); ScopedValue result(scope, Q_V4_PROFILE(v4, f->function())); if (f->function()->compiledFunction->hasQmlDependencies()) - QmlContextWrapper::registerQmlDependencies(v4, f->function()->compiledFunction); + QQmlPropertyCapture::registerQmlDependencies(v4, f->function()->compiledFunction); if (v4->hasException) return Encode::undefined(); @@ -441,24 +449,24 @@ ReturnedValue ScriptFunction::construct(Managed *that, CallData *callData) return obj.asReturnedValue(); } -ReturnedValue ScriptFunction::call(Managed *that, CallData *callData) +ReturnedValue ScriptFunction::call(const Managed *that, CallData *callData) { - ExecutionEngine *v4 = static_cast<Object *>(that)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(that)->engine(); if (v4->hasException) return Encode::undefined(); CHECK_STACK_LIMITS(v4); Scope scope(v4); - Scoped<ScriptFunction> f(scope, static_cast<ScriptFunction *>(that)); - ScopedContext context(scope, v4->currentContext()); + ExecutionContextSaver ctxSaver(scope); - Scoped<CallContext> ctx(scope, context->newCallContext(f, callData)); + Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that)); + Scoped<CallContext> ctx(scope, v4->currentContext->newCallContext(f, callData)); + v4->pushContext(ctx); - ExecutionContextSaver ctxSaver(scope, context); ScopedValue result(scope, Q_V4_PROFILE(v4, f->function())); if (f->function()->compiledFunction->hasQmlDependencies()) - QmlContextWrapper::registerQmlDependencies(ctx->d()->engine, f->function()->compiledFunction); + QQmlPropertyCapture::registerQmlDependencies(scope.engine, f->function()->compiledFunction); return result->asReturnedValue(); } @@ -466,7 +474,6 @@ ReturnedValue ScriptFunction::call(Managed *that, CallData *callData) DEFINE_OBJECT_VTABLE(SimpleScriptFunction); Heap::SimpleScriptFunction::SimpleScriptFunction(QV4::ExecutionContext *scope, Function *function, bool createProto) - : Heap::FunctionObject(function->compilationUnit->engine->simpleScriptFunctionClass, function->compilationUnit->engine->functionPrototype.asObject()) { this->scope = scope->d(); @@ -481,40 +488,42 @@ Heap::SimpleScriptFunction::SimpleScriptFunction(QV4::ExecutionContext *scope, F if (createProto) { ScopedString name(s, function->name()); f->init(name, createProto); - f->defineReadonlyProperty(scope->d()->engine->id_length, Primitive::fromInt32(f->formalParameterCount())); + f->defineReadonlyProperty(scope->d()->engine->id_length(), Primitive::fromInt32(f->formalParameterCount())); } else { - f->ensureMemberIndex(s.engine, Index_Length); - memberData->data[Index_Name] = function->name(); - memberData->data[Index_Length] = Primitive::fromInt32(f->formalParameterCount()); + Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()) == Index_Length); + Q_ASSERT(internalClass && internalClass->find(s.engine->id_name()) == Index_Name); + *propertyData(Index_Name) = function->name(); + *propertyData(Index_Length) = Primitive::fromInt32(f->formalParameterCount()); } if (scope->d()->strictMode) { ScopedProperty pd(s); - pd->value = s.engine->thrower; - pd->set = s.engine->thrower; - f->insertMember(scope->d()->engine->id_caller, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - f->insertMember(scope->d()->engine->id_arguments, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + pd->value = s.engine->thrower(); + pd->set = s.engine->thrower(); + f->insertMember(scope->d()->engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + f->insertMember(scope->d()->engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } } -ReturnedValue SimpleScriptFunction::construct(Managed *that, CallData *callData) +ReturnedValue SimpleScriptFunction::construct(const Managed *that, CallData *callData) { - ExecutionEngine *v4 = static_cast<Object *>(that)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(that)->engine(); if (v4->hasException) return Encode::undefined(); CHECK_STACK_LIMITS(v4); Scope scope(v4); - Scoped<SimpleScriptFunction> f(scope, static_cast<SimpleScriptFunction *>(that)); + ExecutionContextSaver ctxSaver(scope); + + Scoped<SimpleScriptFunction> f(scope, static_cast<const SimpleScriptFunction *>(that)); InternalClass *ic = scope.engine->emptyClass; ScopedObject proto(scope, f->protoForConstructor()); callData->thisObject = v4->newObject(ic, proto); - ExecutionContextSaver ctxSaver(scope, v4->currentContext()); - CallContext::Data ctx(v4); - ctx.vtable = CallContext::staticVTable(); + ctx.mm_data = 0; + ctx.setVtable(CallContext::staticVTable()); ctx.strictMode = f->strictMode(); ctx.callData = callData; ctx.function = f->d(); @@ -524,32 +533,34 @@ ReturnedValue SimpleScriptFunction::construct(Managed *that, CallData *callData) ctx.locals = scope.alloc(f->varCount()); for (int i = callData->argc; i < (int)f->formalParameterCount(); ++i) callData->args[i] = Encode::undefined(); - Q_ASSERT(v4->currentContext() == &ctx); + v4->pushContext(&ctx); + Q_ASSERT(v4->current == &ctx); ScopedObject result(scope, Q_V4_PROFILE(v4, f->function())); if (f->function()->compiledFunction->hasQmlDependencies()) - QmlContextWrapper::registerQmlDependencies(v4, f->function()->compiledFunction); + QQmlPropertyCapture::registerQmlDependencies(v4, f->function()->compiledFunction); if (!result) return callData->thisObject.asReturnedValue(); return result.asReturnedValue(); } -ReturnedValue SimpleScriptFunction::call(Managed *that, CallData *callData) +ReturnedValue SimpleScriptFunction::call(const Managed *that, CallData *callData) { - ExecutionEngine *v4 = static_cast<SimpleScriptFunction *>(that)->internalClass()->engine; + ExecutionEngine *v4 = static_cast<const SimpleScriptFunction *>(that)->internalClass()->engine; if (v4->hasException) return Encode::undefined(); CHECK_STACK_LIMITS(v4); Scope scope(v4); - Scoped<SimpleScriptFunction> f(scope, static_cast<SimpleScriptFunction *>(that)); + ExecutionContextSaver ctxSaver(scope); - ExecutionContextSaver ctxSaver(scope, v4->currentContext()); + Scoped<SimpleScriptFunction> f(scope, static_cast<const SimpleScriptFunction *>(that)); CallContext::Data ctx(v4); - ctx.vtable = CallContext::staticVTable(); + ctx.mm_data = 0; + ctx.setVtable(CallContext::staticVTable()); ctx.strictMode = f->strictMode(); ctx.callData = callData; ctx.function = f->d(); @@ -559,12 +570,13 @@ ReturnedValue SimpleScriptFunction::call(Managed *that, CallData *callData) ctx.locals = scope.alloc(f->varCount()); for (int i = callData->argc; i < (int)f->formalParameterCount(); ++i) callData->args[i] = Encode::undefined(); - Q_ASSERT(v4->currentContext() == &ctx); + v4->pushContext(&ctx); + Q_ASSERT(v4->current == &ctx); ScopedValue result(scope, Q_V4_PROFILE(v4, f->function())); if (f->function()->compiledFunction->hasQmlDependencies()) - QmlContextWrapper::registerQmlDependencies(v4, f->function()->compiledFunction); + QQmlPropertyCapture::registerQmlDependencies(v4, f->function()->compiledFunction); return result->asReturnedValue(); } @@ -575,7 +587,7 @@ Heap::Object *SimpleScriptFunction::protoForConstructor() ScopedObject p(scope, protoProperty()); if (p) return p->d(); - return scope.engine->objectPrototype.asObject()->d(); + return scope.engine->objectPrototype()->d(); } @@ -588,51 +600,53 @@ Heap::BuiltinFunction::BuiltinFunction(QV4::ExecutionContext *scope, QV4::String { } -ReturnedValue BuiltinFunction::construct(Managed *f, CallData *) +ReturnedValue BuiltinFunction::construct(const Managed *f, CallData *) { - return static_cast<BuiltinFunction *>(f)->internalClass()->engine->throwTypeError(); + return static_cast<const BuiltinFunction *>(f)->internalClass()->engine->throwTypeError(); } -ReturnedValue BuiltinFunction::call(Managed *that, CallData *callData) +ReturnedValue BuiltinFunction::call(const Managed *that, CallData *callData) { - BuiltinFunction *f = static_cast<BuiltinFunction *>(that); + const BuiltinFunction *f = static_cast<const BuiltinFunction *>(that); ExecutionEngine *v4 = f->internalClass()->engine; if (v4->hasException) return Encode::undefined(); CHECK_STACK_LIMITS(v4); Scope scope(v4); - ExecutionContextSaver ctxSaver(scope, v4->currentContext()); + ExecutionContextSaver ctxSaver(scope); CallContext::Data ctx(v4); - ctx.vtable = CallContext::staticVTable(); + ctx.mm_data = 0; + ctx.setVtable(CallContext::staticVTable()); ctx.strictMode = f->scope()->strictMode; // ### needed? scope or parent context? ctx.callData = callData; - Q_ASSERT(v4->currentContext() == &ctx); - Scoped<CallContext> sctx(scope, &ctx); + v4->pushContext(&ctx); + Q_ASSERT(v4->current == &ctx); - return f->d()->code(sctx); + return f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext)); } -ReturnedValue IndexedBuiltinFunction::call(Managed *that, CallData *callData) +ReturnedValue IndexedBuiltinFunction::call(const Managed *that, CallData *callData) { - IndexedBuiltinFunction *f = static_cast<IndexedBuiltinFunction *>(that); + const IndexedBuiltinFunction *f = static_cast<const IndexedBuiltinFunction *>(that); ExecutionEngine *v4 = f->internalClass()->engine; if (v4->hasException) return Encode::undefined(); CHECK_STACK_LIMITS(v4); Scope scope(v4); - ExecutionContextSaver ctxSaver(scope, v4->currentContext()); + ExecutionContextSaver ctxSaver(scope); CallContext::Data ctx(v4); - ctx.vtable = CallContext::staticVTable(); + ctx.mm_data = 0; + ctx.setVtable(CallContext::staticVTable()); ctx.strictMode = f->scope()->strictMode; // ### needed? scope or parent context? ctx.callData = callData; - Q_ASSERT(v4->currentContext() == &ctx); - Scoped<CallContext> sctx(scope, &ctx); + v4->pushContext(&ctx); + Q_ASSERT(v4->current == &ctx); - return f->d()->code(sctx, f->d()->index); + return f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext), f->d()->index); } DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); @@ -650,24 +664,24 @@ Heap::BoundFunction::BoundFunction(QV4::ExecutionContext *scope, QV4::FunctionOb Scope s(scope); ScopedObject f(s, this); - ScopedValue l(s, target->get(s.engine->id_length)); + ScopedValue l(s, target->get(s.engine->id_length())); int len = l->toUInt32(); if (boundArgs) len -= boundArgs->size(); if (len < 0) len = 0; - f->defineReadonlyProperty(s.engine->id_length, Primitive::fromInt32(len)); + f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(len)); ScopedProperty pd(s); - pd->value = s.engine->thrower; - pd->set = s.engine->thrower; - f->insertMember(s.engine->id_arguments, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - f->insertMember(s.engine->id_caller, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + pd->value = s.engine->thrower(); + pd->set = s.engine->thrower(); + f->insertMember(s.engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } -ReturnedValue BoundFunction::call(Managed *that, CallData *dd) +ReturnedValue BoundFunction::call(const Managed *that, CallData *dd) { - BoundFunction *f = static_cast<BoundFunction *>(that); + const BoundFunction *f = static_cast<const BoundFunction *>(that); Scope scope(f->engine()); if (scope.hasException()) return Encode::undefined(); @@ -685,9 +699,9 @@ ReturnedValue BoundFunction::call(Managed *that, CallData *dd) return t->call(callData); } -ReturnedValue BoundFunction::construct(Managed *that, CallData *dd) +ReturnedValue BoundFunction::construct(const Managed *that, CallData *dd) { - BoundFunction *f = static_cast<BoundFunction *>(that); + const BoundFunction *f = static_cast<const BoundFunction *>(that); Scope scope(f->engine()); if (scope.hasException()) return Encode::undefined(); @@ -707,7 +721,8 @@ ReturnedValue BoundFunction::construct(Managed *that, CallData *dd) void BoundFunction::markObjects(Heap::Base *that, ExecutionEngine *e) { BoundFunction::Data *o = static_cast<BoundFunction::Data *>(that); - o->target->mark(e); + if (o->target) + o->target->mark(e); o->boundThis.mark(e); if (o->boundArgs) o->boundArgs->mark(e); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 252ff40a1a..896bd2a4d2 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -33,10 +33,21 @@ #ifndef QV4FUNCTIONOBJECT_H #define QV4FUNCTIONOBJECT_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" #include "qv4function_p.h" #include "qv4context_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> QT_BEGIN_NAMESPACE @@ -58,14 +69,14 @@ struct Q_QML_PRIVATE_EXPORT FunctionObject : Object { FunctionObject(ExecutionContext *scope, const QString &name = QString(), bool createProto = false); FunctionObject(QV4::ExecutionContext *scope, const ReturnedValue name); FunctionObject(ExecutionContext *scope, const ReturnedValue name); - FunctionObject(InternalClass *ic, QV4::Object *prototype); + FunctionObject(); ~FunctionObject(); - unsigned int formalParameterCount() { return function ? function->compiledFunction->nFormals : 0; } + unsigned int formalParameterCount() { return function ? function->nFormals : 0; } unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; } bool needsActivation() const { return function ? function->needsActivation() : false; } - ExecutionContext *scope; + Pointer<ExecutionContext> scope; Function *function; }; @@ -74,7 +85,7 @@ struct FunctionCtor : FunctionObject { }; struct FunctionPrototype : FunctionObject { - FunctionPrototype(InternalClass *ic, QV4::Object *prototype); + FunctionPrototype(); }; struct Q_QML_EXPORT BuiltinFunction : FunctionObject { @@ -102,9 +113,9 @@ struct ScriptFunction : SimpleScriptFunction { struct BoundFunction : FunctionObject { BoundFunction(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs); - FunctionObject *target; + Pointer<FunctionObject> target; Value boundThis; - MemberData *boundArgs; + Pointer<MemberData> boundArgs; }; } @@ -115,14 +126,16 @@ struct Q_QML_EXPORT FunctionObject: Object { }; V4_OBJECT2(FunctionObject, Object) Q_MANAGED_TYPE(FunctionObject) + V4_INTERNALCLASS(functionClass) + V4_PROTOTYPE(functionPrototype) V4_NEEDS_DESTROY - Heap::ExecutionContext *scope() { return d()->scope; } - Function *function() { return d()->function; } + Heap::ExecutionContext *scope() const { return d()->scope; } + Function *function() const { return d()->function; } - ReturnedValue name(); - unsigned int formalParameterCount() { return d()->formalParameterCount(); } - unsigned int varCount() { return d()->varCount(); } + ReturnedValue name() const; + unsigned int formalParameterCount() const { return d()->formalParameterCount(); } + unsigned int varCount() const { return d()->varCount(); } void init(String *name, bool createProto); @@ -130,16 +143,14 @@ struct Q_QML_EXPORT FunctionObject: Object { using Object::construct; using Object::call; - static ReturnedValue construct(Managed *that, CallData *); - static ReturnedValue call(Managed *that, CallData *d); - - static FunctionObject *cast(const Value &v) { - return v.asFunctionObject(); - } + static ReturnedValue construct(const Managed *that, CallData *); + static ReturnedValue call(const Managed *that, CallData *d); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function, bool createProto = true); + static Heap::FunctionObject *createQmlFunction(QQmlContextData *qmlContext, QObject *scopeObject, QV4::Function *runtimeFunction, + const QList<QByteArray> &signalParameters = QList<QByteArray>(), QString *error = 0); - ReturnedValue protoProperty() { return memberData()->data[Heap::FunctionObject::Index_Prototype].asReturnedValue(); } + ReturnedValue protoProperty() { return propertyData(Heap::FunctionObject::Index_Prototype)->asReturnedValue(); } bool needsActivation() const { return d()->needsActivation(); } bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } @@ -152,16 +163,17 @@ struct Q_QML_EXPORT FunctionObject: Object { }; template<> -inline FunctionObject *value_cast(const Value &v) { - return v.asFunctionObject(); +inline const FunctionObject *Value::as() const { + return isManaged() && m() && m()->vtable()->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : 0; } + struct FunctionCtor: FunctionObject { V4_OBJECT2(FunctionCtor, FunctionObject) - static ReturnedValue construct(Managed *that, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *that, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct FunctionPrototype: FunctionObject @@ -181,23 +193,23 @@ struct Q_QML_EXPORT BuiltinFunction: FunctionObject { static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(CallContext *)) { - return scope->engine()->memoryManager->alloc<BuiltinFunction>(scope, name, code); + return scope->engine()->memoryManager->allocObject<BuiltinFunction>(scope, name, code); } - static ReturnedValue construct(Managed *, CallData *); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *, CallData *); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct IndexedBuiltinFunction: FunctionObject { V4_OBJECT2(IndexedBuiltinFunction, FunctionObject) - static ReturnedValue construct(Managed *m, CallData *) + static ReturnedValue construct(const Managed *m, CallData *) { - return static_cast<IndexedBuiltinFunction *>(m)->engine()->throwTypeError(); + return static_cast<const IndexedBuiltinFunction *>(m)->engine()->throwTypeError(); } - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; Heap::IndexedBuiltinFunction::IndexedBuiltinFunction(QV4::ExecutionContext *scope, uint index, @@ -211,9 +223,10 @@ Heap::IndexedBuiltinFunction::IndexedBuiltinFunction(QV4::ExecutionContext *scop struct SimpleScriptFunction: FunctionObject { V4_OBJECT2(SimpleScriptFunction, FunctionObject) + V4_INTERNALCLASS(simpleScriptFunctionClass) - static ReturnedValue construct(Managed *, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); Heap::Object *protoForConstructor(); }; @@ -221,8 +234,8 @@ struct SimpleScriptFunction: FunctionObject { struct ScriptFunction: SimpleScriptFunction { V4_OBJECT2(ScriptFunction, FunctionObject) - static ReturnedValue construct(Managed *, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; @@ -231,15 +244,15 @@ struct BoundFunction: FunctionObject { static Heap::BoundFunction *create(ExecutionContext *scope, FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs) { - return scope->engine()->memoryManager->alloc<BoundFunction>(scope, target, boundThis, boundArgs); + return scope->engine()->memoryManager->allocObject<BoundFunction>(scope, target, boundThis, boundArgs); } - Heap::FunctionObject *target() { return d()->target; } + Heap::FunctionObject *target() const { return d()->target; } Value boundThis() const { return d()->boundThis; } Heap::MemberData *boundArgs() const { return d()->boundArgs; } - static ReturnedValue construct(Managed *, CallData *d); - static ReturnedValue call(Managed *that, CallData *dd); + static ReturnedValue construct(const Managed *, CallData *d); + static ReturnedValue call(const Managed *that, CallData *dd); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 4b08194b60..01a21ea06d 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -34,6 +34,17 @@ #ifndef QV4GLOBAL_H #define QV4GLOBAL_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) #define V4_BOOTSTRAP #endif @@ -88,6 +99,8 @@ inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } #define V4_ENABLE_JIT #endif +#elif defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) +#define V4_ENABLE_JIT #endif // Black list some platforms @@ -168,7 +181,7 @@ struct Property; struct Value; struct Lookup; struct ArrayData; -struct ManagedVTable; +struct VTable; struct BooleanObject; struct NumberObject; @@ -211,7 +224,6 @@ class WeakValue; struct IdentifierTable; class RegExpCache; class MultiplyWrappedQObjectMap; -struct QmlExtensions; namespace Global { enum { @@ -277,8 +289,6 @@ struct PropertyAttributes setConfigurable(!(f & Attr_NotConfigurable)); } } - PropertyAttributes(const PropertyAttributes &other) : m_all(other.m_all) {} - PropertyAttributes & operator=(const PropertyAttributes &other) { m_all = other.m_all; return *this; } void setType(Type t) { m_type = t; type_set = true; } Type type() const { return type_set ? (Type)m_type : Generic; } diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 8e33cec57f..110a2c9089 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -32,13 +32,15 @@ ****************************************************************************/ #include "qv4globalobject_p.h" -#include "qv4mm_p.h" -#include "qv4value_inl_p.h" +#include <private/qv4mm_p.h> +#include "qv4value_p.h" #include "qv4context_p.h" #include "qv4function_p.h" #include "qv4debugging_p.h" +#include "qv4profiling_p.h" #include "qv4script_p.h" #include "qv4scopedvalue_p.h" +#include "qv4string_p.h" #include <private/qqmljsengine_p.h> #include <private/qqmljslexer_p.h> @@ -47,6 +49,7 @@ #include <qv4jsir_p.h> #include <qv4codegen_p.h> #include "private/qlocale_tools_p.h" +#include "private/qtools_p.h" #include <QtCore/QDebug> #include <QtCore/QString> @@ -56,25 +59,8 @@ #include <wtf/MathExtras.h> using namespace QV4; - - -static inline char toHex(char c) -{ - static const char hexnumbers[] = "0123456789ABCDEF"; - return hexnumbers[c & 0xf]; -} - -static int fromHex(QChar ch) -{ - ushort c = ch.unicode(); - if ((c >= '0') && (c <= '9')) - return c - '0'; - if ((c >= 'A') && (c <= 'F')) - return c - 'A' + 10; - if ((c >= 'a') && (c <= 'f')) - return c - 'a' + 10; - return -1; -} +using QtMiscUtils::toHexUpper; +using QtMiscUtils::fromHex; static QString escape(const QString &input) { @@ -93,16 +79,16 @@ static QString escape(const QString &input) output.append(QChar(uc)); } else { output.append('%'); - output.append(QLatin1Char(toHex(uc >> 4))); - output.append(QLatin1Char(toHex(uc))); + output.append(QLatin1Char(toHexUpper(uc >> 4))); + output.append(QLatin1Char(toHexUpper(uc))); } } else { output.append('%'); output.append('u'); - output.append(QLatin1Char(toHex(uc >> 12))); - output.append(QLatin1Char(toHex(uc >> 8))); - output.append(QLatin1Char(toHex(uc >> 4))); - output.append(QLatin1Char(toHex(uc))); + output.append(QLatin1Char(toHexUpper(uc >> 12))); + output.append(QLatin1Char(toHexUpper(uc >> 8))); + output.append(QLatin1Char(toHexUpper(uc >> 4))); + output.append(QLatin1Char(toHexUpper(uc))); } } return output; @@ -119,10 +105,10 @@ static QString unescape(const QString &input) if ((c == '%') && (i + 1 < length)) { QChar a = input.at(i); if ((a == 'u') && (i + 4 < length)) { - int d3 = fromHex(input.at(i+1)); - int d2 = fromHex(input.at(i+2)); - int d1 = fromHex(input.at(i+3)); - int d0 = fromHex(input.at(i+4)); + int d3 = fromHex(input.at(i+1).unicode()); + int d2 = fromHex(input.at(i+2).unicode()); + int d1 = fromHex(input.at(i+3).unicode()); + int d0 = fromHex(input.at(i+4).unicode()); if ((d3 != -1) && (d2 != -1) && (d1 != -1) && (d0 != -1)) { ushort uc = ushort((d3 << 12) | (d2 << 8) | (d1 << 4) | d0); result.append(QChar(uc)); @@ -131,8 +117,8 @@ static QString unescape(const QString &input) result.append(c); } } else { - int d1 = fromHex(a); - int d0 = fromHex(input.at(i+1)); + int d1 = fromHex(a.unicode()); + int d0 = fromHex(input.at(i+1).unicode()); if ((d1 != -1) && (d0 != -1)) { c = (d1 << 4) | d0; i += 2; @@ -153,8 +139,8 @@ static const char uriUnescapedReserved[] = "-_.!~*'();/?:@&=+$,#"; static void addEscapeSequence(QString &output, uchar ch) { output.append(QLatin1Char('%')); - output.append(QLatin1Char(toHex(ch >> 4))); - output.append(QLatin1Char(toHex(ch & 0xf))); + output.append(QLatin1Char(toHexUpper(ch >> 4))); + output.append(QLatin1Char(toHexUpper(ch & 0xf))); } static QString encode(const QString &input, const char *unescapedSet, bool *ok) @@ -246,8 +232,8 @@ static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) if (i + 2 >= length) goto error; - int d1 = fromHex(input.at(i+1)); - int d0 = fromHex(input.at(i+2)); + int d1 = fromHex(input.at(i+1).unicode()); + int d0 = fromHex(input.at(i+2).unicode()); if ((d1 == -1) || (d0 == -1)) goto error; @@ -281,8 +267,8 @@ static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) if (input.at(i) != percent) goto error; - d1 = fromHex(input.at(i+1)); - d0 = fromHex(input.at(i+2)); + d1 = fromHex(input.at(i+1).unicode()); + d0 = fromHex(input.at(i+2).unicode()); if ((d1 == -1) || (d0 == -1)) goto error; @@ -339,25 +325,24 @@ static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) DEFINE_OBJECT_VTABLE(EvalFunction); Heap::EvalFunction::EvalFunction(QV4::ExecutionContext *scope) - : Heap::FunctionObject(scope, scope->d()->engine->id_eval) + : Heap::FunctionObject(scope, scope->d()->engine->id_eval()) { Scope s(scope); ScopedFunctionObject f(s, this); - f->defineReadonlyProperty(s.engine->id_length, Primitive::fromInt32(1)); + f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1)); } -ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) +ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const { if (callData->argc < 1) return Encode::undefined(); ExecutionEngine *v4 = engine(); Scope scope(v4); + ExecutionContextSaver ctxSaver(scope); - ScopedContext parentContext(scope, v4->currentContext()); - ExecutionContextSaver ctxSaver(scope, parentContext); - - ScopedContext ctx(scope, parentContext.getPointer()); + ExecutionContext *currentContext = v4->currentContext; + ExecutionContext *ctx = currentContext; if (!directCall) { // the context for eval should be the global scope, so we fake a root @@ -372,10 +357,10 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) bool inheritContext = !ctx->d()->strictMode; Script script(ctx, code, QStringLiteral("eval code")); - script.strictMode = (directCall && parentContext->d()->strictMode); + script.strictMode = (directCall && currentContext->d()->strictMode); script.inheritContext = inheritContext; script.parse(); - if (scope.engine->hasException) + if (v4->hasException) return Encode::undefined(); Function *function = script.function(); @@ -395,14 +380,14 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) ctx->d()->strictMode = false; ctx->d()->compilationUnit = function->compilationUnit; - return function->code(ctx->engine(), function->codeData); + return Q_V4_PROFILE(ctx->engine(), function); } -ReturnedValue EvalFunction::call(Managed *that, CallData *callData) +ReturnedValue EvalFunction::call(const Managed *that, CallData *callData) { // indirect call - return static_cast<EvalFunction *>(that)->evalCall(callData, false); + return static_cast<const EvalFunction *>(that)->evalCall(callData, false); } diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h index 74de233b47..ced85621de 100644 --- a/src/qml/jsruntime/qv4globalobject_p.h +++ b/src/qml/jsruntime/qv4globalobject_p.h @@ -33,6 +33,17 @@ #ifndef QV4GLOBALOBJECT_H #define QV4GLOBALOBJECT_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include "qv4functionobject_p.h" @@ -52,10 +63,10 @@ struct Q_QML_EXPORT EvalFunction : FunctionObject { V4_OBJECT2(EvalFunction, FunctionObject) - ReturnedValue evalCall(CallData *callData, bool directCall); + ReturnedValue evalCall(CallData *callData, bool directCall) const; using Object::construct; - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct GlobalFunctions diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h index 7937391ff7..605b06c685 100644 --- a/src/qml/jsruntime/qv4identifier_p.h +++ b/src/qml/jsruntime/qv4identifier_p.h @@ -33,6 +33,17 @@ #ifndef QV4IDENTIFIER_H #define QV4IDENTIFIER_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <qstring.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index 138d76bf4a..a5336ee44f 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -149,6 +149,22 @@ Identifier *IdentifierTable::identifierImpl(const Heap::String *str) return str->identifier; } +Heap::String *IdentifierTable::stringFromIdentifier(Identifier *i) +{ + if (!i) + return 0; + + uint idx = i->hashValue % alloc; + while (1) { + Heap::String *e = entries[idx]; + Q_ASSERT(e); + if (e->identifier == i) + return e; + ++idx; + idx %= alloc; + } +} + Identifier *IdentifierTable::identifier(const QString &s) { return insertString(s)->identifier; diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h index ff374225f4..3af9db963e 100644 --- a/src/qml/jsruntime/qv4identifiertable_p.h +++ b/src/qml/jsruntime/qv4identifiertable_p.h @@ -33,6 +33,17 @@ #ifndef QV4IDENTIFIERTABLE_H #define QV4IDENTIFIERTABLE_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4identifier_p.h" #include "qv4string_p.h" #include "qv4engine_p.h" @@ -74,14 +85,16 @@ public: Identifier *identifierImpl(const Heap::String *str); + Heap::String *stringFromIdentifier(Identifier *i); + void mark(ExecutionEngine *e) { for (int i = 0; i < alloc; ++i) { Heap::String *entry = entries[i]; if (!entry || entry->isMarked()) continue; entry->setMarkBit(); - Q_ASSERT(entry->gcGetVtable()->markObjects); - entry->gcGetVtable()->markObjects(entry, e); + Q_ASSERT(entry->vtable()->markObjects); + entry->vtable()->markObjects(entry, e); } } }; diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index e4bd460966..90c6738c46 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -49,12 +49,13 @@ QT_BEGIN_NAMESPACE -QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, QQmlContextData *context, - const QV4::Value &qmlglobal, const QV4::Value &callback) - : v4(engine), m_network(0), m_reply(0), m_url(url), m_redirectCount(0), m_context(context) +QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, + QV4::QmlContext *qmlContext, const QV4::Value &callback) + : v4(engine), m_network(0), m_reply(0), m_url(url), m_redirectCount(0) { - m_qmlglobal.set(engine, qmlglobal); - if (callback.asFunctionObject()) + if (qmlContext) + m_qmlContext.set(engine, *qmlContext); + if (callback.as<QV4::FunctionObject>()) m_callbackFunction.set(engine, callback); m_resultObject.set(v4, resultValue(v4)); @@ -94,14 +95,14 @@ void QV4Include::callback(const QV4::Value &callback, const QV4::Value &status) { if (!callback.isObject()) return; - QV4::ExecutionEngine *v4 = callback.asObject()->engine(); + QV4::ExecutionEngine *v4 = callback.as<QV4::Object>()->engine(); QV4::Scope scope(v4); QV4::ScopedFunctionObject f(scope, callback); if (!f) return; QV4::ScopedCallData callData(scope, 1); - callData->thisObject = v4->globalObject()->asReturnedValue(); + callData->thisObject = v4->globalObject->asReturnedValue(); callData->args[0] = status; f->call(callData); if (scope.hasException()) @@ -142,8 +143,8 @@ void QV4Include::finished() QString code = QString::fromUtf8(data); QmlIR::Document::removeScriptPragmas(code); - QV4::ScopedObject qmlglobal(scope, m_qmlglobal.value()); - QV4::Script script(v4, qmlglobal, code, m_url.toString()); + QV4::Scoped<QV4::QmlContext> qml(scope, m_qmlContext.value()); + QV4::Script script(v4, qml, code, m_url.toString()); script.parse(); if (!scope.engine->hasException) @@ -176,7 +177,7 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) return QV4::Encode::undefined(); QV4::Scope scope(ctx->engine()); - QQmlContextData *context = QV4::QmlContextWrapper::callingContext(scope.engine); + QQmlContextData *context = scope.engine->callingQmlContext(); if (!context || !context->isJSContext) V4THROW_ERROR("Qt.include(): Can only be called from JavaScript files"); @@ -184,18 +185,16 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) QUrl url(scope.engine->resolvedUrl(ctx->args()[0].toQStringNoThrow())); QV4::ScopedValue callbackFunction(scope, QV4::Primitive::undefinedValue()); - if (ctx->argc() >= 2 && ctx->args()[1].asFunctionObject()) + if (ctx->argc() >= 2 && ctx->args()[1].as<QV4::FunctionObject>()) callbackFunction = ctx->args()[1]; QString localFile = QQmlFile::urlToLocalFileOrQrc(url); QV4::ScopedValue result(scope); - QV4::ScopedObject qmlcontextobject(scope, scope.engine->qmlContextObject()); + QV4::Scoped<QV4::QmlContext> qmlcontext(scope, scope.engine->qmlContext()); if (localFile.isEmpty()) { - QV4Include *i = new QV4Include(url, scope.engine, context, - qmlcontextobject, - callbackFunction); + QV4Include *i = new QV4Include(url, scope.engine, qmlcontext, callbackFunction); result = i->result(); } else { @@ -203,7 +202,7 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) if (const QQmlPrivate::CachedQmlUnit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(url)) { QV4::CompiledData::CompilationUnit *jsUnit = cachedUnit->createCompilationUnit(); - script.reset(new QV4::Script(scope.engine, qmlcontextobject, jsUnit)); + script.reset(new QV4::Script(scope.engine, qmlcontext, jsUnit)); } else { QFile f(localFile); @@ -212,7 +211,7 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) QString code = QString::fromUtf8(data); QmlIR::Document::removeScriptPragmas(code); - script.reset(new QV4::Script(scope.engine, qmlcontextobject, code, url.toString())); + script.reset(new QV4::Script(scope.engine, qmlcontext, code, url.toString())); } } @@ -224,7 +223,7 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) QV4::ScopedValue ex(scope, scope.engine->catchException()); result = resultValue(scope.engine, Exception); QV4::ScopedString exception(scope, scope.engine->newString(QStringLiteral("exception"))); - result->asObject()->put(exception, ex); + result->as<QV4::Object>()->put(exception, ex); } else { result = resultValue(scope.engine, Ok); } diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h index 5dc94e8555..3e3cf5e770 100644 --- a/src/qml/jsruntime/qv4include_p.h +++ b/src/qml/jsruntime/qv4include_p.h @@ -50,7 +50,7 @@ #include <private/qqmlcontext_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4context_p.h> QT_BEGIN_NAMESPACE @@ -76,8 +76,7 @@ private Q_SLOTS: void finished(); private: - QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, QQmlContextData *context, - const QV4::Value &qmlglobal, const QV4::Value &callback); + QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &callback); ~QV4Include(); QV4::ReturnedValue result(); @@ -95,8 +94,7 @@ private: QV4::PersistentValue m_callbackFunction; QV4::PersistentValue m_resultObject; - QQmlGuardedContextData m_context; - QV4::PersistentValue m_qmlglobal; + QV4::PersistentValue m_qmlContext; }; QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index a90e8e3689..8f0b1776d7 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -134,21 +134,64 @@ InternalClass::InternalClass(const QV4::InternalClass &other) Q_ASSERT(extensible); } +static void insertHoleIntoPropertyData(Object *object, int idx) +{ + int inlineSize = object->d()->inlineMemberSize; + int icSize = object->internalClass()->size; + int from = qMax(idx, inlineSize); + int to = from + 1; + if (from < icSize) + memmove(object->propertyData(to), object->propertyData(from), icSize - from - 1); + if (from == idx) + return; + if (inlineSize < icSize) + *object->propertyData(inlineSize) = *object->propertyData(inlineSize - 1); + from = idx; + to = from + 1; + if (from < inlineSize - 1) + memmove(object->propertyData(to), object->propertyData(from), inlineSize - from - 1); +} + +static void removeFromPropertyData(Object *object, int idx, bool accessor = false) +{ + int inlineSize = object->d()->inlineMemberSize; + int icSize = object->internalClass()->size; + int delta = (accessor ? 2 : 1); + int to = idx; + int from = to + delta; + if (from < inlineSize) { + memmove(object->propertyData(to), object->d()->propertyData(from), (inlineSize - from)*sizeof(Value)); + to = inlineSize - delta; + from = inlineSize; + } + if (to < inlineSize && from < icSize) { + Q_ASSERT(from >= inlineSize); + memcpy(object->propertyData(to), object->d()->propertyData(from), (inlineSize - to)*sizeof(Value)); + to = inlineSize; + from = inlineSize + delta; + } + if (from < icSize + delta) { + Q_ASSERT(to >= inlineSize && from > to); + memmove(object->propertyData(to), object->d()->propertyData(from), (icSize + delta - to)*sizeof(Value)); + } +} + void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index) { uint idx; - InternalClass *newClass = object->internalClass()->changeMember(string->identifier(), data, &idx); + InternalClass *oldClass = object->internalClass(); + InternalClass *newClass = oldClass->changeMember(string->identifier(), data, &idx); if (index) *index = idx; - if (newClass->size > object->internalClass()->size) { - Q_ASSERT(newClass->size == object->internalClass()->size + 1); - memmove(object->memberData()->data + idx + 2, object->memberData()->data + idx + 1, (object->internalClass()->size - idx - 1)*sizeof(Value)); - } else if (newClass->size < object->internalClass()->size) { - Q_ASSERT(newClass->size == object->internalClass()->size - 1); - memmove(object->memberData()->data + idx + 1, object->memberData()->data + idx + 2, (object->internalClass()->size - idx - 2)*sizeof(Value)); - } object->setInternalClass(newClass); + if (newClass->size > oldClass->size) { + Q_ASSERT(newClass->size == oldClass->size + 1); + insertHoleIntoPropertyData(object, idx + 1); + } else if (newClass->size < oldClass->size) { + Q_ASSERT(newClass->size == oldClass->size - 1); + removeFromPropertyData(object, idx + 1); + } } InternalClassTransition &InternalClass::lookupOrInsertTransition(const InternalClassTransition &t) @@ -286,6 +329,8 @@ void InternalClass::removeMember(Object *object, Identifier *id) Transition temp = { id, 0, -1 }; Transition &t = object->internalClass()->lookupOrInsertTransition(temp); + bool accessor = oldClass->propertyData.at(propIdx).isAccessor(); + if (t.lookup) { object->setInternalClass(t.lookup); } else { @@ -300,8 +345,10 @@ void InternalClass::removeMember(Object *object, Identifier *id) object->setInternalClass(newClass); } - // remove the entry in memberdata - memmove(object->memberData()->data + propIdx, object->memberData()->data + propIdx + 1, (object->internalClass()->size - propIdx)*sizeof(Value)); + Q_ASSERT(object->internalClass()->size == oldClass->size - (accessor ? 2 : 1)); + + // remove the entry in the property data + removeFromPropertyData(object, propIdx, accessor); t.lookup = object->internalClass(); Q_ASSERT(t.lookup); @@ -352,20 +399,26 @@ InternalClass *InternalClass::frozen() if (m_frozen) return m_frozen; - m_frozen = engine->emptyClass; + m_frozen = propertiesFrozen(); + m_frozen = m_frozen->nonExtensible(); + + m_frozen->m_frozen = m_frozen; + m_frozen->m_sealed = m_frozen; + return m_frozen; +} + +InternalClass *InternalClass::propertiesFrozen() const +{ + InternalClass *frozen = engine->emptyClass; for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) continue; attrs.setWritable(false); attrs.setConfigurable(false); - m_frozen = m_frozen->addMember(nameMap.at(i), attrs); + frozen = frozen->addMember(nameMap.at(i), attrs); } - m_frozen = m_frozen->nonExtensible(); - - m_frozen->m_frozen = m_frozen; - m_frozen->m_sealed = m_frozen; - return m_frozen; + return frozen; } void InternalClass::destroy() diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 3289058cb7..5b91925ede 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -33,6 +33,17 @@ #ifndef QV4INTERNALCLASS_H #define QV4INTERNALCLASS_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include <QHash> @@ -46,7 +57,7 @@ struct String; struct ExecutionEngine; struct Object; struct Identifier; -struct ManagedVTable; +struct VTable; struct PropertyHashData; struct PropertyHash @@ -233,6 +244,7 @@ struct InternalClass : public QQmlJS::Managed { InternalClass *sealed(); InternalClass *frozen(); + InternalClass *propertiesFrozen() const; void destroy(); diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index e7905974df..2e5283c639 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -38,8 +38,8 @@ #include <qv4objectiterator_p.h> #include <qv4scopedvalue_p.h> #include <qv4runtime_p.h> +#include "qv4string_p.h" -#include <qjsondocument.h> #include <qstack.h> #include <qstringlist.h> @@ -62,33 +62,6 @@ static int indent = 0; DEFINE_OBJECT_VTABLE(JsonObject); -class JsonParser -{ -public: - JsonParser(ExecutionEngine *engine, const QChar *json, int length); - - ReturnedValue parse(QJsonParseError *error); - -private: - inline bool eatSpace(); - inline QChar nextToken(); - - ReturnedValue parseObject(); - ReturnedValue parseArray(); - bool parseMember(Object *o); - bool parseString(QString *string); - bool parseValue(Value *val); - bool parseNumber(Value *val); - - ExecutionEngine *engine; - const QChar *head; - const QChar *json; - const QChar *end; - - int nestingLevel; - QJsonParseError::ParseError lastError; -}; - static const int nestingLimit = 1024; @@ -639,17 +612,22 @@ bool JsonParser::parseString(QString *string) struct Stringify { - ExecutionContext *ctx; + ExecutionEngine *v4; FunctionObject *replacerFunction; - // ### GC - QVector<Heap::String *> propertyList; + QV4::String *propertyList; + int propertyListSize; QString gap; QString indent; + QStack<Object *> stack; - // ### GC - QStack<Heap::Object *> stack; + bool stackContains(Object *o) { + for (int i = 0; i < stack.size(); ++i) + if (stack.at(i)->d() == o->d()) + return true; + return false; + } - Stringify(ExecutionContext *ctx) : ctx(ctx), replacerFunction(0) {} + Stringify(ExecutionEngine *e) : v4(e), replacerFunction(0), propertyList(0), propertyListSize(0) {} QString Str(const QString &key, const Value &v); QString JA(ArrayObject *a); @@ -701,26 +679,26 @@ static QString quote(const QString &str) QString Stringify::Str(const QString &key, const Value &v) { - Scope scope(ctx); + Scope scope(v4); ScopedValue value(scope, v); ScopedObject o(scope, value); if (o) { - ScopedString s(scope, ctx->d()->engine->newString(QStringLiteral("toJSON"))); + ScopedString s(scope, v4->newString(QStringLiteral("toJSON"))); ScopedFunctionObject toJSON(scope, o->get(s)); if (!!toJSON) { ScopedCallData callData(scope, 1); callData->thisObject = value; - callData->args[0] = ctx->d()->engine->newString(key); + callData->args[0] = v4->newString(key); value = toJSON->call(callData); } } if (replacerFunction) { - ScopedObject holder(scope, ctx->d()->engine->newObject()); + ScopedObject holder(scope, v4->newObject()); holder->put(scope.engine, QString(), value); ScopedCallData callData(scope, 2); - callData->args[0] = ctx->d()->engine->newString(key); + callData->args[0] = v4->newString(key); callData->args[1] = value; callData->thisObject = holder; value = replacerFunction->call(callData); @@ -728,11 +706,11 @@ QString Stringify::Str(const QString &key, const Value &v) o = value->asReturnedValue(); if (o) { - if (NumberObject *n = o->asNumberObject()) + if (NumberObject *n = o->as<NumberObject>()) value = Encode(n->value()); - else if (StringObject *so = o->asStringObject()) - value = so->d()->value; - else if (BooleanObject *b =o->asBooleanObject()) + else if (StringObject *so = o->as<StringObject>()) + value = so->d()->string; + else if (BooleanObject *b = o->as<BooleanObject>()) value = Encode(b->value()); } @@ -750,8 +728,8 @@ QString Stringify::Str(const QString &key, const Value &v) o = value->asReturnedValue(); if (o) { - if (!o->asFunctionObject()) { - if (o->asArrayObject()) { + if (!o->as<FunctionObject>()) { + if (o->as<ArrayObject>()) { return JA(static_cast<ArrayObject *>(o.getPointer())); } else { return JO(o); @@ -777,20 +755,20 @@ QString Stringify::makeMember(const QString &key, const Value &v) QString Stringify::JO(Object *o) { - if (stack.contains(o->d())) { - ctx->engine()->throwTypeError(); + if (stackContains(o)) { + v4->throwTypeError(); return QString(); } - Scope scope(ctx); + Scope scope(v4); QString result; - stack.push(o->d()); + stack.push(o); QString stepback = indent; indent += gap; QStringList partial; - if (propertyList.isEmpty()) { + if (!propertyListSize) { ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); ScopedValue name(scope); @@ -805,11 +783,13 @@ QString Stringify::JO(Object *o) partial += member; } } else { - ScopedString s(scope); - for (int i = 0; i < propertyList.size(); ++i) { + ScopedValue v(scope); + for (int i = 0; i < propertyListSize; ++i) { bool exists; - s = propertyList.at(i); - ScopedValue v(scope, o->get(s, &exists)); + String *s = propertyList + i; + if (!s) + continue; + v = o->get(s, &exists); if (!exists) continue; QString member = makeMember(s->toQString(), v); @@ -821,10 +801,11 @@ QString Stringify::JO(Object *o) if (partial.isEmpty()) { result = QStringLiteral("{}"); } else if (gap.isEmpty()) { - result = QStringLiteral("{") + partial.join(QLatin1Char(',')) + QStringLiteral("}"); + result = QStringLiteral("{") + partial.join(QLatin1Char(',')) + QLatin1Char('}'); } else { QString separator = QStringLiteral(",\n") + indent; - result = QStringLiteral("{\n") + indent + partial.join(separator) + QStringLiteral("\n") + stepback + QStringLiteral("}"); + result = QStringLiteral("{\n") + indent + partial.join(separator) + QLatin1Char('\n') + + stepback + QLatin1Char('}'); } indent = stepback; @@ -834,15 +815,15 @@ QString Stringify::JO(Object *o) QString Stringify::JA(ArrayObject *a) { - if (stack.contains(a->d())) { - ctx->engine()->throwTypeError(); + if (stackContains(a)) { + v4->throwTypeError(); return QString(); } Scope scope(a->engine()); QString result; - stack.push(a->d()); + stack.push(a); QString stepback = indent; indent += gap; @@ -878,10 +859,9 @@ QString Stringify::JA(ArrayObject *a) } -Heap::JsonObject::JsonObject(ExecutionEngine *e) - : Heap::Object(e->emptyClass, e->objectPrototype.asObject()) +Heap::JsonObject::JsonObject() { - Scope scope(e); + Scope scope(internalClass->engine); ScopedObject o(scope, this); o->defineDefaultProperty(QStringLiteral("parse"), QV4::JsonObject::method_parse, 2); @@ -911,32 +891,38 @@ ReturnedValue JsonObject::method_stringify(CallContext *ctx) { Scope scope(ctx); - Stringify stringify(ctx); + Stringify stringify(scope.engine); ScopedObject o(scope, ctx->argument(1)); if (o) { - stringify.replacerFunction = o->asFunctionObject(); + stringify.replacerFunction = o->as<FunctionObject>(); if (o->isArrayObject()) { uint arrayLen = o->getLength(); - ScopedValue v(scope); + stringify.propertyList = static_cast<QV4::String *>(scope.alloc(arrayLen)); for (uint i = 0; i < arrayLen; ++i) { - v = o->getIndexed(i); - if (v->asNumberObject() || v->asStringObject() || v->isNumber()) - v = RuntimeHelpers::toString(scope.engine, v); - if (v->isString()) { - String *s = v->stringValue(); - if (!stringify.propertyList.contains(s->d())) - stringify.propertyList.append(s->d()); + Value *v = stringify.propertyList + i; + *v = o->getIndexed(i); + if (v->as<NumberObject>() || v->as<StringObject>() || v->isNumber()) + *v = RuntimeHelpers::toString(scope.engine, *v); + if (!v->isString()) { + v->setM(0); + } else { + for (uint j = 0; j <i; ++j) { + if (stringify.propertyList[j].m() == v->m()) { + v->setM(0); + break; + } + } } } } } ScopedValue s(scope, ctx->argument(2)); - if (NumberObject *n = s->asNumberObject()) + if (NumberObject *n = s->as<NumberObject>()) s = Encode(n->value()); - else if (StringObject *so = s->asStringObject()) - s = so->d()->value; + else if (StringObject *so = s->as<StringObject>()) + s = so->d()->string; if (s->isNumber()) { stringify.gap = QString(qMin(10, (int)s->toInteger()), ' '); @@ -957,7 +943,7 @@ ReturnedValue JsonObject::method_stringify(CallContext *ctx) ReturnedValue JsonObject::fromJsonValue(ExecutionEngine *engine, const QJsonValue &value) { if (value.isString()) - return engine->currentContext()->engine->newString(value.toString())->asReturnedValue(); + return engine->newString(value.toString())->asReturnedValue(); else if (value.isDouble()) return Encode(value.toDouble()); else if (value.isBool()) @@ -986,7 +972,7 @@ QJsonValue JsonObject::toJsonValue(const Value &value, V4ObjectSet &visitedObjec return QJsonValue(value.toQString()); Q_ASSERT(value.isObject()); - Scope scope(value.asObject()->engine()); + Scope scope(value.as<Object>()->engine()); ScopedArrayObject a(scope, value); if (a) return toJsonArray(a, visitedObjects); @@ -1009,22 +995,22 @@ QV4::ReturnedValue JsonObject::fromJsonObject(ExecutionEngine *engine, const QJs return o.asReturnedValue(); } -QJsonObject JsonObject::toJsonObject(Object *o, V4ObjectSet &visitedObjects) +QJsonObject JsonObject::toJsonObject(const Object *o, V4ObjectSet &visitedObjects) { QJsonObject result; - if (!o || o->asFunctionObject()) + if (!o || o->as<FunctionObject>()) return result; Scope scope(o->engine()); - if (visitedObjects.contains(o->d())) { + if (visitedObjects.contains(ObjectItem(o))) { // Avoid recursion. // For compatibility with QVariant{List,Map} conversion, we return an // empty object (and no error is thrown). return result; } - visitedObjects.insert(o->d()); + visitedObjects.insert(ObjectItem(o)); ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); ScopedValue name(scope); @@ -1035,11 +1021,11 @@ QJsonObject JsonObject::toJsonObject(Object *o, V4ObjectSet &visitedObjects) break; QString key = name->toQStringNoThrow(); - if (!val->asFunctionObject()) + if (!val->as<FunctionObject>()) result.insert(key, toJsonValue(val, visitedObjects)); } - visitedObjects.remove(o->d()); + visitedObjects.remove(ObjectItem(o)); return result; } @@ -1057,7 +1043,7 @@ QV4::ReturnedValue JsonObject::fromJsonArray(ExecutionEngine *engine, const QJso return a.asReturnedValue(); } -QJsonArray JsonObject::toJsonArray(ArrayObject *a, V4ObjectSet &visitedObjects) +QJsonArray JsonObject::toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObjects) { QJsonArray result; if (!a) @@ -1065,25 +1051,25 @@ QJsonArray JsonObject::toJsonArray(ArrayObject *a, V4ObjectSet &visitedObjects) Scope scope(a->engine()); - if (visitedObjects.contains(a->d())) { + if (visitedObjects.contains(ObjectItem(a))) { // Avoid recursion. // For compatibility with QVariant{List,Map} conversion, we return an // empty array (and no error is thrown). return result; } - visitedObjects.insert(a->d()); + visitedObjects.insert(ObjectItem(a)); ScopedValue v(scope); quint32 length = a->getLength(); for (quint32 i = 0; i < length; ++i) { v = a->getIndexed(i); - if (v->asFunctionObject()) + if (v->as<FunctionObject>()) v = Encode::null(); result.append(toJsonValue(v, visitedObjects)); } - visitedObjects.remove(a->d()); + visitedObjects.remove(ObjectItem(a)); return result; } diff --git a/src/qml/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h index 81a783ee92..6a6e863bf6 100644 --- a/src/qml/jsruntime/qv4jsonobject_p.h +++ b/src/qml/jsruntime/qv4jsonobject_p.h @@ -33,10 +33,23 @@ #ifndef QV4JSONOBJECT_H #define QV4JSONOBJECT_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" #include <qjsonarray.h> #include <qjsonobject.h> #include <qjsonvalue.h> +#include <qjsondocument.h> +#include <qhash.h> QT_BEGIN_NAMESPACE @@ -45,17 +58,28 @@ namespace QV4 { namespace Heap { struct JsonObject : Object { - JsonObject(ExecutionEngine *e); + JsonObject(); }; } +struct ObjectItem { + const QV4::Object *o; + ObjectItem(const QV4::Object *o) : o(o) {} +}; + +inline bool operator ==(const ObjectItem &a, const ObjectItem &b) +{ return a.o->d() == b.o->d(); } + +inline int qHash(const ObjectItem &i, uint seed = 0) +{ return ::qHash((void *)i.o->d(), seed); } + struct JsonObject : Object { Q_MANAGED_TYPE(JsonObject) V4_OBJECT2(JsonObject, Object) private: - // ### GC - typedef QSet<QV4::Heap::Base *> V4ObjectSet; + + typedef QSet<ObjectItem> V4ObjectSet; public: static ReturnedValue method_parse(CallContext *ctx); @@ -67,18 +91,45 @@ public: static inline QJsonValue toJsonValue(const QV4::Value &value) { V4ObjectSet visitedObjects; return toJsonValue(value, visitedObjects); } - static inline QJsonObject toJsonObject(QV4::Object *o) + static inline QJsonObject toJsonObject(const QV4::Object *o) { V4ObjectSet visitedObjects; return toJsonObject(o, visitedObjects); } - static inline QJsonArray toJsonArray(QV4::ArrayObject *a) + static inline QJsonArray toJsonArray(const QV4::ArrayObject *a) { V4ObjectSet visitedObjects; return toJsonArray(a, visitedObjects); } private: static QJsonValue toJsonValue(const QV4::Value &value, V4ObjectSet &visitedObjects); - static QJsonObject toJsonObject(Object *o, V4ObjectSet &visitedObjects); - static QJsonArray toJsonArray(ArrayObject *a, V4ObjectSet &visitedObjects); + static QJsonObject toJsonObject(const Object *o, V4ObjectSet &visitedObjects); + static QJsonArray toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObjects); }; +class JsonParser +{ +public: + JsonParser(ExecutionEngine *engine, const QChar *json, int length); + + ReturnedValue parse(QJsonParseError *error); + +private: + inline bool eatSpace(); + inline QChar nextToken(); + + ReturnedValue parseObject(); + ReturnedValue parseArray(); + bool parseMember(Object *o); + bool parseString(QString *string); + bool parseValue(Value *val); + bool parseNumber(Value *val); + + ExecutionEngine *engine; + const QChar *head; + const QChar *json; + const QChar *end; + + int nestingLevel; + QJsonParseError::ParseError lastError; +}; + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 82b20337cb..d97abdb461 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -33,6 +33,7 @@ #include "qv4lookup_p.h" #include "qv4functionobject_p.h" #include "qv4scopedvalue_p.h" +#include "qv4string_p.h" QT_BEGIN_NAMESPACE @@ -42,7 +43,7 @@ using namespace QV4; ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttributes *attrs) { ExecutionEngine *engine = o->engine(); - Identifier *name = engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]->identifier; + Identifier *name = engine->current->compilationUnit->runtimeStrings[nameIndex]->identifier; int i = 0; Heap::Object *obj = o->d(); while (i < Size && obj) { @@ -52,7 +53,8 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu if (index != UINT_MAX) { level = i; *attrs = obj->internalClass->propertyData.at(index); - return !attrs->isAccessor() ? obj->memberData->data[index].asReturnedValue() : Object::getValue(thisObject, obj->propertyAt(index), *attrs); + Value *v = obj->propertyData(index); + return !attrs->isAccessor() ? v->asReturnedValue() : Object::getValue(thisObject, *v, *attrs); } obj = obj->prototype; @@ -64,7 +66,8 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu index = obj->internalClass->find(name); if (index != UINT_MAX) { *attrs = obj->internalClass->propertyData.at(index); - return !attrs->isAccessor() ? obj->memberData->data[index].asReturnedValue() : Object::getValue(thisObject, obj->propertyAt(index), *attrs); + Value *v = obj->propertyData(index); + return !attrs->isAccessor() ? v->asReturnedValue() : Object::getValue(thisObject, *v, *attrs); } obj = obj->prototype; @@ -72,11 +75,11 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu return Primitive::emptyValue().asReturnedValue(); } -ReturnedValue Lookup::lookup(Object *thisObject, PropertyAttributes *attrs) +ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs) { Heap::Object *obj = thisObject->d(); ExecutionEngine *engine = thisObject->engine(); - Identifier *name = engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]->identifier; + Identifier *name = engine->current->compilationUnit->runtimeStrings[nameIndex]->identifier; int i = 0; while (i < Size && obj) { classList[i] = obj->internalClass; @@ -85,7 +88,8 @@ ReturnedValue Lookup::lookup(Object *thisObject, PropertyAttributes *attrs) if (index != UINT_MAX) { level = i; *attrs = obj->internalClass->propertyData.at(index); - return !attrs->isAccessor() ? obj->memberData->data[index].asReturnedValue() : thisObject->getValue(obj->propertyAt(index), *attrs); + Value *v = obj->propertyData(index); + return !attrs->isAccessor() ? v->asReturnedValue() : thisObject->getValue(*v, *attrs); } obj = obj->prototype; @@ -97,7 +101,8 @@ ReturnedValue Lookup::lookup(Object *thisObject, PropertyAttributes *attrs) index = obj->internalClass->find(name); if (index != UINT_MAX) { *attrs = obj->internalClass->propertyData.at(index); - return !attrs->isAccessor() ? obj->memberData->data[index].asReturnedValue() : thisObject->getValue(obj->propertyAt(index), *attrs); + Value *v = obj->propertyData(index); + return !attrs->isAccessor() ? v->asReturnedValue() : thisObject->getValue(*v, *attrs); } obj = obj->prototype; @@ -123,7 +128,7 @@ ReturnedValue Lookup::indexedGetterFallback(Lookup *l, const Value &object, cons ScopedObject o(scope, object); if (!o) { if (idx < UINT_MAX) { - if (String *str = object.asString()) { + if (const String *str = object.as<String>()) { if (idx >= (uint)str->toQString().length()) { return Encode::undefined(); } @@ -164,11 +169,11 @@ ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, const Value &object, con { uint idx = index.asArrayIndex(); if (idx == UINT_MAX || !object.isObject()) - return indexedGetterGeneric(l, object, index); + return indexedGetterFallback(l, object, index); Object *o = object.objectValue(); if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (idx < s->len) if (!s->data(idx).isEmpty()) return s->data(idx).asReturnedValue(); @@ -200,7 +205,7 @@ void Lookup::indexedSetterFallback(Lookup *l, const Value &object, const Value & uint idx = index.asArrayIndex(); if (idx < UINT_MAX) { if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (idx < s->len) { s->data(idx) = value; return; @@ -224,7 +229,7 @@ void Lookup::indexedSetterObjectInt(Lookup *l, const Value &object, const Value Object *o = object.objectValue(); if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (idx < s->len) { s->data(idx) = v; return; @@ -235,7 +240,7 @@ void Lookup::indexedSetterObjectInt(Lookup *l, const Value &object, const Value ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object) { - if (Object *o = object.asObject()) + if (const Object *o = object.as<Object>()) return o->getLookup(l); Object *proto; @@ -244,14 +249,14 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va case Value::Null_Type: return engine->throwTypeError(); case Value::Boolean_Type: - proto = engine->booleanPrototype.asObject(); + proto = engine->booleanPrototype(); break; case Value::Managed_Type: { Q_ASSERT(object.isString()); - proto = engine->stringPrototype.asObject(); + proto = engine->stringPrototype(); Scope scope(engine); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[l->nameIndex]); - if (name->equals(engine->id_length)) { + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); + if (name->equals(engine->id_length())) { // special case, as the property is on the object itself l->getter = stringLengthGetter; return stringLengthGetter(l, engine, object); @@ -260,7 +265,7 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va } case Value::Integer_Type: default: // Number - proto = engine->numberPrototype.asObject(); + proto = engine->numberPrototype(); } PropertyAttributes attrs; @@ -291,7 +296,7 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Lookup l1 = *l; if (l1.getter == Lookup::getter0 || l1.getter == Lookup::getter1) { - if (Object *o = object.asObject()) { + if (const Object *o = object.as<Object>()) { ReturnedValue v = o->getLookup(l); Lookup l2 = *l; @@ -328,7 +333,7 @@ ReturnedValue Lookup::getterFallback(Lookup *l, ExecutionEngine *engine, const V QV4::ScopedObject o(scope, object.toObject(scope.engine)); if (!o) return Encode::undefined(); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); return o->get(name); } @@ -339,7 +344,7 @@ ReturnedValue Lookup::getter0(Lookup *l, ExecutionEngine *engine, const Value &o // the internal class won't match Object *o = object.objectValue(); if (l->classList[0] == o->internalClass()) - return o->memberData()->data[l->index].asReturnedValue(); + return o->propertyData(l->index)->asReturnedValue(); } return getterTwoClasses(l, engine, object); } @@ -352,7 +357,7 @@ ReturnedValue Lookup::getter1(Lookup *l, ExecutionEngine *engine, const Value &o Object *o = object.objectValue(); if (l->classList[0] == o->internalClass() && l->classList[1] == o->prototype()->internalClass) - return o->prototype()->memberData->data[l->index].asReturnedValue(); + return o->prototype()->propertyData(l->index)->asReturnedValue(); } return getterTwoClasses(l, engine, object); } @@ -368,7 +373,7 @@ ReturnedValue Lookup::getter2(Lookup *l, ExecutionEngine *engine, const Value &o if (l->classList[1] == p->internalClass) { p = p->prototype; if (l->classList[2] == p->internalClass) - return p->memberData->data[l->index].asReturnedValue(); + return p->propertyData(l->index)->asReturnedValue(); } } } @@ -383,9 +388,9 @@ ReturnedValue Lookup::getter0getter0(Lookup *l, ExecutionEngine *engine, const V // the internal class won't match Object *o = object.objectValue(); if (l->classList[0] == o->internalClass()) - return o->memberData()->data[l->index].asReturnedValue(); + return o->propertyData(l->index)->asReturnedValue(); if (l->classList[2] == o->internalClass()) - return o->memberData()->data[l->index2].asReturnedValue(); + return o->propertyData(l->index2)->asReturnedValue(); } l->getter = getterFallback; return getterFallback(l, engine, object); @@ -398,10 +403,10 @@ ReturnedValue Lookup::getter0getter1(Lookup *l, ExecutionEngine *engine, const V // the internal class won't match Object *o = object.objectValue(); if (l->classList[0] == o->internalClass()) - return o->memberData()->data[l->index].asReturnedValue(); + return o->propertyData(l->index)->asReturnedValue(); if (l->classList[2] == o->internalClass() && l->classList[3] == o->prototype()->internalClass) - return o->prototype()->memberData->data[l->index2].asReturnedValue(); + return o->prototype()->propertyData(l->index2)->asReturnedValue(); } l->getter = getterFallback; return getterFallback(l, engine, object); @@ -415,10 +420,10 @@ ReturnedValue Lookup::getter1getter1(Lookup *l, ExecutionEngine *engine, const V Object *o = object.objectValue(); if (l->classList[0] == o->internalClass() && l->classList[1] == o->prototype()->internalClass) - return o->prototype()->memberData->data[l->index].asReturnedValue(); + return o->prototype()->propertyData(l->index)->asReturnedValue(); if (l->classList[2] == o->internalClass() && l->classList[3] == o->prototype()->internalClass) - return o->prototype()->memberData->data[l->index2].asReturnedValue(); + return o->prototype()->propertyData(l->index2)->asReturnedValue(); return getterFallback(l, engine, object); } l->getter = getterFallback; @@ -434,7 +439,7 @@ ReturnedValue Lookup::getterAccessor0(Lookup *l, ExecutionEngine *engine, const Object *o = object.objectValue(); if (l->classList[0] == o->internalClass()) { Scope scope(o->engine()); - ScopedFunctionObject getter(scope, o->propertyAt(l->index)->getter()); + ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -456,7 +461,7 @@ ReturnedValue Lookup::getterAccessor1(Lookup *l, ExecutionEngine *engine, const if (l->classList[0] == o->internalClass && l->classList[1] == o->prototype->internalClass) { Scope scope(o->internalClass->engine); - ScopedFunctionObject getter(scope, o->prototype->propertyAt(l->index)->getter()); + ScopedFunctionObject getter(scope, o->prototype->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -481,7 +486,7 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const o = o->prototype; if (l->classList[2] == o->internalClass) { Scope scope(o->internalClass->engine); - ScopedFunctionObject getter(scope, o->propertyAt(l->index)->getter()); + ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -501,7 +506,7 @@ ReturnedValue Lookup::primitiveGetter0(Lookup *l, ExecutionEngine *engine, const if (object.type() == l->type) { Object *o = l->proto; if (l->classList[0] == o->internalClass()) - return o->memberData()->data[l->index].asReturnedValue(); + return o->propertyData(l->index)->asReturnedValue(); } l->getter = getterGeneric; return getterGeneric(l, engine, object); @@ -513,7 +518,7 @@ ReturnedValue Lookup::primitiveGetter1(Lookup *l, ExecutionEngine *engine, const Object *o = l->proto; if (l->classList[0] == o->internalClass() && l->classList[1] == o->prototype()->internalClass) - return o->prototype()->memberData->data[l->index].asReturnedValue(); + return o->prototype()->propertyData(l->index)->asReturnedValue(); } l->getter = getterGeneric; return getterGeneric(l, engine, object); @@ -525,7 +530,7 @@ ReturnedValue Lookup::primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engin Object *o = l->proto; if (l->classList[0] == o->internalClass()) { Scope scope(o->engine()); - ScopedFunctionObject getter(scope, o->propertyAt(l->index)->getter()); + ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -545,7 +550,7 @@ ReturnedValue Lookup::primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engin if (l->classList[0] == o->internalClass() && l->classList[1] == o->prototype()->internalClass) { Scope scope(o->engine()); - ScopedFunctionObject getter(scope, o->prototype()->propertyAt(l->index)->getter()); + ScopedFunctionObject getter(scope, o->prototype()->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -560,7 +565,7 @@ ReturnedValue Lookup::primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engin ReturnedValue Lookup::stringLengthGetter(Lookup *l, ExecutionEngine *engine, const Value &object) { - if (String *s = object.asString()) + if (const String *s = object.as<String>()) return Encode(s->d()->length()); l->getter = getterGeneric; @@ -569,8 +574,8 @@ ReturnedValue Lookup::stringLengthGetter(Lookup *l, ExecutionEngine *engine, con ReturnedValue Lookup::arrayLengthGetter(Lookup *l, ExecutionEngine *engine, const Value &object) { - if (ArrayObject *a = object.asArrayObject()) - return a->memberData()->data[Heap::ArrayObject::LengthPropertyIndex].asReturnedValue(); + if (const ArrayObject *a = object.as<ArrayObject>()) + return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->asReturnedValue(); l->getter = getterGeneric; return getterGeneric(l, engine, object); @@ -579,7 +584,7 @@ ReturnedValue Lookup::arrayLengthGetter(Lookup *l, ExecutionEngine *engine, cons ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) { - Object *o = engine->globalObject(); + Object *o = engine->globalObject; PropertyAttributes attrs; ReturnedValue v = l->lookup(o, &attrs); if (v != Primitive::emptyValue().asReturnedValue()) { @@ -602,15 +607,15 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) } } Scope scope(engine); - ScopedString n(scope, engine->currentContext()->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString n(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); return engine->throwReferenceError(n); } ReturnedValue Lookup::globalGetter0(Lookup *l, ExecutionEngine *engine) { - Object *o = engine->globalObject(); + Object *o = engine->globalObject; if (l->classList[0] == o->internalClass()) - return o->memberData()->data[l->index].asReturnedValue(); + return o->propertyData(l->index)->asReturnedValue(); l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -618,10 +623,10 @@ ReturnedValue Lookup::globalGetter0(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetter1(Lookup *l, ExecutionEngine *engine) { - Object *o = engine->globalObject(); + Object *o = engine->globalObject; if (l->classList[0] == o->internalClass() && l->classList[1] == o->prototype()->internalClass) - return o->prototype()->memberData->data[l->index].asReturnedValue(); + return o->prototype()->propertyData(l->index)->asReturnedValue(); l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -629,13 +634,13 @@ ReturnedValue Lookup::globalGetter1(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetter2(Lookup *l, ExecutionEngine *engine) { - Heap::Object *o = engine->globalObject()->d(); + Heap::Object *o = engine->globalObject->d(); if (l->classList[0] == o->internalClass) { o = o->prototype; if (l->classList[1] == o->internalClass) { o = o->prototype; if (l->classList[2] == o->internalClass) { - return o->prototype->memberData->data[l->index].asReturnedValue(); + return o->prototype->propertyData(l->index)->asReturnedValue(); } } } @@ -645,10 +650,10 @@ ReturnedValue Lookup::globalGetter2(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterAccessor0(Lookup *l, ExecutionEngine *engine) { - Object *o = engine->globalObject(); + Object *o = engine->globalObject; if (l->classList[0] == o->internalClass()) { Scope scope(o->engine()); - ScopedFunctionObject getter(scope, o->propertyAt(l->index)->getter()); + ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -662,11 +667,11 @@ ReturnedValue Lookup::globalGetterAccessor0(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterAccessor1(Lookup *l, ExecutionEngine *engine) { - Object *o = engine->globalObject(); + Object *o = engine->globalObject; if (l->classList[0] == o->internalClass() && l->classList[1] == o->prototype()->internalClass) { Scope scope(o->engine()); - ScopedFunctionObject getter(scope, o->prototype()->propertyAt(l->index)->getter()); + ScopedFunctionObject getter(scope, o->prototype()->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -680,14 +685,14 @@ ReturnedValue Lookup::globalGetterAccessor1(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine) { - Heap::Object *o = engine->globalObject()->d(); + Heap::Object *o = engine->globalObject->d(); if (l->classList[0] == o->internalClass) { o = o->prototype; if (l->classList[1] == o->internalClass) { o = o->prototype; if (l->classList[2] == o->internalClass) { Scope scope(o->internalClass->engine); - ScopedFunctionObject getter(scope, o->propertyAt(l->index)->getter()); + ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -701,7 +706,7 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine) return globalGetterGeneric(l, engine); } -void Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Scope scope(engine); ScopedObject o(scope, object); @@ -709,18 +714,18 @@ void Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, const Value &obje o = RuntimeHelpers::convertToObject(scope.engine, object); if (!o) // type error return; - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); o->put(name, value); return; } o->setLookup(l, value); } -void Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Lookup l1 = *l; - if (Object *o = object.asObject()) { + if (Object *o = object.as<Object>()) { o->setLookup(l, value); if (l->setter == Lookup::setter0) { @@ -735,36 +740,34 @@ void Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &o setterFallback(l, engine, object, value); } -void Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { QV4::Scope scope(engine); QV4::ScopedObject o(scope, object.toObject(scope.engine)); if (o) { - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); o->put(name, value); } } -void Lookup::setter0(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = static_cast<Object *>(object.asManaged()); + Object *o = object.as<Object>(); if (o && o->internalClass() == l->classList[0]) { - o->memberData()->data[l->index] = value; + *o->propertyData(l->index) = value; return; } setterTwoClasses(l, engine, object, value); } -void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = static_cast<Object *>(object.asManaged()); + Object *o = object.as<Object>(); if (o && o->internalClass() == l->classList[0]) { if (!o->prototype()) { - if (!o->memberData() || l->index >= o->memberData()->size) - o->ensureMemberIndex(l->index); - o->memberData()->data[l->index] = value; o->setInternalClass(l->classList[3]); + *o->propertyData(l->index) = value; return; } } @@ -773,16 +776,14 @@ void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, const Value &obje setterFallback(l, engine, object, value); } -void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = static_cast<Object *>(object.asManaged()); + Object *o = object.as<Object>(); if (o && o->internalClass() == l->classList[0]) { Heap::Object *p = o->prototype(); if (p && p->internalClass == l->classList[1]) { - if (!o->memberData() || l->index >= o->memberData()->size) - o->ensureMemberIndex(l->index); - o->memberData()->data[l->index] = value; o->setInternalClass(l->classList[3]); + *o->propertyData(l->index) = value; return; } } @@ -791,18 +792,16 @@ void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, const Value &obje setterFallback(l, engine, object, value); } -void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = static_cast<Object *>(object.asManaged()); + Object *o = object.as<Object>(); if (o && o->internalClass() == l->classList[0]) { Heap::Object *p = o->prototype(); if (p && p->internalClass == l->classList[1]) { p = p->prototype; if (p && p->internalClass == l->classList[2]) { - if (!o->memberData() || l->index >= o->memberData()->size) - o->ensureMemberIndex(l->index); - o->memberData()->data[l->index] = value; o->setInternalClass(l->classList[3]); + *o->propertyData(l->index) = value; return; } } @@ -812,16 +811,16 @@ void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, const Value &obje setterFallback(l, engine, object, value); } -void Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = static_cast<Object *>(object.asManaged()); + Object *o = object.as<Object>(); if (o) { if (o->internalClass() == l->classList[0]) { - o->memberData()->data[l->index] = value; + *o->propertyData(l->index) = value; return; } if (o->internalClass() == l->classList[1]) { - o->memberData()->data[l->index2] = value; + *o->propertyData(l->index2) = value; return; } } diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 88397dc36c..77ad1a7b65 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -33,6 +33,17 @@ #ifndef QV4LOOKUP_H #define QV4LOOKUP_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include "qv4runtime_p.h" #include "qv4engine_p.h" @@ -51,7 +62,7 @@ struct Lookup { void (*indexedSetter)(Lookup *l, const Value &object, const Value &index, const Value &v); ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); - void (*setter)(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &v); + void (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v); }; union { ExecutionEngine *engine; @@ -107,17 +118,17 @@ struct Lookup { static ReturnedValue globalGetterAccessor1(Lookup *l, ExecutionEngine *engine); static ReturnedValue globalGetterAccessor2(Lookup *l, ExecutionEngine *engine); - static void setterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setter0(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setterInsert0(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setterInsert1(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setterInsert2(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setter0setter0(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); + static void setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); ReturnedValue lookup(const Value &thisObject, Object *obj, PropertyAttributes *attrs); - ReturnedValue lookup(Object *obj, PropertyAttributes *attrs); + ReturnedValue lookup(const Object *obj, PropertyAttributes *attrs); }; diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index c4b9fc597e..e2de36d18e 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -32,13 +32,13 @@ ****************************************************************************/ #include "qv4managed_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4errorobject_p.h" using namespace QV4; -const ManagedVTable Managed::static_vtbl = +const VTable Managed::static_vtbl = { 0, Managed::IsExecutionContext, @@ -59,7 +59,7 @@ const ManagedVTable Managed::static_vtbl = QString Managed::className() const { const char *s = 0; - switch (Type(d()->vtable->type)) { + switch (Type(d()->vtable()->type)) { case Type_Invalid: case Type_String: return QString(); @@ -88,29 +88,7 @@ QString Managed::className() const s = "RegExp"; break; case Type_ErrorObject: - switch (static_cast<Heap::ErrorObject *>(d())->errorType) { - case Heap::ErrorObject::Error: - s = "Error"; - break; - case Heap::ErrorObject::EvalError: - s = "EvalError"; - break; - case Heap::ErrorObject::RangeError: - s = "RangeError"; - break; - case Heap::ErrorObject::ReferenceError: - s = "ReferenceError"; - break; - case Heap::ErrorObject::SyntaxError: - s = "SyntaxError"; - break; - case Heap::ErrorObject::TypeError: - s = "TypeError"; - break; - case Heap::ErrorObject::URIError: - s = "URIError"; - break; - } + s = ErrorObject::className(static_cast<Heap::ErrorObject *>(d())->errorType); break; case Type_ArgumentsObject: s = "Arguments"; diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index aa3e668eef..6a1bd7a9a4 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -33,16 +33,27 @@ #ifndef QMLJS_MANAGED_H #define QMLJS_MANAGED_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include "qv4value_p.h" -#include "qv4internalclass_p.h" +#include <private/qv4heap_p.h> QT_BEGIN_NAMESPACE namespace QV4 { #define Q_MANAGED_CHECK \ - template <typename _T> inline void qt_check_for_QMANAGED_macro(const _T *_q_argument) const \ + template <typename Type> inline void qt_check_for_QMANAGED_macro(const Type *_q_argument) const \ { int i = qYouForgotTheQ_MANAGED_Macro(this, _q_argument); i = i + 1; } template <typename T> @@ -65,10 +76,10 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} Q_MANAGED_CHECK \ typedef QV4::Heap::DataClass Data; \ typedef superClass SuperClass; \ - static const QV4::ManagedVTable static_vtbl; \ - static inline const QV4::ManagedVTable *staticVTable() { return &static_vtbl; } \ + static const QV4::VTable static_vtbl; \ + static inline const QV4::VTable *staticVTable() { return &static_vtbl; } \ V4_MANAGED_SIZE_TEST \ - QV4::Heap::DataClass *d() const { return static_cast<QV4::Heap::DataClass *>(m); } + QV4::Heap::DataClass *d() const { return static_cast<QV4::Heap::DataClass *>(m()); } #define V4_MANAGED(DataClass, superClass) \ private: \ @@ -84,31 +95,6 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} (classname::func == QV4::Managed::func ? 0 : classname::func) -struct GCDeletable -{ - GCDeletable() : next(0), lastCall(false) {} - virtual ~GCDeletable() {} - GCDeletable *next; - bool lastCall; -}; - -struct ManagedVTable -{ - const ManagedVTable * const parent; - uint isExecutionContext : 1; - uint isString : 1; - uint isObject : 1; - uint isFunctionObject : 1; - uint isErrorObject : 1; - uint isArrayData : 1; - uint unused : 18; - uint type : 8; - const char *className; - void (*destroy)(Heap::Base *); - void (*markObjects)(Heap::Base *, ExecutionEngine *e); - bool (*isEqualTo)(Managed *m, Managed *other); -}; - #define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ { \ parentVTable, \ @@ -127,7 +113,7 @@ struct ManagedVTable } #define DEFINE_MANAGED_VTABLE(classname) \ -const QV4::ManagedVTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0) +const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0) struct Q_QML_PRIVATE_EXPORT Managed : Value { @@ -147,8 +133,6 @@ private: public: - inline void mark(QV4::ExecutionEngine *engine); - enum Type { Type_Invalid, Type_String, @@ -173,55 +157,15 @@ public: }; Q_MANAGED_TYPE(Invalid) - template <typename T> - T *as() { - Q_ASSERT(d()->vtable); -#if !defined(QT_NO_QOBJECT_CHECK) - static_cast<T *>(this)->qt_check_for_QMANAGED_macro(static_cast<T *>(this)); -#endif - const ManagedVTable *vt = d()->vtable; - while (vt) { - if (vt == T::staticVTable()) - return static_cast<T *>(this); - vt = vt->parent; - } - return 0; - } - template <typename T> - const T *as() const { - Q_ASSERT(d()->vtable); -#if !defined(QT_NO_QOBJECT_CHECK) - static_cast<T *>(this)->qt_check_for_QMANAGED_macro(static_cast<T *>(const_cast<Managed *>(this))); -#endif - const ManagedVTable *vt = d()->vtable; - while (vt) { - if (vt == T::staticVTable()) - return static_cast<T *>(this); - vt = vt->parent; - } - return 0; - } - - String *asString() { return d()->vtable->isString ? reinterpret_cast<String *>(this) : 0; } - Object *asObject() { return d()->vtable->isObject ? reinterpret_cast<Object *>(this) : 0; } - ArrayObject *asArrayObject() { return d()->vtable->type == Type_ArrayObject ? reinterpret_cast<ArrayObject *>(this) : 0; } - FunctionObject *asFunctionObject() { return d()->vtable->isFunctionObject ? reinterpret_cast<FunctionObject *>(this) : 0; } - BooleanObject *asBooleanObject() { return d()->vtable->type == Type_BooleanObject ? reinterpret_cast<BooleanObject *>(this) : 0; } - NumberObject *asNumberObject() { return d()->vtable->type == Type_NumberObject ? reinterpret_cast<NumberObject *>(this) : 0; } - StringObject *asStringObject() { return d()->vtable->type == Type_StringObject ? reinterpret_cast<StringObject *>(this) : 0; } - DateObject *asDateObject() { return d()->vtable->type == Type_DateObject ? reinterpret_cast<DateObject *>(this) : 0; } - ErrorObject *asErrorObject() { return d()->vtable->isErrorObject ? reinterpret_cast<ErrorObject *>(this) : 0; } - ArgumentsObject *asArgumentsObject() { return d()->vtable->type == Type_ArgumentsObject ? reinterpret_cast<ArgumentsObject *>(this) : 0; } - - bool isListType() const { return d()->vtable->type == Type_QmlSequence; } - - bool isArrayObject() const { return d()->vtable->type == Type_ArrayObject; } - bool isStringObject() const { return d()->vtable->type == Type_StringObject; } + bool isListType() const { return d()->vtable()->type == Type_QmlSequence; } + + bool isArrayObject() const { return d()->vtable()->type == Type_ArrayObject; } + bool isStringObject() const { return d()->vtable()->type == Type_StringObject; } QString className() const; bool isEqualTo(const Managed *other) const - { return d()->vtable->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); } + { return d()->vtable()->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); } static bool isEqualTo(Managed *m, Managed *other); @@ -237,37 +181,15 @@ private: template<> -inline Managed *value_cast(const Value &v) { - return v.asManaged(); -} - -template<typename T> -inline T *managed_cast(Managed *m) -{ - return m ? m->as<T>() : 0; +inline const Managed *Value::as() const { + if (isManaged()) + return managed(); + return 0; } template<> -inline String *managed_cast(Managed *m) -{ - return m ? m->asString() : 0; -} -template<> -inline Object *managed_cast(Managed *m) -{ - return m ? m->asObject() : 0; -} -template<> -inline FunctionObject *managed_cast(Managed *m) -{ - return m ? m->asFunctionObject() : 0; -} - -inline Value Value::fromManaged(Managed *m) -{ - if (!m) - return QV4::Primitive::undefinedValue(); - return *m; +inline const Object *Value::as() const { + return isManaged() && m() && m()->vtable()->isObject ? objectValue() : 0; } } diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h index 4f550752aa..cf627bcc5d 100644 --- a/src/qml/jsruntime/qv4math_p.h +++ b/src/qml/jsruntime/qv4math_p.h @@ -33,6 +33,17 @@ #ifndef QMLJS_MATH_H #define QMLJS_MATH_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <qglobal.h> #include <QtCore/qnumeric.h> diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index 473e05bf88..3d3ac84576 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -47,10 +47,9 @@ DEFINE_OBJECT_VTABLE(MathObject); static const double qt_PI = 2.0 * ::asin(1.0); -Heap::MathObject::MathObject(ExecutionEngine *e) - : Heap::Object(e->emptyClass, e->objectPrototype.asObject()) +Heap::MathObject::MathObject() { - Scope scope(e); + Scope scope(internalClass->engine); ScopedObject m(scope, this); m->defineReadonlyProperty(QStringLiteral("E"), Primitive::fromDouble(M_E)); @@ -278,10 +277,15 @@ Q_GLOBAL_STATIC(QThreadStorage<bool *>, seedCreatedStorage); ReturnedValue MathObject::method_random(CallContext *context) { if (!seedCreatedStorage()->hasLocalData()) { - qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) ^ reinterpret_cast<quintptr>(context)); + int msecs = QTime(0,0,0).msecsTo(QTime::currentTime()); + Q_ASSERT(msecs >= 0); + qsrand(uint(uint(msecs) ^ reinterpret_cast<quintptr>(context))); seedCreatedStorage()->setLocalData(new bool(true)); } - return Encode(qrand() / (double) RAND_MAX); + // rand()/qrand() return a value where the upperbound is RAND_MAX inclusive. So, instead of + // dividing by RAND_MAX (which would return 0..RAND_MAX inclusive), we divide by RAND_MAX + 1. + qint64 upperLimit = qint64(RAND_MAX) + 1; + return Encode(qrand() / double(upperLimit)); } ReturnedValue MathObject::method_round(CallContext *context) diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h index 472b2020b1..a233f74367 100644 --- a/src/qml/jsruntime/qv4mathobject_p.h +++ b/src/qml/jsruntime/qv4mathobject_p.h @@ -33,6 +33,17 @@ #ifndef QV4MATHOBJECT_H #define QV4MATHOBJECT_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" QT_BEGIN_NAMESPACE @@ -42,7 +53,7 @@ namespace QV4 { namespace Heap { struct MathObject : Object { - MathObject(ExecutionEngine *e); + MathObject(); }; } diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp index 03dfee3dcf..20f8d9ec0f 100644 --- a/src/qml/jsruntime/qv4memberdata.cpp +++ b/src/qml/jsruntime/qv4memberdata.cpp @@ -32,7 +32,8 @@ ****************************************************************************/ #include "qv4memberdata_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> +#include "qv4value_p.h" using namespace QV4; @@ -45,20 +46,30 @@ void MemberData::markObjects(Heap::Base *that, ExecutionEngine *e) m->data[i].mark(e); } -Heap::MemberData *MemberData::reallocate(ExecutionEngine *e, Heap::MemberData *old, uint idx) +static Heap::MemberData *reallocateHelper(ExecutionEngine *e, Heap::MemberData *old, uint n) { - uint s = old ? old->size : 0; - if (idx < s) - return old; - - int newAlloc = qMax((uint)4, 2*idx); - uint alloc = sizeof(Heap::MemberData) + (newAlloc)*sizeof(Value); + uint alloc = sizeof(Heap::MemberData) + (n)*sizeof(Value); Scope scope(e); Scoped<MemberData> newMemberData(scope, e->memoryManager->allocManaged<MemberData>(alloc)); if (old) - memcpy(newMemberData->d(), old, sizeof(Heap::MemberData) + s*sizeof(Value)); + memcpy(newMemberData->d(), old, sizeof(Heap::MemberData) + old->size * sizeof(Value)); else new (newMemberData->d()) Heap::MemberData; - newMemberData->d()->size = newAlloc; + newMemberData->d()->size = n; return newMemberData->d(); } + +Heap::MemberData *MemberData::allocate(ExecutionEngine *e, uint n) +{ + return reallocateHelper(e, 0, n); +} + +Heap::MemberData *MemberData::reallocate(ExecutionEngine *e, Heap::MemberData *old, uint n) +{ + uint s = old ? old->size : 0; + if (n < s) + return old; + + // n is multiplied by two to leave room for growth + return reallocateHelper(e, old, qMax((uint)4, 2*n)); +} diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h index 4c7cfa47cb..12f407e869 100644 --- a/src/qml/jsruntime/qv4memberdata_p.h +++ b/src/qml/jsruntime/qv4memberdata_p.h @@ -33,6 +33,17 @@ #ifndef QV4MEMBERDATA_H #define QV4MEMBERDATA_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include "qv4managed_p.h" @@ -61,7 +72,8 @@ struct MemberData : Managed Value *data() { return d()->data; } inline uint size() const { return d()->size; } - static Heap::MemberData *reallocate(QV4::ExecutionEngine *e, Heap::MemberData *old, uint idx); + static Heap::MemberData *allocate(QV4::ExecutionEngine *e, uint n); + static Heap::MemberData *reallocate(QV4::ExecutionEngine *e, Heap::MemberData *old, uint n); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index 4a1a94e872..4ae30a7f35 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -33,6 +33,7 @@ #include "qv4numberobject_p.h" #include "qv4runtime_p.h" +#include "qv4string_p.h" #include <QtCore/qnumeric.h> #include <QtCore/qmath.h> @@ -50,14 +51,14 @@ Heap::NumberCtor::NumberCtor(QV4::ExecutionContext *scope) { } -ReturnedValue NumberCtor::construct(Managed *m, CallData *callData) +ReturnedValue NumberCtor::construct(const Managed *m, CallData *callData) { Scope scope(m->cast<NumberCtor>()->engine()); double dbl = callData->argc ? callData->args[0].toNumber() : 0.; return Encode(scope.engine->newNumberObject(dbl)); } -ReturnedValue NumberCtor::call(Managed *, CallData *callData) +ReturnedValue NumberCtor::call(const Managed *, CallData *callData) { double dbl = callData->argc ? callData->args[0].toNumber() : 0.; return Encode(dbl); @@ -67,8 +68,8 @@ void NumberPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(qSNaN())); ctor->defineReadonlyProperty(QStringLiteral("NEGATIVE_INFINITY"), Primitive::fromDouble(-qInf())); @@ -81,9 +82,9 @@ QT_WARNING_DISABLE_INTEL(239) QT_WARNING_POP defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString, method_toString); + defineDefaultProperty(engine->id_toString(), method_toString); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); - defineDefaultProperty(engine->id_valueOf, method_valueOf); + defineDefaultProperty(engine->id_valueOf(), method_valueOf); defineDefaultProperty(QStringLiteral("toFixed"), method_toFixed, 1); defineDefaultProperty(QStringLiteral("toExponential"), method_toExponential); defineDefaultProperty(QStringLiteral("toPrecision"), method_toPrecision); @@ -93,7 +94,7 @@ inline ReturnedValue thisNumberValue(ExecutionContext *ctx) { if (ctx->thisObject().isNumber()) return ctx->thisObject().asReturnedValue(); - NumberObject *n = ctx->thisObject().asNumberObject(); + NumberObject *n = ctx->thisObject().as<NumberObject>(); if (!n) return ctx->engine()->throwTypeError(); return Encode(n->value()); @@ -103,7 +104,7 @@ inline double thisNumber(ExecutionContext *ctx) { if (ctx->thisObject().isNumber()) return ctx->thisObject().asDouble(); - NumberObject *n = ctx->thisObject().asNumberObject(); + NumberObject *n = ctx->thisObject().as<NumberObject>(); if (!n) return ctx->engine()->throwTypeError(); return n->value(); @@ -119,7 +120,7 @@ ReturnedValue NumberPrototype::method_toString(CallContext *ctx) if (ctx->argc() && !ctx->args()[0].isUndefined()) { int radix = ctx->args()[0].toInt32(); if (radix < 2 || radix > 36) - return ctx->engine()->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") + return ctx->engine()->throwError(QStringLiteral("Number.prototype.toString: %0 is not a valid radix") .arg(radix)); if (std::isnan(num)) { @@ -197,7 +198,7 @@ ReturnedValue NumberPrototype::method_toFixed(CallContext *ctx) QString str; if (std::isnan(v)) - str = QString::fromLatin1("NaN"); + str = QStringLiteral("NaN"); else if (qIsInf(v)) str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); else if (v < 1.e21) { diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index 205995701b..cc5033531e 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -33,6 +33,17 @@ #ifndef QV4NUMBEROBJECT_H #define QV4NUMBEROBJECT_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" #include "qv4functionobject_p.h" #include <QtCore/qnumeric.h> @@ -53,8 +64,8 @@ struct NumberCtor: FunctionObject { V4_OBJECT2(NumberCtor, FunctionObject) - static ReturnedValue construct(Managed *that, CallData *callData); - static ReturnedValue call(Managed *, CallData *callData); + static ReturnedValue construct(const Managed *that, CallData *callData); + static ReturnedValue call(const Managed *, CallData *callData); }; struct NumberPrototype: NumberObject diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 211fd1812e..ba29d52bc6 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -37,12 +37,14 @@ #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" #include "qv4argumentsobject_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4lookup_p.h" #include "qv4scopedvalue_p.h" #include "qv4memberdata_p.h" #include "qv4objectiterator_p.h" #include "qv4identifier_p.h" +#include "qv4string_p.h" +#include "qv4identifiertable_p.h" #include <stdint.h> @@ -50,15 +52,25 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(Object); -Heap::Object::Object(InternalClass *internalClass, QV4::Object *prototype) - : internalClass(internalClass), - prototype(prototype ? prototype->d() : 0) +void Object::setInternalClass(InternalClass *ic) { - if (internalClass->size) { - Scope scope(internalClass->engine); - ScopedObject o(scope, this); - o->ensureMemberIndex(internalClass->engine, internalClass->size); - } + d()->internalClass = ic; + ensureMemberData(); +} + +void Object::getProperty(uint index, Property *p, PropertyAttributes *attrs) const +{ + p->value = *propertyData(index); + *attrs = internalClass()->propertyData.at(index); + if (attrs->isAccessor()) + p->set = *propertyData(index + SetterOffset); +} + +void Object::setProperty(uint index, const Property *p) +{ + *propertyData(index) = p->value; + if (internalClass()->propertyData.at(index).isAccessor()) + *propertyData(index + SetterOffset) = p->set; } bool Object::setPrototype(Object *proto) @@ -80,28 +92,32 @@ void Object::put(ExecutionEngine *engine, const QString &name, const Value &valu put(n, value); } -ReturnedValue Object::getValue(const Value &thisObject, const Property *p, PropertyAttributes attrs) +ReturnedValue Object::getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs) { if (!attrs.isAccessor()) - return p->value.asReturnedValue(); - if (!p->getter()) + return v.asReturnedValue(); + const QV4::FunctionObject *f = v.as<FunctionObject>(); + if (!f) return Encode::undefined(); - Scope scope(p->getter()->internalClass->engine); - ScopedFunctionObject getter(scope, p->getter()); + Scope scope(f->engine()); ScopedCallData callData(scope); callData->thisObject = thisObject; - return getter->call(callData); + return f->call(callData); } -void Object::putValue(Property *pd, PropertyAttributes attrs, const Value &value) +void Object::putValue(uint memberIndex, const Value &value) { - if (internalClass()->engine->hasException) + QV4::InternalClass *ic = internalClass(); + if (ic->engine->hasException) return; + PropertyAttributes attrs = ic->propertyData[memberIndex]; + if (attrs.isAccessor()) { - if (Heap::FunctionObject *set = pd->setter()) { - Scope scope(set->internalClass->engine); + FunctionObject *set = propertyData(memberIndex + SetterOffset)->as<FunctionObject>(); + if (set) { + Scope scope(ic->engine); ScopedFunctionObject setter(scope, set); ScopedCallData callData(scope, 1); callData->args[0] = value; @@ -115,11 +131,11 @@ void Object::putValue(Property *pd, PropertyAttributes attrs, const Value &value if (!attrs.isWritable()) goto reject; - pd->value = value; + *propertyData(memberIndex) = value; return; reject: - if (engine()->currentContext()->strictMode) + if (engine()->current->strictMode) engine()->throwTypeError(); } @@ -136,9 +152,9 @@ void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(Ca ExecutionEngine *e = engine(); Scope scope(e); ScopedString s(scope, e->newIdentifier(name)); - ScopedContext global(scope, e->rootContext()); + ExecutionContext *global = e->rootContext(); ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code)); - function->defineReadonlyProperty(e->id_length, Primitive::fromInt32(argumentCount)); + function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(s, function); } @@ -146,9 +162,9 @@ void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(CallConte { ExecutionEngine *e = engine(); Scope scope(e); - ScopedContext global(scope, e->rootContext()); + ExecutionContext *global = e->rootContext(); ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code)); - function->defineReadonlyProperty(e->id_length, Primitive::fromInt32(argumentCount)); + function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(name, function); } @@ -165,7 +181,7 @@ void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(CallCo ExecutionEngine *v4 = engine(); QV4::Scope scope(v4); ScopedProperty p(scope); - ScopedContext global(scope, scope.engine->rootContext()); + ExecutionContext *global = v4->rootContext(); p->setGetter(ScopedFunctionObject(scope, (getter ? BuiltinFunction::create(global, name, getter) : 0))); p->setSetter(ScopedFunctionObject(scope, (setter ? BuiltinFunction::create(global, name, setter) : 0))); insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); @@ -188,6 +204,12 @@ void Object::markObjects(Heap::Base *that, ExecutionEngine *e) { Heap::Object *o = static_cast<Heap::Object *>(that); + if (o->inlineMemberSize) { + Value *v = o->propertyData(0); + for (uint i = 0; i < o->inlineMemberSize; ++i) + v[i].mark(e); + } + if (o->memberData) o->memberData->mark(e); if (o->arrayData) @@ -196,9 +218,11 @@ void Object::markObjects(Heap::Base *that, ExecutionEngine *e) o->prototype->mark(e); } -void Object::ensureMemberIndex(uint idx) +void Object::ensureMemberData() { - d()->memberData = MemberData::reallocate(engine(), d()->memberData, idx); + QV4::InternalClass *ic = internalClass(); + if (ic->size > d()->inlineMemberSize) + d()->memberData = MemberData::reallocate(ic->engine, d()->memberData, ic->size - d()->inlineMemberSize); } void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes) @@ -206,102 +230,97 @@ void Object::insertMember(String *s, const Property *p, PropertyAttributes attri uint idx; InternalClass::addMember(this, s, attributes, &idx); - - ensureMemberIndex(internalClass()->size); - if (attributes.isAccessor()) { - Property *pp = propertyAt(idx); - pp->value = p->value; - pp->set = p->set; + *propertyData(idx + GetterOffset) = p->value; + *propertyData(idx + SetterOffset) = p->set; } else { - d()->memberData->data[idx] = p->value; + *propertyData(idx) = p->value; } } // Section 8.12.1 -Property *Object::__getOwnProperty__(String *name, PropertyAttributes *attrs) +void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) - return __getOwnProperty__(idx, attrs); + return getOwnProperty(idx, attrs, p); uint member = internalClass()->find(name); if (member < UINT_MAX) { - if (attrs) - *attrs = internalClass()->propertyData[member]; - return propertyAt(member); + *attrs = internalClass()->propertyData[member]; + if (p) { + p->value = *propertyData(member); + if (attrs->isAccessor()) + p->set = *propertyData(member + SetterOffset); + } + return; } if (attrs) *attrs = Attr_Invalid; - return 0; + return; } -Property *Object::__getOwnProperty__(uint index, PropertyAttributes *attrs) +void Object::getOwnProperty(uint index, PropertyAttributes *attrs, Property *p) { - Property *p = arrayData() ? arrayData()->getProperty(index) : 0; - if (p) { - if (attrs) - *attrs = arrayData()->attributes(index); - return p; + Property *pd = arrayData() ? arrayData()->getProperty(index) : 0; + if (pd) { + *attrs = arrayData()->attributes(index); + if (p) + p->copy(pd, *attrs); + return; } if (isStringObject()) { - if (attrs) - *attrs = Attr_NotConfigurable|Attr_NotWritable; - return static_cast<StringObject *>(this)->getIndex(index); + *attrs = Attr_NotConfigurable|Attr_NotWritable; + if (p) + p->value = static_cast<StringObject *>(this)->getIndex(index); + return; } if (attrs) *attrs = Attr_Invalid; - return 0; + return; } // Section 8.12.2 -Property *Object::__getPropertyDescriptor__(String *name, PropertyAttributes *attrs) const +Value *Object::getValueOrSetter(String *name, PropertyAttributes *attrs) { - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return __getPropertyDescriptor__(idx, attrs); - + Q_ASSERT(name->asArrayIndex() == UINT_MAX); - const Heap::Object *o = d(); + Heap::Object *o = d(); while (o) { uint idx = o->internalClass->find(name); if (idx < UINT_MAX) { - if (attrs) - *attrs = o->internalClass->propertyData[idx]; - return const_cast<Property *>(o->propertyAt(idx)); + *attrs = o->internalClass->propertyData[idx]; + return o->propertyData(attrs->isAccessor() ? idx + SetterOffset : idx); } o = o->prototype; } - if (attrs) - *attrs = Attr_Invalid; + *attrs = Attr_Invalid; return 0; } -Property *Object::__getPropertyDescriptor__(uint index, PropertyAttributes *attrs) const +Value *Object::getValueOrSetter(uint index, PropertyAttributes *attrs) { - const Heap::Object *o = d(); + Heap::Object *o = d(); while (o) { Property *p = o->arrayData ? o->arrayData->getProperty(index) : 0; if (p) { - if (attrs) - *attrs = o->arrayData->attributes(index); - return p; + *attrs = o->arrayData->attributes(index); + return attrs->isAccessor() ? &p->set : &p->value; } - if (o->vtable->type == Type_StringObject) { - Property *p = static_cast<const Heap::StringObject *>(o)->getIndex(index); - if (p) { - if (attrs) - *attrs = (Attr_NotWritable|Attr_NotConfigurable); - return p; + if (o->vtable()->type == Type_StringObject) { + if (index < static_cast<const Heap::StringObject *>(o)->length()) { + // this is an evil hack, but it works, as the method is only ever called from putIndexed, + // where we don't use the returned pointer there for non writable attributes + *attrs = (Attr_NotWritable|Attr_NotConfigurable); + return reinterpret_cast<Value *>(0x1); } } o = o->prototype; } - if (attrs) - *attrs = Attr_Invalid; + *attrs = Attr_Invalid; return 0; } @@ -356,8 +375,7 @@ bool Object::hasOwnProperty(uint index) const return true; if (isStringObject()) { - String *s = static_cast<const StringObject *>(this)->d()->value.asString(); - if (index < (uint)s->d()->length()) + if (index < static_cast<const StringObject *>(this)->length()) return true; } if (!queryIndexed(index).isEmpty()) @@ -365,24 +383,24 @@ bool Object::hasOwnProperty(uint index) const return false; } -ReturnedValue Object::construct(Managed *m, CallData *) +ReturnedValue Object::construct(const Managed *m, CallData *) { - return static_cast<Object *>(m)->engine()->throwTypeError(); + return static_cast<const Object *>(m)->engine()->throwTypeError(); } -ReturnedValue Object::call(Managed *m, CallData *) +ReturnedValue Object::call(const Managed *m, CallData *) { - return static_cast<Object *>(m)->engine()->throwTypeError(); + return static_cast<const Object *>(m)->engine()->throwTypeError(); } -ReturnedValue Object::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty) { - return static_cast<Object *>(m)->internalGet(name, hasProperty); + return static_cast<const Object *>(m)->internalGet(name, hasProperty); } -ReturnedValue Object::getIndexed(Managed *m, uint index, bool *hasProperty) +ReturnedValue Object::getIndexed(const Managed *m, uint index, bool *hasProperty) { - return static_cast<Object *>(m)->internalGetIndexed(index, hasProperty); + return static_cast<const Object *>(m)->internalGetIndexed(index, hasProperty); } void Object::put(Managed *m, String *name, const Value &value) @@ -416,8 +434,7 @@ PropertyAttributes Object::queryIndexed(const Managed *m, uint index) return o->arrayData()->attributes(index); if (o->isStringObject()) { - String *s = static_cast<const StringObject *>(o)->d()->value.asString(); - if (index < (uint)s->d()->length()) + if (index < static_cast<const StringObject *>(o)->length()) return (Attr_NotWritable|Attr_NotConfigurable); } return Attr_Invalid; @@ -433,9 +450,9 @@ bool Object::deleteIndexedProperty(Managed *m, uint index) return static_cast<Object *>(m)->internalDeleteIndexedProperty(index); } -ReturnedValue Object::getLookup(Managed *m, Lookup *l) +ReturnedValue Object::getLookup(const Managed *m, Lookup *l) { - Object *o = static_cast<Object *>(m); + const Object *o = static_cast<const Object *>(m); PropertyAttributes attrs; ReturnedValue v = l->lookup(o, &attrs); if (v != Primitive::emptyValue().asReturnedValue()) { @@ -468,7 +485,7 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value) { Scope scope(static_cast<Object *>(m)->engine()); ScopedObject o(scope, static_cast<Object *>(m)); - ScopedString name(scope, scope.engine->currentContext()->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString name(scope, scope.engine->current->compilationUnit->runtimeStrings[l->nameIndex]); InternalClass *c = o->internalClass(); uint idx = c->find(name); @@ -477,12 +494,12 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value) l->classList[0] = o->internalClass(); l->index = idx; l->setter = Lookup::setter0; - o->memberData()->data[idx] = value; + *o->propertyData(idx) = value; return; } if (idx != UINT_MAX) { - o->putValue(o->propertyAt(idx), o->internalClass()->propertyData[idx], value); + o->putValue(idx, value); return; } } @@ -516,10 +533,10 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value) l->setter = Lookup::setterGeneric; } -void Object::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *pd, PropertyAttributes *attrs) +void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *pd, PropertyAttributes *attrs) { Object *o = static_cast<Object *>(m); - *name = 0; + name->setM(0); *index = UINT_MAX; if (o->arrayData()) { @@ -531,7 +548,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name while (it->arrayNode != o->sparseEnd()) { int k = it->arrayNode->key(); uint pidx = it->arrayNode->value; - Heap::SparseArrayData *sa = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *sa = o->d()->arrayData.cast<Heap::SparseArrayData>(); Property *p = reinterpret_cast<Property *>(sa->arrayData + pidx); it->arrayNode = it->arrayNode->nextNode(); PropertyAttributes a = sa->attrs ? sa->attrs[pidx] : Attr_Data; @@ -548,7 +565,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name } // dense arrays while (it->arrayIndex < o->d()->arrayData->len) { - Heap::SimpleArrayData *sa = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *sa = o->d()->arrayData.cast<Heap::SimpleArrayData>(); Value &val = sa->data(it->arrayIndex); PropertyAttributes a = o->arrayData()->attributes(it->arrayIndex); ++it->arrayIndex; @@ -570,13 +587,15 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name continue; } - Property *p = o->propertyAt(it->memberIndex); + int idx = it->memberIndex; PropertyAttributes a = o->internalClass()->propertyData[it->memberIndex]; ++it->memberIndex; if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { - *name = o->engine()->newString(n->string); + name->setM(o->engine()->identifierTable->stringFromIdentifier(n)); *attrs = a; - pd->copy(p, a); + pd->value = *o->propertyData(idx); + if (a.isAccessor()) + pd->set = *o->propertyData(idx + SetterOffset); return; } } @@ -585,7 +604,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name } // Section 8.12.3 -ReturnedValue Object::internalGet(String *name, bool *hasProperty) +ReturnedValue Object::internalGet(String *name, bool *hasProperty) const { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) @@ -600,7 +619,7 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) if (idx < UINT_MAX) { if (hasProperty) *hasProperty = true; - return getValue(o->propertyAt(idx), o->internalClass()->propertyData.at(idx)); + return getValue(*o->propertyData(idx), o->internalClass()->propertyData.at(idx)); } o = o->prototype(); @@ -611,7 +630,7 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) return Encode::undefined(); } -ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) +ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const { Property *pd = 0; PropertyAttributes attrs; @@ -625,10 +644,12 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) break; } if (o->isStringObject()) { - pd = static_cast<StringObject *>(o.getPointer())->getIndex(index); - if (pd) { + ScopedString str(scope, static_cast<StringObject *>(o.getPointer())->getIndex(index)); + if (str) { attrs = (Attr_NotWritable|Attr_NotConfigurable); - break; + if (hasProperty) + *hasProperty = true; + return str.asReturnedValue(); } } o = o->prototype(); @@ -637,7 +658,7 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) if (pd) { if (hasProperty) *hasProperty = true; - return getValue(pd, attrs); + return getValue(pd->value, attrs); } if (hasProperty) @@ -659,22 +680,22 @@ void Object::internalPut(String *name, const Value &value) name->makeIdentifier(engine()); uint member = internalClass()->find(name); - Property *pd = 0; + Value *v = 0; PropertyAttributes attrs; if (member < UINT_MAX) { - pd = propertyAt(member); attrs = internalClass()->propertyData[member]; + v = propertyData(attrs.isAccessor() ? member + SetterOffset : member); } // clause 1 - if (pd) { + if (v) { if (attrs.isAccessor()) { - if (pd->setter()) + if (v->as<FunctionObject>()) goto cont; goto reject; } else if (!attrs.isWritable()) goto reject; - else if (isArrayObject() && name->equals(engine()->id_length)) { + else if (isArrayObject() && name->equals(engine()->id_length())) { bool ok; uint l = value.asArrayLength(&ok); if (!ok) { @@ -685,7 +706,7 @@ void Object::internalPut(String *name, const Value &value) if (!ok) goto reject; } else { - pd->value = value; + *v = value; } return; } else if (!prototype()) { @@ -694,9 +715,9 @@ void Object::internalPut(String *name, const Value &value) } else { // clause 4 Scope scope(engine()); - if ((pd = ScopedObject(scope, prototype())->__getPropertyDescriptor__(name, &attrs))) { + if ((v = ScopedObject(scope, prototype())->getValueOrSetter(name, &attrs))) { if (attrs.isAccessor()) { - if (!pd->setter()) + if (!v->as<FunctionObject>()) goto reject; } else if (!isExtensible() || !attrs.isWritable()) { goto reject; @@ -709,11 +730,11 @@ void Object::internalPut(String *name, const Value &value) cont: // Clause 5 - if (pd && attrs.isAccessor()) { - assert(pd->setter() != 0); + if (v && attrs.isAccessor()) { + Q_ASSERT(v->as<FunctionObject>()); Scope scope(engine()); - ScopedFunctionObject setter(scope, pd->setter()); + ScopedFunctionObject setter(scope, *v); ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; @@ -725,7 +746,7 @@ void Object::internalPut(String *name, const Value &value) return; reject: - if (engine()->currentContext()->strictMode) { + if (engine()->current->strictMode) { QString message = QStringLiteral("Cannot assign to read-only property \""); message += name->toQString(); message += QLatin1Char('\"'); @@ -740,27 +761,24 @@ void Object::internalPutIndexed(uint index, const Value &value) PropertyAttributes attrs; - Property *pd = arrayData() ? arrayData()->getProperty(index) : 0; - if (pd) - attrs = arrayData()->attributes(index); + Value *v = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : 0; - if (!pd && isStringObject()) { - pd = static_cast<StringObject *>(this)->getIndex(index); - if (pd) + if (!v && isStringObject()) { + if (index < static_cast<StringObject *>(this)->length()) // not writable goto reject; } // clause 1 - if (pd) { + if (v) { if (attrs.isAccessor()) { - if (pd->setter()) + if (v->as<FunctionObject>()) goto cont; goto reject; } else if (!attrs.isWritable()) goto reject; else - pd->value = value; + *v = value; return; } else if (!prototype()) { if (!isExtensible()) @@ -768,9 +786,9 @@ void Object::internalPutIndexed(uint index, const Value &value) } else { // clause 4 Scope scope(engine()); - if ((pd = ScopedObject(scope, prototype())->__getPropertyDescriptor__(index, &attrs))) { + if ((v = ScopedObject(scope, prototype())->getValueOrSetter(index, &attrs))) { if (attrs.isAccessor()) { - if (!pd->setter()) + if (!v->as<FunctionObject>()) goto reject; } else if (!isExtensible() || !attrs.isWritable()) { goto reject; @@ -783,11 +801,11 @@ void Object::internalPutIndexed(uint index, const Value &value) cont: // Clause 5 - if (pd && attrs.isAccessor()) { - assert(pd->setter() != 0); + if (v && attrs.isAccessor()) { + Q_ASSERT(v->as<FunctionObject>()); Scope scope(engine()); - ScopedFunctionObject setter(scope, pd->setter()); + ScopedFunctionObject setter(scope, *v); ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; @@ -799,7 +817,7 @@ void Object::internalPutIndexed(uint index, const Value &value) return; reject: - if (engine()->currentContext()->strictMode) + if (engine()->current->strictMode) engine()->throwTypeError(); } @@ -821,7 +839,7 @@ bool Object::internalDeleteProperty(String *name) InternalClass::removeMember(this, name->identifier()); return true; } - if (engine()->currentContext()->strictMode) + if (engine()->current->strictMode) engine()->throwTypeError(); return false; } @@ -839,7 +857,7 @@ bool Object::internalDeleteIndexedProperty(uint index) if (!ad || ad->vtable()->del(this, index)) return true; - if (engine()->currentContext()->strictMode) + if (engine()->current->strictMode) engine()->throwTypeError(); return false; } @@ -854,17 +872,16 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const Scope scope(engine); name->makeIdentifier(scope.engine); - Property *current; - PropertyAttributes *cattrs; uint memberIndex; - if (isArrayObject() && name->equals(engine->id_length)) { - Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == internalClass()->find(engine->id_length)); - Property *lp = propertyAt(Heap::ArrayObject::LengthPropertyIndex); - cattrs = internalClass()->propertyData.constData() + Heap::ArrayObject::LengthPropertyIndex; - if (attrs.isEmpty() || p->isSubset(attrs, lp, *cattrs)) + if (isArrayObject() && name->equals(engine->id_length())) { + Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == internalClass()->find(engine->id_length())); + ScopedProperty lp(scope); + PropertyAttributes cattrs; + getProperty(Heap::ArrayObject::LengthPropertyIndex, lp, &cattrs); + if (attrs.isEmpty() || p->isSubset(attrs, lp, cattrs)) return true; - if (!cattrs->isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) + if (!cattrs.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) goto reject; bool succeeded = true; if (attrs.type() == PropertyAttributes::Data) { @@ -877,8 +894,10 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const } succeeded = setArrayLength(l); } - if (attrs.hasWritable() && !attrs.isWritable()) - cattrs->setWritable(false); + if (attrs.hasWritable() && !attrs.isWritable()) { + cattrs.setWritable(false); + InternalClass::changeMember(this, engine->id_length(), cattrs); + } if (!succeeded) goto reject; return true; @@ -886,10 +905,8 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const // Clause 1 memberIndex = internalClass()->find(name); - current = (memberIndex < UINT_MAX) ? propertyAt(memberIndex) : 0; - cattrs = internalClass()->propertyData.constData() + memberIndex; - if (!current) { + if (memberIndex == UINT_MAX) { // clause 3 if (!isExtensible()) goto reject; @@ -903,7 +920,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const return __defineOwnProperty__(engine, memberIndex, name, p, attrs); reject: - if (engine->currentContext()->strictMode) + if (engine->current->strictMode) engine->throwTypeError(); return false; } @@ -919,23 +936,23 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, const Pr return defineOwnProperty2(engine, index, p, attrs); reject: - if (engine->currentContext()->strictMode) + if (engine->current->strictMode) engine->throwTypeError(); return false; } bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs) { - Property *current = 0; + bool hasProperty = 0; // Clause 1 if (arrayData()) { - current = arrayData()->getProperty(index); - if (!current && isStringObject()) - current = static_cast<StringObject *>(this)->getIndex(index); + hasProperty = arrayData()->getProperty(index); + if (!hasProperty && isStringObject()) + hasProperty = (index < static_cast<StringObject *>(this)->length()); } - if (!current) { + if (!hasProperty) { // clause 3 if (!isExtensible()) goto reject; @@ -955,7 +972,7 @@ bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Prope return __defineOwnProperty__(engine, index, 0, p, attrs); reject: - if (engine->currentContext()->strictMode) + if (engine->current->strictMode) engine->throwTypeError(); return false; } @@ -966,13 +983,14 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * if (attrs.isEmpty()) return true; - Property *current = 0; + Scope scope(engine); + ScopedProperty current(scope); PropertyAttributes cattrs; if (member) { - current = propertyAt(index); + getProperty(index, current, &cattrs); cattrs = internalClass()->propertyData[index]; } else if (arrayData()) { - current = arrayData()->getProperty(index); + arrayData()->getProperty(index, current, &cattrs); cattrs = arrayData()->attributes(index); } @@ -1006,7 +1024,6 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * initSparseArray(); Q_ASSERT(arrayData()); setArrayAttributes(index, cattrs); - current = arrayData()->getProperty(index); } current->setGetter(0); current->setSetter(0); @@ -1017,7 +1034,6 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * if (!member) { // need to convert the array and the slot setArrayAttributes(index, cattrs); - current = arrayData()->getProperty(index); } current->value = Primitive::undefinedValue(); } @@ -1029,9 +1045,9 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * } else { // clause 10 Q_ASSERT(cattrs.isAccessor() && attrs.isAccessor()); if (!cattrs.isConfigurable()) { - if (!p->value.isEmpty() && current->value.val != p->value.val) + if (!p->value.isEmpty() && current->value.rawValue() != p->value.rawValue()) goto reject; - if (!p->set.isEmpty() && current->set.val != p->set.val) + if (!p->set.isEmpty() && current->set.rawValue() != p->set.rawValue()) goto reject; } } @@ -1041,12 +1057,14 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * current->merge(cattrs, p, attrs); if (member) { InternalClass::changeMember(this, member, cattrs); + setProperty(index, current); } else { setArrayAttributes(index, cattrs); + arrayData()->setProperty(index, current); } return true; reject: - if (engine->currentContext()->strictMode) + if (engine->current->strictMode) engine->throwTypeError(); return false; } @@ -1089,7 +1107,7 @@ void Object::copyArrayData(Object *other) dd->len = other->d()->arrayData->len; dd->offset = other->d()->arrayData->offset; } - memcpy(d()->arrayData->arrayData, other->d()->arrayData->arrayData, d()->arrayData->alloc*sizeof(Value)); + memcpy(d()->arrayData->arrayData, other->d()->arrayData->arrayData, other->d()->arrayData->alloc*sizeof(Value)); } setArrayLengthUnchecked(other->getLength()); } @@ -1097,7 +1115,7 @@ void Object::copyArrayData(Object *other) uint Object::getLength(const Managed *m) { Scope scope(static_cast<const Object *>(m)->engine()); - ScopedValue v(scope, static_cast<Object *>(const_cast<Managed *>(m))->get(scope.engine->id_length)); + ScopedValue v(scope, static_cast<Object *>(const_cast<Managed *>(m))->get(scope.engine->id_length())); return v->toUInt32(); } @@ -1136,11 +1154,11 @@ void Object::initSparseArray() DEFINE_OBJECT_VTABLE(ArrayObject); -Heap::ArrayObject::ArrayObject(ExecutionEngine *engine, const QStringList &list) - : Heap::Object(engine->arrayClass, engine->arrayPrototype.asObject()) +Heap::ArrayObject::ArrayObject(const QStringList &list) + : Heap::Object() { init(); - Scope scope(engine); + Scope scope(internalClass->engine); ScopedObject a(scope, this); // Converts a QStringList to JS. @@ -1151,19 +1169,19 @@ Heap::ArrayObject::ArrayObject(ExecutionEngine *engine, const QStringList &list) a->arrayReserve(len); ScopedValue v(scope); for (int ii = 0; ii < len; ++ii) - a->arrayPut(ii, (v = engine->newString(list.at(ii)))); + a->arrayPut(ii, (v = scope.engine->newString(list.at(ii)))); a->setArrayLengthUnchecked(len); } -ReturnedValue ArrayObject::getLookup(Managed *m, Lookup *l) +ReturnedValue ArrayObject::getLookup(const Managed *m, Lookup *l) { - Scope scope(static_cast<Object *>(m)->engine()); - ScopedString name(scope, scope.engine->currentContext()->compilationUnit->runtimeStrings[l->nameIndex]); - if (name->equals(scope.engine->id_length)) { + Scope scope(static_cast<const Object *>(m)->engine()); + ScopedString name(scope, scope.engine->current->compilationUnit->runtimeStrings[l->nameIndex]); + if (name->equals(scope.engine->id_length())) { // special case, as the property is on the object itself l->getter = Lookup::arrayLengthGetter; - ArrayObject *a = static_cast<ArrayObject *>(m); - return a->memberData()->data[Heap::ArrayObject::LengthPropertyIndex].asReturnedValue(); + const ArrayObject *a = static_cast<const ArrayObject *>(m); + return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->asReturnedValue(); } return Object::getLookup(m, l); } @@ -1171,9 +1189,9 @@ ReturnedValue ArrayObject::getLookup(Managed *m, Lookup *l) uint ArrayObject::getLength(const Managed *m) { const ArrayObject *a = static_cast<const ArrayObject *>(m); - if (a->memberData()->data[Heap::ArrayObject::LengthPropertyIndex].isInteger()) - return a->memberData()->data[Heap::ArrayObject::LengthPropertyIndex].integerValue(); - return Primitive::toUInt32(a->memberData()->data[Heap::ArrayObject::LengthPropertyIndex].doubleValue()); + if (a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->isInteger()) + return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->integerValue(); + return Primitive::toUInt32(a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->doubleValue()); } QStringList ArrayObject::toQStringList() const diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index e2250b9f18..5c660f7e3f 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -33,31 +33,43 @@ #ifndef QV4_OBJECT_H #define QV4_OBJECT_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4managed_p.h" #include "qv4memberdata_p.h" #include "qv4arraydata_p.h" +#include "qv4engine_p.h" +#include "qv4scopedvalue_p.h" +#include "qv4value_p.h" QT_BEGIN_NAMESPACE + namespace QV4 { namespace Heap { struct Object : Base { - Object(ExecutionEngine *engine) - : internalClass(engine->emptyClass), - prototype(static_cast<Object *>(engine->objectPrototype.m)) - { - } - Object(InternalClass *internal, QV4::Object *prototype); + inline Object() {} - const Property *propertyAt(uint index) const { return reinterpret_cast<const Property *>(memberData->data + index); } - Property *propertyAt(uint index) { return reinterpret_cast<Property *>(memberData->data + index); } + const Value *propertyData(uint index) const { if (index < inlineMemberSize) return reinterpret_cast<const Value *>(this) + inlineMemberOffset + index; return memberData->data + index - inlineMemberSize; } + Value *propertyData(uint index) { if (index < inlineMemberSize) return reinterpret_cast<Value *>(this) + inlineMemberOffset + index; return memberData->data + index - inlineMemberSize; } + uint inlineMemberOffset; + uint inlineMemberSize; InternalClass *internalClass; - Heap::Object *prototype; - MemberData *memberData; - ArrayData *arrayData; + Pointer<Object> prototype; + Pointer<MemberData> memberData; + Pointer<ArrayData> arrayData; }; } @@ -67,9 +79,9 @@ struct Object : Base { Q_MANAGED_CHECK \ typedef superClass SuperClass; \ static const QV4::ObjectVTable static_vtbl; \ - static inline const QV4::ManagedVTable *staticVTable() { return &static_vtbl.managedVTable; } \ + static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \ V4_MANAGED_SIZE_TEST \ - Data *d() const { return static_cast<Data *>(m); } + Data *d() const { return static_cast<Data *>(m()); } #define V4_OBJECT2(DataClass, superClass) \ private: \ @@ -80,33 +92,40 @@ struct Object : Base { typedef QV4::Heap::DataClass Data; \ typedef superClass SuperClass; \ static const QV4::ObjectVTable static_vtbl; \ - static inline const QV4::ManagedVTable *staticVTable() { return &static_vtbl.managedVTable; } \ + static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \ V4_MANAGED_SIZE_TEST \ - QV4::Heap::DataClass *d() const { return static_cast<QV4::Heap::DataClass *>(m); } + QV4::Heap::DataClass *d() const { return static_cast<QV4::Heap::DataClass *>(m()); } + +#define V4_INTERNALCLASS(c) \ + static QV4::InternalClass *defaultInternalClass(QV4::ExecutionEngine *e) \ + { return e->c; } +#define V4_PROTOTYPE(p) \ + static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \ + { return e->p(); } struct ObjectVTable { - ManagedVTable managedVTable; - ReturnedValue (*call)(Managed *, CallData *data); - ReturnedValue (*construct)(Managed *, CallData *data); - ReturnedValue (*get)(Managed *, String *name, bool *hasProperty); - ReturnedValue (*getIndexed)(Managed *, uint index, bool *hasProperty); + VTable vTable; + ReturnedValue (*call)(const Managed *, CallData *data); + ReturnedValue (*construct)(const Managed *, CallData *data); + ReturnedValue (*get)(const Managed *, String *name, bool *hasProperty); + ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty); void (*put)(Managed *, String *name, const Value &value); void (*putIndexed)(Managed *, uint index, const Value &value); PropertyAttributes (*query)(const Managed *, String *name); PropertyAttributes (*queryIndexed)(const Managed *, uint index); bool (*deleteProperty)(Managed *m, String *name); bool (*deleteIndexedProperty)(Managed *m, uint index); - ReturnedValue (*getLookup)(Managed *m, Lookup *l); + ReturnedValue (*getLookup)(const Managed *m, Lookup *l); void (*setLookup)(Managed *m, Lookup *l, const Value &v); uint (*getLength)(const Managed *m); - void (*advanceIterator)(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attributes); + void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); }; #define DEFINE_OBJECT_VTABLE(classname) \ const QV4::ObjectVTable classname::static_vtbl = \ { \ - DEFINE_MANAGED_VTABLE_INT(classname, &classname::SuperClass::static_vtbl == &Object::static_vtbl ? 0 : &classname::SuperClass::static_vtbl.managedVTable), \ + DEFINE_MANAGED_VTABLE_INT(classname, &classname::SuperClass::static_vtbl == &Object::static_vtbl ? 0 : &classname::SuperClass::static_vtbl.vTable), \ call, \ construct, \ get, \ @@ -128,31 +147,36 @@ const QV4::ObjectVTable classname::static_vtbl = \ struct Q_QML_EXPORT Object: Managed { V4_OBJECT2(Object, Object) Q_MANAGED_TYPE(Object) + V4_INTERNALCLASS(emptyClass) + V4_PROTOTYPE(objectPrototype) enum { - IsObject = true + IsObject = true, + GetterOffset = 0, + SetterOffset = 1 }; InternalClass *internalClass() const { return d()->internalClass; } - void setInternalClass(InternalClass *ic) { d()->internalClass = ic; } + void setInternalClass(InternalClass *ic); + + const Value *propertyData(uint index) const { return d()->propertyData(index); } + Value *propertyData(uint index) { return d()->propertyData(index); } - Heap::MemberData *memberData() { return d()->memberData; } - const Heap::MemberData *memberData() const { return d()->memberData; } Heap::ArrayData *arrayData() const { return d()->arrayData; } void setArrayData(ArrayData *a) { d()->arrayData = a->d(); } - const Property *propertyAt(uint index) const { return d()->propertyAt(index); } - Property *propertyAt(uint index) { return d()->propertyAt(index); } + void getProperty(uint index, Property *p, PropertyAttributes *attrs) const; + void setProperty(uint index, const Property *p); - const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable); } + const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable()); } Heap::Object *prototype() const { return d()->prototype; } bool setPrototype(Object *proto); - Property *__getOwnProperty__(String *name, PropertyAttributes *attrs = 0); - Property *__getOwnProperty__(uint index, PropertyAttributes *attrs = 0); + void getOwnProperty(String *name, PropertyAttributes *attrs, Property *p = 0); + void getOwnProperty(uint index, PropertyAttributes *attrs, Property *p = 0); - Property *__getPropertyDescriptor__(String *name, PropertyAttributes *attrs = 0) const; - Property *__getPropertyDescriptor__(uint index, PropertyAttributes *attrs = 0) const; + Value *getValueOrSetter(String *name, PropertyAttributes *attrs); + Value *getValueOrSetter(uint index, PropertyAttributes *attrs); bool hasProperty(String *name) const; bool hasProperty(uint index) const; @@ -171,14 +195,14 @@ struct Q_QML_EXPORT Object: Managed { // void put(ExecutionEngine *engine, const QString &name, const Value &value); - static ReturnedValue getValue(const Value &thisObject, const Property *p, PropertyAttributes attrs); - ReturnedValue getValue(const Property *p, PropertyAttributes attrs) const { + static ReturnedValue getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs); + ReturnedValue getValue(const Value &v, PropertyAttributes attrs) const { Scope scope(this->engine()); ScopedValue t(scope, const_cast<Object *>(this)); - return getValue(t, p, attrs); + return getValue(t, v, attrs); } - void putValue(Property *pd, PropertyAttributes attrs, const Value &value); + void putValue(uint memberIndex, const Value &value); /* The spec default: Writable: true, Enumerable: false, Configurable: true */ void defineDefaultProperty(String *name, const Value &value) { @@ -193,10 +217,6 @@ struct Q_QML_EXPORT Object: Managed { void defineReadonlyProperty(const QString &name, const Value &value); void defineReadonlyProperty(String *name, const Value &value); - void ensureMemberIndex(QV4::ExecutionEngine *e, uint idx) { - d()->memberData = MemberData::reallocate(e, d()->memberData, idx); - } - void insertMember(String *s, const Value &v, PropertyAttributes attributes = Attr_Data) { Scope scope(engine()); ScopedProperty p(scope); @@ -273,11 +293,10 @@ public: return false; } - void ensureMemberIndex(uint idx); - inline ReturnedValue get(String *name, bool *hasProperty = 0) + inline ReturnedValue get(String *name, bool *hasProperty = 0) const { return vtable()->get(this, name, hasProperty); } - inline ReturnedValue getIndexed(uint idx, bool *hasProperty = 0) + inline ReturnedValue getIndexed(uint idx, bool *hasProperty = 0) const { return vtable()->getIndexed(this, idx, hasProperty); } inline void put(String *name, const Value &v) { vtable()->put(this, name, v); } @@ -291,38 +310,40 @@ public: { return vtable()->deleteProperty(this, name); } bool deleteIndexedProperty(uint index) { return vtable()->deleteIndexedProperty(this, index); } - ReturnedValue getLookup(Lookup *l) + ReturnedValue getLookup(Lookup *l) const { return vtable()->getLookup(this, l); } void setLookup(Lookup *l, const Value &v) { vtable()->setLookup(this, l, v); } - void advanceIterator(ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attributes) + void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { vtable()->advanceIterator(this, it, name, index, p, attributes); } uint getLength() const { return vtable()->getLength(this); } - inline ReturnedValue construct(CallData *d) + inline ReturnedValue construct(CallData *d) const { return vtable()->construct(this, d); } - inline ReturnedValue call(CallData *d) + inline ReturnedValue call(CallData *d) const { return vtable()->call(this, d); } protected: static void markObjects(Heap::Base *that, ExecutionEngine *e); - static ReturnedValue construct(Managed *m, CallData *); - static ReturnedValue call(Managed *m, CallData *); - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static ReturnedValue construct(const Managed *m, CallData *); + static ReturnedValue call(const Managed *m, CallData *); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); + static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); static void put(Managed *m, String *name, const Value &value); static void putIndexed(Managed *m, uint index, const Value &value); static PropertyAttributes query(const Managed *m, String *name); static PropertyAttributes queryIndexed(const Managed *m, uint index); static bool deleteProperty(Managed *m, String *name); static bool deleteIndexedProperty(Managed *m, uint index); - static ReturnedValue getLookup(Managed *m, Lookup *l); + static ReturnedValue getLookup(const Managed *m, Lookup *l); static void setLookup(Managed *m, Lookup *l, const Value &v); - static void advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attributes); + static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static uint getLength(const Managed *m); + void ensureMemberData(); + private: - ReturnedValue internalGet(String *name, bool *hasProperty); - ReturnedValue internalGetIndexed(uint index, bool *hasProperty); + ReturnedValue internalGet(String *name, bool *hasProperty) const; + ReturnedValue internalGetIndexed(uint index, bool *hasProperty) const; void internalPut(String *name, const Value &value); void internalPutIndexed(uint index, const Value &value); bool internalDeleteProperty(String *name); @@ -335,32 +356,18 @@ private: namespace Heap { struct BooleanObject : Object { - BooleanObject(InternalClass *ic, QV4::Object *prototype) - : Object(ic, prototype), - b(false) - { - } - - BooleanObject(ExecutionEngine *engine, bool b) - : Object(engine->emptyClass, engine->booleanPrototype.asObject()), - b(b) - { - } + BooleanObject() {} + BooleanObject(bool b) + : b(b) + {} bool b; }; struct NumberObject : Object { - NumberObject(InternalClass *ic, QV4::Object *prototype) - : Object(ic, prototype), - value(0) - { - } - - NumberObject(ExecutionEngine *engine, double val) - : Object(engine->emptyClass, engine->numberPrototype.asObject()), - value(val) - { - } + NumberObject() {} + NumberObject(double val) + : value(val) + {} double value; }; @@ -369,15 +376,11 @@ struct ArrayObject : Object { LengthPropertyIndex = 0 }; - ArrayObject(ExecutionEngine *engine) - : Heap::Object(engine->arrayClass, engine->arrayPrototype.asObject()) - { init(); } - ArrayObject(ExecutionEngine *engine, const QStringList &list); - ArrayObject(InternalClass *ic, QV4::Object *prototype) - : Heap::Object(ic, prototype) + ArrayObject() { init(); } + ArrayObject(const QStringList &list); void init() - { memberData->data[LengthPropertyIndex] = Primitive::fromInt32(0); } + { *propertyData(LengthPropertyIndex) = Primitive::fromInt32(0); } }; } @@ -385,6 +388,7 @@ struct ArrayObject : Object { struct BooleanObject: Object { V4_OBJECT2(BooleanObject, Object) Q_MANAGED_TYPE(BooleanObject) + V4_PROTOTYPE(booleanPrototype) bool value() const { return d()->b; } @@ -393,6 +397,7 @@ struct BooleanObject: Object { struct NumberObject: Object { V4_OBJECT2(NumberObject, Object) Q_MANAGED_TYPE(NumberObject) + V4_PROTOTYPE(numberPrototype) double value() const { return d()->value; } }; @@ -400,10 +405,12 @@ struct NumberObject: Object { struct ArrayObject: Object { V4_OBJECT2(ArrayObject, Object) Q_MANAGED_TYPE(ArrayObject) + V4_INTERNALCLASS(arrayClass) + V4_PROTOTYPE(arrayPrototype) void init(ExecutionEngine *engine); - static ReturnedValue getLookup(Managed *m, Lookup *l); + static ReturnedValue getLookup(const Managed *m, Lookup *l); using Object::getLength; static uint getLength(const Managed *m); @@ -413,7 +420,7 @@ struct ArrayObject: Object { inline void Object::setArrayLengthUnchecked(uint l) { if (isArrayObject()) - memberData()->data[Heap::ArrayObject::LengthPropertyIndex] = Primitive::fromUInt32(l); + *propertyData(Heap::ArrayObject::LengthPropertyIndex) = Primitive::fromUInt32(l); } inline void Object::push_back(const Value &v) @@ -436,10 +443,7 @@ inline void Object::arraySet(uint index, const Property *p, PropertyAttributes a arrayData()->vtable()->reallocate(this, index + 1, false); } setArrayAttributes(index, attributes); - Property *pd = ArrayData::insert(this, index, attributes.isAccessor()); - pd->value = p->value; - if (attributes.isAccessor()) - pd->set = p->set; + ArrayData::insert(this, index, &p->value, attributes.isAccessor()); if (isArrayObject() && index >= getLength()) setArrayLengthUnchecked(index + 1); } @@ -451,20 +455,15 @@ inline void Object::arraySet(uint index, const Value &value) if (index > 0x1000 && index > 2*d()->arrayData->alloc) { initSparseArray(); } - Property *pd = ArrayData::insert(this, index); - pd->value = value; + ArrayData::insert(this, index, &value); if (isArrayObject() && index >= getLength()) setArrayLengthUnchecked(index + 1); } -template<> -inline Object *value_cast(const Value &v) { - return v.asObject(); -} template<> -inline ArrayObject *value_cast(const Value &v) { - return v.asArrayObject(); +inline const ArrayObject *Value::as() const { + return isManaged() && m() && m()->vtable()->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : 0; } #ifndef V4_BOOTSTRAP diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index f36ee554a7..1413f439b1 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -35,6 +35,7 @@ #include "qv4stringobject_p.h" #include "qv4identifier_p.h" #include "qv4argumentsobject_p.h" +#include "qv4string_p.h" using namespace QV4; @@ -50,7 +51,7 @@ ObjectIterator::ObjectIterator(ExecutionEngine *e, Value *scratch1, Value *scrat init(o); } -ObjectIterator::ObjectIterator(Scope &scope, Object *o, uint flags) +ObjectIterator::ObjectIterator(Scope &scope, const Object *o, uint flags) : engine(scope.engine) , object(scope.alloc(1)) , current(scope.alloc(1)) @@ -62,14 +63,14 @@ ObjectIterator::ObjectIterator(Scope &scope, Object *o, uint flags) init(o); } -void ObjectIterator::init(Object *o) +void ObjectIterator::init(const Object *o) { - object->m = o ? o->m : 0; - current->m = o ? o->m : 0; + object->setM(o ? o->m() : 0); + current->setM(o ? o->m() : 0); -#if QT_POINTER_SIZE == 4 - object->tag = QV4::Value::Managed_Type; - current->tag = QV4::Value::Managed_Type; +#ifndef QV4_USE_64_BIT_VALUE_ENCODING + object->setTag(QV4::Value::Managed_Type); + current->setTag(QV4::Value::Managed_Type); #endif if (object->as<ArgumentsObject>()) { @@ -78,12 +79,12 @@ void ObjectIterator::init(Object *o) } } -void ObjectIterator::next(Heap::String **name, uint *index, Property *pd, PropertyAttributes *attrs) +void ObjectIterator::next(Value *name, uint *index, Property *pd, PropertyAttributes *attrs) { - *name = 0; + name->setM(0); *index = UINT_MAX; - if (!object->asObject()) { + if (!object->as<Object>()) { *attrs = PropertyAttributes(); return; } @@ -92,19 +93,19 @@ void ObjectIterator::next(Heap::String **name, uint *index, Property *pd, Proper ScopedString n(scope); while (1) { - if (!current->asObject()) + if (!current->as<Object>()) break; while (1) { - current->asObject()->advanceIterator(this, name, index, pd, attrs); + current->as<Object>()->advanceIterator(this, name, index, pd, attrs); if (attrs->isEmpty()) break; // check the property is not already defined earlier in the proto chain if (current->heapObject() != object->heapObject()) { - o = object->asObject(); + o = object->as<Object>(); n = *name; bool shadowed = false; - while (o->asObject()->d() != current->heapObject()) { + while (o->d() != current->heapObject()) { if ((!!n && o->hasOwnProperty(n)) || (*index != UINT_MAX && o->hasOwnProperty(*index))) { shadowed = true; @@ -119,9 +120,9 @@ void ObjectIterator::next(Heap::String **name, uint *index, Property *pd, Proper } if (flags & WithProtoChain) - current->m = current->objectValue()->prototype(); + current->setM(current->objectValue()->prototype()); else - current->m = (Heap::Base *)0; + current->setM(0); arrayIndex = 0; memberIndex = 0; @@ -131,7 +132,7 @@ void ObjectIterator::next(Heap::String **name, uint *index, Property *pd, Proper ReturnedValue ObjectIterator::nextPropertyName(Value *value) { - if (!object->asObject()) + if (!object->as<Object>()) return Encode::null(); PropertyAttributes attrs; @@ -143,17 +144,17 @@ ReturnedValue ObjectIterator::nextPropertyName(Value *value) if (attrs.isEmpty()) return Encode::null(); - *value = object->objectValue()->getValue(p, attrs); + *value = object->objectValue()->getValue(p->value, attrs); if (!!name) return name->asReturnedValue(); - assert(index < UINT_MAX); + Q_ASSERT(index < UINT_MAX); return Encode(index); } ReturnedValue ObjectIterator::nextPropertyNameAsString(Value *value) { - if (!object->asObject()) + if (!object->as<Object>()) return Encode::null(); PropertyAttributes attrs; @@ -165,17 +166,17 @@ ReturnedValue ObjectIterator::nextPropertyNameAsString(Value *value) if (attrs.isEmpty()) return Encode::null(); - *value = object->objectValue()->getValue(p, attrs); + *value = object->objectValue()->getValue(p->value, attrs); if (!!name) return name->asReturnedValue(); - assert(index < UINT_MAX); + Q_ASSERT(index < UINT_MAX); return Encode(engine->newString(QString::number(index))); } ReturnedValue ObjectIterator::nextPropertyNameAsString() { - if (!object->asObject()) + if (!object->as<Object>()) return Encode::null(); PropertyAttributes attrs; diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h index a7abd2ca10..877b34c22d 100644 --- a/src/qml/jsruntime/qv4objectiterator_p.h +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -33,6 +33,17 @@ #ifndef QV4OBJECTITERATOR_H #define QV4OBJECTITERATOR_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include "qv4object_p.h" @@ -57,9 +68,9 @@ struct Q_QML_EXPORT ObjectIterator uint flags; ObjectIterator(ExecutionEngine *e, Value *scratch1, Value *scratch2, Object *o, uint flags); - ObjectIterator(Scope &scope, Object *o, uint flags); - void init(Object *o); - void next(Heap::String **name, uint *index, Property *pd, PropertyAttributes *attributes = 0); + ObjectIterator(Scope &scope, const Object *o, uint flags); + void init(const Object *o); + void next(Value *name, uint *index, Property *pd, PropertyAttributes *attributes = 0); ReturnedValue nextPropertyName(Value *value); ReturnedValue nextPropertyNameAsString(Value *value); ReturnedValue nextPropertyNameAsString(); @@ -67,7 +78,7 @@ struct Q_QML_EXPORT ObjectIterator namespace Heap { struct ForEachIteratorObject : Object { - ForEachIteratorObject(QV4::ExecutionEngine *engine, QV4::Object *o); + ForEachIteratorObject(QV4::Object *o); ObjectIterator it; Value workArea[2]; }; @@ -85,9 +96,8 @@ protected: }; inline -Heap::ForEachIteratorObject::ForEachIteratorObject(QV4::ExecutionEngine *engine, QV4::Object *o) - : Heap::Object(engine) - , it(engine, workArea, workArea + 1, o, ObjectIterator::EnumerableOnly|ObjectIterator::WithProtoChain) +Heap::ForEachIteratorObject::ForEachIteratorObject(QV4::Object *o) + : it(internalClass->engine, workArea, workArea + 1, o, ObjectIterator::EnumerableOnly|ObjectIterator::WithProtoChain) { } diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 9356ea434e..df7441ef5d 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -34,10 +34,11 @@ #include "qv4objectproto_p.h" #include "qv4argumentsobject_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" #include "qv4runtime_p.h" #include "qv4objectiterator_p.h" +#include "qv4string_p.h" #include <QtCore/QDateTime> #include <QtCore/QStringList> @@ -52,14 +53,14 @@ Heap::ObjectCtor::ObjectCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ObjectCtor::construct(Managed *that, CallData *callData) +ReturnedValue ObjectCtor::construct(const Managed *that, CallData *callData) { - ObjectCtor *ctor = static_cast<ObjectCtor *>(that); + const ObjectCtor *ctor = static_cast<const ObjectCtor *>(that); ExecutionEngine *v4 = ctor->engine(); Scope scope(v4); if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) { ScopedObject obj(scope, v4->newObject()); - ScopedObject proto(scope, ctor->get(v4->id_prototype)); + ScopedObject proto(scope, ctor->get(v4->id_prototype())); if (!!proto) obj->setPrototype(proto); return obj.asReturnedValue(); @@ -67,9 +68,9 @@ ReturnedValue ObjectCtor::construct(Managed *that, CallData *callData) return RuntimeHelpers::toObject(scope.engine, callData->args[0]); } -ReturnedValue ObjectCtor::call(Managed *m, CallData *callData) +ReturnedValue ObjectCtor::call(const Managed *m, CallData *callData) { - ObjectCtor *ctor = static_cast<ObjectCtor *>(m); + const ObjectCtor *ctor = static_cast<const ObjectCtor *>(m); ExecutionEngine *v4 = ctor->engine(); if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) return v4->newObject()->asReturnedValue(); @@ -81,8 +82,8 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) Scope scope(v4); ScopedObject o(scope, this); - ctor->defineReadonlyProperty(v4->id_prototype, o); - ctor->defineReadonlyProperty(v4->id_length, Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(v4->id_prototype(), o); + ctor->defineReadonlyProperty(v4->id_length(), Primitive::fromInt32(1)); ctor->defineDefaultProperty(QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); @@ -98,20 +99,20 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) ctor->defineDefaultProperty(QStringLiteral("keys"), method_keys, 1); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(v4->id_toString, method_toString, 0); + defineDefaultProperty(v4->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0); - defineDefaultProperty(v4->id_valueOf, method_valueOf, 0); + defineDefaultProperty(v4->id_valueOf(), method_valueOf, 0); defineDefaultProperty(QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); defineDefaultProperty(QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); defineDefaultProperty(QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1); defineDefaultProperty(QStringLiteral("__defineGetter__"), method_defineGetter, 2); defineDefaultProperty(QStringLiteral("__defineSetter__"), method_defineSetter, 2); - ScopedContext global(scope, scope.engine->rootContext()); + ExecutionContext *global = v4->rootContext(); ScopedProperty p(scope); - p->value = BuiltinFunction::create(global, v4->id___proto__, method_get_proto); - p->set = BuiltinFunction::create(global, v4->id___proto__, method_set_proto); - insertMember(v4->id___proto__, p, Attr_Accessor|Attr_NotEnumerable); + p->value = BuiltinFunction::create(global, v4->id___proto__(), method_get_proto); + p->set = BuiltinFunction::create(global, v4->id___proto__(), method_set_proto); + insertMember(v4->id___proto__(), p, Attr_Accessor|Attr_NotEnumerable); } ReturnedValue ObjectPrototype::method_getPrototypeOf(CallContext *ctx) @@ -140,7 +141,8 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(CallContext *ctx) if (scope.hasException()) return Encode::undefined(); PropertyAttributes attrs; - Property *desc = O->__getOwnProperty__(name, &attrs); + ScopedProperty desc(scope); + O->getOwnProperty(name, &attrs, desc); return fromPropertyDescriptor(scope.engine, desc, attrs); } @@ -163,7 +165,7 @@ ReturnedValue ObjectPrototype::method_create(CallContext *ctx) return ctx->engine()->throwTypeError(); ScopedObject newObject(scope, ctx->d()->engine->newObject()); - newObject->setPrototype(O->asObject()); + newObject->setPrototype(O->as<Object>()); if (ctx->argc() > 1 && !ctx->args()[1].isUndefined()) { ctx->d()->callData->args[0] = newObject.asReturnedValue(); @@ -220,7 +222,7 @@ ReturnedValue ObjectPrototype::method_defineProperties(CallContext *ctx) if (attrs.isEmpty()) break; PropertyAttributes nattrs; - val = o->getValue(pd, attrs); + val = o->getValue(pd->value, attrs); toPropertyDescriptor(scope.engine, val, n, &nattrs); if (scope.engine->hasException) return Encode::undefined(); @@ -390,7 +392,7 @@ ReturnedValue ObjectPrototype::method_toString(CallContext *ctx) } else { ScopedObject obj(scope, RuntimeHelpers::toObject(scope.engine, ctx->thisObject())); QString className = obj->className(); - return ctx->d()->engine->newString(QString::fromLatin1("[object %1]").arg(className))->asReturnedValue(); + return ctx->d()->engine->newString(QStringLiteral("[object %1]").arg(className))->asReturnedValue(); } } @@ -400,7 +402,7 @@ ReturnedValue ObjectPrototype::method_toLocaleString(CallContext *ctx) ScopedObject o(scope, ctx->thisObject().toObject(scope.engine)); if (!o) return Encode::undefined(); - ScopedFunctionObject f(scope, o->get(ctx->d()->engine->id_toString)); + ScopedFunctionObject f(scope, o->get(ctx->d()->engine->id_toString())); if (!f) return ctx->engine()->throwTypeError(); ScopedCallData callData(scope); @@ -462,7 +464,7 @@ ReturnedValue ObjectPrototype::method_propertyIsEnumerable(CallContext *ctx) if (scope.engine->hasException) return Encode::undefined(); PropertyAttributes attrs; - o->__getOwnProperty__(p, &attrs); + o->getOwnProperty(p, &attrs); return Encode(attrs.isEnumerable()); } @@ -484,7 +486,7 @@ ReturnedValue ObjectPrototype::method_defineGetter(CallContext *ctx) if (!o) { if (!ctx->thisObject().isUndefined()) return Encode::undefined(); - o = ctx->d()->engine->globalObject(); + o = ctx->d()->engine->globalObject; } ScopedProperty pd(scope); @@ -512,7 +514,7 @@ ReturnedValue ObjectPrototype::method_defineSetter(CallContext *ctx) if (!o) { if (!ctx->thisObject().isUndefined()) return Encode::undefined(); - o = ctx->d()->engine->globalObject(); + o = ctx->d()->engine->globalObject; } ScopedProperty pd(scope); @@ -525,7 +527,7 @@ ReturnedValue ObjectPrototype::method_defineSetter(CallContext *ctx) ReturnedValue ObjectPrototype::method_get_proto(CallContext *ctx) { Scope scope(ctx); - ScopedObject o(scope, ctx->thisObject().asObject()); + ScopedObject o(scope, ctx->thisObject().as<Object>()); if (!o) return ctx->engine()->throwTypeError(); @@ -572,15 +574,15 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value desc->set = Primitive::emptyValue(); ScopedValue tmp(scope); - if (o->hasProperty(engine->id_enumerable)) - attrs->setEnumerable((tmp = o->get(engine->id_enumerable))->toBoolean()); + if (o->hasProperty(engine->id_enumerable())) + attrs->setEnumerable((tmp = o->get(engine->id_enumerable()))->toBoolean()); - if (o->hasProperty(engine->id_configurable)) - attrs->setConfigurable((tmp = o->get(engine->id_configurable))->toBoolean()); + if (o->hasProperty(engine->id_configurable())) + attrs->setConfigurable((tmp = o->get(engine->id_configurable()))->toBoolean()); - if (o->hasProperty(engine->id_get)) { - ScopedValue get(scope, o->get(engine->id_get)); - FunctionObject *f = get->asFunctionObject(); + if (o->hasProperty(engine->id_get())) { + ScopedValue get(scope, o->get(engine->id_get())); + FunctionObject *f = get->as<FunctionObject>(); if (f || get->isUndefined()) { desc->value = get; } else { @@ -590,9 +592,9 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value attrs->setType(PropertyAttributes::Accessor); } - if (o->hasProperty(engine->id_set)) { - ScopedValue set(scope, o->get(engine->id_set)); - FunctionObject *f = set->asFunctionObject(); + if (o->hasProperty(engine->id_set())) { + ScopedValue set(scope, o->get(engine->id_set())); + FunctionObject *f = set->as<FunctionObject>(); if (f || set->isUndefined()) { desc->set = set; } else { @@ -602,22 +604,22 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value attrs->setType(PropertyAttributes::Accessor); } - if (o->hasProperty(engine->id_writable)) { + if (o->hasProperty(engine->id_writable())) { if (attrs->isAccessor()) { engine->throwTypeError(); return; } - attrs->setWritable((tmp = o->get(engine->id_writable))->toBoolean()); + attrs->setWritable((tmp = o->get(engine->id_writable()))->toBoolean()); // writable forces it to be a data descriptor desc->value = Primitive::undefinedValue(); } - if (o->hasProperty(engine->id_value)) { + if (o->hasProperty(engine->id_value())) { if (attrs->isAccessor()) { engine->throwTypeError(); return; } - desc->value = o->get(engine->id_value); + desc->value = o->get(engine->id_value()); attrs->setType(PropertyAttributes::Data); } @@ -628,7 +630,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value ReturnedValue ObjectPrototype::fromPropertyDescriptor(ExecutionEngine *engine, const Property *desc, PropertyAttributes attrs) { - if (!desc) + if (attrs.isEmpty()) return Encode::undefined(); Scope scope(engine); diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index 4e96681017..ec829e4bd2 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -33,6 +33,17 @@ #ifndef QV4ECMAOBJECTS_P_H #define QV4ECMAOBJECTS_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" #include "qv4functionobject_p.h" #include <QtCore/qnumeric.h> @@ -53,8 +64,8 @@ struct ObjectCtor: FunctionObject { V4_OBJECT2(ObjectCtor, FunctionObject) - static ReturnedValue construct(Managed *that, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *that, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct ObjectPrototype: Object diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp index 88dc1946b8..4a0f84b685 100644 --- a/src/qml/jsruntime/qv4persistent.cpp +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -32,7 +32,7 @@ ****************************************************************************/ #include "qv4persistent_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4object_p.h" #include "PageAllocation.h" @@ -78,11 +78,11 @@ Page *allocatePage(PersistentValueStorage *storage) if (p->header.next) p->header.next->header.prev = &p->header.next; for (int i = 0; i < kEntriesPerPage - 1; ++i) { - p->values[i].tag = QV4::Value::Empty_Type; - p->values[i].int_32 = i + 1; + p->values[i].setTag(QV4::Value::Empty_Type); + p->values[i].setInt_32(i + 1); } - p->values[kEntriesPerPage - 1].tag = QV4::Value::Empty_Type; - p->values[kEntriesPerPage - 1].int_32 = -1; + p->values[kEntriesPerPage - 1].setTag(QV4::Value::Empty_Type); + p->values[kEntriesPerPage - 1].setInt_32(-1); storage->firstPage = p; @@ -93,15 +93,57 @@ Page *allocatePage(PersistentValueStorage *storage) } +PersistentValueStorage::Iterator::Iterator(void *p, int idx) + : p(p), index(idx) +{ + Page *page = static_cast<Page *>(p); + if (page) + ++page->header.refCount; +} + +PersistentValueStorage::Iterator::Iterator(const PersistentValueStorage::Iterator &o) + : p(o.p), index(o.index) +{ + Page *page = static_cast<Page *>(p); + if (page) + ++page->header.refCount; +} + +PersistentValueStorage::Iterator &PersistentValueStorage::Iterator::operator=(const PersistentValueStorage::Iterator &o) +{ + Page *page = static_cast<Page *>(p); + if (page && !--page->header.refCount) + freePage(p); + p = o.p; + index = o.index; + page = static_cast<Page *>(p); + if (page) + ++page->header.refCount; + + return *this; +} + +PersistentValueStorage::Iterator::~Iterator() +{ + Page *page = static_cast<Page *>(p); + if (page && !--page->header.refCount) + freePage(page); +} + PersistentValueStorage::Iterator &PersistentValueStorage::Iterator::operator++() { while (p) { while (index < kEntriesPerPage - 1) { ++index; - if (static_cast<Page *>(p)->values[index].tag != QV4::Value::Empty_Type) + if (static_cast<Page *>(p)->values[index].tag() != QV4::Value::Empty_Type) return *this; } index = -1; - p = static_cast<Page *>(p)->header.next; + Page *next = static_cast<Page *>(p)->header.next; + if (!--static_cast<Page *>(p)->header.refCount) + freePage(p); + p = next; + if (next) + ++next->header.refCount; } index = 0; return *this; @@ -147,10 +189,10 @@ Value *PersistentValueStorage::allocate() p = allocatePage(this); Value *v = p->values + p->header.freeList; - p->header.freeList = v->int_32; + p->header.freeList = v->int_32(); ++p->header.refCount; - v->val = Encode::undefined(); + v->setRawValue(Encode::undefined()); return v; } @@ -162,24 +204,19 @@ void PersistentValueStorage::free(Value *v) Page *p = getPage(v); - v->tag = QV4::Value::Empty_Type; - v->int_32 = p->header.freeList; + v->setTag(QV4::Value::Empty_Type); + v->setInt_32(p->header.freeList); p->header.freeList = v - p->values; - if (!--p->header.refCount) { - if (p->header.prev) - *p->header.prev = p->header.next; - if (p->header.next) - p->header.next->header.prev = p->header.prev; - p->header.alloc.deallocate(); - } + if (!--p->header.refCount) + freePage(p); } static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase) { while (engine->jsStackTop > markBase) { Heap::Base *h = engine->popForGC(); - Q_ASSERT (h->gcGetVtable()->markObjects); - h->gcGetVtable()->markObjects(h, engine); + Q_ASSERT (h->vtable()->markObjects); + h->vtable()->markObjects(h, engine); } } @@ -190,7 +227,7 @@ void PersistentValueStorage::mark(ExecutionEngine *e) Page *p = static_cast<Page *>(firstPage); while (p) { for (int i = 0; i < kEntriesPerPage; ++i) { - if (Managed *m = p->values[i].asManaged()) + if (Managed *m = p->values[i].as<Managed>()) m->mark(e); } drainMarkStack(e, markBase); @@ -204,6 +241,16 @@ ExecutionEngine *PersistentValueStorage::getEngine(Value *v) return getPage(v)->header.engine; } +void PersistentValueStorage::freePage(void *page) +{ + Page *p = static_cast<Page *>(page); + if (p->header.prev) + *p->header.prev = p->header.next; + if (p->header.next) + p->header.next->header.prev = p->header.prev; + p->header.alloc.deallocate(); +} + PersistentValue::PersistentValue(const PersistentValue &other) : val(0) @@ -312,6 +359,12 @@ WeakValue::WeakValue(const WeakValue &other) } } +WeakValue::WeakValue(ExecutionEngine *engine, const Value &value) +{ + val = engine->memoryManager->m_weakValues->allocate(); + *val = value; +} + WeakValue &WeakValue::operator=(const WeakValue &other) { if (!val) { diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h index 7cac2ed95f..80b4ecdea8 100644 --- a/src/qml/jsruntime/qv4persistent_p.h +++ b/src/qml/jsruntime/qv4persistent_p.h @@ -33,7 +33,19 @@ #ifndef QV4PERSISTENT_H #define QV4PERSISTENT_H -#include "qv4value_inl_p.h" +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4value_p.h" +#include "qv4managed_p.h" QT_BEGIN_NAMESPACE @@ -50,8 +62,10 @@ struct Q_QML_EXPORT PersistentValueStorage void mark(ExecutionEngine *e); struct Iterator { - Q_DECL_CONSTEXPR Iterator(void *p, int idx) - : p(p), index(idx) {} + Iterator(void *p, int idx); + Iterator(const Iterator &o); + Iterator & operator=(const Iterator &o); + ~Iterator(); void *p; int index; Iterator &operator++(); @@ -67,6 +81,8 @@ struct Q_QML_EXPORT PersistentValueStorage ExecutionEngine *engine; void *firstPage; +private: + static void freePage(void *page); }; class Q_QML_EXPORT PersistentValue @@ -96,7 +112,13 @@ public: Managed *asManaged() const { if (!val) return 0; - return val->asManaged(); + return val->as<Managed>(); + } + template<typename T> + T *as() const { + if (!val) + return 0; + return val->as<T>(); } ExecutionEngine *engine() const { @@ -122,6 +144,7 @@ class Q_QML_EXPORT WeakValue public: WeakValue() : val(0) {} WeakValue(const WeakValue &other); + WeakValue(ExecutionEngine *engine, const Value &value); WeakValue &operator=(const WeakValue &other); ~WeakValue(); @@ -138,7 +161,13 @@ public: Managed *asManaged() const { if (!val) return 0; - return val->asManaged(); + return val->as<Managed>(); + } + template <typename T> + T *as() const { + if (!val) + return 0; + return val->as<T>(); } ExecutionEngine *engine() const { diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index a7019d0558..b62f367601 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -32,12 +32,13 @@ ****************************************************************************/ #include "qv4profiling_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> +#include <private/qv4string_p.h> QT_BEGIN_NAMESPACE -using namespace QV4; -using namespace QV4::Profiling; +namespace QV4 { +namespace Profiling { FunctionCallProperties FunctionCall::resolve() const { @@ -55,42 +56,43 @@ FunctionCallProperties FunctionCall::resolve() const Profiler::Profiler(QV4::ExecutionEngine *engine) : featuresEnabled(0), m_engine(engine) { - static int metatype = qRegisterMetaType<QList<QV4::Profiling::FunctionCallProperties> >(); - static int metatype2 = qRegisterMetaType<QList<QV4::Profiling::MemoryAllocationProperties> >(); - Q_UNUSED(metatype); - Q_UNUSED(metatype2); + static int meta = qRegisterMetaType<QVector<QV4::Profiling::FunctionCallProperties> >(); + static int meta2 = qRegisterMetaType<QVector<QV4::Profiling::MemoryAllocationProperties> >(); + Q_UNUSED(meta); + Q_UNUSED(meta2); m_timer.start(); } -struct FunctionCallComparator { - bool operator()(const FunctionCallProperties &p1, const FunctionCallProperties &p2) - { return p1.start < p2.start; } -}; - void Profiler::stopProfiling() { featuresEnabled = 0; reportData(); } +bool operator<(const FunctionCall &call1, const FunctionCall &call2) +{ + return call1.m_start < call2.m_start || + (call1.m_start == call2.m_start && (call1.m_end < call2.m_end || + (call1.m_end == call2.m_end && call1.m_function < call2.m_function))); +} + void Profiler::reportData() { - QList<FunctionCallProperties> resolved; + std::sort(m_data.begin(), m_data.end()); + QVector<FunctionCallProperties> resolved; resolved.reserve(m_data.size()); - FunctionCallComparator comp; - foreach (const FunctionCall &call, m_data) { - FunctionCallProperties props = call.resolve(); - resolved.insert(std::upper_bound(resolved.begin(), resolved.end(), props, comp), props); - } + + foreach (const FunctionCall &call, m_data) + resolved.append(call.resolve()); + emit dataReady(resolved, m_memory_data); + m_data.clear(); + m_memory_data.clear(); } void Profiler::startProfiling(quint64 features) { if (featuresEnabled == 0) { - m_data.clear(); - m_memory_data.clear(); - if (features & (1 << FeatureMemoryAllocation)) { qint64 timestamp = m_timer.nsecsElapsed(); MemoryAllocationProperties heap = {timestamp, @@ -111,4 +113,7 @@ void Profiler::startProfiling(quint64 features) } } +} // namespace Profiling +} // namespace QV4 + QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index c3441eaacd..6c54fc9bbd 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -34,6 +34,17 @@ #ifndef QV4PROFILING_H #define QV4PROFILING_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include "qv4engine_p.h" #include "qv4function_p.h" @@ -104,6 +115,7 @@ public: FunctionCallProperties resolve() const; private: + friend bool operator<(const FunctionCall &call1, const FunctionCall &call2); Function *m_function; qint64 m_start; @@ -155,14 +167,14 @@ public slots: void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: - void dataReady(const QList<QV4::Profiling::FunctionCallProperties> &, - const QList<QV4::Profiling::MemoryAllocationProperties> &); + void dataReady(const QVector<QV4::Profiling::FunctionCallProperties> &, + const QVector<QV4::Profiling::MemoryAllocationProperties> &); private: QV4::ExecutionEngine *m_engine; QElapsedTimer m_timer; QVector<FunctionCall> m_data; - QList<MemoryAllocationProperties> m_memory_data; + QVector<MemoryAllocationProperties> m_memory_data; friend class FunctionCallProfiler; }; @@ -202,7 +214,7 @@ Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCallProperties, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCall, Q_MOVABLE_TYPE); QT_END_NAMESPACE -Q_DECLARE_METATYPE(QList<QV4::Profiling::FunctionCallProperties>) -Q_DECLARE_METATYPE(QList<QV4::Profiling::MemoryAllocationProperties>) +Q_DECLARE_METATYPE(QVector<QV4::Profiling::FunctionCallProperties>) +Q_DECLARE_METATYPE(QVector<QV4::Profiling::MemoryAllocationProperties>) #endif // QV4PROFILING_H diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h index 1b55abd1f7..8ddb4cf6f7 100644 --- a/src/qml/jsruntime/qv4property_p.h +++ b/src/qml/jsruntime/qv4property_p.h @@ -33,9 +33,19 @@ #ifndef QV4PROPERTYDESCRIPTOR_H #define QV4PROPERTYDESCRIPTOR_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include "qv4value_p.h" -#include "qv4internalclass_p.h" QT_BEGIN_NAMESPACE @@ -73,8 +83,8 @@ struct Property { inline Heap::FunctionObject *getter() const { return value.isManaged() ? reinterpret_cast<Heap::FunctionObject *>(value.heapObject()) : 0; } inline Heap::FunctionObject *setter() const { return set.isManaged() ? reinterpret_cast<Heap::FunctionObject *>(set.heapObject()) : 0; } - inline void setGetter(FunctionObject *g) { value = Primitive::fromManaged(reinterpret_cast<Managed *>(g)); } - inline void setSetter(FunctionObject *s) { set = s ? Primitive::fromManaged(reinterpret_cast<Managed *>(s)) : Value::fromHeapObject(0); } + inline void setGetter(FunctionObject *g) { value = reinterpret_cast<Managed *>(g); } + inline void setSetter(FunctionObject *s) { set = (s ? reinterpret_cast<Managed *>(s) : 0); } void copy(const Property *other, PropertyAttributes attrs) { value = other->value; @@ -85,12 +95,12 @@ struct Property { explicit Property() { value = Encode::undefined(); set = Value::fromHeapObject(0); } explicit Property(Value v) : value(v) { set = Value::fromHeapObject(0); } Property(FunctionObject *getter, FunctionObject *setter) { - value = Primitive::fromManaged(reinterpret_cast<Managed *>(getter)); - set = Primitive::fromManaged(reinterpret_cast<Managed *>(setter)); + value = reinterpret_cast<Managed *>(getter); + set = reinterpret_cast<Managed *>(setter); } Property(Heap::FunctionObject *getter, Heap::FunctionObject *setter) { - value.m = reinterpret_cast<Heap::Base *>(getter); - set.m = reinterpret_cast<Heap::Base *>(setter); + value.setM(reinterpret_cast<Heap::Base *>(getter)); + set.setM(reinterpret_cast<Heap::Base *>(setter)); } Property &operator=(Value v) { value = v; return *this; } private: diff --git a/src/qml/jsruntime/qv4qmlextensions.cpp b/src/qml/jsruntime/qv4qmlextensions.cpp deleted file mode 100644 index c1c4e0ec78..0000000000 --- a/src/qml/jsruntime/qv4qmlextensions.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4qmlextensions_p.h" -#include "qv4object_p.h" - -using namespace QV4; - -void QmlExtensions::markObjects(ExecutionEngine *e) -{ - if (valueTypeWrapperPrototype) - valueTypeWrapperPrototype->mark(e); -} diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 0a1aa56aab..8f471132b7 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -55,6 +55,7 @@ #include <private/qv4objectproto_p.h> #include <private/qv4jsonobject_p.h> #include <private/qv4regexpobject_p.h> +#include <private/qv4dateobject_p.h> #include <private/qv4scopedvalue_p.h> #include <private/qv4mm_p.h> #include <private/qqmlscriptstring_p.h> @@ -92,7 +93,7 @@ static QPair<QObject *, int> extractQtMethod(QV4::FunctionObject *function) static QPair<QObject *, int> extractQtSignal(const Value &value) { if (value.isObject()) { - QV4::ExecutionEngine *v4 = value.asObject()->engine(); + QV4::ExecutionEngine *v4 = value.as<Object>()->engine(); QV4::Scope scope(v4); QV4::ScopedFunctionObject function(scope, value); if (function) @@ -227,16 +228,15 @@ static QV4::ReturnedValue LoadProperty(QV4::ExecutionEngine *v4, QObject *object } } -Heap::QObjectWrapper::QObjectWrapper(ExecutionEngine *engine, QObject *object) - : Heap::Object(engine) - , object(object) +Heap::QObjectWrapper::QObjectWrapper(QObject *object) + : object(object) { } void QObjectWrapper::initializeBindings(ExecutionEngine *engine) { - engine->functionPrototype.asObject()->defineDefaultProperty(QStringLiteral("connect"), method_connect); - engine->functionPrototype.asObject()->defineDefaultProperty(QStringLiteral("disconnect"), method_disconnect); + engine->functionPrototype()->defineDefaultProperty(QStringLiteral("connect"), method_connect); + engine->functionPrototype()->defineDefaultProperty(QStringLiteral("disconnect"), method_disconnect); } QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const @@ -254,8 +254,8 @@ QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlCont return result; } -ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *n, QObjectWrapper::RevisionMode revisionMode, - bool *hasProperty, bool includeImports) +ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode, + bool *hasProperty, bool includeImports) const { if (QQmlData::wasDeleted(d()->object)) { if (hasProperty) @@ -263,20 +263,18 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String return QV4::Encode::undefined(); } - QV4::Scope scope(engine()); - QV4::ScopedString name(scope, n); + ExecutionEngine *v4 = engine(); - if (name->equals(scope.engine->id_destroy) || name->equals(scope.engine->id_toString)) { - int index = name->equals(scope.engine->id_destroy) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod; - ScopedContext global(scope, scope.engine->rootContext()); - QV4::ScopedValue method(scope, QV4::QObjectMethod::create(global, d()->object, index)); + if (name->equals(v4->id_destroy()) || name->equals(v4->id_toString())) { + int index = name->equals(v4->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod; if (hasProperty) *hasProperty = true; - return method->asReturnedValue(); + ExecutionContext *global = v4->rootContext(); + return QV4::QObjectMethod::create(global, d()->object, index); } QQmlPropertyData local; - QQmlPropertyData *result = findProperty(scope.engine, qmlContext, name, revisionMode, &local); + QQmlPropertyData *result = findProperty(v4, qmlContext, name, revisionMode, &local); if (!result) { if (includeImports && name->startsWithUpper()) { @@ -291,10 +289,10 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String if (r.scriptIndex != -1) { return QV4::Encode::undefined(); } else if (r.type) { - return QmlTypeWrapper::create(scope.engine, d()->object, + return QmlTypeWrapper::create(v4, d()->object, r.type, Heap::QmlTypeWrapper::ExcludeEnums); } else if (r.importNamespace) { - return QmlTypeWrapper::create(scope.engine, d()->object, + return QmlTypeWrapper::create(v4, d()->object, qmlContext->imports, r.importNamespace, Heap::QmlTypeWrapper::ExcludeEnums); } Q_ASSERT(!"Unreachable"); @@ -317,14 +315,11 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String if (hasProperty) *hasProperty = true; - ScopedContext ctx(scope, scope.engine->currentContext()); - return getProperty(d()->object, ctx, result); + return getProperty(v4, d()->object, result); } -ReturnedValue QObjectWrapper::getProperty(QObject *object, ExecutionContext *ctx, QQmlPropertyData *property, bool captureRequired) +ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired) { - QV4::Scope scope(ctx); - QQmlData::flushPendingBinding(object, property->coreIndex); if (property->isFunction() && !property->isVarProperty()) { @@ -333,25 +328,19 @@ ReturnedValue QObjectWrapper::getProperty(QObject *object, ExecutionContext *ctx Q_ASSERT(vmemo); return vmemo->vmeMethod(property->coreIndex); } else if (property->isV4Function()) { - QV4::ScopedObject qmlcontextobject(scope, ctx->d()->engine->qmlContextObject()); - ScopedContext global(scope, scope.engine->rootContext()); - return QV4::QObjectMethod::create(global, object, property->coreIndex, qmlcontextobject); + Scope scope(engine); + ScopedContext global(scope, engine->qmlContext()); + return QV4::QObjectMethod::create(global, object, property->coreIndex); } else if (property->isSignalHandler()) { - QV4::Scoped<QV4::QmlSignalHandler> handler(scope, scope.engine->memoryManager->alloc<QV4::QmlSignalHandler>(ctx->d()->engine, object, property->coreIndex)); - - QV4::ScopedString connect(scope, ctx->d()->engine->newIdentifier(QStringLiteral("connect"))); - QV4::ScopedString disconnect(scope, ctx->d()->engine->newIdentifier(QStringLiteral("disconnect"))); - handler->put(connect, QV4::ScopedValue(scope, ctx->d()->engine->functionPrototype.asObject()->get(connect))); - handler->put(disconnect, QV4::ScopedValue(scope, ctx->d()->engine->functionPrototype.asObject()->get(disconnect))); - - return handler.asReturnedValue(); + QmlSignalHandler::initProto(engine); + return engine->memoryManager->allocObject<QV4::QmlSignalHandler>(object, property->coreIndex)->asReturnedValue(); } else { - ScopedContext global(scope, scope.engine->rootContext()); + ExecutionContext *global = engine->rootContext(); return QV4::QObjectMethod::create(global, object, property->coreIndex); } } - QQmlEnginePrivate *ep = scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : 0; + QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : 0; if (property->hasAccessors()) { QQmlNotifier *n = 0; @@ -360,37 +349,38 @@ ReturnedValue QObjectWrapper::getProperty(QObject *object, ExecutionContext *ctx if (ep && ep->propertyCapture && property->accessors->notifier) nptr = &n; - QV4::ScopedValue rv(scope, LoadProperty<ReadAccessor::Accessor>(ctx->d()->engine, object, *property, nptr)); + Scope scope(engine); + QV4::ScopedValue rv(scope, LoadProperty<ReadAccessor::Accessor>(engine, object, *property, nptr)); if (captureRequired) { if (property->accessors->notifier) { - if (n) - ep->captureProperty(n); + if (n && ep->propertyCapture) + ep->propertyCapture->captureProperty(n); } else { - ep->captureProperty(object, property->coreIndex, property->notifyIndex); + if (ep->propertyCapture) + ep->propertyCapture->captureProperty(object, property->coreIndex, property->notifyIndex); } } return rv->asReturnedValue(); } - if (captureRequired && ep && !property->isConstant()) - ep->captureProperty(object, property->coreIndex, property->notifyIndex); + if (captureRequired && ep && ep->propertyCapture && !property->isConstant()) + ep->propertyCapture->captureProperty(object, property->coreIndex, property->notifyIndex); if (property->isVarProperty()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); return vmemo->vmeProperty(property->coreIndex); } else if (property->isDirect()) { - return LoadProperty<ReadAccessor::Direct>(ctx->d()->engine, object, *property, 0); + return LoadProperty<ReadAccessor::Direct>(engine, object, *property, 0); } else { - return LoadProperty<ReadAccessor::Indirect>(ctx->d()->engine, object, *property, 0); + return LoadProperty<ReadAccessor::Indirect>(engine, object, *property, 0); } } ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty) { - QV4::Scope scope(engine); if (QQmlData::wasDeleted(object)) { if (hasProperty) *hasProperty = false; @@ -403,6 +393,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC return QV4::Encode::null(); } + QV4::Scope scope(engine); QV4::Scoped<QObjectWrapper> wrapper(scope, wrap(engine, object)); if (!wrapper) { if (hasProperty) @@ -419,11 +410,7 @@ bool QObjectWrapper::setQmlProperty(ExecutionEngine *engine, QQmlContextData *qm return false; QQmlPropertyData local; - QQmlPropertyData *result = 0; - { - result = QQmlPropertyCache::property(engine->jsEngine(), object, name, qmlContext, local); - } - + QQmlPropertyData *result = QQmlPropertyCache::property(engine->jsEngine(), object, name, qmlContext, local); if (!result) return false; @@ -433,23 +420,21 @@ bool QObjectWrapper::setQmlProperty(ExecutionEngine *engine, QQmlContextData *qm return false; } - Scope scope(engine); - ScopedContext ctx(scope, engine->currentContext()); - setProperty(object, ctx, result, value); + setProperty(engine, object, result, value); return true; } -void QObjectWrapper::setProperty(QObject *object, ExecutionContext *ctx, QQmlPropertyData *property, const Value &value) +void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value) { if (!property->isWritable() && !property->isQList()) { QString error = QLatin1String("Cannot assign to read-only property \"") + property->name(object) + QLatin1Char('\"'); - ctx->engine()->throwTypeError(error); + engine->throwTypeError(error); return; } QQmlBinding *newBinding = 0; - QV4::Scope scope(ctx); + QV4::Scope scope(engine); QV4::ScopedFunctionObject f(scope, value); if (f) { if (!f->isBinding()) { @@ -460,25 +445,25 @@ void QObjectWrapper::setProperty(QObject *object, ExecutionContext *ctx, QQmlPro error += QLatin1String("[unknown property type]"); else error += QLatin1String(QMetaType::typeName(property->propType)); - ctx->engine()->throwError(error); + scope.engine->throwError(error); return; } } else { // binding assignment. - QQmlContextData *callingQmlContext = QV4::QmlContextWrapper::callingContext(ctx->d()->engine); + QQmlContextData *callingQmlContext = scope.engine->callingQmlContext(); QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); bindingFunction->initBindingLocation(); newBinding = new QQmlBinding(value, object, callingQmlContext); - newBinding->setTarget(object, *property, callingQmlContext); + newBinding->setTarget(object, *property); } } - QQmlAbstractBinding *oldBinding = - QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding); - if (oldBinding) - oldBinding->destroy(); + if (newBinding) + QQmlPropertyPrivate::setBinding(newBinding); + else + QQmlPropertyPrivate::removeBinding(object, property->encodedIndex()); if (!newBinding && property->isVarProperty()) { // allow assignment of "special" values (null, undefined, function) to var properties @@ -505,16 +490,16 @@ void QObjectWrapper::setProperty(QObject *object, ExecutionContext *ctx, QQmlPro } else if (value.isUndefined() && property->propType == QMetaType::QJsonValue) { PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined)); } else if (!newBinding && property->propType == qMetaTypeId<QJSValue>()) { - PROPERTY_STORE(QJSValue, QJSValue(ctx->d()->engine, value.asReturnedValue())); + PROPERTY_STORE(QJSValue, QJSValue(scope.engine, value.asReturnedValue())); } else if (value.isUndefined() && property->propType != qMetaTypeId<QQmlScriptString>()) { QString error = QLatin1String("Cannot assign [undefined] to "); if (!QMetaType::typeName(property->propType)) error += QLatin1String("[unknown property type]"); else error += QLatin1String(QMetaType::typeName(property->propType)); - ctx->engine()->throwError(error); + scope.engine->throwError(error); return; - } else if (value.asFunctionObject()) { + } else if (value.as<FunctionObject>()) { // this is handled by the binding creation above } else if (property->propType == QMetaType::Int && value.isNumber()) { PROPERTY_STORE(int, value.asDouble()); @@ -543,11 +528,11 @@ void QObjectWrapper::setProperty(QObject *object, ExecutionContext *ctx, QQmlPro } else { QVariant v; if (property->isQList()) - v = ctx->d()->engine->toVariant(value, qMetaTypeId<QList<QObject *> >()); + v = scope.engine->toVariant(value, qMetaTypeId<QList<QObject *> >()); else - v = ctx->d()->engine->toVariant(value, property->propType); + v = scope.engine->toVariant(value, property->propType); - QQmlContextData *callingQmlContext = QV4::QmlContextWrapper::callingContext(ctx->d()->engine); + QQmlContextData *callingQmlContext = scope.engine->callingQmlContext(); if (!QQmlPropertyPrivate::write(object, *property, v, callingQmlContext)) { const char *valueType = 0; if (v.userType() == QVariant::Invalid) valueType = "null"; @@ -561,7 +546,7 @@ void QObjectWrapper::setProperty(QObject *object, ExecutionContext *ctx, QQmlPro QLatin1String(valueType) + QLatin1String(" to ") + QLatin1String(targetTypeName); - ctx->engine()->throwError(error); + scope.engine->throwError(error); return; } } @@ -634,7 +619,7 @@ void QObjectWrapper::markWrapper(QObject *object, ExecutionEngine *engine) engine->m_multiplyWrappedQObjects->mark(object, engine); } -ReturnedValue QObjectWrapper::getProperty(QObject *object, ExecutionContext *ctx, int propertyIndex, bool captureRequired) +ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired) { if (QQmlData::wasDeleted(object)) return QV4::Encode::null(); @@ -646,14 +631,19 @@ ReturnedValue QObjectWrapper::getProperty(QObject *object, ExecutionContext *ctx Q_ASSERT(cache); QQmlPropertyData *property = cache->property(propertyIndex); Q_ASSERT(property); // We resolved this property earlier, so it better exist! - return getProperty(object, ctx, property, captureRequired); + return getProperty(engine, object, property, captureRequired); +} + +void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value) +{ + setProperty(engine, d()->object, propertyIndex, value); } -void QObjectWrapper::setProperty(ExecutionContext *ctx, int propertyIndex, const Value &value) +void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value) { - if (QQmlData::wasDeleted(d()->object)) + if (QQmlData::wasDeleted(object)) return; - QQmlData *ddata = QQmlData::get(d()->object, /*create*/false); + QQmlData *ddata = QQmlData::get(object, /*create*/false); if (!ddata) return; @@ -661,14 +651,14 @@ void QObjectWrapper::setProperty(ExecutionContext *ctx, int propertyIndex, const Q_ASSERT(cache); QQmlPropertyData *property = cache->property(propertyIndex); Q_ASSERT(property); // We resolved this property earlier, so it better exist! - return setProperty(d()->object, ctx, property, value); + return setProperty(engine, object, property, value); } bool QObjectWrapper::isEqualTo(Managed *a, Managed *b) { Q_ASSERT(a->as<QV4::QObjectWrapper>()); QV4::QObjectWrapper *qobjectWrapper = static_cast<QV4::QObjectWrapper *>(a); - QV4::Object *o = b->asObject(); + QV4::Object *o = b->as<Object>(); if (o) { if (QV4::QmlTypeWrapper *qmlTypeWrapper = o->as<QV4::QmlTypeWrapper>()) return qmlTypeWrapper->toVariant().value<QObject*>() == qobjectWrapper->object(); @@ -681,13 +671,13 @@ ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object) { if (engine->jsEngine()) QQmlData::ensurePropertyCache(engine->jsEngine(), object); - return (engine->memoryManager->alloc<QV4::QObjectWrapper>(engine, object))->asReturnedValue(); + return (engine->memoryManager->allocObject<QV4::QObjectWrapper>(object))->asReturnedValue(); } -QV4::ReturnedValue QObjectWrapper::get(Managed *m, String *name, bool *hasProperty) +QV4::ReturnedValue QObjectWrapper::get(const Managed *m, String *name, bool *hasProperty) { - QObjectWrapper *that = static_cast<QObjectWrapper*>(m); - QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(that->engine()); + const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); + QQmlContextData *qmlContext = that->engine()->callingQmlContext(); return that->getQmlProperty(qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true); } @@ -699,7 +689,7 @@ void QObjectWrapper::put(Managed *m, String *name, const Value &value) if (v4->hasException || QQmlData::wasDeleted(that->d()->object)) return; - QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4); + QQmlContextData *qmlContext = v4->callingQmlContext(); if (!setQmlProperty(v4, qmlContext, that->d()->object, name, QV4::QObjectWrapper::IgnoreRevision, value)) { QQmlData *ddata = QQmlData::get(that->d()->object); // Types created by QML are not extensible at run-time, but for other QObjects we can store them @@ -718,23 +708,23 @@ PropertyAttributes QObjectWrapper::query(const Managed *m, String *name) { const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); ExecutionEngine *engine = that->engine(); - QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(engine); + QQmlContextData *qmlContext = engine->callingQmlContext(); QQmlPropertyData local; if (that->findProperty(engine, qmlContext, name, IgnoreRevision, &local) - || name->equals(engine->id_destroy) || name->equals(engine->id_toString)) + || name->equals(engine->id_destroy()) || name->equals(engine->id_toString())) return QV4::Attr_Data; else return QV4::Object::query(m, name); } -void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attributes) +void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()"); static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()"); - *name = (Heap::String *)0; + name->setM(0); *index = UINT_MAX; QObjectWrapper *that = static_cast<QObjectWrapper*>(m); @@ -745,10 +735,9 @@ void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Heap::Strin const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject; const int propertyCount = mo->propertyCount(); if (it->arrayIndex < static_cast<uint>(propertyCount)) { - // #### GC Scope scope(that->engine()); ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(mo->property(it->arrayIndex).name()))); - *name = propName->d(); + name->setM(propName->d()); ++it->arrayIndex; *attributes = QV4::Attr_Data; p->value = that->get(propName); @@ -761,10 +750,9 @@ void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Heap::Strin ++it->arrayIndex; if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2))) continue; - // #### GC Scope scope(that->engine()); ScopedString methodName(scope, that->engine()->newString(QString::fromUtf8(method.name()))); - *name = methodName->d(); + name->setM(methodName->d()); *attributes = QV4::Attr_Data; p->value = that->get(methodName); return; @@ -811,7 +799,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase QV4::ScopedFunctionObject f(scope, This->function.value()); QV4::ScopedCallData callData(scope, argCount); - callData->thisObject = This->thisObject.isUndefined() ? v4->globalObject()->asReturnedValue() : This->thisObject.value(); + callData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value(); for (int ii = 0; ii < argCount; ++ii) { int type = argsTypes[ii + 1]; if (type == qMetaTypeId<QVariant>()) { @@ -826,7 +814,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase QQmlError error = v4->catchExceptionAsQmlError(); if (error.description().isEmpty()) { QV4::ScopedString name(scope, f->name()); - error.setDescription(QString::fromLatin1("Unknown exception occurred during evaluation of connected function: %1").arg(name->toQString())); + error.setDescription(QStringLiteral("Unknown exception occurred during evaluation of connected function: %1").arg(name->toQString())); } if (QQmlEngine *qmlEngine = v4->qmlEngine()) { QQmlEnginePrivate::get(qmlEngine)->warning(error); @@ -1028,56 +1016,35 @@ void QObjectWrapper::markObjects(Heap::Base *that, QV4::ExecutionEngine *e) QV4::Object::markObjects(that, e); } -namespace { - struct QObjectDeleter : public QV4::GCDeletable - { - QObjectDeleter(QObject *o) - : m_objectToDelete(o) - {} - ~QObjectDeleter() - { - QQmlData *ddata = QQmlData::get(m_objectToDelete, false); - if (ddata && ddata->ownContext && ddata->context) - ddata->context->emitDestruction(); - // This object is notionally destroyed now - ddata->isQueuedForDeletion = true; - if (lastCall) - delete m_objectToDelete; - else - m_objectToDelete->deleteLater(); - } - - QObject *m_objectToDelete; - }; -} - -void QObjectWrapper::destroy(Heap::Base *that) +void QObjectWrapper::destroyObject(bool lastCall) { - Heap::QObjectWrapper *This = static_cast<Heap::QObjectWrapper*>(that); - QPointer<QObject> object = This->object; - ExecutionEngine *engine = This->internalClass->engine; - This->~Data(); - This = 0; - if (!object) - return; - - QQmlData *ddata = QQmlData::get(object, false); - if (!ddata) - return; - - if (object->parent() || ddata->indestructible) - return; + Heap::QObjectWrapper *h = d(); + if (!h->internalClass) + return; // destroyObject already got called + + if (h->object) { + QQmlData *ddata = QQmlData::get(h->object, false); + if (ddata) { + if (!h->object->parent() && !ddata->indestructible) { + if (ddata && ddata->ownContext && ddata->context) + ddata->context->emitDestruction(); + // This object is notionally destroyed now + ddata->isQueuedForDeletion = true; + if (lastCall) + delete h->object; + else + h->object->deleteLater(); + } + } + } - QObjectDeleter *deleter = new QObjectDeleter(object); - engine->memoryManager->registerDeletable(deleter); + h->internalClass = 0; + h->~Data(); } DEFINE_OBJECT_VTABLE(QObjectWrapper); -// XXX TODO: Need to review all calls to QQmlEngine *engine() to confirm QObjects work -// correctly in a worker thread - namespace { template<typename A, typename B, typename C, typename D, typename E, @@ -1229,7 +1196,7 @@ static int MatchScore(const QV4::Value &actual, int conversionType) default: return 10; } - } else if (actual.asDateObject()) { + } else if (actual.as<DateObject>()) { switch (conversionType) { case QMetaType::QDateTime: return 0; @@ -1247,7 +1214,7 @@ static int MatchScore(const QV4::Value &actual, int conversionType) default: return 10; } - } else if (actual.asArrayObject()) { + } else if (actual.as<ArrayObject>()) { switch (conversionType) { case QMetaType::QJsonArray: return 3; @@ -1276,7 +1243,7 @@ static int MatchScore(const QV4::Value &actual, int conversionType) return 10; } } - } else if (QV4::Object *obj = actual.asObject()) { + } else if (const Object *obj = actual.as<Object>()) { if (obj->as<QV4::VariantObject>()) { if (conversionType == qMetaTypeId<QVariant>()) return 0; @@ -1379,7 +1346,7 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ if (returnType == QMetaType::UnknownType) { QString typeName = QString::fromLatin1(unknownTypeError); - QString error = QString::fromLatin1("Unknown method return type: %1").arg(typeName); + QString error = QStringLiteral("Unknown method return type: %1").arg(typeName); return engine->throwError(error); } @@ -1392,7 +1359,7 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ if (!args) { QString typeName = QString::fromLatin1(unknownTypeError); - QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName); + QString error = QStringLiteral("Unknown method parameter type: %1").arg(typeName); return engine->throwError(error); } @@ -1606,9 +1573,9 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q type = callType; } else if (callType == QMetaType::QObjectStar) { qobjectPtr = 0; - if (QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) + if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) qobjectPtr = qobjectWrapper->object(); - else if (QV4::QmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QmlTypeWrapper>()) + else if (const QV4::QmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QmlTypeWrapper>()) queryEngine = qmlTypeWrapper->isSingleton(); type = callType; } else if (callType == qMetaTypeId<QVariant>()) { @@ -1630,7 +1597,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q } } else { QObject *o = 0; - if (QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) + if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) o = qobjectWrapper->object(); qlistPtr->append(o); } @@ -1739,29 +1706,26 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine) } } -ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal) +ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index) { Scope valueScope(scope); - Scoped<QObjectMethod> method(valueScope, scope->d()->engine->memoryManager->alloc<QObjectMethod>(scope)); + Scoped<QObjectMethod> method(valueScope, scope->d()->engine->memoryManager->allocObject<QObjectMethod>(scope)); method->d()->object = object; if (QQmlData *ddata = QQmlData::get(object)) method->d()->propertyCache = ddata->propertyCache; method->d()->index = index; - method->d()->qmlGlobal = qmlGlobal; - method->d()->valueTypeWrapper = Primitive::undefinedValue(); return method.asReturnedValue(); } -ReturnedValue QObjectMethod::create(ExecutionContext *scope, QQmlValueTypeWrapper *valueType, int index, const Value &qmlGlobal) +ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index) { Scope valueScope(scope); - Scoped<QObjectMethod> method(valueScope, scope->d()->engine->memoryManager->alloc<QObjectMethod>(scope)); + Scoped<QObjectMethod> method(valueScope, scope->d()->engine->memoryManager->allocObject<QObjectMethod>(scope)); method->d()->propertyCache = valueType->d()->propertyCache; method->d()->index = index; - method->d()->qmlGlobal = qmlGlobal; - method->d()->valueTypeWrapper = valueType; + method->d()->valueTypeWrapper = valueType->d(); return method.asReturnedValue(); } @@ -1777,7 +1741,7 @@ const QMetaObject *Heap::QObjectMethod::metaObject() return object->metaObject(); } -QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) +QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) const { QString result; if (const QMetaObject *metaObject = d()->metaObject()) { @@ -1803,7 +1767,7 @@ QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) return ctx->d()->engine->newString(result)->asReturnedValue(); } -QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) +QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const { if (!d()->object) return Encode::undefined(); @@ -1822,16 +1786,16 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, con return Encode::undefined(); } -ReturnedValue QObjectMethod::call(Managed *m, CallData *callData) +ReturnedValue QObjectMethod::call(const Managed *m, CallData *callData) { - QObjectMethod *This = static_cast<QObjectMethod*>(m); + const QObjectMethod *This = static_cast<const QObjectMethod*>(m); return This->callInternal(callData); } -ReturnedValue QObjectMethod::callInternal(CallData *callData) +ReturnedValue QObjectMethod::callInternal(CallData *callData) const { - Scope scope(engine()); - ScopedContext context(scope, scope.engine->currentContext()); + ExecutionEngine *v4 = engine(); + ExecutionContext *context = v4->currentContext; if (d()->index == DestroyMethod) return method_destroy(context, callData->args, callData->argc); else if (d()->index == ToStringMethod) @@ -1839,11 +1803,10 @@ ReturnedValue QObjectMethod::callInternal(CallData *callData) QQmlObjectOrGadget object(d()->object.data()); if (!d()->object) { - Scoped<QQmlValueTypeWrapper> wrapper(scope, d()->valueTypeWrapper); - if (!wrapper) + if (!d()->valueTypeWrapper) return Encode::undefined(); - object = QQmlObjectOrGadget(d()->propertyCache.data(), wrapper->d()->gadgetPtr); + object = QQmlObjectOrGadget(d()->propertyCache.data(), d()->valueTypeWrapper->gadgetPtr); } QQmlPropertyData method; @@ -1875,12 +1838,9 @@ ReturnedValue QObjectMethod::callInternal(CallData *callData) } if (method.isV4Function()) { + Scope scope(v4); QV4::ScopedValue rv(scope, QV4::Primitive::undefinedValue()); - - QV4::ScopedValue qmlGlobal(scope, d()->qmlGlobal); - QQmlV4Function func(callData, rv, qmlGlobal, - QmlContextWrapper::getContext(qmlGlobal), - scope.engine); + QQmlV4Function func(callData, rv, v4); QQmlV4Function *funcptr = &func; void *args[] = { 0, &funcptr }; @@ -1890,32 +1850,46 @@ ReturnedValue QObjectMethod::callInternal(CallData *callData) } if (!method.isOverload()) { - return CallPrecise(object, method, scope.engine, callData); + return CallPrecise(object, method, v4, callData); } else { - return CallOverloaded(object, method, scope.engine, callData, d()->propertyCache); + return CallOverloaded(object, method, v4, callData, d()->propertyCache); } } void QObjectMethod::markObjects(Heap::Base *that, ExecutionEngine *e) { QObjectMethod::Data *This = static_cast<QObjectMethod::Data*>(that); - This->qmlGlobal.mark(e); - This->valueTypeWrapper.mark(e); + if (This->valueTypeWrapper) + This->valueTypeWrapper->mark(e); FunctionObject::markObjects(that, e); } DEFINE_OBJECT_VTABLE(QObjectMethod); -Heap::QmlSignalHandler::QmlSignalHandler(QV4::ExecutionEngine *engine, QObject *object, int signalIndex) - : Heap::Object(engine) - , object(object) +Heap::QmlSignalHandler::QmlSignalHandler(QObject *object, int signalIndex) + : object(object) , signalIndex(signalIndex) { } DEFINE_OBJECT_VTABLE(QmlSignalHandler); +void QmlSignalHandler::initProto(ExecutionEngine *engine) +{ + if (engine->signalHandlerPrototype()->d()) + return; + + Scope scope(engine); + ScopedObject o(scope, engine->newObject()); + QV4::ScopedString connect(scope, engine->newIdentifier(QStringLiteral("connect"))); + QV4::ScopedString disconnect(scope, engine->newIdentifier(QStringLiteral("disconnect"))); + o->put(connect, QV4::ScopedValue(scope, engine->functionPrototype()->get(connect))); + o->put(disconnect, QV4::ScopedValue(scope, engine->functionPrototype()->get(disconnect))); + + engine->jsObjects[QV4::ExecutionEngine::SignalHandlerProto] = o->d(); +} + void MultiplyWrappedQObjectMap::insert(QObject *key, Heap::Object *value) { QV4::WeakValue v; diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 24e8b29e08..1126013806 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -54,7 +54,7 @@ #include <private/qqmlpropertycache_p.h> #include <private/qintrusivelist_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> QT_BEGIN_NAMESPACE @@ -69,8 +69,10 @@ struct QObjectSlotDispatcher; namespace Heap { +struct QQmlValueTypeWrapper; + struct QObjectWrapper : Object { - QObjectWrapper(QV4::ExecutionEngine *engine, QObject *object); + QObjectWrapper(QObject *object); QPointer<QObject> object; }; @@ -79,15 +81,14 @@ struct QObjectMethod : FunctionObject { QPointer<QObject> object; QQmlRefPointer<QQmlPropertyCache> propertyCache; int index; - Value qmlGlobal; - Value valueTypeWrapper; + Pointer<QQmlValueTypeWrapper> valueTypeWrapper; const QMetaObject *metaObject(); }; struct QmlSignalHandler : Object { - QmlSignalHandler(QV4::ExecutionEngine *engine, QObject *object, int signalIndex); + QmlSignalHandler(QObject *object, int signalIndex); QPointer<QObject> object; int signalIndex; }; @@ -104,7 +105,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object QObject *object() const { return d()->object.data(); } - ReturnedValue getQmlProperty(QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = 0, bool includeImports = false); + ReturnedValue getQmlProperty(QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = 0, bool includeImports = false) const; static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = 0); static bool setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value); @@ -114,26 +115,27 @@ struct Q_QML_EXPORT QObjectWrapper : public Object using Object::get; - static ReturnedValue getProperty(QObject *object, ExecutionContext *ctx, int propertyIndex, bool captureRequired); - void setProperty(ExecutionContext *ctx, int propertyIndex, const Value &value); + static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired); + static void setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value); + void setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value); + + void destroyObject(bool lastCall); protected: static bool isEqualTo(Managed *that, Managed *o); -private: - static ReturnedValue getProperty(QObject *object, ExecutionContext *ctx, QQmlPropertyData *property, bool captureRequired = true); - static void setProperty(QObject *object, ExecutionContext *ctx, QQmlPropertyData *property, const Value &value); + static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true); + static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value); static ReturnedValue create(ExecutionEngine *engine, QObject *object); QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static void put(Managed *m, String *name, const Value &value); static PropertyAttributes query(const Managed *, String *name); - static void advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attributes); + static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); - static void destroy(Heap::Base *that); static ReturnedValue method_connect(CallContext *ctx); static ReturnedValue method_disconnect(CallContext *ctx); @@ -148,18 +150,18 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject enum { DestroyMethod = -1, ToStringMethod = -2 }; - static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal = Primitive::undefinedValue()); - static ReturnedValue create(QV4::ExecutionContext *scope, QQmlValueTypeWrapper *valueType, int index, const Value &qmlGlobal = Primitive::undefinedValue()); + static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index); + static ReturnedValue create(QV4::ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index); int methodIndex() const { return d()->index; } QObject *object() const { return d()->object.data(); } - QV4::ReturnedValue method_toString(QV4::ExecutionContext *ctx); - QV4::ReturnedValue method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc); + QV4::ReturnedValue method_toString(QV4::ExecutionContext *ctx) const; + QV4::ReturnedValue method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const; - static ReturnedValue call(Managed *, CallData *callData); + static ReturnedValue call(const Managed *, CallData *callData); - ReturnedValue callInternal(CallData *callData); + ReturnedValue callInternal(CallData *callData) const; static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); }; @@ -167,10 +169,13 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject struct QmlSignalHandler : public QV4::Object { V4_OBJECT2(QmlSignalHandler, QV4::Object) + V4_PROTOTYPE(signalHandlerPrototype) V4_NEEDS_DESTROY int signalIndex() const { return d()->signalIndex; } QObject *object() const { return d()->object.data(); } + + static void initProto(ExecutionEngine *v4); }; class MultiplyWrappedQObjectMap : public QObject, diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index 8e18a5fbdd..31fee534ad 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -34,16 +34,16 @@ #include "qv4regexp_p.h" #include "qv4engine_p.h" #include "qv4scopedvalue_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> using namespace QV4; RegExpCache::~RegExpCache() { - for (RegExpCache::Iterator it = begin(), e = end(); - it != e; ++it) - it.value()->cache = 0; - clear(); + for (RegExpCache::Iterator it = begin(), e = end(); it != e; ++it) { + if (RegExp *re = it.value().as<RegExp>()) + re->d()->cache = 0; + } } DEFINE_MANAGED_VTABLE(RegExp); @@ -68,19 +68,18 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo RegExpCacheKey key(pattern, ignoreCase, multiline); RegExpCache *cache = engine->regExpCache; - if (cache) { - if (Heap::RegExp *result = cache->value(key)) - return result; - } + if (!cache) + cache = engine->regExpCache = new RegExpCache; + + QV4::WeakValue &cachedValue = (*cache)[key]; + if (QV4::RegExp *result = cachedValue.as<RegExp>()) + return result->d(); Scope scope(engine); Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(engine, pattern, ignoreCase, multiline)); - if (!cache) - cache = engine->regExpCache = new RegExpCache; - result->d()->cache = cache; - cache->insert(key, result->d()); + cachedValue.set(engine, result); return result->d(); } diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h index 819e31e5f1..928362a995 100644 --- a/src/qml/jsruntime/qv4regexp_p.h +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -33,6 +33,17 @@ #ifndef QV4REGEXP_H #define QV4REGEXP_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QString> #include <QVector> @@ -133,8 +144,7 @@ inline RegExpCacheKey::RegExpCacheKey(const RegExp::Data *re) inline uint qHash(const RegExpCacheKey& key, uint seed = 0) Q_DECL_NOTHROW { return qHash(key.pattern, seed); } -// ### GC -class RegExpCache : public QHash<RegExpCacheKey, Heap::RegExp*> +class RegExpCache : public QHash<RegExpCacheKey, WeakValue> { public: ~RegExpCache(); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index f6e88e62b7..1839ff17ab 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -37,7 +37,7 @@ #include "qv4objectproto_p.h" #include "qv4regexp_p.h" #include "qv4stringobject_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" #include <private/qqmljsengine_p.h> @@ -62,35 +62,30 @@ Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyn using namespace QV4; DEFINE_OBJECT_VTABLE(RegExpObject); -DEFINE_OBJECT_VTABLE(RegExpPrototype); -Heap::RegExpObject::RegExpObject(InternalClass *ic, QV4::Object *prototype) - : Heap::Object(ic, prototype) +Heap::RegExpObject::RegExpObject() { - Scope scope(ic->engine); + Scope scope(internalClass->engine); Scoped<QV4::RegExpObject> o(scope, this); - o->d()->value = QV4::RegExp::create(ic->engine, QString(), false, false); + o->d()->value = QV4::RegExp::create(scope.engine, QString(), false, false); o->d()->global = false; - o->init(ic->engine); + o->initProperties(); } -Heap::RegExpObject::RegExpObject(QV4::ExecutionEngine *engine, QV4::RegExp *value, bool global) - : Heap::Object(engine->emptyClass, engine->regExpPrototype.asObject()) - , value(value->d()) +Heap::RegExpObject::RegExpObject(QV4::RegExp *value, bool global) + : value(value->d()) , global(global) { - Scope scope(engine); + Scope scope(internalClass->engine); Scoped<QV4::RegExpObject> o(scope, this); - o->init(engine); + o->initProperties(); } // Converts a QRegExp to a JS RegExp. // The conversion is not 100% exact since ECMA regexp and QRegExp // have different semantics/flags, but we try to do our best. -Heap::RegExpObject::RegExpObject(QV4::ExecutionEngine *engine, const QRegExp &re) - : Heap::Object(engine->emptyClass, engine->regExpPrototype.asObject()) +Heap::RegExpObject::RegExpObject(const QRegExp &re) { - value = 0; global = false; // Convert the pattern to a ECMAScript pattern. @@ -130,26 +125,21 @@ Heap::RegExpObject::RegExpObject(QV4::ExecutionEngine *engine, const QRegExp &re pattern = ecmaPattern; } - Scope scope(engine); + Scope scope(internalClass->engine); Scoped<QV4::RegExpObject> o(scope, this); - o->d()->value = QV4::RegExp::create(engine, pattern, re.caseSensitivity() == Qt::CaseInsensitive, false); + o->d()->value = QV4::RegExp::create(scope.engine, pattern, re.caseSensitivity() == Qt::CaseInsensitive, false); - o->init(engine); + o->initProperties(); } -void RegExpObject::init(ExecutionEngine *engine) +void RegExpObject::initProperties() { - Scope scope(engine); - ScopedObject protectThis(scope, this); + *propertyData(Index_LastIndex) = Primitive::fromInt32(0); - ScopedString lastIndex(scope, engine->newIdentifier(QStringLiteral("lastIndex"))); - ScopedValue v(scope, Primitive::fromInt32(0)); - insertMember(lastIndex, v, Attr_NotEnumerable|Attr_NotConfigurable); - if (!this->value()) - return; + Q_ASSERT(value()); - QString p = this->value()->pattern; + QString p = value()->pattern; if (p.isEmpty()) { p = QStringLiteral("(?:)"); } else { @@ -157,10 +147,10 @@ void RegExpObject::init(ExecutionEngine *engine) p.replace('/', QLatin1String("\\/")); } - defineReadonlyProperty(QStringLiteral("source"), (v = engine->newString(p))); - defineReadonlyProperty(QStringLiteral("global"), Primitive::fromBoolean(global())); - defineReadonlyProperty(QStringLiteral("ignoreCase"), Primitive::fromBoolean(this->value()->ignoreCase)); - defineReadonlyProperty(QStringLiteral("multiline"), Primitive::fromBoolean(this->value()->multiLine)); + *propertyData(Index_Source) = engine()->newString(p); + *propertyData(Index_Global) = Primitive::fromBoolean(global()); + *propertyData(Index_IgnoreCase) = Primitive::fromBoolean(value()->ignoreCase); + *propertyData(Index_Multiline) = Primitive::fromBoolean(value()->multiLine); } @@ -172,10 +162,10 @@ void RegExpObject::markObjects(Heap::Base *that, ExecutionEngine *e) Object::markObjects(that, e); } -Property *RegExpObject::lastIndexProperty() +Value *RegExpObject::lastIndexProperty() { - Q_ASSERT(0 == internalClass()->find(engine()->id_lastIndex)); - return propertyAt(0); + Q_ASSERT(0 == internalClass()->find(engine()->id_lastIndex())); + return propertyData(0); } // Converts a JS RegExp to a QRegExp. @@ -231,25 +221,24 @@ Heap::RegExpCtor::RegExpCtor(QV4::ExecutionContext *scope) void Heap::RegExpCtor::clearLastMatch() { lastMatch = Primitive::nullValue(); - lastInput = internalClass->engine->id_empty; + lastInput = internalClass->engine->id_empty()->d(); lastMatchStart = 0; lastMatchEnd = 0; } -ReturnedValue RegExpCtor::construct(Managed *m, CallData *callData) +ReturnedValue RegExpCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<Object *>(m)->engine()); - ScopedContext ctx(scope, scope.engine->currentContext()); + Scope scope(static_cast<const Object *>(m)->engine()); ScopedValue r(scope, callData->argument(0)); ScopedValue f(scope, callData->argument(1)); Scoped<RegExpObject> re(scope, r); if (re) { if (!f->isUndefined()) - return ctx->engine()->throwTypeError(); + return scope.engine->throwTypeError(); Scoped<RegExp> regexp(scope, re->value()); - return Encode(ctx->d()->engine->newRegExpObject(regexp, re->global())); + return Encode(scope.engine->newRegExpObject(regexp, re->global())); } QString pattern; @@ -274,19 +263,19 @@ ReturnedValue RegExpCtor::construct(Managed *m, CallData *callData) } else if (str.at(i) == QLatin1Char('m') && !multiLine) { multiLine = true; } else { - return ctx->engine()->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor")); + return scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor")); } } } - Scoped<RegExp> regexp(scope, RegExp::create(ctx->d()->engine, pattern, ignoreCase, multiLine)); + Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, ignoreCase, multiLine)); if (!regexp->isValid()) - return ctx->engine()->throwSyntaxError(QStringLiteral("Invalid regular expression")); + return scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression")); - return Encode(ctx->d()->engine->newRegExpObject(regexp, global)); + return Encode(scope.engine->newRegExpObject(regexp, global)); } -ReturnedValue RegExpCtor::call(Managed *that, CallData *callData) +ReturnedValue RegExpCtor::call(const Managed *that, CallData *callData) { if (callData->argc > 0 && callData->args[0].as<RegExpObject>()) { if (callData->argc == 1 || callData->args[1].isUndefined()) @@ -300,7 +289,7 @@ void RegExpCtor::markObjects(Heap::Base *that, ExecutionEngine *e) { RegExpCtor::Data *This = static_cast<RegExpCtor::Data *>(that); This->lastMatch.mark(e); - This->lastInput.mark(e); + This->lastInput->mark(e); FunctionObject::markObjects(that, e); } @@ -310,8 +299,8 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) ScopedObject o(scope); ScopedObject ctor(scope, constructor); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(2)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(2)); // Properties deprecated in the spec but required by "the web" :( ctor->defineAccessorProperty(QStringLiteral("lastMatch"), method_get_lastMatch_n<0>, 0); @@ -337,7 +326,7 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(QStringLiteral("exec"), method_exec, 1); defineDefaultProperty(QStringLiteral("test"), method_test, 1); - defineDefaultProperty(engine->id_toString, method_toString, 0); + defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("compile"), method_compile, 2); } @@ -354,25 +343,25 @@ ReturnedValue RegExpPrototype::method_exec(CallContext *ctx) return Encode::undefined(); QString s = arg->stringValue()->toQString(); - int offset = r->global() ? r->lastIndexProperty()->value.toInt32() : 0; + int offset = r->global() ? r->lastIndexProperty()->toInt32() : 0; if (offset < 0 || offset > s.length()) { - r->lastIndexProperty()->value = Primitive::fromInt32(0); + *r->lastIndexProperty() = Primitive::fromInt32(0); return Encode::null(); } uint* matchOffsets = (uint*)alloca(r->value()->captureCount() * 2 * sizeof(uint)); const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets); - Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor); + Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor()); regExpCtor->d()->clearLastMatch(); if (result == -1) { - r->lastIndexProperty()->value = Primitive::fromInt32(0); + *r->lastIndexProperty() = Primitive::fromInt32(0); return Encode::null(); } // fill in result data - ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->regExpExecArrayClass, scope.engine->arrayPrototype.asObject())); + ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->regExpExecArrayClass, scope.engine->arrayPrototype())); int len = r->value()->captureCount(); array->arrayReserve(len); ScopedValue v(scope); @@ -383,17 +372,17 @@ ReturnedValue RegExpPrototype::method_exec(CallContext *ctx) array->arrayPut(i, v); } array->setArrayLengthUnchecked(len); - array->memberData()->data[Index_ArrayIndex] = Primitive::fromInt32(result); - array->memberData()->data[Index_ArrayInput] = arg; + *array->propertyData(Index_ArrayIndex) = Primitive::fromInt32(result); + *array->propertyData(Index_ArrayInput) = arg; RegExpCtor::Data *dd = regExpCtor->d(); dd->lastMatch = array; - dd->lastInput = arg->stringValue(); + dd->lastInput = arg->stringValue()->d(); dd->lastMatchStart = matchOffsets[0]; dd->lastMatchEnd = matchOffsets[1]; if (r->global()) - r->lastIndexProperty()->value = Primitive::fromInt32(matchOffsets[1]); + *r->lastIndexProperty() = Primitive::fromInt32(matchOffsets[1]); return array.asReturnedValue(); } @@ -425,7 +414,7 @@ ReturnedValue RegExpPrototype::method_compile(CallContext *ctx) ScopedCallData callData(scope, ctx->argc()); memcpy(callData->args, ctx->args(), ctx->argc()*sizeof(Value)); - Scoped<RegExpObject> re(scope, ctx->d()->engine->regExpCtor.asFunctionObject()->construct(callData)); + Scoped<RegExpObject> re(scope, ctx->d()->engine->regExpCtor()->as<FunctionObject>()->construct(callData)); r->d()->value = re->value(); r->d()->global = re->global(); @@ -436,7 +425,7 @@ template <int index> ReturnedValue RegExpPrototype::method_get_lastMatch_n(CallContext *ctx) { Scope scope(ctx); - ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor.objectValue())->lastMatch()); + ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor())->lastMatch()); ScopedValue result(scope, lastMatch ? lastMatch->getIndexed(index) : Encode::undefined()); if (result->isUndefined()) return ctx->d()->engine->newString()->asReturnedValue(); @@ -446,7 +435,7 @@ ReturnedValue RegExpPrototype::method_get_lastMatch_n(CallContext *ctx) ReturnedValue RegExpPrototype::method_get_lastParen(CallContext *ctx) { Scope scope(ctx); - ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor.objectValue())->lastMatch()); + ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor())->lastMatch()); ScopedValue result(scope, lastMatch ? lastMatch->getIndexed(lastMatch->getLength() - 1) : Encode::undefined()); if (result->isUndefined()) return ctx->d()->engine->newString()->asReturnedValue(); @@ -455,13 +444,13 @@ ReturnedValue RegExpPrototype::method_get_lastParen(CallContext *ctx) ReturnedValue RegExpPrototype::method_get_input(CallContext *ctx) { - return static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor.objectValue())->lastInput().asReturnedValue(); + return static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor())->lastInput()->asReturnedValue(); } ReturnedValue RegExpPrototype::method_get_leftContext(CallContext *ctx) { Scope scope(ctx); - Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor); + Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor()); QString lastInput = regExpCtor->lastInput()->toQString(); return ctx->d()->engine->newString(lastInput.left(regExpCtor->lastMatchStart()))->asReturnedValue(); } @@ -469,7 +458,7 @@ ReturnedValue RegExpPrototype::method_get_leftContext(CallContext *ctx) ReturnedValue RegExpPrototype::method_get_rightContext(CallContext *ctx) { Scope scope(ctx); - Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor); + Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor()); QString lastInput = regExpCtor->lastInput()->toQString(); return ctx->d()->engine->newString(lastInput.mid(regExpCtor->lastMatchEnd()))->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index f5f255faf5..81ea9cf14b 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -33,6 +33,17 @@ #ifndef QV4REGEXPOBJECT_H #define QV4REGEXPOBJECT_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4runtime_p.h" #include "qv4engine_p.h" #include "qv4context_p.h" @@ -57,33 +68,30 @@ namespace QV4 { namespace Heap { struct RegExpObject : Object { - RegExpObject(InternalClass *ic, QV4::Object *prototype); - RegExpObject(QV4::ExecutionEngine *engine, QV4::RegExp *value, bool global); - RegExpObject(QV4::ExecutionEngine *engine, const QRegExp &re); + RegExpObject(); + RegExpObject(QV4::RegExp *value, bool global); + RegExpObject(const QRegExp &re); - RegExp *value; + Pointer<RegExp> value; bool global; }; struct RegExpCtor : FunctionObject { RegExpCtor(QV4::ExecutionContext *scope); Value lastMatch; - StringValue lastInput; + Pointer<String> lastInput; int lastMatchStart; int lastMatchEnd; void clearLastMatch(); }; -struct RegExpPrototype : RegExpObject -{ - inline RegExpPrototype(ExecutionEngine *e); -}; - } struct RegExpObject: Object { V4_OBJECT2(RegExpObject, Object) Q_MANAGED_TYPE(RegExpObject) + V4_INTERNALCLASS(regExpObjectClass) + V4_PROTOTYPE(regExpPrototype) // needs to be compatible with the flags in qv4jsir_p.h enum Flags { @@ -93,6 +101,11 @@ struct RegExpObject: Object { }; enum { + Index_LastIndex = 0, + Index_Source = 1, + Index_Global = 2, + Index_IgnoreCase = 3, + Index_Multiline = 4, Index_ArrayIndex = Heap::ArrayObject::LengthPropertyIndex + 1, Index_ArrayInput = Index_ArrayIndex + 1 }; @@ -100,9 +113,9 @@ struct RegExpObject: Object { Heap::RegExp *value() const { return d()->value; } bool global() const { return d()->global; } - void init(ExecutionEngine *engine); + void initProperties(); - Property *lastIndexProperty(); + Value *lastIndexProperty(); QRegExp toQRegExp() const; QString toString() const; QString source() const; @@ -117,19 +130,17 @@ struct RegExpCtor: FunctionObject V4_OBJECT2(RegExpCtor, FunctionObject) Value lastMatch() { return d()->lastMatch; } - StringValue lastInput() { return d()->lastInput; } + Heap::String *lastInput() { return d()->lastInput; } int lastMatchStart() { return d()->lastMatchStart; } int lastMatchEnd() { return d()->lastMatchEnd; } - static ReturnedValue construct(Managed *m, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; struct RegExpPrototype: RegExpObject { - V4_OBJECT2(RegExpPrototype, RegExpObject) - void init(ExecutionEngine *engine, Object *ctor); static ReturnedValue method_exec(CallContext *ctx); @@ -145,11 +156,6 @@ struct RegExpPrototype: RegExpObject static ReturnedValue method_get_rightContext(CallContext *ctx); }; -inline Heap::RegExpPrototype::RegExpPrototype(ExecutionEngine *e) - : RegExpObject(e->emptyClass, e->objectPrototype.asObject()) -{ -} - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index b66e917b87..a988313f5f 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -40,12 +40,15 @@ #include "qv4stringobject_p.h" #include "qv4argumentsobject_p.h" #include "qv4objectiterator_p.h" +#include "qv4dateobject_p.h" #include "qv4lookup_p.h" #include "qv4function_p.h" #include "private/qlocale_tools_p.h" #include "qv4scopedvalue_p.h" #include <private/qqmlcontextwrapper_p.h> #include <private/qqmltypewrapper_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmljavascriptexpression_p.h> #include "qv4qobjectwrapper_p.h" #include <private/qv8engine_p.h> #endif @@ -265,10 +268,9 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) ReturnedValue Runtime::closure(ExecutionEngine *engine, int functionId) { - QV4::Function *clos = engine->currentContext()->compilationUnit->runtimeFunctions[functionId]; + QV4::Function *clos = engine->current->compilationUnit->runtimeFunctions[functionId]; Q_ASSERT(clos); - Scope scope(engine); - return FunctionObject::createScriptFunction(ScopedContext(scope, engine->currentContext()), clos)->asReturnedValue(); + return FunctionObject::createScriptFunction(engine->currentContext, clos)->asReturnedValue(); } ReturnedValue Runtime::deleteElement(ExecutionEngine *engine, const Value &base, const Value &index) @@ -289,7 +291,7 @@ ReturnedValue Runtime::deleteElement(ExecutionEngine *engine, const Value &base, ReturnedValue Runtime::deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); return deleteMemberString(engine, base, name); } @@ -305,22 +307,21 @@ ReturnedValue Runtime::deleteMemberString(ExecutionEngine *engine, const Value & ReturnedValue Runtime::deleteName(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); - ScopedContext ctx(scope, engine->currentContext()); - return Encode(ctx->deleteProperty(name)); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + return Encode(engine->currentContext->deleteProperty(name)); } QV4::ReturnedValue Runtime::instanceof(ExecutionEngine *engine, const Value &left, const Value &right) { Scope scope(engine); - ScopedFunctionObject f(scope, right.asFunctionObject()); + ScopedFunctionObject f(scope, right.as<FunctionObject>()); if (!f) return engine->throwTypeError(); if (f->isBoundFunction()) f = static_cast<BoundFunction *>(f.getPointer())->target(); - ScopedObject v(scope, left.asObject()); + ScopedObject v(scope, left.as<Object>()); if (!v) return Encode(false); @@ -380,10 +381,10 @@ Heap::String *RuntimeHelpers::stringFromNumber(ExecutionEngine *engine, double n return engine->newString(qstr); } -ReturnedValue RuntimeHelpers::objectDefaultValue(Object *object, int typeHint) +ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeHint) { if (typeHint == PREFERREDTYPE_HINT) { - if (object->asDateObject()) + if (object->as<DateObject>()) typeHint = STRING_HINT; else typeHint = NUMBER_HINT; @@ -393,18 +394,18 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(Object *object, int typeHint) if (engine->hasException) return Encode::undefined(); - StringValue *meth1 = &engine->id_toString; - StringValue *meth2 = &engine->id_valueOf; + String *meth1 = engine->id_toString(); + String *meth2 = engine->id_valueOf(); if (typeHint == NUMBER_HINT) qSwap(meth1, meth2); Scope scope(engine); ScopedCallData callData(scope, 0); - callData->thisObject = object; + callData->thisObject = *object; - ScopedValue conv(scope, object->get(*meth1)); - if (FunctionObject *o = conv->asFunctionObject()) { + ScopedValue conv(scope, object->get(meth1)); + if (FunctionObject *o = conv->as<FunctionObject>()) { ScopedValue r(scope, o->call(callData)); if (r->isPrimitive()) return r->asReturnedValue(); @@ -413,8 +414,8 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(Object *object, int typeHint) if (engine->hasException) return Encode::undefined(); - conv = object->get(*meth2); - if (FunctionObject *o = conv->asFunctionObject()) { + conv = object->get(meth2); + if (FunctionObject *o = conv->as<FunctionObject>()) { ScopedValue r(scope, o->call(callData)); if (r->isPrimitive()) return r->asReturnedValue(); @@ -437,7 +438,7 @@ Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Val return engine->newBooleanObject(value.booleanValue()); case Value::Managed_Type: Q_ASSERT(value.isString()); - return engine->newStringObject(value); + return engine->newStringObject(value.stringValue()); case Value::Integer_Type: default: // double return engine->newNumberObject(value.asDouble()); @@ -450,14 +451,14 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Val case Value::Empty_Type: Q_ASSERT(!"empty Value encountered"); case Value::Undefined_Type: - return engine->id_undefined->d(); + return engine->id_undefined()->d(); case Value::Null_Type: - return engine->id_null->d(); + return engine->id_null()->d(); case Value::Boolean_Type: if (value.booleanValue()) - return engine->id_true->d(); + return engine->id_true()->d(); else - return engine->id_false->d(); + return engine->id_false()->d(); case Value::Managed_Type: if (value.isString()) return value.stringValue()->d(); @@ -467,7 +468,7 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Val return RuntimeHelpers::convertToString(engine, prim); } case Value::Integer_Type: - return RuntimeHelpers::stringFromNumber(engine, value.int_32); + return RuntimeHelpers::stringFromNumber(engine, value.int_32()); default: // double return RuntimeHelpers::stringFromNumber(engine, value.doubleValue()); } // switch @@ -481,14 +482,14 @@ static Heap::String *convert_to_string_add(ExecutionEngine *engine, const Value case Value::Empty_Type: Q_ASSERT(!"empty Value encountered"); case Value::Undefined_Type: - return engine->id_undefined->d(); + return engine->id_undefined()->d(); case Value::Null_Type: - return engine->id_null->d(); + return engine->id_null()->d(); case Value::Boolean_Type: if (value.booleanValue()) - return engine->id_true->d(); + return engine->id_true()->d(); else - return engine->id_false->d(); + return engine->id_false()->d(); case Value::Managed_Type: if (value.isString()) return value.stringValue()->d(); @@ -498,7 +499,7 @@ static Heap::String *convert_to_string_add(ExecutionEngine *engine, const Value return RuntimeHelpers::convertToString(engine, prim); } case Value::Integer_Type: - return RuntimeHelpers::stringFromNumber(engine, value.int_32); + return RuntimeHelpers::stringFromNumber(engine, value.int_32()); default: // double return RuntimeHelpers::stringFromNumber(engine, value.doubleValue()); } // switch @@ -563,7 +564,7 @@ QV4::ReturnedValue Runtime::addString(ExecutionEngine *engine, const Value &left void Runtime::setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) { Scope scope(engine); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); ScopedObject o(scope, object.toObject(engine)); if (!o) return; @@ -578,7 +579,7 @@ ReturnedValue Runtime::getElement(ExecutionEngine *engine, const Value &object, ScopedObject o(scope, object); if (!o) { if (idx < UINT_MAX) { - if (String *str = object.asString()) { + if (const String *str = object.as<String>()) { if (idx >= (uint)str->toQString().length()) { return Encode::undefined(); } @@ -660,15 +661,14 @@ ReturnedValue Runtime::foreachNextPropertyName(const Value &foreach_iterator) void Runtime::setActivationProperty(ExecutionEngine *engine, int nameIndex, const Value &value) { Scope scope(engine); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); - ScopedContext ctx(scope, engine->currentContext()); - ctx->setProperty(name, value); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + engine->currentContext->setProperty(name, value); } ReturnedValue Runtime::getProperty(ExecutionEngine *engine, const Value &object, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); ScopedObject o(scope, object); if (o) @@ -688,9 +688,8 @@ ReturnedValue Runtime::getProperty(ExecutionEngine *engine, const Value &object, ReturnedValue Runtime::getActivationProperty(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); - ScopedContext ctx(scope, engine->currentContext()); - return ctx->getProperty(name); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + return engine->currentContext->getProperty(name); } #endif // V4_BOOTSTRAP @@ -906,13 +905,13 @@ ReturnedValue Runtime::callGlobalLookup(ExecutionEngine *engine, uint index, Cal Scope scope(engine); Q_ASSERT(callData->thisObject.isUndefined()); - Lookup *l = engine->currentContext()->lookups + index; + Lookup *l = engine->current->lookups + index; ScopedFunctionObject o(scope, l->globalGetter(l, engine)); if (!o) return engine->throwTypeError(); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[l->nameIndex]); - if (o->d() == scope.engine->evalFunction && name->equals(scope.engine->id_eval)) + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); + if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) return static_cast<EvalFunction *>(o.getPointer())->evalCall(callData, true); return o->call(callData); @@ -923,18 +922,17 @@ ReturnedValue Runtime::callActivationProperty(ExecutionEngine *engine, int nameI { Q_ASSERT(callData->thisObject.isUndefined()); Scope scope(engine); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); ScopedObject base(scope); - ScopedContext ctx(scope, engine->currentContext()); - ScopedValue func(scope, ctx->getPropertyAndBase(name, base.getRef())); + ScopedValue func(scope, engine->currentContext->getPropertyAndBase(name, base.getRef())); if (scope.engine->hasException) return Encode::undefined(); if (base) callData->thisObject = base; - FunctionObject *o = func->asFunctionObject(); + FunctionObject *o = func->as<FunctionObject>(); if (!o) { QString objectAsString = QStringLiteral("[null]"); if (base) @@ -943,17 +941,41 @@ ReturnedValue Runtime::callActivationProperty(ExecutionEngine *engine, int nameI return engine->throwTypeError(msg); } - if (o->d() == scope.engine->evalFunction && name->equals(scope.engine->id_eval)) { + if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) { return static_cast<EvalFunction *>(o)->evalCall(callData, true); } return o->call(callData); } +ReturnedValue Runtime::callQmlScopeObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) +{ + Scope scope(engine); + ScopedFunctionObject o(scope, getQmlScopeObjectProperty(engine, callData->thisObject, propertyIndex)); + if (!o) { + QString error = QStringLiteral("Property '%1' of object %2 is not a function").arg(propertyIndex).arg(callData->thisObject.toQStringNoThrow()); + return engine->throwTypeError(error); + } + + return o->call(callData); +} + +ReturnedValue Runtime::callQmlContextObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) +{ + Scope scope(engine); + ScopedFunctionObject o(scope, getQmlContextObjectProperty(engine, callData->thisObject, propertyIndex)); + if (!o) { + QString error = QStringLiteral("Property '%1' of object %2 is not a function").arg(propertyIndex).arg(callData->thisObject.toQStringNoThrow()); + return engine->throwTypeError(error); + } + + return o->call(callData); +} + ReturnedValue Runtime::callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) { Scope scope(engine); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); ScopedObject baseObject(scope, callData->thisObject); if (!baseObject) { Q_ASSERT(!callData->thisObject.isEmpty()); @@ -979,7 +1001,7 @@ ReturnedValue Runtime::callProperty(ExecutionEngine *engine, int nameIndex, Call ReturnedValue Runtime::callPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData) { - Lookup *l = engine->currentContext()->lookups + index; + Lookup *l = engine->current->lookups + index; Value v; v = l->getter(l, engine, callData->thisObject); if (!v.isObject()) @@ -1019,7 +1041,7 @@ ReturnedValue Runtime::constructGlobalLookup(ExecutionEngine *engine, uint index Scope scope(engine); Q_ASSERT(callData->thisObject.isUndefined()); - Lookup *l = engine->currentContext()->lookups + index; + Lookup *l = engine->current->lookups + index; ScopedObject f(scope, l->globalGetter(l, engine)); if (!f) return engine->throwTypeError(); @@ -1031,13 +1053,12 @@ ReturnedValue Runtime::constructGlobalLookup(ExecutionEngine *engine, uint index ReturnedValue Runtime::constructActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) { Scope scope(engine); - ScopedContext ctx(scope, engine->currentContext()); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); - ScopedValue func(scope, ctx->getProperty(name)); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + ScopedValue func(scope, engine->currentContext->getProperty(name)); if (scope.engine->hasException) return Encode::undefined(); - Object *f = func->asObject(); + Object *f = func->as<Object>(); if (!f) return engine->throwTypeError(); @@ -1046,7 +1067,7 @@ ReturnedValue Runtime::constructActivationProperty(ExecutionEngine *engine, int ReturnedValue Runtime::constructValue(ExecutionEngine *engine, const Value &func, CallData *callData) { - Object *f = func.asObject(); + const Object *f = func.as<Object>(); if (!f) return engine->throwTypeError(); @@ -1057,7 +1078,7 @@ ReturnedValue Runtime::constructProperty(ExecutionEngine *engine, int nameIndex, { Scope scope(engine); ScopedObject thisObject(scope, callData->thisObject.toObject(engine)); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); if (scope.engine->hasException) return Encode::undefined(); @@ -1070,7 +1091,7 @@ ReturnedValue Runtime::constructProperty(ExecutionEngine *engine, int nameIndex, ReturnedValue Runtime::constructPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData) { - Lookup *l = engine->currentContext()->lookups + index; + Lookup *l = engine->current->lookups + index; Value v; v = l->getter(l, engine, callData->thisObject); if (!v.isObject()) @@ -1092,24 +1113,24 @@ ReturnedValue Runtime::typeofValue(ExecutionEngine *engine, const Value &value) ScopedString res(scope); switch (value.type()) { case Value::Undefined_Type: - res = engine->id_undefined; + res = engine->id_undefined(); break; case Value::Null_Type: - res = engine->id_object; + res = engine->id_object(); break; case Value::Boolean_Type: - res = engine->id_boolean; + res = engine->id_boolean(); break; case Value::Managed_Type: if (value.isString()) - res = engine->id_string; - else if (value.objectValue()->asFunctionObject()) - res = engine->id_function; + res = engine->id_string(); + else if (value.objectValue()->as<FunctionObject>()) + res = engine->id_function(); else - res = engine->id_object; // ### implementation-defined + res = engine->id_object(); // ### implementation-defined break; default: - res = engine->id_number; + res = engine->id_number(); break; } return res.asReturnedValue(); @@ -1118,18 +1139,39 @@ ReturnedValue Runtime::typeofValue(ExecutionEngine *engine, const Value &value) QV4::ReturnedValue Runtime::typeofName(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); - ScopedContext ctx(scope, engine->currentContext()); - ScopedValue prop(scope, ctx->getProperty(name)); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + ScopedValue prop(scope, engine->currentContext->getProperty(name)); // typeof doesn't throw. clear any possible exception scope.engine->hasException = false; return Runtime::typeofValue(engine, prop); } +#ifndef V4_BOOTSTRAP +ReturnedValue Runtime::typeofScopeObjectProperty(ExecutionEngine *engine, const Value &context, + int propertyIndex) +{ + Scope scope(engine); + ScopedValue prop(scope, getQmlScopeObjectProperty(engine, context, propertyIndex)); + if (scope.engine->hasException) + return Encode::undefined(); + return Runtime::typeofValue(engine, prop); +} + +ReturnedValue Runtime::typeofContextObjectProperty(ExecutionEngine *engine, const Value &context, + int propertyIndex) +{ + Scope scope(engine); + ScopedValue prop(scope, getQmlContextObjectProperty(engine, context, propertyIndex)); + if (scope.engine->hasException) + return Encode::undefined(); + return Runtime::typeofValue(engine, prop); +} +#endif // V4_BOOTSTRAP + QV4::ReturnedValue Runtime::typeofMember(ExecutionEngine *engine, const Value &base, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); ScopedObject obj(scope, base.toObject(engine)); if (scope.engine->hasException) return Encode::undefined(); @@ -1148,14 +1190,6 @@ QV4::ReturnedValue Runtime::typeofElement(ExecutionEngine *engine, const Value & return Runtime::typeofValue(engine, prop); } -void Runtime::pushWithScope(const Value &o, ExecutionEngine *engine) -{ - Scope scope(engine); - ScopedObject obj(scope, o.toObject(engine)); - ScopedContext ctx(scope, engine->currentContext()); - ctx->newWithContext(obj); -} - ReturnedValue Runtime::unwindException(ExecutionEngine *engine) { if (!engine->hasException) @@ -1163,46 +1197,48 @@ ReturnedValue Runtime::unwindException(ExecutionEngine *engine) return engine->catchException(0); } +/* The next three methods are a bit tricky. They can't open up a Scope, as that + * would mess up the pushing of the context. + * + * Instead the push/pop pair acts as a non local scope. + */ +void Runtime::pushWithScope(const Value &o, ExecutionEngine *engine) +{ + engine->pushContext(engine->currentContext->newWithContext(o.toObject(engine))); + Q_ASSERT(engine->jsStackTop = engine->currentContext + 2); +} + void Runtime::pushCatchScope(NoThrowEngine *engine, int exceptionVarNameIndex) { - Scope scope(engine); - ScopedValue v(scope, engine->catchException(0)); - ScopedString exceptionVarName(scope, engine->currentContext()->compilationUnit->runtimeStrings[exceptionVarNameIndex]); - ScopedContext ctx(scope, engine->currentContext()); - ctx->newCatchContext(exceptionVarName, v); + ExecutionContext *c = engine->currentContext; + engine->pushContext(c->newCatchContext(c->d()->compilationUnit->runtimeStrings[exceptionVarNameIndex], engine->catchException(0))); + Q_ASSERT(engine->jsStackTop = engine->currentContext + 2); } void Runtime::popScope(ExecutionEngine *engine) { + Q_ASSERT(engine->jsStackTop = engine->currentContext + 2); engine->popContext(); + engine->jsStackTop -= 2; } void Runtime::declareVar(ExecutionEngine *engine, bool deletable, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); - ScopedContext ctx(scope, engine->currentContext()); - ctx->createMutableBinding(name, deletable); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + engine->currentContext->createMutableBinding(name, deletable); } ReturnedValue Runtime::arrayLiteral(ExecutionEngine *engine, Value *values, uint length) { - Scope scope(engine); - ScopedArrayObject a(scope, engine->newArrayObject()); - - if (length) { - a->arrayReserve(length); - a->arrayPut(0, values, length); - a->setArrayLengthUnchecked(length); - } - return a.asReturnedValue(); + return engine->newArrayObject(values, length)->asReturnedValue(); } ReturnedValue Runtime::objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags) { Scope scope(engine); - QV4::InternalClass *klass = engine->currentContext()->compilationUnit->runtimeClasses[classId]; - ScopedObject o(scope, engine->newObject(klass, engine->objectPrototype.asObject())); + QV4::InternalClass *klass = engine->current->compilationUnit->runtimeClasses[classId]; + ScopedObject o(scope, engine->newObject(klass, engine->objectPrototype())); { bool needSparseArray = arrayGetterSetterCountAndFlags >> 30; @@ -1211,7 +1247,7 @@ ReturnedValue Runtime::objectLiteral(ExecutionEngine *engine, const QV4::Value * } for (uint i = 0; i < klass->size; ++i) - o->memberData()->data[i] = *args++; + *o->propertyData(i) = *args++; if (arrayValueCount > 0) { ScopedValue entry(scope); @@ -1242,10 +1278,10 @@ ReturnedValue Runtime::objectLiteral(ExecutionEngine *engine, const QV4::Value * QV4::ReturnedValue Runtime::setupArgumentsObject(ExecutionEngine *engine) { - Q_ASSERT(engine->currentContext()->type >= Heap::ExecutionContext::Type_CallContext); - Scope scope(engine); - Scoped<CallContext> c(scope, static_cast<Heap::CallContext *>(engine->currentContext())); - return (engine->memoryManager->alloc<ArgumentsObject>(c))->asReturnedValue(); + Q_ASSERT(engine->current->type == Heap::ExecutionContext::Type_CallContext); + QV4::CallContext *c = static_cast<QV4::CallContext *>(engine->currentContext); + QV4::InternalClass *ic = c->d()->strictMode ? engine->strictArgumentsObjectClass : engine->argumentsObjectClass; + return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), c)->asReturnedValue(); } #endif // V4_BOOTSTRAP @@ -1329,32 +1365,14 @@ unsigned Runtime::doubleToUInt(const double &d) #ifndef V4_BOOTSTRAP -ReturnedValue Runtime::regexpLiteral(ExecutionEngine *engine, int id) -{ - return engine->currentContext()->compilationUnit->runtimeRegularExpressions[id].asReturnedValue(); -} - -ReturnedValue Runtime::getQmlIdArray(NoThrowEngine *engine) -{ - Q_ASSERT(engine->qmlContextObject()); - Scope scope(engine); - Scoped<QmlContextWrapper> wrapper(scope, engine->qmlContextObject()); - return wrapper->idObjectsArray(); -} - -ReturnedValue Runtime::getQmlContextObject(NoThrowEngine *engine) +ReturnedValue Runtime::getQmlContext(NoThrowEngine *engine) { - QQmlContextData *context = QmlContextWrapper::callingContext(engine); - if (!context) - return Encode::undefined(); - return QObjectWrapper::wrap(engine, context->contextObject); + return engine->qmlContext()->asReturnedValue(); } -ReturnedValue Runtime::getQmlScopeObject(NoThrowEngine *engine) +ReturnedValue Runtime::regexpLiteral(ExecutionEngine *engine, int id) { - Scope scope(engine); - QV4::Scoped<QmlContextWrapper> c(scope, engine->qmlContextObject()); - return QObjectWrapper::wrap(engine, c->getScopeObject()); + return engine->current->compilationUnit->runtimeRegularExpressions[id].asReturnedValue(); } ReturnedValue Runtime::getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) @@ -1365,21 +1383,29 @@ ReturnedValue Runtime::getQmlQObjectProperty(ExecutionEngine *engine, const Valu engine->throwTypeError(QStringLiteral("Cannot read property of null")); return Encode::undefined(); } - ScopedContext ctx(scope, engine->currentContext()); - return QV4::QObjectWrapper::getProperty(wrapper->object(), ctx, propertyIndex, captureRequired); + return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->object(), propertyIndex, captureRequired); } QV4::ReturnedValue Runtime::getQmlAttachedProperty(ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex) { - Scope scope(engine); - QV4::Scoped<QmlContextWrapper> c(scope, engine->qmlContextObject()); - QObject *scopeObject = c->getScopeObject(); + QObject *scopeObject = engine->qmlScopeObject(); QObject *attachedObject = qmlAttachedPropertiesObjectById(attachedPropertiesId, scopeObject); QJSEngine *jsEngine = engine->jsEngine(); QQmlData::ensurePropertyCache(jsEngine, attachedObject); - ScopedContext ctx(scope, engine->currentContext()); - return QV4::QObjectWrapper::getProperty(attachedObject, ctx, propertyIndex, /*captureRequired*/true); + return QV4::QObjectWrapper::getProperty(engine, attachedObject, propertyIndex, /*captureRequired*/true); +} + +ReturnedValue Runtime::getQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex) +{ + const QmlContext &c = static_cast<const QmlContext &>(context); + return QV4::QObjectWrapper::getProperty(engine, c.d()->qml->scopeObject, propertyIndex, false); +} + +ReturnedValue Runtime::getQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex) +{ + const QmlContext &c = static_cast<const QmlContext &>(context); + return QV4::QObjectWrapper::getProperty(engine, c.d()->qml->context->contextObject, propertyIndex, false); } ReturnedValue Runtime::getQmlSingletonQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) @@ -1390,8 +1416,34 @@ ReturnedValue Runtime::getQmlSingletonQObjectProperty(ExecutionEngine *engine, c scope.engine->throwTypeError(QStringLiteral("Cannot read property of null")); return Encode::undefined(); } - ScopedContext ctx(scope, engine->currentContext()); - return QV4::QObjectWrapper::getProperty(wrapper->singletonObject(), ctx, propertyIndex, captureRequired); + return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->singletonObject(), propertyIndex, captureRequired); +} + +ReturnedValue Runtime::getQmlIdObject(ExecutionEngine *engine, const Value &c, uint index) +{ + Scope scope(engine); + const QmlContext &qmlContext = static_cast<const QmlContext &>(c); + QQmlContextData *context = qmlContext.d()->qml->context; + if (!context || index >= (uint)context->idValueCount) + return Encode::undefined(); + + QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : 0; + if (ep && ep->propertyCapture) + ep->propertyCapture->captureProperty(&context->idValues[index].bindings); + + return QObjectWrapper::wrap(engine, context->idValues[index].data()); +} + +void Runtime::setQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) +{ + const QmlContext &c = static_cast<const QmlContext &>(context); + return QV4::QObjectWrapper::setProperty(engine, c.d()->qml->scopeObject, propertyIndex, value); +} + +void Runtime::setQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) +{ + const QmlContext &c = static_cast<const QmlContext &>(context); + return QV4::QObjectWrapper::setProperty(engine, c.d()->qml->context->contextObject, propertyIndex, value); } void Runtime::setQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value) @@ -1402,13 +1454,12 @@ void Runtime::setQmlQObjectProperty(ExecutionEngine *engine, const Value &object engine->throwTypeError(QStringLiteral("Cannot write property of null")); return; } - ScopedContext ctx(scope, engine->currentContext()); - wrapper->setProperty(ctx, propertyIndex, value); + wrapper->setProperty(engine, propertyIndex, value); } ReturnedValue Runtime::getQmlImportedScripts(NoThrowEngine *engine) { - QQmlContextData *context = QmlContextWrapper::callingContext(engine); + QQmlContextData *context = engine->callingQmlContext(); if (!context) return Encode::undefined(); return context->importedScripts.value(); @@ -1417,18 +1468,17 @@ ReturnedValue Runtime::getQmlImportedScripts(NoThrowEngine *engine) QV4::ReturnedValue Runtime::getQmlSingleton(QV4::NoThrowEngine *engine, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[nameIndex]); - Scoped<QmlContextWrapper> wrapper(scope, engine->qmlContextObject()); - return wrapper->qmlSingletonWrapper(engine, name); + ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + return engine->qmlSingletonWrapper(name); } void Runtime::convertThisToObject(ExecutionEngine *engine) { - Value *t = &engine->currentContext()->callData->thisObject; + Value *t = &engine->current->callData->thisObject; if (t->isObject()) return; if (t->isNullOrUndefined()) { - *t = engine->globalObject()->asReturnedValue(); + *t = engine->globalObject->asReturnedValue(); } else { *t = t->toObject(engine)->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index f2f90bbc15..0d81edca1e 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -33,9 +33,21 @@ #ifndef QMLJS_RUNTIME_H #define QMLJS_RUNTIME_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" -#include "qv4value_inl_p.h" +#include "qv4value_p.h" #include "qv4context_p.h" +#include "qv4engine_p.h" #include "qv4math_p.h" #include <QtCore/qnumeric.h> @@ -90,6 +102,8 @@ struct Q_QML_PRIVATE_EXPORT Runtime { // call static ReturnedValue callGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData); static ReturnedValue callActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData); + static ReturnedValue callQmlScopeObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData); + static ReturnedValue callQmlContextObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData); static ReturnedValue callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData); static ReturnedValue callPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData); static ReturnedValue callElement(ExecutionEngine *engine, const Value &index, CallData *callData); @@ -113,6 +127,8 @@ struct Q_QML_PRIVATE_EXPORT Runtime { // typeof static ReturnedValue typeofValue(ExecutionEngine *engine, const Value &val); static ReturnedValue typeofName(ExecutionEngine *engine, int nameIndex); + static ReturnedValue typeofScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex); + static ReturnedValue typeofContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex); static ReturnedValue typeofMember(ExecutionEngine *engine, const Value &base, int nameIndex); static ReturnedValue typeofElement(ExecutionEngine *engine, const Value &base, const Value &index); @@ -206,19 +222,23 @@ struct Q_QML_PRIVATE_EXPORT Runtime { static unsigned doubleToUInt(const double &d); // qml - static ReturnedValue getQmlIdArray(NoThrowEngine *ctx); - static ReturnedValue getQmlImportedScripts(NoThrowEngine *ctx); - static ReturnedValue getQmlContextObject(NoThrowEngine *ctx); - static ReturnedValue getQmlScopeObject(NoThrowEngine *ctx); - static ReturnedValue getQmlSingleton(NoThrowEngine *ctx, int nameIndex); + static ReturnedValue getQmlContext(NoThrowEngine *engine); + static ReturnedValue getQmlImportedScripts(NoThrowEngine *engine); + static ReturnedValue getQmlSingleton(NoThrowEngine *engine, int nameIndex); static ReturnedValue getQmlAttachedProperty(ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex); + static ReturnedValue getQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex); + static ReturnedValue getQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex); static ReturnedValue getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired); static ReturnedValue getQmlSingletonQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired); + static ReturnedValue getQmlIdObject(ExecutionEngine *engine, const Value &context, uint index); + + static void setQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value); + static void setQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value); static void setQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value); }; struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { - static ReturnedValue objectDefaultValue(Object *object, int typeHint); + static ReturnedValue objectDefaultValue(const Object *object, int typeHint); static ReturnedValue toPrimitive(const Value &value, int typeHint); static double stringToNumber(const QString &s); @@ -243,7 +263,7 @@ struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { #ifndef V4_BOOTSTRAP inline ReturnedValue RuntimeHelpers::toPrimitive(const Value &value, int typeHint) { - Object *o = value.asObject(); + const Object *o = value.as<Object>(); if (!o) return value.asReturnedValue(); return RuntimeHelpers::objectDefaultValue(o, typeHint); @@ -262,7 +282,7 @@ inline ReturnedValue Runtime::uPlus(const Value &value) if (value.isNumber()) return value.asReturnedValue(); if (value.integerCompatible()) - return Encode(value.int_32); + return Encode(value.int_32()); double n = value.toNumberImpl(); return Encode(n); @@ -369,6 +389,15 @@ inline ReturnedValue Runtime::div(const Value &left, const Value &right) { TRACE2(left, right); + if (Value::integerCompatible(left, right)) { + int lval = left.integerValue(); + int rval = right.integerValue(); + if (rval != 0 && (lval % rval == 0)) + return Encode(int(lval / rval)); + else + return Encode(double(lval) / rval); + } + double lval = left.toNumber(); double rval = right.toNumber(); return Primitive::fromDouble(lval / rval).asReturnedValue(); diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index 908248f0f0..d7fd44e1d6 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -33,6 +33,17 @@ #ifndef QV4SCOPEDVALUE_P_H #define QV4SCOPEDVALUE_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4engine_p.h" #include "qv4value_p.h" #include "qv4persistent_p.h" @@ -54,18 +65,12 @@ struct ScopedValue; struct Scope { inline Scope(ExecutionContext *ctx) : engine(ctx->d()->engine) -#ifndef QT_NO_DEBUG - , size(0) -#endif { mark = engine->jsStackTop; } explicit Scope(ExecutionEngine *e) : engine(e) -#ifndef QT_NO_DEBUG - , size(0) -#endif { mark = engine->jsStackTop; } @@ -73,6 +78,7 @@ struct Scope { ~Scope() { #ifndef QT_NO_DEBUG Q_ASSERT(engine->jsStackTop >= mark); + Q_ASSERT(engine->currentContext < mark); memset(mark, 0, (engine->jsStackTop - mark)*sizeof(Value)); #endif #ifdef V4_USE_VALGRIND @@ -82,13 +88,7 @@ struct Scope { } Value *alloc(int nValues) { -#ifndef QT_NO_DEBUG - size += nValues; -#endif - Value *ptr = engine->jsStackTop; - engine->jsStackTop = ptr + nValues; - memset(ptr, 0, nValues*sizeof(Value)); - return ptr; + return engine->jsAlloca(nValues); } bool hasException() const { @@ -97,9 +97,6 @@ struct Scope { ExecutionEngine *engine; Value *mark; -#ifndef QT_NO_DEBUG - mutable int size; -#endif private: Q_DISABLE_COPY(Scope) @@ -110,49 +107,34 @@ struct ScopedValue ScopedValue(const Scope &scope) { ptr = scope.engine->jsStackTop++; - ptr->val = 0; -#ifndef QT_NO_DEBUG - ++scope.size; -#endif + ptr->setRawValue(0); } ScopedValue(const Scope &scope, const Value &v) { ptr = scope.engine->jsStackTop++; *ptr = v; -#ifndef QT_NO_DEBUG - ++scope.size; -#endif } ScopedValue(const Scope &scope, Heap::Base *o) { ptr = scope.engine->jsStackTop++; - ptr->m = o; -#if QT_POINTER_SIZE == 4 - ptr->tag = QV4::Value::Managed_Type; -#endif -#ifndef QT_NO_DEBUG - ++scope.size; + ptr->setM(o); +#ifndef QV4_USE_64_BIT_VALUE_ENCODING + ptr->setTag(QV4::Value::Managed_Type); #endif } ScopedValue(const Scope &scope, Managed *m) { ptr = scope.engine->jsStackTop++; - ptr->val = m->asReturnedValue(); -#ifndef QT_NO_DEBUG - ++scope.size; -#endif + ptr->setRawValue(m->asReturnedValue()); } ScopedValue(const Scope &scope, const ReturnedValue &v) { ptr = scope.engine->jsStackTop++; - ptr->val = v; -#ifndef QT_NO_DEBUG - ++scope.size; -#endif + ptr->setRawValue(v); } ScopedValue &operator=(const Value &v) { @@ -161,9 +143,9 @@ struct ScopedValue } ScopedValue &operator=(Heap::Base *o) { - ptr->m = o; -#if QT_POINTER_SIZE == 4 - ptr->tag = QV4::Value::Managed_Type; + ptr->setM(o); +#ifndef QV4_USE_64_BIT_VALUE_ENCODING + ptr->setTag(QV4::Value::Managed_Type); #endif return *this; } @@ -174,7 +156,7 @@ struct ScopedValue } ScopedValue &operator=(const ReturnedValue &v) { - ptr->val = v; + ptr->setRawValue(v); return *this; } @@ -200,111 +182,78 @@ struct ScopedValue template<typename T> struct Scoped { - enum _Convert { Convert }; + enum ConvertType { Convert }; - inline void setPointer(Managed *p) { - ptr->m = p ? p->m : 0; -#if QT_POINTER_SIZE == 4 - ptr->tag = QV4::Value::Managed_Type; + inline void setPointer(const Managed *p) { + ptr->setM(p ? p->m() : 0); +#ifndef QV4_USE_64_BIT_VALUE_ENCODING + ptr->setTag(QV4::Value::Managed_Type); #endif } Scoped(const Scope &scope) { ptr = scope.engine->jsStackTop++; - ptr->m = 0; -#if QT_POINTER_SIZE == 4 - ptr->tag = QV4::Value::Managed_Type; -#endif -#ifndef QT_NO_DEBUG - ++scope.size; + ptr->setM(0); +#ifndef QV4_USE_64_BIT_VALUE_ENCODING + ptr->setTag(QV4::Value::Managed_Type); #endif } - // ### GC FIX casting below to be safe Scoped(const Scope &scope, const Value &v) { ptr = scope.engine->jsStackTop++; - setPointer(value_cast<T>(v)); -#ifndef QT_NO_DEBUG - ++scope.size; -#endif + setPointer(v.as<T>()); } Scoped(const Scope &scope, Heap::Base *o) { Value v; v = o; ptr = scope.engine->jsStackTop++; - setPointer(value_cast<T>(v)); -#ifndef QT_NO_DEBUG - ++scope.size; -#endif + setPointer(v.as<T>()); } Scoped(const Scope &scope, const ScopedValue &v) { ptr = scope.engine->jsStackTop++; - setPointer(value_cast<T>(*v.ptr)); -#ifndef QT_NO_DEBUG - ++scope.size; -#endif + setPointer(v.ptr->as<T>()); } - Scoped(const Scope &scope, const Value &v, _Convert) + Scoped(const Scope &scope, const Value &v, ConvertType) { ptr = scope.engine->jsStackTop++; - ptr->val = value_convert<T>(scope.engine, v); -#ifndef QT_NO_DEBUG - ++scope.size; -#endif + ptr->setRawValue(value_convert<T>(scope.engine, v)); } Scoped(const Scope &scope, const Value *v) { ptr = scope.engine->jsStackTop++; - setPointer(v ? value_cast<T>(*v) : 0); -#ifndef QT_NO_DEBUG - ++scope.size; -#endif + setPointer(v ? v->as<T>() : 0); } Scoped(const Scope &scope, T *t) { ptr = scope.engine->jsStackTop++; setPointer(t); -#ifndef QT_NO_DEBUG - ++scope.size; -#endif } Scoped(const Scope &scope, typename T::Data *t) { ptr = scope.engine->jsStackTop++; *ptr = t; -#ifndef QT_NO_DEBUG - ++scope.size; -#endif } Scoped(const Scope &scope, const ReturnedValue &v) { ptr = scope.engine->jsStackTop++; - setPointer(value_cast<T>(QV4::Value::fromReturnedValue(v))); -#ifndef QT_NO_DEBUG - ++scope.size; -#endif + setPointer(QV4::Value::fromReturnedValue(v).as<T>()); } - Scoped(const Scope &scope, const ReturnedValue &v, _Convert) + Scoped(const Scope &scope, const ReturnedValue &v, ConvertType) { ptr = scope.engine->jsStackTop++; - ptr->val = value_convert<T>(scope.engine, QV4::Value::fromReturnedValue(v)); -#ifndef QT_NO_DEBUG - ++scope.size; -#endif + ptr->setRawValue(value_convert<T>(scope.engine, QV4::Value::fromReturnedValue(v))); } Scoped<T> &operator=(Heap::Base *o) { - Value v; - v = o; - setPointer(value_cast<T>(v)); + setPointer(Value::fromHeapObject(o).as<T>()); return *this; } Scoped<T> &operator=(typename T::Data *t) { @@ -312,16 +261,16 @@ struct Scoped return *this; } Scoped<T> &operator=(const Value &v) { - setPointer(value_cast<T>(v)); + setPointer(v.as<T>()); return *this; } Scoped<T> &operator=(Value *v) { - setPointer(v ? value_cast<T>(*v) : 0); + setPointer(v ? v->as<T>() : 0); return *this; } Scoped<T> &operator=(const ReturnedValue &v) { - setPointer(value_cast<T>(QV4::Value::fromReturnedValue(v))); + setPointer(QV4::Value::fromReturnedValue(v).as<T>()); return *this; } @@ -347,21 +296,21 @@ struct Scoped } bool operator!() const { - return !ptr->m; + return !ptr->m(); } operator void *() const { - return ptr->m; + return ptr->m(); } T *getPointer() { return ptr->cast<T>(); } - typename T::Data **getRef() { - return reinterpret_cast<typename T::Data **>(&ptr->m); + Value *getRef() { + return ptr; } ReturnedValue asReturnedValue() const { - return ptr->m ? ptr->val : Encode::undefined(); + return ptr->m() ? ptr->rawValue() : Encode::undefined(); } Value *ptr; @@ -390,41 +339,14 @@ struct ScopedCallData { inline Value &Value::operator =(const ScopedValue &v) { - val = v.ptr->val; + _val = v.ptr->val(); return *this; } template<typename T> inline Value &Value::operator=(const Scoped<T> &t) { - val = t.ptr->val; - return *this; -} - -template<typename T> -inline TypedValue<T> &TypedValue<T>::operator =(T *t) -{ - m = t ? t->m : 0; -#if QT_POINTER_SIZE == 4 - tag = Managed_Type; -#endif - return *this; -} - -template<typename T> -inline TypedValue<T> &TypedValue<T>::operator =(const Scoped<T> &v) -{ - m = v.ptr->m; -#if QT_POINTER_SIZE == 4 - tag = Managed_Type; -#endif - return *this; -} - -template<typename T> -inline TypedValue<T> &TypedValue<T>::operator=(const TypedValue<T> &t) -{ - val = t.val; + _val = t.ptr->val(); return *this; } @@ -447,29 +369,18 @@ struct ScopedProperty struct ExecutionContextSaver { ExecutionEngine *engine; - Value *savedContext; + ExecutionContext *savedContext; - ExecutionContextSaver(Scope &scope, ExecutionContext *context) - : engine(context->d()->engine) - , savedContext(scope.alloc(1)) + ExecutionContextSaver(Scope &scope) + : engine(scope.engine) { - savedContext->m = context->d(); -#if QT_POINTER_SIZE == 4 - savedContext->tag = QV4::Value::Managed_Type; -#endif - } - ExecutionContextSaver(Scope &scope, Heap::ExecutionContext *context) - : engine(context->engine) - , savedContext(scope.alloc(1)) - { - savedContext->m = context; -#if QT_POINTER_SIZE == 4 - savedContext->tag = QV4::Value::Managed_Type; -#endif + savedContext = engine->currentContext; } ~ExecutionContextSaver() { - engine->current = static_cast<Heap::ExecutionContext *>(savedContext->heapObject()); + Q_ASSERT(engine->jsStackTop > engine->currentContext); + engine->currentContext = savedContext; + engine->current = savedContext->d(); } }; diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 4fde0e2445..7f2f22780e 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -32,11 +32,12 @@ ****************************************************************************/ #include "qv4script_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4functionobject_p.h" #include "qv4function_p.h" #include "qv4context_p.h" #include "qv4debugging_p.h" +#include "qv4profiling_p.h" #include "qv4scopedvalue_p.h" #include <private/qqmljsengine_p.h> @@ -44,6 +45,7 @@ #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> #include <private/qqmlengine_p.h> +#include <private/qv4profiling_p.h> #include <qv4jsir_p.h> #include <qv4codegen_p.h> #include <private/qqmlcontextwrapper_p.h> @@ -57,11 +59,15 @@ namespace QV4 { namespace Heap { struct CompilationUnitHolder : Object { - inline CompilationUnitHolder(ExecutionEngine *engine, CompiledData::CompilationUnit *unit); + inline CompilationUnitHolder(CompiledData::CompilationUnit *unit); QQmlRefPointer<CompiledData::CompilationUnit> unit; }; +struct QmlBindingWrapper : FunctionObject { + QmlBindingWrapper(QV4::QmlContext *scope, Function *f); +}; + } struct CompilationUnitHolder : public Object @@ -71,12 +77,17 @@ struct CompilationUnitHolder : public Object }; inline -Heap::CompilationUnitHolder::CompilationUnitHolder(ExecutionEngine *engine, CompiledData::CompilationUnit *unit) - : Heap::Object(engine) - , unit(unit) +Heap::CompilationUnitHolder::CompilationUnitHolder(CompiledData::CompilationUnit *unit) + : unit(unit) { } +struct QmlBindingWrapper : FunctionObject { + V4_OBJECT2(QmlBindingWrapper, FunctionObject) + + static ReturnedValue call(const Managed *that, CallData *callData); +}; + } QT_END_NAMESPACE @@ -86,117 +97,52 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(QmlBindingWrapper); DEFINE_OBJECT_VTABLE(CompilationUnitHolder); -Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::ExecutionContext *scope, Function *f, QV4::Object *qml) - : Heap::FunctionObject(scope, scope->d()->engine->id_eval, /*createProto = */ false) - , qml(qml->d()) +Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::QmlContext *scope, Function *f) + : Heap::FunctionObject(scope, scope->d()->engine->id_eval(), /*createProto = */ false) { Q_ASSERT(scope->inUse()); function = f; if (function) function->compilationUnit->addref(); - - Scope s(scope); - Scoped<QV4::QmlBindingWrapper> o(s, this); - - o->defineReadonlyProperty(scope->d()->engine->id_length, Primitive::fromInt32(1)); - - ScopedContext ctx(s, s.engine->currentContext()); - o->d()->qmlContext = ctx->newQmlContext(o, qml); - s.engine->popContext(); } -Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::ExecutionContext *scope, QV4::Object *qml) - : Heap::FunctionObject(scope, scope->d()->engine->id_eval, /*createProto = */ false) - , qml(qml->d()) +ReturnedValue QmlBindingWrapper::call(const Managed *that, CallData *callData) { - Q_ASSERT(scope->inUse()); - - Scope s(scope); - Scoped<QV4::QmlBindingWrapper> o(s, this); - - o->defineReadonlyProperty(scope->d()->engine->id_length, Primitive::fromInt32(1)); - - ScopedContext ctx(s, s.engine->currentContext()); - o->d()->qmlContext = ctx->newQmlContext(o, qml); - s.engine->popContext(); -} + const QmlBindingWrapper *This = static_cast<const QmlBindingWrapper *>(that); + ExecutionEngine *v4 = static_cast<const Object *>(that)->engine(); + if (v4->hasException) + return Encode::undefined(); + CHECK_STACK_LIMITS(v4); -ReturnedValue QmlBindingWrapper::call(Managed *that, CallData *) -{ - ExecutionEngine *engine = static_cast<Object *>(that)->engine(); - CHECK_STACK_LIMITS(engine); + Scope scope(v4); + ExecutionContextSaver ctxSaver(scope); - Scope scope(engine); - QmlBindingWrapper *This = static_cast<QmlBindingWrapper *>(that); - if (!This->function()) + QV4::Function *f = This->function(); + if (!f) return QV4::Encode::undefined(); - Scoped<CallContext> ctx(scope, This->d()->qmlContext); - std::fill(ctx->d()->locals, ctx->d()->locals + ctx->d()->function->varCount(), Primitive::undefinedValue()); - engine->pushContext(ctx); - ScopedValue result(scope, This->function()->code(engine, This->function()->codeData)); - engine->popContext(); + Scoped<CallContext> ctx(scope, v4->currentContext->newCallContext(This, callData)); + v4->pushContext(ctx); - return result->asReturnedValue(); -} - -void QmlBindingWrapper::markObjects(Heap::Base *m, ExecutionEngine *e) -{ - QmlBindingWrapper::Data *wrapper = static_cast<QmlBindingWrapper::Data *>(m); - if (wrapper->qml) - wrapper->qml->mark(e); - FunctionObject::markObjects(m, e); - if (wrapper->qmlContext) - wrapper->qmlContext->mark(e); -} - -static ReturnedValue signalParameterGetter(QV4::CallContext *ctx, uint parameterIndex) -{ - QV4::Scope scope(ctx); - QV4::Scoped<CallContext> signalEmittingContext(scope, static_cast<Heap::CallContext *>(ctx->d()->parent)); - Q_ASSERT(signalEmittingContext && signalEmittingContext->d()->type >= QV4::Heap::ExecutionContext::Type_SimpleCallContext); - return signalEmittingContext->argument(parameterIndex); -} + ScopedValue result(scope, Q_V4_PROFILE(v4, f)); -Heap::FunctionObject *QmlBindingWrapper::createQmlCallableForFunction(QQmlContextData *qmlContext, QObject *scopeObject, Function *runtimeFunction, const QList<QByteArray> &signalParameters, QString *error) -{ - ExecutionEngine *engine = QQmlEnginePrivate::getV4Engine(qmlContext->engine); - QV4::Scope valueScope(engine); - QV4::ScopedObject qmlScopeObject(valueScope, QV4::QmlContextWrapper::qmlScope(engine, qmlContext, scopeObject)); - ScopedContext global(valueScope, valueScope.engine->rootContext()); - QV4::Scoped<QV4::QmlBindingWrapper> wrapper(valueScope, engine->memoryManager->alloc<QV4::QmlBindingWrapper>(global, qmlScopeObject)); - QV4::Scoped<CallContext> wrapperContext(valueScope, wrapper->context()); - - if (!signalParameters.isEmpty()) { - if (error) - QQmlPropertyCache::signalParameterStringForJS(engine, signalParameters, error); - QV4::ScopedProperty p(valueScope); - QV4::ScopedString s(valueScope); - int index = 0; - foreach (const QByteArray ¶m, signalParameters) { - QV4::ScopedFunctionObject g(valueScope, engine->memoryManager->alloc<QV4::IndexedBuiltinFunction>(wrapperContext, index++, signalParameterGetter)); - p->setGetter(g); - p->setSetter(0); - s = engine->newString(QString::fromUtf8(param)); - qmlScopeObject->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable); - } - } - - QV4::ScopedFunctionObject function(valueScope, QV4::FunctionObject::createScriptFunction(wrapperContext, runtimeFunction)); - return function->d(); + return result->asReturnedValue(); } -Script::Script(ExecutionEngine *v4, Object *qml, CompiledData::CompilationUnit *compilationUnit) +Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit) : line(0), column(0), scope(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false) - , qml(v4, qml), vmFunction(0), parseAsBinding(true) + , vmFunction(0), parseAsBinding(true) { + if (qml) + qmlContext.set(v4, *qml); + parsed = true; vmFunction = compilationUnit ? compilationUnit->linkToEngine(v4) : 0; if (vmFunction) { Scope valueScope(v4); - ScopedObject holder(valueScope, v4->memoryManager->alloc<CompilationUnitHolder>(v4, compilationUnit)); + ScopedObject holder(valueScope, v4->memoryManager->allocObject<CompilationUnitHolder>(compilationUnit)); compilationUnitHolder.set(v4, holder); } } @@ -214,7 +160,7 @@ void Script::parse() parsed = true; - ExecutionEngine *v4 = scope->engine; + ExecutionEngine *v4 = scope->engine(); Scope valueScope(v4); MemoryManager::GCBlocker gcBlocker(v4->memoryManager); @@ -267,7 +213,7 @@ void Script::parse() isel->setUseFastLookups(false); QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = isel->compile(); vmFunction = compilationUnit->linkToEngine(v4); - ScopedObject holder(valueScope, v4->memoryManager->alloc<CompilationUnitHolder>(v4, compilationUnit)); + ScopedObject holder(valueScope, v4->memoryManager->allocObject<CompilationUnitHolder>(compilationUnit)); compilationUnitHolder.set(v4, holder); } @@ -285,23 +231,22 @@ ReturnedValue Script::run() if (!vmFunction) return Encode::undefined(); - QV4::ExecutionEngine *engine = scope->engine; + QV4::ExecutionEngine *engine = scope->engine(); QV4::Scope valueScope(engine); - if (qml.isUndefined()) { + if (qmlContext.isUndefined()) { TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction); - ExecutionContextSaver ctxSaver(valueScope, scope); + ExecutionContextSaver ctxSaver(valueScope); ContextStateSaver stateSaver(valueScope, scope); - scope->strictMode = vmFunction->isStrict(); - scope->lookups = vmFunction->compilationUnit->runtimeLookups; - scope->compilationUnit = vmFunction->compilationUnit; + scope->d()->strictMode = vmFunction->isStrict(); + scope->d()->lookups = vmFunction->compilationUnit->runtimeLookups; + scope->d()->compilationUnit = vmFunction->compilationUnit; - return vmFunction->code(engine, vmFunction->codeData); + return Q_V4_PROFILE(engine, vmFunction); } else { - ScopedObject qmlObj(valueScope, qml.value()); - ScopedContext ctx(valueScope, scope); - ScopedFunctionObject f(valueScope, engine->memoryManager->alloc<QmlBindingWrapper>(ctx, vmFunction, qmlObj)); + Scoped<QmlContext> qml(valueScope, qmlContext.value()); + ScopedFunctionObject f(valueScope, engine->memoryManager->allocObject<QmlBindingWrapper>(qml, vmFunction)); ScopedCallData callData(valueScope); callData->thisObject = Primitive::undefinedValue(); return f->call(callData); @@ -376,18 +321,17 @@ ReturnedValue Script::qmlBinding() { if (!parsed) parse(); - ExecutionEngine *v4 = scope->engine; + ExecutionEngine *v4 = scope->engine(); Scope valueScope(v4); - ScopedObject qmlObj(valueScope, qml.value()); - ScopedContext ctx(valueScope, scope); - ScopedObject v(valueScope, v4->memoryManager->alloc<QmlBindingWrapper>(ctx, vmFunction, qmlObj)); + Scoped<QmlContext> qml(valueScope, qmlContext.value()); + ScopedObject v(valueScope, v4->memoryManager->allocObject<QmlBindingWrapper>(qml, vmFunction)); return v.asReturnedValue(); } -QV4::ReturnedValue Script::evaluate(ExecutionEngine *engine, const QString &script, Object *scopeObject) +QV4::ReturnedValue Script::evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext) { QV4::Scope scope(engine); - QV4::Script qmlScript(engine, scopeObject, script, QString()); + QV4::Script qmlScript(engine, qmlContext, script, QString()); qmlScript.parse(); QV4::ScopedValue result(scope); diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index 05a9e45f45..4fecf62082 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -33,9 +33,21 @@ #ifndef QV4SCRIPT_H #define QV4SCRIPT_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" #include "qv4engine_p.h" #include "qv4functionobject_p.h" +#include "qv4context_p.h" #include <QQmlError> @@ -63,7 +75,7 @@ struct ContextStateSaver { , compilationUnit(context->d()->compilationUnit) , lineNumber(context->d()->lineNumber) { - savedContext->m = context->d(); + savedContext->setM(context->d()); } ContextStateSaver(Scope &scope, Heap::ExecutionContext *context) : savedContext(scope.alloc(1)) @@ -72,12 +84,12 @@ struct ContextStateSaver { , compilationUnit(context->compilationUnit) , lineNumber(context->lineNumber) { - savedContext->m = context; + savedContext->setM(context); } ~ContextStateSaver() { - Heap::ExecutionContext *ctx = static_cast<Heap::ExecutionContext *>(savedContext->m); + Heap::ExecutionContext *ctx = static_cast<Heap::ExecutionContext *>(savedContext->m()); ctx->strictMode = strictMode; ctx->lookups = lookups; ctx->compilationUnit = compilationUnit; @@ -85,52 +97,29 @@ struct ContextStateSaver { } }; -namespace Heap { -struct QmlBindingWrapper : Heap::FunctionObject { - QmlBindingWrapper(QV4::ExecutionContext *scope, Function *f, QV4::Object *qml); - // Constructor for QML functions and signal handlers, resulting binding wrapper is not callable! - QmlBindingWrapper(QV4::ExecutionContext *scope, QV4::Object *qml); - Object *qml; - CallContext *qmlContext; -}; - -} - -struct Q_QML_EXPORT QmlBindingWrapper : FunctionObject { - V4_OBJECT2(QmlBindingWrapper, FunctionObject) - - static ReturnedValue call(Managed *that, CallData *); - static void markObjects(Heap::Base *m, ExecutionEngine *e); - - Heap::CallContext *context() const { return d()->qmlContext; } - - static Heap::FunctionObject *createQmlCallableForFunction(QQmlContextData *qmlContext, QObject *scopeObject, QV4::Function *runtimeFunction, - const QList<QByteArray> &signalParameters = QList<QByteArray>(), QString *error = 0); - -private: -}; - struct Q_QML_EXPORT Script { Script(ExecutionContext *scope, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) - , scope(scope->d()), strictMode(false), inheritContext(false), parsed(false) + , scope(scope), strictMode(false), inheritContext(false), parsed(false) , vmFunction(0), parseAsBinding(false) {} - Script(ExecutionEngine *engine, Object *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) + Script(ExecutionEngine *engine, QmlContext *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) , scope(engine->rootContext()), strictMode(false), inheritContext(true), parsed(false) - , qml(engine, qml), vmFunction(0), parseAsBinding(true) {} - Script(ExecutionEngine *engine, Object *qml, CompiledData::CompilationUnit *compilationUnit); + , vmFunction(0), parseAsBinding(true) { + if (qml) + qmlContext.set(engine, *qml); + } + Script(ExecutionEngine *engine, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit); ~Script(); QString sourceFile; int line; int column; QString sourceCode; - // ### GC - Heap::ExecutionContext *scope; + ExecutionContext *scope; bool strictMode; bool inheritContext; bool parsed; - QV4::PersistentValue qml; + QV4::PersistentValue qmlContext; QV4::PersistentValue compilationUnitHolder; Function *vmFunction; bool parseAsBinding; @@ -144,7 +133,7 @@ struct Q_QML_EXPORT Script { static QQmlRefPointer<CompiledData::CompilationUnit> precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors = 0, QQmlJS::Directives *directivesCollector = 0); - static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, Object *scopeObject); + static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext); }; } diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index f1f546bece..36ee848d00 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -175,7 +175,7 @@ template <> QUrl convertValueToElement(const Value &value) template <> QModelIndex convertValueToElement(const Value &value) { - const QQmlValueTypeWrapper *v = value_cast<QQmlValueTypeWrapper>(value); + const QQmlValueTypeWrapper *v = value.as<QQmlValueTypeWrapper>(); if (v) return v->toVariant().toModelIndex(); return QModelIndex(); @@ -183,7 +183,7 @@ template <> QModelIndex convertValueToElement(const Value &value) template <> QItemSelectionRange convertValueToElement(const Value &value) { - const QQmlValueTypeWrapper *v = value_cast<QQmlValueTypeWrapper>(value); + const QQmlValueTypeWrapper *v = value.as<QQmlValueTypeWrapper>(); if (v) return v->toVariant().value<QItemSelectionRange>(); return QItemSelectionRange(); @@ -207,8 +207,8 @@ namespace Heap { template <typename Container> struct QQmlSequence : Object { - QQmlSequence(QV4::ExecutionEngine *engine, const Container &container); - QQmlSequence(QV4::ExecutionEngine *engine, QObject *object, int propertyIndex); + QQmlSequence(const Container &container); + QQmlSequence(QObject *object, int propertyIndex); mutable Container container; QPointer<QObject> object; @@ -223,6 +223,7 @@ struct QQmlSequence : public QV4::Object { V4_OBJECT2(QQmlSequence<Container>, QV4::Object) Q_MANAGED_TYPE(QmlSequence) + V4_PROTOTYPE(sequencePrototype) V4_NEEDS_DESTROY public: @@ -231,7 +232,7 @@ public: defineAccessorProperty(QStringLiteral("length"), method_get_length, method_set_length); } - QV4::ReturnedValue containerGetIndexed(uint index, bool *hasProperty) + QV4::ReturnedValue containerGetIndexed(uint index, bool *hasProperty) const { /* Qt containers have int (rather than uint) allowable indexes. */ if (index > INT_MAX) { @@ -316,9 +317,9 @@ public: return (signedIdx < d()->container.count()) ? QV4::Attr_Data : QV4::Attr_Invalid; } - void containerAdvanceIterator(ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attrs) + void containerAdvanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) { - *name = (Heap::String *)0; + name->setM(0); *index = UINT_MAX; if (d()->isReference) { @@ -400,7 +401,7 @@ public: ScopedCallData callData(scope, 2); callData->args[0] = convertElementToValue(this->m_ctx->d()->engine, lhs); callData->args[1] = convertElementToValue(this->m_ctx->d()->engine, rhs); - callData->thisObject = this->m_ctx->d()->engine->globalObject(); + callData->thisObject = this->m_ctx->d()->engine->globalObject; QV4::ScopedValue result(scope, compare->call(callData)); return result->toNumber() < 0; } @@ -419,7 +420,7 @@ public: } QV4::Scope scope(ctx); - if (ctx->argc() == 1 && ctx->args()[0].asFunctionObject()) { + if (ctx->argc() == 1 && ctx->args()[0].as<FunctionObject>()) { CompareFunctor cf(ctx, ctx->args()[0]); std::sort(d()->container.begin(), d()->container.end(), cf); } else { @@ -526,8 +527,8 @@ public: QMetaObject::metacall(d()->object, QMetaObject::WriteProperty, d()->propertyIndex, a); } - static QV4::ReturnedValue getIndexed(QV4::Managed *that, uint index, bool *hasProperty) - { return static_cast<QQmlSequence<Container> *>(that)->containerGetIndexed(index, hasProperty); } + static QV4::ReturnedValue getIndexed(const QV4::Managed *that, uint index, bool *hasProperty) + { return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(index, hasProperty); } static void putIndexed(Managed *that, uint index, const QV4::Value &value) { static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(index, value); } static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index) @@ -536,33 +537,31 @@ public: { return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index); } static bool isEqualTo(Managed *that, Managed *other) { return static_cast<QQmlSequence<Container> *>(that)->containerIsEqualTo(other); } - static void advanceIterator(Managed *that, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attrs) + static void advanceIterator(Managed *that, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) { return static_cast<QQmlSequence<Container> *>(that)->containerAdvanceIterator(it, name, index, p, attrs); } }; template <typename Container> -Heap::QQmlSequence<Container>::QQmlSequence(QV4::ExecutionEngine *engine, const Container &container) - : Heap::Object(engine->emptyClass, engine->sequencePrototype.asObject()) - , container(container) +Heap::QQmlSequence<Container>::QQmlSequence(const Container &container) + : container(container) , propertyIndex(-1) , isReference(false) { - QV4::Scope scope(engine); + QV4::Scope scope(internalClass->engine); QV4::Scoped<QV4::QQmlSequence<Container> > o(scope, this); o->setArrayType(Heap::ArrayData::Custom); o->init(); } template <typename Container> -Heap::QQmlSequence<Container>::QQmlSequence(QV4::ExecutionEngine *engine, QObject *object, int propertyIndex) - : Heap::Object(engine->emptyClass, engine->sequencePrototype.asObject()) - , object(object) +Heap::QQmlSequence<Container>::QQmlSequence(QObject *object, int propertyIndex) + : object(object) , propertyIndex(propertyIndex) , isReference(true) { - QV4::Scope scope(engine); + QV4::Scope scope(internalClass->engine); QV4::Scoped<QV4::QQmlSequence<Container> > o(scope, this); o->setArrayType(Heap::ArrayData::Custom); o->loadReference(); @@ -605,7 +604,7 @@ void SequencePrototype::init() { FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE) defineDefaultProperty(QStringLiteral("sort"), method_sort, 1); - defineDefaultProperty(engine()->id_valueOf, method_valueOf, 0); + defineDefaultProperty(engine()->id_valueOf(), method_valueOf, 0); } #undef REGISTER_QML_SEQUENCE_METATYPE @@ -644,7 +643,7 @@ bool SequencePrototype::isSequenceType(int sequenceTypeId) #define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ if (sequenceType == qMetaTypeId<SequenceType>()) { \ - QV4::ScopedObject obj(scope, engine->memoryManager->alloc<QQml##ElementTypeName##List>(engine, object, propertyIndex)); \ + QV4::ScopedObject obj(scope, engine->memoryManager->allocObject<QQml##ElementTypeName##List>(object, propertyIndex)); \ return obj.asReturnedValue(); \ } else @@ -662,7 +661,7 @@ ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int s #define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ if (sequenceType == qMetaTypeId<SequenceType>()) { \ - QV4::ScopedObject obj(scope, engine->memoryManager->alloc<QQml##ElementTypeName##List>(engine, v.value<SequenceType >())); \ + QV4::ScopedObject obj(scope, engine->memoryManager->allocObject<QQml##ElementTypeName##List>(v.value<SequenceType >())); \ return obj.asReturnedValue(); \ } else @@ -700,11 +699,11 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, int typeHint, boo { *succeeded = true; - if (!array.asArrayObject()) { + if (!array.as<ArrayObject>()) { *succeeded = false; return QVariant(); } - QV4::Scope scope(array.asObject()->engine()); + QV4::Scope scope(array.as<Object>()->engine()); QV4::ScopedArrayObject a(scope, array); FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ *succeeded = false; return QVariant(); } @@ -717,7 +716,7 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, int typeHint, boo return qMetaTypeId<SequenceType>(); \ } else -int SequencePrototype::metaTypeForSequence(QV4::Object *object) +int SequencePrototype::metaTypeForSequence(const QV4::Object *object) { FOREACH_QML_SEQUENCE_TYPE(MAP_META_TYPE) /*else*/ { diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index 9949278a89..560c3c27ca 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -48,9 +48,10 @@ #include <QtCore/qglobal.h> #include <QtCore/qvariant.h> -#include "qv4value_inl_p.h" +#include "qv4value_p.h" #include "qv4object_p.h" #include "qv4context_p.h" +#include "qv4string_p.h" QT_BEGIN_NAMESPACE @@ -70,7 +71,7 @@ struct SequencePrototype : public QV4::Object static bool isSequenceType(int sequenceTypeId); static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded); static ReturnedValue fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded); - static int metaTypeForSequence(Object *object); + static int metaTypeForSequence(const Object *object); static QVariant toVariant(Object *object); static QVariant toVariant(const Value &array, int typeHint, bool *succeeded); }; diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp index 31d85df13e..33b40796a4 100644 --- a/src/qml/jsruntime/qv4serialize.cpp +++ b/src/qml/jsruntime/qv4serialize.cpp @@ -37,7 +37,7 @@ #include <private/qqmllistmodel_p.h> #include <private/qqmllistmodelworkeragent_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4dateobject_p.h> #include <private/qv4regexpobject_p.h> #include <private/qv4sequenceobject_p.h> @@ -168,11 +168,11 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine char *buffer = data.data() + offset; memcpy(buffer, qstr.constData(), length*sizeof(QChar)); - } else if (v.asFunctionObject()) { + } else if (v.as<FunctionObject>()) { // XXX TODO: Implement passing function objects between the main and // worker scripts push(data, valueheader(WorkerUndefined)); - } else if (QV4::ArrayObject *array = v.asArrayObject()) { + } else if (const QV4::ArrayObject *array = v.as<ArrayObject>()) { uint length = array->getLength(); if (length > 0xFFFFFF) { push(data, valueheader(WorkerUndefined)); @@ -195,11 +195,11 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine reserve(data, sizeof(quint32) + sizeof(double)); push(data, valueheader(WorkerNumber)); push(data, v.asDouble()); - } else if (QV4::DateObject *d = v.asDateObject()) { + } else if (const QV4::DateObject *d = v.as<DateObject>()) { reserve(data, sizeof(quint32) + sizeof(double)); push(data, valueheader(WorkerDate)); - push(data, d->date().asDouble()); - } else if (RegExpObject *re = v.as<RegExpObject>()) { + push(data, d->date()); + } else if (const RegExpObject *re = v.as<RegExpObject>()) { quint32 flags = re->flags(); QString pattern = re->source(); int length = pattern.length() + 1; @@ -218,7 +218,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine char *buffer = data.data() + offset; memcpy(buffer, pattern.constData(), length*sizeof(QChar)); - } else if (QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) { + } else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) { // XXX TODO: Generalize passing objects between the main thread and worker scripts so // that others can trivially plug in their elements. QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object()); @@ -231,10 +231,10 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine } // No other QObject's are allowed to be sent push(data, valueheader(WorkerUndefined)); - } else if (Object *o = v.asObject()) { + } else if (const Object *o = v.as<Object>()) { if (o->isListType()) { // valid sequence. we generate a length (sequence length + 1 for the sequence type) - uint seqLength = ScopedValue(scope, o->get(engine->id_length))->toUInt32(); + uint seqLength = ScopedValue(scope, o->get(engine->id_length()))->toUInt32(); uint length = seqLength + 1; if (length > 0xFFFFFF) { push(data, valueheader(WorkerUndefined)); @@ -265,7 +265,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine s = properties->getIndexed(ii); serialize(data, s, engine); - QV4::String *str = s->asString(); + QV4::String *str = s->as<String>(); val = o->get(str); if (scope.hasException()) scope.engine->catchException(); @@ -356,7 +356,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) QVariant var = qVariantFromValue(ref); QV4::ScopedValue v(scope, scope.engine->fromVariant(var)); QV4::ScopedString s(scope, engine->newString(QStringLiteral("__qml:hidden:ref"))); - rv->asObject()->defineReadonlyProperty(s, v); + rv->as<Object>()->defineReadonlyProperty(s, v); agent->release(); agent->setEngine(engine); diff --git a/src/qml/jsruntime/qv4serialize_p.h b/src/qml/jsruntime/qv4serialize_p.h index 06eaffe4c4..d5d48edee7 100644 --- a/src/qml/jsruntime/qv4serialize_p.h +++ b/src/qml/jsruntime/qv4serialize_p.h @@ -46,7 +46,7 @@ // #include <QtCore/qbytearray.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4sparsearray.cpp b/src/qml/jsruntime/qv4sparsearray.cpp index 01f94eeac6..bb1d3aeb69 100644 --- a/src/qml/jsruntime/qv4sparsearray.cpp +++ b/src/qml/jsruntime/qv4sparsearray.cpp @@ -246,15 +246,12 @@ void SparseArray::deleteNode(SparseArrayNode *z) x->setParent(y->parent()); if (root == y) root = x; - else if (y->parent()->left == y) { + else if (y->parent()->left == y) y->parent()->left = x; - if (x) - x->size_left += y->size_left; - } else { + else y->parent()->right = x; - if (x) - x->size_left += y->size_left; - } + if (x && x == y->right) + x->size_left += y->size_left; y->size_left = 0; } if (y->color() != SparseArrayNode::Red) { diff --git a/src/qml/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h index 861c7dd28d..136f26a25c 100644 --- a/src/qml/jsruntime/qv4sparsearray_p.h +++ b/src/qml/jsruntime/qv4sparsearray_p.h @@ -34,12 +34,19 @@ #ifndef QV4SPARSEARRAY_H #define QV4SPARSEARRAY_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" -#include <QtCore/qmap.h> -#include "qv4value_inl_p.h" -#include "qv4scopedvalue_p.h" -#include "qv4property_p.h" -#include <assert.h> +#include <QtCore/qlist.h> //#define Q_MAP_DEBUG #ifdef Q_MAP_DEBUG @@ -188,7 +195,7 @@ public: typedef qptrdiff difference_type; typedef int size_type; -#ifndef QT_NO_DEBUG +#ifdef Q_MAP_DEBUG void dump() const; #endif }; @@ -261,7 +268,7 @@ inline void SparseArray::push_back(uint index, uint len) n->value = index; } -#ifndef QT_NO_DEBUG +#ifdef Q_MAP_DEBUG inline void SparseArray::dump() const { const SparseArrayNode *it = begin(); @@ -344,4 +351,4 @@ inline SparseArrayNode *SparseArray::upperBound(uint akey) QT_END_NAMESPACE -#endif // QMAP_H +#endif diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 6d55eb2c18..24a13ddd10 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -32,7 +32,7 @@ ****************************************************************************/ #include "qv4string_p.h" -#include "qv4value_inl_p.h" +#include "qv4value_p.h" #ifndef V4_BOOTSTRAP #include "qv4identifiertable_p.h" #include "qv4runtime_p.h" @@ -110,7 +110,7 @@ bool String::isEqualTo(Managed *t, Managed *o) if (t == o) return true; - if (!o->d()->vtable->isString) + if (!o->d()->vtable()->isString) return false; return static_cast<String *>(t)->isEqualTo(static_cast<String *>(o)); diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 1cf8f51a29..85433369fc 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -33,6 +33,17 @@ #ifndef QV4STRING_H #define QV4STRING_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QtCore/qstring.h> #include "qv4managed_p.h" @@ -188,6 +199,20 @@ public: static uint toArrayIndex(const QString &str); }; +template<> +inline const String *Value::as() const { + return isManaged() && m() && m()->vtable()->isString ? static_cast<const String *>(this) : 0; +} + +#ifndef V4_BOOTSTRAP +template<> +inline ReturnedValue value_convert<String>(ExecutionEngine *e, const Value &v) +{ + return v.toString(e)->asReturnedValue(); +} +#endif + + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index e0b84f6da3..757ec6c6bf 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -36,7 +36,7 @@ #include "qv4regexp_p.h" #include "qv4regexpobject_p.h" #include "qv4objectproto_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" #include "qv4alloca_p.h" #include <QtCore/QDateTime> @@ -67,68 +67,62 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(StringObject); -Heap::StringObject::StringObject(InternalClass *ic, QV4::Object *prototype) - : Heap::Object(ic, prototype) +Heap::StringObject::StringObject() { - Q_ASSERT(vtable == QV4::StringObject::staticVTable()); - value = ic->engine->newString()->asReturnedValue(); - tmpProperty.value = Primitive::undefinedValue(); - - Scope scope(ic->engine); - ScopedObject s(scope, this); - s->defineReadonlyProperty(ic->engine->id_length, Primitive::fromInt32(0)); + Q_ASSERT(vtable() == QV4::StringObject::staticVTable()); + string = internalClass->engine->id_empty()->d(); + *propertyData(LengthPropertyIndex) = Primitive::fromInt32(0); } -Heap::StringObject::StringObject(ExecutionEngine *engine, const Value &val) - : Heap::Object(engine->emptyClass, engine->stringPrototype.asObject()) +Heap::StringObject::StringObject(const QV4::String *str) { - value = val; - Q_ASSERT(value.isString()); - tmpProperty.value = Primitive::undefinedValue(); - - Scope scope(engine); - ScopedObject s(scope, this); - s->defineReadonlyProperty(engine->id_length, Primitive::fromUInt32(value.stringValue()->toQString().length())); + string = str->d(); + *propertyData(LengthPropertyIndex) = Primitive::fromInt32(length()); } -Property *Heap::StringObject::getIndex(uint index) const +Heap::String *Heap::StringObject::getIndex(uint index) const { - QString str = value.stringValue()->toQString(); + QString str = string->toQString(); if (index >= (uint)str.length()) return 0; - tmpProperty.value = Encode(internalClass->engine->newString(str.mid(index, 1))); - return &tmpProperty; + return internalClass->engine->newString(str.mid(index, 1)); +} + +uint Heap::StringObject::length() const +{ + return string->len; } bool StringObject::deleteIndexedProperty(Managed *m, uint index) { ExecutionEngine *v4 = static_cast<StringObject *>(m)->engine(); Scope scope(v4); - Scoped<StringObject> o(scope, m->asStringObject()); + Scoped<StringObject> o(scope, m->as<StringObject>()); Q_ASSERT(!!o); - if (index < static_cast<uint>(o->d()->value.stringValue()->toQString().length())) { - if (v4->currentContext()->strictMode) + if (index < static_cast<uint>(o->d()->string->toQString().length())) { + if (v4->current->strictMode) v4->throwTypeError(); return false; } return true; } -void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attrs) +void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) { - *name = (Heap::String *)0; + name->setM(0); StringObject *s = static_cast<StringObject *>(m); - uint slen = s->d()->value.stringValue()->toQString().length(); + uint slen = s->d()->string->toQString().length(); if (it->arrayIndex <= slen) { while (it->arrayIndex < slen) { *index = it->arrayIndex; ++it->arrayIndex; PropertyAttributes a; - Property *pd = s->__getOwnProperty__(*index, &a); + Property pd; + s->getOwnProperty(*index, &a, &pd); if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { *attrs = a; - p->copy(pd, a); + p->copy(&pd, a); return; } } @@ -146,8 +140,7 @@ void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Heap::String void StringObject::markObjects(Heap::Base *that, ExecutionEngine *e) { StringObject::Data *o = static_cast<StringObject::Data *>(that); - o->value.stringValue()->mark(e); - o->tmpProperty.value.mark(e); + o->string->mark(e); Object::markObjects(that, e); } @@ -158,11 +151,11 @@ Heap::StringCtor::StringCtor(QV4::ExecutionContext *scope) { } -ReturnedValue StringCtor::construct(Managed *m, CallData *callData) +ReturnedValue StringCtor::construct(const Managed *m, CallData *callData) { - ExecutionEngine *v4 = static_cast<Object *>(m)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(m)->engine(); Scope scope(v4); - ScopedValue value(scope); + ScopedString value(scope); if (callData->argc) value = callData->args[0].toString(v4); else @@ -170,9 +163,9 @@ ReturnedValue StringCtor::construct(Managed *m, CallData *callData) return Encode(v4->newStringObject(value)); } -ReturnedValue StringCtor::call(Managed *m, CallData *callData) +ReturnedValue StringCtor::call(const Managed *m, CallData *callData) { - ExecutionEngine *v4 = static_cast<Object *>(m)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(m)->engine(); Scope scope(v4); ScopedValue value(scope); if (callData->argc) @@ -187,13 +180,13 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), method_fromCharCode, 1); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString, method_toString); - defineDefaultProperty(engine->id_valueOf, method_toString); // valueOf and toString are identical + defineDefaultProperty(engine->id_toString(), method_toString); + defineDefaultProperty(engine->id_valueOf(), method_toString); // valueOf and toString are identical defineDefaultProperty(QStringLiteral("charAt"), method_charAt, 1); defineDefaultProperty(QStringLiteral("charCodeAt"), method_charCodeAt, 1); defineDefaultProperty(QStringLiteral("concat"), method_concat, 1); @@ -220,8 +213,8 @@ static QString getThisString(ExecutionContext *ctx) ScopedValue t(scope, ctx->thisObject()); if (t->isString()) return t->stringValue()->toQString(); - if (StringObject *thisString = t->asStringObject()) - return thisString->d()->value.stringValue()->toQString(); + if (StringObject *thisString = t->as<StringObject>()) + return thisString->d()->string->toQString(); if (t->isUndefined() || t->isNull()) { scope.engine->throwTypeError(); return QString(); @@ -234,10 +227,10 @@ ReturnedValue StringPrototype::method_toString(CallContext *context) if (context->thisObject().isString()) return context->thisObject().asReturnedValue(); - StringObject *o = context->thisObject().asStringObject(); + StringObject *o = context->thisObject().as<StringObject>(); if (!o) return context->engine()->throwTypeError(); - return o->d()->value.asReturnedValue(); + return Encode(o->d()->string); } ReturnedValue StringPrototype::method_charAt(CallContext *context) @@ -368,7 +361,7 @@ ReturnedValue StringPrototype::method_match(CallContext *context) if (!rx) { ScopedCallData callData(scope, 1); callData->args[0] = regexp; - rx = context->d()->engine->regExpCtor.asFunctionObject()->construct(callData); + rx = context->d()->engine->regExpCtor()->construct(callData); } if (!rx) @@ -379,7 +372,7 @@ ReturnedValue StringPrototype::method_match(CallContext *context) // ### use the standard builtin function, not the one that might be redefined in the proto ScopedString execString(scope, scope.engine->newString(QStringLiteral("exec"))); - ScopedFunctionObject exec(scope, scope.engine->regExpPrototype.asObject()->get(execString)); + ScopedFunctionObject exec(scope, scope.engine->regExpPrototype()->get(execString)); ScopedCallData callData(scope, 1); callData->thisObject = rx; @@ -470,8 +463,8 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) { Scope scope(ctx); QString string; - if (StringObject *thisString = ctx->thisObject().asStringObject()) - string = thisString->d()->value.stringValue()->toQString(); + if (StringObject *thisString = ctx->thisObject().as<StringObject>()) + string = thisString->d()->string->toQString(); else string = ctx->thisObject().toQString(); @@ -481,12 +474,12 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) uint allocatedMatchOffsets = 64; uint _matchOffsets[64]; uint *matchOffsets = _matchOffsets; - uint nMatchOffsets = 0; ScopedValue searchValue(scope, ctx->argument(0)); Scoped<RegExpObject> regExp(scope, searchValue); if (regExp) { uint offset = 0; + uint nMatchOffsets = 0; // We extract the pointer here to work around a compiler bug on Android. Scoped<RegExp> re(scope, regExp->value()); @@ -510,7 +503,7 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) offset = qMax(offset + 1, matchOffsets[oldSize + 1]); } if (regExp->global()) - regExp->lastIndexProperty()->value = Primitive::fromUInt32(0); + *regExp->lastIndexProperty() = Primitive::fromUInt32(0); numStringMatches = nMatchOffsets / (regExp->value()->captureCount() * 2); numCaptures = regExp->value()->captureCount(); } else { @@ -519,7 +512,6 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) int idx = string.indexOf(searchString); if (idx != -1) { numStringMatches = 1; - nMatchOffsets = 2; matchOffsets[0] = idx; matchOffsets[1] = idx + searchString.length(); } @@ -593,7 +585,7 @@ ReturnedValue StringPrototype::method_search(CallContext *ctx) if (!regExp) { ScopedCallData callData(scope, 1); callData->args[0] = regExpValue; - regExpValue = ctx->d()->engine->regExpCtor.asFunctionObject()->construct(callData); + regExpValue = ctx->d()->engine->regExpCtor()->construct(callData); if (scope.engine->hasException) return Encode::undefined(); regExp = regExpValue->as<RegExpObject>(); diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index 459dc1322e..7d4f78d3c5 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -33,6 +33,17 @@ #ifndef QV4STRINGOBJECT_P_H #define QV4STRINGOBJECT_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" #include "qv4functionobject_p.h" #include <QtCore/qnumeric.h> @@ -44,13 +55,16 @@ namespace QV4 { namespace Heap { struct StringObject : Object { - StringObject(InternalClass *ic, QV4::Object *prototype); - StringObject(ExecutionEngine *engine, const Value &value); - Value value; + enum { + LengthPropertyIndex = 0 + }; + + StringObject(); + StringObject(const QV4::String *string); + String *string; - Property *getIndex(uint index) const; - // ### get rid of tmpProperty - mutable Property tmpProperty; + Heap::String *getIndex(uint index) const; + uint length() const; }; struct StringCtor : FunctionObject { @@ -62,15 +76,20 @@ struct StringCtor : FunctionObject { struct StringObject: Object { V4_OBJECT2(StringObject, Object) Q_MANAGED_TYPE(StringObject) + V4_INTERNALCLASS(stringClass) + V4_PROTOTYPE(stringPrototype) - Property *getIndex(uint index) const { + Heap::String *getIndex(uint index) const { return d()->getIndex(index); } + uint length() const { + return d()->length(); + } static bool deleteIndexedProperty(Managed *m, uint index); protected: - static void advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attrs); + static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; @@ -78,8 +97,8 @@ struct StringCtor: FunctionObject { V4_OBJECT2(StringCtor, FunctionObject) - static ReturnedValue construct(Managed *m, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct StringPrototype: StringObject diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index 19e541dba8..b45bbb713c 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -32,6 +32,7 @@ ****************************************************************************/ #include "qv4typedarray_p.h" #include "qv4arraybuffer_p.h" +#include "qv4string_p.h" #include <cmath> @@ -201,10 +202,10 @@ Heap::TypedArrayCtor::TypedArrayCtor(QV4::ExecutionContext *scope, TypedArray::T { } -ReturnedValue TypedArrayCtor::construct(Managed *m, CallData *callData) +ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<Object *>(m)->engine()); - Scoped<TypedArrayCtor> that(scope, static_cast<TypedArrayCtor *>(m)); + Scope scope(static_cast<const Object *>(m)->engine()); + Scoped<TypedArrayCtor> that(scope, static_cast<const TypedArrayCtor *>(m)); if (!callData->argc || !callData->args[0].isObject()) { // ECMA 6 22.2.1.1 @@ -215,11 +216,11 @@ ReturnedValue TypedArrayCtor::construct(Managed *m, CallData *callData) if (l != len) scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array.")); uint byteLength = len * operations[that->d()->type].bytesPerElement; - Scoped<ArrayBuffer> buffer(scope, scope.engine->memoryManager->alloc<ArrayBuffer>(scope.engine, byteLength)); + Scoped<ArrayBuffer> buffer(scope, scope.engine->newArrayBuffer(byteLength)); if (scope.engine->hasException) return Encode::undefined(); - Scoped<TypedArray > array(scope, scope.engine->memoryManager->alloc<TypedArray>(scope.engine, that->d()->type)); + Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer = buffer->d(); array->d()->byteLength = byteLength; array->d()->byteOffset = 0; @@ -235,11 +236,11 @@ ReturnedValue TypedArrayCtor::construct(Managed *m, CallData *callData) uint byteLength = typedArray->d()->byteLength; uint destByteLength = byteLength*destElementSize/srcElementSize; - Scoped<ArrayBuffer> newBuffer(scope, scope.engine->memoryManager->alloc<ArrayBuffer>(scope.engine, destByteLength)); + Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(destByteLength)); if (scope.engine->hasException) return Encode::undefined(); - Scoped<TypedArray > array(scope, scope.engine->memoryManager->alloc<TypedArray>(scope.engine, that->d()->type)); + Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer = newBuffer->d(); array->d()->byteLength = destByteLength; array->d()->byteOffset = 0; @@ -257,7 +258,7 @@ ReturnedValue TypedArrayCtor::construct(Managed *m, CallData *callData) TypedArrayWrite write =array->d()->type->write; for (uint i = 0; i < l; ++i) { Primitive val; - val.val = read(src, i*srcElementSize); + val.setRawValue(read(src, i*srcElementSize)); write(scope.engine, dest, i*destElementSize, val); } } @@ -289,7 +290,7 @@ ReturnedValue TypedArrayCtor::construct(Managed *m, CallData *callData) byteLength = (uint)l; } - Scoped<TypedArray > array(scope, scope.engine->memoryManager->alloc<TypedArray>(scope.engine, that->d()->type)); + Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer = buffer->d(); array->d()->byteLength = byteLength; array->d()->byteOffset = byteOffset; @@ -299,16 +300,16 @@ ReturnedValue TypedArrayCtor::construct(Managed *m, CallData *callData) // ECMA 6 22.2.1.3 ScopedObject o(scope, callData->argument(0)); - uint l = (uint) qBound(0., ScopedValue(scope, o->get(scope.engine->id_length))->toInteger(), (double)UINT_MAX); + uint l = (uint) qBound(0., ScopedValue(scope, o->get(scope.engine->id_length()))->toInteger(), (double)UINT_MAX); if (scope.engine->hasException) return scope.engine->throwTypeError(); uint elementSize = operations[that->d()->type].bytesPerElement; - Scoped<ArrayBuffer> newBuffer(scope, scope.engine->memoryManager->alloc<ArrayBuffer>(scope.engine, l * elementSize)); + Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(l * elementSize)); if (scope.engine->hasException) return Encode::undefined(); - Scoped<TypedArray > array(scope, scope.engine->memoryManager->alloc<TypedArray>(scope.engine, that->d()->type)); + Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer = newBuffer->d(); array->d()->byteLength = l * elementSize; array->d()->byteOffset = 0; @@ -329,28 +330,32 @@ ReturnedValue TypedArrayCtor::construct(Managed *m, CallData *callData) return array.asReturnedValue(); } -ReturnedValue TypedArrayCtor::call(Managed *that, CallData *callData) +ReturnedValue TypedArrayCtor::call(const Managed *that, CallData *callData) { return construct(that, callData); } -Heap::TypedArray::TypedArray(ExecutionEngine *e, Type t) - : Heap::Object(e->emptyClass, e->typedArrayPrototype[t].asObject()), - type(operations + t), +Heap::TypedArray::TypedArray(Type t) + : type(operations + t), arrayType(t) { } +Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t) +{ + return e->memoryManager->allocObject<TypedArray>(e->emptyClass, e->typedArrayPrototype + t, t); +} + void TypedArray::markObjects(Heap::Base *that, ExecutionEngine *e) { static_cast<TypedArray::Data *>(that)->buffer->mark(e); Object::markObjects(that, e); } -ReturnedValue TypedArray::getIndexed(Managed *m, uint index, bool *hasProperty) +ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProperty) { - Scope scope(static_cast<Object *>(m)->engine()); - Scoped<TypedArray> a(scope, static_cast<TypedArray *>(m)); + Scope scope(static_cast<const Object *>(m)->engine()); + Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m)); uint bytesPerElement = a->d()->type->bytesPerElement; uint byteOffset = a->d()->byteOffset + index * bytesPerElement; @@ -382,7 +387,7 @@ void TypedArray::putIndexed(Managed *m, uint index, const Value &value) return; reject: - if (scope.engine->currentContext()->strictMode) + if (scope.engine->current->strictMode) scope.engine->throwTypeError(); } @@ -390,10 +395,10 @@ void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(3)); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); ctor->defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement)); - defineDefaultProperty(engine->id_constructor, (o = ctor)); + defineDefaultProperty(engine->id_constructor(), (o = ctor)); defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, 0); defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, 0); defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, 0); @@ -470,7 +475,7 @@ ReturnedValue TypedArrayPrototype::method_set(CallContext *ctx) if (scope.engine->hasException || !o) return scope.engine->throwTypeError(); - double len = ScopedValue(scope, o->get(scope.engine->id_length))->toNumber(); + double len = ScopedValue(scope, o->get(scope.engine->id_length()))->toNumber(); uint l = (uint)len; if (scope.engine->hasException || l != len) return scope.engine->throwTypeError(); @@ -523,7 +528,7 @@ ReturnedValue TypedArrayPrototype::method_set(CallContext *ctx) TypedArrayWrite write = a->d()->type->write; for (uint i = 0; i < l; ++i) { Primitive val; - val.val = read(src, i*srcElementSize); + val.setRawValue(read(src, i*srcElementSize)); write(scope.engine, dest, i*elementSize, val); } @@ -563,7 +568,7 @@ ReturnedValue TypedArrayPrototype::method_subarray(CallContext *ctx) int newLen = end - begin; - ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor)); + ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor())); if (!constructor) return scope.engine->throwTypeError(); diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index afd1bb97e7..67e04c8ac0 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -33,6 +33,17 @@ #ifndef QV4TYPEDARRAY_H #define QV4TYPEDARRAY_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4object_p.h" #include "qv4functionobject_p.h" #include "qv4arraybuffer_p.h" @@ -69,10 +80,10 @@ struct TypedArray : Object { NTypes }; - TypedArray(ExecutionEngine *e, Type t); + TypedArray(Type t); const TypedArrayOperations *type; - ArrayBuffer *buffer; + Pointer<ArrayBuffer> buffer; uint byteLength; uint byteOffset; Type arrayType; @@ -85,7 +96,7 @@ struct TypedArrayCtor : FunctionObject { }; struct TypedArrayPrototype : Object { - inline TypedArrayPrototype(ExecutionEngine *e, TypedArray::Type t); + inline TypedArrayPrototype(TypedArray::Type t); TypedArray::Type type; }; @@ -96,6 +107,8 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object { V4_OBJECT2(TypedArray, Object) + static Heap::TypedArray *create(QV4::ExecutionEngine *e, Heap::TypedArray::Type t); + uint byteLength() const { return d()->byteLength; } @@ -113,7 +126,7 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object } static void markObjects(Heap::Base *that, ExecutionEngine *e); - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); static void putIndexed(Managed *m, uint index, const Value &value); }; @@ -121,8 +134,8 @@ struct TypedArrayCtor: FunctionObject { V4_OBJECT2(TypedArrayCtor, FunctionObject) - static ReturnedValue construct(Managed *m, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; @@ -142,9 +155,8 @@ struct TypedArrayPrototype : Object }; inline -Heap::TypedArrayPrototype::TypedArrayPrototype(ExecutionEngine *e, TypedArray::Type t) - : Heap::Object(e) - , type(t) +Heap::TypedArrayPrototype::TypedArrayPrototype(TypedArray::Type t) + : type(t) { } diff --git a/src/qml/jsruntime/qv4util_p.h b/src/qml/jsruntime/qv4util_p.h index f9ae37855e..329831f134 100644 --- a/src/qml/jsruntime/qv4util_p.h +++ b/src/qml/jsruntime/qv4util_p.h @@ -33,6 +33,17 @@ #ifndef QV4UTIL_H #define QV4UTIL_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qv4global_p.h" QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index 68228f06bb..4c81199a07 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -32,10 +32,11 @@ ****************************************************************************/ #include <qv4engine_p.h> #include <qv4runtime_p.h> +#include <qv4string_p.h> #ifndef V4_BOOTSTRAP #include <qv4object_p.h> #include <qv4objectproto_p.h> -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #endif #include <wtf/MathExtras.h> @@ -68,10 +69,32 @@ int Value::toUInt16() const return (unsigned short)number; } +bool Value::toBoolean() const +{ + switch (type()) { + case Value::Undefined_Type: + case Value::Null_Type: + return false; + case Value::Boolean_Type: + case Value::Integer_Type: + return (bool)int_32(); + case Value::Managed_Type: +#ifdef V4_BOOTSTRAP + Q_UNIMPLEMENTED(); +#else + if (isString()) + return stringValue()->toQString().length() > 0; +#endif + return true; + default: // double + return doubleValue() && !std::isnan(doubleValue()); + } +} + double Value::toInteger() const { if (integerCompatible()) - return int_32; + return int_32(); return Primitive::toInteger(toNumber()); } @@ -87,10 +110,10 @@ double Value::toNumberImpl() const #else if (isString()) return RuntimeHelpers::stringToNumber(stringValue()->toQString()); - { - Q_ASSERT(isObject()); - Scope scope(objectValue()->engine()); - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, NUMBER_HINT)); + { + Q_ASSERT(isObject()); + Scope scope(objectValue()->engine()); + ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, NUMBER_HINT)); if (scope.engine->hasException) return 0; return prim->toNumber(); @@ -99,7 +122,7 @@ double Value::toNumberImpl() const case QV4::Value::Null_Type: case QV4::Value::Boolean_Type: case QV4::Value::Integer_Type: - return int_32; + return int_32(); default: // double Q_UNREACHABLE(); } @@ -148,7 +171,7 @@ QString Value::toQStringNoThrow() const } case Value::Integer_Type: { QString str; - RuntimeHelpers::numberToString(&str, (double)int_32, 10); + RuntimeHelpers::numberToString(&str, (double)int_32(), 10); return str; } default: { // double @@ -184,7 +207,7 @@ QString Value::toQString() const } case Value::Integer_Type: { QString str; - RuntimeHelpers::numberToString(&str, (double)int_32, 10); + RuntimeHelpers::numberToString(&str, (double)int_32(), 10); return str; } default: { // double @@ -197,14 +220,14 @@ QString Value::toQString() const #endif // V4_BOOTSTRAP bool Value::sameValue(Value other) const { - if (val == other.val) + if (_val == other._val) return true; if (isString() && other.isString()) return stringValue()->isEqualTo(other.stringValue()); if (isInteger() && other.isDouble()) - return int_32 ? (double(int_32) == other.doubleValue()) : (other.val == 0); + return int_32() ? (double(int_32()) == other.doubleValue()) : (other._val == 0); if (isDouble() && other.isInteger()) - return other.int_32 ? (doubleValue() == double(other.int_32)) : (val == 0); + return other.int_32() ? (doubleValue() == double(other.int_32())) : (_val == 0); return false; } @@ -281,4 +304,35 @@ Heap::Object *Value::toObject(ExecutionEngine *e) const return RuntimeHelpers::convertToObject(e, *this); } +uint Value::asArrayLength(bool *ok) const +{ + *ok = true; + if (isInteger()) { + if (int_32() >= 0) { + return (uint)int_32(); + } else { + *ok = false; + return UINT_MAX; + } + } + if (isNumber()) { + double d = doubleValue(); + uint idx = (uint)d; + if (idx != d) { + *ok = false; + return UINT_MAX; + } + return idx; + } + if (isString()) + return stringValue()->toUInt(ok); + + uint idx = toUInt32(); + double d = toNumber(); + if (d != idx) { + *ok = false; + return UINT_MAX; + } + return idx; +} #endif // V4_BOOTSTRAP diff --git a/src/qml/jsruntime/qv4value_inl_p.h b/src/qml/jsruntime/qv4value_inl_p.h deleted file mode 100644 index f3026900d6..0000000000 --- a/src/qml/jsruntime/qv4value_inl_p.h +++ /dev/null @@ -1,293 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4VALUE_INL_H -#define QV4VALUE_INL_H - -#include <cmath> // this HAS to come - -#include "qv4value_p.h" - -#include "qv4string_p.h" -#include "qv4managed_p.h" -#include "qv4engine_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -inline bool Value::isString() const -{ - if (!isManaged()) - return false; - return m && m->vtable->isString; -} -inline bool Value::isObject() const -{ - if (!isManaged()) - return false; - return m && m->vtable->isObject; -} - -inline bool Value::isPrimitive() const -{ - return !isObject(); -} - -inline String *Value::asString() const -{ - if (isString()) - return stringValue(); - return 0; -} - -inline void Value::mark(ExecutionEngine *e) const -{ - if (!val) - return; - Managed *m = asManaged(); - if (m) - m->mark(e); -} - -inline Primitive Primitive::nullValue() -{ - Primitive v; -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - v.val = quint64(_Null_Type) << Tag_Shift; -#else - v.tag = _Null_Type; - v.int_32 = 0; -#endif - return v; -} - -inline Primitive Primitive::fromBoolean(bool b) -{ - Primitive v; - v.tag = _Boolean_Type; - v.int_32 = (bool)b; - return v; -} - -inline Primitive Primitive::fromDouble(double d) -{ - Primitive v; - v.setDouble(d); - return v; -} - -inline Primitive Primitive::fromInt32(int i) -{ - Primitive v; - v.tag = _Integer_Type; - v.int_32 = i; - return v; -} - -inline Primitive Primitive::fromUInt32(uint i) -{ - Primitive v; - if (i < INT_MAX) { - v.tag = _Integer_Type; - v.int_32 = (int)i; - } else { - v.setDouble(i); - } - return v; -} - -inline double Value::toNumber() const -{ - if (isInteger()) - return int_32; - if (isDouble()) - return doubleValue(); - return toNumberImpl(); -} - -inline int Value::toInt32() const -{ - if (isInteger()) - return int_32; - double d = isNumber() ? doubleValue() : toNumberImpl(); - - const double D32 = 4294967296.0; - const double D31 = D32 / 2.0; - - if ((d >= -D31 && d < D31)) - return static_cast<int>(d); - - return Primitive::toInt32(d); -} - -inline unsigned int Value::toUInt32() const -{ - return (unsigned int)toInt32(); -} - - -inline bool Value::toBoolean() const -{ - switch (type()) { - case Value::Undefined_Type: - case Value::Null_Type: - return false; - case Value::Boolean_Type: - case Value::Integer_Type: - return (bool)int_32; - case Value::Managed_Type: -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else - if (isString()) - return stringValue()->toQString().length() > 0; -#endif - return true; - default: // double - return doubleValue() && !std::isnan(doubleValue()); - } -} - -#ifndef V4_BOOTSTRAP -inline uint Value::asArrayIndex() const -{ -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - if (!isNumber()) - return UINT_MAX; - if (isInteger()) - return int_32 >= 0 ? (uint)int_32 : UINT_MAX; -#else - if (isInteger() && int_32 >= 0) - return (uint)int_32; - if (!isDouble()) - return UINT_MAX; -#endif - double d = doubleValue(); - uint idx = (uint)d; - if (idx != d) - return UINT_MAX; - return idx; -} - -inline uint Value::asArrayLength(bool *ok) const -{ - *ok = true; - if (isInteger()) { - if (int_32 >= 0) { - return (uint)int_32; - } else { - *ok = false; - return UINT_MAX; - } - } - if (isNumber()) { - double d = doubleValue(); - uint idx = (uint)d; - if (idx != d) { - *ok = false; - return UINT_MAX; - } - return idx; - } - if (isString()) - return stringValue()->toUInt(ok); - - uint idx = toUInt32(); - double d = toNumber(); - if (d != idx) { - *ok = false; - return UINT_MAX; - } - return idx; -} - -inline Object *Value::asObject() const -{ - return isObject() ? objectValue() : 0; -} - -inline FunctionObject *Value::asFunctionObject() const -{ - return isObject() ? managed()->asFunctionObject() : 0; -} - -inline NumberObject *Value::asNumberObject() const -{ - return isObject() ? managed()->asNumberObject() : 0; -} - -inline StringObject *Value::asStringObject() const -{ - return isObject() ? managed()->asStringObject() : 0; -} - -inline DateObject *Value::asDateObject() const -{ - return isObject() ? managed()->asDateObject() : 0; -} - -inline ArrayObject *Value::asArrayObject() const -{ - return isObject() ? managed()->asArrayObject() : 0; -} - -inline ErrorObject *Value::asErrorObject() const -{ - return isObject() ? managed()->asErrorObject() : 0; -} - -template<typename T> -inline T *Value::as() const { Managed *m = isObject() ? managed() : 0; return m ? m->as<T>() : 0; } - -#ifndef V4_BOOTSTRAP - -template<> -inline String *value_cast(const Value &v) { - return v.asString(); -} - -template<> -inline ReturnedValue value_convert<String>(ExecutionEngine *e, const Value &v) -{ - return v.toString(e)->asReturnedValue(); -} - -#endif - -#endif - -} // namespace QV4 - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 628950784a..089b2bbd34 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -33,10 +33,22 @@ #ifndef QV4VALUE_P_H #define QV4VALUE_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <limits.h> #include <QtCore/QString> #include "qv4global_p.h" +#include <private/qv4heap_p.h> /* We cannot rely on QT_POINTER_SIZE to be set correctly on host builds. In qmldevtools the Value objects are only used to store primitives, never object pointers. So we can use the 64-bit encoding. */ @@ -50,56 +62,12 @@ QT_BEGIN_NAMESPACE namespace QV4 { -typedef uint Bool; - namespace Heap { - -struct Q_QML_EXPORT Base { - union { - const ManagedVTable *vtable; - quintptr mm_data; - }; - - inline ReturnedValue asReturnedValue() const; - inline void mark(QV4::ExecutionEngine *engine); - - enum { - MarkBit = 0x1, - NotInUse = 0x2, - PointerMask = ~0x3 - }; - - ManagedVTable *gcGetVtable() const { - return reinterpret_cast<ManagedVTable *>(mm_data & PointerMask); - } - inline bool isMarked() const { - return mm_data & MarkBit; - } - inline void setMarkBit() { - mm_data |= MarkBit; - } - inline void clearMarkBit() { - mm_data &= ~MarkBit; - } - - inline bool inUse() const { - return !(mm_data & NotInUse); - } - - Base *nextFree() { - return reinterpret_cast<Base *>(mm_data & PointerMask); - } - void setNextFree(Base *m) { - mm_data = (reinterpret_cast<quintptr>(m) | NotInUse); - } - - void *operator new(size_t, Managed *m) { return m; } - void *operator new(size_t, Heap::Base *m) { return m; } - void operator delete(void *, Heap::Base *) {} -}; - + struct Base; } +typedef uint Bool; + struct Q_QML_PRIVATE_EXPORT Value { /* @@ -124,30 +92,38 @@ struct Q_QML_PRIVATE_EXPORT Value Bit 15-17 is then used to encode other immediates. */ + quint64 _val; + + Q_ALWAYS_INLINE quint64 val() const { return _val; } + Q_ALWAYS_INLINE void setVal(quint64 v) { _val = v; } + Q_ALWAYS_INLINE void setValue(quint32 v) { memcpy(&_val, &v, 4); } + Q_ALWAYS_INLINE void setTag(quint32 t) { memcpy(4 + (quint8 *)&_val, &t, 4); } - union { - quint64 val; -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - Heap::Base *m; -#else - double dbl; -#endif - struct { -#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN - uint tag; -#endif - union { - uint uint_32; - int int_32; -#ifndef QV4_USE_64_BIT_VALUE_ENCODING - Heap::Base *m; -#endif - }; #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - uint tag; + static inline int valueOffset() { return 0; } + static inline int tagOffset() { return 4; } + Q_ALWAYS_INLINE void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; } + Q_ALWAYS_INLINE quint32 value() const { return _val & quint64(~quint32(0)); } + Q_ALWAYS_INLINE quint32 tag() const { return _val >> 32; } +#else // !Q_LITTLE_ENDIAN + static inline int valueOffset() { return 4; } + static inline int tagOffset() { return 0; } + Q_ALWAYS_INLINE void setTagValue(quint32 tag, quint32 value) { _val = quint64(value) << 32 | tag; } + Q_ALWAYS_INLINE quint32 tag() const { return _val & quint64(~quint32(0)); } + Q_ALWAYS_INLINE quint32 value() const { return _val >> 32; } +#endif + +#ifdef QV4_USE_64_BIT_VALUE_ENCODING + Q_ALWAYS_INLINE Heap::Base *m() const { Heap::Base *b; memcpy(&b, &_val, 8); return b; } + Q_ALWAYS_INLINE void setM(Heap::Base *b) { memcpy(&_val, &b, 8); } +#else // !QV4_USE_64_BIT_VALUE_ENCODING + Q_ALWAYS_INLINE Heap::Base *m() const { Heap::Base *b; quint32 v = value(); memcpy(&b, &v, 4); return b; } + Q_ALWAYS_INLINE void setM(Heap::Base *b) { quint32 v; memcpy(&v, &b, 4); setValue(v); } #endif - }; - }; + + Q_ALWAYS_INLINE int int_32() const { int i; quint32 v = value(); memcpy(&i, &v, 4); return i; } + Q_ALWAYS_INLINE void setInt_32(int i) { quint32 u; memcpy(&u, &i, 4); setValue(u); } + Q_ALWAYS_INLINE uint uint_32() const { return value(); } #ifndef QV4_USE_64_BIT_VALUE_ENCODING enum Masks { @@ -173,9 +149,9 @@ struct Q_QML_PRIVATE_EXPORT Value }; enum ValueTypeInternal { - _Null_Type = Null_Type | ConvertibleToInt, - _Boolean_Type = Boolean_Type | ConvertibleToInt, - _Integer_Type = Integer_Type | ConvertibleToInt, + Null_Type_Internal = Null_Type | ConvertibleToInt, + Boolean_Type_Internal = Boolean_Type | ConvertibleToInt, + Integer_Type_Internal = Integer_Type | ConvertibleToInt, }; #else @@ -213,134 +189,134 @@ struct Q_QML_PRIVATE_EXPORT Value enum ValueTypeInternal { - _Null_Type = Null_Type, - _Boolean_Type = Boolean_Type, - _Integer_Type = Integer_Type + Null_Type_Internal = Null_Type, + Boolean_Type_Internal = Boolean_Type, + Integer_Type_Internal = Integer_Type }; #endif inline unsigned type() const { - return tag & Type_Mask; + return tag() & Type_Mask; } // used internally in property - inline bool isEmpty() const { return tag == Empty_Type; } + inline bool isEmpty() const { return tag() == Empty_Type; } - inline bool isUndefined() const { return tag == Undefined_Type; } - inline bool isNull() const { return tag == _Null_Type; } - inline bool isBoolean() const { return tag == _Boolean_Type; } + inline bool isUndefined() const { return tag() == Undefined_Type; } + inline bool isNull() const { return tag() == Null_Type_Internal; } + inline bool isBoolean() const { return tag ()== Boolean_Type_Internal; } #ifdef QV4_USE_64_BIT_VALUE_ENCODING - inline bool isInteger() const { return (val >> IsNumber_Shift) == 1; } - inline bool isDouble() const { return (val >> IsDouble_Shift); } - inline bool isNumber() const { return (val >> IsNumber_Shift); } - inline bool isManaged() const { return !(val >> IsManaged_Shift); } - inline bool isNullOrUndefined() const { return ((val >> IsManaged_Shift) & ~2) == 1; } - inline bool integerCompatible() const { return ((val >> IsConvertibleToInt_Shift) & ~2) == 1; } + inline bool isInteger() const { return (_val >> IsNumber_Shift) == 1; } + inline bool isDouble() const { return (_val >> IsDouble_Shift); } + inline bool isNumber() const { return (_val >> IsNumber_Shift); } + inline bool isManaged() const { return !(_val >> IsManaged_Shift); } + inline bool isNullOrUndefined() const { return ((_val >> IsManaged_Shift) & ~2) == 1; } + inline bool integerCompatible() const { return ((_val >> IsConvertibleToInt_Shift) & ~2) == 1; } static inline bool integerCompatible(Value a, Value b) { return a.integerCompatible() && b.integerCompatible(); } static inline bool bothDouble(Value a, Value b) { return a.isDouble() && b.isDouble(); } - double doubleValue() const { - Q_ASSERT(isDouble()); - union { - quint64 i; - double d; - } v; - v.i = val ^ NaNEncodeMask; - return v.d; - } - void setDouble(double d) { - union { - quint64 i; - double d; - } v; - v.d = d; - val = v.i ^ NaNEncodeMask; - Q_ASSERT(isDouble()); - } - inline bool isNaN() const { return (tag & 0x7fff8000) == 0x00078000; } + inline bool isNaN() const { return (tag() & 0x7fff8000) == 0x00078000; } #else - inline bool isInteger() const { return tag == _Integer_Type; } - inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } - inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } - inline bool isManaged() const { return tag == Managed_Type; } - inline bool isNullOrUndefined() const { return (tag & IsNullOrUndefined_Mask) == Undefined_Type; } - inline bool integerCompatible() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } + inline bool isInteger() const { return tag() == Integer_Type_Internal; } + inline bool isDouble() const { return (tag() & NotDouble_Mask) != NotDouble_Mask; } + inline bool isNumber() const { return tag() == Integer_Type_Internal || (tag() & NotDouble_Mask) != NotDouble_Mask; } + inline bool isManaged() const { return tag() == Managed_Type; } + inline bool isNullOrUndefined() const { return (tag() & IsNullOrUndefined_Mask) == Undefined_Type; } + inline bool integerCompatible() const { return (tag() & ConvertibleToInt) == ConvertibleToInt; } static inline bool integerCompatible(Value a, Value b) { - return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; + return ((a.tag() & b.tag()) & ConvertibleToInt) == ConvertibleToInt; } static inline bool bothDouble(Value a, Value b) { - return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; + return ((a.tag() | b.tag()) & NotDouble_Mask) != NotDouble_Mask; } - double doubleValue() const { Q_ASSERT(isDouble()); return dbl; } - void setDouble(double d) { dbl = d; Q_ASSERT(isDouble()); } - inline bool isNaN() const { return (tag & QV4::Value::NotDouble_Mask) == QV4::Value::NaN_Mask; } + inline bool isNaN() const { return (tag() & QV4::Value::NotDouble_Mask) == QV4::Value::NaN_Mask; } #endif + Q_ALWAYS_INLINE double doubleValue() const { + Q_ASSERT(isDouble()); + double d; + quint64 v = _val; +#ifdef QV4_USE_64_BIT_VALUE_ENCODING + v ^= NaNEncodeMask; +#endif + memcpy(&d, &v, 8); + return d; + } + Q_ALWAYS_INLINE void setDouble(double d) { + memcpy(&_val, &d, 8); +#ifdef QV4_USE_64_BIT_VALUE_ENCODING + _val ^= NaNEncodeMask; +#endif + Q_ASSERT(isDouble()); + } inline bool isString() const; inline bool isObject() const; inline bool isInt32() { - if (tag == _Integer_Type) + if (tag() == Integer_Type_Internal) return true; if (isDouble()) { double d = doubleValue(); int i = (int)d; if (i == d) { - int_32 = i; - tag = _Integer_Type; + setInt_32(i); + setTag(Integer_Type_Internal); return true; } } return false; } double asDouble() const { - if (tag == _Integer_Type) - return int_32; + if (tag() == Integer_Type_Internal) + return int_32(); return doubleValue(); } bool booleanValue() const { - return int_32; + return int_32(); } int integerValue() const { - return int_32; + return int_32(); } - String *stringValue() const { - return m ? reinterpret_cast<String*>(const_cast<Value *>(this)) : 0; + Q_ALWAYS_INLINE String *stringValue() const { + return m() ? reinterpret_cast<String*>(const_cast<Value *>(this)) : 0; } - Object *objectValue() const { - return m ? reinterpret_cast<Object*>(const_cast<Value *>(this)) : 0; + Q_ALWAYS_INLINE Object *objectValue() const { + return m() ? reinterpret_cast<Object*>(const_cast<Value *>(this)) : 0; } - Managed *managed() const { - return m ? reinterpret_cast<Managed*>(const_cast<Value *>(this)) : 0; + Q_ALWAYS_INLINE Managed *managed() const { + return m() ? reinterpret_cast<Managed*>(const_cast<Value *>(this)) : 0; } - Heap::Base *heapObject() const { - return m; + Q_ALWAYS_INLINE Heap::Base *heapObject() const { + return m(); } - quint64 rawValue() const { - return val; + Q_ALWAYS_INLINE quint64 &rawValueRef() { + return _val; + } + Q_ALWAYS_INLINE quint64 rawValue() const { + return _val; } + Q_ALWAYS_INLINE void setRawValue(quint64 raw) { _val = raw; } static inline Value fromHeapObject(Heap::Base *m) { Value v; - v.m = m; + v.setRawValue(0); + v.setM(m); #ifndef QV4_USE_64_BIT_VALUE_ENCODING - v.tag = Managed_Type; + v.setTag(Managed_Type); #endif return v; } - static inline Value fromManaged(Managed *m); - int toUInt16() const; inline int toInt32() const; inline unsigned int toUInt32() const; - inline bool toBoolean() const; + bool toBoolean() const; double toInteger() const; inline double toNumber() const; double toNumberImpl() const; @@ -353,21 +329,32 @@ struct Q_QML_PRIVATE_EXPORT Value inline bool tryIntegerConversion() { bool b = integerCompatible(); if (b) - tag = _Integer_Type; + setTag(Integer_Type_Internal); return b; } - inline String *asString() const; - inline Managed *asManaged() const; - inline Object *asObject() const; - inline FunctionObject *asFunctionObject() const; - inline NumberObject *asNumberObject() const; - inline StringObject *asStringObject() const; - inline DateObject *asDateObject() const; - inline ArrayObject *asArrayObject() const; - inline ErrorObject *asErrorObject() const; + template <typename T> + const T *as() const { + if (!m() || !isManaged()) + return 0; + + Q_ASSERT(m()->vtable()); +#if !defined(QT_NO_QOBJECT_CHECK) + static_cast<const T *>(this)->qt_check_for_QMANAGED_macro(static_cast<const T *>(this)); +#endif + const VTable *vt = m()->vtable(); + while (vt) { + if (vt == T::staticVTable()) + return static_cast<const T *>(this); + vt = vt->parent; + } + return 0; + } + template <typename T> + T *as() { + return const_cast<T *>(const_cast<const Value *>(this)->as<T>()); + } - template<typename T> inline T *as() const; template<typename T> inline T *cast() { return static_cast<T *>(managed()); } @@ -376,26 +363,32 @@ struct Q_QML_PRIVATE_EXPORT Value } inline uint asArrayIndex() const; - inline uint asArrayLength(bool *ok) const; +#ifndef V4_BOOTSTRAP + uint asArrayLength(bool *ok) const; +#endif - ReturnedValue asReturnedValue() const { return val; } - static Value fromReturnedValue(ReturnedValue val) { Value v; v.val = val; return v; } + ReturnedValue asReturnedValue() const { return _val; } + static Value fromReturnedValue(ReturnedValue val) { Value v; v._val = val; return v; } // Section 9.12 bool sameValue(Value other) const; - inline void mark(ExecutionEngine *e) const; + inline void mark(ExecutionEngine *e); Value &operator =(const ScopedValue &v); - Value &operator=(ReturnedValue v) { val = v; return *this; } + Value &operator=(ReturnedValue v) { _val = v; return *this; } Value &operator=(Managed *m) { - val = Value::fromManaged(m).val; + if (!m) { + setTagValue(Undefined_Type, 0); + } else { + _val = reinterpret_cast<Value *>(m)->_val; + } return *this; } Value &operator=(Heap::Base *o) { - m = o; + setM(o); #ifndef QV4_USE_64_BIT_VALUE_ENCODING - tag = Managed_Type; + setTag(Managed_Type); #endif return *this; } @@ -403,18 +396,69 @@ struct Q_QML_PRIVATE_EXPORT Value template<typename T> Value &operator=(const Scoped<T> &t); Value &operator=(const Value &v) { - val = v.val; + _val = v._val; return *this; } }; -inline Managed *Value::asManaged() const +inline bool Value::isString() const { - if (isManaged()) - return managed(); - return 0; + if (!isManaged()) + return false; + return m() && m()->vtable()->isString; +} +inline bool Value::isObject() const +{ + if (!isManaged()) + return false; + return m() && m()->vtable()->isObject; } +inline bool Value::isPrimitive() const +{ + return !isObject(); +} + +inline double Value::toNumber() const +{ + if (isInteger()) + return int_32(); + if (isDouble()) + return doubleValue(); + return toNumberImpl(); +} + + +#ifndef V4_BOOTSTRAP +inline uint Value::asArrayIndex() const +{ +#ifdef QV4_USE_64_BIT_VALUE_ENCODING + if (!isNumber()) + return UINT_MAX; + if (isInteger()) + return int_32() >= 0 ? (uint)int_32() : UINT_MAX; +#else + if (isInteger() && int_32() >= 0) + return (uint)int_32(); + if (!isDouble()) + return UINT_MAX; +#endif + double d = doubleValue(); + uint idx = (uint)d; + if (idx != d) + return UINT_MAX; + return idx; +} +#endif + +inline +ReturnedValue Heap::Base::asReturnedValue() const +{ + return Value::fromHeapObject(const_cast<Heap::Base *>(this)).asReturnedValue(); +} + + + struct Q_QML_PRIVATE_EXPORT Primitive : public Value { inline static Primitive emptyValue(); @@ -437,10 +481,11 @@ inline Primitive Primitive::undefinedValue() { Primitive v; #ifdef QV4_USE_64_BIT_VALUE_ENCODING - v.val = quint64(Undefined_Type) << Tag_Shift; + v.setRawValue(quint64(Undefined_Type) << Tag_Shift); #else - v.tag = Undefined_Type; - v.int_32 = 0; + v.setRawValue(0); + v.setTag(Undefined_Type); + v.setValue(0); #endif return v; } @@ -448,66 +493,81 @@ inline Primitive Primitive::undefinedValue() inline Primitive Primitive::emptyValue() { Primitive v; - v.tag = Value::Empty_Type; - v.uint_32 = 0; + v.setTagValue(Value::Empty_Type, 0); return v; } -template <typename T> -struct TypedValue : public Value +inline Primitive Primitive::nullValue() { - template<typename X> - TypedValue &operator =(X *x) { - m = x; + Primitive v; #ifndef QV4_USE_64_BIT_VALUE_ENCODING - tag = Managed_Type; + v.setRawValue(quint64(Null_Type_Internal) << Tag_Shift); +#else + v.setTagValue(Null_Type_Internal, 0); #endif - return *this; - } - TypedValue &operator =(T *t); - TypedValue &operator =(const Scoped<T> &v); -// TypedValue &operator =(const ManagedRef<T> &v); - - TypedValue &operator =(const TypedValue<T> &t); + return v; +} - bool operator!() const { return !managed(); } +inline Primitive Primitive::fromBoolean(bool b) +{ + Primitive v; + v.setTagValue(Boolean_Type_Internal, b); + return v; +} - operator T *() { return static_cast<T *>(managed()); } - T *operator->() { return static_cast<T *>(managed()); } - const T *operator->() const { return static_cast<T *>(managed()); } - T *getPointer() const { return static_cast<T *>(managed()); } +inline Primitive Primitive::fromDouble(double d) +{ + Primitive v; + v.setDouble(d); + return v; +} - void mark(ExecutionEngine *e) { if (managed()) managed()->mark(e); } -}; -typedef TypedValue<String> StringValue; +inline Primitive Primitive::fromInt32(int i) +{ + Primitive v; + v.setTagValue(Integer_Type_Internal, 0); // For mingw482, because it complains, and for VS9, because of internal compiler errors. + v.setInt_32(i); + return v; +} +inline Primitive Primitive::fromUInt32(uint i) +{ + Primitive v; + if (i < INT_MAX) { + v.setTagValue(Integer_Type_Internal, 0); // For mingw482, because it complains, and for VS9, because of internal compiler errors. + v.setInt_32((int)i); + } else { + v.setDouble(i); + } + return v; +} struct Encode { static ReturnedValue undefined() { return quint64(Value::Undefined_Type) << Value::Tag_Shift; } static ReturnedValue null() { - return quint64(Value::_Null_Type) << Value::Tag_Shift; + return quint64(Value::Null_Type_Internal) << Value::Tag_Shift; } Encode(bool b) { - val = (quint64(Value::_Boolean_Type) << Value::Tag_Shift) | (uint)b; + val = (quint64(Value::Boolean_Type_Internal) << Value::Tag_Shift) | (uint)b; } Encode(double d) { Value v; v.setDouble(d); - val = v.val; + val = v.rawValue(); } Encode(int i) { - val = (quint64(Value::_Integer_Type) << Value::Tag_Shift) | (uint)i; + val = (quint64(Value::Integer_Type_Internal) << Value::Tag_Shift) | (uint)i; } Encode(uint i) { if (i <= INT_MAX) { - val = (quint64(Value::_Integer_Type) << Value::Tag_Shift) | i; + val = (quint64(Value::Integer_Type_Internal) << Value::Tag_Shift) | i; } else { Value v; v.setDouble(i); - val = v.val; + val = v.rawValue(); } } Encode(ReturnedValue v) { @@ -527,21 +587,29 @@ private: Encode(void *); }; -inline -ReturnedValue Heap::Base::asReturnedValue() const +template<typename T> +ReturnedValue value_convert(ExecutionEngine *e, const Value &v); + +inline int Value::toInt32() const { - return Value::fromHeapObject(const_cast<Heap::Base *>(this)).asReturnedValue(); -} + if (isInteger()) + return int_32(); + double d = isNumber() ? doubleValue() : toNumberImpl(); + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; -template<typename T> -T *value_cast(const Value &v) + if ((d >= -D31 && d < D31)) + return static_cast<int>(d); + + return Primitive::toInt32(d); +} + +inline unsigned int Value::toUInt32() const { - return v.as<T>(); + return (unsigned int)toInt32(); } -template<typename T> -ReturnedValue value_convert(ExecutionEngine *e, const Value &v); } diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index 966f83acef..4609373cc9 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -43,12 +43,15 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(VariantObject); -Heap::VariantObject::VariantObject(QV4::ExecutionEngine *engine, const QVariant &value) - : Heap::Object(engine->emptyClass, engine->variantPrototype.asObject()) +Heap::VariantObject::VariantObject() +{ +} + +Heap::VariantObject::VariantObject(const QVariant &value) { data = value; if (isScarce()) - engine->scarceResources.insert(this); + internalClass->engine->scarceResources.insert(this); } bool VariantObject::Data::isScarce() const @@ -96,8 +99,8 @@ void VariantPrototype::init() { defineDefaultProperty(QStringLiteral("preserve"), method_preserve, 0); defineDefaultProperty(QStringLiteral("destroy"), method_destroy, 0); - defineDefaultProperty(engine()->id_valueOf, method_valueOf, 0); - defineDefaultProperty(engine()->id_toString, method_toString, 0); + defineDefaultProperty(engine()->id_valueOf(), method_valueOf, 0); + defineDefaultProperty(engine()->id_toString(), method_toString, 0); } QV4::ReturnedValue VariantPrototype::method_preserve(CallContext *ctx) @@ -129,7 +132,7 @@ QV4::ReturnedValue VariantPrototype::method_toString(CallContext *ctx) return Encode::undefined(); QString result = o->d()->data.toString(); if (result.isEmpty() && !o->d()->data.canConvert(QVariant::String)) - result = QString::fromLatin1("QVariant(%0)").arg(QString::fromLatin1(o->d()->data.typeName())); + result = QStringLiteral("QVariant(%0)").arg(QString::fromLatin1(o->d()->data.typeName())); return Encode(ctx->d()->engine->newString(result)); } diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h index 2b48412c4d..4680912354 100644 --- a/src/qml/jsruntime/qv4variantobject_p.h +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -49,7 +49,7 @@ #include <QtQml/qqmllist.h> #include <QtCore/qvariant.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4object_p.h> QT_BEGIN_NAMESPACE @@ -60,10 +60,8 @@ namespace Heap { struct VariantObject : Object, public ExecutionEngine::ScarceResourceData { - VariantObject(InternalClass *ic, QV4::Object *prototype) - : Object(ic, prototype) - {} - VariantObject(QV4::ExecutionEngine *engine, const QVariant &value); + VariantObject(); + VariantObject(const QVariant &value); ~VariantObject() { if (isScarce()) node.remove(); @@ -77,6 +75,7 @@ struct VariantObject : Object, public ExecutionEngine::ScarceResourceData struct VariantObject : Object { V4_OBJECT2(VariantObject, Object) + V4_PROTOTYPE(variantPrototype) V4_NEEDS_DESTROY void addVmePropertyReference(); diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 390bbf5ee3..024a72bde2 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -37,13 +37,14 @@ #include <QtCore/qjsondocument.h> #include <QtCore/qjsonobject.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4debugging_p.h> #include <private/qv4function_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4math_p.h> #include <private/qv4scopedvalue_p.h> #include <private/qv4lookup_p.h> +#include <private/qv4string_p.h> #include <iostream> #include "qv4alloca_p.h" @@ -374,8 +375,8 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code const uchar *exceptionHandler = 0; QV4::Scope scope(engine); - QV4::ScopedContext context(scope, engine->currentContext()); - engine->currentContext()->lineNumber = -1; + QV4::ExecutionContext *context = engine->currentContext; + engine->current->lineNumber = -1; #ifdef DO_TRACE_INSTR qDebug("Starting VME with context=%p and code=%p", context, code); @@ -512,6 +513,28 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code STOREVALUE(instr.result, Runtime::getQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); MOTH_END_INSTR(LoadQObjectProperty) + MOTH_BEGIN_INSTR(StoreScopeObjectProperty) + Runtime::setQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); + CHECK_EXCEPTION; + MOTH_END_INSTR(StoreScopeObjectProperty) + + MOTH_BEGIN_INSTR(LoadScopeObjectProperty) + STOREVALUE(instr.result, Runtime::getQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex)); + MOTH_END_INSTR(LoadScopeObjectProperty) + + MOTH_BEGIN_INSTR(StoreContextObjectProperty) + Runtime::setQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); + CHECK_EXCEPTION; + MOTH_END_INSTR(StoreContextObjectProperty) + + MOTH_BEGIN_INSTR(LoadContextObjectProperty) + STOREVALUE(instr.result, Runtime::getQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex)); + MOTH_END_INSTR(LoadContextObjectProperty) + + MOTH_BEGIN_INSTR(LoadIdObject) + STOREVALUE(instr.result, Runtime::getQmlIdObject(engine, VALUE(instr.base), instr.index)); + MOTH_END_INSTR(LoadIdObject) + MOTH_BEGIN_INSTR(LoadAttachedQObjectProperty) STOREVALUE(instr.result, Runtime::getQmlAttachedProperty(engine, instr.attachedPropertiesId, instr.propertyIndex)); MOTH_END_INSTR(LoadAttachedQObjectProperty) @@ -565,6 +588,26 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code STOREVALUE(instr.result, Runtime::callPropertyLookup(engine, instr.lookupIndex, callData)); MOTH_END_INSTR(CallPropertyLookup) + MOTH_BEGIN_INSTR(CallScopeObjectProperty) + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); + callData->tag = QV4::Value::Integer_Type; + callData->argc = instr.argc; + callData->thisObject = VALUE(instr.base); + STOREVALUE(instr.result, Runtime::callQmlScopeObjectProperty(engine, instr.index, callData)); + MOTH_END_INSTR(CallScopeObjectProperty) + + MOTH_BEGIN_INSTR(CallContextObjectProperty) + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); + callData->tag = QV4::Value::Integer_Type; + callData->argc = instr.argc; + callData->thisObject = VALUE(instr.base); + STOREVALUE(instr.result, Runtime::callQmlContextObjectProperty(engine, instr.index, callData)); + MOTH_END_INSTR(CallContextObjectProperty) + MOTH_BEGIN_INSTR(CallElement) Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); @@ -607,18 +650,18 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CallBuiltinPushCatchScope) Runtime::pushCatchScope(static_cast<QV4::NoThrowEngine*>(engine), instr.name); - context = engine->currentContext(); + context = engine->currentContext; MOTH_END_INSTR(CallBuiltinPushCatchScope) MOTH_BEGIN_INSTR(CallBuiltinPushScope) Runtime::pushWithScope(VALUE(instr.arg), engine); - context = engine->currentContext(); + context = engine->currentContext; CHECK_EXCEPTION; MOTH_END_INSTR(CallBuiltinPushScope) MOTH_BEGIN_INSTR(CallBuiltinPopScope) Runtime::popScope(engine); - context = engine->currentContext(); + context = engine->currentContext; MOTH_END_INSTR(CallBuiltinPopScope) MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) @@ -641,6 +684,14 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code STOREVALUE(instr.result, Runtime::deleteName(engine, instr.name)); MOTH_END_INSTR(CallBuiltinDeleteName) + MOTH_BEGIN_INSTR(CallBuiltinTypeofScopeObjectProperty) + STOREVALUE(instr.result, Runtime::typeofScopeObjectProperty(engine, VALUE(instr.base), instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofContextObjectProperty) + STOREVALUE(instr.result, Runtime::typeofContextObjectProperty(engine, VALUE(instr.base), instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofMember) + MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) STOREVALUE(instr.result, Runtime::typeofMember(engine, VALUE(instr.base), instr.member)); MOTH_END_INSTR(CallBuiltinTypeofMember) @@ -846,7 +897,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(Ret) MOTH_BEGIN_INSTR(Debug) - engine->currentContext()->lineNumber = instr.lineNumber; + engine->current->lineNumber = instr.lineNumber; QV4::Debugging::Debugger *debugger = context->engine()->debugger; if (debugger && debugger->pauseAtNextOpportunity()) debugger->maybeBreakAtInstruction(); @@ -855,31 +906,23 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(Debug) MOTH_BEGIN_INSTR(Line) - engine->currentContext()->lineNumber = instr.lineNumber; + engine->current->lineNumber = instr.lineNumber; if (qt_v4IsDebugging) qt_v4CheckForBreak(context, scopes, scopeDepth); - MOTH_END_INSTR(Debug) + MOTH_END_INSTR(Line) MOTH_BEGIN_INSTR(LoadThis) VALUE(instr.result) = context->thisObject(); MOTH_END_INSTR(LoadThis) - MOTH_BEGIN_INSTR(LoadQmlIdArray) - VALUE(instr.result) = Runtime::getQmlIdArray(static_cast<QV4::NoThrowEngine*>(engine)); - MOTH_END_INSTR(LoadQmlIdArray) + MOTH_BEGIN_INSTR(LoadQmlContext) + VALUE(instr.result) = Runtime::getQmlContext(static_cast<QV4::NoThrowEngine*>(engine)); + MOTH_END_INSTR(LoadQmlContext) MOTH_BEGIN_INSTR(LoadQmlImportedScripts) VALUE(instr.result) = Runtime::getQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine)); MOTH_END_INSTR(LoadQmlImportedScripts) - MOTH_BEGIN_INSTR(LoadQmlContextObject) - VALUE(instr.result) = Runtime::getQmlContextObject(static_cast<QV4::NoThrowEngine*>(engine)); - MOTH_END_INSTR(LoadContextObject) - - MOTH_BEGIN_INSTR(LoadQmlScopeObject) - VALUE(instr.result) = Runtime::getQmlScopeObject(static_cast<QV4::NoThrowEngine*>(engine)); - MOTH_END_INSTR(LoadScopeObject) - MOTH_BEGIN_INSTR(LoadQmlSingleton) VALUE(instr.result) = Runtime::getQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), instr.name); MOTH_END_INSTR(LoadQmlSingleton) diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index 55cac75a66..c0421022d2 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -34,6 +34,17 @@ #ifndef QV4VME_MOTH_P_H #define QV4VME_MOTH_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <private/qv4runtime_p.h> #include <private/qv4instr_moth_p.h> diff --git a/src/qml/memory/memory.pri b/src/qml/memory/memory.pri new file mode 100644 index 0000000000..04b7566ccc --- /dev/null +++ b/src/qml/memory/memory.pri @@ -0,0 +1,14 @@ +INCLUDEPATH += $$PWD +INCLUDEPATH += $$OUT_PWD + +!qmldevtools_build { +SOURCES += \ + $$PWD/qv4mm.cpp \ + +HEADERS += \ + $$PWD/qv4mm_p.h + +} + +HEADERS += \ + $$PWD/qv4heap_p.h diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h new file mode 100644 index 0000000000..a3db71fee1 --- /dev/null +++ b/src/qml/memory/qv4heap_p.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4HEAP_P_H +#define QV4HEAP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QString> +#include <private/qv4global_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct VTable +{ + const VTable * const parent; + uint isExecutionContext : 1; + uint isString : 1; + uint isObject : 1; + uint isFunctionObject : 1; + uint isErrorObject : 1; + uint isArrayData : 1; + uint unused : 18; + uint type : 8; + const char *className; + void (*destroy)(Heap::Base *); + void (*markObjects)(Heap::Base *, ExecutionEngine *e); + bool (*isEqualTo)(Managed *m, Managed *other); +}; + +namespace Heap { + +struct Q_QML_EXPORT Base { + quintptr mm_data; // vtable and markbit + + inline ReturnedValue asReturnedValue() const; + inline void mark(QV4::ExecutionEngine *engine); + + enum { + MarkBit = 0x1, + NotInUse = 0x2, + PointerMask = ~0x3 + }; + + void setVtable(const VTable *v) { + Q_ASSERT(!(mm_data & MarkBit)); + mm_data = reinterpret_cast<quintptr>(v); + } + VTable *vtable() const { + return reinterpret_cast<VTable *>(mm_data & PointerMask); + } + inline bool isMarked() const { + return mm_data & MarkBit; + } + inline void setMarkBit() { + mm_data |= MarkBit; + } + inline void clearMarkBit() { + mm_data &= ~MarkBit; + } + + inline bool inUse() const { + return !(mm_data & NotInUse); + } + + Base *nextFree() { + return reinterpret_cast<Base *>(mm_data & PointerMask); + } + void setNextFree(Base *m) { + mm_data = (reinterpret_cast<quintptr>(m) | NotInUse); + } + + void *operator new(size_t, Managed *m) { return m; } + void *operator new(size_t, Heap::Base *m) { return m; } + void operator delete(void *, Heap::Base *) {} +}; + +template <typename T> +struct Pointer { + Pointer() {} + Pointer(T *t) : ptr(t) {} + + T *operator->() const { return ptr; } + operator T *() const { return ptr; } + + Pointer &operator =(T *t) { ptr = t; return *this; } + + template <typename Type> + Type *cast() { return static_cast<Type *>(ptr); } + + T *ptr; +}; + +} + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 64491e1376..5181bf912b 100644 --- a/src/qml/jsruntime/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -64,10 +64,42 @@ #include <pthread_np.h> #endif +#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT (std::size_t)128*1024 + using namespace WTF; QT_BEGIN_NAMESPACE +static uint maxShiftValue() +{ + static uint result = 0; + if (!result) { + result = 6; + if (Q_UNLIKELY(qEnvironmentVariableIsSet("QV4_MM_MAXBLOCK_SHIFT"))) { + bool ok; + const uint overrideValue = qgetenv("QV4_MM_MAXBLOCK_SHIFT").toUInt(&ok); + if (ok && overrideValue <= 11 && overrideValue > 0) + result = overrideValue; + } + } + return result; +} + +static std::size_t maxChunkSizeValue() +{ + static std::size_t result = 0; + if (!result) { + result = 32 * 1024; + if (Q_UNLIKELY(qEnvironmentVariableIsSet("QV4_MM_MAX_CHUNK_SIZE"))) { + bool ok; + const std::size_t overrideValue = qgetenv("QV4_MM_MAX_CHUNK_SIZE").toUInt(&ok); + if (ok) + result = overrideValue; + } + } + return result; +} + using namespace QV4; struct MemoryManager::Data @@ -111,8 +143,6 @@ struct MemoryManager::Data LargeItem *largeItems; std::size_t totalLargeItemsAllocated; - GCDeletable *deletable; - // statistics: #ifdef DETAILED_MM_STATS QVector<unsigned> allocSizeCounters; @@ -120,34 +150,22 @@ struct MemoryManager::Data Data() : gcBlocked(false) + , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC")) + , gcStats(!qEnvironmentVariableIsEmpty("QV4_MM_STATS")) , engine(0) , totalItems(0) , totalAlloc(0) - , maxShift(6) - , maxChunkSize(32*1024) + , maxShift(maxShiftValue()) + , maxChunkSize(maxChunkSizeValue()) , unmanagedHeapSize(0) - , unmanagedHeapSizeGCLimit(64 * 1024) + , unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT) , largeItems(0) , totalLargeItemsAllocated(0) - , deletable(0) { memset(nonFullChunks, 0, sizeof(nonFullChunks)); memset(nChunks, 0, sizeof(nChunks)); memset(availableItems, 0, sizeof(availableItems)); memset(allocCount, 0, sizeof(allocCount)); - aggressiveGC = !qgetenv("QV4_MM_AGGRESSIVE_GC").isEmpty(); - gcStats = !qgetenv("QV4_MM_STATS").isEmpty(); - - QByteArray overrideMaxShift = qgetenv("QV4_MM_MAXBLOCK_SHIFT"); - bool ok; - uint override = overrideMaxShift.toUInt(&ok); - if (ok && override <= 11 && override > 0) - maxShift = override; - - QByteArray maxChunkString = qgetenv("QV4_MM_MAX_CHUNK_SIZE"); - std::size_t tmpMaxChunkSize = maxChunkString.toUInt(&ok); - if (ok) - maxChunkSize = tmpMaxChunkSize; } ~Data() @@ -189,15 +207,15 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec #ifdef V4_USE_VALGRIND VALGRIND_ENABLE_ERROR_REPORTING; #endif - if (std::size_t(header->itemSize) == MemoryManager::align(sizeof(Heap::String)) && m->gcGetVtable()->isString) { + if (std::size_t(header->itemSize) == MemoryManager::align(sizeof(Heap::String)) && m->vtable()->isString) { std::size_t heapBytes = static_cast<Heap::String *>(m)->retainedTextSize(); Q_ASSERT(*unmanagedHeapSize >= heapBytes); // qDebug() << "-- it's a string holding on to" << heapBytes << "bytes"; *unmanagedHeapSize -= heapBytes; } - if (m->gcGetVtable()->destroy) - m->gcGetVtable()->destroy(m); + if (m->vtable()->destroy) + m->vtable()->destroy(m); memset(m, 0, header->itemSize); #ifdef V4_USE_VALGRIND @@ -222,7 +240,8 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec } // namespace MemoryManager::MemoryManager(ExecutionEngine *engine) - : m_d(new Data) + : engine(engine) + , m_d(new Data) , m_persistentValues(new PersistentValueStorage(engine)) , m_weakValues(new PersistentValueStorage(engine)) { @@ -249,13 +268,12 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize if (m_d->unmanagedHeapSize > m_d->unmanagedHeapSizeGCLimit) { runGC(); - if (m_d->unmanagedHeapSizeGCLimit <= m_d->unmanagedHeapSize) + if (3*m_d->unmanagedHeapSizeGCLimit <= 4*m_d->unmanagedHeapSize) + // more than 75% full, raise limit m_d->unmanagedHeapSizeGCLimit = std::max(m_d->unmanagedHeapSizeGCLimit, m_d->unmanagedHeapSize) * 2; else if (m_d->unmanagedHeapSize * 4 <= m_d->unmanagedHeapSizeGCLimit) - m_d->unmanagedHeapSizeGCLimit /= 2; - else if (m_d->unmanagedHeapSizeGCLimit - m_d->unmanagedHeapSize < 5 * unmanagedSize) - // try preventing running the GC all the time when we're just below the threshold limit and manage to collect just enough to do this one allocation - m_d->unmanagedHeapSizeGCLimit += std::max(std::size_t(8 * 1024), 5 * unmanagedSize); + // less than 25% full, lower limit + m_d->unmanagedHeapSizeGCLimit = qMax(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT, m_d->unmanagedHeapSizeGCLimit/2); didGCRun = true; } @@ -268,7 +286,7 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize // we use malloc for this MemoryManager::Data::LargeItem *item = static_cast<MemoryManager::Data::LargeItem *>( - malloc(Q_V4_PROFILE_ALLOC(m_d->engine, size + sizeof(MemoryManager::Data::LargeItem), + malloc(Q_V4_PROFILE_ALLOC(engine, size + sizeof(MemoryManager::Data::LargeItem), Profiling::LargeItem))); memset(item, 0, size + sizeof(MemoryManager::Data::LargeItem)); item->next = m_d->largeItems; @@ -304,10 +322,9 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize std::size_t allocSize = m_d->maxChunkSize*(size_t(1) << shift); allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); PageAllocation allocation = PageAllocation::allocate( - Q_V4_PROFILE_ALLOC(m_d->engine, allocSize, Profiling::HeapPage), + Q_V4_PROFILE_ALLOC(engine, allocSize, Profiling::HeapPage), OSAllocator::JSGCHeapPages); m_d->heapChunks.append(allocation); - std::sort(m_d->heapChunks.begin(), m_d->heapChunks.end()); header = reinterpret_cast<Data::ChunkHeader *>(allocation.base()); header->itemSize = int(size); @@ -339,7 +356,7 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize #ifdef V4_USE_VALGRIND VALGRIND_MEMPOOL_ALLOC(this, m, size); #endif - Q_V4_PROFILE_ALLOC(m_d->engine, size, Profiling::SmallItem); + Q_V4_PROFILE_ALLOC(engine, size, Profiling::SmallItem); ++m_d->allocCount[pos]; ++m_d->totalAlloc; @@ -353,21 +370,21 @@ static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase) { while (engine->jsStackTop > markBase) { Heap::Base *h = engine->popForGC(); - Q_ASSERT (h->gcGetVtable()->markObjects); - h->gcGetVtable()->markObjects(h, engine); + Q_ASSERT (h->vtable()->markObjects); + h->vtable()->markObjects(h, engine); } } void MemoryManager::mark() { - Value *markBase = m_d->engine->jsStackTop; - - m_d->engine->markObjects(); + Value *markBase = engine->jsStackTop; - m_persistentValues->mark(m_d->engine); + engine->markObjects(); collectFromJSStack(); + m_persistentValues->mark(engine); + // Preserve QObject ownership rules within JavaScript: A parent with c++ ownership // keeps all of its children alive in JavaScript. @@ -377,11 +394,9 @@ void MemoryManager::mark() for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { if (!(*it).isManaged()) continue; - if ((*it).managed()->d()->gcGetVtable() != QObjectWrapper::staticVTable()) + if (!(*it).as<QObjectWrapper>()) continue; QObjectWrapper *qobjectWrapper = static_cast<QObjectWrapper*>((*it).managed()); - if (!qobjectWrapper) - continue; QObject *qobject = qobjectWrapper->object(); if (!qobject) continue; @@ -397,27 +412,32 @@ void MemoryManager::mark() } if (keepAlive) - qobjectWrapper->mark(m_d->engine); + qobjectWrapper->mark(engine); - if (m_d->engine->jsStackTop >= m_d->engine->jsStackLimit) - drainMarkStack(m_d->engine, markBase); + if (engine->jsStackTop >= engine->jsStackLimit) + drainMarkStack(engine, markBase); } - drainMarkStack(m_d->engine, markBase); + drainMarkStack(engine, markBase); } void MemoryManager::sweep(bool lastSweep) { - if (m_weakValues) { - for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { - if (Managed *m = (*it).asManaged()) { - if (!m->markBit()) - (*it) = Primitive::undefinedValue(); - } - } + for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { + if (!(*it).isManaged()) + continue; + Managed *m = (*it).as<Managed>(); + if (m->markBit()) + continue; + // we need to call detroyObject on qobjectwrappers now, so that they can emit the destroyed + // signal before we start sweeping the heap + if (QObjectWrapper *qobjectWrapper = (*it).as<QObjectWrapper>()) + qobjectWrapper->destroyObject(lastSweep); + + (*it) = Primitive::undefinedValue(); } - if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = m_d->engine->m_multiplyWrappedQObjects) { + if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = engine->m_multiplyWrappedQObjects) { for (MultiplyWrappedQObjectMap::Iterator it = multiplyWrappedQObjects->begin(); it != multiplyWrappedQObjects->end();) { if (!it.value().isNullOrUndefined()) it = multiplyWrappedQObjects->erase(it); @@ -433,7 +453,7 @@ void MemoryManager::sweep(bool lastSweep) for (int i = 0; i < m_d->heapChunks.size(); ++i) { Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(m_d->heapChunks[i].base()); - chunkIsEmpty[i] = sweepChunk(header, &itemsInUse[header->itemSize >> 4], m_d->engine, &m_d->unmanagedHeapSize); + chunkIsEmpty[i] = sweepChunk(header, &itemsInUse[header->itemSize >> 4], engine, &m_d->unmanagedHeapSize); } QVector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin(); @@ -445,7 +465,7 @@ void MemoryManager::sweep(bool lastSweep) // Release that chunk if it could have been spared since the last GC run without any difference. if (chunkIsEmpty[i] && m_d->availableItems[pos] - decrease >= itemsInUse[pos]) { - Q_V4_PROFILE_DEALLOC(m_d->engine, 0, chunkIter->size(), Profiling::HeapPage); + Q_V4_PROFILE_DEALLOC(engine, 0, chunkIter->size(), Profiling::HeapPage); #ifdef V4_USE_VALGRIND VALGRIND_MEMPOOL_FREE(this, header); #endif @@ -473,30 +493,21 @@ void MemoryManager::sweep(bool lastSweep) i = i->next; continue; } - if (m->gcGetVtable()->destroy) - m->gcGetVtable()->destroy(m); + if (m->vtable()->destroy) + m->vtable()->destroy(m); *last = i->next; - free(Q_V4_PROFILE_DEALLOC(m_d->engine, i, i->size + sizeof(Data::LargeItem), + free(Q_V4_PROFILE_DEALLOC(engine, i, i->size + sizeof(Data::LargeItem), Profiling::LargeItem)); i = *last; } - GCDeletable *deletable = m_d->deletable; - m_d->deletable = 0; - while (deletable) { - GCDeletable *next = deletable->next; - deletable->lastCall = lastSweep; - delete deletable; - deletable = next; - } - // some execution contexts are allocated on the stack, make sure we clear their markBit as well if (!lastSweep) { - Heap::ExecutionContext *ctx = engine()->current; + QV4::ExecutionContext *ctx = engine->currentContext; while (ctx) { - ctx->clearMarkBit(); - ctx = ctx->parent; + ctx->d()->clearMarkBit(); + ctx = engine->parentContext(ctx); } } } @@ -530,9 +541,11 @@ void MemoryManager::runGC() int markTime = t.elapsed(); t.restart(); const size_t usedBefore = getUsedMem(); + const size_t largeItemsBefore = getLargeItemsMem(); int chunksBefore = m_d->heapChunks.size(); sweep(); const size_t usedAfter = getUsedMem(); + const size_t largeItemsAfter = getLargeItemsMem(); int sweepTime = t.elapsed(); qDebug() << "========== GC =========="; @@ -543,6 +556,9 @@ void MemoryManager::runGC() qDebug() << "Used memory after GC:" << usedAfter; qDebug() << "Freed up bytes:" << (usedBefore - usedAfter); qDebug() << "Released chunks:" << (chunksBefore - m_d->heapChunks.size()); + qDebug() << "Large item memory before GC:" << largeItemsBefore; + qDebug() << "Large item memory after GC:" << largeItemsAfter; + qDebug() << "Large item memory freed up:" << (largeItemsBefore - largeItemsAfter); qDebug() << "======== End GC ========"; } @@ -554,7 +570,7 @@ void MemoryManager::runGC() size_t MemoryManager::getUsedMem() const { size_t usedMem = 0; - for (QVector<PageAllocation>::const_iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) { + for (QVector<PageAllocation>::const_iterator i = m_d->heapChunks.cbegin(), ei = m_d->heapChunks.cend(); i != ei; ++i) { Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(i->base()); for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) { Heap::Base *m = reinterpret_cast<Heap::Base *>(item); @@ -590,19 +606,16 @@ void MemoryManager::growUnmanagedHeapSizeUsage(size_t delta) MemoryManager::~MemoryManager() { delete m_persistentValues; - delete m_weakValues; - m_weakValues = 0; sweep(/*lastSweep*/true); + + delete m_weakValues; #ifdef V4_USE_VALGRIND VALGRIND_DESTROY_MEMPOOL(this); #endif } -ExecutionEngine *MemoryManager::engine() const -{ - return m_d->engine; -} + void MemoryManager::dumpStats() const { @@ -618,12 +631,6 @@ void MemoryManager::dumpStats() const #endif // DETAILED_MM_STATS } -void MemoryManager::registerDeletable(GCDeletable *d) -{ - d->next = m_d->deletable; - m_d->deletable = d; -} - #ifdef DETAILED_MM_STATS void MemoryManager::willAllocate(std::size_t size) { @@ -638,13 +645,13 @@ void MemoryManager::willAllocate(std::size_t size) void MemoryManager::collectFromJSStack() const { - Value *v = m_d->engine->jsStackBase; - Value *top = m_d->engine->jsStackTop; + Value *v = engine->jsStackBase; + Value *top = engine->jsStackTop; while (v < top) { - Managed *m = v->asManaged(); + Managed *m = v->as<Managed>(); if (m && m->inUse()) // Skip pointers to already freed objects, they are bogus as well - m->mark(m_d->engine); + m->mark(engine); ++v; } } diff --git a/src/qml/jsruntime/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 7f6d2edba3..3543da0907 100644 --- a/src/qml/jsruntime/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -34,9 +34,21 @@ #ifndef QV4GC_H #define QV4GC_H -#include "qv4global_p.h" -#include "qv4value_inl_p.h" -#include "qv4scopedvalue_p.h" +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qv4global_p.h> +#include <private/qv4value_p.h> +#include <private/qv4scopedvalue_p.h> +#include <private/qv4object_p.h> //#define DETAILED_MM_STATS @@ -87,14 +99,153 @@ public: { size = align(size); Heap::Base *o = allocData(size, unmanagedSize); - o->vtable = ManagedType::staticVTable(); + o->setVtable(ManagedType::staticVTable()); return static_cast<typename ManagedType::Data *>(o); } + template <typename ObjectType> + typename ObjectType::Data *allocateObject(InternalClass *ic) + { + const int size = (sizeof(typename ObjectType::Data) + (sizeof(Value) - 1)) & ~(sizeof(Value) - 1); + typename ObjectType::Data *o = allocManaged<ObjectType>(size + ic->size*sizeof(Value)); + o->internalClass = ic; + o->inlineMemberSize = ic->size; + o->inlineMemberOffset = size/sizeof(Value); + return o; + } + + template <typename ObjectType> + typename ObjectType::Data *allocateObject() + { + InternalClass *ic = ObjectType::defaultInternalClass(engine); + const int size = (sizeof(typename ObjectType::Data) + (sizeof(Value) - 1)) & ~(sizeof(Value) - 1); + typename ObjectType::Data *o = allocManaged<ObjectType>(size + ic->size*sizeof(Value)); + Object *prototype = ObjectType::defaultPrototype(engine); + o->internalClass = ic; + o->prototype = prototype->d(); + o->inlineMemberSize = ic->size; + o->inlineMemberOffset = size/sizeof(Value); + return o; + } + + template <typename ManagedType, typename Arg1> + typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 arg1) + { + Scope scope(engine); + Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data), unmanagedSize)); + (void)new (t->d()) typename ManagedType::Data(this, arg1); + return t->d(); + } + + template <typename ObjectType> + typename ObjectType::Data *allocObject(InternalClass *ic) + { + Scope scope(engine); + Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); + (void)new (t->d()) typename ObjectType::Data(); + return t->d(); + } + + template <typename ObjectType> + typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype) + { + Scope scope(engine); + Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); + t->d()->prototype = prototype->d(); + (void)new (t->d()) typename ObjectType::Data(); + return t->d(); + } + + template <typename ObjectType, typename Arg1> + typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1) + { + Scope scope(engine); + Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); + t->d()->prototype = prototype->d(); + (void)new (t->d()) typename ObjectType::Data(arg1); + return t->d(); + } + + template <typename ObjectType, typename Arg1, typename Arg2> + typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1, Arg2 arg2) + { + Scope scope(engine); + Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); + t->d()->prototype = prototype->d(); + (void)new (t->d()) typename ObjectType::Data(arg1, arg2); + return t->d(); + } + + template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3> + typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + Scope scope(engine); + Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); + t->d()->prototype = prototype->d(); + (void)new (t->d()) typename ObjectType::Data(arg1, arg2, arg3); + return t->d(); + } + + template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3, typename Arg4> + typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + Scope scope(engine); + Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); + t->d()->prototype = prototype->d(); + (void)new (t->d()) typename ObjectType::Data(arg1, arg2, arg3, arg4); + return t->d(); + } + + template <typename ObjectType> + typename ObjectType::Data *allocObject() + { + Scope scope(engine); + Scoped<ObjectType> t(scope, allocateObject<ObjectType>()); + (void)new (t->d()) typename ObjectType::Data(); + return t->d(); + } + + template <typename ObjectType, typename Arg1> + typename ObjectType::Data *allocObject(Arg1 arg1) + { + Scope scope(engine); + Scoped<ObjectType> t(scope, allocateObject<ObjectType>()); + (void)new (t->d()) typename ObjectType::Data(arg1); + return t->d(); + } + + template <typename ObjectType, typename Arg1, typename Arg2> + typename ObjectType::Data *allocObject(Arg1 arg1, Arg2 arg2) + { + Scope scope(engine); + Scoped<ObjectType> t(scope, allocateObject<ObjectType>()); + (void)new (t->d()) typename ObjectType::Data(arg1, arg2); + return t->d(); + } + + template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3> + typename ObjectType::Data *allocObject(Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + Scope scope(engine); + Scoped<ObjectType> t(scope, allocateObject<ObjectType>()); + (void)new (t->d()) typename ObjectType::Data(arg1, arg2, arg3); + return t->d(); + } + + template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3, typename Arg4> + typename ObjectType::Data *allocObject(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + Scope scope(engine); + Scoped<ObjectType> t(scope, allocateObject<ObjectType>()); + (void)new (t->d()) typename ObjectType::Data(arg1, arg2, arg3, arg4); + return t->d(); + } + + template <typename ManagedType> typename ManagedType::Data *alloc() { - Scope scope(engine()); + Scope scope(engine); Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data))); (void)new (t->d()) typename ManagedType::Data(); return t->d(); @@ -103,25 +254,16 @@ public: template <typename ManagedType, typename Arg1> typename ManagedType::Data *alloc(Arg1 arg1) { - Scope scope(engine()); + Scope scope(engine); Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data))); (void)new (t->d()) typename ManagedType::Data(arg1); return t->d(); } - template <typename ManagedType, typename Arg1> - typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 arg1) - { - Scope scope(engine()); - Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data), unmanagedSize)); - (void)new (t->d()) typename ManagedType::Data(this, arg1); - return t->d(); - } - template <typename ManagedType, typename Arg1, typename Arg2> typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2) { - Scope scope(engine()); + Scope scope(engine); Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data))); (void)new (t->d()) typename ManagedType::Data(arg1, arg2); return t->d(); @@ -130,7 +272,7 @@ public: template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3> typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3) { - Scope scope(engine()); + Scope scope(engine); Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data))); (void)new (t->d()) typename ManagedType::Data(arg1, arg2, arg3); return t->d(); @@ -139,7 +281,7 @@ public: template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3, typename Arg4> typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { - Scope scope(engine()); + Scope scope(engine); Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data))); (void)new (t->d()) typename ManagedType::Data(arg1, arg2, arg3, arg4); return t->d(); @@ -148,7 +290,7 @@ public: template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { - Scope scope(engine()); + Scope scope(engine); Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data))); (void)new (t->d()) typename ManagedType::Data(arg1, arg2, arg3, arg4, arg5); return t->d(); @@ -158,12 +300,8 @@ public: void setGCBlocked(bool blockGC); void runGC(); - ExecutionEngine *engine() const; - void dumpStats() const; - void registerDeletable(GCDeletable *d); - size_t getUsedMem() const; size_t getAllocatedMem() const; size_t getLargeItemsMem() const; @@ -184,9 +322,9 @@ private: void mark(); void sweep(bool lastSweep = false); -protected: - QScopedPointer<Data> m_d; public: + QV4::ExecutionEngine *engine; + QScopedPointer<Data> m_d; PersistentValueStorage *m_persistentValues; PersistentValueStorage *m_weakValues; }; diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index 71b6978983..96b2b24298 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -506,7 +506,24 @@ bool Parser::parse(int startToken) token_buffer[0].token = startToken; first_token = &token_buffer[0]; - last_token = &token_buffer[1]; + if (startToken == T_FEED_JS_PROGRAM && !lexer->qmlMode()) { + Directives ignoreDirectives; + Directives *directives = driver->directives(); + if (!directives) + directives = &ignoreDirectives; + DiagnosticMessage error; + if (!lexer->scanDirectives(directives, &error)) { + diagnostic_messages.append(error); + return false; + } + token_buffer[1].token = lexer->tokenKind(); + token_buffer[1].dval = lexer->tokenValue(); + token_buffer[1].loc = location(lexer); + token_buffer[1].spell = lexer->tokenSpell(); + last_token = &token_buffer[2]; + } else { + last_token = &token_buffer[1]; + } tos = -1; program = 0; diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index fa6b5d2488..3987f6093a 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -103,11 +103,11 @@ namespace QQmlJS { namespace AST { -template <typename _T1, typename _T2> -_T1 cast(_T2 *ast) +template <typename T1, typename T2> +T1 cast(T2 *ast) { - if (ast && ast->kind == static_cast<_T1>(0)->K) - return static_cast<_T1>(ast); + if (ast && ast->kind == static_cast<T1>(0)->K) + return static_cast<T1>(ast); return 0; } diff --git a/src/qml/parser/qqmljsglobal_p.h b/src/qml/parser/qqmljsglobal_p.h index fe2cbe7d1d..48ba2b034b 100644 --- a/src/qml/parser/qqmljsglobal_p.h +++ b/src/qml/parser/qqmljsglobal_p.h @@ -33,6 +33,17 @@ #ifndef QQMLJSGLOBAL_P_H #define QQMLJSGLOBAL_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QtCore/qglobal.h> #ifdef QT_CREATOR diff --git a/src/qml/parser/qqmljsmemorypool_p.h b/src/qml/parser/qqmljsmemorypool_p.h index 16927251c7..ae9f1d8257 100644 --- a/src/qml/parser/qqmljsmemorypool_p.h +++ b/src/qml/parser/qqmljsmemorypool_p.h @@ -102,29 +102,7 @@ public: _ptr = _end = 0; } - template <typename _Tp> _Tp *New() { return new (this->allocate(sizeof(_Tp))) _Tp(); } - - template <typename PoolContentType, typename Visitor> - void visitManagedPool(Visitor &visitor) - { - for (int i = 0; i <= _blockCount; ++i) { - char *p = _blocks[i]; - char *end = p + BLOCK_SIZE; - if (i == _blockCount) { - Q_ASSERT(_ptr <= end); - end = _ptr; - } - - Q_ASSERT(p <= end); - - const qptrdiff increment = (sizeof(PoolContentType) + 7) & ~7; - - while (p + increment <= end) { - visitor(reinterpret_cast<PoolContentType*>(p)); - p += increment; - } - } - } + template <typename Tp> Tp *New() { return new (this->allocate(sizeof(Tp))) Tp(); } private: void *allocate_helper(size_t size) diff --git a/src/qml/qml.pro b/src/qml/qml.pro index 6e06fb42ef..d75262bf0b 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -8,6 +8,9 @@ win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS win32:!wince*:!winrt:LIBS += -lshell32 solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 +# Ensure this gcc optimization is switched off for mips platforms to avoid trouble with JIT. +gcc:isEqual(QT_ARCH, "mips"): QMAKE_CXXFLAGS += -fno-reorder-blocks + MODULE_PLUGIN_TYPES = \ qmltooling @@ -33,6 +36,7 @@ HEADERS += qtqmlglobal.h \ #modules include(util/util.pri) +include(memory/memory.pri) include(parser/parser.pri) include(compiler/compiler.pri) include(jsapi/jsapi.pri) diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri index 4b109107f9..51697b0aff 100644 --- a/src/qml/qml/ftw/ftw.pri +++ b/src/qml/qml/ftw/ftw.pri @@ -4,7 +4,6 @@ HEADERS += \ $$PWD/qpodvector_p.h \ $$PWD/qhashedstring_p.h \ $$PWD/qqmlrefcount_p.h \ - $$PWD/qqmlpool_p.h \ $$PWD/qfieldlist_p.h \ $$PWD/qhashfield_p.h \ $$PWD/qqmlthread_p.h \ @@ -20,7 +19,6 @@ HEADERS += \ SOURCES += \ $$PWD/qintrusivelist.cpp \ $$PWD/qhashedstring.cpp \ - $$PWD/qqmlpool.cpp \ $$PWD/qqmlthread.cpp \ # mirrors logic in $$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri diff --git a/src/qml/qml/ftw/qqmlpool.cpp b/src/qml/qml/ftw/qqmlpool.cpp deleted file mode 100644 index b86dcba107..0000000000 --- a/src/qml/qml/ftw/qqmlpool.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlpool_p.h" -#include <stdlib.h> - -#ifdef Q_OS_QNX -#include <malloc.h> -#endif - -// #define POOL_DEBUG - -QT_BEGIN_NAMESPACE - -void QQmlPool::newpage() -{ -#ifdef POOL_DEBUG - qWarning("QQmlPool: Allocating page"); -#endif - - Page *page = (Page *)malloc(sizeof(Page)); - page->header.next = _page; - page->header.free = page->memory; - _page = page; -} - -void QQmlPool::clear() -{ -#ifdef POOL_DEBUG - int count = 0; -#endif - - Class *c = _classList; - while (c) { - Class *n = c->_next; - c->_destroy(c); -#ifdef POOL_DEBUG - ++count; -#endif - c = n; - } - -#ifdef POOL_DEBUG - qWarning("QQmlPool: Destroyed %d objects", count); -#endif - - Page *p = _page; - while (p) { - Page *n = p->header.next; - free(p); - p = n; - } - - _classList = 0; - _page = 0; -} - - -QT_END_NAMESPACE diff --git a/src/qml/qml/ftw/qqmlpool_p.h b/src/qml/qml/ftw/qqmlpool_p.h deleted file mode 100644 index 4956cd81d8..0000000000 --- a/src/qml/qml/ftw/qqmlpool_p.h +++ /dev/null @@ -1,270 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLPOOL_P_H -#define QQMLPOOL_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qv4global_p.h> -#include <QtCore/qstring.h> -#include <QtCore/qurl.h> - -QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlPool -{ -public: - // The class has a destructor that needs to be called - class Class { - public: - inline QQmlPool *pool() const; - - private: - void *operator new(size_t); - void *operator new(size_t, void *m) { return m; } - friend class QQmlPool; - - QQmlPool *_pool; - Class *_next; - void (*_destroy)(Class *); - }; - - // The class is plain old data and no destructor needs to - // be called - class POD { - public: - inline QQmlPool *pool() const; - - private: - void *operator new(size_t); - void *operator new(size_t, void *m) { return m; } - friend class QQmlPool; - - QQmlPool *_pool; - }; - - inline QQmlPool(); - inline ~QQmlPool(); - - void clear(); - - template<typename T> - inline T *New(); - template<typename T> - inline T *NewRaw(); - template<typename T> - inline T *NewRawArray(int length); - - inline QString *NewString(const QString &); - inline QByteArray *NewByteArray(const QByteArray &); - inline QUrl *NewUrl(const QUrl &); - - template<typename T> - struct List { - List() : m_length(0), m_data(0) {} - List(const List &o) : m_length(o.m_length), m_data(o.m_data) {} - List &operator=(const List &o) { - m_length = o.m_length; - m_data = o.m_data; - return *this; - } - - int count() const { - return m_length; - } - int length() const { - return m_length; - } - const T &at(int index) const { - Q_ASSERT(index < m_length); - return m_data[index]; - }; - T &operator[](int index) { - Q_ASSERT(index < m_length); - return m_data[index]; - }; - const T *data() const { return m_data; } - private: - friend class QQmlPool; - List(T *d, int l) : m_length(l), m_data(d) {} - int m_length; - T *m_data; - }; - - template<typename T> - inline List<T> NewRawList(int length); - -private: - struct StringClass : public QString, public Class { - }; - struct ByteArrayClass : public QByteArray, public Class { - }; - struct UrlClass : public QUrl, public Class { - }; - - inline void *allocate(int size); - void newpage(); - - template<typename T> - inline void initialize(POD *); - template<typename T> - inline void initialize(Class *); - template<typename T> - static void destroy(Class *c); - - struct Page { - struct Header { - Page *next; - char *free; - } header; - - static const int pageSize = 4 * 4096 - sizeof(Header); - - char memory[pageSize]; - }; - - Page *_page; - Class *_classList; -}; - -QQmlPool::QQmlPool() -: _page(0), _classList(0) -{ -} - -QQmlPool::~QQmlPool() -{ - clear(); -} - -template<typename T> -T *QQmlPool::New() -{ - T *rv = new (allocate(sizeof(T))) T; - initialize<T>(rv); - rv->_pool = this; - return rv; -} - -template<typename T> -T *QQmlPool::NewRaw() -{ - return (T*)allocate(sizeof(T)); -} - -template<typename T> -T *QQmlPool::NewRawArray(int length) -{ - return (T*)allocate(length * sizeof(T)); -} - -template<typename T> -QQmlPool::List<T> QQmlPool::NewRawList(int length) -{ - return List<T>(NewRawArray<T>(length), length); -} - -QString *QQmlPool::NewString(const QString &s) -{ - QString *rv = New<StringClass>(); - *rv = s; - return rv; -} - -QByteArray *QQmlPool::NewByteArray(const QByteArray &s) -{ - QByteArray *rv = New<ByteArrayClass>(); - *rv = s; - return rv; -} - -QUrl *QQmlPool::NewUrl(const QUrl &s) -{ - QUrl *rv = New<UrlClass>(); - *rv = s; - return rv; -} - -void *QQmlPool::allocate(int size) -{ - if (!_page || (_page->header.free + size) > (_page->memory + Page::pageSize)) - newpage(); - - void *rv = _page->header.free; - _page->header.free += size + ((8 - size) & 7); // ensure 8 byte alignment; - return rv; -} - -template<typename T> -void QQmlPool::initialize(QQmlPool::POD *) -{ -} - -template<typename T> -void QQmlPool::initialize(QQmlPool::Class *c) -{ - c->_next = _classList; - c->_destroy = &destroy<T>; - _classList = c; -} - -template<typename T> -void QQmlPool::destroy(Class *c) -{ - static_cast<T *>(c)->~T(); -} - -QQmlPool *QQmlPool::Class::pool() const -{ - return _pool; -} - -QQmlPool *QQmlPool::POD::pool() const -{ - return _pool; -} - -QT_END_NAMESPACE - -#endif // QQMLPOOL_P_H - diff --git a/src/qml/qml/ftw/qqmlrefcount_p.h b/src/qml/qml/ftw/qqmlrefcount_p.h index 59ed77b580..ba2ca43990 100644 --- a/src/qml/qml/ftw/qqmlrefcount_p.h +++ b/src/qml/qml/ftw/qqmlrefcount_p.h @@ -72,13 +72,16 @@ template<class T> class QQmlRefPointer { public: + enum Mode { + AddRef, + Adopt + }; inline QQmlRefPointer(); - inline QQmlRefPointer(T *); + inline QQmlRefPointer(T *, Mode m = AddRef); inline QQmlRefPointer(const QQmlRefPointer<T> &); inline ~QQmlRefPointer(); inline QQmlRefPointer<T> &operator=(const QQmlRefPointer<T> &o); - inline QQmlRefPointer<T> &operator=(T *); inline bool isNull() const { return !o; } @@ -87,7 +90,7 @@ public: inline operator T*() const { return o; } inline T* data() const { return o; } - inline QQmlRefPointer<T> &take(T *); + inline QQmlRefPointer<T> &adopt(T *); private: T *o; @@ -133,10 +136,11 @@ QQmlRefPointer<T>::QQmlRefPointer() } template<class T> -QQmlRefPointer<T>::QQmlRefPointer(T *o) +QQmlRefPointer<T>::QQmlRefPointer(T *o, Mode m) : o(o) { - if (o) o->addref(); + if (m == AddRef && o) + o->addref(); } template<class T> @@ -161,21 +165,12 @@ QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(const QQmlRefPointer<T> &other) return *this; } -template<class T> -QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(T *other) -{ - if (other) other->addref(); - if (o) o->release(); - o = other; - return *this; -} - /*! Takes ownership of \a other. take() does *not* add a reference, as it assumes ownership of the callers reference of other. */ template<class T> -QQmlRefPointer<T> &QQmlRefPointer<T>::take(T *other) +QQmlRefPointer<T> &QQmlRefPointer<T>::adopt(T *other) { if (o) o->release(); o = other; diff --git a/src/qml/qml/ftw/qqmlthread.cpp b/src/qml/qml/ftw/qqmlthread.cpp index 4addcd9e58..62f6f76a7e 100644 --- a/src/qml/qml/ftw/qqmlthread.cpp +++ b/src/qml/qml/ftw/qqmlthread.cpp @@ -377,4 +377,31 @@ void QQmlThread::internalPostMethodToMain(Message *message) d->unlock(); } +void QQmlThread::waitForNextMessage() +{ + Q_ASSERT(!isThisThread()); + d->lock(); + Q_ASSERT(d->m_mainThreadWaiting == false); + + d->m_mainThreadWaiting = true; + + if (d->mainSync || !d->threadList.isEmpty()) { + if (d->mainSync) { + QQmlThread::Message *message = d->mainSync; + unlock(); + message->call(this); + delete message; + lock(); + d->mainSync = 0; + wakeOne(); + } else { + d->wait(); + } + } + + d->m_mainThreadWaiting = false; + d->unlock(); +} + + QT_END_NAMESPACE diff --git a/src/qml/qml/ftw/qqmlthread_p.h b/src/qml/qml/ftw/qqmlthread_p.h index 86d7d2cf19..73658536ac 100644 --- a/src/qml/qml/ftw/qqmlthread_p.h +++ b/src/qml/qml/ftw/qqmlthread_p.h @@ -108,6 +108,8 @@ public: template<typename T, typename T2, class V, class V2, class O> inline void postMethodToMain(void (O::*Member)(V, V2), const T &, const T2 &); + void waitForNextMessage(); + protected: virtual void startupThread(); virtual void shutdownThread(); diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index e733bcec05..4d84cc82ae 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -23,7 +23,6 @@ SOURCES += \ $$PWD/qqmlvaluetype.cpp \ $$PWD/qqmlaccessors.cpp \ $$PWD/qqmlxmlhttprequest.cpp \ - $$PWD/qqmlwatcher.cpp \ $$PWD/qqmlcleanup.cpp \ $$PWD/qqmlpropertycache.cpp \ $$PWD/qqmlnotifier.cpp \ @@ -35,7 +34,6 @@ SOURCES += \ $$PWD/qqmlimport.cpp \ $$PWD/qqmllist.cpp \ $$PWD/qqmllocale.cpp \ - $$PWD/qqmlabstractexpression.cpp \ $$PWD/qqmljavascriptexpression.cpp \ $$PWD/qqmlabstractbinding.cpp \ $$PWD/qqmlvaluetypeproxybinding.cpp \ @@ -92,7 +90,6 @@ HEADERS += \ $$PWD/qqmlvaluetype_p.h \ $$PWD/qqmlaccessors_p.h \ $$PWD/qqmlxmlhttprequest_p.h \ - $$PWD/qqmlwatcher_p.h \ $$PWD/qqmlcleanup_p.h \ $$PWD/qqmlpropertycache_p.h \ $$PWD/qqmlnotifier_p.h \ @@ -107,7 +104,6 @@ HEADERS += \ $$PWD/qqmlscriptstring_p.h \ $$PWD/qqmllocale_p.h \ $$PWD/qqmlcomponentattached_p.h \ - $$PWD/qqmlabstractexpression_p.h \ $$PWD/qqmljavascriptexpression_p.h \ $$PWD/qqmlabstractbinding_p.h \ $$PWD/qqmlvaluetypeproxybinding_p.h \ diff --git a/src/qml/qml/qqmlabstractbinding.cpp b/src/qml/qml/qqmlabstractbinding.cpp index 40c8f451b4..4e0763e95a 100644 --- a/src/qml/qml/qqmlabstractbinding.cpp +++ b/src/qml/qml/qqmlabstractbinding.cpp @@ -39,24 +39,19 @@ QT_BEGIN_NAMESPACE -extern QQmlAbstractBinding::VTable QQmlBinding_vtable; -extern QQmlAbstractBinding::VTable QQmlValueTypeProxyBinding_vtable; - -QQmlAbstractBinding::VTable *QQmlAbstractBinding::vTables[] = { - &QQmlBinding_vtable, - &QQmlValueTypeProxyBinding_vtable -}; - -QQmlAbstractBinding::QQmlAbstractBinding(BindingType bt) - : m_nextBindingPtr(bt) +QQmlAbstractBinding::QQmlAbstractBinding() + : m_targetIndex(-1) { + Q_ASSERT(!isAddedToObject()); } QQmlAbstractBinding::~QQmlAbstractBinding() { - Q_ASSERT(isAddedToObject() == false); - Q_ASSERT(nextBinding() == 0); - Q_ASSERT(*m_mePtr == 0); + Q_ASSERT(!ref); + Q_ASSERT(!isAddedToObject()); + + if (m_nextBinding.data() && !m_nextBinding->ref.deref()) + delete m_nextBinding.data(); } /*! @@ -72,40 +67,45 @@ void QQmlAbstractBinding::addToObject() Q_ASSERT(!nextBinding()); Q_ASSERT(isAddedToObject() == false); - QObject *obj = object(); + QObject *obj = targetObject(); Q_ASSERT(obj); QQmlData *data = QQmlData::get(obj, true); int coreIndex; - if (QQmlPropertyData::decodeValueTypePropertyIndex(propertyIndex(), &coreIndex) != -1) { + if (QQmlPropertyData::decodeValueTypePropertyIndex(targetPropertyIndex(), &coreIndex) != -1) { // Value type // Find the value type proxy (if there is one) QQmlValueTypeProxyBinding *proxy = 0; if (data->hasBindingBit(coreIndex)) { QQmlAbstractBinding *b = data->bindings; - while (b && b->propertyIndex() != coreIndex) + while (b && b->targetPropertyIndex() != coreIndex) b = b->nextBinding(); - Q_ASSERT(b && b->bindingType() == QQmlAbstractBinding::ValueTypeProxy); + Q_ASSERT(b && b->isValueTypeProxy()); proxy = static_cast<QQmlValueTypeProxyBinding *>(b); } if (!proxy) { proxy = new QQmlValueTypeProxyBinding(obj, coreIndex); - Q_ASSERT(proxy->propertyIndex() == coreIndex); - Q_ASSERT(proxy->object() == obj); + Q_ASSERT(proxy->targetPropertyIndex() == coreIndex); + Q_ASSERT(proxy->targetObject() == obj); proxy->addToObject(); } - setNextBinding(proxy->m_bindings); + setNextBinding(proxy->m_bindings.data()); proxy->m_bindings = this; } else { setNextBinding(data->bindings); + if (data->bindings) { + data->bindings->ref.deref(); + Q_ASSERT(data->bindings->ref.refCount > 0); + } data->bindings = this; + ref.ref(); data->setBindingBit(obj, coreIndex); } @@ -118,95 +118,81 @@ Remove the binding from the object. */ void QQmlAbstractBinding::removeFromObject() { - if (isAddedToObject()) { - QObject *obj = object(); - QQmlData *data = QQmlData::get(obj, false); - Q_ASSERT(data); - - int coreIndex; - if (QQmlPropertyData::decodeValueTypePropertyIndex(propertyIndex(), &coreIndex) != -1) { - - // Find the value type binding - QQmlAbstractBinding *vtbinding = data->bindings; - while (vtbinding->propertyIndex() != coreIndex) { - vtbinding = vtbinding->nextBinding(); - Q_ASSERT(vtbinding); - } - Q_ASSERT(vtbinding->bindingType() == QQmlAbstractBinding::ValueTypeProxy); - - QQmlValueTypeProxyBinding *vtproxybinding = - static_cast<QQmlValueTypeProxyBinding *>(vtbinding); - - QQmlAbstractBinding *binding = vtproxybinding->m_bindings; - if (binding == this) { - vtproxybinding->m_bindings = nextBinding(); - } else { - while (binding->nextBinding() != this) { - binding = binding->nextBinding(); - Q_ASSERT(binding); - } - binding->setNextBinding(nextBinding()); - } - - // Value type - we don't remove the proxy from the object. It will sit their happily - // doing nothing until it is removed by a write, a binding change or it is reused - // to hold more sub-bindings. + if (!isAddedToObject()) + return; - } else { + setAddedToObject(false); - if (data->bindings == this) { - data->bindings = nextBinding(); - } else { - QQmlAbstractBinding *binding = data->bindings; - while (binding->nextBinding() != this) { - binding = binding->nextBinding(); - Q_ASSERT(binding); - } - binding->setNextBinding(nextBinding()); - } - - data->clearBindingBit(coreIndex); - } + QObject *obj = targetObject(); + QQmlData *data = QQmlData::get(obj, false); + Q_ASSERT(data); - setNextBinding(0); - setAddedToObject(false); - } -} + QQmlAbstractBinding::Ptr next; + next = nextBinding(); + setNextBinding(0); -void QQmlAbstractBinding::printBindingLoopError(QQmlProperty &prop) -{ - qmlInfo(prop.object()) << QString(QLatin1String("Binding loop detected for property \"%1\"")).arg(prop.name()); -} + int coreIndex; + if (QQmlPropertyData::decodeValueTypePropertyIndex(targetPropertyIndex(), &coreIndex) != -1) { + // Find the value type binding + QQmlAbstractBinding *vtbinding = data->bindings; + while (vtbinding->targetPropertyIndex() != coreIndex) { + vtbinding = vtbinding->nextBinding(); + Q_ASSERT(vtbinding); + } + Q_ASSERT(vtbinding->isValueTypeProxy()); -static void bindingDummyDeleter(QQmlAbstractBinding *) -{ -} + QQmlValueTypeProxyBinding *vtproxybinding = + static_cast<QQmlValueTypeProxyBinding *>(vtbinding); -QQmlAbstractBinding::Pointer QQmlAbstractBinding::weakPointer() -{ - if (m_mePtr.value().isNull()) - m_mePtr.value() = QSharedPointer<QQmlAbstractBinding>(this, bindingDummyDeleter); + QQmlAbstractBinding *binding = vtproxybinding->m_bindings.data(); + if (binding == this) { + vtproxybinding->m_bindings = next; + } else { + while (binding->nextBinding() != this) { + binding = binding->nextBinding(); + Q_ASSERT(binding); + } + binding->setNextBinding(next.data()); + } - return m_mePtr.value().toWeakRef(); -} + // Value type - we don't remove the proxy from the object. It will sit their happily + // doing nothing until it is removed by a write, a binding change or it is reused + // to hold more sub-bindings. + return; + } -void QQmlAbstractBinding::clear() -{ - if (!m_mePtr.isNull()) { - **m_mePtr = 0; - m_mePtr = 0; + if (data->bindings == this) { + if (next.data()) + next->ref.ref(); + data->bindings = next.data(); + if (!ref.deref()) + delete this; + } else { + QQmlAbstractBinding *binding = data->bindings; + while (binding->nextBinding() != this) { + binding = binding->nextBinding(); + Q_ASSERT(binding); + } + binding->setNextBinding(next.data()); } + + data->clearBindingBit(coreIndex); } -void QQmlAbstractBinding::default_retargetBinding(QQmlAbstractBinding *, QObject *, int) +void QQmlAbstractBinding::printBindingLoopError(QQmlProperty &prop) { - qFatal("QQmlAbstractBinding::retargetBinding() called on illegal binding."); + qmlInfo(prop.object()) << QString(QLatin1String("Binding loop detected for property \"%1\"")).arg(prop.name()); } -QString QQmlAbstractBinding::default_expression(const QQmlAbstractBinding *) +QString QQmlAbstractBinding::expression() const { return QLatin1String("<Unknown>"); } +bool QQmlAbstractBinding::isValueTypeProxy() const +{ + return false; +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h index b5d8181ca5..dd14301aa9 100644 --- a/src/qml/qml/qqmlabstractbinding_p.h +++ b/src/qml/qml/qqmlabstractbinding_p.h @@ -46,6 +46,7 @@ // #include <QtCore/qsharedpointer.h> +#include <QtCore/qshareddata.h> #include <private/qtqmlglobal_p.h> #include <private/qqmlproperty_p.h> #include <private/qpointervaluepair_p.h> @@ -56,162 +57,83 @@ class QQmlObjectCreator; class Q_QML_PRIVATE_EXPORT QQmlAbstractBinding { +protected: + QQmlAbstractBinding(); public: - enum DestroyMode { - - // The binding should disconnect itself upon destroy - DisconnectBinding, - - // The binding doesn't need to disconnect itself, but it can if it wants to. - // - // This is used in QQmlData::destroyed() - at the point at which the bindings are - // destroyed, the notifiers are already disconnected, so no need to disconnect each - // binding again. - // - // Bindings can use this flag to speed up destruction, especially for v4 bindings - // disconnecting a single binding might be slow. - KeepBindingConnected - }; - - struct VTable { - void (*destroy)(QQmlAbstractBinding *, DestroyMode destroyMode); - QString (*expression)(const QQmlAbstractBinding *); - int (*propertyIndex)(const QQmlAbstractBinding *); - QObject *(*object)(const QQmlAbstractBinding *); - void (*setEnabled)(QQmlAbstractBinding *, bool, QQmlPropertyPrivate::WriteFlags); - void (*update)(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags); - void (*retargetBinding)(QQmlAbstractBinding *, QObject *, int); - }; - - typedef QWeakPointer<QQmlAbstractBinding> Pointer; + virtual ~QQmlAbstractBinding(); - enum BindingType { Binding = 0, ValueTypeProxy = 1 }; - inline BindingType bindingType() const; + typedef QExplicitlySharedDataPointer<QQmlAbstractBinding> Ptr; - // Destroy the binding. Use this instead of calling delete. - // Bindings are free to implement their own memory management, so the delete operator is - // not necessarily safe. The default implementation clears the binding, removes it from - // the object and calls delete. - void destroy(DestroyMode destroyMode = DisconnectBinding) - { vtable()->destroy(this, destroyMode); } + virtual QString expression() const; - QString expression() const { return vtable()->expression(this); } + virtual bool isValueTypeProxy() const; // Should return the encoded property index for the binding. Should return this value // even if the binding is not enabled or added to an object. // Encoding is: coreIndex | (valueTypeIndex << 16) - int propertyIndex() const { return vtable()->propertyIndex(this); } + int targetPropertyIndex() const { return m_targetIndex; } // Should return the object for the binding. Should return this object even if the // binding is not enabled or added to the object. - QObject *object() const { return vtable()->object(this); } - - void setEnabled(bool e) { setEnabled(e, QQmlPropertyPrivate::DontRemoveBinding); } - void setEnabled(bool e, QQmlPropertyPrivate::WriteFlags f) { vtable()->setEnabled(this, e, f); } + QObject *targetObject() const { return m_target.data(); } - void update() { update(QQmlPropertyPrivate::DontRemoveBinding); } - void update(QQmlPropertyPrivate::WriteFlags f) { vtable()->update(this, f); } + virtual void setEnabled(bool e, QQmlPropertyPrivate::WriteFlags f = QQmlPropertyPrivate::DontRemoveBinding) = 0; void addToObject(); void removeFromObject(); - static inline Pointer getPointer(QQmlAbstractBinding *p); static void printBindingLoopError(QQmlProperty &prop); - // Default implementation for some VTable functions - template<typename T> - static void default_destroy(QQmlAbstractBinding *, DestroyMode); - static QString default_expression(const QQmlAbstractBinding *); - static void default_retargetBinding(QQmlAbstractBinding *, QObject *, int); - -protected: - QQmlAbstractBinding(BindingType); - ~QQmlAbstractBinding(); - void clear(); + inline QQmlAbstractBinding *nextBinding() const; - // Called by QQmlPropertyPrivate to "move" a binding to a different property. - // This is only used for alias properties. The default implementation qFatal()'s - // to ensure that the method is never called for binding types that don't support it. - void retargetBinding(QObject *o, int i) { vtable()->retargetBinding(this, o, i); } -private: - Pointer weakPointer(); + struct RefCount { + RefCount() : refCount(0) {} + int refCount; + void ref() { ++refCount; } + int deref() { return --refCount; } + operator int() const { return refCount; } + }; + RefCount ref; +protected: friend class QQmlData; - friend class QQmlComponentPrivate; friend class QQmlValueTypeProxyBinding; - friend class QQmlPropertyPrivate; - friend class QtSharedPointer::ExternalRefCount<QQmlAbstractBinding>; - friend class QV4Bindings; friend class QQmlObjectCreator; - typedef QSharedPointer<QQmlAbstractBinding> SharedPointer; - // To save memory, we also store the rarely used weakPointer() instance in here - // We also use the flag bits: - // m_mePtr.flag1: added to object - QPointerValuePair<QQmlAbstractBinding*, SharedPointer> m_mePtr; - inline void setAddedToObject(bool v); inline bool isAddedToObject() const; - inline QQmlAbstractBinding *nextBinding() const; inline void setNextBinding(QQmlAbstractBinding *); + int m_targetIndex; + QFlagPointer<QObject> m_target; // Pointer to the next binding in the linked list of bindings. - // Being a pointer, the address is always aligned to at least 4 bytes, which means the last two - // bits of the pointer are free to be used for something else. They are used to store the binding - // type. The binding type serves as an index into the static vTables array, which is used instead - // of a compiler-generated vTable. Instead of virtual functions, pointers to static functions in - // the vTables array are used for dispatching. - // This saves a compiler-generated pointer to a compiler-generated vTable, and thus reduces - // the binding object size by sizeof(void*). - qintptr m_nextBindingPtr; - - static VTable *vTables[]; - inline const VTable *vtable() const { return vTables[bindingType()]; } + QFlagPointer<QQmlAbstractBinding> m_nextBinding; }; -QQmlAbstractBinding::Pointer -QQmlAbstractBinding::getPointer(QQmlAbstractBinding *p) -{ - return p ? p->weakPointer() : Pointer(); -} - void QQmlAbstractBinding::setAddedToObject(bool v) { - m_mePtr.setFlagValue(v); + m_nextBinding.setFlagValue(v); } bool QQmlAbstractBinding::isAddedToObject() const { - return m_mePtr.flag(); + return m_nextBinding.flag(); } QQmlAbstractBinding *QQmlAbstractBinding::nextBinding() const { - return (QQmlAbstractBinding *)(m_nextBindingPtr & ~0x3); + return m_nextBinding.data(); } void QQmlAbstractBinding::setNextBinding(QQmlAbstractBinding *b) { - m_nextBindingPtr = qintptr(b) | (m_nextBindingPtr & 0x3); -} - -QQmlAbstractBinding::BindingType QQmlAbstractBinding::bindingType() const -{ - return (BindingType)(m_nextBindingPtr & 0x3); -} - -template<typename T> -void QQmlAbstractBinding::default_destroy(QQmlAbstractBinding *This, DestroyMode mode) -{ - // Assume the binding disconnects itself in the destructor, which for example QQmlBinding - // does in the destructor of its base class, QQmlJavaScriptExpression - Q_UNUSED(mode); - - This->removeFromObject(); - This->clear(); - delete static_cast<T *>(This); + if (b) + b->ref.ref(); + if (m_nextBinding.data() && !m_nextBinding->ref.deref()) + delete m_nextBinding.data(); + m_nextBinding = b; } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlabstractexpression.cpp b/src/qml/qml/qqmlabstractexpression.cpp deleted file mode 100644 index c55c86952c..0000000000 --- a/src/qml/qml/qqmlabstractexpression.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlabstractexpression_p.h" - -QT_BEGIN_NAMESPACE - -QQmlAbstractExpression::QQmlAbstractExpression() -: m_prevExpression(0), m_nextExpression(0) -{ -} - -QQmlAbstractExpression::~QQmlAbstractExpression() -{ - if (m_prevExpression) { - *m_prevExpression = m_nextExpression; - if (m_nextExpression) - m_nextExpression->m_prevExpression = m_prevExpression; - } - - if (m_context.isT2()) - m_context.asT2()->_s = 0; -} - -QQmlContextData *QQmlAbstractExpression::context() const -{ - if (m_context.isT1()) return m_context.asT1(); - else return m_context.asT2()->_c; -} - -void QQmlAbstractExpression::setContext(QQmlContextData *context) -{ - if (m_prevExpression) { - *m_prevExpression = m_nextExpression; - if (m_nextExpression) - m_nextExpression->m_prevExpression = m_prevExpression; - m_prevExpression = 0; - m_nextExpression = 0; - } - - if (m_context.isT1()) m_context = context; - else m_context.asT2()->_c = context; - - if (context) { - m_nextExpression = context->expressions; - if (m_nextExpression) - m_nextExpression->m_prevExpression = &m_nextExpression; - m_prevExpression = &context->expressions; - context->expressions = this; - } -} - -void QQmlAbstractExpression::refresh() -{ -} - -bool QQmlAbstractExpression::isValid() const -{ - return context() != 0; -} - -QT_END_NAMESPACE - diff --git a/src/qml/qml/qqmlabstractexpression_p.h b/src/qml/qml/qqmlabstractexpression_p.h deleted file mode 100644 index 82ba010434..0000000000 --- a/src/qml/qml/qqmlabstractexpression_p.h +++ /dev/null @@ -1,116 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLABSTRACTEXPRESSION_P_H -#define QQMLABSTRACTEXPRESSION_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmlcontext_p.h> -#include <private/qfieldlist_p.h> -#include <private/qflagpointer_p.h> - -QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlAbstractExpression -{ -public: - QQmlAbstractExpression(); - virtual ~QQmlAbstractExpression(); - - bool isValid() const; - - QQmlContextData *context() const; - void setContext(QQmlContextData *); - - virtual void refresh(); - - class DeleteWatcher { - public: - inline DeleteWatcher(QQmlAbstractExpression *); - inline ~DeleteWatcher(); - inline bool wasDeleted() const; - private: - friend class QQmlAbstractExpression; - QQmlContextData *_c; - QQmlAbstractExpression **_w; - QQmlAbstractExpression *_s; - }; - -private: - friend class QQmlContext; - friend class QQmlContextData; - friend class QQmlContextPrivate; - - QBiPointer<QQmlContextData, DeleteWatcher> m_context; - QQmlAbstractExpression **m_prevExpression; - QQmlAbstractExpression *m_nextExpression; -}; - -QQmlAbstractExpression::DeleteWatcher::DeleteWatcher(QQmlAbstractExpression *e) -: _c(0), _w(0), _s(e) -{ - if (e->m_context.isT1()) { - _w = &_s; - _c = e->m_context.asT1(); - e->m_context = this; - } else { - // Another watcher is already registered - _w = &e->m_context.asT2()->_s; - } -} - -QQmlAbstractExpression::DeleteWatcher::~DeleteWatcher() -{ - Q_ASSERT(*_w == 0 || (*_w == _s && _s->m_context.isT2())); - if (*_w && _s->m_context.asT2() == this) - _s->m_context = _c; -} - -bool QQmlAbstractExpression::DeleteWatcher::wasDeleted() const -{ - return *_w == 0; -} - -QT_END_NAMESPACE - -#endif // QQMLABSTRACTEXPRESSION_P_H diff --git a/src/qml/qml/qqmlaccessors_p.h b/src/qml/qml/qqmlaccessors_p.h index 24cd0b60cb..8b0a587740 100644 --- a/src/qml/qml/qqmlaccessors_p.h +++ b/src/qml/qml/qqmlaccessors_p.h @@ -34,6 +34,17 @@ #ifndef QQMLACCESSORS_P_H #define QQMLACCESSORS_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <private/qtqmlglobal_p.h> #include <QtCore/qbytearray.h> #include <QtCore/qvector.h> diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index f223d099e4..f8b737a62a 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -43,43 +43,28 @@ #include <private/qqmlscriptstring_p.h> #include <private/qqmlcontextwrapper_p.h> #include <private/qqmlbuiltinfunctions_p.h> +#include <private/qqmlvmemetaobject_p.h> +#include <private/qqmlvaluetypewrapper_p.h> #include <QVariant> #include <QtCore/qdebug.h> QT_BEGIN_NAMESPACE -// Used in qqmlabstractbinding.cpp -QQmlAbstractBinding::VTable QQmlBinding_vtable = { - QQmlAbstractBinding::default_destroy<QQmlBinding>, - QQmlBinding::expression, - QQmlBinding::propertyIndex, - QQmlBinding::object, - QQmlBinding::setEnabled, - QQmlBinding::update, - QQmlBinding::retargetBinding -}; - -QQmlBinding::Identifier QQmlBinding::Invalid = -1; - -static QQmlJavaScriptExpression::VTable QQmlBinding_jsvtable = { - QQmlBinding::expressionIdentifier, - QQmlBinding::expressionChanged -}; - QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt) -: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding) + : QQmlJavaScriptExpression(), + QQmlAbstractBinding() { setNotifyOnValueChanged(true); - QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt)); + QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt)); setScopeObject(obj); - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(context()->engine)->v4engine(); - v4function.set(v4, qmlBinding(context(), obj, str, QString(), 0)); + createQmlBinding(context(), obj, str, QString(), 0); } QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt) -: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding) + : QQmlJavaScriptExpression(), + QQmlAbstractBinding() { if (ctxt && !ctxt->isValid()) return; @@ -100,51 +85,52 @@ QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlConte } setNotifyOnValueChanged(true); - QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context)); + QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context)); setScopeObject(obj ? obj : scriptPrivate->scope); QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(context()->engine)->v4engine(); if (runtimeFunction) { - v4function.set(v4, QV4::QmlBindingWrapper::createQmlCallableForFunction(ctxtdata, scopeObject(), runtimeFunction)); + m_function.set(v4, QV4::FunctionObject::createQmlFunction(ctxtdata, scopeObject(), runtimeFunction)); } else { QString code = scriptPrivate->script; - v4function.set(v4, qmlBinding(context(), scopeObject(), code, url, scriptPrivate->lineNumber)); + createQmlBinding(context(), scopeObject(), code, url, scriptPrivate->lineNumber); } } QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt) -: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding) + : QQmlJavaScriptExpression(), + QQmlAbstractBinding() { setNotifyOnValueChanged(true); - QQmlAbstractExpression::setContext(ctxt); + QQmlJavaScriptExpression::setContext(ctxt); setScopeObject(obj); - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(context()->engine)->v4engine(); - v4function.set(v4, qmlBinding(ctxt, obj, str, QString(), 0)); + createQmlBinding(ctxt, obj, str, QString(), 0); } QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt, const QString &url, quint16 lineNumber, quint16 columnNumber) -: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding) + : QQmlJavaScriptExpression(), + QQmlAbstractBinding() { Q_UNUSED(columnNumber); setNotifyOnValueChanged(true); - QQmlAbstractExpression::setContext(ctxt); + QQmlJavaScriptExpression::setContext(ctxt); setScopeObject(obj); - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(context()->engine)->v4engine(); - v4function.set(v4, qmlBinding(ctxt, obj, str, url, lineNumber)); + createQmlBinding(ctxt, obj, str, url, lineNumber); } QQmlBinding::QQmlBinding(const QV4::Value &functionPtr, QObject *obj, QQmlContextData *ctxt) -: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding) + : QQmlJavaScriptExpression(), + QQmlAbstractBinding() { setNotifyOnValueChanged(true); - QQmlAbstractExpression::setContext(ctxt); + QQmlJavaScriptExpression::setContext(ctxt); setScopeObject(obj); - v4function.set(functionPtr.asObject()->engine(), functionPtr); + m_function.set(functionPtr.as<QV4::Object>()->engine(), functionPtr); } QQmlBinding::~QQmlBinding() @@ -162,90 +148,243 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) return; // Check that the target has not been deleted - if (QQmlData::wasDeleted(object())) + if (QQmlData::wasDeleted(targetObject())) return; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); QV4::Scope scope(ep->v4engine()); - QV4::ScopedFunctionObject f(scope, v4function.value()); + QV4::ScopedFunctionObject f(scope, m_function.value()); Q_ASSERT(f); - if (!updatingFlag()) { - QQmlBindingProfiler prof(ep->profiler, f); - setUpdatingFlag(true); + if (updatingFlag()) { + QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), getPropertyData(), 0); + QQmlAbstractBinding::printBindingLoopError(p); + return; + } + + QQmlBindingProfiler prof(ep->profiler, f); + setUpdatingFlag(true); - QQmlAbstractExpression::DeleteWatcher watcher(this); + QQmlJavaScriptExpression::DeleteWatcher watcher(this); - if (m_core.propType == qMetaTypeId<QQmlBinding *>()) { + QQmlPropertyData pd = getPropertyData(); - int idx = m_core.coreIndex; - Q_ASSERT(idx != -1); + if (pd.propType == qMetaTypeId<QQmlBinding *>()) { - QQmlBinding *t = this; - int status = -1; - void *a[] = { &t, 0, &status, &flags }; - QMetaObject::metacall(*m_coreObject, QMetaObject::WriteProperty, idx, a); + int idx = pd.coreIndex; + Q_ASSERT(idx != -1); - } else { - ep->referenceScarceResources(); + QQmlBinding *t = this; + int status = -1; + void *a[] = { &t, 0, &status, &flags }; + QMetaObject::metacall(*m_target, QMetaObject::WriteProperty, idx, a); - bool isUndefined = false; + } else { + ep->referenceScarceResources(); - QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(context(), f, &isUndefined)); + bool isUndefined = false; - bool needsErrorLocationData = false; - if (!watcher.wasDeleted() && !hasError()) - needsErrorLocationData = !QQmlPropertyPrivate::writeBinding(*m_coreObject, m_core, context(), - this, result, isUndefined, flags); + QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); - if (!watcher.wasDeleted()) { + bool error = false; + if (!watcher.wasDeleted() && isAddedToObject() && !hasError()) + error = !write(pd, result, isUndefined, flags); - if (needsErrorLocationData) - delayedError()->setErrorLocation(f->sourceLocation()); + if (!watcher.wasDeleted()) { - if (hasError()) { - if (!delayedError()->addError(ep)) ep->warning(this->error(context()->engine)); - } else { - clearError(); - } + if (error) { + delayedError()->setErrorLocation(f->sourceLocation()); + delayedError()->setErrorObject(m_target.data()); + } + 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); + ep->dereferenceScarceResources(); } + + if (!watcher.wasDeleted()) + setUpdatingFlag(false); +} + +// Returns true if successful, false if an error description was set on expression +bool QQmlBinding::write(const QQmlPropertyData &core, + const QV4::Value &result, bool isUndefined, + QQmlPropertyPrivate::WriteFlags flags) +{ + Q_ASSERT(m_target.data()); + Q_ASSERT(core.coreIndex != -1); + + QQmlEngine *engine = context()->engine; + QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); + +#define QUICK_STORE(cpptype, conversion) \ + { \ + cpptype o = (conversion); \ + int status = -1; \ + void *argv[] = { &o, 0, &status, &flags }; \ + QMetaObject::metacall(m_target.data(), QMetaObject::WriteProperty, core.coreIndex, argv); \ + return true; \ + } \ + + + if (!isUndefined && !core.isValueTypeVirtual()) { + switch (core.propType) { + case QMetaType::Int: + if (result.isInteger()) + QUICK_STORE(int, result.integerValue()) + else if (result.isNumber()) + QUICK_STORE(int, result.doubleValue()) + break; + case QMetaType::Double: + if (result.isNumber()) + QUICK_STORE(double, result.asDouble()) + break; + case QMetaType::Float: + if (result.isNumber()) + QUICK_STORE(float, result.asDouble()) + break; + case QMetaType::QString: + if (result.isString()) + QUICK_STORE(QString, result.toQStringNoThrow()) + break; + default: + if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) { + if (vtw->d()->valueType->typeId == core.propType) { + return vtw->write(m_target.data(), core.coreIndex); + } + } + break; + } + } +#undef QUICK_STORE + + int type = core.isValueTypeVirtual() ? core.valueTypePropType : core.propType; + + QQmlJavaScriptExpression::DeleteWatcher watcher(this); + + QVariant value; + bool isVarProperty = core.isVarProperty(); + + if (isUndefined) { + } else if (core.isQList()) { + value = QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QObject *> >()); + } else if (result.isNull() && core.isQObject()) { + value = QVariant::fromValue((QObject *)0); + } else if (core.propType == qMetaTypeId<QList<QUrl> >()) { + value = QQmlPropertyPrivate::resolvedUrlSequence(QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QUrl> >()), context()); + } else if (!isVarProperty && type != qMetaTypeId<QJSValue>()) { + value = QV8Engine::getV4(v8engine)->toVariant(result, type); + } + + if (hasError()) { + return false; + } else if (isVarProperty) { + const QV4::FunctionObject *f = result.as<QV4::FunctionObject>(); + if (f && f->isBinding()) { + // we explicitly disallow this case to avoid confusion. Users can still store one + // in an array in a var property if they need to, but the common case is user error. + delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); + return false; + } + + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_target.data()); + Q_ASSERT(vmemo); + vmemo->setVMEProperty(core.coreIndex, result); + } else if (isUndefined && core.isResettable()) { + void *args[] = { 0 }; + QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex, args); + } else if (isUndefined && type == qMetaTypeId<QVariant>()) { + QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, QVariant(), context(), flags); + } else if (type == qMetaTypeId<QJSValue>()) { + const QV4::FunctionObject *f = result.as<QV4::FunctionObject>(); + if (f && f->isBinding()) { + delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); + return false; + } + QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, QVariant::fromValue( + QJSValue(QV8Engine::getV4(v8engine), result.asReturnedValue())), + context(), flags); + } else if (isUndefined) { + QString errorStr = QLatin1String("Unable to assign [undefined] to "); + if (!QMetaType::typeName(type)) + errorStr += QLatin1String("[unknown property type]"); + else + errorStr += QLatin1String(QMetaType::typeName(type)); + delayedError()->setErrorDescription(errorStr); + return false; + } else if (const QV4::FunctionObject *f = result.as<QV4::FunctionObject>()) { + if (f->isBinding()) + delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); + else + delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var.")); + return false; + } else if (!QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, value, context(), flags)) { + + if (watcher.wasDeleted()) + return true; + + const char *valueType = 0; + const char *propertyType = 0; + + if (value.userType() == QMetaType::QObjectStar) { + if (QObject *o = *(QObject *const *)value.constData()) { + valueType = o->metaObject()->className(); + + QQmlMetaObject propertyMetaObject = QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate::get(engine), type); + if (!propertyMetaObject.isNull()) + propertyType = propertyMetaObject.className(); + } + } else if (value.userType() != QVariant::Invalid) { + if (value.userType() == QMetaType::VoidStar) + valueType = "null"; + else + valueType = QMetaType::typeName(value.userType()); + } + + if (!valueType) + valueType = "undefined"; + if (!propertyType) + propertyType = QMetaType::typeName(type); + if (!propertyType) + propertyType = "[unknown property type]"; + + delayedError()->setErrorDescription(QLatin1String("Unable to assign ") + + QLatin1String(valueType) + + QLatin1String(" to ") + + QLatin1String(propertyType)); + return false; + } + + return true; } QVariant QQmlBinding::evaluate() { QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); - QV4::Scope scope(ep->v4engine()); ep->referenceScarceResources(); bool isUndefined = false; - QV4::ScopedValue f(scope, v4function.value()); - QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(context(), f, &isUndefined)); + QV4::Scope scope(ep->v4engine()); + QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); ep->dereferenceScarceResources(); return scope.engine->toVariant(result, qMetaTypeId<QList<QObject*> >()); } -QString QQmlBinding::expressionIdentifier(QQmlJavaScriptExpression *e) +QString QQmlBinding::expressionIdentifier() { - QQmlBinding *This = static_cast<QQmlBinding *>(e); - - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(This->context()->engine); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); QV4::Scope scope(ep->v4engine()); - QV4::ScopedValue f(scope, This->v4function.value()); - QV4::Function *function = f->asFunctionObject()->function(); + QV4::ScopedValue f(scope, m_function.value()); + QV4::Function *function = f->as<QV4::FunctionObject>()->function(); QString url = function->sourceFile(); quint16 lineNumber = function->compiledFunction->location.line; @@ -254,10 +393,9 @@ QString QQmlBinding::expressionIdentifier(QQmlJavaScriptExpression *e) return url + QLatin1Char(':') + QString::number(lineNumber) + QLatin1Char(':') + QString::number(columnNumber); } -void QQmlBinding::expressionChanged(QQmlJavaScriptExpression *e) +void QQmlBinding::expressionChanged() { - QQmlBinding *This = static_cast<QQmlBinding *>(e); - This->update(); + update(); } void QQmlBinding::refresh() @@ -265,36 +403,6 @@ void QQmlBinding::refresh() update(); } -QString QQmlBinding::expression(const QQmlAbstractBinding *This) -{ - return static_cast<const QQmlBinding *>(This)->expression(); -} - -int QQmlBinding::propertyIndex(const QQmlAbstractBinding *This) -{ - return static_cast<const QQmlBinding *>(This)->propertyIndex(); -} - -QObject *QQmlBinding::object(const QQmlAbstractBinding *This) -{ - return static_cast<const QQmlBinding *>(This)->object(); -} - -void QQmlBinding::setEnabled(QQmlAbstractBinding *This, bool e, QQmlPropertyPrivate::WriteFlags f) -{ - static_cast<QQmlBinding *>(This)->setEnabled(e, f); -} - -void QQmlBinding::update(QQmlAbstractBinding *This , QQmlPropertyPrivate::WriteFlags f) -{ - static_cast<QQmlBinding *>(This)->update(f); -} - -void QQmlBinding::retargetBinding(QQmlAbstractBinding *This, QObject *o, int i) -{ - static_cast<QQmlBinding *>(This)->retargetBinding(o, i); -} - void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) { setEnabledFlag(e); @@ -307,44 +415,92 @@ void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) QString QQmlBinding::expression() const { QV4::Scope scope(QQmlEnginePrivate::get(context()->engine)->v4engine()); - QV4::ScopedValue v(scope, v4function.value()); + QV4::ScopedValue v(scope, m_function.value()); return v->toQStringNoThrow(); } -QObject *QQmlBinding::object() const +void QQmlBinding::setTarget(const QQmlProperty &prop) { - if (m_coreObject.hasValue()) return m_coreObject.constValue()->target; - else return *m_coreObject; + setTarget(prop.object(), QQmlPropertyPrivate::get(prop)->core); } -int QQmlBinding::propertyIndex() const +void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) { - if (m_coreObject.hasValue()) return m_coreObject.constValue()->targetProperty; - else return m_core.encodedIndex(); -} + m_target = object; -void QQmlBinding::retargetBinding(QObject *t, int i) -{ - m_coreObject.value().target = t; - m_coreObject.value().targetProperty = i; -} + if (!object) { + m_targetIndex = -1; + return; + } -void QQmlBinding::setTarget(const QQmlProperty &prop) -{ - setTarget(prop.object(), QQmlPropertyPrivate::get(prop)->core, - QQmlPropertyPrivate::get(prop)->context); -} + QQmlPropertyData pd = core; -void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, QQmlContextData *ctxt) -{ - m_coreObject = object; - m_core = core; - m_ctxt = ctxt; + while (pd.isAlias()) { + int coreIndex = pd.coreIndex; + int valueTypeIndex = pd.getValueTypeCoreIndex(); + QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); + + int aValueTypeIndex; + if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) { + m_target = 0; + m_targetIndex = -1; + return; + } + if (valueTypeIndex == -1) + valueTypeIndex = aValueTypeIndex; + + QQmlData *data = QQmlData::get(object, false); + if (!data || !data->propertyCache) { + m_target = 0; + m_targetIndex = -1; + return; + } + QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); + Q_ASSERT(propertyData); + + m_target = object; + pd = *propertyData; + if (valueTypeIndex != -1) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(pd.propType); + Q_ASSERT(valueTypeMetaObject); + QMetaProperty vtProp = valueTypeMetaObject->property(valueTypeIndex); + pd.setFlags(pd.getFlags() | QQmlPropertyData::IsValueTypeVirtual); + pd.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); + pd.valueTypePropType = vtProp.userType(); + pd.valueTypeCoreIndex = valueTypeIndex; + } + } + m_targetIndex = pd.encodedIndex(); + + QQmlData *data = QQmlData::get(*m_target, true); + if (!data->propertyCache) { + data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject()); + data->propertyCache->addref(); + } } -QQmlProperty QQmlBinding::property() const +QQmlPropertyData QQmlBinding::getPropertyData() const { - return QQmlPropertyPrivate::restore(object(), m_core, *m_ctxt); + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(m_targetIndex, &coreIndex); + + QQmlData *data = QQmlData::get(*m_target, false); + Q_ASSERT(data && data->propertyCache); + + QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); + Q_ASSERT(propertyData); + + QQmlPropertyData d = *propertyData; + if (valueTypeIndex != -1) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d.propType); + Q_ASSERT(valueTypeMetaObject); + QMetaProperty vtProp = valueTypeMetaObject->property(valueTypeIndex); + d.setFlags(d.getFlags() | QQmlPropertyData::IsValueTypeVirtual); + d.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); + d.valueTypePropType = vtProp.userType(); + d.valueTypeCoreIndex = valueTypeIndex; + } + return d; } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 1e440b2e86..a435847819 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -57,16 +57,15 @@ #include <private/qpointervaluepair_p.h> #include <private/qqmlabstractbinding_p.h> -#include <private/qqmlabstractexpression_p.h> #include <private/qqmljavascriptexpression_p.h> QT_BEGIN_NAMESPACE class QQmlContext; class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlJavaScriptExpression, - public QQmlAbstractExpression, public QQmlAbstractBinding { + friend class QQmlAbstractBinding; public: QQmlBinding(const QString &, QObject *, QQmlContext *); QQmlBinding(const QQmlScriptString &, QObject *, QQmlContext *); @@ -74,84 +73,62 @@ public: QQmlBinding(const QString &, QObject *, QQmlContextData *, const QString &url, quint16 lineNumber, quint16 columnNumber); QQmlBinding(const QV4::Value &, QObject *, QQmlContextData *); + ~QQmlBinding(); void setTarget(const QQmlProperty &); - void setTarget(QObject *, const QQmlPropertyData &, QQmlContextData *); - QQmlProperty property() const; + void setTarget(QObject *, const QQmlPropertyData &); void setNotifyOnValueChanged(bool); - // Inherited from QQmlAbstractExpression + // Inherited from QQmlJavaScriptExpression virtual void refresh(); - // "Inherited" from QQmlAbstractBinding - static QString expression(const QQmlAbstractBinding *); - static int propertyIndex(const QQmlAbstractBinding *); - static QObject *object(const QQmlAbstractBinding *); - static void setEnabled(QQmlAbstractBinding *, bool, QQmlPropertyPrivate::WriteFlags); - static void update(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags); - static void retargetBinding(QQmlAbstractBinding *, QObject *, int); - - void setEnabled(bool, QQmlPropertyPrivate::WriteFlags flags); - void update(QQmlPropertyPrivate::WriteFlags flags); - void update() { update(QQmlPropertyPrivate::DontRemoveBinding); } - - QString expression() const; - QObject *object() const; - int propertyIndex() const; - void retargetBinding(QObject *, int); + // Inherited from QQmlAbstractBinding + virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding); + virtual QString expression() const; + void update(QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding); typedef int Identifier; - static Identifier Invalid; + enum { + Invalid = -1 + }; QVariant evaluate(); - static QString expressionIdentifier(QQmlJavaScriptExpression *); - static void expressionChanged(QQmlJavaScriptExpression *); - -protected: - friend class QQmlAbstractBinding; - ~QQmlBinding(); + virtual QString expressionIdentifier(); + virtual void expressionChanged(); private: - QV4::PersistentValue v4function; - inline bool updatingFlag() const; inline void setUpdatingFlag(bool); inline bool enabledFlag() const; inline void setEnabledFlag(bool); + QQmlPropertyData getPropertyData() const; - struct Retarget { - QObject *target; - int targetProperty; - }; + bool write(const QQmlPropertyData &core, + const QV4::Value &result, bool isUndefined, + QQmlPropertyPrivate::WriteFlags flags); - QPointerValuePair<QObject, Retarget> m_coreObject; - QQmlPropertyData m_core; - // We store some flag bits in the following flag pointers. - // m_ctxt:flag1 - updatingFlag - // m_ctxt:flag2 - enabledFlag - QFlagPointer<QQmlContextData> m_ctxt; }; bool QQmlBinding::updatingFlag() const { - return m_ctxt.flag(); + return m_target.flag(); } void QQmlBinding::setUpdatingFlag(bool v) { - m_ctxt.setFlagValue(v); + m_target.setFlagValue(v); } bool QQmlBinding::enabledFlag() const { - return m_ctxt.flag2(); + return m_target.flag2(); } void QQmlBinding::setEnabledFlag(bool v) { - m_ctxt.setFlag2Value(v); + m_target.setFlag2Value(v); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 766e657c59..477a517e32 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -43,11 +43,12 @@ #include "qqmlcontext.h" #include "qqmlglobal_p.h" #include <private/qqmlprofiler_p.h> -#include <private/qv4debugservice_p.h> +#include <private/qqmldebugconnector_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> #include <private/qqmlcompiler_p.h> #include "qqmlinfo.h" -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <QtCore/qstringbuilder.h> #include <QtCore/qdebug.h> @@ -55,71 +56,79 @@ QT_BEGIN_NAMESPACE -static QQmlJavaScriptExpression::VTable QQmlBoundSignalExpression_jsvtable = { - QQmlBoundSignalExpression::expressionIdentifier, - QQmlBoundSignalExpression::expressionChanged -}; - -QQmlBoundSignalExpression::ExtraData::ExtraData(const QString &handlerName, const QString ¶meterString, - const QString &expression, const QString &fileName, - quint16 line, quint16 column) - : m_handlerName(handlerName), - m_parameterString(parameterString), - m_expression(expression), - m_sourceLocation(fileName, line, column) -{ -} - 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 ¶meterString) - : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable), + : QQmlJavaScriptExpression(), m_index(index), - m_target(target), - m_extra(new ExtraData(handlerName, parameterString, expression, fileName, line, column)) + m_target(target) { - setExpressionFunctionValid(false); - setInvalidParameterName(false); - 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. + } QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, const QV4::Value &function) - : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable), + : QQmlJavaScriptExpression(), m_index(index), - m_function(function.asObject()->engine(), function), - m_target(target), - m_extra(0) + m_target(target) { - setExpressionFunctionValid(true); - setInvalidParameterName(false); - + m_function.set(function.as<QV4::Object>()->engine(), function); init(ctxt, scope); } QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, QV4::Function *runtimeFunction) - : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable), + : QQmlJavaScriptExpression(), m_index(index), - m_target(target), - m_extra(0) + m_target(target) { - 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; QV4::ExecutionEngine *engine = QQmlEnginePrivate::getV4Engine(ctxt->engine); - m_function.set(engine, QV4::QmlBindingWrapper::createQmlCallableForFunction(ctxt, scope, runtimeFunction, signal.parameterNames(), &error)); + m_function.set(engine, QV4::FunctionObject::createQmlFunction(ctxt, scope, runtimeFunction, signal.parameterNames(), &error)); if (!error.isEmpty()) { qmlInfo(scopeObject()) << error; - setInvalidParameterName(true); - } else - setInvalidParameterName(false); + m_function.clear(); + } } void QQmlBoundSignalExpression::init(QQmlContextData *ctxt, QObject *scope) @@ -134,34 +143,30 @@ void QQmlBoundSignalExpression::init(QQmlContextData *ctxt, QObject *scope) QQmlBoundSignalExpression::~QQmlBoundSignalExpression() { - delete m_extra.data(); } -QString QQmlBoundSignalExpression::expressionIdentifier(QQmlJavaScriptExpression *e) +QString QQmlBoundSignalExpression::expressionIdentifier() { - QQmlBoundSignalExpression *This = static_cast<QQmlBoundSignalExpression *>(e); - QQmlSourceLocation loc = This->sourceLocation(); + QQmlSourceLocation loc = sourceLocation(); return loc.sourceFile + QLatin1Char(':') + QString::number(loc.line); } -void QQmlBoundSignalExpression::expressionChanged(QQmlJavaScriptExpression *) +void QQmlBoundSignalExpression::expressionChanged() { // bound signals do not notify on change. } QQmlSourceLocation QQmlBoundSignalExpression::sourceLocation() const { - if (expressionFunctionValid()) { - QV4::Function *f = function(); - Q_ASSERT(f); + QV4::Function *f = function(); + if (f) { QQmlSourceLocation loc; loc.sourceFile = f->sourceFile(); loc.line = f->compiledFunction->location.line; loc.column = f->compiledFunction->location.column; return loc; } - Q_ASSERT(!m_extra.isNull()); - return m_extra->m_sourceLocation; + return QQmlSourceLocation(); } QString QQmlBoundSignalExpression::expression() const @@ -171,10 +176,8 @@ QString QQmlBoundSignalExpression::expression() const QV4::Scope scope(QQmlEnginePrivate::get(engine())->v4engine()); QV4::ScopedValue v(scope, m_function.value()); return v->toQStringNoThrow(); - } else { - Q_ASSERT(!m_extra.isNull()); - return m_extra->m_expression; } + return QString(); } QV4::Function *QQmlBoundSignalExpression::function() const @@ -194,108 +197,79 @@ void QQmlBoundSignalExpression::evaluate(void **a) { Q_ASSERT (context() && engine()); - if (invalidParameterName()) + if (!expressionFunctionValid()) 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(scope.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_function.set(scope.engine, evalFunction(context(), scopeObject(), expression, - m_extra->m_sourceLocation.sourceFile, m_extra->m_sourceLocation.line, &m_extra->m_v8qmlscope)); - - if (m_function.isNullOrUndefined()) { - ep->dereferenceScarceResources(); - return; // could not evaluate function. Not valid. - } - - setExpressionFunctionValid(true); - } - 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 = QQmlMetaObject(m_target).methodParameterTypes(methodIndex, dummy, 0); - int argCount = argsTypes ? *argsTypes : 0; - - QV4::ScopedValue f(scope, m_function.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] = scope.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] = scope.engine->fromVariant(QVariant(type, a[ii + 1])); - } + 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 = QQmlMetaObject(m_target).methodParameterTypes(methodIndex, dummy, 0); + int argCount = argsTypes ? *argsTypes : 0; + + 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] = scope.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] = scope.engine->fromVariant(QVariant(type, a[ii + 1])); } - - QQmlJavaScriptExpression::evaluate(context(), f, callData, 0); } + + QQmlJavaScriptExpression::evaluate(callData, 0); + ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. } //////////////////////////////////////////////////////////////////////// -QQmlAbstractBoundSignal::QQmlAbstractBoundSignal() -: m_prevSignal(0), m_nextSignal(0) + +/*! \internal + \a signal MUST be in the signal index range (see QObjectPrivate::signalIndex()). + This is different from QMetaMethod::methodIndex(). +*/ +QQmlBoundSignal::QQmlBoundSignal(QObject *target, int signal, QObject *owner, + QQmlEngine *engine) + : QQmlNotifierEndpoint(QQmlNotifierEndpoint::QQmlBoundSignal), + m_prevSignal(0), m_nextSignal(0), + m_expression(0) { + addToObject(owner); + + /* + If this is a cloned method, connect to the 'original'. For example, + for the signal 'void aSignal(int parameter = 0)', if the method + index refers to 'aSignal()', get the index of 'aSignal(int)'. + This ensures that 'parameter' will be available from QML. + */ + signal = QQmlPropertyCache::originalClone(target, signal); + QQmlNotifierEndpoint::connect(target, signal, engine); } -QQmlAbstractBoundSignal::~QQmlAbstractBoundSignal() +QQmlBoundSignal::~QQmlBoundSignal() { removeFromObject(); } -void QQmlAbstractBoundSignal::addToObject(QObject *obj) +void QQmlBoundSignal::addToObject(QObject *obj) { Q_ASSERT(!m_prevSignal); Q_ASSERT(obj); @@ -308,7 +282,7 @@ void QQmlAbstractBoundSignal::addToObject(QObject *obj) data->signalHandlers = this; } -void QQmlAbstractBoundSignal::removeFromObject() +void QQmlBoundSignal::removeFromObject() { if (m_prevSignal) { *m_prevSignal = m_nextSignal; @@ -318,40 +292,6 @@ void QQmlAbstractBoundSignal::removeFromObject() } } -/*! \internal - \a signal MUST be in the signal index range (see QObjectPrivate::signalIndex()). - This is different from QMetaMethod::methodIndex(). -*/ -QQmlBoundSignal::QQmlBoundSignal(QObject *target, int signal, QObject *owner, - QQmlEngine *engine) -: m_expression(0), m_index(signal), m_isEvaluating(false) -{ - addToObject(owner); - setCallback(QQmlNotifierEndpoint::QQmlBoundSignal); - - /* - If this is a cloned method, connect to the 'original'. For example, - for the signal 'void aSignal(int parameter = 0)', if the method - index refers to 'aSignal()', get the index of 'aSignal(int)'. - This ensures that 'parameter' will be available from QML. - */ - m_index = QQmlPropertyCache::originalClone(target, m_index); - QQmlNotifierEndpoint::connect(target, m_index, engine); -} - -QQmlBoundSignal::~QQmlBoundSignal() -{ - m_expression = 0; -} - -/*! - Returns the signal index in the range returned by QObjectPrivate::signalIndex(). - This is different from QMetaMethod::methodIndex(). -*/ -int QQmlBoundSignal::index() const -{ - return m_index; -} /*! Returns the signal expression. @@ -362,45 +302,29 @@ QQmlBoundSignalExpression *QQmlBoundSignal::expression() const } /*! - Sets the signal expression to \a e. Returns the current signal expression, - or null if there is no signal expression. + Sets the signal expression to \a e. - The QQmlBoundSignal instance adds a reference to \a e. The caller - assumes ownership of the returned QQmlBoundSignalExpression reference. + The QQmlBoundSignal instance takes ownership of \a e (and does not add a reference). */ -QQmlBoundSignalExpressionPointer QQmlBoundSignal::setExpression(QQmlBoundSignalExpression *e) +void QQmlBoundSignal::takeExpression(QQmlBoundSignalExpression *e) { - QQmlBoundSignalExpressionPointer rv = m_expression; - m_expression = e; - if (m_expression) m_expression->setNotifyOnValueChanged(false); - return rv; -} - -/*! - Sets the signal expression to \a e. Returns the current signal expression, - or null if there is no signal expression. - - The QQmlBoundSignal instance takes ownership of \a e (and does not add a reference). The caller - assumes ownership of the returned QQmlBoundSignalExpression reference. -*/ -QQmlBoundSignalExpressionPointer QQmlBoundSignal::takeExpression(QQmlBoundSignalExpression *e) -{ - QQmlBoundSignalExpressionPointer rv = m_expression; m_expression.take(e); - if (m_expression) m_expression->setNotifyOnValueChanged(false); - return rv; + if (m_expression) + m_expression->setNotifyOnValueChanged(false); } void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a) { QQmlBoundSignal *s = static_cast<QQmlBoundSignal*>(e); + if (!s->m_expression) return; - if (QQmlDebugService::isDebuggingEnabled()) - QV4DebugService::instance()->signalEmitted(QString::fromLatin1(QMetaObjectPrivate::signal(s->m_expression->target()->metaObject(), s->m_index).methodSignature())); - - s->m_isEvaluating = true; + QV4DebugService *service = QQmlDebugConnector::service<QV4DebugService>(); + if (service) + service->signalEmitted(QString::fromLatin1(QMetaObjectPrivate::signal( + s->m_expression->target()->metaObject(), + s->signalIndex()).methodSignature())); QQmlEngine *engine; if (s->m_expression && (engine = s->m_expression->engine())) { @@ -410,8 +334,6 @@ void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a) QQmlEnginePrivate::warning(engine, s->m_expression->error(engine)); } } - - s->m_isEvaluating = false; } //////////////////////////////////////////////////////////////////////// diff --git a/src/qml/qml/qqmlboundsignal_p.h b/src/qml/qml/qqmlboundsignal_p.h index 8d677ea039..3742317484 100644 --- a/src/qml/qml/qqmlboundsignal_p.h +++ b/src/qml/qml/qqmlboundsignal_p.h @@ -47,7 +47,6 @@ #include <QtCore/qmetaobject.h> -#include <private/qqmlabstractexpression_p.h> #include <private/qqmljavascriptexpression_p.h> #include <private/qqmlboundsignalexpressionpointer_p.h> #include <private/qqmlnotifier_p.h> @@ -58,7 +57,7 @@ QT_BEGIN_NAMESPACE -class Q_QML_PRIVATE_EXPORT QQmlBoundSignalExpression : public QQmlAbstractExpression, public QQmlJavaScriptExpression, public QQmlRefCount +class Q_QML_PRIVATE_EXPORT QQmlBoundSignalExpression : public QQmlJavaScriptExpression, public QQmlRefCount { public: QQmlBoundSignalExpression(QObject *target, int index, @@ -73,9 +72,9 @@ public: QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, QV4::Function *runtimeFunction); - // "inherited" from QQmlJavaScriptExpression. - static QString expressionIdentifier(QQmlJavaScriptExpression *); - static void expressionChanged(QQmlJavaScriptExpression *); + // inherited from QQmlJavaScriptExpression. + virtual QString expressionIdentifier(); + virtual void expressionChanged(); // evaluation of a bound signal expression doesn't return any value void evaluate(void **a); @@ -92,80 +91,35 @@ private: void init(QQmlContextData *ctxt, QObject *scope); - bool expressionFunctionValid() const { return m_extra.flag(); } - void setExpressionFunctionValid(bool v) { m_extra.setFlagValue(v); } - - bool invalidParameterName() const { return m_extra.flag2(); } - void setInvalidParameterName(bool v) { m_extra.setFlag2Value(v); } + bool expressionFunctionValid() const { return !m_function.isNullOrUndefined(); } int m_index; - QV4::PersistentValue m_function; - QObject *m_target; - - // only needed when !expressionFunctionValid() - struct ExtraData { - ExtraData(const QString &handlerName, const QString ¶meterString, - const QString &expression, const QString &fileName, - quint16 line, quint16 column); - QString m_handlerName; - QString m_parameterString; - QString m_expression; - QQmlSourceLocation m_sourceLocation; - QV4::PersistentValue m_v8qmlscope; - }; - - // We store some flag bits in the following flag pointers. - // flag - expressionFunctionValid - // flag2 - invalidParameterName - QFlagPointer<ExtraData> m_extra; }; -class Q_QML_PRIVATE_EXPORT QQmlAbstractBoundSignal +class Q_QML_PRIVATE_EXPORT QQmlBoundSignal : public QQmlNotifierEndpoint { public: - QQmlAbstractBoundSignal(); - virtual ~QQmlAbstractBoundSignal(); - - virtual int index() const = 0; - virtual QQmlBoundSignalExpression *expression() const = 0; - virtual QQmlBoundSignalExpressionPointer setExpression(QQmlBoundSignalExpression *) = 0; - virtual QQmlBoundSignalExpressionPointer takeExpression(QQmlBoundSignalExpression *) = 0; - virtual bool isEvaluating() const = 0; + QQmlBoundSignal(QObject *target, int signal, QObject *owner, QQmlEngine *engine); + ~QQmlBoundSignal(); void removeFromObject(); -protected: - void addToObject(QObject *owner); + + QQmlBoundSignalExpression *expression() const; + void takeExpression(QQmlBoundSignalExpression *); private: - friend class QQmlData; + friend void QQmlBoundSignal_callback(QQmlNotifierEndpoint *, void **); friend class QQmlPropertyPrivate; + friend class QQmlData; friend class QQmlEngineDebugService; - QQmlAbstractBoundSignal **m_prevSignal; - QQmlAbstractBoundSignal *m_nextSignal; -}; -class Q_QML_PRIVATE_EXPORT QQmlBoundSignal : public QQmlAbstractBoundSignal, - public QQmlNotifierEndpoint -{ -public: - QQmlBoundSignal(QObject *target, int signal, QObject *owner, QQmlEngine *engine); - virtual ~QQmlBoundSignal(); - - int index() const; - - QQmlBoundSignalExpression *expression() const; - QQmlBoundSignalExpressionPointer setExpression(QQmlBoundSignalExpression *); - QQmlBoundSignalExpressionPointer takeExpression(QQmlBoundSignalExpression *); + void addToObject(QObject *owner); - bool isEvaluating() const { return m_isEvaluating; } - -private: - friend void QQmlBoundSignal_callback(QQmlNotifierEndpoint *, void **); + QQmlBoundSignal **m_prevSignal; + QQmlBoundSignal *m_nextSignal; QQmlBoundSignalExpressionPointer m_expression; - int m_index; - bool m_isEvaluating; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 35182d6319..22a54d732e 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -42,8 +42,8 @@ #include "qqml.h" #include "qqmlengine.h" #include "qqmlbinding_p.h" -#include "qqmlglobal_p.h" -#include <private/qqmlenginedebugservice_p.h> +#include <private/qqmldebugconnector_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> #include "qqmlincubator.h" #include "qqmlincubator_p.h" #include <private/qqmljavascriptexpression_p.h> @@ -53,6 +53,7 @@ #include <private/qv4functionobject_p.h> #include <private/qv4script_p.h> #include <private/qv4scopedvalue_p.h> +#include <private/qv4objectiterator_p.h> #include <QStack> #include <QStringList> @@ -61,23 +62,6 @@ #include <qqmlinfo.h> #include "qqmlmemoryprofiler_p.h" -#define INITIALPROPERTIES_SOURCE \ - "(function(object, values) {"\ - "try {"\ - "for (var property in values) {" \ - "try {"\ - "var properties = property.split(\".\");"\ - "var o = object;"\ - "for (var ii = 0; ii < properties.length - 1; ++ii) {"\ - "o = o[properties[ii]];"\ - "}"\ - "o[properties[properties.length - 1]] = values[property];"\ - "} catch(e) {}"\ - "}"\ - "} catch(e) {}"\ - "})" - - namespace { QThreadStorage<int> creationDepth; } @@ -98,7 +82,6 @@ V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension); \class QQmlComponent \since 5.0 \inmodule QtQml - \mainclass \brief The QQmlComponent class encapsulates a QML component definition @@ -896,10 +879,11 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) depthIncreased = false; } - if (enginePriv->isDebugging && rv) { + QQmlEngineDebugService *service = QQmlDebugConnector::service<QQmlEngineDebugService>(); + if (service && rv) { if (!context->isInternal) context->asQQmlContextPrivate()->instances.append(rv); - QQmlEngineDebugService::instance()->objectCreated(engine, rv); + service->objectCreated(engine, rv); } return rv; @@ -1075,11 +1059,10 @@ namespace QV4 { namespace Heap { struct QmlIncubatorObject : Object { - QmlIncubatorObject(QV4::ExecutionEngine *engine, QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous); + QmlIncubatorObject(QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous); QScopedPointer<QQmlComponentIncubator> incubator; QPointer<QObject> parent; QV4::Value valuemap; - QV4::Value qmlGlobal; QV4::Value statusChanged; }; @@ -1194,6 +1177,46 @@ static void QQmlComponent_setQmlParent(QObject *me, QObject *parent) \sa incubateObject() */ + +static void setInitialProperties(QV4::ExecutionEngine *engine, const QV4::Value &o, const QV4::Value &v) +{ + QV4::Scope scope(engine); + QV4::ScopedObject object(scope); + QV4::ScopedObject valueMap(scope, v); + QV4::ObjectIterator it(scope, valueMap, QV4::ObjectIterator::EnumerableOnly|QV4::ObjectIterator::WithProtoChain); + QV4::ScopedString name(scope); + QV4::ScopedValue val(scope); + if (engine->hasException) + return; + + while (1) { + name = it.nextPropertyNameAsString(val); + if (!name) + break; + object = o; + const QStringList properties = name->toQString().split(QLatin1Char('.')); + for (int i = 0; i < properties.length() - 1; ++i) { + name = engine->newString(properties.at(i)); + object = object->get(name); + if (engine->hasException || !object) { + break; + } + } + if (engine->hasException || !object) { + engine->hasException = false; + continue; + } + name = engine->newString(properties.last()); + object->put(name, val); + if (engine->hasException) { + engine->hasException = false; + continue; + } + } + + engine->hasException = false; +} + /*! \internal */ @@ -1216,7 +1239,7 @@ void QQmlComponent::createObject(QQmlV4Function *args) if (args->length() >= 2) { QV4::ScopedValue v(scope, (*args)[1]); - if (!v->asObject() || v->asArrayObject()) { + if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) { qmlInfo(this) << tr("createObject: value is not an object"); args->setReturnValue(QV4::Encode::null()); return; @@ -1239,16 +1262,8 @@ void QQmlComponent::createObject(QQmlV4Function *args) QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4, rv)); Q_ASSERT(object->isObject()); - if (!valuemap->isUndefined()) { - QV4::ScopedObject qmlglobal(scope, args->qmlGlobal()); - QV4::ScopedValue f(scope, QV4::Script::evaluate(v4, QString::fromLatin1(INITIALPROPERTIES_SOURCE), qmlglobal)); - Q_ASSERT(f->asFunctionObject()); - QV4::ScopedCallData callData(scope, 2); - callData->thisObject = v4->globalObject(); - callData->args[0] = object; - callData->args[1] = valuemap; - f->asFunctionObject()->call(callData); - } + if (!valuemap->isUndefined()) + setInitialProperties(v4, object, valuemap); d->completeCreate(); @@ -1256,10 +1271,7 @@ void QQmlComponent::createObject(QQmlV4Function *args) QQmlData::get(rv)->explicitIndestructibleSet = false; QQmlData::get(rv)->indestructible = false; - if (!rv) - args->setReturnValue(QV4::Encode::null()); - else - args->setReturnValue(object->asReturnedValue()); + args->setReturnValue(object->asReturnedValue()); } /*! @@ -1342,7 +1354,7 @@ void QQmlComponent::incubateObject(QQmlV4Function *args) if (args->length() >= 2) { QV4::ScopedValue v(scope, (*args)[1]); if (v->isNull()) { - } else if (!v->asObject() || v->asArrayObject()) { + } else if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) { qmlInfo(this) << tr("createObject: value is not an object"); args->setReturnValue(QV4::Encode::null()); return; @@ -1362,14 +1374,12 @@ void QQmlComponent::incubateObject(QQmlV4Function *args) QQmlComponentExtension *e = componentExtension(args->v4engine()); - QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->alloc<QV4::QmlIncubatorObject>(args->v4engine(), mode)); + QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocObject<QV4::QmlIncubatorObject>(mode)); QV4::ScopedObject p(scope, e->incubationProto.value()); r->setPrototype(p); - if (!valuemap->isUndefined()) { + if (!valuemap->isUndefined()) r->d()->valuemap = valuemap; - r->d()->qmlGlobal = args->qmlGlobal(); - } r->d()->parent = parent; QQmlIncubator *incubator = r->d()->incubator.data(); @@ -1383,25 +1393,17 @@ void QQmlComponent::incubateObject(QQmlV4Function *args) } // XXX used by QSGLoader -void QQmlComponentPrivate::initializeObjectWithInitialProperties(const QV4::Value &qmlGlobal, const QV4::Value &valuemap, QObject *toCreate) +void QQmlComponentPrivate::initializeObjectWithInitialProperties(const QV4::Value &valuemap, QObject *toCreate) { QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); QV4::ExecutionEngine *v4engine = QV8Engine::getV4(ep->v8engine()); QV4::Scope scope(v4engine); QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4engine, toCreate)); - Q_ASSERT(object->asObject()); - - if (!valuemap.isUndefined()) { - QV4::ScopedObject qmlGlobalObj(scope, qmlGlobal); - QV4::ScopedFunctionObject f(scope, QV4::Script::evaluate(v4engine, - QString::fromLatin1(INITIALPROPERTIES_SOURCE), qmlGlobalObj)); - QV4::ScopedCallData callData(scope, 2); - callData->thisObject = v4engine->globalObject(); - callData->args[0] = object; - callData->args[1] = valuemap; - f->call(callData); - } + Q_ASSERT(object->as<QV4::Object>()); + + if (!valuemap.isUndefined()) + setInitialProperties(v4engine, object, valuemap); } QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4) @@ -1475,10 +1477,8 @@ QQmlComponentExtension::~QQmlComponentExtension() { } -QV4::Heap::QmlIncubatorObject::QmlIncubatorObject(ExecutionEngine *engine, QQmlIncubator::IncubationMode m) - : QV4::Heap::Object(engine) - , valuemap(QV4::Primitive::undefinedValue()) - , qmlGlobal(QV4::Primitive::undefinedValue()) +QV4::Heap::QmlIncubatorObject::QmlIncubatorObject(QQmlIncubator::IncubationMode m) + : valuemap(QV4::Primitive::undefinedValue()) , statusChanged(QV4::Primitive::undefinedValue()) { incubator.reset(new QQmlComponentIncubator(this, m)); @@ -1491,13 +1491,8 @@ void QV4::QmlIncubatorObject::setInitialState(QObject *o) if (!d()->valuemap.isUndefined()) { QV4::ExecutionEngine *v4 = engine(); QV4::Scope scope(v4); - - QV4::ScopedFunctionObject f(scope, QV4::Script::evaluate(v4, QString::fromLatin1(INITIALPROPERTIES_SOURCE), d()->qmlGlobal.asObject())); - QV4::ScopedCallData callData(scope, 2); - callData->thisObject = v4->globalObject(); - callData->args[0] = QV4::QObjectWrapper::wrap(v4, o); - callData->args[1] = d()->valuemap; - f->call(callData); + QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(v4, o)); + setInitialProperties(v4, obj, d()->valuemap); } } @@ -1505,7 +1500,6 @@ void QV4::QmlIncubatorObject::markObjects(QV4::Heap::Base *that, QV4::ExecutionE { QmlIncubatorObject::Data *o = static_cast<QmlIncubatorObject::Data *>(that); o->valuemap.mark(e); - o->qmlGlobal.mark(e); o->statusChanged.mark(e); Object::markObjects(that, e); } diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h index 8c866c585a..121c83db5c 100644 --- a/src/qml/qml/qqmlcomponent.h +++ b/src/qml/qml/qqmlcomponent.h @@ -63,8 +63,8 @@ class Q_QML_EXPORT QQmlComponent : public QObject Q_PROPERTY(QUrl url READ url CONSTANT) public: - Q_ENUMS(CompilationMode) enum CompilationMode { PreferSynchronous, Asynchronous }; + Q_ENUM(CompilationMode) QQmlComponent(QObject *parent = 0); QQmlComponent(QQmlEngine *, QObject *parent=0); @@ -74,8 +74,8 @@ public: QQmlComponent(QQmlEngine *, const QUrl &url, CompilationMode mode, QObject *parent = 0); virtual ~QQmlComponent(); - Q_ENUMS(Status) enum Status { Null, Ready, Loading, Error }; + Q_ENUM(Status) Status status() const; bool isNull() const; @@ -125,7 +125,6 @@ private: QT_END_NAMESPACE -Q_DECLARE_METATYPE(QQmlComponent::Status) QML_DECLARE_TYPE(QQmlComponent) QML_DECLARE_TYPEINFO(QQmlComponent, QML_HAS_ATTACHED_PROPERTIES) diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index adc6e173d2..15ec88dd52 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -82,7 +82,7 @@ public: QObject *beginCreate(QQmlContextData *); void completeCreate(); - void initializeObjectWithInitialProperties(const QV4::Value &qmlGlobal, const QV4::Value &valuemap, QObject *toCreate); + void initializeObjectWithInitialProperties(const QV4::Value &valuemap, QObject *toCreate); QQmlTypeData *typeData; virtual void typeDataReady(QQmlTypeData *); diff --git a/src/qml/qml/qqmlcomponentattached_p.h b/src/qml/qml/qqmlcomponentattached_p.h index b6ad3dec00..fa103e7fce 100644 --- a/src/qml/qml/qqmlcomponentattached_p.h +++ b/src/qml/qml/qqmlcomponentattached_p.h @@ -34,6 +34,17 @@ #ifndef QQMLCOMPONENTATTACHED_P_H #define QQMLCOMPONENTATTACHED_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QtQml/qqml.h> #include <private/qtqmlglobal_p.h> #include <QtCore/QObject> diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index fb51bad3a7..b056731e96 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -57,7 +57,6 @@ QQmlContextPrivate::QQmlContextPrivate() /*! \class QQmlContext \brief The QQmlContext class defines a context within a QML engine. - \mainclass \inmodule QtQml Contexts allow data to be exposed to the QML components instantiated by the @@ -585,9 +584,9 @@ void QQmlContextData::clearContext() { emitDestruction(); - QQmlAbstractExpression *expression = expressions; + QQmlJavaScriptExpression *expression = expressions; while (expression) { - QQmlAbstractExpression *nextExpression = expression->m_nextExpression; + QQmlJavaScriptExpression *nextExpression = expression->m_nextExpression; expression->m_prevExpression = 0; expression->m_nextExpression = 0; @@ -652,9 +651,9 @@ void QQmlContextData::setParent(QQmlContextData *p, bool parentTakesOwnership) } } -void QQmlContextData::refreshExpressionsRecursive(QQmlAbstractExpression *expression) +void QQmlContextData::refreshExpressionsRecursive(QQmlJavaScriptExpression *expression) { - QQmlAbstractExpression::DeleteWatcher w(expression); + QQmlJavaScriptExpression::DeleteWatcher w(expression); if (expression->m_nextExpression) refreshExpressionsRecursive(expression->m_nextExpression); @@ -808,7 +807,7 @@ QV4::IdentifierHash<int> &QQmlContextData::propertyNames() const { if (propertyNameCache.isEmpty()) { propertyNameCache = QV4::IdentifierHash<int>(QV8Engine::getV4(engine->handle())); - for (QHash<int, int>::ConstIterator it = objectIndexToId.begin(), end = objectIndexToId.end(); + for (QHash<int, int>::ConstIterator it = objectIndexToId.cbegin(), end = objectIndexToId.cend(); it != end; ++it) { const QV4::CompiledData::Object *obj = typeCompilationUnit->data->objectAt(it.key()); const QString name = typeCompilationUnit->data->stringAt(obj->idIndex); diff --git a/src/qml/qml/qqmlcontext.h b/src/qml/qml/qqmlcontext.h index c714846147..e69a2f8f69 100644 --- a/src/qml/qml/qqmlcontext.h +++ b/src/qml/qml/qqmlcontext.h @@ -72,6 +72,7 @@ public: void setContextProperty(const QString &, QObject *); void setContextProperty(const QString &, const QVariant &); + // ### Qt 6: no need for a mutable object, this should become a const QObject pointer QString nameForObject(QObject *) const; QUrl resolvedUrl(const QUrl &); diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index f5fd7d0a5c..95254d4baa 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -69,7 +69,7 @@ class QQmlExpression; class QQmlEngine; class QQmlExpression; class QQmlExpressionPrivate; -class QQmlAbstractExpression; +class QQmlJavaScriptExpression; class QQmlContextData; class QQmlContextPrivate : public QObjectPrivate @@ -171,7 +171,7 @@ public: QQmlContextData **prevChild; // Expressions that use this context - QQmlAbstractExpression *expressions; + QQmlJavaScriptExpression *expressions; // Doubly-linked list of objects that are owned by this context QQmlData *contextObjects; @@ -212,7 +212,7 @@ public: private: void refreshExpressionsRecursive(bool isGlobal); - void refreshExpressionsRecursive(QQmlAbstractExpression *); + void refreshExpressionsRecursive(QQmlJavaScriptExpression *); ~QQmlContextData() {} }; diff --git a/src/qml/qml/qqmlcontextwrapper.cpp b/src/qml/qml/qqmlcontextwrapper.cpp index 5844eab54f..0d84c3bb64 100644 --- a/src/qml/qml/qqmlcontextwrapper.cpp +++ b/src/qml/qml/qqmlcontextwrapper.cpp @@ -38,13 +38,14 @@ #include <private/qqmlcontext_p.h> #include <private/qv4engine_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4objectproto_p.h> #include <private/qv4mm_p.h> #include <private/qv4function_p.h> #include <private/qv4compileddata_p.h> #include <private/qqmltypewrapper_p.h> #include <private/qqmllistwrapper_p.h> +#include <private/qqmljavascriptexpression_p.h> #include <private/qjsvalue_p.h> QT_BEGIN_NAMESPACE @@ -53,14 +54,12 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(QmlContextWrapper); -Heap::QmlContextWrapper::QmlContextWrapper(QV4::ExecutionEngine *engine, QQmlContextData *context, QObject *scopeObject, bool ownsContext) - : Heap::Object(engine) - , readOnly(true) +Heap::QmlContextWrapper::QmlContextWrapper(QQmlContextData *context, QObject *scopeObject, bool ownsContext) + : readOnly(true) , ownsContext(ownsContext) , isNullWrapper(false) , context(context) , scopeObject(scopeObject) - , idObjectsWrapper(Q_NULLPTR) { } @@ -74,7 +73,7 @@ ReturnedValue QmlContextWrapper::qmlScope(ExecutionEngine *v4, QQmlContextData * { Scope valueScope(v4); - Scoped<QmlContextWrapper> w(valueScope, v4->memoryManager->alloc<QmlContextWrapper>(v4, ctxt, scope)); + Scoped<QmlContextWrapper> w(valueScope, v4->memoryManager->allocObject<QmlContextWrapper>(ctxt, scope)); return w.asReturnedValue(); } @@ -88,67 +87,26 @@ ReturnedValue QmlContextWrapper::urlScope(ExecutionEngine *v4, const QUrl &url) context->isInternal = true; context->isJSContext = true; - Scoped<QmlContextWrapper> w(scope, v4->memoryManager->alloc<QmlContextWrapper>(v4, context, (QObject*)0, true)); + Scoped<QmlContextWrapper> w(scope, v4->memoryManager->allocObject<QmlContextWrapper>(context, (QObject*)0, true)); w->d()->isNullWrapper = true; return w.asReturnedValue(); } -QQmlContextData *QmlContextWrapper::callingContext(ExecutionEngine *v4) -{ - Scope scope(v4); - QV4::Scoped<QmlContextWrapper> c(scope, v4->qmlContextObject()); - - return !!c ? c->getContext() : 0; -} - -QQmlContextData *QmlContextWrapper::getContext(const Value &value) -{ - if (!value.isObject()) - return 0; - - QV4::ExecutionEngine *v4 = value.asObject()->engine(); - Scope scope(v4); - QV4::Scoped<QmlContextWrapper> c(scope, value); - - return c ? c->getContext() : 0; -} - -void QmlContextWrapper::takeContextOwnership(const Value &qmlglobal) -{ - Q_ASSERT(qmlglobal.isObject()); - - QV4::ExecutionEngine *v4 = qmlglobal.asObject()->engine(); - Scope scope(v4); - QV4::Scoped<QmlContextWrapper> c(scope, qmlglobal); - Q_ASSERT(c); - c->d()->ownsContext = true; -} - - -ReturnedValue QmlContextWrapper::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue QmlContextWrapper::get(const Managed *m, String *name, bool *hasProperty) { Q_ASSERT(m->as<QmlContextWrapper>()); - QmlContextWrapper *resource = static_cast<QmlContextWrapper *>(m); + const QmlContextWrapper *resource = static_cast<const QmlContextWrapper *>(m); QV4::ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); - // In V8 the JS global object would come _before_ the QML global object, - // so simulate that here. - bool hasProp; - QV4::ScopedValue result(scope, v4->globalObject()->get(name, &hasProp)); - if (hasProp) { - if (hasProperty) - *hasProperty = hasProp; - return result->asReturnedValue(); - } - if (resource->d()->isNullWrapper) return Object::get(m, name, hasProperty); - if (QV4::QmlContextWrapper::callingContext(v4) != resource->d()->context) + if (v4->callingQmlContext() != resource->d()->context) return Object::get(m, name, hasProperty); - result = Object::get(m, name, &hasProp); + bool hasProp; + QV4::ScopedValue result(scope, Object::get(m, name, &hasProp)); if (hasProp) { if (hasProperty) *hasProperty = hasProp; @@ -209,7 +167,8 @@ ReturnedValue QmlContextWrapper::get(Managed *m, String *name, bool *hasProperty if (propertyIdx < context->idValueCount) { - ep->captureProperty(&context->idValues[propertyIdx].bindings); + if (ep->propertyCapture) + ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings); if (hasProperty) *hasProperty = true; return QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx]); @@ -217,8 +176,8 @@ ReturnedValue QmlContextWrapper::get(Managed *m, String *name, bool *hasProperty QQmlContextPrivate *cp = context->asQQmlContextPrivate(); - ep->captureProperty(context->asQQmlContext(), -1, - propertyIdx + cp->notifyIndex); + if (ep->propertyCapture) + ep->propertyCapture->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex); const QVariant &value = cp->propertyValues.at(propertyIdx); if (hasProperty) @@ -278,10 +237,9 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) return; QV4::Scoped<QmlContextWrapper> wrapper(scope, resource); - PropertyAttributes attrs; - Property *pd = wrapper->__getOwnProperty__(name, &attrs); - if (pd) { - wrapper->putValue(pd, attrs, value); + uint member = wrapper->internalClass()->find(name); + if (member < UINT_MAX) { + wrapper->putValue(member, value); return; } @@ -289,7 +247,7 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) if (wrapper && wrapper->d()->readOnly) { QString error = QLatin1String("Invalid write to global property \"") + name->toQString() + QLatin1Char('"'); - ScopedString e(scope, v4->currentContext()->engine->newString(error)); + ScopedString e(scope, v4->newString(error)); v4->throwError(e); return; } @@ -342,127 +300,4 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) Object::put(m, name, value); } -void QmlContextWrapper::markObjects(Heap::Base *m, ExecutionEngine *engine) -{ - QmlContextWrapper::Data *This = static_cast<QmlContextWrapper::Data *>(m); - if (This->idObjectsWrapper) - This->idObjectsWrapper->mark(engine); - Object::markObjects(m, engine); -} - -void QmlContextWrapper::registerQmlDependencies(ExecutionEngine *engine, const CompiledData::Function *compiledFunction) -{ - // Let the caller check and avoid the function call :) - Q_ASSERT(compiledFunction->hasQmlDependencies()); - - QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : 0; - if (!ep) - return; - QQmlEnginePrivate::PropertyCapture *capture = ep->propertyCapture; - if (!capture) - return; - - QV4::Scope scope(engine); - QV4::Scoped<QmlContextWrapper> contextWrapper(scope, engine->qmlContextObject()); - QQmlContextData *qmlContext = contextWrapper->getContext(); - - const quint32 *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable(); - const int idObjectDependencyCount = compiledFunction->nDependingIdObjects; - for (int i = 0; i < idObjectDependencyCount; ++i, ++idObjectDependency) { - Q_ASSERT(int(*idObjectDependency) < qmlContext->idValueCount); - capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings); - } - - Q_ASSERT(qmlContext->contextObject); - const quint32 *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable(); - const int contextPropertyDependencyCount = compiledFunction->nDependingContextProperties; - for (int i = 0; i < contextPropertyDependencyCount; ++i) { - const int propertyIndex = *contextPropertyDependency++; - const int notifyIndex = *contextPropertyDependency++; - capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex); - } - - QObject *scopeObject = contextWrapper->getScopeObject(); - const quint32 *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable(); - const int scopePropertyDependencyCount = compiledFunction->nDependingScopeProperties; - for (int i = 0; i < scopePropertyDependencyCount; ++i) { - const int propertyIndex = *scopePropertyDependency++; - const int notifyIndex = *scopePropertyDependency++; - capture->captureProperty(scopeObject, propertyIndex, notifyIndex); - } - -} - -ReturnedValue QmlContextWrapper::idObjectsArray() -{ - if (!d()->idObjectsWrapper) { - ExecutionEngine *v4 = engine(); - d()->idObjectsWrapper = v4->memoryManager->alloc<QQmlIdObjectsArray>(v4, this); - } - return d()->idObjectsWrapper->asReturnedValue(); -} - -ReturnedValue QmlContextWrapper::qmlSingletonWrapper(ExecutionEngine *v4, String *name) -{ - if (!d()->context->imports) - return Encode::undefined(); - // Search for attached properties, enums and imported scripts - QQmlTypeNameCache::Result r = d()->context->imports->query(name); - - Q_ASSERT(r.isValid()); - Q_ASSERT(r.type); - Q_ASSERT(r.type->isSingleton()); - Q_ASSERT(v4); - - QQmlEngine *e = v4->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = r.type->singletonInstanceInfo(); - siinfo->init(e); - - if (QObject *qobjectSingleton = siinfo->qobjectApi(e)) - return QV4::QObjectWrapper::wrap(engine(), qobjectSingleton); - return QJSValuePrivate::convertedToValue(engine(), siinfo->scriptApi(e)); -} - -DEFINE_OBJECT_VTABLE(QQmlIdObjectsArray); - -Heap::QQmlIdObjectsArray::QQmlIdObjectsArray(ExecutionEngine *engine, QV4::QmlContextWrapper *contextWrapper) - : Heap::Object(engine) - , contextWrapper(contextWrapper->d()) -{ -} - -ReturnedValue QQmlIdObjectsArray::getIndexed(Managed *m, uint index, bool *hasProperty) -{ - Scope scope(static_cast<QV4::QQmlIdObjectsArray*>(m)->engine()); - Scoped<QQmlIdObjectsArray> This(scope, static_cast<QV4::QQmlIdObjectsArray*>(m)); - Scoped<QmlContextWrapper> contextWrapper(scope, This->d()->contextWrapper); - QQmlContextData *context = contextWrapper->getContext(); - if (!context) { - if (hasProperty) - *hasProperty = false; - return Encode::undefined(); - } - if (index >= (uint)context->idValueCount) { - if (hasProperty) - *hasProperty = false; - return Encode::undefined(); - } - - if (hasProperty) - *hasProperty = true; - - QQmlEnginePrivate *ep = scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : 0; - if (ep) - ep->captureProperty(&context->idValues[index].bindings); - - return QObjectWrapper::wrap(This->engine(), context->idValues[index].data()); -} - -void QQmlIdObjectsArray::markObjects(Heap::Base *that, ExecutionEngine *engine) -{ - QQmlIdObjectsArray::Data *This = static_cast<QQmlIdObjectsArray::Data *>(that); - This->contextWrapper->mark(engine); - Object::markObjects(that, engine); -} - QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcontextwrapper_p.h b/src/qml/qml/qqmlcontextwrapper_p.h index 52d8677103..9dd71b708f 100644 --- a/src/qml/qml/qqmlcontextwrapper_p.h +++ b/src/qml/qml/qqmlcontextwrapper_p.h @@ -48,7 +48,7 @@ #include <QtCore/qglobal.h> #include <private/qtqmlglobal_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4object_p.h> #include <private/qqmlcontext_p.h> #include <private/qv4functionobject_p.h> @@ -65,10 +65,8 @@ struct QmlContextWrapper; namespace Heap { -struct QQmlIdObjectsArray; - struct QmlContextWrapper : Object { - QmlContextWrapper(ExecutionEngine *engine, QQmlContextData *context, QObject *scopeObject, bool ownsContext = false); + QmlContextWrapper(QQmlContextData *context, QObject *scopeObject, bool ownsContext = false); ~QmlContextWrapper(); bool readOnly; bool ownsContext; @@ -76,12 +74,6 @@ struct QmlContextWrapper : Object { QQmlGuardedContextData context; QPointer<QObject> scopeObject; - QQmlIdObjectsArray *idObjectsWrapper; -}; - -struct QQmlIdObjectsArray : Object { - QQmlIdObjectsArray(QV4::ExecutionEngine *engine, QV4::QmlContextWrapper *contextWrapper); - QmlContextWrapper *contextWrapper; }; } @@ -94,33 +86,17 @@ struct Q_QML_EXPORT QmlContextWrapper : Object static ReturnedValue qmlScope(ExecutionEngine *e, QQmlContextData *ctxt, QObject *scope); static ReturnedValue urlScope(ExecutionEngine *v4, const QUrl &); - static QQmlContextData *callingContext(ExecutionEngine *v4); - static void takeContextOwnership(const Value &qmlglobal); + void takeContextOwnership() { + d()->ownsContext = true; + } inline QObject *getScopeObject() const { return d()->scopeObject; } inline QQmlContextData *getContext() const { return d()->context; } - static QQmlContextData *getContext(const Value &value); void setReadOnly(bool b) { d()->readOnly = b; } - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static void put(Managed *m, String *name, const Value &value); - static void markObjects(Heap::Base *m, ExecutionEngine *engine); - - static void registerQmlDependencies(ExecutionEngine *context, const CompiledData::Function *compiledFunction); - - ReturnedValue idObjectsArray(); - ReturnedValue qmlSingletonWrapper(ExecutionEngine *e, String *name); - -}; - -struct QQmlIdObjectsArray : public Object -{ - V4_OBJECT2(QQmlIdObjectsArray, Object) - - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); - static void markObjects(Heap::Base *that, ExecutionEngine *engine); - }; } diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index ebca9d2718..517f8d42ed 100644 --- a/src/qml/qml/qqmlcustomparser.cpp +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -34,6 +34,7 @@ #include "qqmlcustomparser_p.h" #include "qqmlcompiler_p.h" +#include <private/qqmltypecompiler_p.h> #include <QtCore/qdebug.h> @@ -140,7 +141,7 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const type = result.type; } - return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; + return type ? type->enumValue(engine, QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; } const QMetaObject *mo = StaticQtMetaObject::get(); @@ -159,34 +160,8 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const */ const QMetaObject *QQmlCustomParser::resolveType(const QString& name) const { - return compiler->resolveType(name); -} - -int QQmlCustomParserCompilerBackend::evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const -{ - Q_ASSERT_X(ok, "QQmlCompiler::evaluateEnum", "ok must not be a null pointer"); - *ok = false; - - if (scope != QLatin1String("Qt")) { - QQmlType *type = 0; - imports().resolveType(scope, &type, 0, 0, 0); - return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; - } - - const QMetaObject *mo = StaticQtMetaObject::get(); - int i = mo->enumeratorCount(); - while (i--) { - int v = mo->enumerator(i).keyToValue(enumValue.constData(), ok); - if (*ok) - return v; - } - return -1; -} - -const QMetaObject *QQmlCustomParserCompilerBackend::resolveType(const QString &name) const -{ QQmlType *qmltype = 0; - if (!imports().resolveType(name, &qmltype, 0, 0, 0)) + if (!validator->imports().resolveType(name, &qmltype, 0, 0, 0)) return 0; if (!qmltype) return 0; diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h index 88282b1bbc..8bdc73fab1 100644 --- a/src/qml/qml/qqmlcustomparser_p.h +++ b/src/qml/qml/qqmlcustomparser_p.h @@ -55,15 +55,8 @@ QT_BEGIN_NAMESPACE class QQmlCompiledData; - -struct QQmlCustomParserCompilerBackend -{ - virtual ~QQmlCustomParserCompilerBackend() {} - virtual const QQmlImports &imports() const = 0; - - int evaluateEnum(const QString &scope, const QByteArray& enumValue, bool *ok) const; - const QMetaObject *resolveType(const QString& name) const; -}; +class QQmlPropertyValidator; +class QQmlEnginePrivate; class Q_QML_PRIVATE_EXPORT QQmlCustomParser { @@ -75,8 +68,8 @@ public: }; Q_DECLARE_FLAGS(Flags, Flag) - QQmlCustomParser() : compiler(0), m_flags(NoFlag) {} - QQmlCustomParser(Flags f) : compiler(0), m_flags(f) {} + QQmlCustomParser() : engine(0), validator(0), m_flags(NoFlag) {} + QQmlCustomParser(Flags f) : engine(0), validator(0), m_flags(f) {} virtual ~QQmlCustomParser() {} void clearErrors(); @@ -100,7 +93,8 @@ protected: private: QList<QQmlError> exceptions; - const QQmlCustomParserCompilerBackend *compiler; + QQmlEnginePrivate *engine; + const QQmlPropertyValidator *validator; Flags m_flags; QBiPointer<const QQmlImports, QQmlTypeNameCache> imports; friend class QQmlPropertyValidator; diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 04c42b638d..ef05dd1fd7 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -48,8 +48,9 @@ #include <private/qtqmlglobal_p.h> #include <private/qobject_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4persistent_p.h> +#include <qjsengine.h> QT_BEGIN_NAMESPACE @@ -58,7 +59,7 @@ class QQmlEngine; class QQmlGuardImpl; class QQmlCompiledData; class QQmlAbstractBinding; -class QQmlAbstractBoundSignal; +class QQmlBoundSignal; class QQmlContext; class QQmlPropertyCache; class QQmlContextData; @@ -72,15 +73,7 @@ class QQmlNotifierEndpoint; class Q_QML_PRIVATE_EXPORT QQmlData : public QAbstractDeclarativeData { public: - QQmlData() - : ownedByQml1(false), ownMemory(true), ownContext(false), indestructible(true), explicitIndestructibleSet(false), - hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false), - hasVMEMetaObject(false), parentFrozen(false), bindingBitsSize(0), bindingBits(0), notifyList(0), context(0), outerContext(0), - bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0), - lineNumber(0), columnNumber(0), jsEngineId(0), compiledData(0), deferredData(0), - propertyCache(0), guards(0), extendedData(0) { - init(); - } + QQmlData(); static inline void init() { static bool initialized = false; @@ -119,9 +112,10 @@ public: * v8 GC will check this flag, only deletes the objects when rootObjectInCreation is false. */ quint32 rootObjectInCreation:1; + quint32 hasInterceptorMetaObject:1; quint32 hasVMEMetaObject:1; quint32 parentFrozen:1; - quint32 dummy:22; + quint32 dummy:21; // When bindingBitsSize < 32, we store the binding bit flags inside // bindingBitsValue. When we need more than 32 bits, we allocated @@ -158,7 +152,7 @@ public: QQmlContextData *outerContext; QQmlAbstractBinding *bindings; - QQmlAbstractBoundSignal *signalHandlers; + QQmlBoundSignal *signalHandlers; // Linked list for QQmlContext::contextObjects QQmlData *nextContextObject; diff --git a/src/qml/qml/qqmldirparser.cpp b/src/qml/qml/qqmldirparser.cpp index 7f6310d58e..57b50733ea 100644 --- a/src/qml/qml/qqmldirparser.cpp +++ b/src/qml/qml/qqmldirparser.cpp @@ -146,7 +146,7 @@ bool QQmlDirParser::parse(const QString &source) if (invalidLine) { reportError(lineNumber, 0, - QString::fromLatin1("invalid qmldir directive contains too many tokens")); + QStringLiteral("invalid qmldir directive contains too many tokens")); continue; } else if (sectionCount == 0) { continue; // no sections, no party. @@ -154,17 +154,17 @@ bool QQmlDirParser::parse(const QString &source) } else if (sections[0] == QLatin1String("module")) { if (sectionCount != 2) { reportError(lineNumber, 0, - QString::fromLatin1("module identifier directive requires one argument, but %1 were provided").arg(sectionCount - 1)); + QStringLiteral("module identifier directive requires one argument, but %1 were provided").arg(sectionCount - 1)); continue; } if (!_typeNamespace.isEmpty()) { reportError(lineNumber, 0, - QString::fromLatin1("only one module identifier directive may be defined in a qmldir file")); + QStringLiteral("only one module identifier directive may be defined in a qmldir file")); continue; } if (!firstLine) { reportError(lineNumber, 0, - QString::fromLatin1("module identifier directive must be the first directive in a qmldir file")); + QStringLiteral("module identifier directive must be the first directive in a qmldir file")); continue; } @@ -173,7 +173,7 @@ bool QQmlDirParser::parse(const QString &source) } else if (sections[0] == QLatin1String("plugin")) { if (sectionCount < 2 || sectionCount > 3) { reportError(lineNumber, 0, - QString::fromLatin1("plugin directive requires one or two arguments, but %1 were provided").arg(sectionCount - 1)); + QStringLiteral("plugin directive requires one or two arguments, but %1 were provided").arg(sectionCount - 1)); continue; } @@ -185,7 +185,7 @@ bool QQmlDirParser::parse(const QString &source) } else if (sections[0] == QLatin1String("internal")) { if (sectionCount != 3) { reportError(lineNumber, 0, - QString::fromLatin1("internal types require 2 arguments, but %1 were provided").arg(sectionCount - 1)); + QStringLiteral("internal types require 2 arguments, but %1 were provided").arg(sectionCount - 1)); continue; } Component entry(sections[1], sections[2], -1, -1); @@ -194,7 +194,7 @@ bool QQmlDirParser::parse(const QString &source) } else if (sections[0] == QLatin1String("singleton")) { if (sectionCount < 3 || sectionCount > 4) { reportError(lineNumber, 0, - QString::fromLatin1("singleton types require 2 or 3 arguments, but %1 were provided").arg(sectionCount - 1)); + QStringLiteral("singleton types require 2 or 3 arguments, but %1 were provided").arg(sectionCount - 1)); continue; } else if (sectionCount == 3) { // handle qmldir directory listing case where singleton is defined in the following pattern: @@ -218,7 +218,7 @@ bool QQmlDirParser::parse(const QString &source) } else if (sections[0] == QLatin1String("typeinfo")) { if (sectionCount != 2) { reportError(lineNumber, 0, - QString::fromLatin1("typeinfo requires 1 argument, but %1 were provided").arg(sectionCount - 1)); + QStringLiteral("typeinfo requires 1 argument, but %1 were provided").arg(sectionCount - 1)); continue; } #ifdef QT_CREATOR @@ -228,13 +228,13 @@ bool QQmlDirParser::parse(const QString &source) } else if (sections[0] == QLatin1String("designersupported")) { if (sectionCount != 1) - reportError(lineNumber, 0, QString::fromLatin1("designersupported does not expect any argument")); + reportError(lineNumber, 0, QStringLiteral("designersupported does not expect any argument")); else _designerSupported = true; } else if (sections[0] == QLatin1String("depends")) { if (sectionCount != 3) { reportError(lineNumber, 0, - QString::fromLatin1("depends requires 2 arguments, but %1 were provided").arg(sectionCount - 1)); + QStringLiteral("depends requires 2 arguments, but %1 were provided").arg(sectionCount - 1)); continue; } @@ -268,7 +268,7 @@ bool QQmlDirParser::parse(const QString &source) } } else { reportError(lineNumber, 0, - QString::fromLatin1("a component declaration requires two or three arguments, but %1 were provided").arg(sectionCount)); + QStringLiteral("a component declaration requires two or three arguments, but %1 were provided").arg(sectionCount)); } firstLine = false; @@ -304,7 +304,9 @@ QList<QQmlError> QQmlDirParser::errors(const QString &uri) const { QUrl url(uri); QList<QQmlError> errors; - for (int i = 0; i < _errors.size(); ++i) { + const int numErrors = _errors.size(); + errors.reserve(numErrors); + for (int i = 0; i < numErrors; ++i) { const QQmlJS::DiagnosticMessage &msg = _errors.at(i); QQmlError e; QString description = msg.message; @@ -362,14 +364,14 @@ bool QQmlDirParser::designerSupported() const QDebug &operator<< (QDebug &debug, const QQmlDirParser::Component &component) { - const QString output = QString::fromLatin1("{%1 %2.%3}"). + const QString output = QStringLiteral("{%1 %2.%3}"). arg(component.typeName).arg(component.majorVersion).arg(component.minorVersion); return debug << qPrintable(output); } QDebug &operator<< (QDebug &debug, const QQmlDirParser::Script &script) { - const QString output = QString::fromLatin1("{%1 %2.%3}"). + const QString output = QStringLiteral("{%1 %2.%3}"). arg(script.nameSpace).arg(script.majorVersion).arg(script.minorVersion); return debug << qPrintable(output); } diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 06a61c3307..109cfac0c3 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -42,7 +42,6 @@ #include "qqmlexpression.h" #include "qqmlcomponent.h" #include "qqmlvme_p.h" -#include <private/qqmlenginedebugservice_p.h> #include "qqmlstringconverters_p.h" #include "qqmlxmlhttprequest_p.h" #include "qqmlscriptstring.h" @@ -54,11 +53,7 @@ #include "qqmllist_p.h" #include "qqmltypenamecache_p.h" #include "qqmlnotifier_p.h" -#include <private/qqmldebugserver_p.h> -#include <private/qqmlprofilerservice_p.h> -#include <private/qv4debugservice_p.h> -#include <private/qdebugmessageservice_p.h> -#include <private/qqmlenginecontrolservice_p.h> +#include <private/qqmldebugconnector_p.h> #include "qqmlincubator.h" #include "qqmlabstracturlinterceptor.h" #include <private/qqmlboundsignal_p.h> @@ -225,7 +220,6 @@ void QQmlEnginePrivate::activateDesignerMode() /*! \class QQmlImageProviderBase \brief The QQmlImageProviderBase class is used to register image providers in the QML engine. - \mainclass \inmodule QtQml Image providers must be registered with the QML engine. The only information the QML @@ -247,6 +241,10 @@ void QQmlEnginePrivate::activateDesignerMode() The QQuickImageProvider::requestPixmap() method will be called for all image requests. \value Texture The Image Provider provides QSGTextureProvider based images. The QQuickImageProvider::requestTexture() method will be called for all image requests. + \value ImageResponse The Image provider provides QQuickTextureFactory based images. + Should only be used in QQuickAsyncImageProvider or its subclasses. + The QQuickAsyncImageProvider::requestImageResponse() method will be called for all image requests. + Since Qt 5.6 \omitvalue Invalid */ @@ -594,7 +592,7 @@ the same object as is returned from the Qt.include() call. // Qt.include() is implemented in qv4include.cpp QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) -: propertyCapture(0), rootContext(0), isDebugging(false), +: propertyCapture(0), rootContext(0), profiler(0), outputWarningsToMsgLog(true), cleanup(0), erroredBindings(0), inProgressCreations(0), workerScriptEngine(0), @@ -607,8 +605,8 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) QQmlEnginePrivate::~QQmlEnginePrivate() { - typedef QHash<QPair<QQmlType *, int>, QQmlPropertyCache *>::Iterator TypePropertyCacheIt; - typedef QHash<int, QQmlCompiledData *>::Iterator CompositeTypesIt; + typedef QHash<QPair<QQmlType *, int>, QQmlPropertyCache *>::const_iterator TypePropertyCacheIt; + typedef QHash<int, QQmlCompiledData *>::const_iterator CompositeTypesIt; if (inProgressCreations) qWarning() << QQmlEngine::tr("There are still \"%1\" items in the process of being created at engine destruction.").arg(inProgressCreations); @@ -627,9 +625,9 @@ QQmlEnginePrivate::~QQmlEnginePrivate() if (incubationController) incubationController->d = 0; incubationController = 0; - for (TypePropertyCacheIt iter = typePropertyCache.begin(), end = typePropertyCache.end(); iter != end; ++iter) + for (TypePropertyCacheIt iter = typePropertyCache.cbegin(), end = typePropertyCache.cend(); iter != end; ++iter) (*iter)->release(); - for (CompositeTypesIt iter = m_compositeTypes.begin(), end = m_compositeTypes.end(); iter != end; ++iter) { + for (CompositeTypesIt iter = m_compositeTypes.cbegin(), end = m_compositeTypes.cend(); iter != end; ++iter) { iter.value()->isRegisteredWithEngine = false; // since unregisterInternalCompositeType() will not be called in this @@ -666,6 +664,17 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) } } +QQmlData::QQmlData() + : ownedByQml1(false), ownMemory(true), ownContext(false), indestructible(true), explicitIndestructibleSet(false), + hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false), + hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), bindingBitsSize(0), bindingBits(0), notifyList(0), context(0), outerContext(0), + bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0), + lineNumber(0), columnNumber(0), jsEngineId(0), compiledData(0), deferredData(0), + propertyCache(0), guards(0), extendedData(0) +{ + init(); +} + void QQmlData::destroyed(QAbstractDeclarativeData *d, QObject *o) { QQmlData *ddata = static_cast<QQmlData *>(d); @@ -799,7 +808,7 @@ void QQmlData::markAsDeleted(QObject *o) QQmlData::setQueuedForDeletion(o); QObjectPrivate *p = QObjectPrivate::get(o); - for (QList<QObject *>::iterator it = p->children.begin(), end = p->children.end(); it != end; ++it) { + for (QList<QObject *>::const_iterator it = p->children.constBegin(), end = p->children.constEnd(); it != end; ++it) { QQmlData::markAsDeleted(*it); } } @@ -824,14 +833,12 @@ void QQmlData::flushPendingBindingImpl(int coreIndex) // Find the binding QQmlAbstractBinding *b = bindings; - while (b && *b->m_mePtr && b->propertyIndex() != coreIndex) + while (b && b->targetPropertyIndex() != coreIndex) b = b->nextBinding(); - if (b && b->propertyIndex() == coreIndex) { - b->clear(); + if (b && b->targetPropertyIndex() == coreIndex) b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); - } } bool QQmlEnginePrivate::baseModulesUninitialized = true; @@ -861,15 +868,9 @@ void QQmlEnginePrivate::init() rootContext = new QQmlContext(q,true); - if (QCoreApplication::instance()->thread() == q->thread() && - QQmlEngineDebugService::isDebuggingEnabled()) { - isDebugging = true; - QQmlEngineDebugService::instance(); - QV4DebugService::instance(); - QQmlProfilerService::instance(); - QDebugMessageService::instance(); - QQmlEngineControlService::instance(); - QQmlDebugServer::instance()->addEngine(q); + if (QCoreApplication::instance()->thread() == q->thread() && QQmlDebugConnector::instance()) { + QQmlDebugConnector::instance()->open(); + QQmlDebugConnector::instance()->addEngine(q); } } @@ -886,7 +887,6 @@ QQuickWorkerScriptEngine *QQmlEnginePrivate::getWorkerScriptEngine() \since 5.0 \inmodule QtQml \brief The QQmlEngine class provides an environment for instantiating QML components. - \mainclass Each QML component is instantiated in a QQmlContext. QQmlContext's are essential for passing data to QML @@ -947,8 +947,9 @@ QQmlEngine::QQmlEngine(QQmlEnginePrivate &dd, QObject *parent) QQmlEngine::~QQmlEngine() { Q_D(QQmlEngine); - if (d->isDebugging) - QQmlDebugServer::instance()->removeEngine(this); + QQmlDebugConnector *server = QQmlDebugConnector::instance(); + if (server) + server->removeEngine(this); d->typeLoader.invalidate(); @@ -1237,7 +1238,7 @@ bool QQmlEngine::outputWarningsToStandardError() const If \a enabled is true, any warning messages generated by QML will be output to stderr and emitted by the warnings() signal. If \a enabled - is false, on the warnings() signal will be emitted. This allows + is false, only the warnings() signal will be emitted. This allows applications to handle warning output themselves. The default value is true. @@ -1433,7 +1434,8 @@ QObject *qmlAttachedPropertiesObjectById(int id, const QObject *object, bool cre if (rv || !create) return rv; - QQmlAttachedPropertiesFunc pf = QQmlMetaType::attachedPropertiesFuncById(id); + QQmlEnginePrivate *engine = QQmlEnginePrivate::get(data->context); + QQmlAttachedPropertiesFunc pf = QQmlMetaType::attachedPropertiesFuncById(engine, id); if (!pf) return 0; @@ -1448,8 +1450,10 @@ QObject *qmlAttachedPropertiesObjectById(int id, const QObject *object, bool cre QObject *qmlAttachedPropertiesObject(int *idCache, const QObject *object, const QMetaObject *attachedMetaObject, bool create) { - if (*idCache == -1) - *idCache = QQmlMetaType::attachedPropertiesFuncId(attachedMetaObject); + if (*idCache == -1) { + QQmlEngine *engine = object ? qmlEngine(object) : 0; + *idCache = QQmlMetaType::attachedPropertiesFuncId(engine ? QQmlEnginePrivate::get(engine) : 0, attachedMetaObject); + } if (*idCache == -1 || !object) return 0; @@ -1492,51 +1496,6 @@ Q_QML_EXPORT QObject *qmlAttachedPropertiesObject(int *idCache, const QObject *o #endif // QT_DEPRECATED_SINCE(5, 1) -QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool printWarning) -{ -#ifndef QQML_NO_DEBUG_PROTOCOL - if (!QQmlEnginePrivate::qml_debugging_enabled - && printWarning) { - qDebug("QML debugging is enabled. Only use this in a safe environment."); - } - QQmlEnginePrivate::qml_debugging_enabled = true; -#else - Q_UNUSED(printWarning); -#endif -} - -/*! - * \enum QQmlDebuggingEnabler::StartMode - * - * Defines the debug server's start behavior. You can interrupt QML engines starting while a debug - * client is connecting, in order to set breakpoints in or profile startup code. - * - * \value DoNotWaitForClient Run any QML engines as usual while the debug services are connecting. - * \value WaitForClient If a QML engine starts while the debug services are connecting, - * interrupt it until they are done. - */ - -/*! - * Enables debugging for QML engines created after calling this function. The debug server will - * listen on \a port at \a hostName and block the QML engine until it receives a connection if - * \a mode is \c WaitForClient. If \a mode is not specified it won't block and if \a hostName is not - * specified it will listen on all available interfaces. You can only start one debug server at a - * time. A debug server may have already been started if the -qmljsdebugger= command line argument - * was given. This method returns \c true if a new debug server was successfully started, or - * \c false otherwise. - */ -bool QQmlDebuggingEnabler::startTcpDebugServer(int port, StartMode mode, const QString &hostName) -{ -#ifndef QQML_NO_DEBUG_PROTOCOL - return QQmlDebugServer::enable(port, port, mode == WaitForClient, hostName); -#else - Q_UNUSED(port); - Q_UNUSED(block); - Q_UNUSED(hostName); - return false; -#endif -} - class QQmlDataExtended { public: QQmlDataExtended(); @@ -1678,12 +1637,11 @@ void QQmlData::destroyed(QObject *object) QQmlAbstractBinding *binding = bindings; while (binding) { - QQmlAbstractBinding *next = binding->nextBinding(); binding->setAddedToObject(false); - binding->setNextBinding(0); - binding->destroy(); - binding = next; + binding = binding->nextBinding(); } + if (bindings && !bindings->ref.deref()) + delete bindings; if (compiledData) { compiledData->release(); @@ -1696,9 +1654,9 @@ void QQmlData::destroyed(QObject *object) deferredData = 0; } - QQmlAbstractBoundSignal *signalHandler = signalHandlers; + QQmlBoundSignal *signalHandler = signalHandlers; while (signalHandler) { - if (signalHandler->isEvaluating()) { + if (signalHandler->isNotifying()) { // The object is being deleted during signal handler evaluation. // This will cause a crash due to invalid memory access when the // evaluation has completed. @@ -1710,7 +1668,7 @@ void QQmlData::destroyed(QObject *object) if (location.sourceFile.isEmpty()) location.sourceFile = QStringLiteral("<Unknown File>"); locationString.append(location.sourceFile); - locationString.append(QString::fromLatin1(":%0: ").arg(location.line)); + locationString.append(QStringLiteral(":%0: ").arg(location.line)); QString source = expr->expression(); if (source.size() > 100) { source.truncate(96); @@ -1727,7 +1685,7 @@ void QQmlData::destroyed(QObject *object) "%s", object, qPrintable(locationString)); } - QQmlAbstractBoundSignal *next = signalHandler->m_nextSignal; + QQmlBoundSignal *next = signalHandler->m_nextSignal; signalHandler->m_prevSignal = 0; signalHandler->m_nextSignal = 0; delete signalHandler; @@ -2140,8 +2098,7 @@ QString QQmlEngine::offlineStoragePath() const return d->offlineStoragePath; } -QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersion, - QQmlError &error) +QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersion) { QList<QQmlType *> types; @@ -2203,10 +2160,10 @@ QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersi // Properties override: // * other elements of the same name +#if 0 bool overloadError = false; QString overloadName; -#if 0 for (QQmlPropertyCache::StringCache::ConstIterator iter = raw->stringCache.begin(); !overloadError && iter != raw->stringCache.end(); ++iter) { @@ -2223,7 +2180,6 @@ QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersi overloadError = true; } } -#endif if (overloadError) { if (hasCopied) raw->release(); @@ -2231,6 +2187,7 @@ QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersi error.setDescription(QLatin1String("Type ") + type->qmlTypeName() + QLatin1Char(' ') + QString::number(type->majorVersion()) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation.")); return 0; } +#endif if (!hasCopied) raw->addref(); typePropertyCache.insert(qMakePair(type, minorVersion), raw); @@ -2281,8 +2238,8 @@ bool QQmlEnginePrivate::isList(int t) const int QQmlEnginePrivate::listType(int t) const { Locker locker(this); - QHash<int, int>::ConstIterator iter = m_qmlLists.find(t); - if (iter != m_qmlLists.end()) + QHash<int, int>::ConstIterator iter = m_qmlLists.constFind(t); + if (iter != m_qmlLists.cend()) return *iter; else return QQmlMetaType::listType(t); @@ -2291,8 +2248,8 @@ int QQmlEnginePrivate::listType(int t) const QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const { Locker locker(this); - QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.find(t); - if (iter != m_compositeTypes.end()) { + QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.constFind(t); + if (iter != m_compositeTypes.cend()) { return QQmlMetaObject((*iter)->rootPropertyCache); } else { QQmlType *type = QQmlMetaType::qmlType(t); @@ -2303,8 +2260,8 @@ QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const { Locker locker(this); - QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.find(t); - if (iter != m_compositeTypes.end()) { + QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.constFind(t); + if (iter != m_compositeTypes.cend()) { return QQmlMetaObject((*iter)->rootPropertyCache); } else { QQmlType *type = QQmlMetaType::qmlType(t); @@ -2315,8 +2272,8 @@ QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t) { Locker locker(this); - QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.find(t); - if (iter != m_compositeTypes.end()) { + QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.constFind(t); + if (iter != m_compositeTypes.cend()) { return (*iter)->rootPropertyCache; } else { QQmlType *type = QQmlMetaType::qmlType(t); @@ -2328,8 +2285,8 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t) QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t) { Locker locker(this); - QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.find(t); - if (iter != m_compositeTypes.end()) { + QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.constFind(t); + if (iter != m_compositeTypes.cend()) { return (*iter)->rootPropertyCache; } else { QQmlType *type = QQmlMetaType::qmlType(t); @@ -2482,6 +2439,8 @@ bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */) Returns the QQmlEngine associated with \a object, if any. This is equivalent to QQmlEngine::contextForObject(object)->engine(), but more efficient. + \note Add \c{#include <QtQml>} to use this function. + \sa {QQmlEngine::contextForObject()}{contextForObject()}, qmlContext() */ @@ -2492,6 +2451,8 @@ bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */) Returns the QQmlContext associated with \a object, if any. This is equivalent to QQmlEngine::contextForObject(object). + \note Add \c{#include <QtQml>} to use this function. + \sa {QQmlEngine::contextForObject()}{contextForObject()}, qmlEngine() */ diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index df673c1fd5..61a884279d 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -52,7 +52,9 @@ public: Image, Pixmap, Texture, - Invalid + Invalid, + ImageResponse + // ### Qt6: reorder these, and give Invalid a fixed large value }; enum Flag { diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index f1fbad3cf8..26ee3bd655 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -85,7 +85,6 @@ class QQmlImportDatabase; class QNetworkReply; class QNetworkAccessManager; class QQmlNetworkAccessManagerFactory; -class QQmlAbstractBinding; class QQmlTypeNameCache; class QQmlComponentAttached; class QQmlCleanup; @@ -95,6 +94,7 @@ class QQmlObjectCreator; class QDir; class QQmlIncubator; class QQmlProfiler; +class QQmlPropertyCapture; // This needs to be declared here so that the pool for it can live in QQmlEnginePrivate. // The inline method definitions are in qqmljavascriptexpression_p.h @@ -123,21 +123,11 @@ public: // is just qmlClearTypeRegistrations (which can't be called while an engine exists) static bool baseModulesUninitialized; - class PropertyCapture { - public: - inline virtual ~PropertyCapture() {} - virtual void captureProperty(QQmlNotifier *) = 0; - virtual void captureProperty(QObject *, int, int) = 0; - }; - - PropertyCapture *propertyCapture; - inline void captureProperty(QQmlNotifier *); - inline void captureProperty(QObject *, int, int); + QQmlPropertyCapture *propertyCapture; QRecyclePool<QQmlJavaScriptExpressionGuard> jsExpressionGuardPool; QQmlContext *rootContext; - bool isDebugging; QQmlProfiler *profiler; void enableProfiler(); @@ -208,7 +198,7 @@ public: inline static void deleteInEngineThread(QQmlEngine *, T *); // These methods may be called from the loader thread - inline QQmlPropertyCache *cache(QQmlType *, int, QQmlError &error); + inline QQmlPropertyCache *cache(QQmlType *, int); using QJSEnginePrivate::cache; // These methods may be called from the loader thread @@ -262,7 +252,7 @@ public: private: // Must be called locked - QQmlPropertyCache *createCache(QQmlType *, int, QQmlError &error); + QQmlPropertyCache *createCache(QQmlType *, int); // These members must be protected by a QQmlEnginePrivate::Locker as they are required by // the threaded loader. Only access them through their respective accessor methods. @@ -346,7 +336,7 @@ Returns a QQmlPropertyCache for \a type with \a minorVersion. The returned cache is not referenced, so if it is to be stored, call addref(). */ -QQmlPropertyCache *QQmlEnginePrivate::cache(QQmlType *type, int minorVersion, QQmlError &error) +QQmlPropertyCache *QQmlEnginePrivate::cache(QQmlType *type, int minorVersion) { Q_ASSERT(type); @@ -355,7 +345,7 @@ QQmlPropertyCache *QQmlEnginePrivate::cache(QQmlType *type, int minorVersion, QQ Locker locker(this); QQmlPropertyCache *rv = typePropertyCache.value(qMakePair(type, minorVersion)); - if (!rv) rv = createCache(type, minorVersion, error); + if (!rv) rv = createCache(type, minorVersion); return rv; } @@ -414,18 +404,6 @@ QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e) return get(qmlEngine); } -void QQmlEnginePrivate::captureProperty(QQmlNotifier *n) -{ - if (propertyCapture) - propertyCapture->captureProperty(n); -} - -void QQmlEnginePrivate::captureProperty(QObject *o, int c, int n) -{ - if (propertyCapture) - propertyCapture->captureProperty(o, c, n); -} - void QQmlEnginePrivate::setDebugChangesCache(const QHash<QUrl, QByteArray> &changes) { Locker locker(this); diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index 35e0bc8c64..e9700712e9 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -45,13 +45,8 @@ QT_BEGIN_NAMESPACE -static QQmlJavaScriptExpression::VTable QQmlExpressionPrivate_jsvtable = { - QQmlExpressionPrivate::expressionIdentifier, - QQmlExpressionPrivate::expressionChanged -}; - QQmlExpressionPrivate::QQmlExpressionPrivate() -: QQmlJavaScriptExpression(&QQmlExpressionPrivate_jsvtable), +: QQmlJavaScriptExpression(), expressionFunctionValid(true), line(0), column(0) { @@ -65,7 +60,7 @@ void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, QOb { expression = expr; - QQmlAbstractExpression::setContext(ctxt); + QQmlJavaScriptExpression::setContext(ctxt); setScopeObject(me); expressionFunctionValid = false; } @@ -74,9 +69,9 @@ void QQmlExpressionPrivate::init(QQmlContextData *ctxt, QV4::Function *runtimeFu { expressionFunctionValid = true; QV4::ExecutionEngine *engine = QQmlEnginePrivate::getV4Engine(ctxt->engine); - function.set(engine, QV4::QmlBindingWrapper::createQmlCallableForFunction(ctxt, me, runtimeFunction)); + m_function.set(engine, QV4::FunctionObject::createQmlFunction(ctxt, me, runtimeFunction)); - QQmlAbstractExpression::setContext(ctxt); + QQmlJavaScriptExpression::setContext(ctxt); setScopeObject(me); } @@ -246,18 +241,12 @@ void QQmlExpression::setExpression(const QString &expression) // Must be called with a valid handle scope QV4::ReturnedValue QQmlExpressionPrivate::v4value(bool *isUndefined) { - Q_Q(QQmlExpression); - - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(q->engine())->v4engine(); - if (!expressionFunctionValid) { - function.set(v4, qmlBinding(context(), scopeObject(), expression, url, line, &qmlscope)); + createQmlBinding(context(), scopeObject(), expression, url, line); expressionFunctionValid = true; } - QV4::Scope scope(v4); - QV4::ScopedValue f(scope, function.value()); - return evaluate(context(), f, isUndefined); + return evaluate(isUndefined); } QVariant QQmlExpressionPrivate::value(bool *isUndefined) @@ -432,22 +421,15 @@ QQmlError QQmlExpression::error() const calling QQmlExpression::evaluate()) before this signal will be emitted. */ -void QQmlExpressionPrivate::expressionChanged(QQmlJavaScriptExpression *e) -{ - QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e); - This->expressionChanged(); -} - void QQmlExpressionPrivate::expressionChanged() { Q_Q(QQmlExpression); emit q->valueChanged(); } -QString QQmlExpressionPrivate::expressionIdentifier(QQmlJavaScriptExpression *e) +QString QQmlExpressionPrivate::expressionIdentifier() { - QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e); - return QLatin1Char('"') + This->expression + QLatin1Char('"'); + return QLatin1Char('"') + expression + QLatin1Char('"'); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h index d8da387878..2303539194 100644 --- a/src/qml/qml/qqmlexpression_p.h +++ b/src/qml/qml/qqmlexpression_p.h @@ -52,7 +52,6 @@ #include <private/qflagpointer_p.h> #include <private/qdeletewatcher_p.h> #include <private/qpointervaluepair_p.h> -#include <private/qqmlabstractexpression_p.h> #include <private/qqmljavascriptexpression_p.h> QT_BEGIN_NAMESPACE @@ -60,8 +59,7 @@ QT_BEGIN_NAMESPACE class QQmlExpression; class QString; class QQmlExpressionPrivate : public QObjectPrivate, - public QQmlJavaScriptExpression, - public QQmlAbstractExpression + public QQmlJavaScriptExpression { Q_DECLARE_PUBLIC(QQmlExpression) public: @@ -82,16 +80,12 @@ public: bool expressionFunctionValid:1; - // "Inherited" from QQmlJavaScriptExpression - static QString expressionIdentifier(QQmlJavaScriptExpression *); - static void expressionChanged(QQmlJavaScriptExpression *); + // Inherited from QQmlJavaScriptExpression + virtual QString expressionIdentifier(); virtual void expressionChanged(); QString expression; - QV4::PersistentValue qmlscope; - QV4::PersistentValue function; - QString url; // This is a QString for a reason. QUrls are slooooooow... quint16 line; quint16 column; diff --git a/src/qml/qml/qqmlfileselector.cpp b/src/qml/qml/qqmlfileselector.cpp index 8597e8a5c7..ab880b7069 100644 --- a/src/qml/qml/qqmlfileselector.cpp +++ b/src/qml/qml/qqmlfileselector.cpp @@ -123,6 +123,12 @@ QQmlFileSelectorPrivate::QQmlFileSelectorPrivate() myInstance.reset(new QQmlFileSelectorInterceptor(this)); } +QQmlFileSelectorPrivate::~QQmlFileSelectorPrivate() +{ + if (ownSelector) + delete selector; +} + /*! Sets the QFileSelector instance for use by the QQmlFileSelector to \a selector. QQmlFileSelector does not take ownership of the new QFileSelector. To reset QQmlFileSelector diff --git a/src/qml/qml/qqmlfileselector_p.h b/src/qml/qml/qqmlfileselector_p.h index 58248bf1c1..46ea027bc1 100644 --- a/src/qml/qml/qqmlfileselector_p.h +++ b/src/qml/qml/qqmlfileselector_p.h @@ -60,6 +60,8 @@ class Q_QML_PRIVATE_EXPORT QQmlFileSelectorPrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QQmlFileSelector) public: QQmlFileSelectorPrivate(); + ~QQmlFileSelectorPrivate(); + QFileSelector* selector; QPointer<QQmlEngine> engine; bool ownSelector; diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp index d904242f93..aa2b4b6aee 100644 --- a/src/qml/qml/qqmlglobal.cpp +++ b/src/qml/qml/qqmlglobal.cpp @@ -62,40 +62,11 @@ const QMetaObject *QQmlValueTypeProvider::metaObjectForMetaType(int type) return 0; } -bool QQmlValueTypeProvider::initValueType(int type, void *data, size_t n) +bool QQmlValueTypeProvider::initValueType(int type, QVariant& dst) { - Q_ASSERT(data); - - QQmlValueTypeProvider *p = this; - do { - if (p->init(type, data, n)) - return true; - } while ((p = p->next)); - - return false; -} - -bool QQmlValueTypeProvider::destroyValueType(int type, void *data, size_t n) -{ - Q_ASSERT(data); - QQmlValueTypeProvider *p = this; do { - if (p->destroy(type, data, n)) - return true; - } while ((p = p->next)); - - return false; -} - -bool QQmlValueTypeProvider::copyValueType(int type, const void *src, void *dst, size_t n) -{ - Q_ASSERT(src); - Q_ASSERT(dst); - - QQmlValueTypeProvider *p = this; - do { - if (p->copy(type, src, dst, n)) + if (p->init(type, dst)) return true; } while ((p = p->next)); @@ -188,14 +159,13 @@ QVariant QQmlValueTypeProvider::createVariantFromJsObject(int type, QQmlV4Handle return QVariant(); } -bool QQmlValueTypeProvider::equalValueType(int type, const void *lhs, const void *rhs, size_t rhsSize) +bool QQmlValueTypeProvider::equalValueType(int type, const void *lhs, const QVariant& rhs) { Q_ASSERT(lhs); - Q_ASSERT(rhs); QQmlValueTypeProvider *p = this; do { - if (p->equal(type, lhs, rhs, rhsSize)) + if (p->equal(type, lhs, rhs)) return true; } while ((p = p->next)); @@ -216,28 +186,26 @@ bool QQmlValueTypeProvider::storeValueType(int type, const void *src, void *dst, return false; } -bool QQmlValueTypeProvider::readValueType(int srcType, const void *src, size_t srcSize, int dstType, void *dst) +bool QQmlValueTypeProvider::readValueType(const QVariant& src, void *dst, int dstType) { - Q_ASSERT(src); Q_ASSERT(dst); QQmlValueTypeProvider *p = this; do { - if (p->read(srcType, src, srcSize, dstType, dst)) + if (p->read(src, dst, dstType)) return true; } while ((p = p->next)); return false; } -bool QQmlValueTypeProvider::writeValueType(int type, const void *src, void *dst, size_t n) +bool QQmlValueTypeProvider::writeValueType(int type, const void *src, QVariant& dst) { Q_ASSERT(src); - Q_ASSERT(dst); QQmlValueTypeProvider *p = this; do { - if (p->write(type, src, dst, n)) + if (p->write(type, src, dst)) return true; } while ((p = p->next)); @@ -245,19 +213,17 @@ bool QQmlValueTypeProvider::writeValueType(int type, const void *src, void *dst, } const QMetaObject *QQmlValueTypeProvider::getMetaObjectForMetaType(int) { return 0; } -bool QQmlValueTypeProvider::init(int, void *, size_t) { return false; } -bool QQmlValueTypeProvider::destroy(int, void *, size_t) { return false; } -bool QQmlValueTypeProvider::copy(int, const void *, void *, size_t) { return false; } +bool QQmlValueTypeProvider::init(int, QVariant&) { return false; } bool QQmlValueTypeProvider::create(int, int, const void *[], QVariant *) { return false; } bool QQmlValueTypeProvider::createFromString(int, const QString &, void *, size_t) { return false; } bool QQmlValueTypeProvider::createStringFrom(int, const void *, QString *) { return false; } bool QQmlValueTypeProvider::variantFromString(const QString &, QVariant *) { return false; } bool QQmlValueTypeProvider::variantFromString(int, const QString &, QVariant *) { return false; } bool QQmlValueTypeProvider::variantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, QVariant *) { return false; } -bool QQmlValueTypeProvider::equal(int, const void *, const void *, size_t) { return false; } +bool QQmlValueTypeProvider::equal(int, const void *, const QVariant&) { return false; } bool QQmlValueTypeProvider::store(int, const void *, void *, size_t) { return false; } -bool QQmlValueTypeProvider::read(int, const void *, size_t, int, void *) { return false; } -bool QQmlValueTypeProvider::write(int, const void *, void *, size_t) { return false; } +bool QQmlValueTypeProvider::read(const QVariant&, void *, int) { return false; } +bool QQmlValueTypeProvider::write(int, const void *, QVariant&) { return false; } Q_GLOBAL_STATIC(QQmlValueTypeProvider, nullValueTypeProvider) static QQmlValueTypeProvider *valueTypeProvider = 0; @@ -359,7 +325,7 @@ QObject *QQmlGuiProvider::inputMethod() { // We don't have any input method code by default QObject *o = new QObject(); - o->setObjectName(QString::fromLatin1("No inputMethod available")); + o->setObjectName(QStringLiteral("No inputMethod available")); QQmlEngine::setObjectOwnership(o, QQmlEngine::JavaScriptOwnership); return o; } @@ -368,7 +334,7 @@ QObject *QQmlGuiProvider::inputMethod() QObject *QQmlGuiProvider::styleHints() { QObject *o = new QObject(); - o->setObjectName(QString::fromLatin1("No styleHints available")); + o->setObjectName(QStringLiteral("No styleHints available")); QQmlEngine::setObjectOwnership(o, QQmlEngine::JavaScriptOwnership); return o; } diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h index 7856d85376..23cfc24e7a 100644 --- a/src/qml/qml/qqmlglobal_p.h +++ b/src/qml/qml/qqmlglobal_p.h @@ -34,6 +34,17 @@ #ifndef QQMLGLOBAL_H #define QQMLGLOBAL_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <private/qtqmlglobal_p.h> #include <QtCore/QObject> #include <private/qqmlpropertycache_p.h> @@ -48,10 +59,12 @@ QT_BEGIN_NAMESPACE { \ static enum { Yes, No, Unknown } status = Unknown; \ if (status == Unknown) { \ - QByteArray v = qgetenv(#var); \ - bool value = !v.isEmpty() && v != "0" && v != "false"; \ - if (value) status = Yes; \ - else status = No; \ + status = No; \ + if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty(#var))) { \ + const QByteArray v = qgetenv(#var); \ + if (v != "0" && v != "false") \ + status = Yes; \ + } \ } \ return status == Yes; \ } @@ -224,9 +237,7 @@ public: const QMetaObject *metaObjectForMetaType(int); - bool initValueType(int, void *, size_t); - bool destroyValueType(int, void *, size_t); - bool copyValueType(int, const void *, void *, size_t); + bool initValueType(int, QVariant&); QVariant createValueType(int, int, const void *[]); bool createValueFromString(int, const QString &, void *, size_t); @@ -236,16 +247,14 @@ public: QVariant createVariantFromString(int, const QString &, bool *); QVariant createVariantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, bool*); - bool equalValueType(int, const void *, const void *, size_t); + bool equalValueType(int, const void *, const QVariant&); bool storeValueType(int, const void *, void *, size_t); - bool readValueType(int, const void *, size_t, int, void *); - bool writeValueType(int, const void *, void *, size_t); + bool readValueType(const QVariant&, void *, int); + bool writeValueType(int, const void *, QVariant&); private: virtual const QMetaObject *getMetaObjectForMetaType(int); - virtual bool init(int, void *, size_t); - virtual bool destroy(int, void *, size_t); - virtual bool copy(int, const void *, void *, size_t); + virtual bool init(int, QVariant&); virtual bool create(int, int, const void *[], QVariant *); virtual bool createFromString(int, const QString &, void *, size_t); @@ -255,10 +264,10 @@ private: virtual bool variantFromString(int, const QString &, QVariant *); virtual bool variantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, QVariant *); - virtual bool equal(int, const void *, const void *, size_t); + virtual bool equal(int, const void *, const QVariant&); virtual bool store(int, const void *, void *, size_t); - virtual bool read(int, const void *, size_t, int, void *); - virtual bool write(int, const void *, void *, size_t); + virtual bool read(const QVariant&, void *, int); + virtual bool write(int, const void *, QVariant&); friend Q_QML_PRIVATE_EXPORT void QQml_addValueTypeProvider(QQmlValueTypeProvider *); friend Q_QML_PRIVATE_EXPORT void QQml_removeValueTypeProvider(QQmlValueTypeProvider *); diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index ff48a10d95..d538199520 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -125,7 +125,9 @@ bool isPathAbsolute(const QString &path) } // If the type does not already exist as a file import, add the type and return the new type -QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, bool isCompositeSingleton, QList<QQmlError> *errors) +QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, + bool isCompositeSingleton, QList<QQmlError> *errors, + int majorVersion=-1, int minorVersion=-1) { QUrl url(urlString); QQmlType *ret = QQmlMetaType::qmlType(url); @@ -140,8 +142,8 @@ QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeNa QQmlPrivate::RegisterCompositeSingletonType reg = { url, "", //Empty URI indicates loaded via file imports - -1, - -1, + majorVersion, + minorVersion, buf.constData() }; ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeSingletonRegistration, ®)); @@ -149,8 +151,8 @@ QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeNa QQmlPrivate::RegisterCompositeType reg = { url, "", //Empty URI indicates loaded via file imports - -1, - -1, + majorVersion, + minorVersion, buf.constData() }; ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, ®)); @@ -169,6 +171,7 @@ QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeNa } // namespace +#ifndef QT_NO_LIBRARY struct RegisteredPlugin { QString uri; QPluginLoader* loader; @@ -193,6 +196,7 @@ void qmlClearEnginePlugins() } typedef QPair<QStaticPlugin, QJsonArray> StaticPluginPair; +#endif class QQmlImportNamespace { @@ -292,9 +296,10 @@ public: const QString &uri, const QString &url, int vmaj, int vmin, QV4::CompiledData::Import::ImportType type, QList<QQmlError> *errors, bool lowPrecedence = false); - +#ifndef QT_NO_LIBRARY bool populatePluginPairVector(QVector<StaticPluginPair> &result, const QString &uri, const QString &qmldirPath, QList<QQmlError> *errors); +#endif }; /*! @@ -382,7 +387,7 @@ void QQmlImports::populateCache(QQmlTypeNameCache *cache) const // We need to exclude the entry for the current baseUrl. This can happen for example // when handling qmldir files on the remote dir case and the current type is marked as // singleton. -bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QString baseUrl) +bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QString &baseUrl) { if (importUrl.isEmpty()) return false; @@ -398,7 +403,7 @@ bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QSt return true; } -void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports::CompositeSingletonReference> &resultList, QUrl baseUrl) +void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports::CompositeSingletonReference> &resultList, const QUrl &baseUrl) { typedef QQmlDirComponents::const_iterator ConstIterator; @@ -413,6 +418,8 @@ void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports:: QQmlImports::CompositeSingletonReference ref; ref.typeName = cit->typeName; ref.prefix = set.prefix; + ref.majorVersion = cit->majorVersion; + ref.minorVersion = cit->minorVersion; resultList.append(ref); } } @@ -653,7 +660,10 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, } if (candidate != end) { - QQmlType *returnType = getTypeForUrl(componentUrl, type, isCompositeSingleton, 0); + int major = vmajor ? *vmajor : -1; + int minor = vminor ? *vminor : -1; + QQmlType *returnType = getTypeForUrl(componentUrl, type, isCompositeSingleton, 0, + major, minor); if (type_return) *type_return = returnType; return returnType != 0; @@ -826,6 +836,7 @@ QQmlImportNamespace *QQmlImportsPrivate::findQualifiedNamespace(const QHashedStr } +#ifndef QT_NO_LIBRARY /*! Get all static plugins that are QML plugins and has a meta data URI that begins with \a uri. Note that if e.g uri == "a", and different plugins have meta data "a", "a.2.1", "a.b.c", all @@ -869,6 +880,7 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res } return true; } +#endif /*! Import an extension defined by a qmldir file. @@ -995,6 +1007,13 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, } #else + Q_UNUSED(qmldirFilePath); + Q_UNUSED(uri); + Q_UNUSED(vmaj); + Q_UNUSED(vmin); + Q_UNUSED(database); + Q_UNUSED(qmldir); + Q_UNUSED(errors); return false; #endif // QT_NO_LIBRARY return true; @@ -1550,8 +1569,8 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e) addImportPath(installImportsPath); // env import paths - QByteArray envImportPath = qgetenv("QML2_IMPORT_PATH"); - if (!envImportPath.isEmpty()) { + if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QML2_IMPORT_PATH"))) { + const QByteArray envImportPath = qgetenv("QML2_IMPORT_PATH"); #if defined(Q_OS_WIN) QLatin1Char pathSep(';'); #else @@ -1639,8 +1658,6 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, \header \li Platform \li Valid suffixes \row \li Windows \li \c .dll \row \li Unix/Linux \li \c .so - \row \li AIX \li \c .a - \row \li HP-UX \li \c .sl, \c .so (HP-UXi) \row \li OS X \li \c .dylib, \c .bundle, \c .so \endtable @@ -1657,9 +1674,7 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, << QLatin1String("d.dll") // try a qmake-style debug build first # endif << QLatin1String(".dll")); -#else - -# if defined(Q_OS_DARWIN) +#elif defined(Q_OS_DARWIN) return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, QStringList() @@ -1673,31 +1688,8 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, << QLatin1String(".so") << QLatin1String(".bundle"), QLatin1String("lib")); -# else // Generic Unix - QStringList validSuffixList; - -# if defined(Q_OS_HPUX) -/* - See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF": - "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit), - the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix." - */ - validSuffixList << QLatin1String(".sl"); -# if defined __ia64 - validSuffixList << QLatin1String(".so"); -# endif -# elif defined(Q_OS_AIX) - validSuffixList << QLatin1String(".a") << QLatin1String(".so"); -# elif defined(Q_OS_UNIX) - validSuffixList << QLatin1String(".so"); -# endif - - // Examples of valid library names: - // libfoo.so - - return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib")); -# endif - +# else // Unix + return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, QStringList() << QLatin1String(".so"), QLatin1String("lib")); #endif } @@ -1931,6 +1923,12 @@ bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &ba return true; #else + Q_UNUSED(instance); + Q_UNUSED(basePath); + Q_UNUSED(uri); + Q_UNUSED(typeNamespace); + Q_UNUSED(vmaj); + Q_UNUSED(errors); return false; #endif } @@ -2011,6 +2009,11 @@ bool QQmlImportDatabase::importDynamicPlugin(const QString &filePath, const QStr return true; #else + Q_UNUSED(filePath); + Q_UNUSED(uri); + Q_UNUSED(typeNamespace); + Q_UNUSED(vmaj); + Q_UNUSED(errors); return false; #endif } diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index bda87f29b1..951b9752f3 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -118,6 +118,8 @@ public: { QString typeName; QString prefix; + int majorVersion; + int minorVersion; }; QList<CompositeSingletonReference> resolvedCompositeSingletons() const; diff --git a/src/qml/qml/qqmlinfo.cpp b/src/qml/qml/qqmlinfo.cpp index b9f96a724c..7a801032d3 100644 --- a/src/qml/qml/qqmlinfo.cpp +++ b/src/qml/qml/qqmlinfo.cpp @@ -109,34 +109,8 @@ QQmlInfo::~QQmlInfo() if (object) { engine = qmlEngine(d->object); - QString typeName; - QQmlType *type = QQmlMetaType::qmlType(object->metaObject()); - if (type) { - typeName = type->qmlTypeName(); - int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); - if (lastSlash != -1) - typeName = typeName.mid(lastSlash+1); - } else { - typeName = QString::fromUtf8(object->metaObject()->className()); - int marker = typeName.indexOf(QLatin1String("_QMLTYPE_")); - if (marker != -1) - typeName = typeName.left(marker); - - marker = typeName.indexOf(QLatin1String("_QML_")); - if (marker != -1) { - typeName = typeName.left(marker); - typeName += QLatin1Char('*'); - type = QQmlMetaType::qmlType(QMetaType::type(typeName.toLatin1())); - if (type) { - typeName = type->qmlTypeName(); - int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); - if (lastSlash != -1) - typeName = typeName.mid(lastSlash+1); - } - } - } - d->buffer.prepend(QLatin1String("QML ") + typeName + QLatin1String(": ")); + d->buffer.prepend(QLatin1String("QML ") + QQmlMetaType::prettyTypeName(object) + QLatin1String(": ")); QQmlData *ddata = QQmlData::get(object, false); if (ddata && ddata->outerContext) { diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 02bd1c4b83..5938ebf5d7 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -35,7 +35,7 @@ #include <private/qqmlexpression_p.h> #include <private/qqmlcontextwrapper_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4script_p.h> #include <private/qv4errorobject_p.h> @@ -83,13 +83,22 @@ void QQmlDelayedError::catchJavaScriptException(QV4::ExecutionEngine *engine) } -QQmlJavaScriptExpression::QQmlJavaScriptExpression(VTable *v) -: m_vtable(v) +QQmlJavaScriptExpression::QQmlJavaScriptExpression() + : m_error(0), + m_context(0), + m_prevExpression(0), + m_nextExpression(0) { } QQmlJavaScriptExpression::~QQmlJavaScriptExpression() { + if (m_prevExpression) { + *m_prevExpression = m_nextExpression; + if (m_nextExpression) + m_nextExpression->m_prevExpression = m_prevExpression; + } + clearGuards(); if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion. m_scopeObject.asT2()->_s = 0; @@ -106,40 +115,62 @@ void QQmlJavaScriptExpression::resetNotifyOnValueChanged() clearGuards(); } -QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QQmlContextData *context, - const QV4::Value &function, bool *isUndefined) +void QQmlJavaScriptExpression::setContext(QQmlContextData *context) { - QV4::ExecutionEngine *v4 = QV8Engine::getV4(context->engine); + if (m_prevExpression) { + *m_prevExpression = m_nextExpression; + if (m_nextExpression) + m_nextExpression->m_prevExpression = m_prevExpression; + m_prevExpression = 0; + m_nextExpression = 0; + } + + m_context = context; + + if (context) { + m_nextExpression = context->expressions; + if (m_nextExpression) + m_nextExpression->m_prevExpression = &m_nextExpression; + m_prevExpression = &context->expressions; + context->expressions = this; + } +} + +void QQmlJavaScriptExpression::refresh() +{ +} + +QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(bool *isUndefined) +{ + QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_context->engine); QV4::Scope scope(v4); QV4::ScopedCallData callData(scope); - return evaluate(context, function, callData, isUndefined); + return evaluate(callData, isUndefined); } -QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QQmlContextData *context, - const QV4::Value &function, - QV4::CallData *callData, - bool *isUndefined) +QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined) { - Q_ASSERT(context && context->engine); + Q_ASSERT(m_context && m_context->engine); - if (function.isUndefined()) { + QV4::Value *f = m_function.valueRef(); + if (!f || f->isUndefined()) { if (isUndefined) *isUndefined = true; return QV4::Encode::undefined(); } - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_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); + QQmlPropertyCapture capture(m_context->engine, this, &watcher); - QQmlEnginePrivate::PropertyCapture *lastPropertyCapture = ep->propertyCapture; - ep->propertyCapture = notifyOnValueChanged()?&capture:0; + QQmlPropertyCapture *lastPropertyCapture = ep->propertyCapture; + ep->propertyCapture = notifyOnValueChanged() ? &capture : 0; if (notifyOnValueChanged()) @@ -148,14 +179,14 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QQmlContextData *context, QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); QV4::Scope scope(v4); QV4::ScopedValue result(scope, QV4::Primitive::undefinedValue()); - callData->thisObject = v4->globalObject(); + callData->thisObject = v4->globalObject; if (scopeObject()) { QV4::ScopedValue value(scope, QV4::QObjectWrapper::wrap(v4, scopeObject())); if (value->isObject()) callData->thisObject = value; } - result = function.asFunctionObject()->call(callData); + result = f->as<QV4::FunctionObject>()->call(callData); if (scope.hasException()) { if (watcher.wasDeleted()) scope.engine->catchException(); // ignore exception @@ -178,7 +209,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QQmlContextData *context, capture.errorString = 0; } - while (Guard *g = capture.guards.takeFirst()) + while (QQmlJavaScriptExpressionGuard *g = capture.guards.takeFirst()) g->Delete(); ep->propertyCapture = lastPropertyCapture; @@ -186,7 +217,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QQmlContextData *context, return result->asReturnedValue(); } -void QQmlJavaScriptExpression::GuardCapture::captureProperty(QQmlNotifier *n) +void QQmlPropertyCapture::captureProperty(QQmlNotifier *n) { if (watcher->wasDeleted()) return; @@ -196,13 +227,13 @@ void QQmlJavaScriptExpression::GuardCapture::captureProperty(QQmlNotifier *n) while (!guards.isEmpty() && !guards.first()->isConnected(n)) guards.takeFirst()->Delete(); - Guard *g = 0; + QQmlJavaScriptExpressionGuard *g = 0; if (!guards.isEmpty()) { g = guards.takeFirst(); g->cancelNotify(); Q_ASSERT(g->isConnected(n)); } else { - g = Guard::New(expression, engine); + g = QQmlJavaScriptExpressionGuard::New(expression, engine); g->connect(n); } @@ -213,7 +244,7 @@ void QQmlJavaScriptExpression::GuardCapture::captureProperty(QQmlNotifier *n) \a n is in the signal index range (see QObjectPrivate::signalIndex()). */ -void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, int n) +void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n) { if (watcher->wasDeleted()) return; @@ -223,7 +254,7 @@ void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, if (!errorString) { errorString = new QStringList; QString preamble = QLatin1String("QQmlExpression: Expression ") + - expression->m_vtable->expressionIdentifier(expression) + + expression->expressionIdentifier() + QLatin1String(" depends on non-NOTIFYable properties:"); errorString->append(preamble); } @@ -242,13 +273,13 @@ void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, while (!guards.isEmpty() && !guards.first()->isConnected(o, n)) guards.takeFirst()->Delete(); - Guard *g = 0; + QQmlJavaScriptExpressionGuard *g = 0; if (!guards.isEmpty()) { g = guards.takeFirst(); g->cancelNotify(); Q_ASSERT(g->isConnected(o, n)); } else { - g = Guard::New(expression, engine); + g = QQmlJavaScriptExpressionGuard::New(expression, engine); g->connect(o, n, engine); } @@ -256,33 +287,77 @@ void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, } } -void QQmlJavaScriptExpression::clearError() +void QQmlPropertyCapture::registerQmlDependencies(QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction) { - if (m_vtable.hasValue()) { - m_vtable.value().clearError(); - m_vtable.value().removeError(); + // Let the caller check and avoid the function call :) + Q_ASSERT(compiledFunction->hasQmlDependencies()); + + QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : 0; + if (!ep) + return; + QQmlPropertyCapture *capture = ep->propertyCapture; + if (!capture) + return; + + QV4::Scope scope(engine); + QV4::Scoped<QV4::QmlContext> context(scope, engine->qmlContext()); + QQmlContextData *qmlContext = context->qmlContext(); + + const quint32 *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable(); + const int idObjectDependencyCount = compiledFunction->nDependingIdObjects; + for (int i = 0; i < idObjectDependencyCount; ++i, ++idObjectDependency) { + Q_ASSERT(int(*idObjectDependency) < qmlContext->idValueCount); + capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings); + } + + Q_ASSERT(qmlContext->contextObject); + const quint32 *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable(); + const int contextPropertyDependencyCount = compiledFunction->nDependingContextProperties; + for (int i = 0; i < contextPropertyDependencyCount; ++i) { + const int propertyIndex = *contextPropertyDependency++; + const int notifyIndex = *contextPropertyDependency++; + capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex); + } + + QObject *scopeObject = context->qmlScope(); + const quint32 *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable(); + const int scopePropertyDependencyCount = compiledFunction->nDependingScopeProperties; + for (int i = 0; i < scopePropertyDependencyCount; ++i) { + const int propertyIndex = *scopePropertyDependency++; + const int notifyIndex = *scopePropertyDependency++; + capture->captureProperty(scopeObject, propertyIndex, notifyIndex); } + +} + + +void QQmlJavaScriptExpression::clearError() +{ + if (m_error) + delete m_error; + m_error = 0; } QQmlError QQmlJavaScriptExpression::error(QQmlEngine *engine) const { Q_UNUSED(engine); - if (m_vtable.hasValue()) - return m_vtable.constValue()->error(); + if (m_error) + return m_error->error(); else return QQmlError(); } QQmlDelayedError *QQmlJavaScriptExpression::delayedError() { - return &m_vtable.value(); + if (!m_error) + m_error = new QQmlDelayedError; + return m_error; } QV4::ReturnedValue QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scopeObject, - const QString &code, const QString &filename, quint16 line, - QV4::PersistentValue *qmlscope) + const QString &code, const QString &filename, quint16 line) { QQmlEngine *engine = ctxt->engine; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); @@ -290,8 +365,8 @@ QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scopeObje QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); QV4::Scope scope(v4); - QV4::ScopedObject qmlScopeObject(scope, QV4::QmlContextWrapper::qmlScope(v4, ctxt, scopeObject)); - QV4::Script script(v4, qmlScopeObject, code, filename, line); + QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->rootContext()->newQmlContext(ctxt, scopeObject)); + QV4::Script script(v4, qmlContext, code, filename, line); QV4::ScopedValue result(scope); script.parse(); if (!v4->hasException) @@ -308,14 +383,11 @@ QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scopeObje ep->warning(error); return QV4::Encode::undefined(); } - if (qmlscope) - qmlscope->set(v4, qmlScopeObject); return result->asReturnedValue(); } -QV4::ReturnedValue QQmlJavaScriptExpression::qmlBinding(QQmlContextData *ctxt, QObject *qmlScope, - const QString &code, const QString &filename, quint16 line, - QV4::PersistentValue *qmlscope) +void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject *qmlScope, + const QString &code, const QString &filename, quint16 line) { QQmlEngine *engine = ctxt->engine; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); @@ -323,8 +395,8 @@ QV4::ReturnedValue QQmlJavaScriptExpression::qmlBinding(QQmlContextData *ctxt, Q QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); QV4::Scope scope(v4); - QV4::ScopedObject qmlScopeObject(scope, QV4::QmlContextWrapper::qmlScope(v4, ctxt, qmlScope)); - QV4::Script script(v4, qmlScopeObject, code, filename, line); + QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->rootContext()->newQmlContext(ctxt, qmlScope)); + QV4::Script script(v4, qmlContext, code, filename, line); QV4::ScopedValue result(scope); script.parse(); if (!v4->hasException) @@ -339,17 +411,15 @@ QV4::ReturnedValue QQmlJavaScriptExpression::qmlBinding(QQmlContextData *ctxt, Q error.setUrl(QUrl::fromLocalFile(filename)); error.setObject(qmlScope); ep->warning(error); - return QV4::Encode::undefined(); + result = QV4::Encode::undefined(); } - if (qmlscope) - qmlscope->set(v4, qmlScopeObject); - return result->asReturnedValue(); + m_function.set(v4, result); } void QQmlJavaScriptExpression::clearGuards() { - while (Guard *g = activeGuards.takeFirst()) + while (QQmlJavaScriptExpressionGuard *g = activeGuards.takeFirst()) g->Delete(); } @@ -358,7 +428,7 @@ void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **) QQmlJavaScriptExpression *expression = static_cast<QQmlJavaScriptExpressionGuard *>(e)->expression; - expression->m_vtable->expressionChanged(expression); + expression->expressionChanged(); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 989d5a0b0d..f0a3741588 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -89,23 +89,17 @@ private: QQmlDelayedError **prevError; }; -class QQmlJavaScriptExpression +class Q_QML_PRIVATE_EXPORT QQmlJavaScriptExpression { public: - // Although this looks crazy, we implement our own "vtable" here, rather than relying on - // C++ virtuals, to save memory. By doing it ourselves, we can overload the storage - // location that is use for the vtable to also store the rarely used delayed error. - // If we use C++ virtuals, we can't do this and it consts us an extra sizeof(void *) in - // memory for every expression. - struct VTable { - QString (*expressionIdentifier)(QQmlJavaScriptExpression *); - void (*expressionChanged)(QQmlJavaScriptExpression *); - }; + QQmlJavaScriptExpression(); + virtual ~QQmlJavaScriptExpression(); - QQmlJavaScriptExpression(VTable *vtable); + virtual QString expressionIdentifier() = 0; + virtual void expressionChanged() = 0; - QV4::ReturnedValue evaluate(QQmlContextData *, const QV4::Value &function, bool *isUndefined); - QV4::ReturnedValue evaluate(QQmlContextData *, const QV4::Value &function, QV4::CallData *callData, bool *isUndefined); + QV4::ReturnedValue evaluate(bool *isUndefined); + QV4::ReturnedValue evaluate(QV4::CallData *callData, bool *isUndefined); inline bool notifyOnValueChanged() const; @@ -115,6 +109,13 @@ public: inline QObject *scopeObject() const; inline void setScopeObject(QObject *v); + bool isValid() const { return context() != 0; } + + QQmlContextData *context() const { return m_context; } + void setContext(QQmlContextData *context); + + virtual void refresh(); + class DeleteWatcher { public: inline DeleteWatcher(QQmlJavaScriptExpression *); @@ -136,46 +137,52 @@ public: static QV4::ReturnedValue evalFunction(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, - quint16 line, - QV4::PersistentValue *qmlscope = 0); - // doesn't require rewriting the expression - static QV4::ReturnedValue qmlBinding(QQmlContextData *ctxt, QObject *scope, - const QString &code, - const QString &filename, quint16 line, - QV4::PersistentValue *qmlscope = 0); + quint16 line); protected: - ~QQmlJavaScriptExpression(); + void createQmlBinding(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line); private: - typedef QQmlJavaScriptExpressionGuard Guard; + friend class QQmlContextData; + friend class QQmlPropertyCapture; friend void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *, void **); - struct GuardCapture : public QQmlEnginePrivate::PropertyCapture { - GuardCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e, DeleteWatcher *w) - : engine(engine), expression(e), watcher(w), errorString(0) { } - - ~GuardCapture() { - Q_ASSERT(guards.isEmpty()); - Q_ASSERT(errorString == 0); - } - - virtual void captureProperty(QQmlNotifier *); - virtual void captureProperty(QObject *, int, int); - - QQmlEngine *engine; - QQmlJavaScriptExpression *expression; - DeleteWatcher *watcher; - QFieldList<Guard, &Guard::next> guards; - QStringList *errorString; - }; - - QPointerValuePair<VTable, QQmlDelayedError> m_vtable; + QQmlDelayedError *m_error; // We store some flag bits in the following flag pointers. // activeGuards:flag1 - notifyOnValueChanged // activeGuards:flag2 - useSharedContext QBiPointer<QObject, DeleteWatcher> m_scopeObject; - QForwardFieldList<Guard, &Guard::next> activeGuards; + QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> activeGuards; + + QQmlContextData *m_context; + QQmlJavaScriptExpression **m_prevExpression; + QQmlJavaScriptExpression *m_nextExpression; + +protected: + QV4::PersistentValue m_function; +}; + +class QQmlPropertyCapture +{ +public: + QQmlPropertyCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e, QQmlJavaScriptExpression::DeleteWatcher *w) + : engine(engine), expression(e), watcher(w), errorString(0) { } + + ~QQmlPropertyCapture() { + Q_ASSERT(guards.isEmpty()); + Q_ASSERT(errorString == 0); + } + + void captureProperty(QQmlNotifier *); + void captureProperty(QObject *, int, int); + + static void registerQmlDependencies(QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction); + + QQmlEngine *engine; + QQmlJavaScriptExpression *expression; + QQmlJavaScriptExpression::DeleteWatcher *watcher; + QFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> guards; + QStringList *errorString; }; QQmlJavaScriptExpression::DeleteWatcher::DeleteWatcher(QQmlJavaScriptExpression *e) @@ -222,18 +229,18 @@ void QQmlJavaScriptExpression::setScopeObject(QObject *v) bool QQmlJavaScriptExpression::hasError() const { - return m_vtable.hasValue() && m_vtable.constValue()->isValid(); + return m_error && m_error->isValid(); } bool QQmlJavaScriptExpression::hasDelayedError() const { - return m_vtable.hasValue(); + return m_error; } QQmlJavaScriptExpressionGuard::QQmlJavaScriptExpressionGuard(QQmlJavaScriptExpression *e) -: expression(e), next(0) + : QQmlNotifierEndpoint(QQmlNotifierEndpoint::QQmlJavaScriptExpressionGuard), + expression(e), next(0) { - setCallback(QQmlNotifierEndpoint::QQmlJavaScriptExpressionGuard); } QQmlJavaScriptExpressionGuard * diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index bcb1e72f0b..942f4f79e7 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -45,10 +45,9 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(QmlListWrapper); -Heap::QmlListWrapper::QmlListWrapper(ExecutionEngine *engine) - : Heap::Object(engine) +Heap::QmlListWrapper::QmlListWrapper() { - QV4::Scope scope(engine); + QV4::Scope scope(internalClass->engine); QV4::ScopedObject o(scope, this); o->setArrayType(Heap::ArrayData::Custom); } @@ -64,7 +63,7 @@ ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QObject *object, i Scope scope(engine); - Scoped<QmlListWrapper> r(scope, engine->memoryManager->alloc<QmlListWrapper>(engine)); + Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocObject<QmlListWrapper>()); r->d()->object = object; r->d()->propertyType = propType; void *args[] = { &r->d()->property, 0 }; @@ -76,7 +75,7 @@ ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, const QQmlListProp { Scope scope(engine); - Scoped<QmlListWrapper> r(scope, engine->memoryManager->alloc<QmlListWrapper>(engine)); + Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocObject<QmlListWrapper>()); r->d()->object = prop.object; r->d()->property = prop; r->d()->propertyType = propType; @@ -92,13 +91,13 @@ QVariant QmlListWrapper::toVariant() const } -ReturnedValue QmlListWrapper::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue QmlListWrapper::get(const Managed *m, String *name, bool *hasProperty) { Q_ASSERT(m->as<QmlListWrapper>()); - QmlListWrapper *w = static_cast<QmlListWrapper *>(m); + const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m); QV4::ExecutionEngine *v4 = w->engine(); - if (name->equals(v4->id_length) && !w->d()->object.isNull()) { + if (name->equals(v4->id_length()) && !w->d()->object.isNull()) { quint32 count = w->d()->property.count ? w->d()->property.count(&w->d()->property) : 0; return Primitive::fromUInt32(count).asReturnedValue(); } @@ -110,12 +109,12 @@ ReturnedValue QmlListWrapper::get(Managed *m, String *name, bool *hasProperty) return Object::get(m, name, hasProperty); } -ReturnedValue QmlListWrapper::getIndexed(Managed *m, uint index, bool *hasProperty) +ReturnedValue QmlListWrapper::getIndexed(const Managed *m, uint index, bool *hasProperty) { Q_UNUSED(hasProperty); Q_ASSERT(m->as<QmlListWrapper>()); - QmlListWrapper *w = static_cast<QmlListWrapper *>(m); + const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m); QV4::ExecutionEngine *v4 = w->engine(); quint32 count = w->d()->property.count ? w->d()->property.count(&w->d()->property) : 0; @@ -138,9 +137,9 @@ void QmlListWrapper::put(Managed *m, String *name, const Value &value) Q_UNUSED(value); } -void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attrs) +void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) { - *name = (Heap::String *)0; + name->setM(0); *index = UINT_MAX; Q_ASSERT(m->as<QmlListWrapper>()); QmlListWrapper *w = static_cast<QmlListWrapper *>(m); diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h index 3590bcb1c9..6df3d83b2e 100644 --- a/src/qml/qml/qqmllistwrapper_p.h +++ b/src/qml/qml/qqmllistwrapper_p.h @@ -50,7 +50,7 @@ #include <QtQml/qqmllist.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4object_p.h> QT_BEGIN_NAMESPACE @@ -62,7 +62,7 @@ namespace QV4 { namespace Heap { struct QmlListWrapper : Object { - QmlListWrapper(ExecutionEngine *engine); + QmlListWrapper(); ~QmlListWrapper(); QPointer<QObject> object; QQmlListProperty<QObject> property; @@ -81,10 +81,10 @@ struct Q_QML_EXPORT QmlListWrapper : Object QVariant toVariant() const; - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); + static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); static void put(Managed *m, String *name, const Value &value); - static void advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attributes); + static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); }; } diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index 62b5b76ede..af7b394a1b 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -65,13 +65,13 @@ static bool isLocaleObject(const QV4::Value &val) void QQmlDateExtension::registerExtension(QV4::ExecutionEngine *engine) { - engine->datePrototype.asObject()->defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); - engine->datePrototype.asObject()->defineDefaultProperty(QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString); - engine->datePrototype.asObject()->defineDefaultProperty(QStringLiteral("toLocaleDateString"), method_toLocaleDateString); - engine->dateCtor.objectValue()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString); - engine->dateCtor.objectValue()->defineDefaultProperty(QStringLiteral("fromLocaleTimeString"), method_fromLocaleTimeString); - engine->dateCtor.objectValue()->defineDefaultProperty(QStringLiteral("fromLocaleDateString"), method_fromLocaleDateString); - engine->dateCtor.objectValue()->defineDefaultProperty(QStringLiteral("timeZoneUpdated"), method_timeZoneUpdated); + engine->datePrototype()->defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); + engine->datePrototype()->defineDefaultProperty(QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString); + engine->datePrototype()->defineDefaultProperty(QStringLiteral("toLocaleDateString"), method_toLocaleDateString); + engine->dateCtor()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString); + engine->dateCtor()->defineDefaultProperty(QStringLiteral("fromLocaleTimeString"), method_fromLocaleTimeString); + engine->dateCtor()->defineDefaultProperty(QStringLiteral("fromLocaleDateString"), method_fromLocaleDateString); + engine->dateCtor()->defineDefaultProperty(QStringLiteral("timeZoneUpdated"), method_timeZoneUpdated); } QV4::ReturnedValue QQmlDateExtension::method_toLocaleString(QV4::CallContext *ctx) @@ -81,7 +81,7 @@ QV4::ReturnedValue QQmlDateExtension::method_toLocaleString(QV4::CallContext *ct QV4::Scope scope(ctx); - QV4::DateObject *date = ctx->thisObject().asDateObject(); + QV4::DateObject *date = ctx->thisObject().as<DateObject>(); if (!date) return QV4::DatePrototype::method_toLocaleString(ctx); @@ -125,7 +125,7 @@ QV4::ReturnedValue QQmlDateExtension::method_toLocaleTimeString(QV4::CallContext QV4::Scope scope(ctx); - QV4::DateObject *date = ctx->thisObject().asDateObject(); + QV4::DateObject *date = ctx->thisObject().as<DateObject>(); if (!date) return QV4::DatePrototype::method_toLocaleTimeString(ctx); @@ -170,7 +170,7 @@ QV4::ReturnedValue QQmlDateExtension::method_toLocaleDateString(QV4::CallContext QV4::Scope scope(ctx); - QV4::DateObject *dateObj = ctx->thisObject().asDateObject(); + QV4::DateObject *dateObj = ctx->thisObject().as<DateObject>(); if (!dateObj) return QV4::DatePrototype::method_toLocaleDateString(ctx); @@ -347,9 +347,9 @@ QV4::ReturnedValue QQmlDateExtension::method_timeZoneUpdated(QV4::CallContext *c void QQmlNumberExtension::registerExtension(QV4::ExecutionEngine *engine) { - engine->numberPrototype.asObject()->defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); - engine->numberPrototype.asObject()->defineDefaultProperty(QStringLiteral("toLocaleCurrencyString"), method_toLocaleCurrencyString); - engine->numberCtor.objectValue()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString); + engine->numberPrototype()->defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); + engine->numberPrototype()->defineDefaultProperty(QStringLiteral("toLocaleCurrencyString"), method_toLocaleCurrencyString); + engine->numberCtor()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString); } QV4::ReturnedValue QQmlNumberExtension::method_toLocaleString(QV4::CallContext *ctx) @@ -806,7 +806,7 @@ QV4::ReturnedValue QQmlLocale::wrap(ExecutionEngine *v4, const QLocale &locale) { QV4::Scope scope(v4); QV4LocaleDataDeletable *d = localeV4Data(scope.engine); - QV4::Scoped<QQmlLocaleData> wrapper(scope, v4->memoryManager->alloc<QQmlLocaleData>(v4)); + QV4::Scoped<QQmlLocaleData> wrapper(scope, v4->memoryManager->allocObject<QQmlLocaleData>()); wrapper->d()->locale = locale; QV4::ScopedObject p(scope, d->prototype.value()); wrapper->setPrototype(p); @@ -815,15 +815,15 @@ QV4::ReturnedValue QQmlLocale::wrap(ExecutionEngine *v4, const QLocale &locale) void QQmlLocale::registerStringLocaleCompare(QV4::ExecutionEngine *engine) { - engine->stringPrototype.asObject()->defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare); + engine->stringPrototype()->defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare); } QV4::ReturnedValue QQmlLocale::method_localeCompare(QV4::CallContext *ctx) { - if (ctx->argc() != 1 || (!ctx->args()[0].isString() && !ctx->args()[0].asStringObject())) + if (ctx->argc() != 1 || (!ctx->args()[0].isString() && !ctx->args()[0].as<StringObject>())) return QV4::StringPrototype::method_localeCompare(ctx); - if (!ctx->thisObject().isString() && !ctx->thisObject().asStringObject()) + if (!ctx->thisObject().isString() && !ctx->thisObject().as<StringObject>()) return QV4::StringPrototype::method_localeCompare(ctx); QString thisString = ctx->thisObject().toQStringNoThrow(); diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h index d4436482cf..aa6b2e8681 100644 --- a/src/qml/qml/qqmllocale_p.h +++ b/src/qml/qml/qqmllocale_p.h @@ -34,6 +34,17 @@ #ifndef QQMLLOCALE_H #define QQMLLOCALE_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <qqml.h> #include <QtCore/qlocale.h> @@ -71,13 +82,9 @@ private: }; -class Q_AUTOTEST_EXPORT QQmlLocale +class Q_QML_PRIVATE_EXPORT QQmlLocale { Q_GADGET - Q_ENUMS(MeasurementSystem) - Q_ENUMS(FormatType) - Q_ENUMS(CurrencySymbolFormat) - Q_ENUMS(DayOfWeek) public: ~QQmlLocale(); @@ -88,16 +95,19 @@ public: ImperialUSSystem = QLocale::ImperialUSSystem, ImperialUKSystem = QLocale::ImperialUKSystem }; + Q_ENUM(MeasurementSystem) enum FormatType { LongFormat = QLocale::LongFormat, ShortFormat = QLocale::ShortFormat, NarrowFormat = QLocale::NarrowFormat }; + Q_ENUM(FormatType) enum CurrencySymbolFormat { CurrencyIsoCode = QLocale::CurrencyIsoCode, CurrencySymbol = QLocale::CurrencySymbol, CurrencyDisplayName = QLocale::CurrencyDisplayName }; + Q_ENUM(CurrencySymbolFormat) // Qt defines Sunday as 7, but JS Date assigns Sunday 0 enum DayOfWeek { Sunday = 0, @@ -108,6 +118,7 @@ public: Friday = Qt::Friday, Saturday = Qt::Saturday }; + Q_ENUM(DayOfWeek) static QV4::ReturnedValue locale(QV4::ExecutionEngine *engine, const QString &localeName); static QV4::ReturnedValue wrap(QV4::ExecutionEngine *engine, const QLocale &locale); @@ -125,7 +136,7 @@ namespace QV4 { namespace Heap { struct QQmlLocaleData : Object { - inline QQmlLocaleData(ExecutionEngine *engine); + inline QQmlLocaleData() {} QLocale locale; }; @@ -137,7 +148,7 @@ struct QQmlLocaleData : public QV4::Object V4_NEEDS_DESTROY static QLocale *getThisLocale(QV4::CallContext *ctx) { - QV4::Object *o = ctx->thisObject().asObject(); + QV4::Object *o = ctx->thisObject().as<Object>(); QQmlLocaleData *thisObject = o ? o->as<QQmlLocaleData>() : 0; if (!thisObject) { ctx->engine()->throwTypeError(); @@ -175,11 +186,6 @@ struct QQmlLocaleData : public QV4::Object static QV4::ReturnedValue method_get_pmText(QV4::CallContext *ctx); }; -Heap::QQmlLocaleData::QQmlLocaleData(ExecutionEngine *engine) - : Heap::Object(engine) -{ -} - } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmemoryprofiler.cpp b/src/qml/qml/qqmlmemoryprofiler.cpp index cdd60e2dec..531666340b 100644 --- a/src/qml/qml/qqmlmemoryprofiler.cpp +++ b/src/qml/qml/qqmlmemoryprofiler.cpp @@ -63,11 +63,13 @@ static qmlmemprofile_pop_location *memprofile_pop_location; static qmlmemprofile_save *memprofile_save; static qmlmemprofile_is_enabled *memprofile_is_enabled; +#ifndef QT_NO_LIBRARY extern QFunctionPointer qt_linux_find_symbol_sys(const char *symbol); +#endif static bool openLibrary() { -#ifdef Q_OS_LINUX +#if defined(Q_OS_LINUX) && !defined(QT_NO_LIBRARY) if (state == Unloaded) { memprofile_stats = (qmlmemprofile_stats *) qt_linux_find_symbol_sys("qmlmemprofile_stats"); memprofile_clear = (qmlmemprofile_clear *) qt_linux_find_symbol_sys("qmlmemprofile_clear"); diff --git a/src/qml/qml/qqmlmemoryprofiler_p.h b/src/qml/qml/qqmlmemoryprofiler_p.h index 98977f9db5..77008bd448 100644 --- a/src/qml/qml/qqmlmemoryprofiler_p.h +++ b/src/qml/qml/qqmlmemoryprofiler_p.h @@ -34,6 +34,17 @@ #ifndef QQMLMEMORYPROFILER_H #define QQMLMEMORYPROFILER_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <private/qtqmlglobal_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index cb8c2bd3b5..1a27a487bd 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -38,6 +38,7 @@ #include <private/qqmlcustomparser_p.h> #include <private/qhashedstring_p.h> #include <private/qqmlimport_p.h> +#include <private/qqmlcompiler_p.h> #include <QtCore/qdebug.h> #include <QtCore/qstringlist.h> @@ -217,12 +218,10 @@ public: void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) { QV4::ExecutionEngine *v4 = QV8Engine::getV4(e->handle()); + v4->pushGlobalContext(); if (scriptCallback && scriptApi(e).isUndefined()) { - v4->pushGlobalContext(); setScriptApi(e, scriptCallback(e, e)); - v4->popContext(); } else if (qobjectCallback && !qobjectApi(e)) { - v4->pushGlobalContext(); QObject *o = qobjectCallback(e, e); setQObjectApi(e, o); if (!o) { @@ -230,14 +229,12 @@ void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) } // if this object can use a property cache, create it now QQmlData::ensurePropertyCache(e, o); - v4->popContext(); } else if (!url.isEmpty() && !qobjectApi(e)) { - v4->pushGlobalContext(); QQmlComponent component(e, url, QQmlComponent::PreferSynchronous); QObject *o = component.create(); setQObjectApi(e, o); - v4->popContext(); } + v4->popContext(); } void QQmlType::SingletonInstanceInfo::destroy(QQmlEngine *e) @@ -245,7 +242,12 @@ void QQmlType::SingletonInstanceInfo::destroy(QQmlEngine *e) // cleans up the engine-specific singleton instances if they exist. scriptApis.remove(e); QObject *o = qobjectApis.take(e); - delete o; + if (o) { + QQmlData *ddata = QQmlData::get(o, false); + if (ddata && ddata->indestructible) + return; + delete o; + } } void QQmlType::SingletonInstanceInfo::setQObjectApi(QQmlEngine *e, QObject *o) @@ -258,7 +260,7 @@ QObject *QQmlType::SingletonInstanceInfo::qobjectApi(QQmlEngine *e) const return qobjectApis.value(e); } -void QQmlType::SingletonInstanceInfo::setScriptApi(QQmlEngine *e, QJSValue v) +void QQmlType::SingletonInstanceInfo::setScriptApi(QQmlEngine *e, const QJSValue &v) { scriptApis.insert(e, v); } @@ -477,6 +479,29 @@ QQmlType *QQmlType::superType() const return d->superType; } +QQmlType *QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const +{ + Q_ASSERT(isComposite()); + if (!engine) + return 0; + QQmlTypeData *td = engine->typeLoader.getType(sourceUrl()); + if (!td || !td->isComplete()) + return 0; + QQmlCompiledData *cd = td->compiledData(); + const QMetaObject *mo = cd->rootPropertyCache->firstCppMetaObject(); + return QQmlMetaType::qmlType(mo); +} + +int QQmlType::resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const +{ + Q_ASSERT(isComposite()); + *ok = false; + QQmlType *type = resolveCompositeBaseType(engine); + if (!type) + return -1; + return type->enumValue(engine, name, ok); +} + static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd) { @@ -842,18 +867,26 @@ int QQmlType::metaObjectRevision() const return d->revision; } -QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction() const +QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivate *engine) const { - if (d->regType != CppType) - return 0; - return d->extraData.cd->attachedPropertiesFunc; + if (d->regType == CppType) + return d->extraData.cd->attachedPropertiesFunc; + + QQmlType *base = 0; + if (d->regType == CompositeType) + base = resolveCompositeBaseType(engine); + return base ? base->attachedPropertiesFunction(engine) : 0; } -const QMetaObject *QQmlType::attachedPropertiesType() const +const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) const { - if (d->regType != CppType) - return 0; - return d->extraData.cd->attachedPropertiesType; + if (d->regType == CppType) + return d->extraData.cd->attachedPropertiesType; + + QQmlType *base = 0; + if (d->regType == CompositeType) + base = resolveCompositeBaseType(engine); + return base ? base->attachedPropertiesType(engine) : 0; } /* @@ -861,11 +894,15 @@ This is the id passed to qmlAttachedPropertiesById(). This is different from th for the case that a single class is registered under two or more names (eg. Item in Qt 4.7 and QtQuick 1.0). */ -int QQmlType::attachedPropertiesId() const +int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const { - if (d->regType != CppType) - return 0; - return d->extraData.cd->attachedPropertiesId; + if (d->regType == CppType) + return d->extraData.cd->attachedPropertiesId; + + QQmlType *base = 0; + if (d->regType == CompositeType) + base = resolveCompositeBaseType(engine); + return base ? base->attachedPropertiesId(engine) : 0; } int QQmlType::parserStatusCast() const @@ -911,9 +948,11 @@ QUrl QQmlType::sourceUrl() const return QUrl(); } -int QQmlType::enumValue(const QHashedStringRef &name, bool *ok) const +int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const { Q_ASSERT(ok); + if (isComposite()) + return resolveCompositeEnumValue(engine, name.toString(), ok); *ok = true; d->initEnums(); @@ -926,9 +965,11 @@ int QQmlType::enumValue(const QHashedStringRef &name, bool *ok) const return -1; } -int QQmlType::enumValue(const QHashedCStringRef &name, bool *ok) const +int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name, bool *ok) const { Q_ASSERT(ok); + if (isComposite()) + return resolveCompositeEnumValue(engine, name.toUtf16(), ok); *ok = true; d->initEnums(); @@ -941,9 +982,11 @@ int QQmlType::enumValue(const QHashedCStringRef &name, bool *ok) const return -1; } -int QQmlType::enumValue(const QV4::String *name, bool *ok) const +int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const { Q_ASSERT(ok); + if (isComposite()) + return resolveCompositeEnumValue(engine, name->toQString(), ok); *ok = true; d->initEnums(); @@ -1110,7 +1153,9 @@ void qmlClearTypeRegistrations() // Declared in qqml.h data->uriToModule.clear(); QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types +#ifndef QT_NO_LIBRARY qmlClearEnginePlugins(); +#endif } int registerAutoParentFunction(QQmlPrivate::RegisterAutoParent &autoparent) @@ -1440,8 +1485,8 @@ bool QQmlMetaType::isAnyModule(const QString &uri) QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - for (QQmlMetaTypeData::TypeModules::ConstIterator iter = data->uriToModule.begin(); - iter != data->uriToModule.end(); ++iter) { + for (QQmlMetaTypeData::TypeModules::ConstIterator iter = data->uriToModule.cbegin(); + iter != data->uriToModule.cend(); ++iter) { if ((*iter)->module() == uri) return true; } @@ -1538,25 +1583,25 @@ int QQmlMetaType::listType(int id) return 0; } -int QQmlMetaType::attachedPropertiesFuncId(const QMetaObject *mo) +int QQmlMetaType::attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMetaObject *mo) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); QQmlType *type = data->metaObjectToType.value(mo); - if (type && type->attachedPropertiesFunction()) - return type->attachedPropertiesId(); + if (type && type->attachedPropertiesFunction(engine)) + return type->attachedPropertiesId(engine); else return -1; } -QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFuncById(int id) +QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFuncById(QQmlEnginePrivate *engine, int id) { if (id < 0) return 0; QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - return data->types.at(id)->attachedPropertiesFunction(); + return data->types.at(id)->attachedPropertiesFunction(engine); } QMetaProperty QQmlMetaType::defaultProperty(const QMetaObject *metaObject) @@ -1718,7 +1763,7 @@ QQmlType *QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStrin QQmlMetaTypeData *data = metaTypeData(); QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(name); - while (it != data->nameToType.end() && it.key() == name) { + while (it != data->nameToType.cend() && it.key() == name) { // XXX version_major<0 just a kludge for QQmlPropertyPrivate::initProperty if (version_major < 0 || module.isEmpty() || (*it)->availableInVersion(module, version_major,version_minor)) return (*it); @@ -1752,7 +1797,7 @@ QQmlType *QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStri QQmlMetaTypeData *data = metaTypeData(); QQmlMetaTypeData::MetaObjects::const_iterator it = data->metaObjectToType.constFind(metaObject); - while (it != data->metaObjectToType.end() && it.key() == metaObject) { + while (it != data->metaObjectToType.cend() && it.key() == metaObject) { QQmlType *t = *it; if (version_major < 0 || module.isEmpty() || t->availableInVersion(module, version_major,version_minor)) return t; @@ -1823,8 +1868,9 @@ QList<QString> QQmlMetaType::qmlTypeNames() QQmlMetaTypeData *data = metaTypeData(); QList<QString> names; - QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.begin(); - while (it != data->nameToType.end()) { + names.reserve(data->nameToType.count()); + QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.cbegin(); + while (it != data->nameToType.cend()) { names += (*it)->qmlTypeName(); ++it; } @@ -1885,4 +1931,43 @@ const QQmlPrivate::CachedQmlUnit *QQmlMetaType::findCachedCompilationUnit(const return 0; } +/*! + Returns the pretty QML type name (e.g. 'Item' instead of 'QtQuickItem') for the given object. + */ +QString QQmlMetaType::prettyTypeName(const QObject *object) +{ + QString typeName; + + if (!object) + return typeName; + + const QQmlType *type = QQmlMetaType::qmlType(object->metaObject()); + if (type) { + typeName = type->qmlTypeName(); + const int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); + if (lastSlash != -1) + typeName = typeName.mid(lastSlash + 1); + } else { + typeName = QString::fromUtf8(object->metaObject()->className()); + int marker = typeName.indexOf(QLatin1String("_QMLTYPE_")); + if (marker != -1) + typeName = typeName.left(marker); + + marker = typeName.indexOf(QLatin1String("_QML_")); + if (marker != -1) { + typeName = typeName.left(marker); + typeName += QLatin1Char('*'); + type = QQmlMetaType::qmlType(QMetaType::type(typeName.toLatin1())); + if (type) { + typeName = type->qmlTypeName(); + const int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); + if (lastSlash != -1) + typeName = typeName.mid(lastSlash + 1); + } + } + } + + return typeName; +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index e5ac20d314..c120941a03 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -57,6 +57,7 @@ QT_BEGIN_NAMESPACE class QQmlType; class QQmlEngine; +class QQmlEnginePrivate; class QQmlCustomParser; class QQmlTypePrivate; class QQmlTypeModule; @@ -91,8 +92,8 @@ public: static QObject *toQObject(const QVariant &, bool *ok = 0); static int listType(int); - static int attachedPropertiesFuncId(const QMetaObject *); - static QQmlAttachedPropertiesFunc attachedPropertiesFuncById(int); + static int attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMetaObject *); + static QQmlAttachedPropertiesFunc attachedPropertiesFuncById(QQmlEnginePrivate *, int); enum TypeCategory { Unknown, Object, List }; static TypeCategory typeCategory(int); @@ -122,6 +123,8 @@ public: static QStringList typeRegistrationFailures(); static QMutex *typeRegistrationLock(); + + static QString prettyTypeName(const QObject *object); }; struct QQmlMetaTypeData; @@ -166,9 +169,9 @@ public: int metaObjectRevision() const; bool containsRevisionedAttributes() const; - QQmlAttachedPropertiesFunc attachedPropertiesFunction() const; - const QMetaObject *attachedPropertiesType() const; - int attachedPropertiesId() const; + QQmlAttachedPropertiesFunc attachedPropertiesFunction(QQmlEnginePrivate *engine) const; + const QMetaObject *attachedPropertiesType(QQmlEnginePrivate *engine) const; + int attachedPropertiesId(QQmlEnginePrivate *engine) const; int parserStatusCast() const; const char *interfaceIId() const; @@ -191,7 +194,7 @@ public: void setQObjectApi(QQmlEngine *, QObject *); QObject *qobjectApi(QQmlEngine *) const; - void setScriptApi(QQmlEngine *, QJSValue); + void setScriptApi(QQmlEngine *, const QJSValue &); QJSValue scriptApi(QQmlEngine *) const; void init(QQmlEngine *); @@ -204,11 +207,13 @@ public: QUrl sourceUrl() const; - int enumValue(const QHashedStringRef &, bool *ok) const; - int enumValue(const QHashedCStringRef &, bool *ok) const; - int enumValue(const QV4::String *, bool *ok) const; + int enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &, bool *ok) const; + int enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &, bool *ok) const; + int enumValue(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const; private: QQmlType *superType() const; + QQmlType *resolveCompositeBaseType(QQmlEnginePrivate *engine) const; + int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const; friend class QQmlTypePrivate; friend struct QQmlMetaTypeData; diff --git a/src/qml/qml/qqmlnotifier_p.h b/src/qml/qml/qqmlnotifier_p.h index 2742bfc84b..ac0aab892f 100644 --- a/src/qml/qml/qqmlnotifier_p.h +++ b/src/qml/qml/qqmlnotifier_p.h @@ -34,6 +34,17 @@ #ifndef QQMLNOTIFIER_P_H #define QQMLNOTIFIER_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qqmldata_p.h" #include <QtCore/qmetaobject.h> #include <private/qmetaobject_p.h> @@ -63,9 +74,6 @@ class QQmlNotifierEndpoint QQmlNotifierEndpoint *next; QQmlNotifierEndpoint **prev; public: - inline QQmlNotifierEndpoint(); - inline ~QQmlNotifierEndpoint(); - // QQmlNotifierEndpoint can only invoke one of a set of pre-defined callbacks. // To add another callback, extend this enum and add the callback to the top // of qqmlnotifier.cpp. Four bits are reserved for the callback, so there can @@ -77,7 +85,8 @@ public: QQmlVMEMetaObjectEndpoint = 3 }; - inline void setCallback(Callback c) { callback = c; } + inline QQmlNotifierEndpoint(Callback callback); + inline ~QQmlNotifierEndpoint(); inline bool isConnected(); inline bool isConnected(QObject *source, int sourceSignal); @@ -90,6 +99,8 @@ public: inline bool isNotifying() const; inline void cancelNotify(); + inline int signalIndex() const { return sourceSignal; } + private: friend class QQmlData; friend class QQmlNotifier; @@ -135,8 +146,8 @@ void QQmlNotifier::notify() if (endpoints) emitNotify(endpoints, args); } -QQmlNotifierEndpoint::QQmlNotifierEndpoint() -: next(0), prev(0), senderPtr(0), callback(None), sourceSignal(-1) +QQmlNotifierEndpoint::QQmlNotifierEndpoint(Callback callback) +: next(0), prev(0), senderPtr(0), callback(callback), sourceSignal(-1) { } diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 3c2f3690b9..21e6d5f6de 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -62,14 +62,6 @@ struct ActiveOCRestorer }; } -static void removeBindingOnProperty(QObject *o, int index) -{ - int coreIndex; - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex); - QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(o, coreIndex, valueTypeIndex, 0); - if (binding) binding->destroy(); -} - QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlContextData *creationContext, void *activeVMEDataForRootContext) : phase(Startup) , compiledData(compiledData) @@ -128,7 +120,7 @@ void QQmlObjectCreator::init(QQmlContextData *providedParentContext) _ddata = 0; _propertyCache = 0; _vmeMetaObject = 0; - _qmlBindingWrapper = 0; + _qmlContext = 0; } QQmlObjectCreator::~QQmlObjectCreator() @@ -137,11 +129,6 @@ QQmlObjectCreator::~QQmlObjectCreator() { QQmlObjectCreatorRecursionWatcher watcher(this); } - for (int i = 0; i < sharedState->allCreatedBindings.count(); ++i) { - QQmlAbstractBinding *b = sharedState->allCreatedBindings.at(i); - if (b) - b->m_mePtr = 0; - } for (int i = 0; i < sharedState->allParserStatusCallbacks.count(); ++i) { QQmlParserStatus *ps = sharedState->allParserStatusCallbacks.at(i); if (ps) @@ -198,9 +185,10 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI if (subComponentIndex == -1 && compiledData->scripts.count()) { QV4::ScopedObject scripts(scope, v4->newArrayObject(compiledData->scripts.count())); context->importedScripts.set(v4, scripts); + QV4::ScopedValue v(scope); for (int i = 0; i < compiledData->scripts.count(); ++i) { QQmlScriptData *s = compiledData->scripts.at(i); - scripts->putIndexed(i, *s->scriptValueForContext(context).valueRef()); + scripts->putIndexed(i, (v = s->scriptValueForContext(context))); } } else if (sharedState->creationContext) { context->importedScripts = sharedState->creationContext->importedScripts; @@ -249,9 +237,9 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance) Q_ASSERT(!sharedState->allJavaScriptObjects); sharedState->allJavaScriptObjects = valueScope.alloc(compiledData->totalObjectCount); - QV4::Value *qmlBindingWrapper = valueScope.alloc(1); + QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1)); - qSwap(_qmlBindingWrapper, qmlBindingWrapper); + qSwap(_qmlContext, qmlContext); qSwap(_propertyCache, cache); qSwap(_qobject, instance); @@ -280,7 +268,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance) qSwap(_qobject, instance); qSwap(_propertyCache, cache); - qSwap(_qmlBindingWrapper, qmlBindingWrapper); + qSwap(_qmlContext, qmlContext); qSwap(_scopeObject, scopeObject); phase = ObjectsCreated; @@ -660,15 +648,12 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip) // ### this is best done through type-compile-time binding skip lists. if (_valueTypeProperty) { - QQmlAbstractBinding *binding = - QQmlPropertyPrivate::binding(_bindingTarget, _valueTypeProperty->coreIndex, -1); + QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(_bindingTarget, _valueTypeProperty->coreIndex); - if (binding && binding->bindingType() != QQmlAbstractBinding::ValueTypeProxy) { - QQmlPropertyPrivate::setBinding(_bindingTarget, _valueTypeProperty->coreIndex, -1, 0); - binding->destroy(); + if (binding && !binding->isValueTypeProxy()) { + QQmlPropertyPrivate::removeBinding(_bindingTarget, _valueTypeProperty->coreIndex); } else if (binding) { - QQmlValueTypeProxyBinding *proxy = - static_cast<QQmlValueTypeProxyBinding *>(binding); + QQmlValueTypeProxyBinding *proxy = static_cast<QQmlValueTypeProxyBinding *>(binding); if (qmlTypeForObject(_bindingTarget)) { quint32 bindingSkipList = 0; @@ -721,7 +706,12 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlCompiledData::TypeReference *tr = resolvedTypes.value(binding->propertyNameIndex); Q_ASSERT(tr); QQmlType *attachedType = tr->type; - const int id = attachedType->attachedPropertiesId(); + if (!attachedType) { + QQmlTypeNameCache::Result res = context->imports->query(stringAt(binding->propertyNameIndex)); + if (res.isValid()) + attachedType = res.type; + } + const int id = attachedType->attachedPropertiesId(QQmlEnginePrivate::get(engine)); QObject *qmlObject = qmlAttachedPropertiesObjectById(id, _qobject); if (!populateInstance(binding->value.objectIndex, qmlObject, qmlObject, /*value type property*/0)) return false; @@ -731,7 +721,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con // ### resolve this at compile time if (property && property->propType == qMetaTypeId<QQmlScriptString>()) { QQmlScriptString ss(binding->valueAsScriptString(qmlUnit), context->asQQmlContext(), _scopeObject); - ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : QQmlBinding::Invalid; + ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid; ss.d.data()->lineNumber = binding->location.line; ss.d.data()->columnNumber = binding->location.column; ss.d.data()->isStringLiteral = binding->type == QV4::CompiledData::Binding::Type_String; @@ -800,7 +790,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con if (_ddata->hasBindingBit(property->coreIndex) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) && !_valueTypeProperty) - removeBindingOnProperty(_bindingTarget, property->coreIndex); + QQmlPropertyPrivate::removeBinding(_bindingTarget, property->coreIndex); if (binding->type == QV4::CompiledData::Binding::Type_Script) { QV4::Function *runtimeFunction = compiledData->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; @@ -828,18 +818,12 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con if (_valueTypeProperty) targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine); - sharedState->allCreatedBindings.push(qmlBinding); - qmlBinding->m_mePtr = &sharedState->allCreatedBindings.top(); + sharedState->allCreatedBindings.push(QQmlAbstractBinding::Ptr(qmlBinding)); - qmlBinding->setTarget(_bindingTarget, targetCorePropertyData, context); + qmlBinding->setTarget(_bindingTarget, targetCorePropertyData); if (targetCorePropertyData.isAlias()) { - QQmlAbstractBinding *old = - QQmlPropertyPrivate::setBindingNoEnable(_bindingTarget, - targetCorePropertyData.coreIndex, - targetCorePropertyData.getValueTypeCoreIndex(), - qmlBinding); - if (old) { old->destroy(); } + QQmlPropertyPrivate::setBinding(qmlBinding, QQmlPropertyPrivate::DontEnable); } else { qmlBinding->addToObject(); @@ -875,11 +859,24 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlPropertyValueInterceptor *vi = reinterpret_cast<QQmlPropertyValueInterceptor *>(reinterpret_cast<char *>(createdSubObject) + valueInterceptorCast); QObject *target = createdSubObject->parent(); + if (targetCorePropertyData.isAlias()) { + int propIndex; + QQmlPropertyPrivate::findAliasTarget(target, targetCorePropertyData.coreIndex, &target, &propIndex); + QQmlData *data = QQmlData::get(target); + if (!data || !data->propertyCache) { + qWarning() << "can't resolve property alias for 'on' assignment"; + return false; + } + targetCorePropertyData = *data->propertyCache->property(propIndex); + } + QQmlProperty prop = QQmlPropertyPrivate::restore(target, targetCorePropertyData, context); + vi->setTarget(prop); - QQmlVMEMetaObject *mo = QQmlVMEMetaObject::get(target); - Q_ASSERT(mo); + QQmlInterceptorMetaObject *mo = QQmlInterceptorMetaObject::get(target); + if (!mo) + mo = new QQmlInterceptorMetaObject(target, QQmlData::get(target)->propertyCache); mo->registerInterceptor(prop.index(), QQmlPropertyPrivate::valueTypeCoreIndex(prop), vi); return true; } @@ -1007,15 +1004,12 @@ void QQmlObjectCreator::registerObjectWithContextById(int objectIndex, QObject * context->setIdProperty(idEntry.value(), instance); } -QV4::Heap::ExecutionContext *QQmlObjectCreator::currentQmlContext() +QV4::Heap::QmlContext *QQmlObjectCreator::currentQmlContext() { - if (!_qmlBindingWrapper->objectValue()) { - QV4::Scope valueScope(v4); - QV4::ScopedObject qmlScope(valueScope, QV4::QmlContextWrapper::qmlScope(v4, context, _scopeObject)); - QV4::ScopedContext global(valueScope, v4->rootContext()); - *_qmlBindingWrapper = v4->memoryManager->alloc<QV4::QmlBindingWrapper>(global, qmlScope); - } - return static_cast<QV4::QmlBindingWrapper*>(_qmlBindingWrapper->objectValue())->context(); + if (!_qmlContext->objectValue()) + _qmlContext->setM(v4->rootContext()->newQmlContext(context, _scopeObject)); + + return _qmlContext->d(); } QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isContextObject) @@ -1124,8 +1118,9 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo QBitArray bindingsToSkip; if (customParser) { - QHash<int, QBitArray>::ConstIterator customParserBindings = compiledData->customParserBindings.find(index); + QHash<int, QBitArray>::ConstIterator customParserBindings = compiledData->customParserBindings.constFind(index); if (customParserBindings != compiledData->customParserBindings.constEnd()) { + customParser->engine = QQmlEnginePrivate::get(engine); customParser->imports = compiledData->importCache; QList<const QV4::CompiledData::Binding *> bindings; @@ -1135,6 +1130,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo bindings << obj->bindingTable() + i; customParser->applyBindings(instance, compiledData, bindings); + customParser->engine = 0; customParser->imports = (QQmlTypeNameCache*)0; bindingsToSkip = *customParserBindings; } @@ -1162,13 +1158,13 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo ++sharedState->allJavaScriptObjects; QV4::Scope valueScope(v4); - QV4::Value *qmlBindingWrapper = valueScope.alloc(1); + QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1)); - qSwap(_qmlBindingWrapper, qmlBindingWrapper); + qSwap(_qmlContext, qmlContext); bool result = populateInstance(index, instance, /*binding target*/instance, /*value type property*/0, bindingsToSkip); - qSwap(_qmlBindingWrapper, qmlBindingWrapper); + qSwap(_qmlContext, qmlContext); qSwap(_scopeObject, scopeObject); return result ? instance : 0; @@ -1183,13 +1179,14 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru ActiveOCRestorer ocRestorer(this, QQmlEnginePrivate::get(engine)); while (!sharedState->allCreatedBindings.isEmpty()) { - QQmlAbstractBinding *b = sharedState->allCreatedBindings.pop(); - if (!b) + QQmlAbstractBinding::Ptr b = sharedState->allCreatedBindings.pop(); + Q_ASSERT(b); + // skip, if b is not added to an object + if (!b->isAddedToObject()) continue; - b->m_mePtr = 0; - QQmlData *data = QQmlData::get(b->object()); + QQmlData *data = QQmlData::get(b->targetObject()); Q_ASSERT(data); - data->clearPendingBindingBit(b->propertyIndex()); + data->clearPendingBindingBit(b->targetPropertyIndex()); b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); @@ -1299,7 +1296,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * QBitArray bindingSkipList = bindingsToSkip; { - QHash<int, QBitArray>::ConstIterator deferredBindings = compiledData->deferredBindingsPerObject.find(_compiledObjectIndex); + QHash<int, QBitArray>::ConstIterator deferredBindings = compiledData->deferredBindingsPerObject.constFind(_compiledObjectIndex); if (deferredBindings != compiledData->deferredBindingsPerObject.constEnd()) { if (bindingSkipList.isEmpty()) bindingSkipList.resize(deferredBindings->count()); diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 60fefe494f..620ae75f53 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -33,6 +33,17 @@ #ifndef QQMLOBJECTCREATOR_P_H #define QQMLOBJECTCREATOR_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <private/qqmlimport_p.h> #include <private/qqmltypenamecache_p.h> #include <private/qv4compileddata_p.h> @@ -55,7 +66,7 @@ struct QQmlObjectCreatorSharedState : public QSharedData { QQmlContextData *rootContext; QQmlContextData *creationContext; - QFiniteStack<QQmlAbstractBinding*> allCreatedBindings; + QFiniteStack<QQmlAbstractBinding::Ptr> allCreatedBindings; QFiniteStack<QQmlParserStatus*> allParserStatusCallbacks; QFiniteStack<QPointer<QObject> > allCreatedObjects; QV4::Value *allJavaScriptObjects; // pointer to vector on JS stack to reference JS wrappers during creation phase. @@ -107,7 +118,7 @@ private: void registerObjectWithContextById(int objectIndex, QObject *instance) const; - QV4::Heap::ExecutionContext *currentQmlContext(); + QV4::Heap::QmlContext *currentQmlContext(); enum Phase { Startup, @@ -143,7 +154,7 @@ private: QQmlRefPointer<QQmlPropertyCache> _propertyCache; QQmlVMEMetaObject *_vmeMetaObject; QQmlListProperty<void> _currentList; - QV4::Value *_qmlBindingWrapper; + QV4::QmlContext *_qmlContext; friend struct QQmlObjectCreatorRecursionWatcher; }; diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp index fc24b15fd2..9188ba6174 100644 --- a/src/qml/qml/qqmlopenmetaobject.cpp +++ b/src/qml/qml/qqmlopenmetaobject.cpp @@ -35,6 +35,7 @@ #include <private/qqmlpropertycache_p.h> #include <private/qqmldata_p.h> #include <private/qmetaobjectbuilder_p.h> +#include <private/qv8engine_p.h> #include <qqmlengine.h> #include <qdebug.h> @@ -106,6 +107,28 @@ QMetaObject *QQmlOpenMetaObjectType::metaObject() const return d->mem; } +void QQmlOpenMetaObjectType::createProperties(const QVector<QByteArray> &names) +{ + for (int i = 0; i < names.count(); ++i) { + const QByteArray &name = names.at(i); + const int id = d->mob.propertyCount(); + d->mob.addSignal("__" + QByteArray::number(id) + "()"); + QMetaPropertyBuilder build = d->mob.addProperty(name, "QVariant", id); + propertyCreated(id, build); + d->names.insert(name, id); + } + free(d->mem); + d->mem = d->mob.toMetaObject(); + QSet<QQmlOpenMetaObject*>::iterator it = d->referers.begin(); + while (it != d->referers.end()) { + QQmlOpenMetaObject *omo = *it; + *static_cast<QMetaObject *>(omo) = *d->mem; + if (d->cache) + d->cache->update(omo); + ++it; + } +} + int QQmlOpenMetaObjectType::createProperty(const QByteArray &name) { int id = d->mob.propertyCount(); @@ -230,8 +253,18 @@ QQmlOpenMetaObjectType *QQmlOpenMetaObject::type() const return d->type; } -int QQmlOpenMetaObject::metaCall(QMetaObject::Call c, int id, void **a) +void QQmlOpenMetaObject::emitPropertyNotification(const QByteArray &propertyName) { + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(propertyName); + if (iter == d->type->d->names.constEnd()) + return; + activate(d->object, *iter + d->type->d->signalOffset, 0); +} + +int QQmlOpenMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void **a) +{ + Q_ASSERT(d->object == o); + if (( c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) && id >= d->type->d->propertyOffset) { int propId = id - d->type->d->propertyOffset; @@ -245,15 +278,15 @@ int QQmlOpenMetaObject::metaCall(QMetaObject::Call c, int id, void **a) prop.first = propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0])); prop.second = true; propertyWritten(propId); - activate(d->object, d->type->d->signalOffset + propId, 0); + activate(o, d->type->d->signalOffset + propId, 0); } } return -1; } else { if (d->parent) - return d->parent->metaCall(c, id, a); + return d->parent->metaCall(o, c, id, a); else - return d->object->qt_metacall(c, id, a); + return o->qt_metacall(c, id, a); } } @@ -277,8 +310,8 @@ void QQmlOpenMetaObject::setValue(int id, const QVariant &value) QVariant QQmlOpenMetaObject::value(const QByteArray &name) const { - QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); - if (iter == d->type->d->names.end()) + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name); + if (iter == d->type->d->names.cend()) return QVariant(); return d->getData(*iter); @@ -286,8 +319,8 @@ QVariant QQmlOpenMetaObject::value(const QByteArray &name) const QVariant &QQmlOpenMetaObject::operator[](const QByteArray &name) { - QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); - Q_ASSERT(iter != d->type->d->names.end()); + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name); + Q_ASSERT(iter != d->type->d->names.cend()); return d->getData(*iter); } @@ -299,10 +332,10 @@ QVariant &QQmlOpenMetaObject::operator[](int id) bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val) { - QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name); int id = -1; - if (iter == d->type->d->names.end()) { + if (iter == d->type->d->names.cend()) { id = createProperty(name.constData(), "") - d->type->d->propertyOffset; } else { id = *iter; @@ -337,7 +370,7 @@ void QQmlOpenMetaObject::setCached(bool c) QQmlData *qmldata = QQmlData::get(d->object, true); if (d->cacheProperties) { if (!d->type->d->cache) - d->type->d->cache = new QQmlPropertyCache(d->type->d->engine, this); + d->type->d->cache = new QQmlPropertyCache(QV8Engine::getV4(d->type->d->engine), this); qmldata->propertyCache = d->type->d->cache; d->type->d->cache->addref(); } else { diff --git a/src/qml/qml/qqmlopenmetaobject_p.h b/src/qml/qml/qqmlopenmetaobject_p.h index 6a29d08d4e..9728220b5a 100644 --- a/src/qml/qml/qqmlopenmetaobject_p.h +++ b/src/qml/qml/qqmlopenmetaobject_p.h @@ -34,6 +34,17 @@ #ifndef QQMLOPENMETAOBJECT_H #define QQMLOPENMETAOBJECT_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QtCore/QMetaObject> #include <QtCore/QObject> @@ -54,6 +65,7 @@ public: QQmlOpenMetaObjectType(const QMetaObject *base, QQmlEngine *engine); ~QQmlOpenMetaObjectType(); + void createProperties(const QVector<QByteArray> &names); int createProperty(const QByteArray &name); int propertyOffset() const; @@ -101,8 +113,10 @@ public: QQmlOpenMetaObjectType *type() const; + void emitPropertyNotification(const QByteArray &propertyName); + protected: - virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + virtual int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a); virtual int createProperty(const char *, const char *); virtual void propertyRead(int); diff --git a/src/qml/qml/qqmlplatform_p.h b/src/qml/qml/qqmlplatform_p.h index 363341c89e..c1a481a439 100644 --- a/src/qml/qml/qqmlplatform_p.h +++ b/src/qml/qml/qqmlplatform_p.h @@ -34,6 +34,17 @@ #ifndef QQMLPLATFORM_P_H #define QQMLPLATFORM_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QtCore/QObject> #include <qqml.h> #include <private/qtqmlglobal_p.h> diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 800f650075..1b78ada698 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -50,11 +50,13 @@ #include "qqmlvaluetypeproxybinding_p.h" #include <private/qjsvalue_p.h> #include <private/qv4functionobject_p.h> +#include <private/qv4runtime_p.h> #include <QStringList> #include <private/qmetaobject_p.h> #include <private/qqmlvaluetypewrapper_p.h> #include <QtCore/qdebug.h> +#include <cmath> Q_DECLARE_METATYPE(QList<int>) Q_DECLARE_METATYPE(QList<qreal>) @@ -246,10 +248,11 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) QQmlTypeNameCache::Result r = typeNameCache->query(pathName); if (r.isValid()) { if (r.type) { - QQmlAttachedPropertiesFunc func = r.type->attachedPropertiesFunction(); + QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); + QQmlAttachedPropertiesFunc func = r.type->attachedPropertiesFunction(enginePrivate); if (!func) return; // Not an attachable type - currentObject = qmlAttachedPropertiesObjectById(r.type->attachedPropertiesId(), currentObject); + currentObject = qmlAttachedPropertiesObjectById(r.type->attachedPropertiesId(enginePrivate), currentObject); if (!currentObject) return; // Something is broken with the attachable type } else if (r.importNamespace) { if ((ii + 1) == path.count()) return; // No type following the namespace @@ -257,10 +260,11 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) ++ii; r = typeNameCache->query(path.at(ii), r.importNamespace); if (!r.type) return; // Invalid type in namespace - QQmlAttachedPropertiesFunc func = r.type->attachedPropertiesFunction(); + QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); + QQmlAttachedPropertiesFunc func = r.type->attachedPropertiesFunction(enginePrivate); if (!func) return; // Not an attachable type - currentObject = qmlAttachedPropertiesObjectById(r.type->attachedPropertiesId(), currentObject); + currentObject = qmlAttachedPropertiesObjectById(r.type->attachedPropertiesId(enginePrivate), currentObject); if (!currentObject) return; // Something is broken with the attachable type } else if (r.scriptIndex != -1) { @@ -699,8 +703,7 @@ QQmlPropertyPrivate::binding(const QQmlProperty &that) if (!that.d || !that.isProperty() || !that.d->object) return 0; - return binding(that.d->object, that.d->core.coreIndex, - that.d->core.getValueTypeCoreIndex()); + return binding(that.d->object, that.d->core.encodedIndex()); } /*! @@ -716,65 +719,96 @@ QQmlPropertyPrivate::binding(const QQmlProperty &that) \a flags is passed through to the binding and is used for the initial update (when the binding sets the initial value, it will use these flags for the write). */ -QQmlAbstractBinding * -QQmlPropertyPrivate::setBinding(const QQmlProperty &that, - QQmlAbstractBinding *newBinding, - WriteFlags flags) +void +QQmlPropertyPrivate::setBinding(const QQmlProperty &that, QQmlAbstractBinding *newBinding) { + if (!newBinding) { + removeBinding(that); + return; + } + if (!that.d || !that.isProperty() || !that.d->object) { - if (newBinding) - newBinding->destroy(); - return 0; + if (!newBinding->ref) + delete newBinding; + return; } + setBinding(newBinding); +} + +static void removeOldBinding(QObject *object, int index, QQmlPropertyPrivate::BindingFlags flags = QQmlPropertyPrivate::None) +{ + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex); - if (newBinding) { - // In the case that the new binding is provided, we must target the property it - // is associated with. If we don't do this, retargetBinding() can fail. - QObject *object = newBinding->object(); - int pi = newBinding->propertyIndex(); + QQmlData *data = QQmlData::get(object, false); - int core; - int vt = QQmlPropertyData::decodeValueTypePropertyIndex(pi, &core); + if (!data || !data->hasBindingBit(coreIndex)) + return; - return setBinding(object, core, vt, newBinding, flags); - } else { - return setBinding(that.d->object, that.d->core.coreIndex, - that.d->core.getValueTypeCoreIndex(), - newBinding, flags); - } + QQmlAbstractBinding::Ptr oldBinding; + oldBinding = data->bindings; + + while (oldBinding && oldBinding->targetPropertyIndex() != coreIndex) + oldBinding = oldBinding->nextBinding(); + + if (!oldBinding) + return; + + if (valueTypeIndex != -1 && oldBinding->isValueTypeProxy()) + oldBinding = static_cast<QQmlValueTypeProxyBinding *>(oldBinding.data())->binding(index); + + if (!oldBinding) + return; + + if (!(flags & QQmlPropertyPrivate::DontEnable)) + oldBinding->setEnabled(false, 0); + oldBinding->removeFromObject(); +} + +void QQmlPropertyPrivate::removeBinding(QQmlAbstractBinding *b) +{ + removeBinding(b->targetObject(), b->targetPropertyIndex()); +} + +void QQmlPropertyPrivate::removeBinding(QObject *o, int index) +{ + Q_ASSERT(o); + + QObject *target; + int targetIndex; + findAliasTarget(o, index, &target, &targetIndex); + removeOldBinding(target, targetIndex); +} + +void QQmlPropertyPrivate::removeBinding(const QQmlProperty &that) +{ + if (!that.d || !that.isProperty() || !that.d->object) + return; + + removeBinding(that.d->object, that.d->core.encodedIndex()); } QQmlAbstractBinding * -QQmlPropertyPrivate::binding(QObject *object, int coreIndex, int valueTypeIndex) +QQmlPropertyPrivate::binding(QObject *object, int index) { QQmlData *data = QQmlData::get(object); if (!data) return 0; - QQmlPropertyData *propertyData = - data->propertyCache?data->propertyCache->property(coreIndex):0; - if (propertyData && propertyData->isAlias()) { - QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); + findAliasTarget(object, index, &object, &index); - QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; - if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex) || aCoreIndex == -1) - return 0; - - // This will either be a value type sub-reference or an alias to a value-type sub-reference not both - Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); - aValueTypeIndex = (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex; - return binding(aObject, aCoreIndex, aValueTypeIndex); - } + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex); if (!data->hasBindingBit(coreIndex)) return 0; QQmlAbstractBinding *binding = data->bindings; - while (binding && binding->propertyIndex() != coreIndex) + while (binding && binding->targetPropertyIndex() != coreIndex) binding = binding->nextBinding(); if (binding && valueTypeIndex != -1) { - if (binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) { + if (binding->isValueTypeProxy()) { int index = QQmlPropertyData::encodeValueTypePropertyIndex(coreIndex, valueTypeIndex); binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index); } @@ -786,11 +820,11 @@ QQmlPropertyPrivate::binding(QObject *object, int coreIndex, int valueTypeIndex) void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, QObject **targetObject, int *targetBindingIndex) { - int coreIndex; - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(bindingIndex, &coreIndex); - QQmlData *data = QQmlData::get(object, false); if (data) { + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(bindingIndex, &coreIndex); + QQmlPropertyData *propertyData = data->propertyCache?data->propertyCache->property(coreIndex):0; if (propertyData && propertyData->isAlias()) { @@ -817,119 +851,30 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, *targetBindingIndex = bindingIndex; } -QQmlAbstractBinding * -QQmlPropertyPrivate::setBinding(QObject *object, int coreIndex, int valueTypeIndex, - QQmlAbstractBinding *newBinding, WriteFlags flags) -{ - QQmlData *data = QQmlData::get(object, 0 != newBinding); - QQmlAbstractBinding *binding = 0; - - if (data) { - QQmlPropertyData *propertyData = - data->propertyCache?data->propertyCache->property(coreIndex):0; - if (propertyData && propertyData->isAlias()) { - QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); - QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; - if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { - if (newBinding) newBinding->destroy(); - return 0; - } - - // This will either be a value type sub-reference or an alias to a value-type sub-reference not both - Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); - aValueTypeIndex = (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex; - return setBinding(aObject, aCoreIndex, aValueTypeIndex, newBinding, flags); - } - } - - if (data && data->hasBindingBit(coreIndex)) { - binding = data->bindings; - - while (binding && binding->propertyIndex() != coreIndex) - binding = binding->nextBinding(); - } - - int index = coreIndex; - if (valueTypeIndex != -1) - index = QQmlPropertyData::encodeValueTypePropertyIndex(index, valueTypeIndex); - - if (binding && valueTypeIndex != -1 && binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) - binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index); - - if (binding) { - binding->removeFromObject(); - binding->setEnabled(false, 0); - } - - if (newBinding) { - if (newBinding->propertyIndex() != index || newBinding->object() != object) - newBinding->retargetBinding(object, index); - - Q_ASSERT(newBinding->propertyIndex() == index); - Q_ASSERT(newBinding->object() == object); - - newBinding->addToObject(); - newBinding->setEnabled(true, flags); - } - - return binding; -} - -QQmlAbstractBinding * -QQmlPropertyPrivate::setBindingNoEnable(QObject *object, int coreIndex, int valueTypeIndex, - QQmlAbstractBinding *newBinding) +void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, WriteFlags writeFlags) { - QQmlData *data = QQmlData::get(object, 0 != newBinding); - QQmlAbstractBinding *binding = 0; + Q_ASSERT(binding); - if (data) { - QQmlPropertyData *propertyData = - data->propertyCache?data->propertyCache->property(coreIndex):0; - if (propertyData && propertyData->isAlias()) { - QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); - - QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; - if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { - if (newBinding) newBinding->destroy(); - return 0; - } - - // This will either be a value type sub-reference or an alias to a value-type sub-reference not both - Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); - aValueTypeIndex = (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex; - return setBindingNoEnable(aObject, aCoreIndex, aValueTypeIndex, newBinding); - } - } - - if (data && data->hasBindingBit(coreIndex)) { - binding = data->bindings; + QObject *object = binding->targetObject(); + int index = binding->targetPropertyIndex(); - while (binding && binding->propertyIndex() != coreIndex) - binding = binding->nextBinding(); +#ifndef QT_NO_DEBUG + int coreIndex; + QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex); + QQmlData *data = QQmlData::get(object, true); + if (data->propertyCache) { + QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); + Q_ASSERT(propertyData && !propertyData->isAlias()); } +#endif - int index = coreIndex; - if (valueTypeIndex != -1) - index = QQmlPropertyData::encodeValueTypePropertyIndex(index, valueTypeIndex); - - if (binding && valueTypeIndex != -1 && binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) - binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index); - - if (binding) - binding->removeFromObject(); - - if (newBinding) { - if (newBinding->propertyIndex() != index || newBinding->object() != object) - newBinding->retargetBinding(object, index); + removeOldBinding(object, index, flags); - Q_ASSERT(newBinding->propertyIndex() == index); - Q_ASSERT(newBinding->object() == object); + binding->addToObject(); + if (!(flags & DontEnable)) + binding->setEnabled(true, writeFlags); - newBinding->addToObject(); - } - - return binding; } /*! @@ -946,9 +891,9 @@ QQmlPropertyPrivate::signalExpression(const QQmlProperty &that) if (!data) return 0; - QQmlAbstractBoundSignal *signalHandler = data->signalHandlers; + QQmlBoundSignal *signalHandler = data->signalHandlers; - while (signalHandler && signalHandler->index() != QQmlPropertyPrivate::get(that)->signalIndex()) + while (signalHandler && signalHandler->signalIndex() != QQmlPropertyPrivate::get(that)->signalIndex()) signalHandler = signalHandler->m_nextSignal; if (signalHandler) @@ -959,48 +904,41 @@ QQmlPropertyPrivate::signalExpression(const QQmlProperty &that) /*! Set the signal expression associated with this signal property to \a expr. - Returns the existing signal expression (if any), otherwise null. - - A reference to \a expr will be added by QML. Ownership of the return value - reference is assumed by the caller. + A reference to \a expr will be added by QML. */ -QQmlBoundSignalExpressionPointer -QQmlPropertyPrivate::setSignalExpression(const QQmlProperty &that, - QQmlBoundSignalExpression *expr) +void QQmlPropertyPrivate::setSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *expr) { if (expr) expr->addref(); - return QQmlPropertyPrivate::takeSignalExpression(that, expr); + QQmlPropertyPrivate::takeSignalExpression(that, expr); } /*! Set the signal expression associated with this signal property to \a expr. - Returns the existing signal expression (if any), otherwise null. - - Ownership of \a expr transfers to QML. Ownership of the return value - reference is assumed by the caller. + Ownership of \a expr transfers to QML. */ -QQmlBoundSignalExpressionPointer -QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that, +void QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *expr) { if (!(that.type() & QQmlProperty::SignalProperty)) { if (expr) expr->release(); - return 0; + return; } QQmlData *data = QQmlData::get(that.d->object, 0 != expr); if (!data) - return 0; + return; - QQmlAbstractBoundSignal *signalHandler = data->signalHandlers; + QQmlBoundSignal *signalHandler = data->signalHandlers; - while (signalHandler && signalHandler->index() != QQmlPropertyPrivate::get(that)->signalIndex()) + while (signalHandler && signalHandler->signalIndex() != QQmlPropertyPrivate::get(that)->signalIndex()) signalHandler = signalHandler->m_nextSignal; - if (signalHandler) - return signalHandler->takeExpression(expr); + if (signalHandler) { + signalHandler->takeExpression(expr); + return; + } if (expr) { int signalIndex = QQmlPropertyPrivate::get(that)->signalIndex(); @@ -1008,7 +946,6 @@ QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that, expr->context()->engine); signal->takeExpression(expr); } - return 0; } /*! @@ -1125,7 +1062,7 @@ QVariant QQmlPropertyPrivate::readValueProperty() } // helper function to allow assignment / binding to QList<QUrl> properties. -static QVariant resolvedUrlSequence(const QVariant &value, QQmlContextData *context) +QVariant QQmlPropertyPrivate::resolvedUrlSequence(const QVariant &value, QQmlContextData *context) { QList<QUrl> urls; if (value.userType() == qMetaTypeId<QUrl>()) { @@ -1138,16 +1075,22 @@ static QVariant resolvedUrlSequence(const QVariant &value, QQmlContextData *cont urls = value.value<QList<QUrl> >(); } else if (value.userType() == qMetaTypeId<QStringList>()) { QStringList urlStrings = value.value<QStringList>(); - for (int i = 0; i < urlStrings.size(); ++i) + const int urlStringsSize = urlStrings.size(); + urls.reserve(urlStringsSize); + for (int i = 0; i < urlStringsSize; ++i) urls.append(QUrl(urlStrings.at(i))); } else if (value.userType() == qMetaTypeId<QList<QString> >()) { QList<QString> urlStrings = value.value<QList<QString> >(); - for (int i = 0; i < urlStrings.size(); ++i) + const int urlStringsSize = urlStrings.size(); + urls.reserve(urlStringsSize); + for (int i = 0; i < urlStringsSize; ++i) urls.append(QUrl(urlStrings.at(i))); } // note: QList<QByteArray> is not currently supported. QList<QUrl> resolvedUrls; - for (int i = 0; i < urls.size(); ++i) { + const int urlsSize = urls.size(); + resolvedUrls.reserve(urlsSize); + for (int i = 0; i < urlsSize; ++i) { QUrl u = urls.at(i); if (context && u.isRelative() && !u.isEmpty()) u = context->resolvedUrl(u); @@ -1210,12 +1153,8 @@ QQmlPropertyPrivate::writeValueProperty(QObject *object, QQmlContextData *context, WriteFlags flags) { // Remove any existing bindings on this property - if (!(flags & DontRemoveBinding) && object) { - QQmlAbstractBinding *binding = setBinding(object, core.coreIndex, - core.getValueTypeCoreIndex(), - 0, flags); - if (binding) binding->destroy(); - } + if (!(flags & DontRemoveBinding) && object) + removeBinding(object, core.encodedIndex()); bool rv = false; if (core.isValueTypeVirtual()) { @@ -1255,7 +1194,7 @@ bool QQmlPropertyPrivate::write(QObject *object, // Enum values come through the script engine as doubles if (value.userType() == QVariant::Double) { double integral; - double fractional = modf(value.toDouble(), &integral); + double fractional = std::modf(value.toDouble(), &integral); if (qFuzzyIsNull(fractional)) v.convert(QVariant::Int); } @@ -1414,7 +1353,12 @@ bool QQmlPropertyPrivate::write(QObject *object, if (!ok) { v = value; - if (v.convert(propertyType)) { + if (variantType == QVariant::Double && propertyType == QVariant::String) { + QString number; + QV4::RuntimeHelpers::numberToString(&number, v.toDouble()); + v = number; + ok = true; + } else if (v.convert(propertyType)) { ok = true; } else if (v.isValid() && value.isNull()) { // For historical reasons converting a null QVariant to another type will do the trick @@ -1475,166 +1419,6 @@ bool QQmlPropertyPrivate::write(QObject *object, return true; } -// Returns true if successful, false if an error description was set on expression -bool QQmlPropertyPrivate::writeBinding(QObject *object, - const QQmlPropertyData &core, - QQmlContextData *context, - QQmlJavaScriptExpression *expression, - const QV4::Value &result, bool isUndefined, - WriteFlags flags) -{ - Q_ASSERT(object); - Q_ASSERT(core.coreIndex != -1); - - QQmlEngine *engine = context->engine; - QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); - -#define QUICK_STORE(cpptype, conversion) \ - { \ - cpptype o = (conversion); \ - int status = -1; \ - void *argv[] = { &o, 0, &status, &flags }; \ - QMetaObject::metacall(object, QMetaObject::WriteProperty, core.coreIndex, argv); \ - return true; \ - } \ - - - if (!isUndefined && !core.isValueTypeVirtual()) { - switch (core.propType) { - case QMetaType::Int: - if (result.isInteger()) - QUICK_STORE(int, result.integerValue()) - else if (result.isNumber()) - QUICK_STORE(int, result.doubleValue()) - break; - case QMetaType::Double: - if (result.isNumber()) - QUICK_STORE(double, result.asDouble()) - break; - case QMetaType::Float: - if (result.isNumber()) - QUICK_STORE(float, result.asDouble()) - break; - case QMetaType::QString: - if (result.isString()) - QUICK_STORE(QString, result.toQStringNoThrow()) - break; - default: - if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) { - if (vtw->d()->valueType->typeId == core.propType) { - return vtw->write(object, core.coreIndex); - } - } - break; - } - } -#undef QUICK_STORE - - int type = core.isValueTypeVirtual()?core.valueTypePropType:core.propType; - - QQmlJavaScriptExpression::DeleteWatcher watcher(expression); - - QVariant value; - bool isVarProperty = core.isVarProperty(); - - if (isUndefined) { - } else if (core.isQList()) { - value = QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QObject *> >()); - } else if (result.isNull() && core.isQObject()) { - value = QVariant::fromValue((QObject *)0); - } else if (core.propType == qMetaTypeId<QList<QUrl> >()) { - value = resolvedUrlSequence(QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QUrl> >()), context); - } else if (!isVarProperty && type != qMetaTypeId<QJSValue>()) { - value = QV8Engine::getV4(v8engine)->toVariant(result, type); - } - - if (expression->hasError()) { - return false; - } else if (isVarProperty) { - QV4::FunctionObject *f = result.asFunctionObject(); - if (f && f->isBinding()) { - // we explicitly disallow this case to avoid confusion. Users can still store one - // in an array in a var property if they need to, but the common case is user error. - expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); - expression->delayedError()->setErrorObject(object); - return false; - } - - QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); - Q_ASSERT(vmemo); - vmemo->setVMEProperty(core.coreIndex, result); - } else if (isUndefined && core.isResettable()) { - void *args[] = { 0 }; - QMetaObject::metacall(object, QMetaObject::ResetProperty, core.coreIndex, args); - } else if (isUndefined && type == qMetaTypeId<QVariant>()) { - writeValueProperty(object, core, QVariant(), context, flags); - } else if (type == qMetaTypeId<QJSValue>()) { - QV4::FunctionObject *f = result.asFunctionObject(); - if (f && f->isBinding()) { - expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); - expression->delayedError()->setErrorObject(object); - return false; - } - writeValueProperty(object, core, QVariant::fromValue( - QJSValue(QV8Engine::getV4(v8engine), result.asReturnedValue())), - context, flags); - } else if (isUndefined) { - QString errorStr = QLatin1String("Unable to assign [undefined] to "); - if (!QMetaType::typeName(type)) - errorStr += QLatin1String("[unknown property type]"); - else - errorStr += QLatin1String(QMetaType::typeName(type)); - expression->delayedError()->setErrorDescription(errorStr); - expression->delayedError()->setErrorObject(object); - return false; - } else if (QV4::FunctionObject *f = result.asFunctionObject()) { - if (f->isBinding()) - expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); - else - expression->delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var.")); - expression->delayedError()->setErrorObject(object); - return false; - } else if (!writeValueProperty(object, core, value, context, flags)) { - - if (watcher.wasDeleted()) - return true; - - const char *valueType = 0; - const char *propertyType = 0; - - if (value.userType() == QMetaType::QObjectStar) { - if (QObject *o = *(QObject *const *)value.constData()) { - valueType = o->metaObject()->className(); - - QQmlMetaObject propertyMetaObject = rawMetaObjectForType(QQmlEnginePrivate::get(engine), type); - if (!propertyMetaObject.isNull()) - propertyType = propertyMetaObject.className(); - } - } else if (value.userType() != QVariant::Invalid) { - if (value.userType() == QMetaType::VoidStar) - valueType = "null"; - else - valueType = QMetaType::typeName(value.userType()); - } - - if (!valueType) - valueType = "undefined"; - if (!propertyType) - propertyType = QMetaType::typeName(type); - if (!propertyType) - propertyType = "[unknown property type]"; - - expression->delayedError()->setErrorDescription(QLatin1String("Unable to assign ") + - QLatin1String(valueType) + - QLatin1String(" to ") + - QLatin1String(propertyType)); - expression->delayedError()->setErrorObject(object); - return false; - } - - return true; -} - QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate *engine, int userType) { QMetaType metaType(userType); diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index 98e310ebce..51a1db7b90 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -58,6 +58,7 @@ QT_BEGIN_NAMESPACE class QQmlContext; class QQmlEnginePrivate; class QQmlJavaScriptExpression; + class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount { public: @@ -103,15 +104,18 @@ public: QQmlContextData *, WriteFlags flags = 0); static void findAliasTarget(QObject *, int, QObject **, int *); - static QQmlAbstractBinding *setBinding(QObject *, int coreIndex, - int valueTypeIndex /* -1 */, - QQmlAbstractBinding *, - WriteFlags flags = DontRemoveBinding); - static QQmlAbstractBinding *setBindingNoEnable(QObject *, int coreIndex, - int valueTypeIndex /* -1 */, - QQmlAbstractBinding *); - static QQmlAbstractBinding *binding(QObject *, int coreIndex, - int valueTypeIndex /* -1 */); + enum BindingFlag { + None = 0, + DontEnable = 0x1 + }; + Q_DECLARE_FLAGS(BindingFlags, BindingFlag) + + static void setBinding(QQmlAbstractBinding *binding, BindingFlags flags = None, WriteFlags writeFlags = DontRemoveBinding); + + static void removeBinding(const QQmlProperty &that); + static void removeBinding(QObject *o, int index); + static void removeBinding(QQmlAbstractBinding *b); + static QQmlAbstractBinding *binding(QObject *, int index); static QQmlPropertyData saveValueType(const QQmlPropertyData &, const QMetaObject *, int, @@ -128,20 +132,13 @@ public: // "Public" (to QML) methods static QQmlAbstractBinding *binding(const QQmlProperty &that); - static QQmlAbstractBinding *setBinding(const QQmlProperty &that, - QQmlAbstractBinding *, - WriteFlags flags = DontRemoveBinding); + static void setBinding(const QQmlProperty &that, QQmlAbstractBinding *); static QQmlBoundSignalExpression *signalExpression(const QQmlProperty &that); - static QQmlBoundSignalExpressionPointer setSignalExpression(const QQmlProperty &that, + static void setSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *); - static QQmlBoundSignalExpressionPointer takeSignalExpression(const QQmlProperty &that, + static void takeSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *); static bool write(const QQmlProperty &that, const QVariant &, WriteFlags); - static bool writeBinding(QObject *, const QQmlPropertyData &, - QQmlContextData *context, - QQmlJavaScriptExpression *expression, - const QV4::Value &result, bool isUndefined, - WriteFlags flags); static int valueTypeCoreIndex(const QQmlProperty &that); static int bindingIndex(const QQmlProperty &that); static int bindingIndex(const QQmlPropertyData &that); @@ -150,9 +147,12 @@ public: const QObject *receiver, int method_index, int type = 0, int *types = 0); static void flushSignal(const QObject *sender, int signal_index); + + static QVariant resolvedUrlSequence(const QVariant &value, QQmlContextData *context); }; Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyPrivate::WriteFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyPrivate::BindingFlags) QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 0018275b95..e8c9989fdf 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -42,7 +42,7 @@ #include <private/qqmlaccessors_p.h> #include <private/qmetaobjectbuilder_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <QtCore/qdebug.h> @@ -231,10 +231,10 @@ void QQmlPropertyData::lazyLoad(const QMetaMethod &m) /*! Creates a new empty QQmlPropertyCache. */ -QQmlPropertyCache::QQmlPropertyCache(QJSEngine *e) -: engine(e), _parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0), - signalHandlerIndexCacheStart(0), _hasPropertyOverrides(false), _ownMetaObject(false), - _metaObject(0), argumentsCache(0) +QQmlPropertyCache::QQmlPropertyCache(QV4::ExecutionEngine *e) + : engine(e), _parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0), + signalHandlerIndexCacheStart(0), _hasPropertyOverrides(false), _ownMetaObject(false), + _metaObject(0), argumentsCache(0) { Q_ASSERT(engine); } @@ -242,10 +242,10 @@ QQmlPropertyCache::QQmlPropertyCache(QJSEngine *e) /*! Creates a new QQmlPropertyCache of \a metaObject. */ -QQmlPropertyCache::QQmlPropertyCache(QJSEngine *e, const QMetaObject *metaObject) -: engine(e), _parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0), - signalHandlerIndexCacheStart(0), _hasPropertyOverrides(false), _ownMetaObject(false), - _metaObject(0), argumentsCache(0) +QQmlPropertyCache::QQmlPropertyCache(QV4::ExecutionEngine *e, const QMetaObject *metaObject) + : engine(e), _parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0), + signalHandlerIndexCacheStart(0), _hasPropertyOverrides(false), _ownMetaObject(false), + _metaObject(0), argumentsCache(0) { Q_ASSERT(engine); Q_ASSERT(metaObject); @@ -346,25 +346,6 @@ void QQmlPropertyCache::appendProperty(const QString &name, setNamedProperty(name, index + propertyOffset(), propertyIndexCache.data() + index, (old != 0)); } -void QQmlPropertyCache::appendProperty(const QHashedCStringRef &name, - quint32 flags, int coreIndex, int propType, int notifyIndex) -{ - QQmlPropertyData data; - data.propType = propType; - data.coreIndex = coreIndex; - data.notifyIndex = notifyIndex; - data.flags = flags; - - QQmlPropertyData *old = findNamedProperty(name); - if (old) - data.markAsOverrideOf(old); - - int index = propertyIndexCache.count(); - propertyIndexCache.append(data); - - setNamedProperty(name, index + propertyOffset(), propertyIndexCache.data() + index, (old != 0)); -} - void QQmlPropertyCache::appendSignal(const QString &name, quint32 flags, int coreIndex, const int *types, const QList<QByteArray> &names) { @@ -402,43 +383,6 @@ void QQmlPropertyCache::appendSignal(const QString &name, quint32 flags, int cor setNamedProperty(handlerName, signalHandlerIndex + signalOffset(), signalHandlerIndexCache.data() + signalHandlerIndex, (old != 0)); } -void QQmlPropertyCache::appendSignal(const QHashedCStringRef &name, quint32 flags, int coreIndex, - const int *types, const QList<QByteArray> &names) -{ - QQmlPropertyData data; - data.propType = QVariant::Invalid; - data.coreIndex = coreIndex; - data.flags = flags; - data.arguments = 0; - - QQmlPropertyData handler = data; - handler.flags |= QQmlPropertyData::IsSignalHandler; - - if (types) { - int argumentCount = *types; - QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names); - ::memcpy(args->arguments, types, (argumentCount + 1) * sizeof(int)); - args->argumentsValid = true; - data.arguments = args; - } - - QQmlPropertyData *old = findNamedProperty(name); - if (old) - data.markAsOverrideOf(old); - - int methodIndex = methodIndexCache.count(); - methodIndexCache.append(data); - - int signalHandlerIndex = signalHandlerIndexCache.count(); - signalHandlerIndexCache.append(handler); - - QString handlerName = QLatin1String("on") + name.toUtf16(); - handlerName[2] = handlerName[2].toUpper(); - - setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0)); - setNamedProperty(handlerName, signalHandlerIndex + signalOffset(), signalHandlerIndexCache.data() + signalHandlerIndex, (old != 0)); -} - void QQmlPropertyCache::appendMethod(const QString &name, quint32 flags, int coreIndex, const QList<QByteArray> &names) { @@ -466,33 +410,6 @@ void QQmlPropertyCache::appendMethod(const QString &name, quint32 flags, int cor setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0)); } -void QQmlPropertyCache::appendMethod(const QHashedCStringRef &name, quint32 flags, int coreIndex, - const QList<QByteArray> &names) -{ - int argumentCount = names.count(); - - QQmlPropertyData data; - data.propType = QMetaType::QVariant; - data.coreIndex = coreIndex; - - QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names); - for (int ii = 0; ii < argumentCount; ++ii) - args->arguments[ii + 1] = QMetaType::QVariant; - args->argumentsValid = true; - data.arguments = args; - - data.flags = flags; - - QQmlPropertyData *old = findNamedProperty(name); - if (old) - data.markAsOverrideOf(old); - - int methodIndex = methodIndexCache.count(); - methodIndexCache.append(data); - - setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0)); -} - // Returns this property cache's metaObject. May be null if it hasn't been created yet. const QMetaObject *QQmlPropertyCache::metaObject() const { @@ -818,7 +735,7 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const data->propType = registerResult == -1 ? QMetaType::UnknownType : registerResult; } } - data->flags |= flagsForPropertyType(data->propType, qobject_cast<QQmlEngine*>(engine)); + data->flags |= flagsForPropertyType(data->propType, engine->qmlEngine()); } data->flags &= ~QQmlPropertyData::NotFullyResolved; @@ -837,7 +754,7 @@ void QQmlPropertyCache::updateRecur(const QMetaObject *metaObject) void QQmlPropertyCache::update(const QMetaObject *metaObject) { Q_ASSERT(metaObject); - Q_ASSERT(stringCache.isEmpty()); + stringCache.clear(); // Preallocate enough space in the index caches for all the properties/methods/signals that // are not cached in a parent cache so that the caches never need to be reallocated as this @@ -862,7 +779,6 @@ void QQmlPropertyCache::update(const QMetaObject *metaObject) */ void QQmlPropertyCache::invalidate(const QMetaObject *metaObject) { - stringCache.clear(); propertyIndexCache.clear(); methodIndexCache.clear(); signalHandlerIndexCache.clear(); @@ -894,19 +810,17 @@ void QQmlPropertyCache::invalidate(const QMetaObject *metaObject) This is different from QMetaMethod::methodIndex(). */ QQmlPropertyData * -QQmlPropertyCache::signal(int index, QQmlPropertyCache **c) const +QQmlPropertyCache::signal(int index) const { if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.count())) return 0; if (index < signalHandlerIndexCacheStart) - return _parent->signal(index, c); + return _parent->signal(index); QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - signalHandlerIndexCacheStart)); - if (rv->notFullyResolved()) resolve(rv); Q_ASSERT(rv->isSignal() || rv->coreIndex == -1); - if (c) *c = const_cast<QQmlPropertyCache *>(this); - return rv; + return ensureResolved(rv); } int QQmlPropertyCache::methodIndexToSignalIndex(int index) const @@ -1102,52 +1016,6 @@ QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int a return args; } -/*! \internal - \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()). - This is different from QMetaMethod::methodIndex(). -*/ -QString QQmlPropertyCache::signalParameterStringForJS(int index, QString *errorString) -{ - QQmlPropertyCache *c = 0; - QQmlPropertyData *signalData = signal(index, &c); - if (!signalData) - return QString(); - - typedef QQmlPropertyCacheMethodArguments A; - - if (signalData->arguments) { - A *arguments = static_cast<A *>(signalData->arguments); - if (arguments->signalParameterStringForJS) { - if (arguments->parameterError) { - if (errorString) - *errorString = *arguments->signalParameterStringForJS; - return QString(); - } - return *arguments->signalParameterStringForJS; - } - } - - QList<QByteArray> parameterNameList = signalParameterNames(index); - - if (!signalData->arguments) { - A *args = c->createArgumentsObject(parameterNameList.count(), parameterNameList); - signalData->arguments = args; - } - - QString error; - QString parameters = signalParameterStringForJS(QV8Engine::getV4(engine), parameterNameList, &error); - - A *arguments = static_cast<A *>(signalData->arguments); - arguments->signalParameterStringForJS = new QString(!error.isEmpty() ? error : parameters); - if (!error.isEmpty()) { - arguments->parameterError = true; - if (errorString) - *errorString = *arguments->signalParameterStringForJS; - return QString(); - } - return *arguments->signalParameterStringForJS; -} - QString QQmlPropertyCache::signalParameterStringForJS(QV4::ExecutionEngine *engine, const QList<QByteArray> ¶meterNameList, QString *errorString) { bool unnamedParameter = false; @@ -1406,19 +1274,19 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) if (data->propType != 0) returnType = QMetaType::typeName(data->propType); - QByteArray signature = methods.at(ii).first.toUtf8() + "("; + QByteArray signature = methods.at(ii).first.toUtf8() + '('; QQmlPropertyCacheMethodArguments *arguments = 0; if (data->hasArguments()) { arguments = (QQmlPropertyCacheMethodArguments *)data->arguments; Q_ASSERT(arguments->argumentsValid); for (int ii = 0; ii < arguments->arguments[0]; ++ii) { - if (ii != 0) signature.append(","); + if (ii != 0) signature.append(','); signature.append(QMetaType::typeName(arguments->arguments[1 + ii])); } } - signature.append(")"); + signature.append(')'); QMetaMethodBuilder method; if (data->isSignal()) { diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 6ed9ec0d36..610709ef7f 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -54,7 +54,7 @@ #include <QtCore/qvarlengtharray.h> #include <QtCore/qvector.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> QT_BEGIN_NAMESPACE @@ -174,7 +174,6 @@ public: int propType; // When !NotFullyResolved const char *propTypeName; // When NotFullyResolved }; - int coreIndex; union { // The notify index is in the range returned by QObjectPrivate::signalIndex(). // This is different from QMetaMethod::methodIndex(). @@ -208,6 +207,7 @@ public: qintptr accessorData; }; }; + int coreIndex; private: friend class QQmlPropertyData; @@ -243,8 +243,8 @@ class QQmlPropertyCacheMethodArguments; class Q_QML_PRIVATE_EXPORT QQmlPropertyCache : public QQmlRefCount, public QQmlCleanup { public: - QQmlPropertyCache(QJSEngine *); - QQmlPropertyCache(QJSEngine *, const QMetaObject *); + QQmlPropertyCache(QV4::ExecutionEngine *); + QQmlPropertyCache(QV4::ExecutionEngine *, const QMetaObject *); virtual ~QQmlPropertyCache(); void update(const QMetaObject *); @@ -267,16 +267,10 @@ public: int methodCount, int signalCount); void appendProperty(const QString &, quint32 flags, int coreIndex, int propType, int notifyIndex); - void appendProperty(const QHashedCStringRef &, - quint32 flags, int coreIndex, int propType, int notifyIndex); void appendSignal(const QString &, quint32, int coreIndex, const int *types = 0, const QList<QByteArray> &names = QList<QByteArray>()); - void appendSignal(const QHashedCStringRef &, quint32, int coreIndex, const int *types = 0, - const QList<QByteArray> &names = QList<QByteArray>()); void appendMethod(const QString &, quint32 flags, int coreIndex, const QList<QByteArray> &names = QList<QByteArray>()); - void appendMethod(const QHashedCStringRef &, quint32 flags, int coreIndex, - const QList<QByteArray> &names = QList<QByteArray>()); const QMetaObject *metaObject() const; const QMetaObject *createMetaObject(); @@ -290,7 +284,7 @@ public: QQmlPropertyData *property(int) const; QQmlPropertyData *method(int) const; - QQmlPropertyData *signal(int index) const { return signal(index, 0); } + QQmlPropertyData *signal(int index) const; int methodIndexToSignalIndex(int) const; QStringList propertyNames() const; @@ -313,7 +307,6 @@ public: static int originalClone(QObject *, int index); QList<QByteArray> signalParameterNames(int index) const; - QString signalParameterStringForJS(int index, QString *errorString = 0); static QString signalParameterStringForJS(QV4::ExecutionEngine *engine, const QList<QByteArray> ¶meterNameList, QString *errorString = 0); const char *className() const; @@ -347,9 +340,7 @@ private: QQmlPropertyData::Flag methodFlags = QQmlPropertyData::NoFlags, QQmlPropertyData::Flag signalFlags = QQmlPropertyData::NoFlags); - QQmlPropertyCacheMethodArguments *createArgumentsObject(int count, - const QList<QByteArray> &names); - QQmlPropertyData *signal(int, QQmlPropertyCache **) const; + QQmlPropertyCacheMethodArguments *createArgumentsObject(int count, const QList<QByteArray> &names); typedef QVector<QQmlPropertyData> IndexCache; typedef QStringMultiHash<QPair<int, QQmlPropertyData *> > StringCache; @@ -377,8 +368,10 @@ private: _hasPropertyOverrides |= isOverride; } - QJSEngine *engine; +public: + QV4::ExecutionEngine *engine; +private: QQmlPropertyCache *_parent; int propertyIndexCacheStart; int methodIndexCacheStart; diff --git a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h index ea267f9c30..6403e85f2a 100644 --- a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h +++ b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h @@ -60,7 +60,7 @@ public: virtual void write(const QVariant &value) = 0; private: - friend class QQmlVMEMetaObject; + friend class QQmlInterceptorMetaObject; int m_coreIndex; int m_valueTypeCoreIndex; diff --git a/src/qml/qml/qqmlproxymetaobject.cpp b/src/qml/qml/qqmlproxymetaobject.cpp index bf735a9f0c..ba5215b831 100644 --- a/src/qml/qml/qqmlproxymetaobject.cpp +++ b/src/qml/qml/qqmlproxymetaobject.cpp @@ -59,8 +59,10 @@ QQmlProxyMetaObject::~QQmlProxyMetaObject() proxies = 0; } -int QQmlProxyMetaObject::metaCall(QMetaObject::Call c, int id, void **a) +int QQmlProxyMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void **a) { + Q_ASSERT(object == o); + if ((c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) && id >= metaObjects->last().propertyOffset) { @@ -108,7 +110,7 @@ int QQmlProxyMetaObject::metaCall(QMetaObject::Call c, int id, void **a) } if (parent) - return parent->metaCall(c, id, a); + return parent->metaCall(o, c, id, a); else return object->qt_metacall(c, id, a); } diff --git a/src/qml/qml/qqmlproxymetaobject_p.h b/src/qml/qml/qqmlproxymetaobject_p.h index 862b0fb7c2..6ae35fe11f 100644 --- a/src/qml/qml/qqmlproxymetaobject_p.h +++ b/src/qml/qml/qqmlproxymetaobject_p.h @@ -71,7 +71,7 @@ public: virtual ~QQmlProxyMetaObject(); protected: - virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + virtual int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a); private: QList<ProxyData> *metaObjects; diff --git a/src/qml/qml/qqmlscriptstring_p.h b/src/qml/qml/qqmlscriptstring_p.h index 24bfdcdd4e..aa0a1e5695 100644 --- a/src/qml/qml/qqmlscriptstring_p.h +++ b/src/qml/qml/qqmlscriptstring_p.h @@ -34,6 +34,17 @@ #ifndef QQMLSCRIPTSTRING_P_H #define QQMLSCRIPTSTRING_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qqmlscriptstring.h" #include <QtQml/qqmlcontext.h> diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index d713e9ee03..1e671542be 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -81,7 +81,7 @@ #define ASSERT_MAINTHREAD() do { if (m_thread->isThisThread()) qFatal("QQmlTypeLoader: Caller not in main thread"); } while (false) #define ASSERT_LOADTHREAD() do { if (!m_thread->isThisThread()) qFatal("QQmlTypeLoader: Caller not in load thread"); } while (false) -#define ASSERT_CALLBACK() do { if(!m_manager || !m_manager->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while(false) +#define ASSERT_CALLBACK() do { if (!m_typeLoader || !m_typeLoader->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while (false) #else @@ -647,6 +647,8 @@ void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob) { Q_ASSERT(m_waitingFor.contains(blob)); Q_ASSERT(blob->status() == Error || blob->status() == Complete); + QQmlCompilingProfiler prof(QQmlEnginePrivate::get(typeLoader()->engine())->profiler, + blob->url()); m_inCallback = true; @@ -909,97 +911,116 @@ void QQmlTypeLoader::unlock() m_thread->unlock(); } -/*! -Load the provided \a blob from the network or filesystem. +struct PlainLoader { + void loadThread(QQmlTypeLoader *loader, QQmlDataBlob *blob) const + { + loader->loadThread(blob); + } + void load(QQmlTypeLoader *loader, QQmlDataBlob *blob) const + { + loader->m_thread->load(blob); + } + void loadAsync(QQmlTypeLoader *loader, QQmlDataBlob *blob) const + { + loader->m_thread->loadAsync(blob); + } +}; -The loader must be locked. -*/ -void QQmlTypeLoader::load(QQmlDataBlob *blob, Mode mode) +struct StaticLoader { + const QByteArray &data; + StaticLoader(const QByteArray &data) : data(data) {} + + void loadThread(QQmlTypeLoader *loader, QQmlDataBlob *blob) const + { + loader->loadWithStaticDataThread(blob, data); + } + void load(QQmlTypeLoader *loader, QQmlDataBlob *blob) const + { + loader->m_thread->loadWithStaticData(blob, data); + } + void loadAsync(QQmlTypeLoader *loader, QQmlDataBlob *blob) const + { + loader->m_thread->loadWithStaticDataAsync(blob, data); + } +}; + +struct CachedLoader { + const QQmlPrivate::CachedQmlUnit *unit; + CachedLoader(const QQmlPrivate::CachedQmlUnit *unit) : unit(unit) {} + + void loadThread(QQmlTypeLoader *loader, QQmlDataBlob *blob) const + { + loader->loadWithCachedUnitThread(blob, unit); + } + void load(QQmlTypeLoader *loader, QQmlDataBlob *blob) const + { + loader->m_thread->loadWithCachedUnit(blob, unit); + } + void loadAsync(QQmlTypeLoader *loader, QQmlDataBlob *blob) const + { + loader->m_thread->loadWithCachedUnit(blob, unit); + } +}; + +template<typename Loader> +void QQmlTypeLoader::doLoad(const Loader &loader, QQmlDataBlob *blob, Mode mode) { #ifdef DATABLOB_DEBUG - qWarning("QQmlTypeLoader::load(%s): %s thread", qPrintable(blob->m_url.toString()), + qWarning("QQmlTypeLoader::doLoad(%s): %s thread", qPrintable(blob->m_url.toString()), m_thread->isThisThread()?"Compile":"Engine"); #endif blob->startLoading(); if (m_thread->isThisThread()) { unlock(); - loadThread(blob); + loader.loadThread(this, blob); lock(); - } else if (mode == PreferSynchronous) { + } else if (mode == Asynchronous) { + blob->m_data.setIsAsync(true); unlock(); - m_thread->load(blob); + loader.loadAsync(this, blob); lock(); - if (!blob->isCompleteOrError()) - blob->m_data.setIsAsync(true); } else { - Q_ASSERT(mode == Asynchronous); - blob->m_data.setIsAsync(true); unlock(); - m_thread->loadAsync(blob); + loader.load(this, blob); lock(); + if (mode == PreferSynchronous) { + if (!blob->isCompleteOrError()) + blob->m_data.setIsAsync(true); + } else { + Q_ASSERT(mode == Synchronous); + while (!blob->isCompleteOrError()) { + unlock(); + m_thread->waitForNextMessage(); + lock(); + } + } } } /*! -Load the provided \a blob with \a data. The blob's URL is not used by the data loader in this case. +Load the provided \a blob from the network or filesystem. The loader must be locked. */ -void QQmlTypeLoader::loadWithStaticData(QQmlDataBlob *blob, const QByteArray &data, Mode mode) +void QQmlTypeLoader::load(QQmlDataBlob *blob, Mode mode) { -#ifdef DATABLOB_DEBUG - qWarning("QQmlTypeLoader::loadWithStaticData(%s, data): %s thread", qPrintable(blob->m_url.toString()), - m_thread->isThisThread()?"Compile":"Engine"); -#endif + doLoad(PlainLoader(), blob, mode); +} - blob->startLoading(); +/*! +Load the provided \a blob with \a data. The blob's URL is not used by the data loader in this case. - if (m_thread->isThisThread()) { - unlock(); - loadWithStaticDataThread(blob, data); - lock(); - } else if (mode == PreferSynchronous) { - unlock(); - m_thread->loadWithStaticData(blob, data); - lock(); - if (!blob->isCompleteOrError()) - blob->m_data.setIsAsync(true); - } else { - Q_ASSERT(mode == Asynchronous); - blob->m_data.setIsAsync(true); - unlock(); - m_thread->loadWithStaticDataAsync(blob, data); - lock(); - } +The loader must be locked. +*/ +void QQmlTypeLoader::loadWithStaticData(QQmlDataBlob *blob, const QByteArray &data, Mode mode) +{ + doLoad(StaticLoader(data), blob, mode); } void QQmlTypeLoader::loadWithCachedUnit(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit, Mode mode) { -#ifdef DATABLOB_DEBUG - qWarning("QQmlTypeLoader::loadWithUnitFcatory(%s, data): %s thread", qPrintable(blob->m_url.toString()), - m_thread->isThisThread()?"Compile":"Engine"); -#endif - - blob->startLoading(); - - if (m_thread->isThisThread()) { - unlock(); - loadWithCachedUnitThread(blob, unit); - lock(); - } else if (mode == PreferSynchronous) { - unlock(); - m_thread->loadWithCachedUnit(blob, unit); - lock(); - if (!blob->isCompleteOrError()) - blob->m_data.setIsAsync(true); - } else { - Q_ASSERT(mode == Asynchronous); - blob->m_data.setIsAsync(true); - unlock(); - m_thread->loadWithCachedUnitAsync(blob, unit); - lock(); - } + doLoad(CachedLoader(unit), blob, mode); } void QQmlTypeLoader::loadWithStaticDataThread(QQmlDataBlob *blob, const QByteArray &data) @@ -1194,6 +1215,8 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, QQmlFile *file) void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::Data &d) { QML_MEMORY_SCOPE_URL(blob->url()); + QQmlCompilingProfiler prof(QQmlEnginePrivate::get(engine())->profiler, blob->url()); + blob->m_inCallback = true; blob->dataReceived(d); @@ -1212,6 +1235,8 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::Data &d) void QQmlTypeLoader::setCachedUnit(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit) { QML_MEMORY_SCOPE_URL(blob->url()); + QQmlCompilingProfiler prof(QQmlEnginePrivate::get(engine())->profiler, blob->url()); + blob->m_inCallback = true; blob->initializeFromCachedUnit(unit); @@ -1612,6 +1637,20 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode) } else { QQmlTypeLoader::load(typeData, mode); } + } else if ((mode == PreferSynchronous || mode == Synchronous) && QQmlFile::isSynchronous(url)) { + // this was started Asynchronous, but we need to force Synchronous + // completion now (if at all possible with this type of URL). + + if (!m_thread->isThisThread()) { + // this only works when called directly from the UI thread, but not + // when recursively called on the QML thread via resolveTypes() + + while (!typeData->isCompleteOrError()) { + unlock(); + m_thread->waitForNextMessage(); + lock(); + } + } } typeData->addref(); @@ -1623,12 +1662,12 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode) Returns a QQmlTypeData for the given \a data with the provided base \a url. The QQmlTypeData will not be cached. */ -QQmlTypeData *QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url) +QQmlTypeData *QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url, Mode mode) { LockHolder<QQmlTypeLoader> holder(this); QQmlTypeData *typeData = new QQmlTypeData(url, this); - QQmlTypeLoader::loadWithStaticData(typeData, data); + QQmlTypeLoader::loadWithStaticData(typeData, data, mode); return typeData; } @@ -2114,6 +2153,7 @@ void QQmlTypeData::dataReceived(const Data &data) QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames()); if (!compiler.generateFromQml(code, finalUrlString(), m_document.data())) { QList<QQmlError> errors; + errors.reserve(compiler.errors.count()); foreach (const QQmlJS::DiagnosticMessage &msg, compiler.errors) { QQmlError e; e.setUrl(finalUrl()); @@ -2238,8 +2278,6 @@ void QQmlTypeData::compile() m_compiledData = new QQmlCompiledData(typeLoader()->engine()); - QQmlCompilingProfiler prof(QQmlEnginePrivate::get(typeLoader()->engine())->profiler, finalUrlString()); - QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), m_compiledData, this, m_document.data()); if (!compiler.compile()) { setError(compiler.compilationErrors()); @@ -2282,8 +2320,8 @@ void QQmlTypeData::resolveTypes() m_namespaces.insert(csRef.prefix); } - int majorVersion = -1; - int minorVersion = -1; + int majorVersion = csRef.majorVersion > -1 ? csRef.majorVersion : -1; + int minorVersion = csRef.minorVersion > -1 ? csRef.minorVersion : -1; if (!resolveType(typeName, majorVersion, minorVersion, ref)) return; @@ -2452,12 +2490,10 @@ void QQmlScriptData::initialize(QQmlEngine *engine) addref(); } -QV4::PersistentValue QQmlScriptData::scriptValueForContext(QQmlContextData *parentCtxt) +QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parentCtxt) { if (m_loaded) - return m_value; - - QV4::PersistentValue rv; + return m_value.value(); Q_ASSERT(parentCtxt && parentCtxt->engine); QQmlEnginePrivate *ep = QQmlEnginePrivate::get(parentCtxt->engine); @@ -2507,8 +2543,9 @@ QV4::PersistentValue QQmlScriptData::scriptValueForContext(QQmlContextData *pare } else { scriptsArray = ctxt->importedScripts.valueRef(); } + QV4::ScopedValue v(scope); for (int ii = 0; ii < scripts.count(); ++ii) - scriptsArray->putIndexed(ii, *scripts.at(ii)->scriptData()->scriptValueForContext(ctxt).valueRef()); + scriptsArray->putIndexed(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(ctxt))); if (!hasEngine()) initialize(parentCtxt->engine); @@ -2516,13 +2553,13 @@ QV4::PersistentValue QQmlScriptData::scriptValueForContext(QQmlContextData *pare if (!m_program) { if (shared) m_loaded = true; - return QV4::PersistentValue(); + return QV4::Encode::undefined(); } - QV4::ScopedValue qmlglobal(scope, QV4::QmlContextWrapper::qmlScope(v4, ctxt, 0)); - QV4::QmlContextWrapper::takeContextOwnership(qmlglobal); + QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->rootContext()->newQmlContext(ctxt, 0)); + qmlContext->takeContextOwnership(); - m_program->qml.set(scope.engine, qmlglobal); + m_program->qmlContext.set(scope.engine, qmlContext); m_program->run(); if (scope.engine->hasException) { QQmlError error = scope.engine->catchExceptionAsQmlError(); @@ -2530,13 +2567,13 @@ QV4::PersistentValue QQmlScriptData::scriptValueForContext(QQmlContextData *pare ep->warning(error); } - rv.set(scope.engine, qmlglobal); + QV4::ScopedValue retval(scope, qmlContext->d()->qml); if (shared) { - m_value = rv; + m_value.set(scope.engine, retval); m_loaded = true; } - return rv; + return retval->asReturnedValue(); } void QQmlScriptData::clear() @@ -2594,7 +2631,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) return; } if (!unit) { - unit.take(new EmptyCompilationUnit); + unit.adopt(new EmptyCompilationUnit); } irUnit.javaScriptCompilationUnit = unit; irUnit.imports = collector.imports; diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index d9ea273698..6433601ba8 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -60,7 +60,7 @@ #include <private/qflagpointer_p.h> #include <private/qqmlirbuilder_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4script_p.h> QT_BEGIN_NAMESPACE @@ -215,7 +215,7 @@ class Q_AUTOTEST_EXPORT QQmlTypeLoader { Q_DECLARE_TR_FUNCTIONS(QQmlTypeLoader) public: - enum Mode { PreferSynchronous, Asynchronous }; + enum Mode { PreferSynchronous, Asynchronous, Synchronous }; class Q_QML_PRIVATE_EXPORT Blob : public QQmlDataBlob { @@ -283,7 +283,7 @@ public: QQmlImportDatabase *importDatabase(); QQmlTypeData *getType(const QUrl &url, Mode mode = PreferSynchronous); - QQmlTypeData *getType(const QByteArray &, const QUrl &url); + QQmlTypeData *getType(const QByteArray &, const QUrl &url, Mode mode = PreferSynchronous); QQmlScriptBlob *getScript(const QUrl &); QQmlQmldirData *getQmldir(const QUrl &); @@ -362,6 +362,13 @@ private: QmldirCache m_qmldirCache; ImportDirCache m_importDirCache; ImportQmlDirCache m_importQmlDirCache; + + template<typename Loader> + void doLoad(const Loader &loader, QQmlDataBlob *blob, Mode mode); + + friend struct PlainLoader; + friend struct CachedLoader; + friend struct StaticLoader; }; class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob @@ -474,7 +481,7 @@ public: QQmlTypeNameCache *importCache; QList<QQmlScriptBlob *> scripts; - QV4::PersistentValue scriptValueForContext(QQmlContextData *parentCtxt); + QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt); protected: virtual void clear(); // From QQmlCleanup diff --git a/src/qml/qml/qqmltypenamecache.cpp b/src/qml/qml/qqmltypenamecache.cpp index d0658f2c3c..23cc12c895 100644 --- a/src/qml/qml/qqmltypenamecache.cpp +++ b/src/qml/qml/qqmltypenamecache.cpp @@ -125,7 +125,7 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name, cons const Import *i = static_cast<const Import *>(importNamespace); Q_ASSERT(i->scriptIndex == -1); - QMap<const Import *, QStringHash<Import> >::const_iterator it = m_namespacedImports.find(i); + QMap<const Import *, QStringHash<Import> >::const_iterator it = m_namespacedImports.constFind(i); if (it != m_namespacedImports.constEnd()) { Result r = query(*it, name); if (r.isValid()) diff --git a/src/qml/qml/qqmltypenotavailable_p.h b/src/qml/qml/qqmltypenotavailable_p.h index f69a4f8732..324e07b80c 100644 --- a/src/qml/qml/qqmltypenotavailable_p.h +++ b/src/qml/qml/qqmltypenotavailable_p.h @@ -34,6 +34,17 @@ #ifndef QQMLTYPENOTAVAILABLE_H #define QQMLTYPENOTAVAILABLE_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <qqml.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 8a2118ef27..6c29f2fbb5 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -48,12 +48,8 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(QmlTypeWrapper); -Heap::QmlTypeWrapper::QmlTypeWrapper(ExecutionEngine *engine) - : Heap::Object(engine) - , mode(IncludeEnums) - , type(Q_NULLPTR) - , typeNamespace(Q_NULLPTR) - , importNamespace(Q_NULLPTR) +Heap::QmlTypeWrapper::QmlTypeWrapper() + : mode(IncludeEnums) { } @@ -103,7 +99,7 @@ ReturnedValue QmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, Q Q_ASSERT(t); Scope scope(engine); - Scoped<QmlTypeWrapper> w(scope, engine->memoryManager->alloc<QmlTypeWrapper>(engine)); + Scoped<QmlTypeWrapper> w(scope, engine->memoryManager->allocObject<QmlTypeWrapper>()); w->d()->mode = mode; w->d()->object = o; w->d()->type = t; return w.asReturnedValue(); } @@ -117,26 +113,26 @@ ReturnedValue QmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, Q Q_ASSERT(importNamespace); Scope scope(engine); - Scoped<QmlTypeWrapper> w(scope, engine->memoryManager->alloc<QmlTypeWrapper>(engine)); + Scoped<QmlTypeWrapper> w(scope, engine->memoryManager->allocObject<QmlTypeWrapper>()); w->d()->mode = mode; w->d()->object = o; w->d()->typeNamespace = t; w->d()->importNamespace = importNamespace; t->addref(); return w.asReturnedValue(); } -ReturnedValue QmlTypeWrapper::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasProperty) { Q_ASSERT(m->as<QmlTypeWrapper>()); - QV4::ExecutionEngine *v4 = static_cast<QmlTypeWrapper *>(m)->engine(); + QV4::ExecutionEngine *v4 = static_cast<const QmlTypeWrapper *>(m)->engine(); QV4::Scope scope(v4); - Scoped<QmlTypeWrapper> w(scope, static_cast<QmlTypeWrapper *>(m)); + Scoped<QmlTypeWrapper> w(scope, static_cast<const QmlTypeWrapper *>(m)); if (hasProperty) *hasProperty = true; - QQmlContextData *context = v4->v8Engine->callingContext(); + QQmlContextData *context = v4->callingQmlContext(); QObject *object = w->d()->object; @@ -182,14 +178,14 @@ ReturnedValue QmlTypeWrapper::get(Managed *m, String *name, bool *hasProperty) if (name->startsWithUpper()) { bool ok = false; - int value = type->enumValue(name, &ok); + int value = type->enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); if (ok) return QV4::Primitive::fromInt32(value).asReturnedValue(); // Fall through to base implementation } else if (w->d()->object) { - QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object); + QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(QQmlEnginePrivate::get(v4->qmlEngine())), object); if (ao) return QV4::QObjectWrapper::getQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, hasProperty); @@ -240,12 +236,13 @@ void QmlTypeWrapper::put(Managed *m, String *name, const Value &value) return; QV4::Scope scope(v4); - QQmlContextData *context = v4->v8Engine->callingContext(); + QQmlContextData *context = v4->callingQmlContext(); QQmlType *type = w->d()->type; if (type && !type->isSingleton() && w->d()->object) { QObject *object = w->d()->object; - QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object); + QQmlEngine *e = scope.engine->qmlEngine(); + QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(QQmlEnginePrivate::get(e)), object); if (ao) QV4::QObjectWrapper::setQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); } else if (type && type->isSingleton()) { diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index 660d2836ff..e67b457c59 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -48,7 +48,7 @@ #include <QtCore/qglobal.h> #include <QtCore/qpointer.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4object_p.h> QT_BEGIN_NAMESPACE @@ -66,7 +66,7 @@ struct QmlTypeWrapper : Object { ExcludeEnums }; - QmlTypeWrapper(QV4::ExecutionEngine *engine); + QmlTypeWrapper(); ~QmlTypeWrapper(); TypeNameMode mode; QPointer<QObject> object; @@ -94,7 +94,7 @@ struct Q_QML_EXPORT QmlTypeWrapper : Object Heap::QmlTypeWrapper::TypeNameMode = Heap::QmlTypeWrapper::IncludeEnums); - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static void put(Managed *m, String *name, const Value &value); static PropertyAttributes query(const Managed *, String *name); static bool isEqualTo(Managed *that, Managed *o); diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index 6825e5f004..53b1ffccb5 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -518,7 +518,9 @@ void QQmlEasingValueType::setBezierCurve(const QVariantList &customCurveVariant) if ((variantList.count() % 6) == 0) { bool allRealsOk = true; QList<qreal> reals; - for (int i = 0; i < variantList.count(); i++) { + const int variantListCount = variantList.count(); + reals.reserve(variantListCount); + for (int i = 0; i < variantListCount; i++) { bool ok; const qreal real = variantList.at(i).toReal(&ok); reals.append(real); diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h index 2c02cc0aa1..abd73d7f35 100644 --- a/src/qml/qml/qqmlvaluetype_p.h +++ b/src/qml/qml/qqmlvaluetype_p.h @@ -209,7 +209,6 @@ struct QQmlEasingValueType { QEasingCurve v; Q_GADGET - Q_ENUMS(Type) Q_PROPERTY(QQmlEasingValueType::Type type READ type WRITE setType FINAL) Q_PROPERTY(qreal amplitude READ amplitude WRITE setAmplitude FINAL) @@ -243,6 +242,7 @@ public: SineCurve = QEasingCurve::SineCurve, CosineCurve = QEasingCurve::CosineCurve, Bezier = QEasingCurve::BezierSpline }; + Q_ENUM(Type) Type type() const; qreal amplitude() const; diff --git a/src/qml/qml/qqmlvaluetypeproxybinding.cpp b/src/qml/qml/qqmlvaluetypeproxybinding.cpp index cfc9b196d2..3bc8493cbb 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding.cpp +++ b/src/qml/qml/qqmlvaluetypeproxybinding.cpp @@ -35,80 +35,42 @@ QT_BEGIN_NAMESPACE -// Used in qqmlabstractbinding.cpp -QQmlAbstractBinding::VTable QQmlValueTypeProxyBinding_vtable = { - QQmlAbstractBinding::default_destroy<QQmlValueTypeProxyBinding>, - QQmlAbstractBinding::default_expression, - QQmlValueTypeProxyBinding::propertyIndex, - QQmlValueTypeProxyBinding::object, - QQmlValueTypeProxyBinding::setEnabled, - QQmlValueTypeProxyBinding::update, - QQmlAbstractBinding::default_retargetBinding -}; - QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, int index) -: QQmlAbstractBinding(ValueTypeProxy), m_object(o), m_index(index), m_bindings(0) + : QQmlAbstractBinding(), + m_bindings(0) { + m_target = o; + m_targetIndex = index; } QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding() { - QQmlAbstractBinding *binding = m_bindings; - // This must be identical to the logic in QQmlData::destroyed() + QQmlAbstractBinding *binding = m_bindings.data(); while (binding) { - QQmlAbstractBinding *next = binding->nextBinding(); binding->setAddedToObject(false); - binding->setNextBinding(0); - binding->destroy(); - binding = next; + binding = binding->nextBinding(); } } -void QQmlValueTypeProxyBinding::setEnabled(QQmlAbstractBinding *_This, - bool e, QQmlPropertyPrivate::WriteFlags flags) +void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) { - QQmlValueTypeProxyBinding *This = static_cast<QQmlValueTypeProxyBinding *>(_This); - - if (e) { - QQmlAbstractBinding *bindings = This->m_bindings; - This->recursiveEnable(bindings, flags); - } else { - QQmlAbstractBinding *bindings = This->m_bindings; - This->recursiveDisable(bindings); + QQmlAbstractBinding *b = m_bindings.data(); + while (b) { + b->setEnabled(e, flags); + b = b->nextBinding(); } } -void QQmlValueTypeProxyBinding::recursiveEnable(QQmlAbstractBinding *b, QQmlPropertyPrivate::WriteFlags flags) -{ - if (!b) - return; - - recursiveEnable(b->nextBinding(), flags); - - if (b) - b->setEnabled(true, flags); -} - -void QQmlValueTypeProxyBinding::recursiveDisable(QQmlAbstractBinding *b) -{ - if (!b) - return; - - recursiveDisable(b->nextBinding()); - - if (b) - b->setEnabled(false, 0); -} - -void QQmlValueTypeProxyBinding::update(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags) +bool QQmlValueTypeProxyBinding::isValueTypeProxy() const { + return true; } QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex) { - QQmlAbstractBinding *binding = m_bindings; + QQmlAbstractBinding *binding = m_bindings.data(); - while (binding && binding->propertyIndex() != propertyIndex) + while (binding && binding->targetPropertyIndex() != propertyIndex) binding = binding->nextBinding(); return binding; @@ -119,23 +81,20 @@ Removes a collection of bindings, corresponding to the set bits in \a mask. */ void QQmlValueTypeProxyBinding::removeBindings(quint32 mask) { - QQmlAbstractBinding *binding = m_bindings; + QQmlAbstractBinding *binding = m_bindings.data(); QQmlAbstractBinding *lastBinding = 0; while (binding) { - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(binding->propertyIndex()); + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(binding->targetPropertyIndex()); if (valueTypeIndex != -1 && (mask & (1 << valueTypeIndex))) { QQmlAbstractBinding *remove = binding; + remove->setAddedToObject(false); binding = remove->nextBinding(); if (lastBinding == 0) m_bindings = remove->nextBinding(); else lastBinding->setNextBinding(remove->nextBinding()); - - remove->setAddedToObject(false); - remove->setNextBinding(0); - remove->destroy(); } else { lastBinding = binding; binding = binding->nextBinding(); @@ -143,24 +102,4 @@ void QQmlValueTypeProxyBinding::removeBindings(quint32 mask) } } -int QQmlValueTypeProxyBinding::propertyIndex(const QQmlAbstractBinding *This) -{ - return static_cast<const QQmlValueTypeProxyBinding *>(This)->m_index; -} - -QObject *QQmlValueTypeProxyBinding::object(const QQmlAbstractBinding *This) -{ - return static_cast<const QQmlValueTypeProxyBinding *>(This)->m_object; -} - -int QQmlValueTypeProxyBinding::propertyIndex() const -{ - return m_index; -} - -QObject *QQmlValueTypeProxyBinding::object() const -{ - return m_object; -} - QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlvaluetypeproxybinding_p.h b/src/qml/qml/qqmlvaluetypeproxybinding_p.h index 873fbb4af1..4afadfc17d 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding_p.h +++ b/src/qml/qml/qqmlvaluetypeproxybinding_p.h @@ -54,30 +54,18 @@ class QQmlValueTypeProxyBinding : public QQmlAbstractBinding public: QQmlValueTypeProxyBinding(QObject *o, int coreIndex); - int propertyIndex() const; - QObject *object() const; - - QQmlAbstractBinding *binding(int propertyIndex); - + QQmlAbstractBinding *binding(int targetPropertyIndex); void removeBindings(quint32 mask); - // "Inherited" from QQmlAbstractBinding - static void setEnabled(QQmlAbstractBinding *, bool, QQmlPropertyPrivate::WriteFlags); - static void update(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags); - static int propertyIndex(const QQmlAbstractBinding *); - static QObject *object(const QQmlAbstractBinding *); + virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags); + virtual bool isValueTypeProxy() const; protected: ~QQmlValueTypeProxyBinding(); private: - void recursiveEnable(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags); - void recursiveDisable(QQmlAbstractBinding *); - friend class QQmlAbstractBinding; - QObject *m_object; - int m_index; - QQmlAbstractBinding *m_bindings; + Ptr m_bindings; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index e87d9ede77..8ddf91ef3c 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -43,7 +43,6 @@ #include <private/qv4engine_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4variantobject_p.h> -#include <private/qv4qmlextensions_p.h> #include <private/qv4alloca_p.h> QT_BEGIN_NAMESPACE @@ -56,7 +55,7 @@ namespace Heap { struct QQmlValueTypeReference : QQmlValueTypeWrapper { - QQmlValueTypeReference(ExecutionEngine *engine); + QQmlValueTypeReference() {} QPointer<QObject> object; int property; }; @@ -78,11 +77,6 @@ DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeReference); using namespace QV4; -Heap::QQmlValueTypeWrapper::QQmlValueTypeWrapper(ExecutionEngine *engine) - : Heap::Object(engine) -{ -} - Heap::QQmlValueTypeWrapper::~QQmlValueTypeWrapper() { if (gadgetPtr) { @@ -108,11 +102,6 @@ QVariant Heap::QQmlValueTypeWrapper::toVariant() const } -Heap::QQmlValueTypeReference::QQmlValueTypeReference(ExecutionEngine *engine) - : Heap::QQmlValueTypeWrapper(engine) -{ -} - bool QQmlValueTypeReference::readReferenceValue() const { if (!d()->object) @@ -165,13 +154,13 @@ bool QQmlValueTypeReference::readReferenceValue() const void QQmlValueTypeWrapper::initProto(ExecutionEngine *v4) { - if (v4->qmlExtensions()->valueTypeWrapperPrototype) + if (v4->valueTypeWrapperPrototype()->d()) return; Scope scope(v4); ScopedObject o(scope, v4->newObject()); - o->defineDefaultProperty(v4->id_toString, method_toString, 1); - v4->qmlExtensions()->valueTypeWrapperPrototype = o->d(); + o->defineDefaultProperty(v4->id_toString(), method_toString, 1); + v4->jsObjects[QV4::ExecutionEngine::ValueTypeProto] = o->d(); } ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *object, int property, const QMetaObject *metaObject, int typeId) @@ -179,10 +168,9 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *obj Scope scope(engine); initProto(engine); - Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->alloc<QQmlValueTypeReference>(engine)); - ScopedObject proto(scope, engine->qmlExtensions()->valueTypeWrapperPrototype); - r->setPrototype(proto); - r->d()->object = object; r->d()->property = property; + Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->allocObject<QQmlValueTypeReference>()); + r->d()->object = object; + r->d()->property = property; r->d()->propertyCache = QJSEnginePrivate::get(engine)->cache(metaObject); r->d()->valueType = QQmlValueTypeFactory::valueType(typeId); r->d()->gadgetPtr = 0; @@ -194,9 +182,7 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, const QVaria Scope scope(engine); initProto(engine); - Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->alloc<QQmlValueTypeWrapper>(engine)); - ScopedObject proto(scope, engine->qmlExtensions()->valueTypeWrapperPrototype); - r->setPrototype(proto); + Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocObject<QQmlValueTypeWrapper>()); r->d()->propertyCache = QJSEnginePrivate::get(engine)->cache(metaObject); r->d()->valueType = QQmlValueTypeFactory::valueType(typeId); r->d()->gadgetPtr = 0; @@ -292,7 +278,7 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const ReturnedValue QQmlValueTypeWrapper::method_toString(CallContext *ctx) { - Object *o = ctx->thisObject().asObject(); + Object *o = ctx->thisObject().as<Object>(); if (!o) return ctx->engine()->throwTypeError(); QQmlValueTypeWrapper *w = o->as<QQmlValueTypeWrapper>(); @@ -327,14 +313,14 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(CallContext *ctx) return Encode(ctx->engine()->newString(result)); } -ReturnedValue QQmlValueTypeWrapper::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *hasProperty) { Q_ASSERT(m->as<QQmlValueTypeWrapper>()); - QQmlValueTypeWrapper *r = static_cast<QQmlValueTypeWrapper *>(m); + const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); QV4::ExecutionEngine *v4 = r->engine(); // Note: readReferenceValue() can change the reference->type. - if (QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) { + if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) { if (!reference->readReferenceValue()) return Primitive::undefinedValue().asReturnedValue(); } @@ -346,12 +332,9 @@ ReturnedValue QQmlValueTypeWrapper::get(Managed *m, String *name, bool *hasPrope if (hasProperty) *hasProperty = true; - if (result->isFunction()) { + if (result->isFunction()) // calling a Q_INVOKABLE function of a value type - Scope scope(v4); - ScopedContext c(scope, v4->rootContext()); - return QV4::QObjectMethod::create(c, r, result->coreIndex); - } + return QV4::QObjectMethod::create(v4->rootContext(), r, result->coreIndex); #define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \ if (result->propType == metatype) { \ @@ -410,45 +393,41 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QMetaProperty property = metaObject->property(pd->coreIndex); Q_ASSERT(property.isValid()); - QQmlBinding *newBinding = 0; - - QV4::ScopedFunctionObject f(scope, value); - if (reference && f) { - if (!f->isBinding()) { - // assigning a JS function to a non-var-property is not allowed. - QString error = QStringLiteral("Cannot assign JavaScript function to value-type property"); - ScopedString e(scope, v4->newString(error)); - v4->throwError(e); - return; - } + if (reference) { + QV4::ScopedFunctionObject f(scope, value); + if (f) { + if (!f->isBinding()) { + // assigning a JS function to a non-var-property is not allowed. + QString error = QStringLiteral("Cannot assign JavaScript function to value-type property"); + ScopedString e(scope, v4->newString(error)); + v4->throwError(e); + return; + } - QQmlContextData *context = QmlContextWrapper::callingContext(v4); + QQmlContextData *context = v4->callingQmlContext(); - QQmlPropertyData cacheData; - cacheData.setFlags(QQmlPropertyData::IsWritable | - QQmlPropertyData::IsValueTypeVirtual); - cacheData.propType = writeBackPropertyType; - cacheData.coreIndex = reference->d()->property; - cacheData.valueTypeFlags = 0; - cacheData.valueTypeCoreIndex = pd->coreIndex; - cacheData.valueTypePropType = property.userType(); + QQmlPropertyData cacheData; + cacheData.setFlags(QQmlPropertyData::IsWritable | + QQmlPropertyData::IsValueTypeVirtual); + cacheData.propType = writeBackPropertyType; + cacheData.coreIndex = reference->d()->property; + cacheData.valueTypeFlags = 0; + cacheData.valueTypeCoreIndex = pd->coreIndex; + cacheData.valueTypePropType = property.userType(); - QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); - bindingFunction->initBindingLocation(); + QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); + bindingFunction->initBindingLocation(); - newBinding = new QQmlBinding(value, reference->d()->object, context); - newBinding->setTarget(reference->d()->object, cacheData, context); - } + QQmlBinding *newBinding = new QQmlBinding(value, reference->d()->object, context); + newBinding->setTarget(reference->d()->object, cacheData); + QQmlPropertyPrivate::setBinding(newBinding); + return; + } else { + QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyData::encodeValueTypePropertyIndex(reference->d()->property, pd->coreIndex)); - if (reference) { - QQmlAbstractBinding *oldBinding = - QQmlPropertyPrivate::setBinding(reference->d()->object, reference->d()->property, pd->coreIndex, newBinding); - if (oldBinding) - oldBinding->destroy(); + } } - if (newBinding) - return; QVariant v = v4->toVariant(value, property.userType()); diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index cad48e661c..156b4c85d8 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -48,7 +48,7 @@ #include <QtCore/qglobal.h> #include <private/qtqmlglobal_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4object_p.h> QT_BEGIN_NAMESPACE @@ -61,7 +61,7 @@ namespace QV4 { namespace Heap { struct QQmlValueTypeWrapper : Object { - QQmlValueTypeWrapper(ExecutionEngine *engine); + QQmlValueTypeWrapper() {} ~QQmlValueTypeWrapper(); QQmlRefPointer<QQmlPropertyCache> propertyCache; mutable void *gadgetPtr; @@ -76,6 +76,7 @@ struct QQmlValueTypeWrapper : Object { struct Q_QML_EXPORT QQmlValueTypeWrapper : Object { V4_OBJECT2(QQmlValueTypeWrapper, Object) + V4_PROTOTYPE(valueTypeWrapperPrototype) static void destroy(Heap::Base *b); public: @@ -89,7 +90,7 @@ public: int typeId() const; bool write(QObject *target, int propertyIndex) const; - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static void put(Managed *m, String *name, const Value &value); static bool isEqualTo(Managed *m, Managed *other); static PropertyAttributes query(const Managed *, String *name); diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 97fc382c33..e85b3dc82c 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 BasysKom GmbH. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -51,8 +52,8 @@ QT_BEGIN_NAMESPACE -QQmlVMEVariantQObjectPtr::QQmlVMEVariantQObjectPtr(bool isVar) - : QQmlGuard<QObject>(0), m_target(0), m_isVar(isVar), m_index(-1) +QQmlVMEVariantQObjectPtr::QQmlVMEVariantQObjectPtr() + : QQmlGuard<QObject>(0), m_target(0), m_index(-1) { } @@ -62,20 +63,19 @@ QQmlVMEVariantQObjectPtr::~QQmlVMEVariantQObjectPtr() void QQmlVMEVariantQObjectPtr::objectDestroyed(QObject *) { - if (m_target && m_index >= 0) { - if (m_isVar && m_target->varPropertiesInitialized && !m_target->varProperties.isUndefined()) { - // Set the var property to NULL - QV4::ExecutionEngine *v4 = m_target->varProperties.engine(); - if (v4) { - QV4::Scope scope(v4); - QV4::ScopedArrayObject a(scope, m_target->varProperties.value()); - if (a) - a->putIndexed(m_index - m_target->firstVarPropertyIndex, QV4::ScopedValue(scope, QV4::Primitive::nullValue())); - } + if (!m_target || QQmlData::wasDeleted(m_target->object)) + return; + + if (m_index >= 0) { + QV4::ExecutionEngine *v4 = m_target->properties.engine(); + if (v4) { + QV4::Scope scope(v4); + QV4::Scoped<QV4::MemberData> sp(scope, m_target->properties.value()); + if (sp) + *(sp->data() + m_index) = QV4::Primitive::nullValue(); } - if (!QQmlData::wasDeleted(m_target->object)) - m_target->activate(m_target->object, m_target->methodOffset() + m_index, 0); + m_target->activate(m_target->object, m_target->methodOffset() + m_index, 0); } } @@ -86,620 +86,509 @@ void QQmlVMEVariantQObjectPtr::setGuardedValue(QObject *obj, QQmlVMEMetaObject * setObject(obj); } -class QQmlVMEVariant -{ -public: - inline QQmlVMEVariant(); - inline ~QQmlVMEVariant(); - - inline const void *dataPtr() const; - inline void *dataPtr(); - inline int dataType() const; - inline size_t dataSize() const; - - inline QObject *asQObject(); - inline const QVariant &asQVariant(); - inline int asInt(); - inline bool asBool(); - inline double asDouble(); - inline const QString &asQString(); - inline const QUrl &asQUrl(); - inline const QTime &asQTime(); - inline const QDate &asQDate(); - inline const QDateTime &asQDateTime(); - inline const QRectF &asQRectF(); - inline const QPointF &asQPointF(); - inline const QSizeF &asQSizeF(); - inline const QJSValue &asQJSValue(); - - inline void setValue(QObject *v, QQmlVMEMetaObject *target, int index); - inline void setValue(const QVariant &); - inline void setValue(int); - inline void setValue(bool); - inline void setValue(double); - inline void setValue(const QString &); - inline void setValue(const QUrl &); - inline void setValue(const QTime &); - inline void setValue(const QDate &); - inline void setValue(const QDateTime &); - inline void setValue(const QRectF &); - inline void setValue(const QPointF &); - inline void setValue(const QSizeF &); - inline void setValue(const QJSValue &); - - inline void setDataType(int t); - - inline void ensureValueType(int); - -private: - int type; - void *data[8]; // Large enough to hold all types - - inline void cleanup(); -}; - class QQmlVMEMetaObjectEndpoint : public QQmlNotifierEndpoint { public: QQmlVMEMetaObjectEndpoint(); - static void vmecallback(QQmlNotifierEndpoint *, void **); void tryConnect(); QFlagPointer<QQmlVMEMetaObject> metaObject; }; - -QQmlVMEVariant::QQmlVMEVariant() -: type(QVariant::Invalid) -{ -} - -QQmlVMEVariant::~QQmlVMEVariant() -{ - cleanup(); -} - -void QQmlVMEVariant::cleanup() -{ - if (type == QVariant::Invalid) { - } else if (type == QMetaType::Int || - type == QMetaType::Bool || - type == QMetaType::Double) { - type = QVariant::Invalid; - } else if (type == QMetaType::QObjectStar) { - ((QQmlVMEVariantQObjectPtr*)dataPtr())->~QQmlVMEVariantQObjectPtr(); - type = QVariant::Invalid; - } else if (type == QMetaType::QString) { - ((QString *)dataPtr())->~QString(); - type = QVariant::Invalid; - } else if (type == QMetaType::QUrl) { - ((QUrl *)dataPtr())->~QUrl(); - type = QVariant::Invalid; - } else if (type == QMetaType::QTime) { - ((QTime *)dataPtr())->~QTime(); - type = QVariant::Invalid; - } else if (type == QMetaType::QDate) { - ((QDate *)dataPtr())->~QDate(); - type = QVariant::Invalid; - } else if (type == QMetaType::QDateTime) { - ((QDateTime *)dataPtr())->~QDateTime(); - type = QVariant::Invalid; - } else if (type == QMetaType::QRectF) { - ((QRectF *)dataPtr())->~QRectF(); - type = QVariant::Invalid; - } else if (type == QMetaType::QPointF) { - ((QPointF *)dataPtr())->~QPointF(); - type = QVariant::Invalid; - } else if (type == QMetaType::QSizeF) { - ((QSizeF *)dataPtr())->~QSizeF(); - type = QVariant::Invalid; - } else if (type == qMetaTypeId<QVariant>()) { - ((QVariant *)dataPtr())->~QVariant(); - type = QVariant::Invalid; - } else if (type == qMetaTypeId<QJSValue>()) { - ((QJSValue *)dataPtr())->~QJSValue(); - type = QVariant::Invalid; - } else { - if (QQml_valueTypeProvider()->destroyValueType(type, dataPtr(), dataSize())) { - type = QVariant::Invalid; - } - } -} - -int QQmlVMEVariant::dataType() const +QQmlVMEMetaObjectEndpoint::QQmlVMEMetaObjectEndpoint() + : QQmlNotifierEndpoint(QQmlNotifierEndpoint::QQmlVMEMetaObjectEndpoint) { - return type; } -const void *QQmlVMEVariant::dataPtr() const +void QQmlVMEMetaObjectEndpoint_callback(QQmlNotifierEndpoint *e, void **) { - return &data; + QQmlVMEMetaObjectEndpoint *vmee = static_cast<QQmlVMEMetaObjectEndpoint*>(e); + vmee->tryConnect(); } -void *QQmlVMEVariant::dataPtr() +void QQmlVMEMetaObjectEndpoint::tryConnect() { - return &data; -} + int aliasId = this - metaObject->aliasEndpoints; -size_t QQmlVMEVariant::dataSize() const -{ - return sizeof(data); -} + if (metaObject.flag()) { + // This is actually notify + int sigIdx = metaObject->methodOffset() + aliasId + metaObject->metaData->propertyCount; + metaObject->activate(metaObject->object, sigIdx, 0); + } else { + QQmlVMEMetaData::AliasData *d = metaObject->metaData->aliasData() + aliasId; + if (!d->isObjectAlias()) { + QQmlContextData *ctxt = metaObject->ctxt; + QObject *target = ctxt->idValues[d->contextIdx].data(); + if (!target) + return; -QObject *QQmlVMEVariant::asQObject() -{ - if (type != QMetaType::QObjectStar) - setValue((QObject *)0, 0, -1); + if (d->notifySignal != -1) + connect(target, d->notifySignal, ctxt->engine); + } - return *(QQmlGuard<QObject> *)(dataPtr()); + metaObject.setFlag(); + } } -const QVariant &QQmlVMEVariant::asQVariant() -{ - if (type != QMetaType::QVariant) - setValue(QVariant()); - return *(QVariant *)(dataPtr()); -} - -int QQmlVMEVariant::asInt() +QQmlInterceptorMetaObject::QQmlInterceptorMetaObject(QObject *obj, QQmlPropertyCache *cache) + : object(obj), + cache(cache), + interceptors(0), + hasAssignedMetaObjectData(false) { - if (type != QMetaType::Int) - setValue(int(0)); + QObjectPrivate *op = QObjectPrivate::get(obj); + + if (op->metaObject) { + parent = op->metaObject; + // Use the extra flag in QBiPointer to know if we can safely cast parent.asT1() to QQmlVMEMetaObject* + parent.setFlagValue(QQmlData::get(obj)->hasVMEMetaObject); + } else { + parent = obj->metaObject(); + } - return *(int *)(dataPtr()); + op->metaObject = this; + QQmlData::get(obj)->hasInterceptorMetaObject = true; } -bool QQmlVMEVariant::asBool() +QQmlInterceptorMetaObject::~QQmlInterceptorMetaObject() { - if (type != QMetaType::Bool) - setValue(bool(false)); - return *(bool *)(dataPtr()); } -double QQmlVMEVariant::asDouble() +void QQmlInterceptorMetaObject::registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor) { - if (type != QMetaType::Double) - setValue(double(0)); - - return *(double *)(dataPtr()); + interceptor->m_coreIndex = index; + interceptor->m_valueTypeCoreIndex = valueIndex; + interceptor->m_next = interceptors; + interceptors = interceptor; } -const QString &QQmlVMEVariant::asQString() +int QQmlInterceptorMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void **a) { - if (type != QMetaType::QString) - setValue(QString()); + Q_ASSERT(o == object); + Q_UNUSED(o); - return *(QString *)(dataPtr()); + if (intercept(c, id, a)) + return -1; + return object->qt_metacall(c, id, a); } -const QUrl &QQmlVMEVariant::asQUrl() +bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a) { - if (type != QMetaType::QUrl) - setValue(QUrl()); + if (c == QMetaObject::WriteProperty && interceptors && + !(*reinterpret_cast<int*>(a[3]) & QQmlPropertyPrivate::BypassInterceptor)) { - return *(QUrl *)(dataPtr()); -} + for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) { + if (vi->m_coreIndex != id) + continue; -const QTime &QQmlVMEVariant::asQTime() -{ - if (type != QMetaType::QTime) - setValue(QTime()); + int valueIndex = vi->m_valueTypeCoreIndex; + int type = QQmlData::get(object)->propertyCache->property(id)->propType; - return *(QTime *)(dataPtr()); -} + if (type != QVariant::Invalid) { + if (valueIndex != -1) { + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type); + Q_ASSERT(valueType); -const QDate &QQmlVMEVariant::asQDate() -{ - if (type != QMetaType::QDate) - setValue(QDate()); + // + // 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. + // - return *(QDate *)(dataPtr()); -} + QMetaProperty valueProp = valueType->metaObject()->property(valueIndex); + QVariant newValue(type, a[0]); -const QDateTime &QQmlVMEVariant::asQDateTime() -{ - if (type != QMetaType::QDateTime) - setValue(QDateTime()); + valueType->read(object, id); + QVariant prevComponentValue = valueProp.read(valueType); - return *(QDateTime *)(dataPtr()); -} + valueType->setValue(newValue); + QVariant newComponentValue = valueProp.read(valueType); -const QRectF &QQmlVMEVariant::asQRectF() -{ - if (type != QMetaType::QRectF) - setValue(QRectF()); + // 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); - return *(QRectF *)(dataPtr()); + vi->write(newComponentValue); + updated = true; + } + + if (updated) + return true; + } else { + vi->write(QVariant(type, a[0])); + return true; + } + } + } + } + return false; } -const QSizeF &QQmlVMEVariant::asQSizeF() + +QAbstractDynamicMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObject *o) { - if (type != QMetaType::QSizeF) - setValue(QSizeF()); + if (!hasAssignedMetaObjectData) { + *static_cast<QMetaObject *>(this) = *cache->createMetaObject(); - return *(QSizeF *)(dataPtr()); -} + if (parent.isT1()) + this->d.superdata = parent.asT1()->toDynamicMetaObject(o); + else + this->d.superdata = parent.asT2(); -const QPointF &QQmlVMEVariant::asQPointF() -{ - if (type != QMetaType::QPointF) - setValue(QPointF()); + hasAssignedMetaObjectData = true; + } - return *(QPointF *)(dataPtr()); + return this; } -const QJSValue &QQmlVMEVariant::asQJSValue() +QQmlVMEMetaObject::QQmlVMEMetaObject(QObject *obj, + QQmlPropertyCache *cache, + const QQmlVMEMetaData *meta) + : QQmlInterceptorMetaObject(obj, cache), + ctxt(QQmlData::get(obj, true)->outerContext), metaData(meta), + aliasEndpoints(0), + methods(0) { - if (type != qMetaTypeId<QJSValue>()) - setValue(QJSValue()); + cache->addref(); - return *(QJSValue *)(dataPtr()); -} + QQmlData::get(obj)->hasVMEMetaObject = true; -void QQmlVMEVariant::setValue(QObject *v, QQmlVMEMetaObject *target, int index) -{ - if (type != QMetaType::QObjectStar) { - cleanup(); - type = QMetaType::QObjectStar; - new (dataPtr()) QQmlVMEVariantQObjectPtr(false); - } - reinterpret_cast<QQmlVMEVariantQObjectPtr*>(dataPtr())->setGuardedValue(v, target, index); -} + int qobject_type = qMetaTypeId<QObject*>(); + int variant_type = qMetaTypeId<QVariant>(); + // Need JS wrapper to ensure properties are marked. + // ### FIXME: I hope that this can be removed once we have the proper scope chain + // set up and the JS wrappers always exist. + bool needsJSWrapper = (metaData->propertyCount > 0); -void QQmlVMEVariant::setValue(const QVariant &v) -{ - if (type != qMetaTypeId<QVariant>()) { - cleanup(); - type = qMetaTypeId<QVariant>(); - new (dataPtr()) QVariant(v); - } else { - *(QVariant *)(dataPtr()) = v; + // ### Optimize + for (int ii = 0; ii < metaData->propertyCount; ++ii) { + int t = (metaData->propertyData() + ii)->propertyType; + if (t == qobject_type || t == variant_type) { + needsJSWrapper = true; + break; + } } -} -void QQmlVMEVariant::setValue(int v) -{ - if (type != QMetaType::Int) { - cleanup(); - type = QMetaType::Int; - } - *(int *)(dataPtr()) = v; + if (needsJSWrapper) + ensureQObjectWrapper(); } -void QQmlVMEVariant::setValue(bool v) +QQmlVMEMetaObject::~QQmlVMEMetaObject() { - if (type != QMetaType::Bool) { - cleanup(); - type = QMetaType::Bool; - } - *(bool *)(dataPtr()) = v; + if (parent.isT1()) parent.asT1()->objectDestroyed(object); + delete [] aliasEndpoints; + delete [] methods; + + qDeleteAll(varObjectGuards); + + cache->release(); } -void QQmlVMEVariant::setValue(double v) +QV4::MemberData *QQmlVMEMetaObject::propertiesAsMemberData() { - if (type != QMetaType::Double) { - cleanup(); - type = QMetaType::Double; + if (properties.isUndefined()) { + if (properties.valueRef()) + // in some situations, the QObject wrapper (and associated data, + // such as the varProperties array) will have been cleaned up, but the + // QObject ptr will not yet have been deleted (eg, waiting on deleteLater). + // In this situation, return 0. + return 0; + allocateProperties(); } - *(double *)(dataPtr()) = v; + + return static_cast<QV4::MemberData*>(properties.asManaged()); } -void QQmlVMEVariant::setValue(const QString &v) +void QQmlVMEMetaObject::writeProperty(int id, int v) { - if (type != QMetaType::QString) { - cleanup(); - type = QMetaType::QString; - new (dataPtr()) QString(v); - } else { - *(QString *)(dataPtr()) = v; - } + QV4::MemberData *md = propertiesAsMemberData(); + if (md) + *(md->data() + id) = QV4::Primitive::fromInt32(v); } -void QQmlVMEVariant::setValue(const QUrl &v) +void QQmlVMEMetaObject::writeProperty(int id, bool v) { - if (type != QMetaType::QUrl) { - cleanup(); - type = QMetaType::QUrl; - new (dataPtr()) QUrl(v); - } else { - *(QUrl *)(dataPtr()) = v; - } + QV4::MemberData *md = propertiesAsMemberData(); + if (md) + *(md->data() + id) = QV4::Primitive::fromBoolean(v); } -void QQmlVMEVariant::setValue(const QTime &v) +void QQmlVMEMetaObject::writeProperty(int id, double v) { - if (type != QMetaType::QTime) { - cleanup(); - type = QMetaType::QTime; - new (dataPtr()) QTime(v); - } else { - *(QTime *)(dataPtr()) = v; - } + QV4::MemberData *md = propertiesAsMemberData(); + if (md) + *(md->data() + id) = QV4::Primitive::fromDouble(v); } -void QQmlVMEVariant::setValue(const QDate &v) +void QQmlVMEMetaObject::writeProperty(int id, const QString& v) { - if (type != QMetaType::QDate) { - cleanup(); - type = QMetaType::QDate; - new (dataPtr()) QDate(v); - } else { - *(QDate *)(dataPtr()) = v; - } + QV4::MemberData *md = propertiesAsMemberData(); + if (md) + *(md->data() + id) = cache->engine->newString(v); } -void QQmlVMEVariant::setValue(const QDateTime &v) +void QQmlVMEMetaObject::writeProperty(int id, const QUrl& v) { - if (type != QMetaType::QDateTime) { - cleanup(); - type = QMetaType::QDateTime; - new (dataPtr()) QDateTime(v); - } else { - *(QDateTime *)(dataPtr()) = v; - } + QV4::MemberData *md = propertiesAsMemberData(); + if (md) + *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); } -void QQmlVMEVariant::setValue(const QRectF &v) +void QQmlVMEMetaObject::writeProperty(int id, const QDate& v) { - if (type != QMetaType::QRectF) { - cleanup(); - type = QMetaType::QRectF; - new (dataPtr()) QRectF(v); - } else { - *(QRectF *)(dataPtr()) = v; - } + QV4::MemberData *md = propertiesAsMemberData(); + if (md) + *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); } -void QQmlVMEVariant::setValue(const QPointF &v) +void QQmlVMEMetaObject::writeProperty(int id, const QDateTime& v) { - if (type != QMetaType::QPointF) { - cleanup(); - type = QMetaType::QPointF; - new (dataPtr()) QPointF(v); - } else { - *(QPointF *)(dataPtr()) = v; - } + QV4::MemberData *md = propertiesAsMemberData(); + if (md) + *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); } -void QQmlVMEVariant::setValue(const QSizeF &v) +void QQmlVMEMetaObject::writeProperty(int id, const QPointF& v) { - if (type != QMetaType::QSizeF) { - cleanup(); - type = QMetaType::QSizeF; - new (dataPtr()) QSizeF(v); - } else { - *(QSizeF *)(dataPtr()) = v; - } + QV4::MemberData *md = propertiesAsMemberData(); + if (md) + *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); } -void QQmlVMEVariant::setValue(const QJSValue &v) +void QQmlVMEMetaObject::writeProperty(int id, const QSizeF& v) { - if (type != qMetaTypeId<QJSValue>()) { - cleanup(); - type = qMetaTypeId<QJSValue>(); - new (dataPtr()) QJSValue(v); - } else { - *(QJSValue *)(dataPtr()) = v; - } + QV4::MemberData *md = propertiesAsMemberData(); + if (md) + *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); } -void QQmlVMEVariant::setDataType(int t) +void QQmlVMEMetaObject::writeProperty(int id, const QRectF& v) { - type = t; + QV4::MemberData *md = propertiesAsMemberData(); + if (md) + *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); } -void QQmlVMEVariant::ensureValueType(int t) +void QQmlVMEMetaObject::writeProperty(int id, QObject* v) { - if (type != t) { - cleanup(); - type = t; - QQml_valueTypeProvider()->initValueType(t, dataPtr(), dataSize()); + QV4::MemberData *md = propertiesAsMemberData(); + if (md) + *(md->data() + id) = QV4::QObjectWrapper::wrap(cache->engine, v); + + QQmlVMEVariantQObjectPtr *guard = getQObjectGuardForProperty(id); + if (v && !guard) { + guard = new QQmlVMEVariantQObjectPtr(); + varObjectGuards.append(guard); } + if (guard) + guard->setGuardedValue(v, this, id); } -QQmlVMEMetaObjectEndpoint::QQmlVMEMetaObjectEndpoint() +int QQmlVMEMetaObject::readPropertyAsInt(int id) { - setCallback(QQmlNotifierEndpoint::QQmlVMEMetaObjectEndpoint); + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) + return 0; + + QV4::Scope scope(cache->engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + if (!sv->isInt32()) + return 0; + return sv->integerValue(); } -void QQmlVMEMetaObjectEndpoint_callback(QQmlNotifierEndpoint *e, void **) +bool QQmlVMEMetaObject::readPropertyAsBool(int id) { - QQmlVMEMetaObjectEndpoint *vmee = static_cast<QQmlVMEMetaObjectEndpoint*>(e); - vmee->tryConnect(); + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) + return false; + + QV4::Scope scope(cache->engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + if (!sv->isBoolean()) + return false; + return sv->booleanValue(); } -void QQmlVMEMetaObjectEndpoint::tryConnect() +double QQmlVMEMetaObject::readPropertyAsDouble(int id) { - int aliasId = this - metaObject->aliasEndpoints; + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) + return 0.0; - if (metaObject.flag()) { - // This is actually notify - int sigIdx = metaObject->methodOffset() + aliasId + metaObject->metaData->propertyCount; - metaObject->activate(metaObject->object, sigIdx, 0); - } else { - QQmlVMEMetaData::AliasData *d = metaObject->metaData->aliasData() + aliasId; - if (!d->isObjectAlias()) { - QQmlContextData *ctxt = metaObject->ctxt; - QObject *target = ctxt->idValues[d->contextIdx].data(); - if (!target) - return; + QV4::Scope scope(cache->engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + if (!sv->isDouble()) + return 0.0; + return sv->doubleValue(); +} - if (d->notifySignal != -1) - connect(target, d->notifySignal, ctxt->engine); - } +QString QQmlVMEMetaObject::readPropertyAsString(int id) +{ + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) + return QString(); - metaObject.setFlag(); - } + QV4::Scope scope(cache->engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + if (!sv->isString()) + return QString(); + return sv->stringValue()->toQString(); } -QAbstractDynamicMetaObject *QQmlVMEMetaObject::toDynamicMetaObject(QObject *o) +QUrl QQmlVMEMetaObject::readPropertyAsUrl(int id) { - if (!hasAssignedMetaObjectData) { - *static_cast<QMetaObject *>(this) = *cache->createMetaObject(); + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) + return QUrl(); - if (parent.isT1()) - this->d.superdata = parent.asT1()->toDynamicMetaObject(o); - else - this->d.superdata = parent.asT2(); + QV4::Scope scope(cache->engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); + if (!v || v->d()->data.type() != QVariant::Url) + return QUrl(); + return v->d()->data.value<QUrl>(); +} - hasAssignedMetaObjectData = true; - } +QDate QQmlVMEMetaObject::readPropertyAsDate(int id) +{ + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) + return QDate(); - return this; + QV4::Scope scope(cache->engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); + if (!v || v->d()->data.type() != QVariant::Date) + return QDate(); + return v->d()->data.value<QDate>(); } -QQmlVMEMetaObject::QQmlVMEMetaObject(QObject *obj, - QQmlPropertyCache *cache, - const QQmlVMEMetaData *meta, QV4::ExecutionContext *qmlBindingContext, QQmlCompiledData *compiledData) -: object(obj), - ctxt(QQmlData::get(obj, true)->outerContext), cache(cache), metaData(meta), - hasAssignedMetaObjectData(false), data(0), aliasEndpoints(0), firstVarPropertyIndex(-1), - varPropertiesInitialized(false), interceptors(0), v8methods(0) +QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id) { - QObjectPrivate *op = QObjectPrivate::get(obj); - - if (op->metaObject) { - parent = op->metaObject; - // Use the extra flag in QBiPointer to know if we can safely cast parent.asT1() to QQmlVMEMetaObject* - parent.setFlagValue(QQmlData::get(obj)->hasVMEMetaObject); - } else - parent = obj->metaObject(); + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) + return QDateTime(); - op->metaObject = this; - QQmlData::get(obj)->hasVMEMetaObject = true; + QV4::Scope scope(cache->engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); + if (!v || v->d()->data.type() != QVariant::DateTime) + return QDateTime(); + return v->d()->data.value<QDateTime>(); +} - data = new QQmlVMEVariant[metaData->propertyCount - metaData->varPropertyCount]; +QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id) +{ + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) + return QSizeF(); - aConnected.resize(metaData->aliasCount); - int list_type = qMetaTypeId<QQmlListProperty<QObject> >(); - int qobject_type = qMetaTypeId<QObject*>(); - int variant_type = qMetaTypeId<QVariant>(); - // Need JS wrapper to ensure variant and var properties are marked. - // ### FIXME: I hope that this can be removed once we have the proper scope chain - // set up and the JS wrappers always exist. - bool needsJSWrapper = (metaData->varPropertyCount > 0); + QV4::Scope scope(cache->engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); + if (!v || v->d()->data.type() != QVariant::SizeF) + return QSizeF(); + return v->d()->data.value<QSizeF>(); +} - // ### Optimize - for (int ii = 0; ii < metaData->propertyCount - metaData->varPropertyCount; ++ii) { - int t = (metaData->propertyData() + ii)->propertyType; - if (t == list_type) { - listProperties.append(List(methodOffset() + ii, this)); - data[ii].setValue(listProperties.count() - 1); - } else if (!needsJSWrapper && (t == qobject_type || t == variant_type)) { - needsJSWrapper = true; - } - } +QPointF QQmlVMEMetaObject::readPropertyAsPointF(int id) +{ + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) + return QPointF(); - firstVarPropertyIndex = metaData->propertyCount - metaData->varPropertyCount; + QV4::Scope scope(cache->engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); + if (!v || v->d()->data.type() != QVariant::PointF) + return QPointF(); + return v->d()->data.value<QPointF>(); +} - if (needsJSWrapper) - ensureQObjectWrapper(); +QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id) +{ + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) + return 0; - if (qmlBindingContext && metaData->methodCount) { - v8methods = new QV4::PersistentValue[metaData->methodCount]; + QV4::Scope scope(cache->engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + const QV4::QObjectWrapper *wrapper = sv->as<QV4::QObjectWrapper>(); + if (!wrapper) + return 0; + return wrapper->object(); +} - QV4::CompiledData::CompilationUnit *compilationUnit = compiledData->compilationUnit; - QV4::Scope scope(QQmlEnginePrivate::get(ctxt->engine)->v4engine()); - QV4::ScopedObject o(scope); - for (int index = 0; index < metaData->methodCount; ++index) { - QQmlVMEMetaData::MethodData *data = metaData->methodData() + index; +QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id) +{ + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) + return 0; - QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[data->runtimeFunctionIndex]; - o = QV4::FunctionObject::createScriptFunction(qmlBindingContext, runtimeFunction); - v8methods[index].set(qmlBindingContext->engine(), o); - } + QV4::Scope scope(cache->engine); + QV4::Scoped<QV4::VariantObject> v(scope, *(md->data() + id)); + if (!v || (int)v->d()->data.userType() != qMetaTypeId<QList<QObject *> >()) { + QVariant variant(qVariantFromValue(QList<QObject*>())); + v = cache->engine->newVariantObject(variant); + *(md->data() + id) = v; } + return static_cast<QList<QObject *> *>(v->d()->data.data()); } -QQmlVMEMetaObject::~QQmlVMEMetaObject() +QRectF QQmlVMEMetaObject::readPropertyAsRectF(int id) { - if (parent.isT1()) parent.asT1()->objectDestroyed(object); - delete [] data; - delete [] aliasEndpoints; - delete [] v8methods; + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) + return QRectF(); - qDeleteAll(varObjectGuards); + QV4::Scope scope(cache->engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); + if (!v || v->d()->data.type() != QVariant::RectF) + return QRectF(); + return v->d()->data.value<QRectF>(); } -int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) +int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void **a) { - int id = _id; - if (c == QMetaObject::WriteProperty && interceptors && - !(*reinterpret_cast<int*>(a[3]) & QQmlPropertyPrivate::BypassInterceptor)) { + Q_ASSERT(o == object); + Q_UNUSED(o); - 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); + int id = _id; - // 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); + if (intercept(c, _id, a)) + return -1; - 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(); @@ -708,8 +597,7 @@ int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) int t = (metaData->propertyData() + id)->propertyType; bool needActivate = false; - if (id >= firstVarPropertyIndex) { - Q_ASSERT(t == QMetaType::QVariant); + if (t == QQmlVMEMetaData::VarPropertyType) { // 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(); @@ -729,110 +617,128 @@ int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) if (c == QMetaObject::ReadProperty) { switch(t) { case QVariant::Int: - *reinterpret_cast<int *>(a[0]) = data[id].asInt(); + *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id); break; case QVariant::Bool: - *reinterpret_cast<bool *>(a[0]) = data[id].asBool(); + *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id); break; case QVariant::Double: - *reinterpret_cast<double *>(a[0]) = data[id].asDouble(); + *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id); break; case QVariant::String: - *reinterpret_cast<QString *>(a[0]) = data[id].asQString(); + *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id); break; case QVariant::Url: - *reinterpret_cast<QUrl *>(a[0]) = data[id].asQUrl(); + *reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id); break; case QVariant::Date: - *reinterpret_cast<QDate *>(a[0]) = data[id].asQDate(); + *reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id); break; case QVariant::DateTime: - *reinterpret_cast<QDateTime *>(a[0]) = data[id].asQDateTime(); + *reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id); break; case QVariant::RectF: - *reinterpret_cast<QRectF *>(a[0]) = data[id].asQRectF(); + *reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id); break; case QVariant::SizeF: - *reinterpret_cast<QSizeF *>(a[0]) = data[id].asQSizeF(); + *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id); break; case QVariant::PointF: - *reinterpret_cast<QPointF *>(a[0]) = data[id].asQPointF(); + *reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id); break; case QMetaType::QObjectStar: - *reinterpret_cast<QObject **>(a[0]) = data[id].asQObject(); + *reinterpret_cast<QObject **>(a[0]) = readPropertyAsQObject(id); 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]); + { + if (t == qMetaTypeId<QQmlListProperty<QObject> >()) { + QList<QObject *> *list = readPropertyAsList(id); + QQmlListProperty<QObject> *p = static_cast<QQmlListProperty<QObject> *>(a[0]); + *p = QQmlListProperty<QObject>(object, list, + list_append, list_count, list_at, + list_clear); + p->dummy1 = this; + p->dummy2 = reinterpret_cast<void *>(quintptr(methodOffset() + id)); + } else { + QV4::MemberData *md = propertiesAsMemberData(); + if (md) { + QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); + if (v) + QQml_valueTypeProvider()->readValueType(v->d()->data, a[0], t); + } + } 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, const_cast<List *>(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])); + needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id); + writeProperty(id, *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])); + needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id); + writeProperty(id, *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])); + needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id); + writeProperty(id, *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])); + needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id); + writeProperty(id, *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])); + needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id); + writeProperty(id, *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])); + needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id); + writeProperty(id, *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])); + needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id); + writeProperty(id, *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])); + needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id); + writeProperty(id, *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])); + needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id); + writeProperty(id, *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])); + needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id); + writeProperty(id, *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); + needActivate = *reinterpret_cast<QObject **>(a[0]) != readPropertyAsQObject(id); + writeProperty(id, *reinterpret_cast<QObject **>(a[0])); 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()); + default: { + QV4::MemberData *md = propertiesAsMemberData(); + if (md) { + QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); + if (!v) { + *(md->data() + id) = cache->engine->newVariantObject(QVariant()); + v = (md->data() + id)->as<QV4::VariantObject>(); + QQml_valueTypeProvider()->initValueType(t, v->d()->data); + } + needActivate = !QQml_valueTypeProvider()->equalValueType(t, a[0], v->d()->data); + QQml_valueTypeProvider()->writeValueType(t, a[0], v->d()->data); + } break; } + } } } @@ -874,10 +780,8 @@ int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) 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 (targetData && targetData->hasBindingBit(d->propertyIndex())) + QQmlPropertyPrivate::removeBinding(target, d->propertyIdx); } } @@ -975,129 +879,125 @@ QV4::ReturnedValue QQmlVMEMetaObject::method(int index) { if (!ctxt || !ctxt->isValid()) { qWarning("QQmlVMEMetaObject: Internal error - attempted to evaluate a function in an invalid context"); - return QV4::Primitive::undefinedValue().asReturnedValue(); + return QV4::Encode::undefined(); } - if (!v8methods) - v8methods = new QV4::PersistentValue[metaData->methodCount]; + if (!methods) + return QV4::Encode::undefined(); - return v8methods[index].value(); + return methods[index].value(); } QV4::ReturnedValue QQmlVMEMetaObject::readVarProperty(int id) { - Q_ASSERT(id >= firstVarPropertyIndex); + Q_ASSERT((metaData->propertyData() + id)->propertyType == QQmlVMEMetaData::VarPropertyType); - if (ensureVarPropertiesAllocated()) { - QV4::Scope scope(varProperties.engine()); - QV4::ScopedObject o(scope, varProperties.value()); - return o->getIndexed(id - firstVarPropertyIndex); - } + QV4::MemberData *md = propertiesAsMemberData(); + if (md) + return (md->data() + id)->asReturnedValue(); return QV4::Primitive::undefinedValue().asReturnedValue(); } QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id) { - if (id >= firstVarPropertyIndex) { - if (ensureVarPropertiesAllocated()) { - QV4::ExecutionEngine *v4 = varProperties.engine(); - QV4::Scope scope(v4); - QV4::ScopedObject o(scope, varProperties.value()); - QV4::ScopedValue val(scope, o->getIndexed(id - firstVarPropertyIndex)); - return scope.engine->toVariant(val, -1); - } - return QVariant(); - } else { - if (data[id].dataType() == QMetaType::QObjectStar) { - return QVariant::fromValue(data[id].asQObject()); - } else { - return data[id].asQVariant(); - } + QV4::MemberData *md = propertiesAsMemberData(); + if (md) { + const QV4::QObjectWrapper *wrapper = (md->data() + id)->as<QV4::QObjectWrapper>(); + if (wrapper) + return QVariant::fromValue(wrapper->object()); + const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); + if (v) + return v->d()->data; + return cache->engine->toVariant(*(md->data() + id), -1); } + return QVariant(); } void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value) { - Q_ASSERT(id >= firstVarPropertyIndex); - if (!ensureVarPropertiesAllocated()) + Q_ASSERT((metaData->propertyData() + id)->propertyType == QQmlVMEMetaData::VarPropertyType); + + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) return; - QV4::Scope scope(varProperties.engine()); // Importantly, if the current value is a scarce resource, we need to ensure that it // gets automatically released by the engine if no other references to it exist. - QV4::ScopedObject vp(scope, varProperties.value()); - QV4::Scoped<QV4::VariantObject> oldv(scope, vp->getIndexed(id - firstVarPropertyIndex)); - if (!!oldv) - oldv->removeVmePropertyReference(); + QV4::VariantObject *oldVariant = (md->data() + id)->as<QV4::VariantObject>(); + if (oldVariant) + oldVariant->removeVmePropertyReference(); QObject *valueObject = 0; QQmlVMEVariantQObjectPtr *guard = getQObjectGuardForProperty(id); - QV4::ScopedObject o(scope, value); - if (o) { - // And, if the new value is a scarce resource, we need to ensure that it does not get - // automatically released by the engine until no other references to it exist. - if (QV4::VariantObject *v = o->as<QV4::VariantObject>()) { - v->addVmePropertyReference(); - } else if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) { - // We need to track this QObject to signal its deletion - valueObject = wrapper->object(); - - // Do we already have a QObject guard for this property? - if (valueObject && !guard) { - guard = new QQmlVMEVariantQObjectPtr(true); - varObjectGuards.append(guard); - } + // And, if the new value is a scarce resource, we need to ensure that it does not get + // automatically released by the engine until no other references to it exist. + if (QV4::VariantObject *v = const_cast<QV4::VariantObject*>(value.as<QV4::VariantObject>())) { + v->addVmePropertyReference(); + } else if (QV4::QObjectWrapper *wrapper = const_cast<QV4::QObjectWrapper*>(value.as<QV4::QObjectWrapper>())) { + // We need to track this QObject to signal its deletion + valueObject = wrapper->object(); + + // Do we already have a QObject guard for this property? + if (valueObject && !guard) { + guard = new QQmlVMEVariantQObjectPtr(); + varObjectGuards.append(guard); } } - if (guard) { + if (guard) guard->setGuardedValue(valueObject, this, id); - } // Write the value and emit change signal as appropriate. - vp->putIndexed(id - firstVarPropertyIndex, value); + *(md->data() + id) = value; activate(object, methodOffset() + id, 0); } void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) { - if (id >= firstVarPropertyIndex) { - if (!ensureVarPropertiesAllocated()) + if ((metaData->propertyData() + id)->propertyType == QQmlVMEMetaData::VarPropertyType) { + QV4::MemberData *md = propertiesAsMemberData(); + if (!md) return; - QV4::Scope scope(varProperties.engine()); - // Importantly, if the current value is a scarce resource, we need to ensure that it // gets automatically released by the engine if no other references to it exist. - QV4::ScopedObject vp(scope, varProperties.value()); - QV4::Scoped<QV4::VariantObject> oldv(scope, vp->getIndexed(id - firstVarPropertyIndex)); - if (!!oldv) + QV4::VariantObject *oldv = (md->data() + id)->as<QV4::VariantObject>(); + if (oldv) oldv->removeVmePropertyReference(); // And, if the new value is a scarce resource, we need to ensure that it does not get // automatically released by the engine until no other references to it exist. - QV4::ScopedValue newv(scope, scope.engine->fromVariant(value)); + QV4::Scope scope(cache->engine); + QV4::ScopedValue newv(scope, cache->engine->fromVariant(value)); QV4::Scoped<QV4::VariantObject> v(scope, newv); if (!!v) v->addVmePropertyReference(); // Write the value and emit change signal as appropriate. QVariant currentValue = readPropertyAsVariant(id); - vp->putIndexed(id - firstVarPropertyIndex, newv); + *(md->data() + id) = newv; if ((currentValue.userType() != value.userType() || currentValue != value)) activate(object, methodOffset() + id, 0); } else { bool needActivate = false; if (value.userType() == QMetaType::QObjectStar) { QObject *o = *(QObject *const *)value.data(); - needActivate = (data[id].dataType() != QMetaType::QObjectStar || data[id].asQObject() != o); - data[id].setValue(o, this, id); + needActivate = readPropertyAsQObject(id) != o; // TODO: still correct? + writeProperty(id, o); } else { - needActivate = (data[id].dataType() != qMetaTypeId<QVariant>() || - data[id].asQVariant().userType() != value.userType() || - data[id].asQVariant() != value); - data[id].setValue(value); + QV4::MemberData *md = propertiesAsMemberData(); + if (md) { + QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); + needActivate = (!v || + v->d()->data.userType() != value.userType() || + v->d()->data != value); + if (v) + v->removeVmePropertyReference(); + *(md->data() + id) = cache->engine->newVariantObject(value); + v = static_cast<QV4::VariantObject *>(md->data() + id); + v->addVmePropertyReference(); + } } if (needActivate) @@ -1112,34 +1012,28 @@ void QQmlVMEMetaObject::listChanged(int id) void QQmlVMEMetaObject::list_append(QQmlListProperty<QObject> *prop, QObject *o) { - List *list = static_cast<List *>(prop->data); + QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); list->append(o); - list->mo->activate(prop->object, list->notifyIndex, 0); + static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), 0); } int QQmlVMEMetaObject::list_count(QQmlListProperty<QObject> *prop) { - return static_cast<List *>(prop->data)->count(); + QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); + return list->count(); } QObject *QQmlVMEMetaObject::list_at(QQmlListProperty<QObject> *prop, int index) { - return static_cast<List *>(prop->data)->at(index); + QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); + return list->at(index); } void QQmlVMEMetaObject::list_clear(QQmlListProperty<QObject> *prop) { - List *list = static_cast<List *>(prop->data); + QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); list->clear(); - list->mo->activate(prop->object, list->notifyIndex, 0); -} - -void QQmlVMEMetaObject::registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor) -{ - interceptor->m_coreIndex = index; - interceptor->m_valueTypeCoreIndex = valueIndex; - interceptor->m_next = interceptors; - interceptors = interceptor; + static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), 0); } quint16 QQmlVMEMetaObject::vmeMethodLineNumber(int index) @@ -1179,11 +1073,11 @@ void QQmlVMEMetaObject::setVmeMethod(int index, const QV4::Value &function) int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; Q_ASSERT(index >= (methodOffset() + plainSignals) && index < (methodOffset() + plainSignals + metaData->methodCount)); - if (!v8methods) - v8methods = new QV4::PersistentValue[metaData->methodCount]; + if (!methods) + methods = new QV4::PersistentValue[metaData->methodCount]; int methodIndex = index - methodOffset() - plainSignals; - v8methods[methodIndex].set(function.asObject()->engine(), function); + methods[methodIndex].set(function.as<QV4::Object>()->engine(), function); } QV4::ReturnedValue QQmlVMEMetaObject::vmeProperty(int index) @@ -1205,57 +1099,34 @@ void QQmlVMEMetaObject::setVMEProperty(int index, const QV4::Value &v) return writeVarProperty(index - propOffset(), v); } -bool QQmlVMEMetaObject::ensureVarPropertiesAllocated() -{ - if (!varPropertiesInitialized) - allocateVarPropertiesArray(); - - // in some situations, the QObject's v8object (and associated v8 data, - // such as the varProperties array) will have been cleaned up, but the - // QObject ptr will not yet have been deleted (eg, waiting on deleteLater). - // In this situation, the varProperties handle will be (and should remain) - // empty. - return !varProperties.isUndefined(); -} - void QQmlVMEMetaObject::ensureQObjectWrapper() { - QQmlEnginePrivate *ep = (ctxt == 0 || ctxt->engine == 0) ? 0 : QQmlEnginePrivate::get(ctxt->engine); - QV4::ExecutionEngine *v4 = (ep == 0) ? 0 : ep->v4engine(); + Q_ASSERT(cache && cache->engine); + QV4::ExecutionEngine *v4 = cache->engine; QV4::QObjectWrapper::wrap(v4, object); } void QQmlVMEMetaObject::mark(QV4::ExecutionEngine *e) { - QQmlEnginePrivate *ep = (ctxt == 0 || ctxt->engine == 0) ? 0 : QQmlEnginePrivate::get(ctxt->engine); - QV4::ExecutionEngine *v4 = (ep == 0) ? 0 : ep->v4engine(); + QV4::ExecutionEngine *v4 = cache ? cache->engine : 0; if (v4 != e) return; - varProperties.markOnce(e); - - // add references created by VMEVariant properties - int maxDataIdx = metaData->propertyCount - metaData->varPropertyCount; - for (int ii = 0; ii < maxDataIdx; ++ii) { // XXX TODO: optimize? - if (data[ii].dataType() == QMetaType::QObjectStar) { - // possible QObject reference. - if (QObject *ref = data[ii].asQObject()) - QV4::QObjectWrapper::markWrapper(ref, e); - } - } + properties.markOnce(e); if (QQmlVMEMetaObject *parent = parentVMEMetaObject()) parent->mark(e); } -void QQmlVMEMetaObject::allocateVarPropertiesArray() +void QQmlVMEMetaObject::allocateProperties() { - QQmlEngine *qml = qmlEngine(object); - assert(qml); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(qml->handle()); - QV4::Scope scope(v4); - varProperties.set(scope.engine, v4->newArrayObject(metaData->varPropertyCount)); - varPropertiesInitialized = true; + Q_ASSERT(cache && cache->engine); + Q_ASSERT(!properties.valueRef()); + QV4::ExecutionEngine *v4 = cache->engine; + QV4::Heap::MemberData *data = QV4::MemberData::allocate(v4, metaData->propertyCount); + properties.set(v4, data); + for (uint i = 0; i < data->size; ++i) + data->data[i] = QV4::Encode::undefined(); } bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const @@ -1290,22 +1161,21 @@ bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, void QQmlVMEMetaObject::connectAlias(int aliasId) { - if (!aConnected.testBit(aliasId)) { - - if (!aliasEndpoints) - aliasEndpoints = new QQmlVMEMetaObjectEndpoint[metaData->aliasCount]; + if (!aliasEndpoints) + aliasEndpoints = new QQmlVMEMetaObjectEndpoint[metaData->aliasCount]; - aConnected.setBit(aliasId); + QQmlVMEMetaData::AliasData *d = metaData->aliasData() + aliasId; - QQmlVMEMetaData::AliasData *d = metaData->aliasData() + aliasId; - - QQmlVMEMetaObjectEndpoint *endpoint = aliasEndpoints + aliasId; - endpoint->metaObject = this; - - endpoint->connect(&ctxt->idValues[d->contextIdx].bindings); - - endpoint->tryConnect(); + QQmlVMEMetaObjectEndpoint *endpoint = aliasEndpoints + aliasId; + if (endpoint->metaObject.data()) { + // already connected + Q_ASSERT(endpoint->metaObject.data() == this); + return; } + + endpoint->metaObject = this; + endpoint->connect(&ctxt->idValues[d->contextIdx].bindings); + endpoint->tryConnect(); } void QQmlVMEMetaObject::connectAliasSignal(int index, bool indexInSignalRange) diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index f3048d426a..7da44e3b82 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 BasysKom GmbH. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -63,7 +64,7 @@ #include <private/qv8engine_p.h> #include <private/qflagpointer_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> QT_BEGIN_NAMESPACE @@ -71,13 +72,11 @@ QT_BEGIN_NAMESPACE struct QQmlVMEMetaData { - short varPropertyCount; short propertyCount; short aliasCount; short signalCount; short methodCount; - short dummyForAlignment; // Add padding to ensure that the following - // AliasData/PropertyData/MethodData is int aligned. + // Make sure this structure is always aligned to int struct AliasData { int contextIdx; @@ -108,6 +107,10 @@ struct QQmlVMEMetaData } }; + enum { + VarPropertyType = -1 + }; + struct PropertyData { int propertyType; }; @@ -135,29 +138,67 @@ class QQmlVMEMetaObject; class QQmlVMEVariantQObjectPtr : public QQmlGuard<QObject> { public: - inline QQmlVMEVariantQObjectPtr(bool isVar); + inline QQmlVMEVariantQObjectPtr(); inline ~QQmlVMEVariantQObjectPtr(); inline void objectDestroyed(QObject *); inline void setGuardedValue(QObject *obj, QQmlVMEMetaObject *target, int index); QQmlVMEMetaObject *m_target; - unsigned m_isVar : 1; - int m_index : 31; + int m_index; +}; + + +class Q_QML_PRIVATE_EXPORT QQmlInterceptorMetaObject : public QAbstractDynamicMetaObject +{ +public: + QQmlInterceptorMetaObject(QObject *obj, QQmlPropertyCache *cache); + ~QQmlInterceptorMetaObject(); + + void registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor); + + static QQmlInterceptorMetaObject *get(QObject *obj); + + virtual QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *o); + + // Used by auto-tests for inspection + QQmlPropertyCache *propertyCache() const { return cache; } + +protected: + virtual int metaCall(QObject *o, QMetaObject::Call c, int id, void **a); + bool intercept(QMetaObject::Call c, int id, void **a); + +public: + QObject *object; + QQmlPropertyCache *cache; + QBiPointer<QDynamicMetaObjectData, const QMetaObject> parent; + + QQmlPropertyValueInterceptor *interceptors; + bool hasAssignedMetaObjectData; }; +inline QQmlInterceptorMetaObject *QQmlInterceptorMetaObject::get(QObject *obj) +{ + if (obj) { + if (QQmlData *data = QQmlData::get(obj)) { + if (data->hasInterceptorMetaObject) + return static_cast<QQmlInterceptorMetaObject *>(QObjectPrivate::get(obj)->metaObject); + } + } + + return 0; +} + class QQmlVMEVariant; class QQmlRefCount; class QQmlVMEMetaObjectEndpoint; -class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QAbstractDynamicMetaObject +class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject { public: - QQmlVMEMetaObject(QObject *obj, QQmlPropertyCache *cache, const QQmlVMEMetaData *data, - QV4::ExecutionContext *qmlBindingContext = 0, QQmlCompiledData *compiledData = 0); + QQmlVMEMetaObject(QObject *obj, QQmlPropertyCache *cache, const QQmlVMEMetaData *data); ~QQmlVMEMetaObject(); bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const; - void registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor); QV4::ReturnedValue vmeMethod(int index); quint16 vmeMethodLineNumber(int index); void setVmeMethod(int index, const QV4::Value &function); @@ -166,27 +207,20 @@ public: void connectAliasSignal(int index, bool indexInSignalRange); - virtual QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *o); - - // Used by auto-tests for inspection - QQmlPropertyCache *propertyCache() const { return cache; } - static inline QQmlVMEMetaObject *get(QObject *o); static QQmlVMEMetaObject *getForProperty(QObject *o, int coreIndex); static QQmlVMEMetaObject *getForMethod(QObject *o, int coreIndex); static QQmlVMEMetaObject *getForSignal(QObject *o, int coreIndex); protected: - virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + virtual int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a); public: friend class QQmlVMEMetaObjectEndpoint; friend class QQmlVMEVariantQObjectPtr; friend class QQmlPropertyCache; - QObject *object; QQmlGuardedContextData ctxt; - QQmlPropertyCache *cache; const QQmlVMEMetaData *metaData; inline int propOffset() const; @@ -194,26 +228,44 @@ public: inline int signalOffset() const; inline int signalCount() const; - bool hasAssignedMetaObjectData; - QQmlVMEVariant *data; QQmlVMEMetaObjectEndpoint *aliasEndpoints; - QV4::WeakValue varProperties; - int firstVarPropertyIndex; - bool varPropertiesInitialized; - inline void allocateVarPropertiesArray(); - inline bool ensureVarPropertiesAllocated(); + QV4::WeakValue properties; + inline void allocateProperties(); + QV4::MemberData *propertiesAsMemberData(); + + int readPropertyAsInt(int id); + bool readPropertyAsBool(int id); + double readPropertyAsDouble(int id); + QString readPropertyAsString(int id); + QSizeF readPropertyAsSizeF(int id); + QPointF readPropertyAsPointF(int id); + QUrl readPropertyAsUrl(int id); + QDate readPropertyAsDate(int id); + QDateTime readPropertyAsDateTime(int id); + QRectF readPropertyAsRectF(int id); + QObject *readPropertyAsQObject(int id); + QList<QObject *> *readPropertyAsList(int id); + + void writeProperty(int id, int v); + void writeProperty(int id, bool v); + void writeProperty(int id, double v); + void writeProperty(int id, const QString& v); + void writeProperty(int id, const QPointF& v); + void writeProperty(int id, const QSizeF& v); + void writeProperty(int id, const QUrl& v); + void writeProperty(int id, const QDate& v); + void writeProperty(int id, const QDateTime& v); + void writeProperty(int id, const QRectF& v); + void writeProperty(int id, QObject *v); void ensureQObjectWrapper(); void mark(QV4::ExecutionEngine *e); void connectAlias(int aliasId); - QBitArray aConnected; - QQmlPropertyValueInterceptor *interceptors; - - QV4::PersistentValue *v8methods; + QV4::PersistentValue *methods; QV4::ReturnedValue method(int); QV4::ReturnedValue readVarProperty(int); @@ -221,19 +273,9 @@ public: QVariant readPropertyAsVariant(int); void writeProperty(int, const QVariant &); - QBiPointer<QDynamicMetaObjectData, const QMetaObject> parent; - inline QQmlVMEMetaObject *parentVMEMetaObject() const; void listChanged(int); - class List : public QList<QObject*> - { - public: - List(int lpi, QQmlVMEMetaObject *mo) : notifyIndex(lpi), mo(mo) {} - int notifyIndex; - QQmlVMEMetaObject *mo; - }; - QList<List> listProperties; static void list_append(QQmlListProperty<QObject> *, QObject *); static int list_count(QQmlListProperty<QObject> *); @@ -245,8 +287,6 @@ public: QList<QQmlVMEVariantQObjectPtr *> varObjectGuards; QQmlVMEVariantQObjectPtr *getQObjectGuardForProperty(int) const; - - friend class QV8GCCallback; }; QQmlVMEMetaObject *QQmlVMEMetaObject::get(QObject *obj) diff --git a/src/qml/qml/qqmlwatcher.cpp b/src/qml/qml/qqmlwatcher.cpp deleted file mode 100644 index 9726b6f3b9..0000000000 --- a/src/qml/qml/qqmlwatcher.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlwatcher_p.h" - -#include "qqmlexpression.h" -#include "qqmlcontext.h" -#include "qqml.h" - -#include <private/qqmldebugservice_p.h> -#include "qqmlproperty_p.h" -#include "qqmlvaluetype_p.h" - -#include <QtCore/qmetaobject.h> -#include <QtCore/qdebug.h> - -QT_BEGIN_NAMESPACE - - -class QQmlWatchProxy : public QObject -{ - Q_OBJECT -public: - QQmlWatchProxy(int id, - QObject *object, - int debugId, - const QMetaProperty &prop, - QQmlWatcher *parent = 0); - - QQmlWatchProxy(int id, - QQmlExpression *exp, - int debugId, - QQmlWatcher *parent = 0); - -public slots: - void notifyValueChanged(); - -private: - friend class QQmlWatcher; - int m_id; - QQmlWatcher *m_watch; - QObject *m_object; - int m_debugId; - QMetaProperty m_property; - - QQmlExpression *m_expr; -}; - -QQmlWatchProxy::QQmlWatchProxy(int id, - QQmlExpression *exp, - int debugId, - QQmlWatcher *parent) -: QObject(parent), m_id(id), m_watch(parent), m_object(0), m_debugId(debugId), m_expr(exp) -{ - QObject::connect(m_expr, SIGNAL(valueChanged()), this, SLOT(notifyValueChanged())); -} - -QQmlWatchProxy::QQmlWatchProxy(int id, - QObject *object, - int debugId, - const QMetaProperty &prop, - QQmlWatcher *parent) -: QObject(parent), m_id(id), m_watch(parent), m_object(object), m_debugId(debugId), m_property(prop), m_expr(0) -{ - static int refreshIdx = -1; - if(refreshIdx == -1) - refreshIdx = QQmlWatchProxy::staticMetaObject.indexOfMethod("notifyValueChanged()"); - - if (prop.hasNotifySignal()) - QQmlPropertyPrivate::connect(m_object, prop.notifySignalIndex(), this, refreshIdx); -} - -void QQmlWatchProxy::notifyValueChanged() -{ - QVariant v; - if (m_expr) - v = m_expr->evaluate(); - else if (QQmlValueTypeFactory::isValueType(m_property.userType())) - v = m_property.read(m_object); - - emit m_watch->propertyChanged(m_id, m_debugId, m_property, v); -} - - -QQmlWatcher::QQmlWatcher(QObject *parent) - : QObject(parent) -{ -} - -bool QQmlWatcher::addWatch(int id, quint32 debugId) -{ - QObject *object = QQmlDebugService::objectForId(debugId); - if (object) { - int propCount = object->metaObject()->propertyCount(); - for (int ii=0; ii<propCount; ii++) - addPropertyWatch(id, object, debugId, object->metaObject()->property(ii)); - return true; - } - return false; -} - -bool QQmlWatcher::addWatch(int id, quint32 debugId, const QByteArray &property) -{ - QObject *object = QQmlDebugService::objectForId(debugId); - if (object) { - int index = object->metaObject()->indexOfProperty(property.constData()); - if (index >= 0) { - addPropertyWatch(id, object, debugId, object->metaObject()->property(index)); - return true; - } - } - return false; -} - -bool QQmlWatcher::addWatch(int id, quint32 objectId, const QString &expr) -{ - QObject *object = QQmlDebugService::objectForId(objectId); - QQmlContext *context = qmlContext(object); - if (context) { - QQmlExpression *exprObj = new QQmlExpression(context, object, expr); - exprObj->setNotifyOnValueChanged(true); - QQmlWatchProxy *proxy = new QQmlWatchProxy(id, exprObj, objectId, this); - exprObj->setParent(proxy); - m_proxies[id].append(proxy); - proxy->notifyValueChanged(); - return true; - } - return false; -} - -bool QQmlWatcher::removeWatch(int id) -{ - if (!m_proxies.contains(id)) - return false; - - QList<QPointer<QQmlWatchProxy> > proxies = m_proxies.take(id); - qDeleteAll(proxies); - return true; -} - -void QQmlWatcher::addPropertyWatch(int id, QObject *object, quint32 debugId, const QMetaProperty &property) -{ - QQmlWatchProxy *proxy = new QQmlWatchProxy(id, object, debugId, property, this); - m_proxies[id].append(proxy); - - proxy->notifyValueChanged(); -} - -QT_END_NAMESPACE - -#include <qqmlwatcher.moc> diff --git a/src/qml/qml/qqmlwatcher_p.h b/src/qml/qml/qqmlwatcher_p.h deleted file mode 100644 index a7bb3c3418..0000000000 --- a/src/qml/qml/qqmlwatcher_p.h +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLWATCHER_P_H -#define QQMLWATCHER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qobject.h> -#include <QtCore/qlist.h> -#include <QtCore/qpair.h> -#include <QtCore/qhash.h> -#include <QtCore/qset.h> -#include <QtCore/qpointer.h> - -QT_BEGIN_NAMESPACE - -class QQmlWatchProxy; -class QQmlExpression; -class QQmlContext; -class QMetaProperty; - -class QQmlWatcher : public QObject -{ - Q_OBJECT -public: - QQmlWatcher(QObject * = 0); - - bool addWatch(int id, quint32 objectId); - bool addWatch(int id, quint32 objectId, const QByteArray &property); - bool addWatch(int id, quint32 objectId, const QString &expr); - - bool removeWatch(int id); - -Q_SIGNALS: - void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value); - -private: - friend class QQmlWatchProxy; - void addPropertyWatch(int id, QObject *object, quint32 objectId, const QMetaProperty &property); - - QHash<int, QList<QPointer<QQmlWatchProxy> > > m_proxies; -}; - -QT_END_NAMESPACE - -#endif // QQMLWATCHER_P_H diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 5586bdbb50..3521384d77 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -60,6 +60,7 @@ #include <private/qv4objectproto_p.h> #include <private/qv4scopedvalue_p.h> #include <private/qv4arraybuffer_p.h> +#include <private/qv4jsonobject_p.h> using namespace QV4; @@ -94,16 +95,6 @@ static inline QQmlXMLHttpRequestData *xhrdata(ExecutionEngine *v4) return (QQmlXMLHttpRequestData *)v4->v8Engine->xmlHttpRequestData(); } -static ReturnedValue constructMeObject(const Value &thisObj, ExecutionEngine *v4) -{ - Scope scope(v4); - ScopedObject meObj(scope, v4->newObject()); - meObj->put(ScopedString(scope, v4->newString(QStringLiteral("ThisObject"))), thisObj); - ScopedValue v(scope, QmlContextWrapper::qmlScope(v4, v4->v8Engine->callingContext(), 0)); - meObj->put(ScopedString(scope, v4->newString(QStringLiteral("ActivationObject"))), v); - return meObj.asReturnedValue(); -} - QQmlXMLHttpRequestData::QQmlXMLHttpRequestData() { } @@ -179,7 +170,7 @@ public: namespace Heap { struct NamedNodeMap : Object { - NamedNodeMap(ExecutionEngine *engine, NodeImpl *data, const QList<NodeImpl *> &list); + NamedNodeMap(NodeImpl *data, const QList<NodeImpl *> &list); ~NamedNodeMap() { if (d) d->release(); @@ -189,7 +180,7 @@ struct NamedNodeMap : Object { }; struct NodeList : Object { - NodeList(ExecutionEngine *engine, NodeImpl *data); + NodeList(NodeImpl *data); ~NodeList() { if (d) d->release(); @@ -198,11 +189,11 @@ struct NodeList : Object { }; struct NodePrototype : Object { - NodePrototype(ExecutionEngine *engine); + NodePrototype(); }; struct Node : Object { - Node(ExecutionEngine *engine, NodeImpl *data); + Node(NodeImpl *data); ~Node() { if (d) d->release(); @@ -222,13 +213,12 @@ public: static ReturnedValue create(ExecutionEngine *, NodeImpl *, const QList<NodeImpl *> &); // JS API - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); + static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); }; -Heap::NamedNodeMap::NamedNodeMap(ExecutionEngine *engine, NodeImpl *data, const QList<NodeImpl *> &list) - : Heap::Object(engine) - , list(list) +Heap::NamedNodeMap::NamedNodeMap(NodeImpl *data, const QList<NodeImpl *> &list) + : list(list) , d(data) { if (d) @@ -244,17 +234,16 @@ public: V4_NEEDS_DESTROY // JS API - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); + static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); // C++ API static ReturnedValue create(ExecutionEngine *, NodeImpl *); }; -Heap::NodeList::NodeList(ExecutionEngine *engine, NodeImpl *data) - : Heap::Object(engine) - , d(data) +Heap::NodeList::NodeList(NodeImpl *data) + : d(data) { if (d) d->addref(); @@ -273,6 +262,7 @@ public: static ReturnedValue method_get_nodeName(CallContext *ctx); static ReturnedValue method_get_nodeValue(CallContext *ctx); static ReturnedValue method_get_nodeType(CallContext *ctx); + static ReturnedValue method_get_namespaceUri(CallContext *ctx); static ReturnedValue method_get_parentNode(CallContext *ctx); static ReturnedValue method_get_childNodes(CallContext *ctx); @@ -293,15 +283,15 @@ public: }; -Heap::NodePrototype::NodePrototype(ExecutionEngine *engine) - : Heap::Object(engine) +Heap::NodePrototype::NodePrototype() { - Scope scope(engine); + Scope scope(internalClass->engine); ScopedObject o(scope, this); o->defineAccessorProperty(QStringLiteral("nodeName"), QV4::NodePrototype::method_get_nodeName, 0); o->defineAccessorProperty(QStringLiteral("nodeValue"), QV4::NodePrototype::method_get_nodeValue, 0); o->defineAccessorProperty(QStringLiteral("nodeType"), QV4::NodePrototype::method_get_nodeType, 0); + o->defineAccessorProperty(QStringLiteral("namespaceUri"), QV4::NodePrototype::method_get_namespaceUri, 0); o->defineAccessorProperty(QStringLiteral("parentNode"), QV4::NodePrototype::method_get_parentNode, 0); o->defineAccessorProperty(QStringLiteral("childNodes"), QV4::NodePrototype::method_get_childNodes, 0); @@ -326,9 +316,8 @@ struct Node : public Object bool isNull() const; }; -Heap::Node::Node(ExecutionEngine *engine, NodeImpl *data) - : Heap::Object(engine) - , d(data) +Heap::Node::Node(NodeImpl *data) + : d(data) { if (d) d->addref(); @@ -466,6 +455,16 @@ ReturnedValue NodePrototype::method_get_nodeType(CallContext *ctx) return Encode(r->d()->d->type); } +ReturnedValue NodePrototype::method_get_namespaceUri(CallContext *ctx) +{ + Scope scope(ctx); + Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + if (!r) + return ctx->engine()->throwTypeError(); + + return Encode(ctx->d()->engine->newString(r->d()->d->namespaceUri)); +} + ReturnedValue NodePrototype::method_get_parentNode(CallContext *ctx) { Scope scope(ctx); @@ -577,7 +576,7 @@ ReturnedValue NodePrototype::getProto(ExecutionEngine *v4) Scope scope(v4); QQmlXMLHttpRequestData *d = xhrdata(v4); if (d->nodePrototype.isUndefined()) { - ScopedObject p(scope, v4->memoryManager->alloc<NodePrototype>(v4)); + ScopedObject p(scope, v4->memoryManager->allocObject<NodePrototype>()); d->nodePrototype.set(v4, p); v4->v8Engine->freezeObject(p); } @@ -588,7 +587,7 @@ ReturnedValue Node::create(ExecutionEngine *v4, NodeImpl *data) { Scope scope(v4); - Scoped<Node> instance(scope, v4->memoryManager->alloc<Node>(v4, data)); + Scoped<Node> instance(scope, v4->memoryManager->allocObject<Node>(data)); ScopedObject p(scope); switch (data->type) { @@ -856,7 +855,8 @@ ReturnedValue Document::load(ExecutionEngine *v4, const QByteArray &data) return Encode::null(); } - ScopedObject instance(scope, v4->memoryManager->alloc<Node>(v4, document)); + ScopedObject instance(scope, v4->memoryManager->allocObject<Node>(document)); + document->release(); // the GC should own the NodeImpl via Node now ScopedObject p(scope); instance->setPrototype((p = Document::prototype(v4))); return instance.asReturnedValue(); @@ -867,10 +867,10 @@ bool Node::isNull() const return d()->d == 0; } -ReturnedValue NamedNodeMap::getIndexed(Managed *m, uint index, bool *hasProperty) +ReturnedValue NamedNodeMap::getIndexed(const Managed *m, uint index, bool *hasProperty) { Q_ASSERT(m->as<NamedNodeMap>()); - NamedNodeMap *r = static_cast<NamedNodeMap *>(m); + const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m); QV4::ExecutionEngine *v4 = r->engine(); if ((int)index < r->d()->list.count()) { @@ -883,14 +883,14 @@ ReturnedValue NamedNodeMap::getIndexed(Managed *m, uint index, bool *hasProperty return Encode::undefined(); } -ReturnedValue NamedNodeMap::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue NamedNodeMap::get(const Managed *m, String *name, bool *hasProperty) { Q_ASSERT(m->as<NamedNodeMap>()); - NamedNodeMap *r = static_cast<NamedNodeMap *>(m); + const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m); QV4::ExecutionEngine *v4 = r->engine(); name->makeIdentifier(v4); - if (name->equals(v4->id_length)) + if (name->equals(v4->id_length())) return Primitive::fromInt32(r->d()->list.count()).asReturnedValue(); QString str = name->toQString(); @@ -909,13 +909,13 @@ ReturnedValue NamedNodeMap::get(Managed *m, String *name, bool *hasProperty) ReturnedValue NamedNodeMap::create(ExecutionEngine *v4, NodeImpl *data, const QList<NodeImpl *> &list) { - return (v4->memoryManager->alloc<NamedNodeMap>(v4, data, list))->asReturnedValue(); + return (v4->memoryManager->allocObject<NamedNodeMap>(data, list))->asReturnedValue(); } -ReturnedValue NodeList::getIndexed(Managed *m, uint index, bool *hasProperty) +ReturnedValue NodeList::getIndexed(const Managed *m, uint index, bool *hasProperty) { Q_ASSERT(m->as<NodeList>()); - NodeList *r = static_cast<NodeList *>(m); + const NodeList *r = static_cast<const NodeList *>(m); QV4::ExecutionEngine *v4 = r->engine(); if ((int)index < r->d()->d->children.count()) { @@ -928,22 +928,22 @@ ReturnedValue NodeList::getIndexed(Managed *m, uint index, bool *hasProperty) return Encode::undefined(); } -ReturnedValue NodeList::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue NodeList::get(const Managed *m, String *name, bool *hasProperty) { Q_ASSERT(m->as<NodeList>()); - NodeList *r = static_cast<NodeList *>(m); + const NodeList *r = static_cast<const NodeList *>(m); QV4::ExecutionEngine *v4 = r->engine(); name->makeIdentifier(v4); - if (name->equals(v4->id_length)) + if (name->equals(v4->id_length())) return Primitive::fromInt32(r->d()->d->children.count()).asReturnedValue(); return Object::get(m, name, hasProperty); } ReturnedValue NodeList::create(ExecutionEngine *v4, NodeImpl *data) { - return (v4->memoryManager->alloc<NodeList>(v4, data))->asReturnedValue(); + return (v4->memoryManager->allocObject<NodeList>(data))->asReturnedValue(); } ReturnedValue Document::method_documentElement(CallContext *ctx) @@ -998,7 +998,7 @@ public: Opened = 1, HeadersReceived = 2, Loading = 3, Done = 4 }; - QQmlXMLHttpRequest(ExecutionEngine *engine, QNetworkAccessManager *manager); + QQmlXMLHttpRequest(QNetworkAccessManager *manager); virtual ~QQmlXMLHttpRequest(); bool sendFlag() const; @@ -1007,21 +1007,23 @@ public: int replyStatus() const; QString replyStatusText() const; - ReturnedValue open(const Value &me, const QString &, const QUrl &, LoadType); - ReturnedValue send(const Value &me, const QByteArray &); - ReturnedValue abort(const Value &me); + ReturnedValue open(Object *thisObject, QQmlContextData *context, const QString &, const QUrl &, LoadType); + ReturnedValue send(Object *thisObject, QQmlContextData *context, const QByteArray &); + ReturnedValue abort(Object *thisObject, QQmlContextData *context); void addHeader(const QString &, const QString &); QString header(const QString &name); QString headers(); - QString responseBody(); const QByteArray & rawResponseBody() const; bool receivedXml() const; const QString & responseType() const; void setResponseType(const QString &); + + QV4::ReturnedValue jsonResponseBody(QV4::ExecutionEngine*); + QV4::ReturnedValue xmlResponseBody(QV4::ExecutionEngine*); private slots: void readyRead(); void error(QNetworkReply::NetworkError); @@ -1030,7 +1032,6 @@ private slots: private: void requestFromUrl(const QUrl &url); - ExecutionEngine *v4; State m_state; bool m_errorFlag; bool m_sendFlag; @@ -1054,12 +1055,11 @@ private: #endif void readEncoding(); - ReturnedValue getMe() const; - void setMe(const Value &me); - PersistentValue m_me; + PersistentValue m_thisObject; + QQmlGuardedContextData m_qmlContext; - void dispatchCallbackImpl(const Value &me); - void dispatchCallback(const Value &me); + static void dispatchCallback(Object *thisObj, QQmlContextData *context); + void dispatchCallback(); int m_status; QString m_statusText; @@ -1072,13 +1072,14 @@ private: QNetworkAccessManager *networkAccessManager() { return m_nam; } QString m_responseType; + QV4::PersistentValue m_parsedDocument; }; -QQmlXMLHttpRequest::QQmlXMLHttpRequest(ExecutionEngine *engine, QNetworkAccessManager *manager) - : v4(engine) - , m_state(Unsent), m_errorFlag(false), m_sendFlag(false) +QQmlXMLHttpRequest::QQmlXMLHttpRequest(QNetworkAccessManager *manager) + : m_state(Unsent), m_errorFlag(false), m_sendFlag(false) , m_redirectCount(0), m_gotXml(false), m_textCodec(0), m_network(0), m_nam(manager) , m_responseType() + , m_parsedDocument() { } @@ -1112,7 +1113,7 @@ QString QQmlXMLHttpRequest::replyStatusText() const return m_statusText; } -ReturnedValue QQmlXMLHttpRequest::open(const Value &me, const QString &method, const QUrl &url, LoadType loadType) +ReturnedValue QQmlXMLHttpRequest::open(Object *thisObject, QQmlContextData *context, const QString &method, const QUrl &url, LoadType loadType) { destroyNetwork(); m_sendFlag = false; @@ -1123,7 +1124,7 @@ ReturnedValue QQmlXMLHttpRequest::open(const Value &me, const QString &method, c m_request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, loadType == SynchronousLoad); m_state = Opened; m_addedHeaders.clear(); - dispatchCallback(me); + dispatchCallback(thisObject, context); return Encode::undefined(); } @@ -1229,11 +1230,12 @@ void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url) m_network = networkAccessManager()->put(request, m_data); } else if (m_method == QLatin1String("DELETE")) { m_network = networkAccessManager()->deleteResource(request); - } else if (m_method == QLatin1String("OPTIONS")) { + } else if ((m_method == QLatin1String("OPTIONS")) || + m_method == QLatin1String("PROPFIND")) { QBuffer *buffer = new QBuffer; buffer->setData(m_data); buffer->open(QIODevice::ReadOnly); - m_network = networkAccessManager()->sendCustomRequest(request, QByteArrayLiteral("OPTIONS"), buffer); + m_network = networkAccessManager()->sendCustomRequest(request, QByteArray(m_method.toUtf8().constData()), buffer); buffer->setParent(m_network); } @@ -1257,21 +1259,22 @@ void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url) } } -ReturnedValue QQmlXMLHttpRequest::send(const Value &me, const QByteArray &data) +ReturnedValue QQmlXMLHttpRequest::send(Object *thisObject, QQmlContextData *context, const QByteArray &data) { m_errorFlag = false; m_sendFlag = true; m_redirectCount = 0; m_data = data; - setMe(me); + m_thisObject = thisObject; + m_qmlContext = context; requestFromUrl(m_url); return Encode::undefined(); } -ReturnedValue QQmlXMLHttpRequest::abort(const Value &me) +ReturnedValue QQmlXMLHttpRequest::abort(Object *thisObject, QQmlContextData *context) { destroyNetwork(); m_responseEntityBody = QByteArray(); @@ -1284,7 +1287,7 @@ ReturnedValue QQmlXMLHttpRequest::abort(const Value &me) m_state = Done; m_sendFlag = false; - dispatchCallback(me); + dispatchCallback(thisObject, context); } m_state = Unsent; @@ -1292,16 +1295,6 @@ ReturnedValue QQmlXMLHttpRequest::abort(const Value &me) return Encode::undefined(); } -ReturnedValue QQmlXMLHttpRequest::getMe() const -{ - return m_me.value(); -} - -void QQmlXMLHttpRequest::setMe(const Value &me) -{ - m_me.set(v4, me); -} - void QQmlXMLHttpRequest::readyRead() { m_status = @@ -1309,14 +1302,11 @@ void QQmlXMLHttpRequest::readyRead() m_statusText = QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray()); - Scope scope(v4); - ScopedValue me(scope, m_me.value()); - // ### We assume if this is called the headers are now available if (m_state < HeadersReceived) { m_state = HeadersReceived; fillHeadersList (); - dispatchCallback(me); + dispatchCallback(); } bool wasEmpty = m_responseEntityBody.isEmpty(); @@ -1324,7 +1314,7 @@ void QQmlXMLHttpRequest::readyRead() if (wasEmpty && !m_responseEntityBody.isEmpty()) m_state = Loading; - dispatchCallback(me); + dispatchCallback(); } static const char *errorToString(QNetworkReply::NetworkError error) @@ -1355,26 +1345,26 @@ void QQmlXMLHttpRequest::error(QNetworkReply::NetworkError error) qWarning().nospace() << " " << error << ' ' << errorToString(error) << ' ' << m_statusText; } - Scope scope(v4); - ScopedValue me(scope, m_me.value()); - if (error == QNetworkReply::ContentAccessDenied || error == QNetworkReply::ContentOperationNotPermittedError || error == QNetworkReply::ContentNotFoundError || error == QNetworkReply::AuthenticationRequiredError || error == QNetworkReply::ContentReSendError || error == QNetworkReply::UnknownContentError || - error == QNetworkReply::ProtocolInvalidOperationError) { + error == QNetworkReply::ProtocolInvalidOperationError || + error == QNetworkReply::InternalServerError || + error == QNetworkReply::OperationNotImplementedError || + error == QNetworkReply::ServiceUnavailableError || + error == QNetworkReply::UnknownServerError) { m_state = Loading; - dispatchCallback(me); + dispatchCallback(); } else { m_errorFlag = true; m_responseEntityBody = QByteArray(); } m_state = Done; - - dispatchCallback(me); + dispatchCallback(); } #define XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION 15 @@ -1406,7 +1396,7 @@ void QQmlXMLHttpRequest::finished() if (m_state < HeadersReceived) { m_state = HeadersReceived; fillHeadersList (); - dispatchCallback(*m_me.valueRef()); + dispatchCallback(); } m_responseEntityBody.append(m_network->readAll()); readEncoding(); @@ -1423,15 +1413,14 @@ void QQmlXMLHttpRequest::finished() destroyNetwork(); if (m_state < Loading) { m_state = Loading; - dispatchCallback(*m_me.valueRef()); + dispatchCallback(); } m_state = Done; - dispatchCallback(*m_me.valueRef()); + dispatchCallback(); - Scope scope(v4); - ScopedValue v(scope, Primitive::undefinedValue()); - setMe(v); + m_thisObject.clear(); + m_qmlContext.setContextData(0); } @@ -1474,6 +1463,32 @@ void QQmlXMLHttpRequest::setResponseType(const QString &responseType) m_responseType = responseType; } +QV4::ReturnedValue QQmlXMLHttpRequest::jsonResponseBody(QV4::ExecutionEngine* engine) +{ + if (m_parsedDocument.isEmpty()) { + Scope scope(engine); + + QJsonParseError error; + const QString& jtext = responseBody(); + JsonParser parser(scope.engine, jtext.constData(), jtext.length()); + ScopedValue jsonObject(scope, parser.parse(&error)); + if (error.error != QJsonParseError::NoError) + return engine->throwSyntaxError(QStringLiteral("JSON.parse: Parse error")); + + m_parsedDocument.set(scope.engine, jsonObject); + } + + return m_parsedDocument.value(); +} + +QV4::ReturnedValue QQmlXMLHttpRequest::xmlResponseBody(QV4::ExecutionEngine* engine) +{ + if (m_parsedDocument.isEmpty()) { + m_parsedDocument.set(engine, Document::load(engine, rawResponseBody())); + } + + return m_parsedDocument.value(); +} #ifndef QT_NO_TEXTCODEC QTextCodec* QQmlXMLHttpRequest::findTextCodec() const @@ -1519,57 +1534,38 @@ const QByteArray &QQmlXMLHttpRequest::rawResponseBody() const return m_responseEntityBody; } -void QQmlXMLHttpRequest::dispatchCallbackImpl(const Value &me) +void QQmlXMLHttpRequest::dispatchCallback(Object *thisObj, QQmlContextData *context) { - QV4::Scope scope(v4); - ScopedObject o(scope, me); - if (!o) { - v4->throwError(QStringLiteral("QQmlXMLHttpRequest: internal error: empty ThisObject")); - return; - } + Q_ASSERT(thisObj); - ScopedString s(scope, v4->newString(QStringLiteral("ThisObject"))); - ScopedObject thisObj(scope, o->get(s)); - if (!thisObj) { - v4->throwError(QStringLiteral("QQmlXMLHttpRequest: internal error: empty ThisObject")); + if (!context) + // if the calling context object is no longer valid, then it has been + // deleted explicitly (e.g., by a Loader deleting the itemContext when + // the source is changed). We do nothing in this case, as the evaluation + // cannot succeed. return; - } - s = v4->newString(QStringLiteral("onreadystatechange")); + QV4::Scope scope(thisObj->engine()); + ScopedString s(scope, scope.engine->newString(QStringLiteral("onreadystatechange"))); ScopedFunctionObject callback(scope, thisObj->get(s)); if (!callback) { // not an error, but no onreadystatechange function to call. return; } - s = v4->newString(QStringLiteral("ActivationObject")); - ScopedObject activationObject(scope, o->get(s)); - if (!activationObject) { - v4->throwError(QStringLiteral("QQmlXMLHttpRequest: internal error: empty ActivationObject")); - return; - } + QV4::ScopedCallData callData(scope); + callData->thisObject = Encode::undefined(); + callback->call(callData); - QQmlContextData *callingContext = QmlContextWrapper::getContext(activationObject); - if (callingContext) { - QV4::ScopedCallData callData(scope); - callData->thisObject = activationObject.asReturnedValue(); - callback->call(callData); + if (scope.engine->hasException) { + QQmlError error = scope.engine->catchExceptionAsQmlError(); + QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error); } - - // if the callingContext object is no longer valid, then it has been - // deleted explicitly (e.g., by a Loader deleting the itemContext when - // the source is changed). We do nothing in this case, as the evaluation - // cannot succeed. - } -void QQmlXMLHttpRequest::dispatchCallback(const Value &me) +void QQmlXMLHttpRequest::dispatchCallback() { - dispatchCallbackImpl(me); - if (v4->hasException) { - QQmlError error = v4->catchExceptionAsQmlError(); - QQmlEnginePrivate::warning(QQmlEnginePrivate::get(v4->qmlEngine()), error); - } + dispatchCallback(m_thisObject.as<Object>(), m_qmlContext.contextData()); } void QQmlXMLHttpRequest::destroyNetwork() @@ -1585,7 +1581,7 @@ namespace QV4 { namespace Heap { struct QQmlXMLHttpRequestWrapper : Object { - QQmlXMLHttpRequestWrapper(ExecutionEngine *engine, QQmlXMLHttpRequest *request); + QQmlXMLHttpRequestWrapper(QQmlXMLHttpRequest *request); ~QQmlXMLHttpRequestWrapper() { delete request; } @@ -1595,7 +1591,7 @@ struct QQmlXMLHttpRequestWrapper : Object { struct QQmlXMLHttpRequestCtor : FunctionObject { QQmlXMLHttpRequestCtor(ExecutionEngine *engine); - Object *proto; + Pointer<Object> proto; }; } @@ -1606,9 +1602,8 @@ struct QQmlXMLHttpRequestWrapper : public Object V4_NEEDS_DESTROY }; -Heap::QQmlXMLHttpRequestWrapper::QQmlXMLHttpRequestWrapper(ExecutionEngine *engine, QQmlXMLHttpRequest *request) - : Heap::Object(engine) - , request(request) +Heap::QQmlXMLHttpRequestWrapper::QQmlXMLHttpRequestWrapper(QQmlXMLHttpRequest *request) + : request(request) { } @@ -1621,21 +1616,21 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject c->proto->mark(e); FunctionObject::markObjects(that, e); } - static ReturnedValue construct(Managed *that, QV4::CallData *) + static ReturnedValue construct(const Managed *that, QV4::CallData *) { - Scope scope(static_cast<QQmlXMLHttpRequestCtor *>(that)->engine()); + Scope scope(static_cast<const QQmlXMLHttpRequestCtor *>(that)->engine()); Scoped<QQmlXMLHttpRequestCtor> ctor(scope, that->as<QQmlXMLHttpRequestCtor>()); if (!ctor) return scope.engine->throwTypeError(); - QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine, scope.engine->v8Engine->networkAccessManager()); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->alloc<QQmlXMLHttpRequestWrapper>(scope.engine, r)); + QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager()); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocObject<QQmlXMLHttpRequestWrapper>(r)); ScopedObject proto(scope, ctor->d()->proto); w->setPrototype(proto); return w.asReturnedValue(); } - static ReturnedValue call(Managed *, QV4::CallData *) { + static ReturnedValue call(const Managed *, QV4::CallData *) { return Primitive::undefinedValue().asReturnedValue(); } @@ -1675,7 +1670,7 @@ Heap::QQmlXMLHttpRequestCtor::QQmlXMLHttpRequestCtor(ExecutionEngine *engine) ctor->defineReadonlyProperty(QStringLiteral("DONE"), Primitive::fromInt32(4)); if (!ctor->d()->proto) ctor->setupProto(); - ScopedString s(scope, engine->id_prototype); + ScopedString s(scope, engine->id_prototype()); ctor->defineDefaultProperty(s, ScopedObject(scope, ctor->d()->proto)); } @@ -1735,14 +1730,15 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(CallContext *ctx) method != QLatin1String("HEAD") && method != QLatin1String("POST") && method != QLatin1String("DELETE") && - method != QLatin1String("OPTIONS")) + method != QLatin1String("OPTIONS") && + method != QLatin1String("PROPFIND")) V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Unsupported HTTP method type"); // Argument 1 - URL QUrl url = QUrl(ctx->args()[1].toQStringNoThrow()); if (url.isRelative()) - url = scope.engine->v8Engine->callingContext()->resolvedUrl(url); + url = scope.engine->callingQmlContext()->resolvedUrl(url); bool async = true; // Argument 2 - async (optional) @@ -1764,8 +1760,7 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(CallContext *ctx) if (!username.isNull()) url.setUserName(username); if (!password.isNull()) url.setPassword(password); - ScopedValue meObject(scope, constructMeObject(ctx->thisObject(), scope.engine)); - return r->open(meObject, method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad); + return r->open(w, scope.engine->callingQmlContext(), method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad); } ReturnedValue QQmlXMLHttpRequestCtor::method_setRequestHeader(CallContext *ctx) @@ -1831,8 +1826,7 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_send(CallContext *ctx) if (ctx->argc() > 0) data = ctx->args()[0].toQStringNoThrow().toUtf8(); - ScopedValue meObject(scope, constructMeObject(ctx->thisObject(), scope.engine)); - return r->send(meObject, data); + return r->send(w, scope.engine->callingQmlContext(), data); } ReturnedValue QQmlXMLHttpRequestCtor::method_abort(CallContext *ctx) @@ -1843,8 +1837,7 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_abort(CallContext *ctx) V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - ScopedValue meObject(scope, constructMeObject(ctx->thisObject(), scope.engine)); - return r->abort(meObject); + return r->abort(w, scope.engine->callingQmlContext()); } ReturnedValue QQmlXMLHttpRequestCtor::method_getResponseHeader(CallContext *ctx) @@ -1961,7 +1954,9 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseXML(CallContext *ctx) r->readyState() != QQmlXMLHttpRequest::Done)) { return Encode::null(); } else { - return Document::load(scope.engine, r->rawResponseBody()); + if (r->responseType().isEmpty()) + r->setResponseType(QLatin1String("document")); + return r->xmlResponseBody(scope.engine); } } @@ -1982,6 +1977,10 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_get_response(CallContext *ctx) return QV4::Encode(scope.engine->newString(r->responseBody())); } else if (responseType.compare(QLatin1String("arraybuffer"), Qt::CaseInsensitive) == 0) { return QV4::Encode(scope.engine->newArrayBuffer(r->rawResponseBody())); + } else if (responseType.compare(QLatin1String("json"), Qt::CaseInsensitive) == 0) { + return r->jsonResponseBody(scope.engine); + } else if (responseType.compare(QLatin1String("document"), Qt::CaseInsensitive) == 0) { + return r->xmlResponseBody(scope.engine); } else { return QV4::Encode(scope.engine->newString(QString())); } @@ -2027,9 +2026,9 @@ void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4) { Scope scope(v4); - Scoped<QQmlXMLHttpRequestCtor> ctor(scope, v4->memoryManager->alloc<QQmlXMLHttpRequestCtor>(v4)); + Scoped<QQmlXMLHttpRequestCtor> ctor(scope, v4->memoryManager->allocObject<QQmlXMLHttpRequestCtor>(v4)); ScopedString s(scope, v4->newString(QStringLiteral("XMLHttpRequest"))); - v4->globalObject()->defineReadonlyProperty(s, ctor); + v4->globalObject->defineReadonlyProperty(s, ctor); QQmlXMLHttpRequestData *data = new QQmlXMLHttpRequestData; return data; diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index f53b9a0c7d..f4e980eefb 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -41,7 +41,8 @@ #include <private/qv8engine_p.h> #include <QFileInfo> -#include <private/qqmlprofilerservice_p.h> +#include <private/qqmldebugconnector_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> #include <private/qqmlglobal_p.h> #include <private/qqmlplatform_p.h> @@ -51,6 +52,7 @@ #include <private/qv4include_p.h> #include <private/qv4context_p.h> #include <private/qv4stringobject_p.h> +#include <private/qv4dateobject_p.h> #include <private/qv4mm_p.h> #include <private/qv4jsonobject_p.h> #include <private/qv4objectproto_p.h> @@ -78,10 +80,9 @@ struct StaticQtMetaObject : public QObject { return &staticQtMetaObject; } }; -Heap::QtObject::QtObject(ExecutionEngine *v4, QQmlEngine *qmlEngine) - : Heap::Object(v4) +Heap::QtObject::QtObject(QQmlEngine *qmlEngine) { - Scope scope(v4); + Scope scope(internalClass->engine); ScopedObject o(scope, this); // Set all the enums from the "Qt" namespace @@ -91,11 +92,11 @@ Heap::QtObject::QtObject(ExecutionEngine *v4, QQmlEngine *qmlEngine) for (int ii = 0; ii < qtMetaObject->enumeratorCount(); ++ii) { QMetaEnum enumerator = qtMetaObject->enumerator(ii); for (int jj = 0; jj < enumerator.keyCount(); ++jj) { - o->put((str = v4->newString(QString::fromUtf8(enumerator.key(jj)))), (v = QV4::Primitive::fromInt32(enumerator.value(jj)))); + o->put((str = scope.engine->newString(QString::fromUtf8(enumerator.key(jj)))), (v = QV4::Primitive::fromInt32(enumerator.value(jj)))); } } - o->put((str = v4->newString(QStringLiteral("Asynchronous"))), (v = QV4::Primitive::fromInt32(0))); - o->put((str = v4->newString(QStringLiteral("Synchronous"))), (v = QV4::Primitive::fromInt32(1))); + o->put((str = scope.engine->newString(QStringLiteral("Asynchronous"))), (v = QV4::Primitive::fromInt32(0))); + o->put((str = scope.engine->newString(QStringLiteral("Synchronous"))), (v = QV4::Primitive::fromInt32(1))); o->defineDefaultProperty(QStringLiteral("include"), QV4Include::method_include); o->defineDefaultProperty(QStringLiteral("isQtObject"), QV4::QtObject::method_isQtObject); @@ -433,11 +434,16 @@ Returns a Matrix4x4 with the specified values. Alternatively, the function may be called with a single argument where that argument is a JavaScript array which contains the sixteen matrix values. +Finally, the function may be called with no arguments and the resulting +matrix will be the identity matrix. */ ReturnedValue QtObject::method_matrix4x4(QV4::CallContext *ctx) { QV4::ExecutionEngine *v4 = ctx->d()->engine; + if (ctx->argc() == 0) + return ctx->engine()->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QMatrix4x4, 0, Q_NULLPTR)); + if (ctx->argc() == 1 && ctx->args()[0].isObject()) { bool ok = false; QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, QQmlV4Handle(ctx->args()[0]), v4, &ok); @@ -669,7 +675,7 @@ ReturnedValue QtObject::method_formatTime(QV4::CallContext *ctx) QVariant argVariant = ctx->engine()->toVariant(ctx->args()[0], -1); QTime time; - if (ctx->args()[0].asDateObject() || (argVariant.type() == QVariant::String)) + if (ctx->args()[0].as<DateObject>() || (argVariant.type() == QVariant::String)) time = argVariant.toDateTime().time(); else // if (argVariant.type() == QVariant::Time), or invalid. time = argVariant.toTime(); @@ -839,21 +845,21 @@ ReturnedValue QtObject::method_openUrlExternally(QV4::CallContext *ctx) */ ReturnedValue QtObject::method_resolvedUrl(QV4::CallContext *ctx) { - QV8Engine *v8engine = ctx->d()->engine->v8Engine; + ExecutionEngine *v4 = ctx->engine(); - QUrl url = ctx->engine()->toVariant(ctx->args()[0], -1).toUrl(); - QQmlEngine *e = v8engine->engine(); + QUrl url = v4->toVariant(ctx->args()[0], -1).toUrl(); + QQmlEngine *e = v4->qmlEngine(); QQmlEnginePrivate *p = 0; if (e) p = QQmlEnginePrivate::get(e); if (p) { - QQmlContextData *ctxt = v8engine->callingContext(); + QQmlContextData *ctxt = v4->callingQmlContext(); if (ctxt) - return ctx->d()->engine->newString(ctxt->resolvedUrl(url).toString())->asReturnedValue(); + return v4->newString(ctxt->resolvedUrl(url).toString())->asReturnedValue(); else - return ctx->d()->engine->newString(url.toString())->asReturnedValue(); + return v4->newString(url.toString())->asReturnedValue(); } - return ctx->d()->engine->newString(e->baseUrl().resolved(url).toString())->asReturnedValue(); + return v4->newString(e->baseUrl().resolved(url).toString())->asReturnedValue(); } /*! @@ -983,7 +989,7 @@ ReturnedValue QtObject::method_createQmlObject(CallContext *ctx) QV8Engine *v8engine = ctx->d()->engine->v8Engine; QQmlEngine *engine = v8engine->engine(); - QQmlContextData *context = v8engine->callingContext(); + QQmlContextData *context = scope.engine->callingQmlContext(); Q_ASSERT(context); QQmlContext *effectiveContext = 0; if (context->isPragmaLibraryContext) @@ -1012,8 +1018,13 @@ ReturnedValue QtObject::method_createQmlObject(CallContext *ctx) if (!parentArg) V4THROW_ERROR("Qt.createQmlObject(): Missing parent object"); + QQmlTypeData *typeData = QQmlEnginePrivate::get(engine)->typeLoader.getType( + qml.toUtf8(), url, QQmlTypeLoader::Synchronous); + Q_ASSERT(typeData->isCompleteOrError()); QQmlComponent component(engine); - component.setData(qml.toUtf8(), url); + QQmlComponentPrivate *componentPrivate = QQmlComponentPrivate::get(&component); + componentPrivate->fromTypeData(typeData); + componentPrivate->progress = 1.0; if (component.isError()) { ScopedValue v(scope, Error::create(ctx->d()->engine, component.errors())); @@ -1063,10 +1074,23 @@ If the optional \a mode parameter is set to \c Component.Asynchronous, the component will be loaded in a background thread. The Component::status property will be \c Component.Loading while it is loading. The status will change to \c Component.Ready if the component loads successfully, or \c Component.Error -if loading fails. +if loading fails. This parameter defaults to \c Component.PreferSynchronous +if omitted. + +If \a mode is set to \c Component.PreferSynchronous, Qt will attempt to load +the component synchronously, but may end up loading it asynchronously if +necessary. Scenarios that may cause asynchronous loading include, but are not +limited to, the following: + +\list +\li The URL refers to a network resource +\li The component is being created as a result of another component that is +being loaded asynchronously +\endlist If the optional \a parent parameter is given, it should refer to the object -that will become the parent for the created \l Component object. +that will become the parent for the created \l Component object. If no mode +was passed, this can be the second argument. Call \l {Component::createObject()}{Component.createObject()} on the returned component to create an object instance of the component. @@ -1090,7 +1114,7 @@ ReturnedValue QtObject::method_createComponent(CallContext *ctx) QV8Engine *v8engine = ctx->d()->engine->v8Engine; QQmlEngine *engine = v8engine->engine(); - QQmlContextData *context = v8engine->callingContext(); + QQmlContextData *context = scope.engine->callingQmlContext(); Q_ASSERT(context); QQmlContextData *effectiveContext = context; if (context->isPragmaLibraryContext) @@ -1178,7 +1202,7 @@ ReturnedValue QtObject::method_locale(CallContext *ctx) return QQmlLocale::locale(ctx->engine(), code); } -Heap::QQmlBindingFunction::QQmlBindingFunction(QV4::FunctionObject *originalFunction) +Heap::QQmlBindingFunction::QQmlBindingFunction(const QV4::FunctionObject *originalFunction) : QV4::Heap::FunctionObject(originalFunction->scope(), originalFunction->name()) , originalFunction(originalFunction->d()) { @@ -1191,10 +1215,10 @@ void QQmlBindingFunction::initBindingLocation() d()->bindingLocation.line = frame.line; } -ReturnedValue QQmlBindingFunction::call(Managed *that, CallData *callData) +ReturnedValue QQmlBindingFunction::call(const Managed *that, CallData *callData) { - Scope scope(static_cast<QQmlBindingFunction*>(that)->engine()); - ScopedFunctionObject function(scope, static_cast<QQmlBindingFunction*>(that)->d()->originalFunction); + Scope scope(static_cast<const QQmlBindingFunction*>(that)->engine()); + ScopedFunctionObject function(scope, static_cast<const QQmlBindingFunction*>(that)->d()->originalFunction); return function->call(callData); } @@ -1256,18 +1280,18 @@ ReturnedValue QtObject::method_binding(CallContext *ctx) { if (ctx->argc() != 1) V4THROW_ERROR("binding() requires 1 argument"); - QV4::FunctionObject *f = ctx->args()[0].asFunctionObject(); + const QV4::FunctionObject *f = ctx->args()[0].as<FunctionObject>(); if (!f) V4THROW_TYPE("binding(): argument (binding expression) must be a function"); - return (ctx->d()->engine->memoryManager->alloc<QQmlBindingFunction>(f))->asReturnedValue(); + return (ctx->d()->engine->memoryManager->allocObject<QQmlBindingFunction>(f))->asReturnedValue(); } ReturnedValue QtObject::method_get_platform(CallContext *ctx) { // ### inefficient. Should be just a value based getter - Object *o = ctx->thisObject().asObject(); + Object *o = ctx->thisObject().as<Object>(); if (!o) return ctx->engine()->throwTypeError(); QtObject *qt = o->as<QtObject>(); @@ -1284,7 +1308,7 @@ ReturnedValue QtObject::method_get_platform(CallContext *ctx) ReturnedValue QtObject::method_get_application(CallContext *ctx) { // ### inefficient. Should be just a value based getter - Object *o = ctx->thisObject().asObject(); + Object *o = ctx->thisObject().as<Object>(); if (!o) return ctx->engine()->throwTypeError(); QtObject *qt = o->as<QtObject>(); @@ -1313,10 +1337,9 @@ ReturnedValue QtObject::method_get_styleHints(CallContext *ctx) } -QV4::Heap::ConsoleObject::ConsoleObject(ExecutionEngine *v4) - : Heap::Object(v4) +QV4::Heap::ConsoleObject::ConsoleObject() { - QV4::Scope scope(v4); + QV4::Scope scope(internalClass->engine); QV4::ScopedObject o(scope, this); o->defineDefaultProperty(QStringLiteral("debug"), QV4::ConsoleObject::method_log); @@ -1353,12 +1376,12 @@ static QString jsStack(QV4::ExecutionEngine *engine) { QString stackFrame; if (frame.column >= 0) - stackFrame = QString::fromLatin1("%1 (%2:%3:%4)").arg(frame.function, + stackFrame = QStringLiteral("%1 (%2:%3:%4)").arg(frame.function, frame.source, QString::number(frame.line), QString::number(frame.column)); else - stackFrame = QString::fromLatin1("%1 (%2:%3)").arg(frame.function, + stackFrame = QStringLiteral("%1 (%2:%3)").arg(frame.function, frame.source, QString::number(frame.line)); @@ -1379,38 +1402,41 @@ static QV4::ReturnedValue writeToConsole(ConsoleLogTypes logType, CallContext *c if (i != 0) result.append(QLatin1Char(' ')); - if (ctx->args()[i].asArrayObject()) - result.append(QStringLiteral("[") + ctx->args()[i].toQStringNoThrow() + QStringLiteral("]")); + if (ctx->args()[i].as<ArrayObject>()) + result.append(QLatin1Char('[') + ctx->args()[i].toQStringNoThrow() + QLatin1Char(']')); else result.append(ctx->args()[i].toQStringNoThrow()); } if (printStack) { - result.append(QLatin1String("\n")); + result.append(QLatin1Char('\n')); result.append(jsStack(v4)); } - static QLoggingCategory loggingCategory("qml"); + static QLoggingCategory qmlLoggingCategory("qml"); + static QLoggingCategory jsLoggingCategory("js"); + + QLoggingCategory *loggingCategory = v4->qmlEngine() ? &qmlLoggingCategory : &jsLoggingCategory; QV4::StackFrame frame = v4->currentStackFrame(); const QByteArray baSource = frame.source.toUtf8(); const QByteArray baFunction = frame.function.toUtf8(); - QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData(), loggingCategory.categoryName()); + QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData(), loggingCategory->categoryName()); switch (logType) { case Log: - if (loggingCategory.isDebugEnabled()) + if (loggingCategory->isDebugEnabled()) logger.debug("%s", result.toUtf8().constData()); break; case Info: - if (loggingCategory.isInfoEnabled()) + if (loggingCategory->isInfoEnabled()) logger.info("%s", result.toUtf8().constData()); break; case Warn: - if (loggingCategory.isWarningEnabled()) + if (loggingCategory->isWarningEnabled()) logger.warning("%s", result.toUtf8().constData()); break; case Error: - if (loggingCategory.isCriticalEnabled()) + if (loggingCategory->isCriticalEnabled()) logger.critical("%s", result.toUtf8().constData()); break; default: @@ -1442,14 +1468,18 @@ QV4::ReturnedValue ConsoleObject::method_profile(CallContext *ctx) { QV4::ExecutionEngine *v4 = ctx->d()->engine; + if (!v4->qmlEngine()) + return QV4::Encode::undefined(); // Not yet implemented for JavaScript. + QV4::StackFrame frame = v4->currentStackFrame(); const QByteArray baSource = frame.source.toUtf8(); const QByteArray baFunction = frame.function.toUtf8(); QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData()); - if (!QQmlDebugService::isDebuggingEnabled()) { + QQmlProfilerService *service = QQmlDebugConnector::service<QQmlProfilerService>(); + if (!service) { logger.warning("Cannot start profiling because debug service is disabled. Start with -qmljsdebugger=port:XXXXX."); } else { - QQmlProfilerService::instance()->startProfiling(v4->qmlEngine()); + service->startProfiling(v4->qmlEngine()); logger.debug("Profiling started."); } @@ -1460,15 +1490,19 @@ QV4::ReturnedValue ConsoleObject::method_profileEnd(CallContext *ctx) { QV4::ExecutionEngine *v4 = ctx->d()->engine; + if (!v4->qmlEngine()) + return QV4::Encode::undefined(); // Not yet implemented for JavaScript. + QV4::StackFrame frame = v4->currentStackFrame(); const QByteArray baSource = frame.source.toUtf8(); const QByteArray baFunction = frame.function.toUtf8(); QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData()); - if (!QQmlDebugService::isDebuggingEnabled()) { + QQmlProfilerService *service = QQmlDebugConnector::service<QQmlProfilerService>(); + if (!service) { logger.warning("Ignoring console.profileEnd(): the debug service is disabled."); } else { - QQmlProfilerService::instance()->stopProfiling(v4->qmlEngine()); + service->stopProfiling(v4->qmlEngine()); logger.debug("Profiling ended."); } @@ -1588,31 +1622,36 @@ QV4::ReturnedValue ConsoleObject::method_exception(CallContext *ctx) -void QV4::GlobalExtensions::init(QQmlEngine *qmlEngine, Object *globalObject) +void QV4::GlobalExtensions::init(Object *globalObject, QJSEngine::Extensions extensions) { ExecutionEngine *v4 = globalObject->engine(); Scope scope(v4); -#ifndef QT_NO_TRANSLATION - globalObject->defineDefaultProperty(QStringLiteral("qsTranslate"), method_qsTranslate); - globalObject->defineDefaultProperty(QStringLiteral("QT_TRANSLATE_NOOP"), method_qsTranslateNoOp); - globalObject->defineDefaultProperty(QStringLiteral("qsTr"), method_qsTr); - globalObject->defineDefaultProperty(QStringLiteral("QT_TR_NOOP"), method_qsTrNoOp); - globalObject->defineDefaultProperty(QStringLiteral("qsTrId"), method_qsTrId); - globalObject->defineDefaultProperty(QStringLiteral("QT_TRID_NOOP"), method_qsTrIdNoOp); -#endif + if (extensions.testFlag(QJSEngine::TranslationExtension)) { + #ifndef QT_NO_TRANSLATION + globalObject->defineDefaultProperty(QStringLiteral("qsTranslate"), QV4::GlobalExtensions::method_qsTranslate); + globalObject->defineDefaultProperty(QStringLiteral("QT_TRANSLATE_NOOP"), QV4::GlobalExtensions::method_qsTranslateNoOp); + globalObject->defineDefaultProperty(QStringLiteral("qsTr"), QV4::GlobalExtensions::method_qsTr); + globalObject->defineDefaultProperty(QStringLiteral("QT_TR_NOOP"), QV4::GlobalExtensions::method_qsTrNoOp); + globalObject->defineDefaultProperty(QStringLiteral("qsTrId"), QV4::GlobalExtensions::method_qsTrId); + globalObject->defineDefaultProperty(QStringLiteral("QT_TRID_NOOP"), QV4::GlobalExtensions::method_qsTrIdNoOp); + + // string prototype extension + scope.engine->stringPrototype()->defineDefaultProperty(QStringLiteral("arg"), QV4::GlobalExtensions::method_string_arg); + #endif + } - globalObject->defineDefaultProperty(QStringLiteral("print"), ConsoleObject::method_log); - globalObject->defineDefaultProperty(QStringLiteral("gc"), method_gc); + if (extensions.testFlag(QJSEngine::ConsoleExtension)) { + globalObject->defineDefaultProperty(QStringLiteral("print"), QV4::ConsoleObject::method_log); - ScopedObject console(scope, v4->memoryManager->alloc<QV4::ConsoleObject>(v4)); - globalObject->defineDefaultProperty(QStringLiteral("console"), console); - ScopedObject qt(scope, v4->memoryManager->alloc<QV4::QtObject>(v4, qmlEngine)); - globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt); + QV4::ScopedObject console(scope, globalObject->engine()->memoryManager->allocObject<QV4::ConsoleObject>()); + globalObject->defineDefaultProperty(QStringLiteral("console"), console); + } - // string prototype extension - v4->stringPrototype.asObject()->defineDefaultProperty(QStringLiteral("arg"), method_string_arg); + if (extensions.testFlag(QJSEngine::GarbageCollectionExtension)) { + globalObject->defineDefaultProperty(QStringLiteral("gc"), QV4::GlobalExtensions::method_gc); + } } @@ -1726,18 +1765,17 @@ ReturnedValue GlobalExtensions::method_qsTr(CallContext *ctx) V4THROW_ERROR("qsTr(): third argument (n) must be a number"); Scope scope(ctx); - QV8Engine *v8engine = ctx->d()->engine->v8Engine; QString context; - if (QQmlContextData *ctxt = v8engine->callingContext()) { + if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) { QString path = ctxt->urlString(); int lastSlash = path.lastIndexOf(QLatin1Char('/')); int lastDot = path.lastIndexOf(QLatin1Char('.')); int length = lastDot - (lastSlash + 1); context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString(); - } else if (ctx->d()->parent) { - ScopedContext parentCtx(scope, ctx->d()->parent); + } else { + ExecutionContext *parentCtx = scope.engine->parentContext(ctx); // The first non-empty source URL in the call stack determines the translation context. - while (parentCtx && context.isEmpty()) { + while (!!parentCtx && context.isEmpty()) { if (QV4::CompiledData::CompilationUnit *unit = parentCtx->d()->compilationUnit) { QString fileName = unit->fileName(); QUrl url(unit->fileName()); @@ -1750,7 +1788,7 @@ ReturnedValue GlobalExtensions::method_qsTr(CallContext *ctx) } context = QFileInfo(context).baseName(); } - parentCtx = parentCtx->d()->parent; + parentCtx = scope.engine->parentContext(parentCtx); } } diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index b78375118b..d373fb6ee6 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -47,6 +47,7 @@ #include <private/qqmlglobal_p.h> #include <private/qv4functionobject_p.h> +#include <private/qjsengine_p.h> QT_BEGIN_NAMESPACE @@ -58,18 +59,18 @@ namespace QV4 { namespace Heap { struct QtObject : Object { - QtObject(ExecutionEngine *v4, QQmlEngine *qmlEngine); + QtObject(QQmlEngine *qmlEngine); QObject *platform; QObject *application; }; struct ConsoleObject : Object { - ConsoleObject(ExecutionEngine *engine); + ConsoleObject(); }; struct QQmlBindingFunction : FunctionObject { - QQmlBindingFunction(QV4::FunctionObject *originalFunction); - FunctionObject *originalFunction; + QQmlBindingFunction(const QV4::FunctionObject *originalFunction); + Pointer<FunctionObject> originalFunction; // Set when the binding is created later QQmlSourceLocation bindingLocation; }; @@ -142,7 +143,7 @@ struct ConsoleObject : Object }; struct GlobalExtensions { - static void init(QQmlEngine *qmlEngine, Object *globalObject); + static void init(Object *globalObject, QJSEngine::Extensions extensions); #ifndef QT_NO_TRANSLATION static ReturnedValue method_qsTranslate(CallContext *ctx); @@ -166,7 +167,7 @@ struct QQmlBindingFunction : public QV4::FunctionObject void initBindingLocation(); // from caller stack trace - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; diff --git a/src/qml/qml/v8/qv4domerrors.cpp b/src/qml/qml/v8/qv4domerrors.cpp index c318e2e550..1baaa113aa 100644 --- a/src/qml/qml/v8/qv4domerrors.cpp +++ b/src/qml/qml/v8/qv4domerrors.cpp @@ -59,7 +59,7 @@ void qt_add_domexceptions(ExecutionEngine *e) domexception->defineReadonlyProperty(QStringLiteral("INVALID_ACCESS_ERR"), Primitive::fromInt32(DOMEXCEPTION_INVALID_ACCESS_ERR)); domexception->defineReadonlyProperty(QStringLiteral("VALIDATION_ERR"), Primitive::fromInt32(DOMEXCEPTION_VALIDATION_ERR)); domexception->defineReadonlyProperty(QStringLiteral("TYPE_MISMATCH_ERR"), Primitive::fromInt32(DOMEXCEPTION_TYPE_MISMATCH_ERR)); - e->globalObject()->defineDefaultProperty(QStringLiteral("DOMException"), domexception); + e->globalObject->defineDefaultProperty(QStringLiteral("DOMException"), domexception); } QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv4sqlerrors.cpp b/src/qml/qml/v8/qv4sqlerrors.cpp index b7a5b71540..c61e57560d 100644 --- a/src/qml/qml/v8/qv4sqlerrors.cpp +++ b/src/qml/qml/v8/qv4sqlerrors.cpp @@ -51,7 +51,7 @@ void qt_add_sqlexceptions(QV4::ExecutionEngine *engine) sqlexception->defineReadonlyProperty(QStringLiteral("SYNTAX_ERR"), Primitive::fromInt32(SQLEXCEPTION_SYNTAX_ERR)); sqlexception->defineReadonlyProperty(QStringLiteral("CONSTRAINT_ERR"), Primitive::fromInt32(SQLEXCEPTION_CONSTRAINT_ERR)); sqlexception->defineReadonlyProperty(QStringLiteral("TIMEOUT_ERR"), Primitive::fromInt32(SQLEXCEPTION_TIMEOUT_ERR)); - engine->globalObject()->defineDefaultProperty(QStringLiteral("SQLException"), sqlexception); + engine->globalObject->defineDefaultProperty(QStringLiteral("SQLException"), sqlexception); } QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index a7c63c9df1..dae932e705 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -61,7 +61,7 @@ #include <QtCore/qdatastream.h> #include <private/qsimd_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4dateobject_p.h> #include <private/qv4objectiterator_p.h> #include <private/qv4mm_p.h> @@ -171,15 +171,13 @@ const QSet<QString> &QV8Engine::illegalNames() const return m_illegalNames; } -QQmlContextData *QV8Engine::callingContext() -{ - return QV4::QmlContextWrapper::callingContext(m_v4Engine); -} - void QV8Engine::initializeGlobal() { QV4::Scope scope(m_v4Engine); - QV4::GlobalExtensions::init(m_engine, m_v4Engine->globalObject()); + QV4::GlobalExtensions::init(m_v4Engine->globalObject, QJSEngine::AllExtensions); + + QV4::ScopedObject qt(scope, m_v4Engine->memoryManager->allocObject<QV4::QtObject>(m_engine)); + m_v4Engine->globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt); QQmlLocale::registerStringLocaleCompare(m_v4Engine); QQmlDateExtension::registerExtension(m_v4Engine); @@ -191,46 +189,52 @@ void QV8Engine::initializeGlobal() qt_add_sqlexceptions(m_v4Engine); { - for (uint i = 0; i < m_v4Engine->globalObject()->internalClass()->size; ++i) { - if (m_v4Engine->globalObject()->internalClass()->nameMap.at(i)) - m_illegalNames.insert(m_v4Engine->globalObject()->internalClass()->nameMap.at(i)->string); + for (uint i = 0; i < m_v4Engine->globalObject->internalClass()->size; ++i) { + if (m_v4Engine->globalObject->internalClass()->nameMap.at(i)) + m_illegalNames.insert(m_v4Engine->globalObject->internalClass()->nameMap.at(i)->string); } } +} - { -#define FREEZE_SOURCE "(function freeze_recur(obj) { "\ - " if (Qt.isQtObject(obj)) return;"\ - " if (obj != Function.connect && obj != Function.disconnect && "\ - " obj instanceof Object) {"\ - " var properties = Object.getOwnPropertyNames(obj);"\ - " for (var prop in properties) { "\ - " if (prop == \"connect\" || prop == \"disconnect\") {"\ - " Object.freeze(obj[prop]); "\ - " continue;"\ - " }"\ - " freeze_recur(obj[prop]);"\ - " }"\ - " }"\ - " if (obj instanceof Object) {"\ - " Object.freeze(obj);"\ - " }"\ - "})" - - QV4::ScopedFunctionObject result(scope, QV4::Script::evaluate(m_v4Engine, QString::fromUtf8(FREEZE_SOURCE), 0)); - Q_ASSERT(!!result); - m_freezeObject.set(scope.engine, result); -#undef FREEZE_SOURCE +static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object) +{ + if (object->as<QV4::QObjectWrapper>()) + return; + + QV4::Scope scope(v4); + + bool instanceOfObject = false; + QV4::ScopedObject p(scope, object->prototype()); + while (p) { + if (p->d() == v4->objectPrototype()->d()) { + instanceOfObject = true; + break; + } + p = p->prototype(); + } + if (!instanceOfObject) + return; + + QV4::InternalClass *frozen = object->internalClass()->propertiesFrozen(); + if (object->internalClass() == frozen) + return; + object->setInternalClass(frozen); + + QV4::ScopedObject o(scope); + for (uint i = 0; i < frozen->size; ++i) { + if (!frozen->nameMap.at(i)) + continue; + o = *object->propertyData(i); + if (o) + freeze_recursive(v4, o); } } void QV8Engine::freezeObject(const QV4::Value &value) { QV4::Scope scope(m_v4Engine); - QV4::ScopedFunctionObject f(scope, m_freezeObject.value()); - QV4::ScopedCallData callData(scope, 1); - callData->args[0] = value; - callData->thisObject = m_v4Engine->globalObject(); - f->call(callData); + QV4::ScopedObject o(scope, value); + freeze_recursive(m_v4Engine, o); } struct QV8EngineRegistrationData @@ -266,9 +270,7 @@ void QV8Engine::setExtensionData(int index, Deletable *data) void QV8Engine::initQmlGlobalObject() { initializeGlobal(); - QV4::Scope scope(m_v4Engine); - QV4::ScopedValue v(scope, m_v4Engine->globalObject()); - freezeObject(v); + freezeObject(*m_v4Engine->globalObject); } void QV8Engine::setEngine(QQmlEngine *engine) @@ -279,7 +281,7 @@ void QV8Engine::setEngine(QQmlEngine *engine) QV4::ReturnedValue QV8Engine::global() { - return m_v4Engine->globalObject()->asReturnedValue(); + return m_v4Engine->globalObject->asReturnedValue(); } void QV8Engine::startTimer(const QString &timerName) diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index fb538772d1..552470c88c 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -60,9 +60,10 @@ #include <private/qqmlpropertycache_p.h> #include <private/qv4qobjectwrapper_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4object_p.h> #include <private/qv4identifier_p.h> +#include <private/qqmlcontextwrapper_p.h> QT_BEGIN_NAMESPACE @@ -101,26 +102,24 @@ namespace QV4 { return rv; \ } \ -// Used to allow a QObject method take and return raw V8 handles without having to expose -// v8 in the public API. +// Used to allow a QObject method take and return raw V4 handles without having to expose +// 48 in the public API. // Use like this: // class MyClass : public QObject { // Q_OBJECT // ... -// Q_INVOKABLE void myMethod(QQmlV8Function*); +// Q_INVOKABLE void myMethod(QQmlV4Function*); // }; // The QQmlV8Function - and consequently the arguments and return value - only remains // valid during the call. If the return value isn't set within myMethod(), the will return // undefined. class QV8Engine; -// ### GC + class QQmlV4Function { public: int length() const { return callData->argc; } QV4::ReturnedValue operator[](int idx) { return (idx < callData->argc ? callData->args[idx].asReturnedValue() : QV4::Encode::undefined()); } - QQmlContextData *context() { return ctx; } - QV4::ReturnedValue qmlGlobal() { return callData->thisObject.asReturnedValue(); } void setReturnValue(QV4::ReturnedValue rv) { *retVal = rv; } QV4::ExecutionEngine *v4engine() const { return e; } private: @@ -129,16 +128,14 @@ private: QQmlV4Function(const QQmlV4Function &); QQmlV4Function &operator=(const QQmlV4Function &); - QQmlV4Function(QV4::CallData *callData, QV4::Value *retVal, - const QV4::Value &global, QQmlContextData *c, QV4::ExecutionEngine *e) - : callData(callData), retVal(retVal), ctx(c), e(e) + QQmlV4Function(QV4::CallData *callData, QV4::Value *retVal, QV4::ExecutionEngine *e) + : callData(callData), retVal(retVal), e(e) { - callData->thisObject.val = global.asReturnedValue(); + callData->thisObject = QV4::Encode::undefined(); } QV4::CallData *callData; QV4::Value *retVal; - QQmlContextData *ctx; QV4::ExecutionEngine *e; }; @@ -163,8 +160,6 @@ class QQmlContextData; class Q_QML_PRIVATE_EXPORT QV8Engine { friend class QJSEngine; - // ### GC - typedef QSet<QV4::Heap::Object *> V8ObjectSet; public: static QV8Engine* get(QJSEngine* q) { Q_ASSERT(q); return q->handle(); } // static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; } @@ -192,8 +187,6 @@ public: Deletable *listModelData() { return m_listModelData; } void setListModelData(Deletable *d) { if (m_listModelData) delete m_listModelData; m_listModelData = d; } - QQmlContextData *callingContext(); - void freezeObject(const QV4::Value &value); // Return the network access manager for this engine. By default this returns the network @@ -224,8 +217,6 @@ protected: QV4::ExecutionEngine *m_v4Engine; - QV4::PersistentValue m_freezeObject; - void *m_xmlHttpRequestData; QVector<Deletable *> m_extensionData; diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp index 7814fa6d56..45b7edd316 100644 --- a/src/qml/types/qqmlbind.cpp +++ b/src/qml/types/qqmlbind.cpp @@ -36,6 +36,7 @@ #include <private/qqmlnullablevalue_p.h> #include <private/qqmlproperty_p.h> #include <private/qqmlbinding_p.h> +#include <private/qqmlmetatype_p.h> #include <qqmlengine.h> #include <qqmlcontext.h> @@ -52,8 +53,8 @@ QT_BEGIN_NAMESPACE class QQmlBindPrivate : public QObjectPrivate { public: - QQmlBindPrivate() : componentComplete(true), obj(0), prevBind(0) {} - ~QQmlBindPrivate() { if (prevBind) prevBind->destroy(); } + QQmlBindPrivate() : componentComplete(true), obj(0) {} + ~QQmlBindPrivate() { } QQmlNullableValue<bool> when; bool componentComplete; @@ -61,74 +62,82 @@ public: QString propName; QQmlNullableValue<QVariant> value; QQmlProperty prop; - QQmlAbstractBinding *prevBind; + QQmlAbstractBinding::Ptr prevBind; + + void validate(QObject *binding) const; }; +void QQmlBindPrivate::validate(QObject *binding) const +{ + if (!obj) + return; + + if (!prop.isValid()) { + qmlInfo(binding) << "Property '" << propName << "' does not exist on " << QQmlMetaType::prettyTypeName(obj) << "."; + return; + } + + if (!prop.isWritable()) { + qmlInfo(binding) << "Property '" << propName << "' on " << QQmlMetaType::prettyTypeName(obj) << " is read-only."; + return; + } +} /*! \qmltype Binding \instantiates QQmlBind \inqmlmodule QtQml \ingroup qtquick-interceptors - \brief Enables the arbitrary creation of property bindings + \brief Enables the arbitrary creation of property bindings. - \section1 Binding to an Inaccessible Property + In QML, property bindings result in a dependency between the properties of + different objects. - Sometimes it is necessary to bind to a property of an object that wasn't - directly instantiated by QML - generally a property of a class exported - to QML by C++. In these cases, regular property binding doesn't work. Binding - allows you to bind any value to any property. + \section1 Binding to an inaccessible property + + Sometimes it is necessary to bind an object's property to + that of another object that isn't directly instantiated by QML, such as a + property of a class exported to QML by C++. You can use the Binding type + to establish this dependency; binding any value to any object's property. + + For example, in a C++ application that maps an "app.enteredText" property + into QML, you can use Binding to update the enteredText property. - For example, imagine a C++ application that maps an "app.enteredText" - property into QML. You could use Binding to update the enteredText property - like this. \code TextEdit { id: myTextField; text: "Please type here..." } Binding { target: app; property: "enteredText"; value: myTextField.text } \endcode - Whenever the text in the TextEdit is updated, the C++ property will be - updated also. - \section1 "Single-branch" conditional binding + When \c{text} changes, the C++ property \c{enteredText} will update + automatically. - In some circumstances you may want to control the value of a property - only when a certain condition is true (and relinquish control in all - other circumstances). This often isn't possible to accomplish with a direct - binding, as you need to supply values for all possible branches. + \section1 Conditional bindings - \code + In some cases you may want to modify the value of a property when a certain + condition is met but leave it unmodified otherwise. Often, it's not possible + to do this with direct bindings, as you have to supply values for all + possible branches. + + For example, the code snippet below results in a warning whenever you + release the mouse. This is because the value of the binding is undefined + when the mouse isn't pressed. + + \qml // produces warning: "Unable to assign [undefined] to double value" value: if (mouse.pressed) mouse.mouseX - \endcode + \endqml - The above example will produce a warning whenever we release the mouse, as the value - of the binding is undefined when the mouse isn't pressed. We can use the Binding - type to rewrite the above code and avoid the warning. + The Binding type can prevent this warning. - \code + \qml Binding on value { when: mouse.pressed value: mouse.mouseX } - \endcode - - The Binding type will also restore any previously set direct bindings on - the property. In that sense, it functions much like a simplified State. - - \qml - // this is equivalent to the above Binding - State { - name: "pressed" - when: mouse.pressed - PropertyChanges { - target: obj - value: mouse.mouseX - } - } \endqml - If the binding target or binding property is changed, the bound value is - immediately pushed onto the new target. + The Binding type restores any previously set direct bindings on the + property. \sa {Qt QML} */ @@ -195,8 +204,10 @@ void QQmlBind::setObject(QObject *obj) d->when = true; } d->obj = obj; - if (d->componentComplete) + if (d->componentComplete) { d->prop = QQmlProperty(d->obj, d->propName); + d->validate(this); + } eval(); } @@ -204,6 +215,23 @@ void QQmlBind::setObject(QObject *obj) \qmlproperty string QtQml::Binding::property The property to be updated. + + This can be a group property if the expression results in accessing a + property of a \l {QML Basic Types}{value type}. For example: + + \qml + Item { + id: item + + property rect rectangle: Qt.rect(0, 0, 200, 200) + } + + Binding { + target: item + property: "rectangle.x" + value: 100 + } + \endqml */ QString QQmlBind::property() const { @@ -222,8 +250,10 @@ void QQmlBind::setProperty(const QString &p) d->when = true; } d->propName = p; - if (d->componentComplete) + if (d->componentComplete) { d->prop = QQmlProperty(d->obj, d->propName); + d->validate(this); + } eval(); } @@ -262,8 +292,10 @@ void QQmlBind::componentComplete() { Q_D(QQmlBind); d->componentComplete = true; - if (!d->prop.isValid()) + if (!d->prop.isValid()) { d->prop = QQmlProperty(d->obj, d->propName); + d->validate(this); + } eval(); } @@ -277,22 +309,17 @@ void QQmlBind::eval() if (!d->when) { //restore any previous binding if (d->prevBind) { - QQmlAbstractBinding *tmp = d->prevBind; + QQmlAbstractBinding::Ptr p = d->prevBind; d->prevBind = 0; - tmp = QQmlPropertyPrivate::setBinding(d->prop, tmp); - if (tmp) //should this ever be true? - tmp->destroy(); + QQmlPropertyPrivate::setBinding(p.data()); } return; } //save any set binding for restoration - QQmlAbstractBinding *tmp; - tmp = QQmlPropertyPrivate::setBinding(d->prop, 0); - if (tmp && d->prevBind) - tmp->destroy(); - else if (!d->prevBind) - d->prevBind = tmp; + if (!d->prevBind) + d->prevBind = QQmlPropertyPrivate::binding(d->prop); + QQmlPropertyPrivate::removeBinding(d->prop); } d->prop.write(d->value.value); diff --git a/src/qml/types/qqmlbind_p.h b/src/qml/types/qqmlbind_p.h index 6d7eea4014..ac1cb0f32f 100644 --- a/src/qml/types/qqmlbind_p.h +++ b/src/qml/types/qqmlbind_p.h @@ -34,6 +34,17 @@ #ifndef QQMLBIND_H #define QQMLBIND_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <qqml.h> #include <QtCore/qobject.h> diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index 0c81855e49..6a93410ecb 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -165,7 +165,7 @@ void QQmlConnections::setTarget(QObject *obj) foreach (QQmlBoundSignal *s, d->boundsignals) { // It is possible that target is being changed due to one of our signal // handlers -> use deleteLater(). - if (s->isEvaluating()) + if (s->isNotifying()) (new QQmlBoundSignalDeleter(s))->deleteLater(); else delete s; diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h index 170f47b54f..234c5061a7 100644 --- a/src/qml/types/qqmlconnections_p.h +++ b/src/qml/types/qqmlconnections_p.h @@ -34,6 +34,17 @@ #ifndef QQMLCONNECTIONS_H #define QQMLCONNECTIONS_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <qqml.h> #include <private/qqmlcustomparser_p.h> diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 201fd4572c..f2de911725 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -44,7 +44,7 @@ #include <private/qqmlincubator_p.h> #include <private/qqmlcompiler_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> #include <qv4objectiterator_p.h> @@ -64,13 +64,13 @@ struct DelegateModelGroupFunction : FunctionObject { }; struct QQmlDelegateModelGroupChange : Object { - QQmlDelegateModelGroupChange(QV4::ExecutionEngine *engine); + QQmlDelegateModelGroupChange() {} QQmlChangeSet::Change change; }; struct QQmlDelegateModelGroupChangeArray : Object { - QQmlDelegateModelGroupChangeArray(QV4::ExecutionEngine *engine, const QVector<QQmlChangeSet::Change> &changes); + QQmlDelegateModelGroupChangeArray(const QVector<QQmlChangeSet::Change> &changes); QVector<QQmlChangeSet::Change> changes; }; @@ -83,19 +83,14 @@ struct DelegateModelGroupFunction : QV4::FunctionObject static Heap::DelegateModelGroupFunction *create(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)) { - return scope->engine()->memoryManager->alloc<DelegateModelGroupFunction>(scope, flag, code); + return scope->engine()->memoryManager->allocObject<DelegateModelGroupFunction>(scope, flag, code); } - static QV4::ReturnedValue construct(QV4::Managed *m, QV4::CallData *) + static QV4::ReturnedValue call(const QV4::Managed *that, QV4::CallData *callData) { - return static_cast<DelegateModelGroupFunction *>(m)->engine()->throwTypeError(); - } - - static QV4::ReturnedValue call(QV4::Managed *that, QV4::CallData *callData) - { - QV4::ExecutionEngine *v4 = static_cast<DelegateModelGroupFunction *>(that)->engine(); + QV4::ExecutionEngine *v4 = static_cast<const DelegateModelGroupFunction *>(that)->engine(); QV4::Scope scope(v4); - QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<DelegateModelGroupFunction *>(that)); + QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<const DelegateModelGroupFunction *>(that)); QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject); if (!o) return v4->throwTypeError(QStringLiteral("Not a valid VisualData object")); @@ -1062,7 +1057,7 @@ int QQmlDelegateModel::indexOf(QObject *item, QObject *) const return -1; } -void QQmlDelegateModel::setWatchedRoles(QList<QByteArray> roles) +void QQmlDelegateModel::setWatchedRoles(const QList<QByteArray> &roles) { Q_D(QQmlDelegateModel); d->m_adaptorModel.replaceWatchedRoles(d->m_watchedRoles, roles); @@ -1637,7 +1632,7 @@ bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const if (!object.isObject()) return false; - QV4::ExecutionEngine *v4 = object.asObject()->engine(); + QV4::ExecutionEngine *v4 = object.as<QV4::Object>()->engine(); QV4::Scope scope(v4); QV4::ScopedObject o(scope, object); if (!o) @@ -1722,7 +1717,7 @@ void QQmlDelegateModelItemMetaType::initializePrototype() s = v4->newString(QStringLiteral("isUnresolved")); QV4::ScopedFunctionObject f(scope); - QV4::ScopedContext global(scope, scope.engine->rootContext()); + QV4::ExecutionContext *global = scope.engine->rootContext(); p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, 30, QQmlDelegateModelItem::get_member))); p->setSetter(0); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); @@ -2493,7 +2488,7 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index) QV8Engine *v8 = model->m_cacheMetaType->v8Engine; QV4::ExecutionEngine *v4 = QV8Engine::getV4(v8); QV4::Scope scope(v4); - QV4::ScopedObject o(scope, v4->memoryManager->alloc<QQmlDelegateModelItemObject>(v4, cacheItem)); + QV4::ScopedObject o(scope, v4->memoryManager->allocObject<QQmlDelegateModelItemObject>(cacheItem)); QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value()); o->setPrototype(p); ++cacheItem->scriptRef; @@ -2511,7 +2506,7 @@ bool QQmlDelegateModelGroupPrivate::parseIndex(const QV4::Value &value, int *ind if (!value.isObject()) return false; - QV4::ExecutionEngine *v4 = value.asObject()->engine(); + QV4::ExecutionEngine *v4 = value.as<QV4::Object>()->engine(); QV4::Scope scope(v4); QV4::Scoped<QQmlDelegateModelItemObject> object(scope, value); @@ -2579,9 +2574,9 @@ void QQmlDelegateModelGroup::insert(QQmlV4Function *args) groups |= model->m_cacheMetaType->parseGroups(val); } - if (v->asArrayObject()) { + if (v->as<QV4::ArrayObject>()) { return; - } else if (v->asObject()) { + } else if (v->as<QV4::Object>()) { model->insert(before, v, groups); model->emitChanges(); } @@ -2626,7 +2621,7 @@ void QQmlDelegateModelGroup::create(QQmlV4Function *args) if (i < args->length() && index >= 0 && index <= model->m_compositor.count(group)) { v = (*args)[i]; - if (v->asObject()) { + if (v->as<QV4::Object>()) { int groups = 1 << d->group; if (++i < args->length()) { QV4::ScopedValue val(scope, (*args)[i]); @@ -3190,7 +3185,7 @@ QString QQmlPartsModel::stringValue(int index, const QString &role) return QQmlDelegateModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); } -void QQmlPartsModel::setWatchedRoles(QList<QByteArray> roles) +void QQmlPartsModel::setWatchedRoles(const QList<QByteArray> &roles) { QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles); @@ -3238,7 +3233,7 @@ struct QQmlDelegateModelGroupChange : QV4::Object V4_OBJECT2(QQmlDelegateModelGroupChange, QV4::Object) static QV4::Heap::QQmlDelegateModelGroupChange *create(QV4::ExecutionEngine *e) { - return e->memoryManager->alloc<QQmlDelegateModelGroupChange>(e); + return e->memoryManager->allocObject<QQmlDelegateModelGroupChange>(); } static QV4::ReturnedValue method_get_index(QV4::CallContext *ctx) { @@ -3266,11 +3261,6 @@ struct QQmlDelegateModelGroupChange : QV4::Object } }; -QV4::Heap::QQmlDelegateModelGroupChange::QQmlDelegateModelGroupChange(QV4::ExecutionEngine *engine) - : QV4::Heap::Object(engine) -{ -} - DEFINE_OBJECT_VTABLE(QQmlDelegateModelGroupChange); struct QQmlDelegateModelGroupChangeArray : public QV4::Object @@ -3280,18 +3270,18 @@ struct QQmlDelegateModelGroupChangeArray : public QV4::Object public: static QV4::Heap::QQmlDelegateModelGroupChangeArray *create(QV4::ExecutionEngine *engine, const QVector<QQmlChangeSet::Change> &changes) { - return engine->memoryManager->alloc<QQmlDelegateModelGroupChangeArray>(engine, changes); + return engine->memoryManager->allocObject<QQmlDelegateModelGroupChangeArray>(changes); } quint32 count() const { return d()->changes.count(); } const QQmlChangeSet::Change &at(int index) const { return d()->changes.at(index); } - static QV4::ReturnedValue getIndexed(QV4::Managed *m, uint index, bool *hasProperty) + static QV4::ReturnedValue getIndexed(const QV4::Managed *m, uint index, bool *hasProperty) { Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); - QV4::ExecutionEngine *v4 = static_cast<QQmlDelegateModelGroupChangeArray *>(m)->engine(); + QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine(); QV4::Scope scope(v4); - QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<QQmlDelegateModelGroupChangeArray *>(m)); + QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<const QQmlDelegateModelGroupChangeArray *>(m)); if (index >= array->count()) { if (hasProperty) @@ -3311,12 +3301,12 @@ public: return object.asReturnedValue(); } - static QV4::ReturnedValue get(QV4::Managed *m, QV4::String *name, bool *hasProperty) + static QV4::ReturnedValue get(const QV4::Managed *m, QV4::String *name, bool *hasProperty) { Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); - QQmlDelegateModelGroupChangeArray *array = static_cast<QQmlDelegateModelGroupChangeArray *>(m); + const QQmlDelegateModelGroupChangeArray *array = static_cast<const QQmlDelegateModelGroupChangeArray *>(m); - if (name->equals(array->engine()->id_length)) { + if (name->equals(array->engine()->id_length())) { if (hasProperty) *hasProperty = true; return QV4::Encode(array->count()); @@ -3326,11 +3316,10 @@ public: } }; -QV4::Heap::QQmlDelegateModelGroupChangeArray::QQmlDelegateModelGroupChangeArray(QV4::ExecutionEngine *engine, const QVector<QQmlChangeSet::Change> &changes) - : QV4::Heap::Object(engine) - , changes(changes) +QV4::Heap::QQmlDelegateModelGroupChangeArray::QQmlDelegateModelGroupChangeArray(const QVector<QQmlChangeSet::Change> &changes) + : changes(changes) { - QV4::Scope scope(engine); + QV4::Scope scope(internalClass->engine); QV4::ScopedObject o(scope, this); o->setArrayType(QV4::Heap::ArrayData::Custom); } diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h index 5ebffd5b9d..6af052c1b4 100644 --- a/src/qml/types/qqmldelegatemodel_p.h +++ b/src/qml/types/qqmldelegatemodel_p.h @@ -34,6 +34,17 @@ #ifndef QQMLDATAMODEL_P_H #define QQMLDATAMODEL_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <private/qtqmlglobal_p.h> #include <private/qqmllistcompositor_p.h> #include <private/qqmlobjectmodel_p.h> @@ -96,7 +107,7 @@ public: ReleaseFlags release(QObject *object); void cancel(int index); virtual QString stringValue(int index, const QString &role); - virtual void setWatchedRoles(QList<QByteArray> roles); + virtual void setWatchedRoles(const QList<QByteArray> &roles); int indexOf(QObject *object, QObject *objectContext) const; diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index dc289eb35e..3a19163cbd 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -154,7 +154,7 @@ protected: namespace QV4 { namespace Heap { struct QQmlDelegateModelItemObject : Object { - inline QQmlDelegateModelItemObject(QV4::ExecutionEngine *engine, QQmlDelegateModelItem *item); + inline QQmlDelegateModelItemObject(QQmlDelegateModelItem *item); ~QQmlDelegateModelItemObject(); QQmlDelegateModelItem *item; }; @@ -168,9 +168,8 @@ struct QQmlDelegateModelItemObject : QV4::Object V4_NEEDS_DESTROY }; -QV4::Heap::QQmlDelegateModelItemObject::QQmlDelegateModelItemObject(QV4::ExecutionEngine *engine, QQmlDelegateModelItem *item) - : QV4::Heap::Object(engine) - , item(item) +QV4::Heap::QQmlDelegateModelItemObject::QQmlDelegateModelItemObject(QQmlDelegateModelItem *item) + : item(item) { } @@ -356,7 +355,7 @@ public: ReleaseFlags release(QObject *item); QString stringValue(int index, const QString &role); QList<QByteArray> watchedRoles() const { return m_watchedRoles; } - void setWatchedRoles(QList<QByteArray> roles); + void setWatchedRoles(const QList<QByteArray> &roles); int indexOf(QObject *item, QObject *objectContext) const; diff --git a/src/qml/types/qqmlinstantiator.cpp b/src/qml/types/qqmlinstantiator.cpp index 735463a058..448a591e22 100644 --- a/src/qml/types/qqmlinstantiator.cpp +++ b/src/qml/types/qqmlinstantiator.cpp @@ -426,7 +426,7 @@ void QQmlInstantiator::setModel(const QVariant &v) } /*! - \qmlproperty QtQml::QtObject QtQml::Instantiator::object + \qmlproperty QtObject QtQml::Instantiator::object This is a reference to the first created object, intended as a convenience for the case where only one object has been created. @@ -440,7 +440,7 @@ QObject *QQmlInstantiator::object() const } /*! - \qmlmethod QtQml::QtObject QtQml::Instantiator::objectAt + \qmlmethod QtObject QtQml::Instantiator::objectAt(int index) Returns a reference to the object with the given \a index. */ diff --git a/src/qml/types/qqmlinstantiator_p.h b/src/qml/types/qqmlinstantiator_p.h index aa098817cd..842a0b947b 100644 --- a/src/qml/types/qqmlinstantiator_p.h +++ b/src/qml/types/qqmlinstantiator_p.h @@ -34,6 +34,17 @@ #ifndef QQMLINSTANTIATOR_P_H #define QQMLINSTANTIATOR_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlparserstatus.h> diff --git a/src/qml/types/qqmlitemmodels.qdoc b/src/qml/types/qqmlitemmodels.qdoc index 4e64aaa338..6733330209 100644 --- a/src/qml/types/qqmlitemmodels.qdoc +++ b/src/qml/types/qqmlitemmodels.qdoc @@ -26,7 +26,8 @@ ****************************************************************************/ /*! - \chapter QModelIndex & Co. in QML + \page qmodelindex-and-related-classes-in-qml.html + \title QModelIndex and related Classes in QML Since Qt 5.5, QModelIndex and QPersistentModelIndex are exposed in QML as value-based types. Also exposed in a similar fashion are QModelIndexList, @@ -34,15 +35,14 @@ be passed back and forth between QML and C++ as \c var properties or plain JavaScript variables. - We detail here which API all these classes get exposed in QML. Please refer - to the C++ documentation for more information. + Below you will find an overview of the API exposed to QML for these classes. + For more information, refer to their C++ documentation. - \note Since all these types are exposed as gadgets, there are no property + \note Since all these types are exposed as \l{Q_GADGET}{gadgets}, there are no property change notification signals emitted. Therefore binding to their properties may not give the expected results. This is especially true for QPersistentModelIndex. - It is perfectly possible to bind to properties holding any of those types. - \section1 \l QModelIndex and \l QPersistentModelIndex + \section1 QModelIndex and QPersistentModelIndex Types \list \li \b row : int @@ -53,25 +53,25 @@ \li \b internalId : quint64 \endlist - All these properties are read-only, as their C++ counterpart. + All these properties are read-only, as are their C++ counterparts. \note The usual caveats apply to QModelIndex in QML. If the underlying model changes or gets deleted, it may become dangerous to access its properties. Therefore, you - should not store any QModelIndex. You can, however, store QPersistentModelIndexes - in a safe way. + should not store any QModelIndex objects. You can, however, store QPersistentModelIndexe + objects in a safe way. - \section1 QModelIndexList + \section1 QModelIndexList Type \l QModelIndexList is exposed in QML as a JavaScript array. Conversions are automatically made from and to C++. In fact, any JavaScript array can be converted back to QModelIndexList, with non-QModelIndex objects replaced by - invalid QModelIndexes. + invalid \l{QModelIndex}es. \note QModelIndex to QPersistentModelIndex conversion happens when accessing the array elements because any QModelIndexList property retains reference semantics when exposed this way. - \section1 \l QItemSelectionRange + \section1 \l QItemSelectionRange Type \list \li \b top : int @@ -88,22 +88,22 @@ \li \b model : QAbstractItemModel \endlist - All these properties are read-only, as their C++ counterpart. In addition, + All these properties are read-only, as are their C++ counterparts. In addition, we also expose the following functions: \list - \li bool \b{contains}(QModelIndex index) - \li bool \b{contains}(int row, int column, QModelIndex parentIndex) - \li bool \b{intersects}(QItemSelectionRange other) - \li QItemSelectionRange \b{intersected}(QItemSelectionRange other) + \li bool \b{contains}(QModelIndex \e index) + \li bool \b{contains}(int \e row, int \e column, QModelIndex \e parentIndex) + \li bool \b{intersects}(QItemSelectionRange \e other) + \li QItemSelectionRange \b{intersected}(QItemSelectionRange \e other) \endlist - \section1 QItemSelection + \section1 QItemSelection Type Similarly to QModelIndexList, \l QItemSelection is exposed in QML as a JavaScript - array of QItemSelectionRanges. Conversions are automatically made from and to C++. + array of QItemSelectionRange objects. Conversions are automatically made from and to C++. In fact, any JavaScript array can be converted back to QItemSelection, with - non-QItemSelectionRange objects replaced by empty QItemSelectionRanges. + non-QItemSelectionRange objects replaced by empty \l {QItemSelectionRange}s. \sa ItemSelectionModel diff --git a/src/qml/types/qqmlitemselectionmodel.qdoc b/src/qml/types/qqmlitemselectionmodel.qdoc index 02919b388e..c223ef614e 100644 --- a/src/qml/types/qqmlitemselectionmodel.qdoc +++ b/src/qml/types/qqmlitemselectionmodel.qdoc @@ -55,7 +55,7 @@ It will trigger property binding updates every time \l selectionChanged() is emitted, even though its value hasn't changed. - \sa selection(), selectedIndexes(), select(), selectionChanged() + \sa selection(), selectedIndexes, select(), selectionChanged() */ /*! @@ -103,7 +103,7 @@ */ /*! - \qmlmethod QItemSelection selection() + \qmlmethod QItemSelection ItemSelectionModel::selection() */ /*! diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index ed97690adb..a4c0f7043f 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -88,7 +88,7 @@ const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::Da if (node) { const Role &r = *node->value; if (type != r.type) - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); + qmlInfo(0) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); return r; } @@ -101,7 +101,7 @@ const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::Data if (node) { const Role &r = *node->value; if (type != r.type) - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); + qmlInfo(0) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); return r; } @@ -150,7 +150,9 @@ const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::R ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0) { - for (int i=0 ; i < other->roles.count() ; ++i) { + const int otherRolesCount = other->roles.count(); + roles.reserve(otherRolesCount); + for (int i=0 ; i < otherRolesCount; ++i) { Role *role = new Role(other->roles[i]); roles.append(role); roleHash.insert(role->name, role); @@ -240,11 +242,12 @@ const ListLayout::Role *ListLayout::getExistingRole(QV4::String *key) return r; } -ModelObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex) +QObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex) { ListElement *e = elements[elementIndex]; if (e->m_objectCache == 0) { - e->m_objectCache = new ModelObject(model, elementIndex); + e->m_objectCache = new QObject; + (void)new ModelNodeMetaObject(e->m_objectCache, model, elementIndex); } return e->m_objectCache; } @@ -315,8 +318,8 @@ void ListModel::sync(ListModel *src, ListModel *target, QHash<int, ListModel *> // Update values stored in target meta objects for (int i=0 ; i < target->elements.count() ; ++i) { ListElement *e = target->elements[i]; - if (e->m_objectCache) - e->m_objectCache->updateValues(); + if (ModelNodeMetaObject *mo = e->objectCache()) + mo->updateValues(); } } @@ -382,9 +385,8 @@ void ListModel::updateCacheIndices() { for (int i=0 ; i < elements.count() ; ++i) { ListElement *e = elements.at(i); - if (e->m_objectCache) { - e->m_objectCache->m_elementIndex = i; - } + if (ModelNodeMetaObject *mo = e->objectCache()) + mo->m_elementIndex = i; } } @@ -421,13 +423,13 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) int roleIndex = -1; // Add the value now - if (QV4::String *s = propertyValue->asString()) { + if (const QV4::String *s = propertyValue->as<QV4::String>()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); roleIndex = e->setStringProperty(r, s->toQString()); } else if (propertyValue->isNumber()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); roleIndex = e->setDoubleProperty(r, propertyValue->asDouble()); - } else if (QV4::ArrayObject *a = propertyValue->asArrayObject()) { + } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); ListModel *subModel = new ListModel(r.subLayout, 0, -1); @@ -441,11 +443,11 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) } else if (propertyValue->isBoolean()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); roleIndex = e->setBoolProperty(r, propertyValue->booleanValue()); - } else if (QV4::DateObject *dd = propertyValue->asDateObject()) { + } else if (QV4::DateObject *dd = propertyValue->as<QV4::DateObject>()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); QDateTime dt = dd->toQDateTime(); roleIndex = e->setDateTimeProperty(r, dt); - } else if (QV4::Object *o = propertyValue->asObject()) { + } else if (QV4::Object *o = propertyValue->as<QV4::Object>()) { if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) { QObject *o = wrapper->object(); const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); @@ -468,9 +470,8 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) roles->append(roleIndex); } - if (e->m_objectCache) { - e->m_objectCache->updateValues(*roles); - } + if (ModelNodeMetaObject *mo = e->objectCache()) + mo->updateValues(*roles); } void ListModel::set(int elementIndex, QV4::Object *object) @@ -502,7 +503,7 @@ void ListModel::set(int elementIndex, QV4::Object *object) if (r.type == ListLayout::Role::Number) { e->setDoublePropertyFast(r, propertyValue->asDouble()); } - } else if (QV4::ArrayObject *a = propertyValue->asArrayObject()) { + } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); if (r.type == ListLayout::Role::List) { ListModel *subModel = new ListModel(r.subLayout, 0, -1); @@ -520,13 +521,13 @@ void ListModel::set(int elementIndex, QV4::Object *object) if (r.type == ListLayout::Role::Bool) { e->setBoolPropertyFast(r, propertyValue->booleanValue()); } - } else if (QV4::DateObject *date = propertyValue->asDateObject()) { + } else if (QV4::DateObject *date = propertyValue->as<QV4::DateObject>()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); if (r.type == ListLayout::Role::DateTime) { QDateTime dt = date->toQDateTime();; e->setDateTimePropertyFast(r, dt); } - } else if (QV4::Object *o = propertyValue->asObject()) { + } else if (QV4::Object *o = propertyValue->as<QV4::Object>()) { if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) { QObject *o = wrapper->object(); const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); @@ -589,10 +590,12 @@ int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const Q if (r) { roleIndex = e->setVariantProperty(*r, data); - if (roleIndex != -1 && e->m_objectCache) { + ModelNodeMetaObject *cache = e->objectCache(); + + if (roleIndex != -1 && cache) { QVector<int> roles; roles << roleIndex; - e->m_objectCache->updateValues(roles); + cache->updateValues(roles); } } } @@ -631,6 +634,13 @@ inline char *ListElement::getPropertyMemory(const ListLayout::Role &role) return mem; } +ModelNodeMetaObject *ListElement::objectCache() +{ + if (!m_objectCache) + return 0; + return ModelNodeMetaObject::get(m_objectCache); +} + QString *ListElement::getStringProperty(const ListLayout::Role &role) { char *mem = getPropertyMemory(role); @@ -1169,7 +1179,7 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d roleIndex = setStringProperty(role, qstr); } else if (d.isNumber()) { roleIndex = setDoubleProperty(role, d.asDouble()); - } else if (d.asArrayObject()) { + } else if (d.as<QV4::ArrayObject>()) { QV4::ScopedArrayObject a(scope, d); if (role.type == ListLayout::Role::List) { QV4::Scope scope(a->engine()); @@ -1183,11 +1193,11 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d } roleIndex = setListProperty(role, subModel); } else { - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List)); + qmlInfo(0) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List)); } } else if (d.isBoolean()) { roleIndex = setBoolProperty(role, d.booleanValue()); - } else if (d.asDateObject()) { + } else if (d.as<QV4::DateObject>()) { QV4::Scoped<QV4::DateObject> dd(scope, d); QDateTime dt = dd->toQDateTime(); roleIndex = setDateTimeProperty(role, dt); @@ -1207,15 +1217,48 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d return roleIndex; } -ModelObject::ModelObject(QQmlListModel *model, int elementIndex) -: m_model(model), m_elementIndex(elementIndex), m_meta(new ModelNodeMetaObject(this)) +ModelNodeMetaObject::ModelNodeMetaObject(QObject *object, QQmlListModel *model, int elementIndex) +: QQmlOpenMetaObject(object), m_enabled(false), m_model(model), m_elementIndex(elementIndex), m_initialized(false) +{} + +void ModelNodeMetaObject::initialize() { + const int roleCount = m_model->m_listModel->roleCount(); + QVector<QByteArray> properties; + properties.reserve(roleCount); + for (int i = 0 ; i < roleCount ; ++i) { + const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i); + QByteArray name = role.name.toUtf8(); + properties << name; + } + type()->createProperties(properties); updateValues(); - setNodeUpdatesEnabled(true); + m_enabled = true; +} + +ModelNodeMetaObject::~ModelNodeMetaObject() +{ +} + +QAbstractDynamicMetaObject *ModelNodeMetaObject::toDynamicMetaObject(QObject *object) +{ + if (!m_initialized) { + m_initialized = true; + initialize(); + } + return QQmlOpenMetaObject::toDynamicMetaObject(object); +} + +ModelNodeMetaObject *ModelNodeMetaObject::get(QObject *obj) +{ + QObjectPrivate *op = QObjectPrivate::get(obj); + return static_cast<ModelNodeMetaObject*>(op->metaObject); } -void ModelObject::updateValues() +void ModelNodeMetaObject::updateValues() { + if (!m_initialized) + return; int roleCount = m_model->m_listModel->roleCount(); for (int i=0 ; i < roleCount ; ++i) { const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i); @@ -1225,8 +1268,10 @@ void ModelObject::updateValues() } } -void ModelObject::updateValues(const QVector<int> &roles) +void ModelNodeMetaObject::updateValues(const QVector<int> &roles) { + if (!m_initialized) + return; int roleCount = roles.count(); for (int i=0 ; i < roleCount ; ++i) { int roleIndex = roles.at(i); @@ -1237,15 +1282,6 @@ void ModelObject::updateValues(const QVector<int> &roles) } } -ModelNodeMetaObject::ModelNodeMetaObject(ModelObject *object) -: QQmlOpenMetaObject(object), m_enabled(false), m_obj(object) -{ -} - -ModelNodeMetaObject::~ModelNodeMetaObject() -{ -} - void ModelNodeMetaObject::propertyWritten(int index) { if (!m_enabled) @@ -1254,17 +1290,75 @@ void ModelNodeMetaObject::propertyWritten(int index) QString propName = QString::fromUtf8(name(index)); QVariant value = operator[](index); - QV4::Scope scope(m_obj->m_model->engine()); + QV4::Scope scope(m_model->engine()); QV4::ScopedValue v(scope, scope.engine->fromVariant(value)); - int roleIndex = m_obj->m_model->m_listModel->setExistingProperty(m_obj->m_elementIndex, propName, v, scope.engine); + int roleIndex = m_model->m_listModel->setExistingProperty(m_elementIndex, propName, v, scope.engine); if (roleIndex != -1) { QVector<int> roles; roles << roleIndex; - m_obj->m_model->emitItemsChanged(m_obj->m_elementIndex, 1, roles); + m_model->emitItemsChanged(m_elementIndex, 1, roles); } } +namespace QV4 { + +void ModelObject::put(Managed *m, String *name, const Value &value) +{ + ModelObject *that = static_cast<ModelObject*>(m); + + ExecutionEngine *eng = that->engine(); + const int elementIndex = that->d()->m_elementIndex; + const QString propName = name->toQString(); + int roleIndex = that->d()->m_model->m_listModel->setExistingProperty(elementIndex, propName, value, eng); + if (roleIndex != -1) { + QVector<int> roles; + roles << roleIndex; + that->d()->m_model->emitItemsChanged(elementIndex, 1, roles); + } + + ModelNodeMetaObject *mo = ModelNodeMetaObject::get(that->object()); + if (mo->initialized()) + mo->emitPropertyNotification(name->toQString().toUtf8()); +} + +ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty) +{ + const ModelObject *that = static_cast<const ModelObject*>(m); + const ListLayout::Role *role = that->d()->m_model->m_listModel->getExistingRole(name); + if (!role) + return QObjectWrapper::get(m, name, hasProperty); + if (hasProperty) + *hasProperty = true; + const int elementIndex = that->d()->m_elementIndex; + QVariant value = that->d()->m_model->data(elementIndex, role->index); + return that->engine()->fromVariant(value); +} + +void ModelObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) +{ + ModelObject *that = static_cast<ModelObject*>(m); + ExecutionEngine *v4 = that->engine(); + name->setM(0); + *index = UINT_MAX; + if (it->arrayIndex < uint(that->d()->m_model->m_listModel->roleCount())) { + Scope scope(that->engine()); + const ListLayout::Role &role = that->d()->m_model->m_listModel->getExistingRole(it->arrayIndex); + ++it->arrayIndex; + ScopedString roleName(scope, v4->newString(role.name)); + name->setM(roleName->d()); + *attributes = QV4::Attr_Data; + QVariant value = that->d()->m_model->data(that->d()->m_elementIndex, role.index); + p->value = v4->fromVariant(value); + return; + } + QV4::QObjectWrapper::advanceIterator(m, it, name, index, p, attributes); +} + +DEFINE_OBJECT_VTABLE(ModelObject); + +} // namespace QV4 + DynamicRoleModelNode::DynamicRoleModelNode(QQmlListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this)) { setNodeUpdatesEnabled(true); @@ -1330,8 +1424,8 @@ void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector<int> QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner); QVariantList subArray = value.toList(); - QVariantList::const_iterator subIt = subArray.begin(); - QVariantList::const_iterator subEnd = subArray.end(); + QVariantList::const_iterator subIt = subArray.cbegin(); + QVariantList::const_iterator subEnd = subArray.cend(); while (subIt != subEnd) { const QVariantMap &subObject = subIt->toMap(); subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); @@ -1398,8 +1492,8 @@ void DynamicRoleModelNodeMetaObject::propertyWritten(int index) QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel); QVariantList subArray = v.toList(); - QVariantList::const_iterator subIt = subArray.begin(); - QVariantList::const_iterator subEnd = subArray.end(); + QVariantList::const_iterator subIt = subArray.cbegin(); + QVariantList::const_iterator subEnd = subArray.cend(); while (subIt != subEnd) { const QVariantMap &subObject = subIt->toMap(); subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); @@ -1781,6 +1875,30 @@ QVariant QQmlListModel::data(const QModelIndex &index, int role) const return data(index.row(), role); } +bool QQmlListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + const int row = index.row(); + if (row >= count() || row < 0) + return false; + + if (m_dynamicRoles) { + const QByteArray property = m_roles.at(role).toUtf8(); + if (m_modelObjects[row]->setValue(property, value)) { + emitItemsChanged(row, 1, QVector<int>() << role); + return true; + } + } else { + const ListLayout::Role &r = m_listModel->getExistingRole(role); + const int roleIndex = m_listModel->setOrCreateProperty(row, r.name, value); + if (roleIndex != -1) { + emitItemsChanged(row, 1, QVector<int>() << role); + return true; + } + } + + return false; +} + QVariant QQmlListModel::data(int index, int role) const { QVariant v; @@ -2157,8 +2275,8 @@ QQmlV4Handle QQmlListModel::get(int index) const DynamicRoleModelNode *object = m_modelObjects[index]; result = QV4::QObjectWrapper::wrap(scope.engine, object); } else { - ModelObject *object = m_listModel->getOrCreateModelObject(const_cast<QQmlListModel *>(this), index); - result = QV4::QObjectWrapper::wrap(scope.engine, object); + QObject *object = m_listModel->getOrCreateModelObject(const_cast<QQmlListModel *>(this), index); + result = scope.engine->memoryManager->allocObject<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this), index); } } diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h index 75373be47c..b3ae806c5e 100644 --- a/src/qml/types/qqmllistmodel_p.h +++ b/src/qml/types/qqmllistmodel_p.h @@ -34,6 +34,17 @@ #ifndef QQMLLISTMODEL_H #define QQMLLISTMODEL_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <qqml.h> #include <private/qqmlcustomparser_p.h> @@ -54,6 +65,10 @@ class QQmlListModelWorkerAgent; class ListModel; class ListLayout; +namespace QV4 { +struct ModelObject; +} + class Q_QML_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel { Q_OBJECT @@ -67,6 +82,7 @@ public: QModelIndex index(int row, int column, const QModelIndex &parent) const; int rowCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); QHash<int,QByteArray> roleNames() const; QVariant data(int index, int role) const; @@ -94,6 +110,7 @@ private: friend class QQmlListModelParser; friend class QQmlListModelWorkerAgent; friend class ModelObject; + friend struct QV4::ModelObject; friend class ModelNodeMetaObject; friend class ListModel; friend class ListElement; diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h index 4e3132b860..d7e0defaec 100644 --- a/src/qml/types/qqmllistmodel_p_p.h +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -111,56 +111,72 @@ private: friend class DynamicRoleModelNodeMetaObject; }; -class ModelObject; - class ModelNodeMetaObject : public QQmlOpenMetaObject { public: - ModelNodeMetaObject(ModelObject *object); + ModelNodeMetaObject(QObject *object, QQmlListModel *model, int elementIndex); ~ModelNodeMetaObject(); + virtual QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *object); + + static ModelNodeMetaObject *get(QObject *obj); + bool m_enabled; + QQmlListModel *m_model; + int m_elementIndex; + + void updateValues(); + void updateValues(const QVector<int> &roles); + + bool initialized() const { return m_initialized; } protected: void propertyWritten(int index); private: - - ModelObject *m_obj; -}; - -class ModelObject : public QObject -{ - Q_OBJECT -public: - ModelObject(QQmlListModel *model, int elementIndex); - + using QQmlOpenMetaObject::setValue; void setValue(const QByteArray &name, const QVariant &val, bool force) { if (force) { - QVariant existingValue = m_meta->value(name); + QVariant existingValue = value(name); if (existingValue.isValid()) { - (*m_meta)[name] = QVariant(); + (*this)[name] = QVariant(); } } - m_meta->setValue(name, val); + setValue(name, val); } - void setNodeUpdatesEnabled(bool enable) - { - m_meta->m_enabled = enable; - } + void initialize(); + bool m_initialized; +}; - void updateValues(); - void updateValues(const QVector<int> &roles); +namespace QV4 { + +namespace Heap { +struct ModelObject : public QObjectWrapper { + ModelObject(QObject *object, QQmlListModel *model, int elementIndex) + : QObjectWrapper(object) + , m_model(model) + , m_elementIndex(elementIndex) + {} QQmlListModel *m_model; int m_elementIndex; +}; -private: - ModelNodeMetaObject *m_meta; +} + +struct ModelObject : public QObjectWrapper +{ + static void put(Managed *m, String *name, const Value& value); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); + static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); + + V4_OBJECT2(ModelObject, QObjectWrapper) }; +} // namespace QV4 + class ListLayout { public: @@ -236,7 +252,7 @@ public: enum { - BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelObject *) + BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelNodeMetaObject *) }; private: @@ -278,11 +294,13 @@ private: int getUid() const { return uid; } + ModelNodeMetaObject *objectCache(); + char data[BLOCK_SIZE]; ListElement *next; int uid; - ModelObject *m_objectCache; + QObject *m_objectCache; friend class ListModel; }; @@ -315,6 +333,11 @@ public: return m_layout->getExistingRole(index); } + const ListLayout::Role *getExistingRole(QV4::String *key) + { + return m_layout->getExistingRole(key); + } + const ListLayout::Role &getOrCreateListRole(const QString &name) { return m_layout->getRoleOrCreate(name, ListLayout::Role::List); @@ -343,7 +366,7 @@ public: static void sync(ListModel *src, ListModel *target, QHash<int, ListModel *> *srcModelHash); - ModelObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex); + QObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex); private: QPODVector<ListElement *, 4> elements; diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp index 3e53efd8b9..062d30c252 100644 --- a/src/qml/types/qqmlmodelsmodule.cpp +++ b/src/qml/types/qqmlmodelsmodule.cpp @@ -48,6 +48,7 @@ void QQmlModelsModule::defineModule() qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel"); qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup"); qmlRegisterType<QQmlObjectModel>(uri, 2, 1, "ObjectModel"); + qmlRegisterType<QQmlObjectModel,3>(uri, 2, 3, "ObjectModel"); qmlRegisterType<QItemSelectionModel>(uri, 2, 2, "ItemSelectionModel"); } diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qml/types/qqmlmodelsmodule_p.h index 7fd02b600a..3471a16684 100644 --- a/src/qml/types/qqmlmodelsmodule_p.h +++ b/src/qml/types/qqmlmodelsmodule_p.h @@ -34,6 +34,17 @@ #ifndef QQMLMODELSMODULE_H #define QQMLMODELSMODULE_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <private/qtqmlglobal_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qml/types/qqmlobjectmodel.cpp index 7081e96070..4b64f24806 100644 --- a/src/qml/types/qqmlobjectmodel.cpp +++ b/src/qml/types/qqmlobjectmodel.cpp @@ -36,10 +36,12 @@ #include <QtCore/qcoreapplication.h> #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlengine.h> +#include <QtQml/qqmlinfo.h> #include <private/qqmlchangeset_p.h> #include <private/qqmlglobal_p.h> #include <private/qobject_p.h> +#include <private/qpodvector_p.h> #include <QtCore/qhash.h> #include <QtCore/qlist.h> @@ -67,9 +69,8 @@ public: QQmlObjectModelPrivate() : QObjectPrivate() {} static void children_append(QQmlListProperty<QObject> *prop, QObject *item) { - static_cast<QQmlObjectModelPrivate *>(prop->data)->children.append(Item(item)); - static_cast<QQmlObjectModelPrivate *>(prop->data)->itemAppended(); - static_cast<QQmlObjectModelPrivate *>(prop->data)->emitChildrenChanged(); + int index = static_cast<QQmlObjectModelPrivate *>(prop->data)->children.count(); + static_cast<QQmlObjectModelPrivate *>(prop->data)->insert(index, item); } static int children_count(QQmlListProperty<QObject> *prop) { @@ -81,33 +82,77 @@ public: } static void children_clear(QQmlListProperty<QObject> *prop) { - static_cast<QQmlObjectModelPrivate *>(prop->data)->itemCleared(static_cast<QQmlObjectModelPrivate *>(prop->data)->children); - static_cast<QQmlObjectModelPrivate *>(prop->data)->children.clear(); - static_cast<QQmlObjectModelPrivate *>(prop->data)->emitChildrenChanged(); + static_cast<QQmlObjectModelPrivate *>(prop->data)->clear(); } - void itemAppended() { + void insert(int index, QObject *item) { Q_Q(QQmlObjectModel); - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.last().item); - attached->setIndex(children.count()-1); + children.insert(index, Item(item)); + for (int i = index; i < children.count(); ++i) { + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); + attached->setIndex(i); + } QQmlChangeSet changeSet; - changeSet.insert(children.count() - 1, 1); + changeSet.insert(index, 1); emit q->modelUpdated(changeSet, false); emit q->countChanged(); + emit q->childrenChanged(); } - void itemCleared(const QList<Item> &children) { + void move(int from, int to, int n) { Q_Q(QQmlObjectModel); - foreach (const Item &child, children) - emit q->destroyingItem(child.item); - emit q->countChanged(); + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + from = tto; + to = tto+n; + n = tfrom-tto; + } + + QPODVector<QQmlObjectModelPrivate::Item, 4> store; + for (int i = 0; i < to - from; ++i) + store.append(children[from + n + i]); + for (int i = 0; i < n; ++i) + store.append(children[from + i]); + + for (int i = 0; i < store.count(); ++i) { + children[from + i] = store[i]; + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(from + i).item); + attached->setIndex(from + i); + } + + QQmlChangeSet changeSet; + changeSet.move(from, to, n, -1); + emit q->modelUpdated(changeSet, false); + emit q->childrenChanged(); } - void emitChildrenChanged() { + void remove(int index, int n) { Q_Q(QQmlObjectModel); + for (int i = index; i < index + n; ++i) { + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); + attached->setIndex(-1); + } + children.erase(children.begin() + index, children.begin() + index + n); + for (int i = index; i < children.count(); ++i) { + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); + attached->setIndex(i); + } + QQmlChangeSet changeSet; + changeSet.remove(index, n); + emit q->modelUpdated(changeSet, false); + emit q->countChanged(); emit q->childrenChanged(); } + void clear() { + Q_Q(QQmlObjectModel); + foreach (const Item &child, children) + emit q->destroyingItem(child.item); + remove(0, children.count()); + } + int indexOf(QObject *item) const { for (int i = 0; i < children.count(); ++i) if (children.at(i).item == item) @@ -258,4 +303,133 @@ QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj) return QQmlObjectModelAttached::properties(obj); } +/*! + \qmlmethod object QtQml.Models::ObjectModel::get(int index) + \since 5.6 + + Returns the item at \a index in the model. This allows the item + to be accessed or modified from JavaScript: + + \code + Component.onCompleted: { + objectModel.append(objectComponent.createObject()) + console.log(objectModel.get(0).objectName); + objectModel.get(0).objectName = "first"; + } + \endcode + + The \a index must be an element in the list. + + \sa append() +*/ +QObject *QQmlObjectModel::get(int index) const +{ + Q_D(const QQmlObjectModel); + if (index < 0 || index >= d->children.count()) + return 0; + return d->children.at(index).item; +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::append(object item) + \since 5.6 + + Appends a new item to the end of the model. + + \code + objectModel.append(objectComponent.createObject()) + \endcode + + \sa insert(), remove() +*/ +void QQmlObjectModel::append(QObject *object) +{ + Q_D(QQmlObjectModel); + d->insert(count(), object); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::insert(int index, object item) + \since 5.6 + + Inserts a new item to the model at position \a index. + + \code + objectModel.insert(2, objectComponent.createObject()) + \endcode + + The \a index must be to an existing item in the list, or one past + the end of the list (equivalent to append). + + \sa append(), remove() +*/ +void QQmlObjectModel::insert(int index, QObject *object) +{ + Q_D(QQmlObjectModel); + if (index < 0 || index > count()) { + qmlInfo(this) << tr("insert: index %1 out of range").arg(index); + return; + } + d->insert(index, object); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::move(int from, int to, int n = 1) + \since 5.6 + + Moves \a n items \a from one position \a to another. + + The from and to ranges must exist; for example, to move the first 3 items + to the end of the model: + + \code + objectModel.move(0, objectModel.count - 3, 3) + \endcode + + \sa append() +*/ +void QQmlObjectModel::move(int from, int to, int n) +{ + Q_D(QQmlObjectModel); + if (n <= 0 || from == to) + return; + if (from < 0 || to < 0 || from + n > count() || to + n > count()) { + qmlInfo(this) << tr("move: out of range"); + return; + } + d->move(from, to, n); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::remove(int index, int n = 1) + \since 5.6 + + Removes the items at \a index from the model. + + \sa clear() +*/ +void QQmlObjectModel::remove(int index, int n) +{ + Q_D(QQmlObjectModel); + if (index < 0 || n <= 0 || index + n > count()) { + qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+n).arg(count()); + return; + } + d->remove(index, n); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::clear() + \since 5.6 + + Clears all items from the model. + + \sa append(), remove() +*/ +void QQmlObjectModel::clear() +{ + Q_D(QQmlObjectModel); + d->clear(); +} + QT_END_NAMESPACE diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h index 4c37a5ac30..868f736147 100644 --- a/src/qml/types/qqmlobjectmodel_p.h +++ b/src/qml/types/qqmlobjectmodel_p.h @@ -34,6 +34,17 @@ #ifndef QQMLINSTANCEMODEL_P_H #define QQMLINSTANCEMODEL_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <private/qtqmlglobal_p.h> #include <QtQml/qqml.h> #include <QtCore/qobject.h> @@ -61,7 +72,7 @@ public: virtual ReleaseFlags release(QObject *object) = 0; virtual void cancel(int) {} virtual QString stringValue(int, const QString &) = 0; - virtual void setWatchedRoles(QList<QByteArray> roles) = 0; + virtual void setWatchedRoles(const QList<QByteArray> &roles) = 0; virtual int indexOf(QObject *object, QObject *objectContext) const = 0; @@ -99,7 +110,7 @@ public: virtual QObject *object(int index, bool asynchronous=false); virtual ReleaseFlags release(QObject *object); virtual QString stringValue(int index, const QString &role); - virtual void setWatchedRoles(QList<QByteArray>) {} + virtual void setWatchedRoles(const QList<QByteArray> &) {} virtual int indexOf(QObject *object, QObject *objectContext) const; @@ -107,6 +118,15 @@ public: static QQmlObjectModelAttached *qmlAttachedProperties(QObject *obj); + Q_REVISION(3) Q_INVOKABLE QObject *get(int index) const; + Q_REVISION(3) Q_INVOKABLE void append(QObject *object); + Q_REVISION(3) Q_INVOKABLE void insert(int index, QObject *object); + Q_REVISION(3) Q_INVOKABLE void move(int from, int to, int n = 1); + Q_REVISION(3) Q_INVOKABLE void remove(int index, int n = 1); + +public Q_SLOTS: + Q_REVISION(3) void clear(); + Q_SIGNALS: void childrenChanged(); @@ -120,7 +140,7 @@ class QQmlObjectModelAttached : public QObject public: QQmlObjectModelAttached(QObject *parent) - : QObject(parent), m_index(0) {} + : QObject(parent), m_index(-1) {} ~QQmlObjectModelAttached() { attachedProperties.remove(parent()); } diff --git a/src/qml/types/qqmltimer_p.h b/src/qml/types/qqmltimer_p.h index 0da81b481f..2dcf827926 100644 --- a/src/qml/types/qqmltimer_p.h +++ b/src/qml/types/qqmltimer_p.h @@ -34,6 +34,17 @@ #ifndef QQMLTIMER_H #define QQMLTIMER_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <qqml.h> #include <QtCore/qobject.h> diff --git a/src/qml/types/qquickpackage_p.h b/src/qml/types/qquickpackage_p.h index 6398e6a4fa..1c7f255eb4 100644 --- a/src/qml/types/qquickpackage_p.h +++ b/src/qml/types/qquickpackage_p.h @@ -34,6 +34,17 @@ #ifndef QQUICKPACKAGE_H #define QQUICKPACKAGE_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <qqml.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index c2c6e5ef5c..03915ab414 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -54,7 +54,7 @@ #include <private/qv8engine_p.h> #include <private/qv4serialize_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4script_p.h> #include <private/qv4scopedvalue_p.h> @@ -165,7 +165,7 @@ public: QUrl source; bool initialized; QQuickWorkerScript *owner; - QV4::PersistentValue object; + QV4::PersistentValue qmlContext; }; QHash<int, WorkerScript *> workers; @@ -221,7 +221,7 @@ void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() "})" QV4::Scope scope(m_v4Engine); - QV4::ScopedContext globalContext(scope, scope.engine->rootContext()); + QV4::ExecutionContext *globalContext = scope.engine->rootContext(); onmessage.set(scope.engine, QV4::Script(globalContext, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro Q_ASSERT(!scope.engine->hasException); QV4::Script createsendscript(globalContext, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro @@ -291,7 +291,6 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(QV4::Call return QV4::Encode::undefined(); } -// Requires handle scope and context scope QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *script) { if (!script->initialized) { @@ -300,9 +299,7 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *scri QV4::ExecutionEngine *v4 = QV8Engine::getV4(workerEngine); QV4::Scope scope(v4); - script->object.set(v4, QV4::QmlContextWrapper::urlScope(v4, script->source)); - - QV4::Scoped<QV4::QmlContextWrapper> w(scope, script->object.value()); + QV4::Scoped<QV4::QmlContextWrapper> w(scope, QV4::QmlContextWrapper::urlScope(v4, script->source)); Q_ASSERT(!!w); w->setReadOnly(false); @@ -312,9 +309,11 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *scri w->QV4::Object::put(QV4::ScopedString(scope, v4->newString(QStringLiteral("WorkerScript"))), api); w->setReadOnly(true); + + script->qmlContext.set(v4, v4->rootContext()->newQmlContext(w)); } - return script->object.value(); + return script->qmlContext.value(); } bool QQuickWorkerScriptEnginePrivate::event(QEvent *event) @@ -354,10 +353,12 @@ void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &d QV4::ScopedFunctionObject f(scope, workerEngine->onmessage.value()); QV4::ScopedValue value(scope, QV4::Serialize::deserialize(data, v4)); + QV4::Scoped<QV4::QmlContext> qmlContext(scope, script->qmlContext.value()); + Q_ASSERT(!!qmlContext); QV4::ScopedCallData callData(scope, 2); callData->thisObject = workerEngine->global(); - callData->args[0] = script->object.value(); + callData->args[0] = qmlContext->d()->qml; // ### callData->args[1] = value; f->call(callData); if (scope.hasException()) { @@ -382,13 +383,12 @@ void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) return; script->source = url; - QV4::ScopedObject activation(scope, getWorker(script)); - if (!activation) - return; + QV4::Scoped<QV4::QmlContext> qmlContext(scope, getWorker(script)); + Q_ASSERT(!!qmlContext); if (const QQmlPrivate::CachedQmlUnit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(url)) { QV4::CompiledData::CompilationUnit *jsUnit = cachedUnit->createCompilationUnit(); - program.reset(new QV4::Script(v4, activation, jsUnit)); + program.reset(new QV4::Script(v4, qmlContext, jsUnit)); } else { QFile f(fileName); if (!f.open(QIODevice::ReadOnly)) { @@ -400,7 +400,7 @@ void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) QString sourceCode = QString::fromUtf8(data); QmlIR::Document::removeScriptPragmas(sourceCode); - program.reset(new QV4::Script(v4, activation, sourceCode, url.toString())); + program.reset(new QV4::Script(v4, qmlContext, sourceCode, url.toString())); program->parse(); } diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index 356970eef0..c61144dd8f 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -38,7 +38,7 @@ #include <private/qqmlproperty_p.h> #include <private/qv8engine_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> QT_BEGIN_NAMESPACE @@ -159,7 +159,9 @@ public: signalIndexes.append(propertyId + signalOffset); } if (roles.isEmpty()) { - for (int propertyId = 0; propertyId < propertyRoles.count(); ++propertyId) + const int propertyRolesCount = propertyRoles.count(); + signalIndexes.reserve(propertyRolesCount); + for (int propertyId = 0; propertyId < propertyRolesCount; ++propertyId) signalIndexes.append(propertyId + signalOffset); } @@ -219,9 +221,9 @@ public: const QByteArray &propertyName = it.key(); QV4::ScopedString name(scope, v4->newString(QString::fromUtf8(propertyName))); - QV4::ScopedContext global(scope, v4->rootContext()); - QV4::ScopedFunctionObject g(scope, v4->memoryManager->alloc<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::get_property)); - QV4::ScopedFunctionObject s(scope, v4->memoryManager->alloc<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::set_property)); + QV4::ExecutionContext *global = v4->rootContext(); + QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocObject<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::get_property)); + QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocObject<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::set_property)); p->setGetter(g); p->setSetter(s); proto->insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable); @@ -426,7 +428,7 @@ public: } QV4::Scope scope(v4); QV4::ScopedObject proto(scope, type->prototype.value()); - QV4::ScopedObject o(scope, proto->engine()->memoryManager->alloc<QQmlDelegateModelItemObject>(proto->engine(), this)); + QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocObject<QQmlDelegateModelItemObject>(this)); o->setPrototype(proto); ++scriptRef; return o.asReturnedValue(); @@ -545,7 +547,7 @@ public: metaObject = builder.toMetaObject(); *static_cast<QMetaObject *>(this) = *metaObject; - propertyCache = new QQmlPropertyCache(engine, metaObject); + propertyCache = new QQmlPropertyCache(QV8Engine::getV4(engine), metaObject); } }; @@ -604,7 +606,7 @@ public: { QQmlAdaptorModelEngineData *data = engineData(v4); QV4::Scope scope(v4); - QV4::ScopedObject o(scope, v4->memoryManager->alloc<QQmlDelegateModelItemObject>(v4, this)); + QV4::ScopedObject o(scope, v4->memoryManager->allocObject<QQmlDelegateModelItemObject>(this)); QV4::ScopedObject p(scope, data->listItemProto.value()); o->setPrototype(p); ++scriptRef; @@ -786,8 +788,11 @@ public: m_type->release(); } - int metaCall(QMetaObject::Call call, int id, void **arguments) + int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) { + Q_ASSERT(o == m_data); + Q_UNUSED(o); + static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount(); if (id >= m_type->propertyOffset && (call == QMetaObject::ReadProperty diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h index b0f211a0f2..9da04462aa 100644 --- a/src/qml/util/qqmladaptormodel_p.h +++ b/src/qml/util/qqmladaptormodel_p.h @@ -34,6 +34,17 @@ #ifndef QQMLADAPTORMODEL_P_H #define QQMLADAPTORMODEL_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QtCore/qabstractitemmodel.h> #include "private/qqmllistaccessor_p.h" diff --git a/src/qml/util/qqmllistaccessor_p.h b/src/qml/util/qqmllistaccessor_p.h index cf0ec52be4..1d9069118a 100644 --- a/src/qml/util/qqmllistaccessor_p.h +++ b/src/qml/util/qqmllistaccessor_p.h @@ -34,6 +34,17 @@ #ifndef QQMLLISTACCESSOR_H #define QQMLLISTACCESSOR_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QtCore/QVariant> QT_BEGIN_NAMESPACE diff --git a/src/qml/util/qqmllistcompositor_p.h b/src/qml/util/qqmllistcompositor_p.h index 8e10cb1546..2ddbc5d5a1 100644 --- a/src/qml/util/qqmllistcompositor_p.h +++ b/src/qml/util/qqmllistcompositor_p.h @@ -166,7 +166,7 @@ public: struct Change { inline Change() {} - inline Change(iterator it, int count, uint flags, int moveId = -1); + inline Change(const iterator &it, int count, uint flags, int moveId = -1); int count; uint flags; int moveId; @@ -188,14 +188,14 @@ public: struct Insert : public Change { Insert() {} - Insert(iterator it, int count, uint flags, int moveId = -1) + Insert(const iterator &it, int count, uint flags, int moveId = -1) : Change(it, count, flags, moveId) {} }; struct Remove : public Change { Remove() {} - Remove(iterator it, int count, uint flags, int moveId = -1) + Remove(const iterator &it, int count, uint flags, int moveId = -1) : Change(it, count, flags, moveId) {} }; @@ -224,14 +224,14 @@ public: void setFlags(iterator from, int count, Group group, uint flags, QVector<Insert> *inserts = 0); void setFlags(Group fromGroup, int from, int count, uint flags, QVector<Insert> *inserts = 0) { setFlags(fromGroup, from, count, fromGroup, flags, inserts); } - void setFlags(iterator from, int count, uint flags, QVector<Insert> *inserts = 0) { + void setFlags(const iterator from, int count, uint flags, QVector<Insert> *inserts = 0) { setFlags(from, count, from.group, flags, inserts); } void clearFlags(Group fromGroup, int from, int count, Group group, uint flags, QVector<Remove> *removals = 0); void clearFlags(iterator from, int count, Group group, uint flags, QVector<Remove> *removals = 0); void clearFlags(Group fromGroup, int from, int count, uint flags, QVector<Remove> *removals = 0) { clearFlags(fromGroup, from, count, fromGroup, flags, removals); } - void clearFlags(iterator from, int count, uint flags, QVector<Remove> *removals = 0) { + void clearFlags(const iterator &from, int count, uint flags, QVector<Remove> *removals = 0) { clearFlags(from, count, from.group, flags, removals); } bool verifyMoveTo(Group fromGroup, int from, Group toGroup, int to, int count, Group group) const; @@ -347,7 +347,7 @@ inline QQmlListCompositor::insert_iterator::insert_iterator( Range *range, int offset, Group group, int groupCount) : iterator(range, offset, group, groupCount) {} -inline QQmlListCompositor::Change::Change(iterator it, int count, uint flags, int moveId) +inline QQmlListCompositor::Change::Change(const iterator &it, int count, uint flags, int moveId) : count(count), flags(flags), moveId(moveId) { for (int i = 0; i < MaximumGroupCount; ++i) diff --git a/src/qml/util/qqmlpropertymap.cpp b/src/qml/util/qqmlpropertymap.cpp index 2e95ab7cb5..28f3c8f215 100644 --- a/src/qml/util/qqmlpropertymap.cpp +++ b/src/qml/util/qqmlpropertymap.cpp @@ -172,8 +172,7 @@ int QQmlPropertyMapMetaObject::createProperty(const char *name, const char *valu modify or clear its associated value. \note When deriving a class from QQmlPropertyMap, use the - \l {QQmlPropertyMap::QQmlPropertyMap(DerivedType *derived, QObject *parent)} - {protected two-argument constructor} + \l {QQmlPropertyMap::QQmlPropertyMap(DerivedType *derived, QObject *parent)} {protected two-argument constructor} which ensures that the class is correctly registered with the Qt \l {Meta-Object System}. */ |