diff options
Diffstat (limited to 'src/qml')
195 files changed, 10204 insertions, 6955 deletions
diff --git a/src/qml/animations/qabstractanimationjob.cpp b/src/qml/animations/qabstractanimationjob.cpp index 4bd02c1934..f7f6939e4b 100644 --- a/src/qml/animations/qabstractanimationjob.cpp +++ b/src/qml/animations/qabstractanimationjob.cpp @@ -588,8 +588,7 @@ void QAbstractAnimationJob::updateDirection(QAbstractAnimationJob::Direction dir void QAbstractAnimationJob::finished() { //TODO: update this code so it is valid to delete the animation in animationFinished - for (int i = 0; i < changeListeners.count(); ++i) { - const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i); + for (const auto &change : changeListeners) { if (change.types & QAbstractAnimationJob::Completion) { RETURN_IF_DELETED(change.listener->animationFinished(this)); } @@ -603,8 +602,7 @@ void QAbstractAnimationJob::finished() void QAbstractAnimationJob::stateChanged(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState) { - for (int i = 0; i < changeListeners.count(); ++i) { - const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i); + for (const auto &change : changeListeners) { if (change.types & QAbstractAnimationJob::StateChange) { RETURN_IF_DELETED(change.listener->animationStateChanged(this, newState, oldState)); } @@ -613,8 +611,7 @@ void QAbstractAnimationJob::stateChanged(QAbstractAnimationJob::State newState, void QAbstractAnimationJob::currentLoopChanged() { - for (int i = 0; i < changeListeners.count(); ++i) { - const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i); + for (const auto &change : changeListeners) { if (change.types & QAbstractAnimationJob::CurrentLoop) { RETURN_IF_DELETED(change.listener->animationCurrentLoopChanged(this)); } @@ -625,8 +622,7 @@ void QAbstractAnimationJob::currentTimeChanged(int currentTime) { Q_ASSERT(m_hasCurrentTimeChangeListeners); - for (int i = 0; i < changeListeners.count(); ++i) { - const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i); + for (const auto &change : changeListeners) { if (change.types & QAbstractAnimationJob::CurrentTime) { RETURN_IF_DELETED(change.listener->animationCurrentTimeChanged(this, currentTime)); } @@ -638,17 +634,18 @@ void QAbstractAnimationJob::addAnimationChangeListener(QAnimationJobChangeListen if (changes & QAbstractAnimationJob::CurrentTime) m_hasCurrentTimeChangeListeners = true; - changeListeners.append(ChangeListener(listener, changes)); + changeListeners.push_back(ChangeListener(listener, changes)); } void QAbstractAnimationJob::removeAnimationChangeListener(QAnimationJobChangeListener *listener, QAbstractAnimationJob::ChangeTypes changes) { m_hasCurrentTimeChangeListeners = false; - changeListeners.removeOne(ChangeListener(listener, changes)); + const auto it = std::find(changeListeners.begin(), changeListeners.end(), ChangeListener(listener, changes)); + if (it != changeListeners.end()) + changeListeners.erase(it); - for (int i = 0; i < changeListeners.count(); ++i) { - const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i); + for (const auto &change: changeListeners) { if (change.types & QAbstractAnimationJob::CurrentTime) { m_hasCurrentTimeChangeListeners = true; break; diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h index b04f523585..efc86a5823 100644 --- a/src/qml/animations/qabstractanimationjob_p.h +++ b/src/qml/animations/qabstractanimationjob_p.h @@ -54,7 +54,7 @@ #include <private/qtqmlglobal_p.h> #include <QtCore/QObject> #include <QtCore/private/qabstractanimation_p.h> -#include "private/qpodvector_p.h" +#include <vector> QT_BEGIN_NAMESPACE @@ -164,7 +164,7 @@ protected: QAbstractAnimationJob::ChangeTypes types; bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; } }; - QPODVector<ChangeListener,1> changeListeners; + std::vector<ChangeListener> changeListeners; QAbstractAnimationJob *m_nextSibling; QAbstractAnimationJob *m_previousSibling; diff --git a/src/qml/animations/qanimationgroupjob_p.h b/src/qml/animations/qanimationgroupjob_p.h index 4b94e79d40..9bcd63127a 100644 --- a/src/qml/animations/qanimationgroupjob_p.h +++ b/src/qml/animations/qanimationgroupjob_p.h @@ -52,6 +52,7 @@ // #include "private/qabstractanimationjob_p.h" +#include <QtCore/qdebug.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 585fef7603..ad73c26b15 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -26,12 +26,16 @@ SOURCES += \ HEADERS += \ $$PWD/qqmltypecompiler_p.h \ $$PWD/qv4isel_moth_p.h \ - $$PWD/qv4instr_moth_p.h + $$PWD/qv4instr_moth_p.h \ + $$PWD/qqmlpropertycachecreator_p.h \ + $$PWD/qqmlpropertyvalidator_p.h SOURCES += \ $$PWD/qqmltypecompiler.cpp \ $$PWD/qv4instr_moth.cpp \ - $$PWD/qv4isel_moth.cpp + $$PWD/qv4isel_moth.cpp \ + $$PWD/qqmlpropertycachecreator.cpp \ + $$PWD/qqmlpropertyvalidator.cpp } diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index eaf0e72296..f736e04b88 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -49,7 +49,6 @@ #include <private/qqmlglobal_p.h> #include <private/qqmltypeloader_p.h> #include <private/qqmlengine_p.h> -#include <private/qqmlcompiler_p.h> #endif #ifdef CONST @@ -72,21 +71,24 @@ using namespace QmlIR; return false; \ } -void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int id, const QQmlJS::AST::SourceLocation &loc) +void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &loc) { inheritedTypeNameIndex = typeNameIndex; location.line = loc.startLine; location.column = loc.startColumn; - idIndex = id; - indexOfDefaultProperty = -1; + idNameIndex = idIndex; + id = -1; + indexOfDefaultPropertyOrAlias = -1; + defaultPropertyIsAlias = false; + flags = QV4::CompiledData::Object::NoFlag; properties = pool->New<PoolList<Property> >(); + aliases = pool->New<PoolList<Alias> >(); qmlSignals = pool->New<PoolList<Signal> >(); bindings = pool->New<PoolList<Binding> >(); functions = pool->New<PoolList<Function> >(); functionsAndExpressions = pool->New<PoolList<CompiledFunctionOrExpression> >(); - runtimeFunctionIndices = 0; declarationsOverride = 0; } @@ -145,15 +147,42 @@ QString Object::appendProperty(Property *prop, const QString &propertyName, bool const int index = target->properties->append(prop); if (isDefaultProperty) { - if (target->indexOfDefaultProperty != -1) { + if (target->indexOfDefaultPropertyOrAlias != -1) { *errorLocation = defaultToken; return tr("Duplicate default property"); } - target->indexOfDefaultProperty = index; + target->indexOfDefaultPropertyOrAlias = index; } return QString(); // no error } +QString Object::appendAlias(Alias *alias, const QString &aliasName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation) +{ + Object *target = declarationsOverride; + if (!target) + target = this; + + for (Alias *p = target->aliases->first; p; p = p->next) + if (p->nameIndex == alias->nameIndex) + return tr("Duplicate alias name"); + + if (aliasName.constData()->isUpper()) + return tr("Alias names cannot begin with an upper case letter"); + + const int index = target->aliases->append(alias); + + if (isDefaultProperty) { + if (target->indexOfDefaultPropertyOrAlias != -1) { + *errorLocation = defaultToken; + return tr("Duplicate default property"); + } + target->indexOfDefaultPropertyOrAlias = index; + target->defaultPropertyIsAlias = true; + } + + return QString(); // no error +} + void Object::appendFunction(QmlIR::Function *f) { Object *target = declarationsOverride; @@ -164,7 +193,7 @@ void Object::appendFunction(QmlIR::Function *f) QString Object::appendBinding(Binding *b, bool isListBinding) { - const bool bindingToDefaultProperty = (b->propertyNameIndex == 0); + const bool bindingToDefaultProperty = (b->propertyNameIndex == quint32(0)); if (!isListBinding && !bindingToDefaultProperty && b->type != QV4::CompiledData::Binding::Type_GroupProperty && b->type != QV4::CompiledData::Binding::Type_AttachedProperty @@ -190,7 +219,7 @@ Binding *Object::findBinding(quint32 nameIndex) const void Object::insertSorted(Binding *b) { - Binding *insertionPoint = bindings->findSortedInsertionPoint<QV4::CompiledData::Location, QV4::CompiledData::Binding, &QV4::CompiledData::Binding::valueLocation>(b); + Binding *insertionPoint = bindings->findSortedInsertionPoint<quint32, Binding, &Binding::offset>(b); bindings->insertAfter(insertionPoint, b); } @@ -222,32 +251,6 @@ static void replaceWithSpace(QString &str, int idx, int n) *data++ = space; } -void Document::collectTypeReferences() -{ - foreach (Object *obj, objects) { - if (obj->inheritedTypeNameIndex != emptyStringIndex) { - QV4::CompiledData::TypeReference &r = typeReferences.add(obj->inheritedTypeNameIndex, obj->location); - r.needsCreation = true; - r.errorWhenNotFound = true; - } - - for (const Property *prop = obj->firstProperty(); prop; prop = prop->next) { - if (prop->type >= QV4::CompiledData::Property::Custom) { - // ### FIXME: We could report the more accurate location here by using prop->location, but the old - // compiler can't and the tests expect it to be the object location right now. - QV4::CompiledData::TypeReference &r = typeReferences.add(prop->customTypeNameIndex, obj->location); - r.needsCreation = true; - r.errorWhenNotFound = true; - } - } - - for (const Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) - typeReferences.add(binding->propertyNameIndex, binding->location); - } - } -} - void Document::removeScriptPragmas(QString &script) { const QLatin1String pragma("pragma"); @@ -637,7 +640,10 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node) } if (node->versionToken.isValid()) { - extractVersion(textRefAt(node->versionToken), &import->majorVersion, &import->minorVersion); + int major, minor; + extractVersion(textRefAt(node->versionToken), &major, &minor); + import->majorVersion = major; + import->minorVersion = minor; } else if (import->type == QV4::CompiledData::Import::ImportLibrary) { recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Library import requires a version")); return false; @@ -807,130 +813,87 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) } } else { const QStringRef &memberType = node->memberType; - const QStringRef &name = node->name; - - bool typeFound = false; - QV4::CompiledData::Property::Type type; - if (memberType == QLatin1String("alias")) { - type = QV4::CompiledData::Property::Alias; - typeFound = true; - } + return appendAlias(node); + } else { + const QStringRef &name = node->name; - for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) { - const TypeNameToType *t = propTypeNameToTypes + ii; - if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) { - type = t->type; - typeFound = true; + bool typeFound = false; + QV4::CompiledData::Property::Type type; + + for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) { + const TypeNameToType *t = propTypeNameToTypes + ii; + if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) { + type = t->type; + typeFound = true; + } } - } - if (!typeFound && memberType.at(0).isUpper()) { - const QStringRef &typeModifier = node->typeModifier; + if (!typeFound && memberType.at(0).isUpper()) { + const QStringRef &typeModifier = node->typeModifier; - if (typeModifier.isEmpty()) { - type = QV4::CompiledData::Property::Custom; - } else if (typeModifier == QLatin1String("list")) { - type = QV4::CompiledData::Property::CustomList; - } else { - recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier")); + if (typeModifier.isEmpty()) { + type = QV4::CompiledData::Property::Custom; + } else if (typeModifier == QLatin1String("list")) { + type = QV4::CompiledData::Property::CustomList; + } else { + recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier")); + return false; + } + typeFound = true; + } else if (!node->typeModifier.isNull()) { + recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Unexpected property type modifier")); return false; } - typeFound = true; - } else if (!node->typeModifier.isNull()) { - recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Unexpected property type modifier")); - return false; - } - - if (!typeFound) { - recordError(node->typeToken, QCoreApplication::translate("QQmlParser","Expected property type")); - return false; - } - - Property *property = New<Property>(); - property->flags = 0; - if (node->isReadonlyMember) - property->flags |= QV4::CompiledData::Property::IsReadOnly; - property->type = type; - if (type >= QV4::CompiledData::Property::Custom) - property->customTypeNameIndex = registerString(memberType.toString()); - else - property->customTypeNameIndex = emptyStringIndex; - const QString propName = name.toString(); - property->nameIndex = registerString(propName); - - QQmlJS::AST::SourceLocation loc = node->firstSourceLocation(); - property->location.line = loc.startLine; - property->location.column = loc.startColumn; - - property->aliasPropertyValueIndex = emptyStringIndex; - - if (type == QV4::CompiledData::Property::Alias) { - if (!node->statement && !node->binding) - COMPILE_EXCEPTION(loc, tr("No property alias location")); + if (!typeFound) { + recordError(node->typeToken, QCoreApplication::translate("QQmlParser","Expected property type")); + return false; + } - QQmlJS::AST::SourceLocation rhsLoc; - if (node->binding) - rhsLoc = node->binding->firstSourceLocation(); - else if (node->statement) - rhsLoc = node->statement->firstSourceLocation(); + Property *property = New<Property>(); + property->flags = 0; + if (node->isReadonlyMember) + property->flags |= QV4::CompiledData::Property::IsReadOnly; + property->type = type; + if (type >= QV4::CompiledData::Property::Custom) + property->customTypeNameIndex = registerString(memberType.toString()); else - rhsLoc = node->semicolonToken; - property->aliasLocation.line = rhsLoc.startLine; - property->aliasLocation.column = rhsLoc.startColumn; - - QStringList alias; - - if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(node->statement)) { - alias = astNodeToStringList(stmt->expression); - if (alias.isEmpty()) { - if (isStatementNodeScript(node->statement)) { - COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); - } else { - COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias location")); - } - } - } else { - COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); - } + property->customTypeNameIndex = emptyStringIndex; - if (alias.count() < 1 || alias.count() > 3) - COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); + const QString propName = name.toString(); + property->nameIndex = registerString(propName); - property->aliasIdValueIndex = registerString(alias.first()); + QQmlJS::AST::SourceLocation loc = node->firstSourceLocation(); + property->location.line = loc.startLine; + property->location.column = loc.startColumn; - QString propertyValue = alias.value(1); - if (alias.count() == 3) { - propertyValue += QLatin1Char('.'); - propertyValue += alias.at(2); - } - property->aliasPropertyValueIndex = registerString(propertyValue); - } - QQmlJS::AST::SourceLocation errorLocation; - QString error; + QQmlJS::AST::SourceLocation errorLocation; + QString error; - if (illegalNames.contains(propName)) - error = tr("Illegal property name"); - else - error = _object->appendProperty(property, propName, node->isDefaultMember, node->defaultToken, &errorLocation); + if (illegalNames.contains(propName)) + error = tr("Illegal property name"); + else + error = _object->appendProperty(property, propName, node->isDefaultMember, node->defaultToken, &errorLocation); - if (!error.isEmpty()) { - if (errorLocation.startLine == 0) - errorLocation = node->identifierToken; + if (!error.isEmpty()) { + if (errorLocation.startLine == 0) + errorLocation = node->identifierToken; - recordError(errorLocation, error); - return false; - } + recordError(errorLocation, error); + return false; + } - qSwap(_propertyDeclaration, property); - if (node->binding) { - // process QML-like initializers (e.g. property Object o: Object {}) - QQmlJS::AST::Node::accept(node->binding, this); - } else if (node->statement && type != QV4::CompiledData::Property::Alias) { - appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement); + qSwap(_propertyDeclaration, property); + if (node->binding) { + // process QML-like initializers (e.g. property Object o: Object {}) + QQmlJS::AST::Node::accept(node->binding, this); + } else if (node->statement) { + if (!isRedundantNullInitializerForPropertyDeclaration(_propertyDeclaration, node->statement)) + appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement); + } + qSwap(_propertyDeclaration, property); } - qSwap(_propertyDeclaration, property); } return false; @@ -952,6 +915,16 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) f->location.column = loc.startColumn; f->index = index; f->nameIndex = registerString(funDecl->name.toString()); + + int formalsCount = 0; + for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next) + ++formalsCount; + f->formals.allocate(pool, formalsCount); + + int i = 0; + for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next, ++i) + f->formals[i] = registerString(it->name.toString()); + _object->appendFunction(f); } else { recordError(node->firstSourceLocation(), QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element")); @@ -1027,13 +1000,13 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST binding->value.b = false; } else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr)) { binding->type = QV4::CompiledData::Binding::Type_Number; - binding->value.d = lit->value; + binding->setNumberValueInternal(lit->value); } else { if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) { if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) { binding->type = QV4::CompiledData::Binding::Type_Number; - binding->value.d = -lit->value; + binding->setNumberValueInternal(-lit->value); } } } @@ -1085,6 +1058,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo { Binding *binding = New<Binding>(); binding->propertyNameIndex = propertyNameIndex; + binding->offset = nameLocation.offset; binding->location.line = nameLocation.startLine; binding->location.column = nameLocation.startColumn; binding->flags = 0; @@ -1104,6 +1078,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo Binding *binding = New<Binding>(); binding->propertyNameIndex = propertyNameIndex; + binding->offset = nameLocation.offset; binding->location.line = nameLocation.startLine; binding->location.column = nameLocation.startColumn; @@ -1133,6 +1108,79 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo } } +bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node) +{ + Alias *alias = New<Alias>(); + alias->flags = 0; + if (node->isReadonlyMember) + alias->flags |= QV4::CompiledData::Alias::IsReadOnly; + + const QString propName = node->name.toString(); + alias->nameIndex = registerString(propName); + + QQmlJS::AST::SourceLocation loc = node->firstSourceLocation(); + alias->location.line = loc.startLine; + alias->location.column = loc.startColumn; + + alias->propertyNameIndex = emptyStringIndex; + + if (!node->statement && !node->binding) + COMPILE_EXCEPTION(loc, tr("No property alias location")); + + QQmlJS::AST::SourceLocation rhsLoc; + if (node->binding) + rhsLoc = node->binding->firstSourceLocation(); + else if (node->statement) + rhsLoc = node->statement->firstSourceLocation(); + else + rhsLoc = node->semicolonToken; + alias->referenceLocation.line = rhsLoc.startLine; + alias->referenceLocation.column = rhsLoc.startColumn; + + QStringList aliasReference; + + if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(node->statement)) { + aliasReference = astNodeToStringList(stmt->expression); + if (aliasReference.isEmpty()) { + if (isStatementNodeScript(node->statement)) { + COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); + } else { + COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias location")); + } + } + } else { + COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); + } + + if (aliasReference.count() < 1 || aliasReference.count() > 3) + COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); + + alias->idIndex = registerString(aliasReference.first()); + + QString propertyValue = aliasReference.value(1); + if (aliasReference.count() == 3) + propertyValue += QLatin1Char('.') + aliasReference.at(2); + alias->propertyNameIndex = registerString(propertyValue); + + QQmlJS::AST::SourceLocation errorLocation; + QString error; + + if (illegalNames.contains(propName)) + error = tr("Illegal property name"); + else + error = _object->appendAlias(alias, propName, node->isDefaultMember, node->defaultToken, &errorLocation); + + if (!error.isEmpty()) { + if (errorLocation.startLine == 0) + errorLocation = node->identifierToken; + + recordError(errorLocation, error); + return false; + } + + return false; +} + Object *IRBuilder::bindingsTarget() const { if (_propertyDeclaration && _object->declarationsOverride) @@ -1178,10 +1226,10 @@ bool IRBuilder::setId(const QQmlJS::AST::SourceLocation &idLocation, QQmlJS::AST if (illegalNames.contains(idQString)) COMPILE_EXCEPTION(loc, tr( "ID illegally masks global JavaScript property")); - if (_object->idIndex != emptyStringIndex) + if (_object->idNameIndex != emptyStringIndex) COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times")); - _object->idIndex = registerString(idQString); + _object->idNameIndex = registerString(idQString); _object->locationOfIdProperty.line = idLocation.startLine; _object->locationOfIdProperty.column = idLocation.startColumn; @@ -1202,8 +1250,7 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O if (import->qualifierIndex != emptyStringIndex && stringAt(import->qualifierIndex) == currentName) { qualifiedIdElement = qualifiedIdElement->next; - currentName += QLatin1Char('.'); - currentName += qualifiedIdElement->name; + currentName += QLatin1Char('.') + qualifiedIdElement->name; if (!qualifiedIdElement->name.unicode()->isUpper()) COMPILE_EXCEPTION(qualifiedIdElement->firstSourceLocation(), tr("Expected type name")); @@ -1229,6 +1276,7 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O if (!binding) { binding = New<Binding>(); binding->propertyNameIndex = propertyNameIndex; + binding->offset = qualifiedIdElement->identifierToken.offset; binding->location.line = qualifiedIdElement->identifierToken.startLine; binding->location.column = qualifiedIdElement->identifierToken.startColumn; binding->valueLocation.line = qualifiedIdElement->next->identifierToken.startLine; @@ -1300,6 +1348,17 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement) return true; } +bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement) +{ + if (property->type != QV4::CompiledData::Property::Custom) + return false; + QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement); + if (!exprStmt) + return false; + QQmlJS::AST::ExpressionNode * const expr = exprStmt->expression; + return QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr); +} + QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) { QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit; @@ -1309,12 +1368,12 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) const int importSize = sizeof(QV4::CompiledData::Import) * output.imports.count(); const int objectOffsetTableSize = output.objects.count() * sizeof(quint32); - QHash<Object*, quint32> objectOffsets; + QHash<const Object*, quint32> objectOffsets; int objectsSize = 0; foreach (Object *o, output.objects) { objectOffsets.insert(o, unitSize + importSize + objectOffsetTableSize + objectsSize); - objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount()); + objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count); int signalTableSize = 0; for (const Signal *s = o->firstSignal(); s; s = s->next) @@ -1354,13 +1413,17 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) // write objects quint32 *objectTable = reinterpret_cast<quint32*>(data + qmlUnit->offsetToObjects); char *objectPtr = data + qmlUnit->offsetToObjects + objectOffsetTableSize; - foreach (Object *o, output.objects) { + for (int i = 0; i < output.objects.count(); ++i) { + const Object *o = output.objects.at(i); *objectTable++ = objectOffsets.value(o); QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr); objectToWrite->inheritedTypeNameIndex = o->inheritedTypeNameIndex; - objectToWrite->indexOfDefaultProperty = o->indexOfDefaultProperty; - objectToWrite->idIndex = o->idIndex; + objectToWrite->indexOfDefaultPropertyOrAlias = o->indexOfDefaultPropertyOrAlias; + objectToWrite->defaultPropertyIsAlias = o->defaultPropertyIsAlias; + objectToWrite->flags = o->flags; + objectToWrite->idNameIndex = o->idNameIndex; + objectToWrite->id = o->id; objectToWrite->location = o->location; objectToWrite->locationOfIdProperty = o->locationOfIdProperty; @@ -1374,6 +1437,10 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) objectToWrite->offsetToProperties = nextOffset; nextOffset += objectToWrite->nProperties * sizeof(QV4::CompiledData::Property); + objectToWrite->nAliases = o->aliasCount(); + objectToWrite->offsetToAliases = nextOffset; + nextOffset += objectToWrite->nAliases * sizeof(QV4::CompiledData::Alias); + objectToWrite->nSignals = o->signalCount(); objectToWrite->offsetToSignals = nextOffset; nextOffset += objectToWrite->nSignals * sizeof(quint32); @@ -1382,9 +1449,13 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) objectToWrite->offsetToBindings = nextOffset; nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding); + objectToWrite->nNamedObjectsInComponent = o->namedObjectsInComponent.count; + objectToWrite->offsetToNamedObjectsInComponent = nextOffset; + nextOffset += objectToWrite->nNamedObjectsInComponent * sizeof(quint32); + quint32 *functionsTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToFunctions); for (const Function *f = o->firstFunction(); f; f = f->next) - *functionsTable++ = o->runtimeFunctionIndices->at(f->index); + *functionsTable++ = o->runtimeFunctionIndices.at(f->index); char *propertiesPtr = objectPtr + objectToWrite->offsetToProperties; for (const Property *p = o->firstProperty(); p; p = p->next) { @@ -1393,6 +1464,13 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) propertiesPtr += sizeof(QV4::CompiledData::Property); } + char *aliasesPtr = objectPtr + objectToWrite->offsetToAliases; + for (const Alias *a = o->firstAlias(); a; a = a->next) { + QV4::CompiledData::Alias *aliasToWrite = reinterpret_cast<QV4::CompiledData::Alias*>(aliasesPtr); + *aliasToWrite = *a; + aliasesPtr += sizeof(QV4::CompiledData::Alias); + } + char *bindingPtr = objectPtr + objectToWrite->offsetToBindings; bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isValueBindingNoAlias); bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isSignalHandler); @@ -1421,7 +1499,12 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) signalPtr += size; } - objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount()); + quint32 *namedObjectInComponentPtr = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToNamedObjectsInComponent); + for (int i = 0; i < o->namedObjectsInComponent.count; ++i) { + *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i); + } + + objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count); objectPtr += signalTableSize; } @@ -1438,7 +1521,7 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) return qmlUnit; } -char *QmlUnitGenerator::writeBindings(char *bindingPtr, Object *o, BindingFilter filter) const +char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const { for (const Binding *b = o->firstBinding(); b; b = b->next) { if (!(b->*(filter))()) @@ -1446,7 +1529,7 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, Object *o, BindingFilter QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr); *bindingToWrite = *b; if (b->type == QV4::CompiledData::Binding::Type_Script) - bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices->at(b->value.compiledScriptIndex); + bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices.at(b->value.compiledScriptIndex); bindingPtr += sizeof(QV4::CompiledData::Binding); } return bindingPtr; @@ -1614,7 +1697,7 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, if (tdata->isComplete()) { auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>(); newResolver->owner = resolver->owner; - initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compiledData()->metaTypeId)); + initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compilationUnit()->metaTypeId)); newResolver->flags |= AllPropertiesAreFinal; return newResolver->resolveMember(qmlEngine, newResolver, member); } @@ -1822,7 +1905,9 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int // Look for IDs first. foreach (const IdMapping &mapping, _idObjects) if (name == mapping.name) { - _function->idObjectDependencies.insert(mapping.idIndex); + if (_function->isQmlBinding) + _function->idObjectDependencies.insert(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); @@ -1907,7 +1992,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int #ifndef V4_BOOTSTRAP -QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) +QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) const { if (notInRevision) *notInRevision = false; @@ -1926,7 +2011,7 @@ QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRev } -QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) +QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) const { if (notInRevision) *notInRevision = false; diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 057ed1be9f..821a6bba6d 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -113,7 +113,7 @@ struct PoolList T *insertPos = 0; for (T *it = first; it; it = it->next) { - if (!(it->*sortMember < item->*sortMember)) + if (!(it->*sortMember <= item->*sortMember)) break; insertPos = it; } @@ -161,6 +161,43 @@ struct PoolList } return result; } + + struct Iterator { + T *ptr; + + explicit Iterator(T *p) : ptr(p) {} + + T *operator->() { + return ptr; + } + + const T *operator->() const { + return ptr; + } + + T &operator*() { + return *ptr; + } + + const T &operator*() const { + return *ptr; + } + + void operator++() { + ptr = ptr->next; + } + + bool operator==(const Iterator &rhs) const { + return ptr == rhs.ptr; + } + + bool operator!=(const Iterator &rhs) const { + return ptr != rhs.ptr; + } + }; + + Iterator begin() { return Iterator(first); } + Iterator end() { return Iterator(nullptr); } }; template <typename T> @@ -170,7 +207,18 @@ class FixedPoolArray public: int count; - void init(QQmlJS::MemoryPool *pool, const QVector<T> &vector) + FixedPoolArray() + : data(0) + , count(0) + {} + + void allocate(QQmlJS::MemoryPool *pool, int size) + { + count = size; + data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); + } + + void allocate(QQmlJS::MemoryPool *pool, const QVector<T> &vector) { count = vector.count(); data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); @@ -183,6 +231,16 @@ public: } } + template <typename Container> + void allocate(QQmlJS::MemoryPool *pool, const Container &container) + { + count = container.count(); + data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); + typename Container::ConstIterator it = container.constBegin(); + for (int i = 0; i < count; ++i) + new (data + i) T(*it++); + } + const T &at(int index) const { Q_ASSERT(index >= 0 && index < count); return data[index]; @@ -200,6 +258,9 @@ public: return i; return -1; } + + const T *begin() const { return data; } + const T *end() const { return data + count; } }; struct Object; @@ -217,6 +278,10 @@ struct Signal QStringList parameterStringList(const QV4::Compiler::StringTableGenerator *stringPool) const; + int parameterCount() const { return parameters->count; } + PoolList<SignalParameter>::Iterator parametersBegin() const { return parameters->begin(); } + PoolList<SignalParameter>::Iterator parametersEnd() const { return parameters->end(); } + Signal *next; }; @@ -227,16 +292,32 @@ struct Property : public QV4::CompiledData::Property struct Binding : public QV4::CompiledData::Binding { + // The offset in the source file where the binding appeared. This is used for sorting to ensure + // that assignments to list properties are done in the correct order. We use the offset here instead + // of Binding::location as the latter has limited precision. + quint32 offset; // Binding's compiledScriptIndex is index in object's functionsAndExpressions Binding *next; }; +struct Alias : public QV4::CompiledData::Alias +{ + Alias *next; +}; + struct Function { QQmlJS::AST::FunctionDeclaration *functionDeclaration; QV4::CompiledData::Location location; int nameIndex; quint32 index; // index in parsedQML::functions + FixedPoolArray<int> formals; + + // --- QQmlPropertyCacheCreator interface + const int *formalsBegin() const { return formals.begin(); } + const int *formalsEnd() const { return formals.end(); } + // --- + Function *next; }; @@ -265,14 +346,19 @@ struct Q_QML_PRIVATE_EXPORT Object Q_DECLARE_TR_FUNCTIONS(Object) public: quint32 inheritedTypeNameIndex; - quint32 idIndex; - int indexOfDefaultProperty; + quint32 idNameIndex; + int id; + int indexOfDefaultPropertyOrAlias; + bool defaultPropertyIsAlias; + quint32 flags; QV4::CompiledData::Location location; QV4::CompiledData::Location locationOfIdProperty; const Property *firstProperty() const { return properties->first; } int propertyCount() const { return properties->count; } + Alias *firstAlias() const { return aliases->first; } + int aliasCount() const { return aliases->count; } const Signal *firstSignal() const { return qmlSignals->first; } int signalCount() const { return qmlSignals->count; } Binding *firstBinding() const { return bindings->first; } @@ -280,16 +366,28 @@ public: const Function *firstFunction() const { return functions->first; } int functionCount() const { return functions->count; } + PoolList<Binding>::Iterator bindingsBegin() const { return bindings->begin(); } + PoolList<Binding>::Iterator bindingsEnd() const { return bindings->end(); } + PoolList<Property>::Iterator propertiesBegin() const { return properties->begin(); } + PoolList<Property>::Iterator propertiesEnd() const { return properties->end(); } + PoolList<Alias>::Iterator aliasesBegin() const { return aliases->begin(); } + PoolList<Alias>::Iterator aliasesEnd() const { return aliases->end(); } + PoolList<Signal>::Iterator signalsBegin() const { return qmlSignals->begin(); } + PoolList<Signal>::Iterator signalsEnd() const { return qmlSignals->end(); } + PoolList<Function>::Iterator functionsBegin() const { return functions->begin(); } + PoolList<Function>::Iterator functionsEnd() const { return functions->end(); } + // If set, then declarations for this object (and init bindings for these) should go into the // specified object. Used for declarations inside group properties. Object *declarationsOverride; - void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int id, const QQmlJS::AST::SourceLocation &location = QQmlJS::AST::SourceLocation()); + void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &location = QQmlJS::AST::SourceLocation()); QString sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation); QString appendSignal(Signal *signal); QString appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation); + QString appendAlias(Alias *prop, const QString &aliasName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation); void appendFunction(QmlIR::Function *f); QString appendBinding(Binding *b, bool isListBinding); @@ -299,12 +397,17 @@ public: QString bindingAsString(Document *doc, int scriptIndex) const; PoolList<CompiledFunctionOrExpression> *functionsAndExpressions; - FixedPoolArray<int> *runtimeFunctionIndices; + FixedPoolArray<int> runtimeFunctionIndices; + + FixedPoolArray<quint32> namedObjectsInComponent; + int namedObjectsInComponentCount() const { return namedObjectsInComponent.count; } + const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); } private: friend struct IRLoader; PoolList<Property> *properties; + PoolList<Alias> *aliases; PoolList<Signal> *qmlSignals; PoolList<Binding> *bindings; PoolList<Function> *functions; @@ -330,15 +433,11 @@ struct Q_QML_PRIVATE_EXPORT Document QList<Pragma*> pragmas; QQmlJS::AST::UiProgram *program; int indexOfRootObject; - QList<Object*> objects; + QVector<Object*> objects; QV4::Compiler::JSUnitGenerator jsGenerator; quint32 unitFlags; QQmlRefPointer<QV4::CompiledData::CompilationUnit> javaScriptCompilationUnit; - QHash<int, QStringList> extraSignalParameters; - - QV4::CompiledData::TypeReferenceMap typeReferences; - void collectTypeReferences(); int registerString(const QString &str) { return jsGenerator.registerString(str); } QString stringAt(int index) const { return jsGenerator.stringForIndex(index); } @@ -410,6 +509,8 @@ public: void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value); void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false); + bool appendAlias(QQmlJS::AST::UiPublicMember *node); + Object *bindingsTarget() const; bool setId(const QQmlJS::AST::SourceLocation &idLocation, QQmlJS::AST::Statement *value); @@ -426,6 +527,7 @@ public: QString stringAt(int index) const { return jsGenerator->stringForIndex(index); } static bool isStatementNodeScript(QQmlJS::AST::Statement *statement); + static bool isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement); QList<QQmlJS::DiagnosticMessage> errors; @@ -433,7 +535,7 @@ public: QList<const QV4::CompiledData::Import *> _imports; QList<Pragma*> _pragmas; - QList<Object*> _objects; + QVector<Object*> _objects; QV4::CompiledData::TypeReferenceMap _typeReferences; @@ -451,17 +553,17 @@ struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator private: typedef bool (Binding::*BindingFilter)() const; - char *writeBindings(char *bindingPtr, Object *o, BindingFilter filter) const; + char *writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const; }; #ifndef V4_BOOTSTRAP struct Q_QML_EXPORT PropertyResolver { - PropertyResolver(QQmlPropertyCache *cache) + PropertyResolver(const QQmlPropertyCache *cache) : cache(cache) {} - QQmlPropertyData *property(int index) + QQmlPropertyData *property(int index) const { return cache->property(index); } @@ -471,12 +573,12 @@ struct Q_QML_EXPORT PropertyResolver IgnoreRevision }; - QQmlPropertyData *property(const QString &name, bool *notInRevision = 0, RevisionCheck check = CheckRevision); + QQmlPropertyData *property(const QString &name, bool *notInRevision = 0, RevisionCheck check = CheckRevision) const; // This code must match the semantics of QQmlPropertyPrivate::findSignalByName - QQmlPropertyData *signal(const QString &name, bool *notInRevision); + QQmlPropertyData *signal(const QString &name, bool *notInRevision) const; - QQmlPropertyCache *cache; + const QQmlPropertyCache *cache; }; #endif diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp index 9fcba64038..f8d63ec634 100644 --- a/src/qml/jsruntime/qv4debugging.cpp +++ b/src/qml/compiler/qqmlpropertycachecreator.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the tools applications of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -37,24 +37,35 @@ ** ****************************************************************************/ -#include "qv4debugging_p.h" -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include "qv4function_p.h" -#include "qv4instr_moth_p.h" -#include "qv4runtime_p.h" -#include "qv4script_p.h" -#include "qv4identifier_p.h" -#include "qv4string_p.h" -#include "qv4objectiterator_p.h" +#include "qqmlpropertycachecreator_p.h" -#include <iostream> -#include <algorithm> - -#include <QtCore/QJsonArray> -#include <QtCore/QJsonDocument> -#include <QtCore/QJsonValue> +#include <private/qqmlengine_p.h> QT_BEGIN_NAMESPACE +QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0); + +QQmlBindingInstantiationContext::QQmlBindingInstantiationContext() + : referencingObjectIndex(-1) + , instantiatingBinding(nullptr) + , instantiatingProperty(nullptr) +{ + +} + +QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache) + : referencingObjectIndex(referencingObjectIndex) + , instantiatingBinding(instantiatingBinding) + , instantiatingProperty(nullptr) +{ + if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) { + Q_ASSERT(referencingObjectIndex >= 0); + Q_ASSERT(referencingObjectPropertyCache); + Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); + + bool notInRevision = false; + instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, ¬InRevision); + } +} + QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h new file mode 100644 index 0000000000..09a093d911 --- /dev/null +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -0,0 +1,746 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QQMLPROPERTYCACHECREATOR_P_H +#define QQMLPROPERTYCACHECREATOR_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 "qqmltypecompiler_p.h" +#include <private/qqmlvaluetype_p.h> +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +struct QQmlBindingInstantiationContext { + QQmlBindingInstantiationContext(); + QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache); + int referencingObjectIndex; + const QV4::CompiledData::Binding *instantiatingBinding; + QQmlPropertyData *instantiatingProperty; +}; + +struct QQmlPropertyCacheCreatorBase +{ + Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase) +public: + static QAtomicInt classIndexCounter; +}; + +template <typename ObjectContainer> +class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase +{ +public: + typedef typename ObjectContainer::CompiledObject CompiledObject; + + QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const ObjectContainer *objectContainer, const QQmlImports *imports); + + QQmlCompileError buildMetaObjects(); + +protected: + QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context); + QQmlPropertyCache *propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const; + QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache); + + QString stringAt(int index) const { return objectContainer->stringAt(index); } + + QQmlEnginePrivate * const enginePrivate; + const ObjectContainer * const objectContainer; + const QQmlImports * const imports; + QQmlPropertyCacheVector *propertyCaches; +}; + +template <typename ObjectContainer> +inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const ObjectContainer *objectContainer, const QQmlImports *imports) + : enginePrivate(enginePrivate) + , objectContainer(objectContainer) + , imports(imports) + , propertyCaches(propertyCaches) +{ + propertyCaches->resize(objectContainer->objectCount()); +} + +template <typename ObjectContainer> +inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects() +{ + QQmlBindingInstantiationContext context; + return buildMetaObjectRecursively(objectContainer->rootObjectIndex(), context); +} + +template <typename ObjectContainer> +inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) +{ + const CompiledObject *obj = objectContainer->objectAt(objectIndex); + + bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0; + if (!needVMEMetaObject) { + for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) { + if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { + // If the on assignment is inside a group property, we need to distinguish between QObject based + // group properties and value type group properties. For the former the base type is derived from + // the property that references us, for the latter we only need a meta-object on the referencing object + // because interceptors can't go to the shared value type instances. + if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType)) { + if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) { + const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex); + auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache); + if (error.isSet()) + return error; + } + } else { + // On assignments are implemented using value interceptors, which require a VME meta object. + needVMEMetaObject = true; + } + break; + } + } + } + + QQmlPropertyCache *baseTypeCache; + { + QQmlCompileError error; + baseTypeCache = propertyCacheForObject(obj, context, &error); + if (error.isSet()) + return error; + } + + if (baseTypeCache) { + if (needVMEMetaObject) { + QQmlCompileError error = createMetaObject(objectIndex, obj, baseTypeCache); + if (error.isSet()) + return error; + } else { + propertyCaches->set(objectIndex, baseTypeCache); + } + } + + if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) { + for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) + if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); + QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context); + if (error.isSet()) + return error; + } + } + + QQmlCompileError noError; + return noError; +} + +template <typename ObjectContainer> +inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const +{ + if (context.instantiatingProperty) { + if (context.instantiatingProperty->isQObject()) { + return enginePrivate->rawPropertyCacheForType(context.instantiatingProperty->propType); + } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(context.instantiatingProperty->propType)) { + return enginePrivate->cache(vtmo); + } + } else if (obj->inheritedTypeNameIndex != 0) { + auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + + if (typeRef->isFullyDynamicType) { + if (obj->propertyCount() > 0 || obj->aliasCount() > 0) { + *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties.")); + return nullptr; + } + if (obj->signalCount() > 0) { + *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals.")); + return nullptr; + } + if (obj->functionCount() > 0) { + *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions.")); + return nullptr; + } + } + + return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) { + auto *typeRef = objectContainer->resolvedTypes.value(context.instantiatingBinding->propertyNameIndex); + Q_ASSERT(typeRef); + QQmlType *qmltype = typeRef->type; + if (!qmltype) { + QString propertyName = stringAt(context.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()); + + auto compilationUnit = tdata->compilationUnit(); + qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId); + + tdata->release(); + } + } + } + + const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0; + if (!attachedMo) { + *error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object")); + return nullptr; + } + return enginePrivate->cache(attachedMo); + } + return nullptr; +} + +template <typename ObjectContainer> +inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache) +{ + QQmlRefPointer<QQmlPropertyCache> cache; + cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), + obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), + obj->signalCount() + obj->propertyCount() + obj->aliasCount())); + + propertyCaches->set(objectIndex, cache); + propertyCaches->setNeedsVMEMetaObject(objectIndex); + + struct TypeData { + QV4::CompiledData::Property::Type dtype; + int metaType; + } builtinTypes[] = { + { QV4::CompiledData::Property::Var, QMetaType::QVariant }, + { QV4::CompiledData::Property::Variant, QMetaType::QVariant }, + { QV4::CompiledData::Property::Int, QMetaType::Int }, + { QV4::CompiledData::Property::Bool, QMetaType::Bool }, + { QV4::CompiledData::Property::Real, QMetaType::Double }, + { QV4::CompiledData::Property::String, QMetaType::QString }, + { QV4::CompiledData::Property::Url, QMetaType::QUrl }, + { QV4::CompiledData::Property::Color, QMetaType::QColor }, + { QV4::CompiledData::Property::Font, QMetaType::QFont }, + { QV4::CompiledData::Property::Time, QMetaType::QTime }, + { QV4::CompiledData::Property::Date, QMetaType::QDate }, + { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime }, + { QV4::CompiledData::Property::Rect, QMetaType::QRectF }, + { QV4::CompiledData::Property::Point, QMetaType::QPointF }, + { QV4::CompiledData::Property::Size, QMetaType::QSizeF }, + { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D }, + { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D }, + { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D }, + { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 }, + { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion } +}; + static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); + + QByteArray newClassName; + + if (objectIndex == objectContainer->rootObjectIndex()) { + const QString path = objectContainer->url().path(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + if (lastSlash > -1) { + const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5); + if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) + newClassName = nameBase.toUtf8() + "_QMLTYPE_" + + QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)); + } + } + if (newClassName.isEmpty()) { + newClassName = QQmlMetaObject(baseTypeCache).className(); + newClassName.append("_QML_"); + newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); + } + + cache->_dynamicClassName = newClassName; + + int varPropCount = 0; + + QmlIR::PropertyResolver resolver(baseTypeCache); + + for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p) { + if (p->type == QV4::CompiledData::Property::Var) + varPropCount++; + + bool notInRevision = false; + QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); + if (d && d->isFinal()) + return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); + } + + for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) { + bool notInRevision = false; + QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); + if (d && d->isFinal()) + return QQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); + } + + int effectivePropertyIndex = cache->propertyIndexCacheStart; + int effectiveMethodIndex = cache->methodIndexCacheStart; + + // For property change signal override detection. + // We prepopulate a set of signal names which already exist in the object, + // and throw an error if there is a signal/method defined as an override. + QSet<QString> seenSignals; + seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged"); + QQmlPropertyCache *parentCache = cache; + while ((parentCache = parentCache->parent())) { + if (int pSigCount = parentCache->signalCount()) { + int pSigOffset = parentCache->signalOffset(); + for (int i = pSigOffset; i < pSigCount; ++i) { + QQmlPropertyData *currPSig = parentCache->signal(i); + // XXX TODO: find a better way to get signal name from the property data :-/ + for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin(); + iter != parentCache->stringCache.end(); ++iter) { + if (currPSig == (*iter).second) { + seenSignals.insert(iter.key()); + break; + } + } + } + } + } + + // Set up notify signals for properties - first normal, then alias + for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p) { + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + + QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed"); + seenSignals.insert(changedSigName); + + cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); + } + + for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) { + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + + QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed"); + seenSignals.insert(changedSigName); + + cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); + } + + // Dynamic signals + for (auto s = obj->signalsBegin(), end = obj->signalsEnd(); s != end; ++s) { + const int paramCount = s->parameterCount(); + + QList<QByteArray> names; + names.reserve(paramCount); + QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0); + + if (paramCount) { + paramTypes[0] = paramCount; + + int i = 0; + for (auto param = s->parametersBegin(), end = s->parametersEnd(); param != end; ++param, ++i) { + names.append(stringAt(param->nameIndex).toUtf8()); + if (param->type < builtinTypeCount) { + // built-in type + paramTypes[i + 1] = builtinTypes[param->type].metaType; + } else { + // lazily resolved type + Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); + const QString customTypeName = stringAt(param->customTypeNameIndex); + QQmlType *qmltype = 0; + if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0)) + return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName)); + + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + auto compilationUnit = tdata->compilationUnit(); + + paramTypes[i + 1] = compilationUnit->metaTypeId; + + tdata->release(); + } else { + paramTypes[i + 1] = qmltype->typeId(); + } + } + } + } + + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + if (paramCount) + flags |= QQmlPropertyData::HasArguments; + + QString signalName = stringAt(s->nameIndex); + if (seenSignals.contains(signalName)) + return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal")); + seenSignals.insert(signalName); + + cache->appendSignal(signalName, flags, effectiveMethodIndex++, + paramCount?paramTypes.constData():0, names); + } + + + // Dynamic slots + for (auto function = objectContainer->objectFunctionsBegin(obj), end = objectContainer->objectFunctionsEnd(obj); function != end; ++function) { + quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; + + const QString slotName = stringAt(function->nameIndex); + if (seenSignals.contains(slotName)) + return QQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal")); + // Note: we don't append slotName to the seenSignals list, since we don't + // protect against overriding change signals or methods with properties. + + QList<QByteArray> parameterNames; + for (auto formal = function->formalsBegin(), end = function->formalsEnd(); formal != end; ++formal) { + flags |= QQmlPropertyData::HasArguments; + parameterNames << stringAt(*formal).toUtf8(); + } + + cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); + } + + + // Dynamic properties + int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; + int propertyIdx = 0; + for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p, ++propertyIdx) { + int propertyType = 0; + quint32 propertyFlags = 0; + + if (p->type == QV4::CompiledData::Property::Var) { + propertyType = QMetaType::QVariant; + propertyFlags = QQmlPropertyData::IsVarProperty; + } else if (p->type < builtinTypeCount) { + propertyType = builtinTypes[p->type].metaType; + + if (p->type == QV4::CompiledData::Property::Variant) + propertyFlags |= QQmlPropertyData::IsQVariant; + } else { + Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || + p->type == QV4::CompiledData::Property::Custom); + + QQmlType *qmltype = 0; + if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) { + return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type")); + } + + Q_ASSERT(qmltype); + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + auto compilationUnit = tdata->compilationUnit(); + + if (p->type == QV4::CompiledData::Property::Custom) { + propertyType = compilationUnit->metaTypeId; + } else { + propertyType = compilationUnit->listMetaTypeId; + } + + tdata->release(); + } else { + if (p->type == QV4::CompiledData::Property::Custom) { + propertyType = qmltype->typeId(); + } else { + propertyType = qmltype->qListTypeId(); + } + } + + if (p->type == QV4::CompiledData::Property::Custom) + propertyFlags |= QQmlPropertyData::IsQObjectDerived; + else + propertyFlags |= QQmlPropertyData::IsQList; + } + + if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList) + propertyFlags |= QQmlPropertyData::IsWritable; + + + QString propertyName = stringAt(p->nameIndex); + if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias) + cache->_defaultPropertyName = propertyName; + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + propertyType, effectiveSignalIndex); + + effectiveSignalIndex++; + } + + QQmlCompileError noError; + return noError; +} + +template <typename ObjectContainer> +class QQmlPropertyCacheAliasCreator +{ +public: + typedef typename ObjectContainer::CompiledObject CompiledObject; + + QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer); + + void appendAliasPropertiesToMetaObjects(); + + void appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex); + +private: + void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex); + void propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, quint32 *propertyFlags); + + void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const; + + int objectForId(const CompiledObject &component, int id) const; + + QQmlPropertyCacheVector *propertyCaches; + const ObjectContainer *objectContainer; +}; + +template <typename ObjectContainer> +inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer) + : propertyCaches(propertyCaches) + , objectContainer(objectContainer) +{ + +} + +template <typename ObjectContainer> +inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects() +{ + for (int i = 0; i < objectContainer->objectCount(); ++i) { + const CompiledObject &component = *objectContainer->objectAt(i); + if (!(component.flags & QV4::CompiledData::Object::IsComponent)) + continue; + + const auto rootBinding = component.bindingsBegin(); + appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex); + } + + const int rootObjectIndex = objectContainer->rootObjectIndex(); + appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex); +} + +template <typename ObjectContainer> +inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex) +{ + QVector<int> objectsWithAliases; + collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases); + if (objectsWithAliases.isEmpty()) + return; + + const auto allAliasTargetsExist = [this, &component](const CompiledObject &object) { + for (auto alias = object.aliasesBegin(), end = object.aliasesEnd(); alias != end; ++alias) { + Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); + + const int targetObjectIndex = objectForId(component, alias->targetObjectId); + Q_ASSERT(targetObjectIndex >= 0); + + if (alias->encodedMetaPropertyIndex == -1) + continue; + + const QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); + Q_ASSERT(targetCache); + + int coreIndex; + QQmlPropertyData::decodeValueTypePropertyIndex(alias->encodedMetaPropertyIndex, &coreIndex); + QQmlPropertyData *targetProperty = targetCache->property(coreIndex); + if (!targetProperty) + return false; + } + return true; + }; + + do { + QVector<int> pendingObjects; + + for (int objectIndex: qAsConst(objectsWithAliases)) { + const CompiledObject &object = *objectContainer->objectAt(objectIndex); + + if (allAliasTargetsExist(object)) { + appendAliasesToPropertyCache(component, objectIndex); + } else { + pendingObjects.append(objectIndex); + } + + } + qSwap(objectsWithAliases, pendingObjects); + } while (!objectsWithAliases.isEmpty()); +} + +template <typename ObjectContainer> +inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const +{ + const CompiledObject &object = *objectContainer->objectAt(objectIndex); + if (object.aliasCount() > 0) + objectsWithAliases->append(objectIndex); + + // Stop at Component boundary + if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != objectContainer->rootObjectIndex()) + return; + + for (auto binding = object.bindingsBegin(), end = object.bindingsEnd(); binding != end; ++binding) { + if (binding->type != QV4::CompiledData::Binding::Type_Object + && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty + && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) + continue; + + collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases); + } +} + +template <typename ObjectContainer> +inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, quint32 *propertyFlags) +{ + const int targetObjectIndex = objectForId(component, alias.targetObjectId); + Q_ASSERT(targetObjectIndex >= 0); + + const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex); + + *type = 0; + bool writable = false; + bool resettable = false; + + *propertyFlags = QQmlPropertyData::IsAlias; + + if (alias.aliasToLocalAlias) { + auto targetAlias = targetObject.aliasesBegin(); + for (uint i = 0; i < alias.localAliasIndex; ++i) + ++targetAlias; + propertyDataForAlias(component, *targetAlias, type, propertyFlags); + return; + } else if (alias.encodedMetaPropertyIndex == -1) { + Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject); + auto *typeRef = objectContainer->resolvedTypes.value(targetObject.inheritedTypeNameIndex); + Q_ASSERT(typeRef); + + if (typeRef->type) + *type = typeRef->type->typeId(); + else + *type = typeRef->compilationUnit->metaTypeId; + + *propertyFlags |= QQmlPropertyData::IsQObjectDerived; + } else { + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(alias.encodedMetaPropertyIndex, &coreIndex); + + QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); + Q_ASSERT(targetCache); + QQmlPropertyData *targetProperty = targetCache->property(coreIndex); + Q_ASSERT(targetProperty); + + *type = targetProperty->propType; + + writable = targetProperty->isWritable(); + resettable = targetProperty->isResettable(); + + if (valueTypeIndex != -1) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type); + if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) + *type = QVariant::Int; + else + *type = valueTypeMetaObject->property(valueTypeIndex).userType(); + } else { + if (targetProperty->isEnum()) { + *type = QVariant::Int; + } else { + // Copy type flags + *propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask; + + if (targetProperty->isVarProperty()) + *propertyFlags |= QQmlPropertyData::IsQVariant; + } + } + } + + if (!(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable) + *propertyFlags |= QQmlPropertyData::IsWritable; + else + *propertyFlags &= ~QQmlPropertyData::IsWritable; + + if (resettable) + *propertyFlags |= QQmlPropertyData::IsResettable; + else + *propertyFlags &= ~QQmlPropertyData::IsResettable; +} + +template <typename ObjectContainer> +inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex) +{ + const CompiledObject &object = *objectContainer->objectAt(objectIndex); + if (!object.aliasCount()) + return; + + QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); + Q_ASSERT(propertyCache); + + int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); + int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count(); + + int aliasIndex = 0; + for (auto alias = object.aliasesBegin(), end = object.aliasesEnd(); alias != end; ++alias, ++aliasIndex) { + Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); + + int type = 0; + quint32 propertyFlags = 0; + propertyDataForAlias(component, *alias, &type, &propertyFlags); + + const QString propertyName = objectContainer->stringAt(alias->nameIndex); + + if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias) + propertyCache->_defaultPropertyName = propertyName; + + propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + type, effectiveSignalIndex++); + } +} + +template <typename ObjectContainer> +inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const CompiledObject &component, int id) const +{ + for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) { + const int candidateIndex = component.namedObjectsInComponentTable()[i]; + const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex); + if (candidate.id == id) + return candidateIndex; + } + return -1; +} + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYCACHECREATOR_P_H diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp new file mode 100644 index 0000000000..c644330e97 --- /dev/null +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -0,0 +1,703 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpropertyvalidator_p.h" + +#include <private/qqmlcustomparser_p.h> +#include <private/qqmlstringconverters_p.h> +#include <QtCore/qdatetime.h> + +QT_BEGIN_NAMESPACE + +QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit) + : enginePrivate(enginePrivate) + , imports(imports) + , qmlUnit(compilationUnit->data) + , resolvedTypes(compilationUnit->resolvedTypes) + , propertyCaches(compilationUnit->propertyCaches) + , bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject) +{ + bindingPropertyDataPerObject->resize(qmlUnit->nObjects); +} + +QVector<QQmlCompileError> QQmlPropertyValidator::validate() +{ + return validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0); +} + +typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector; + +struct BindingFinder +{ + bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const + { + return name < binding->propertyNameIndex; + } + bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const + { + return binding->propertyNameIndex < name; + } + bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const + { + return lhs->propertyNameIndex < rhs->propertyNameIndex; + } +}; + +QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const +{ + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex); + + if (obj->flags & QV4::CompiledData::Object::IsComponent) { + Q_ASSERT(obj->nBindings == 1); + const QV4::CompiledData::Binding *componentBinding = obj->bindingTable(); + Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); + return validateObject(componentBinding->value.objectIndex, componentBinding); + } + + QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); + if (!propertyCache) + return QVector<QQmlCompileError>(); + + QStringList deferredPropertyNames; + { + const QMetaObject *mo = propertyCache->firstCppMetaObject(); + const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); + if (namesIndex != -1) { + QMetaClassInfo classInfo = mo->classInfo(namesIndex); + deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + } + } + + QQmlCustomParser *customParser = 0; + if (auto typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { + if (typeRef->type) + customParser = typeRef->type->customParser(); + } + + QList<const QV4::CompiledData::Binding*> customBindings; + + // Collect group properties first for sanity checking + // vector values are sorted by property name string index. + GroupPropertyVector groupProperties; + const QV4::CompiledData::Binding *binding = obj->bindingTable(); + for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { + if (!binding->isGroupProperty()) + continue; + + if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) + continue; + + if (populatingValueTypeGroupProperty) { + return recordError(binding->location, tr("Property assignment expected")); + } + + GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); + groupProperties.insert(pos, binding); + } + + QmlIR::PropertyResolver propertyResolver(propertyCache); + + QString defaultPropertyName; + QQmlPropertyData *defaultProperty = 0; + if (obj->indexOfDefaultPropertyOrAlias != -1) { + QQmlPropertyCache *cache = propertyCache->parent(); + defaultPropertyName = cache->defaultPropertyName(); + defaultProperty = cache->defaultProperty(); + } else { + defaultPropertyName = propertyCache->defaultPropertyName(); + defaultProperty = propertyCache->defaultProperty(); + } + + QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings); + + binding = obj->bindingTable(); + for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { + QString name = stringAt(binding->propertyNameIndex); + + if (customParser) { + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { + customBindings << binding; + continue; + } + } else if (QmlIR::IRBuilder::isSignalPropertyName(name) + && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { + customBindings << binding; + continue; + } + } + + bool bindingToDefaultProperty = false; + bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty; + + bool notInRevision = false; + QQmlPropertyData *pd = 0; + if (!name.isEmpty()) { + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + pd = propertyResolver.signal(name, ¬InRevision); + else + pd = propertyResolver.property(name, ¬InRevision, isGroupProperty ? QmlIR::PropertyResolver::IgnoreRevision : QmlIR::PropertyResolver::CheckRevision); + + if (notInRevision) { + QString typeName = stringAt(obj->inheritedTypeNameIndex); + auto *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex); + if (objectType && objectType->type) { + return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type->module()).arg(objectType->majorVersion).arg(objectType->minorVersion)); + } else { + return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name)); + } + } + } else { + if (isGroupProperty) + return recordError(binding->location, tr("Cannot assign a value directly to a grouped property")); + + pd = defaultProperty; + name = defaultPropertyName; + bindingToDefaultProperty = true; + } + + if (pd) + collectedBindingPropertyData[i] = pd; + + if (name.constData()->isUpper() && !binding->isAttachedProperty()) { + QQmlType *type = 0; + QQmlImportNamespace *typeNamespace = 0; + imports.resolveType(stringAt(binding->propertyNameIndex), &type, 0, 0, &typeNamespace); + if (typeNamespace) + return recordError(binding->location, tr("Invalid use of namespace")); + return recordError(binding->location, tr("Invalid attached object assignment")); + } + + if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { + const QVector<QQmlCompileError> subObjectValidatorErrors = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)); + if (!subObjectValidatorErrors.isEmpty()) + return subObjectValidatorErrors; + } + + // Signal handlers were resolved and checked earlier in the signal handler conversion pass. + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + continue; + + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) { + return recordError(binding->location, tr("Attached properties cannot be used here")); + } + continue; + } + + if (pd) { + GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); + const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex); + + if (!pd->isWritable() + && !pd->isQList() + && !binding->isGroupProperty() + && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration) + ) { + + if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object) + return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property")); + return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); + } + + if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) { + QString error; + if (pd->propType == qMetaTypeId<QQmlScriptString>()) + error = tr( "Cannot assign multiple values to a script property"); + else + error = tr( "Cannot assign multiple values to a singular property"); + return recordError(binding->valueLocation, error); + } + + if (!bindingToDefaultProperty + && !binding->isGroupProperty() + && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) + && assigningToGroupProperty) { + QV4::CompiledData::Location loc = binding->valueLocation; + if (loc < (*assignedGroupProperty)->valueLocation) + loc = (*assignedGroupProperty)->valueLocation; + + if (pd && QQmlValueTypeFactory::isValueType(pd->propType)) + return recordError(loc, tr("Property has already been assigned a value")); + return recordError(loc, tr("Cannot assign a value directly to a grouped property")); + } + + if (binding->type < QV4::CompiledData::Binding::Type_Script) { + QQmlCompileError bindingError = validateLiteralBinding(propertyCache, pd, binding); + if (bindingError.isSet()) + return recordError(bindingError); + } else if (binding->type == QV4::CompiledData::Binding::Type_Object) { + QQmlCompileError bindingError = validateObjectBinding(pd, name, binding); + if (bindingError.isSet()) + return recordError(bindingError); + } else if (binding->isGroupProperty()) { + if (QQmlValueTypeFactory::isValueType(pd->propType)) { + if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)) { + if (!pd->isWritable()) { + return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); + } + } else { + return recordError(binding->location, tr("Invalid grouped property access")); + } + } else { + if (!enginePrivate->propertyCacheForType(pd->propType)) { + return recordError(binding->location, tr("Invalid grouped property access")); + } + } + } + } else { + if (customParser) { + customBindings << binding; + continue; + } + if (bindingToDefaultProperty) { + return recordError(binding->location, tr("Cannot assign to non-existent default property")); + } else { + return recordError(binding->location, tr("Cannot assign to non-existent property \"%1\"").arg(name)); + } + } + } + + if (obj->idNameIndex) { + bool notInRevision = false; + collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), ¬InRevision); + } + + if (customParser && !customBindings.isEmpty()) { + customParser->clearErrors(); + customParser->validator = this; + customParser->engine = enginePrivate; + customParser->imports = &imports; + customParser->verifyBindings(qmlUnit, customBindings); + customParser->validator = 0; + customParser->engine = 0; + customParser->imports = (QQmlImports*)0; + QVector<QQmlCompileError> parserErrors = customParser->errors(); + if (!parserErrors.isEmpty()) + return parserErrors; + } + + (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData; + + QVector<QQmlCompileError> noError; + return noError; +} + +QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const +{ + if (property->isQList()) { + return QQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists")); + } + + QQmlCompileError noError; + + if (property->isEnum()) { + if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) + return noError; + + QString value = binding->valueAsString(qmlUnit); + QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex); + bool ok; + if (p.isFlagType()) { + p.enumerator().keysToValue(value.toUtf8().constData(), &ok); + } else + p.enumerator().keyToValue(value.toUtf8().constData(), &ok); + + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration")); + } + return noError; + } + + switch (property->propType) { + case QMetaType::QVariant: + break; + case QVariant::String: { + if (!binding->evaluatesToString()) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string expected")); + } + } + break; + case QVariant::StringList: { + if (!binding->evaluatesToString()) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or string list expected")); + } + } + break; + case QVariant::ByteArray: { + if (binding->type != QV4::CompiledData::Binding::Type_String) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: byte array expected")); + } + } + break; + case QVariant::Url: { + if (binding->type != QV4::CompiledData::Binding::Type_String) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url expected")); + } + } + break; + case QVariant::UInt: { + if (binding->type == QV4::CompiledData::Binding::Type_Number) { + double d = binding->valueAsNumber(); + if (double(uint(d)) == d) + return noError; + } + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsigned int expected")); + } + break; + case QVariant::Int: { + if (binding->type == QV4::CompiledData::Binding::Type_Number) { + double d = binding->valueAsNumber(); + if (double(int(d)) == d) + return noError; + } + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int expected")); + } + break; + case QMetaType::Float: { + if (binding->type != QV4::CompiledData::Binding::Type_Number) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected")); + } + } + break; + case QVariant::Double: { + if (binding->type != QV4::CompiledData::Binding::Type_Number) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected")); + } + } + break; + case QVariant::Color: { + bool ok = false; + QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: color expected")); + } + } + break; +#ifndef QT_NO_DATESTRING + case QVariant::Date: { + bool ok = false; + QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: date expected")); + } + } + break; + case QVariant::Time: { + bool ok = false; + QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: time expected")); + } + } + break; + case QVariant::DateTime: { + bool ok = false; + QQmlStringConverters::dateTimeFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: datetime expected")); + } + } + break; +#endif // QT_NO_DATESTRING + case QVariant::Point: { + bool ok = false; + QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); + } + } + break; + case QVariant::PointF: { + bool ok = false; + QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); + } + } + break; + case QVariant::Size: { + bool ok = false; + QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected")); + } + } + break; + case QVariant::SizeF: { + bool ok = false; + QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected")); + } + } + break; + case QVariant::Rect: { + bool ok = false; + QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: rect expected")); + } + } + break; + case QVariant::RectF: { + bool ok = false; + QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok); + if (!ok) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); + } + } + break; + case QVariant::Bool: { + if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: boolean expected")); + } + } + break; + case QVariant::Vector2D: { + struct { + float xp; + float yp; + } vec; + if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 2D vector expected")); + } + } + break; + case QVariant::Vector3D: { + struct { + float xp; + float yp; + float zy; + } vec; + if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 3D vector expected")); + } + } + break; + case QVariant::Vector4D: { + struct { + float xp; + float yp; + float zy; + float wp; + } vec; + if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 4D vector expected")); + } + } + break; + case QVariant::Quaternion: { + struct { + float wp; + float xp; + float yp; + float zp; + } vec; + if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: quaternion expected")); + } + } + break; + case QVariant::RegExp: + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); + default: { + // generate single literal value assignment to a list property if required + if (property->propType == qMetaTypeId<QList<qreal> >()) { + if (binding->type != QV4::CompiledData::Binding::Type_Number) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number or array of numbers expected")); + } + break; + } else if (property->propType == qMetaTypeId<QList<int> >()) { + bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number); + if (ok) { + double n = binding->valueAsNumber(); + if (double(int(n)) != n) + ok = false; + } + if (!ok) + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int or array of ints expected")); + break; + } else if (property->propType == qMetaTypeId<QList<bool> >()) { + if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: bool or array of bools expected")); + } + break; + } else if (property->propType == qMetaTypeId<QList<QUrl> >()) { + if (binding->type != QV4::CompiledData::Binding::Type_String) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url or array of urls expected")); + } + break; + } else if (property->propType == qMetaTypeId<QList<QString> >()) { + if (!binding->evaluatesToString()) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or array of strings expected")); + } + break; + } else if (property->propType == qMetaTypeId<QJSValue>()) { + break; + } else if (property->propType == qMetaTypeId<QQmlScriptString>()) { + break; + } + + // otherwise, try a custom type assignment + QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType); + if (!converter) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType)))); + } + } + break; + } + return noError; +} + +/*! + Returns true if from can be assigned to a (QObject) property of type + to. +*/ +bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const +{ + QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to); + + while (fromMo) { + if (fromMo == toMo) + return true; + fromMo = fromMo->parent(); + } + return false; +} + +QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const +{ + QVector<QQmlCompileError> errors; + errors.append(QQmlCompileError(location, description)); + return errors; +} + +QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QQmlCompileError &error) const +{ + QVector<QQmlCompileError> errors; + errors.append(error); + return errors; +} + +QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const +{ + QQmlCompileError noError; + + if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { + Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object); + + bool isValueSource = false; + bool isPropertyInterceptor = false; + + QQmlType *qmlType = 0; + const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex); + if (auto *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex)) { + QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + const QMetaObject *mo = cache->firstCppMetaObject(); + while (mo && !qmlType) { + qmlType = QQmlMetaType::qmlType(mo); + mo = mo->superClass(); + } + Q_ASSERT(qmlType); + } + + if (qmlType) { + isValueSource = qmlType->propertyValueSourceCast() != -1; + isPropertyInterceptor = qmlType->propertyValueInterceptorCast() != -1; + } + + if (!isValueSource && !isPropertyInterceptor) { + return QQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName)); + } + + return noError; + } + + if (QQmlMetaType::isInterface(property->propType)) { + // Can only check at instantiation time if the created sub-object successfully casts to the + // target interface. + return noError; + } else if (property->propType == QMetaType::QVariant) { + // We can convert everything to QVariant :) + return noError; + } else if (property->isQList()) { + const int listType = enginePrivate->listType(property->propType); + if (!QQmlMetaType::isInterface(listType)) { + QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); + if (!canCoerce(listType, source)) { + return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); + } + } + return noError; + } else if (qmlUnit->objectAt(binding->value.objectIndex)->flags & QV4::CompiledData::Object::IsComponent) { + return noError; + } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { + return noError; + } else if (QQmlValueTypeFactory::isValueType(property->propType)) { + return QQmlCompileError(binding->location, tr("Unexpected object assignment")); + } else if (property->propType == qMetaTypeId<QQmlScriptString>()) { + return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); + } else { + // We want to raw metaObject here as the raw metaobject is the + // actual property type before we applied any extensions that might + // effect the properties on the type, but don't effect assignability + QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType); + + // Will be true if the assgned type inherits propertyMetaObject + bool isAssignable = false; + // Determine isAssignable value + if (propertyMetaObject) { + QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex); + while (c && !isAssignable) { + isAssignable |= c == propertyMetaObject; + c = c->parent(); + } + } + + if (!isAssignable) { + return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to property")); + } + } + return noError; +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h new file mode 100644 index 0000000000..d0bd314461 --- /dev/null +++ b/src/qml/compiler/qqmlpropertyvalidator_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QQMLPROPERTYVALIDATOR_P_H +#define QQMLPROPERTYVALIDATOR_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/qqmltypecompiler_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlPropertyValidator +{ + Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) +public: + QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit); + + QVector<QQmlCompileError> validate(); + +private: + QVector<QQmlCompileError> validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const; + QQmlCompileError validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const; + QQmlCompileError validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const; + + bool canCoerce(int to, QQmlPropertyCache *fromMo) const; + + QVector<QQmlCompileError> recordError(const QV4::CompiledData::Location &location, const QString &description) const Q_REQUIRED_RESULT; + QVector<QQmlCompileError> recordError(const QQmlCompileError &error) const Q_REQUIRED_RESULT; + QString stringAt(int index) const { return qmlUnit->stringAt(index); } + + QQmlEnginePrivate *enginePrivate; + const QQmlImports &imports; + const QV4::CompiledData::Unit *qmlUnit; + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes; + const QQmlPropertyCacheVector &propertyCaches; + + QVector<QV4::CompiledData::BindingPropertyData> * const bindingPropertyDataPerObject; +}; + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYVALIDATOR_P_H diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index f49996a3b8..a0b219e28e 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -44,9 +44,10 @@ #include <private/qqmlcustomparser_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlcomponent_p.h> -#include <private/qqmlstringconverters_p.h> #include <private/qv4ssa_p.h> +#include "qqmlpropertycachecreator_p.h" + #define COMPILE_EXCEPTION(token, desc) \ { \ recordError((token)->location, desc); \ @@ -55,95 +56,35 @@ QT_BEGIN_NAMESPACE -QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlCompiledData *compiledData, QQmlTypeData *typeData, QmlIR::Document *parsedQML) - : engine(engine) - , compiledData(compiledData) +QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, + QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &importCache, + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) + : resolvedTypes(resolvedTypeCache) + , engine(engine) , typeData(typeData) + , importCache(importCache) , document(parsedQML) { } -bool QQmlTypeCompiler::compile() +QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() { - compiledData->importCache = new QQmlTypeNameCache; - - foreach (const QString &ns, typeData->namespaces()) - compiledData->importCache->add(ns); - - // Add any Composite Singletons that were used to the import cache - foreach (const QQmlTypeData::TypeReference &singleton, typeData->compositeSingletons()) - compiledData->importCache->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); - - typeData->imports().populateCache(compiledData->importCache); - - const QHash<int, QQmlTypeData::TypeReference> &resolvedTypes = typeData->resolvedTypeRefs(); - for (QHash<int, QQmlTypeData::TypeReference>::ConstIterator resolvedType = resolvedTypes.constBegin(), end = resolvedTypes.constEnd(); - resolvedType != end; ++resolvedType) { - QScopedPointer<QQmlCompiledData::TypeReference> ref(new QQmlCompiledData::TypeReference); - QQmlType *qmlType = resolvedType->type; - if (resolvedType->typeData) { - if (resolvedType->needsCreation && qmlType->isCompositeSingleton()) { - QQmlError error; - QString reason = tr("Composite Singleton Type %1 is not creatable.").arg(qmlType->qmlTypeName()); - error.setDescription(reason); - error.setColumn(resolvedType->location.column); - error.setLine(resolvedType->location.line); - recordError(error); - return false; - } - ref->component = resolvedType->typeData->compiledData(); - ref->component->addref(); - } else if (qmlType) { - ref->type = qmlType; - Q_ASSERT(ref->type); - - if (resolvedType->needsCreation && !ref->type->isCreatable()) { - QQmlError error; - QString reason = ref->type->noCreationReason(); - if (reason.isEmpty()) - reason = tr("Element is not creatable."); - error.setDescription(reason); - error.setColumn(resolvedType->location.column); - error.setLine(resolvedType->location.line); - recordError(error); - return false; - } - - if (ref->type->containsRevisionedAttributes()) { - ref->typePropertyCache = engine->cache(ref->type, - resolvedType->minorVersion); - if (!ref->typePropertyCache) { - QQmlError cacheError; - cacheError.setColumn(resolvedType->location.column); - cacheError.setLine(resolvedType->location.line); - recordError(cacheError); - return false; - } - ref->typePropertyCache->addref(); - } - } - ref->majorVersion = resolvedType->majorVersion; - ref->minorVersion = resolvedType->minorVersion; - ref->doDynamicTypeCheck(); - compiledData->resolvedTypes.insert(resolvedType.key(), ref.take()); - } - // Build property caches and VME meta object data - for (QHash<int, QQmlCompiledData::TypeReference*>::ConstIterator it = compiledData->resolvedTypes.constBegin(), end = compiledData->resolvedTypes.constEnd(); + for (auto it = resolvedTypes.constBegin(), end = resolvedTypes.constEnd(); it != end; ++it) { QQmlCustomParser *customParser = (*it)->type ? (*it)->type->customParser() : 0; if (customParser) customParsers.insert(it.key(), customParser); } - compiledData->metaObjects.reserve(document->objects.count()); - compiledData->propertyCaches.reserve(document->objects.count()); - { - QQmlPropertyCacheCreator propertyCacheBuilder(this); - if (!propertyCacheBuilder.buildMetaObjects()) - return false; + QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, engine, this, imports()); + QQmlCompileError error = propertyCacheBuilder.buildMetaObjects(); + if (error.isSet()) { + recordError(error); + return nullptr; + } } { @@ -154,13 +95,13 @@ bool QQmlTypeCompiler::compile() { SignalHandlerConverter converter(this); if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations()) - return false; + return nullptr; } { QQmlEnumTypeResolver enumResolver(this); if (!enumResolver.resolveEnumBindings()) - return false; + return nullptr; } { @@ -173,34 +114,19 @@ bool QQmlTypeCompiler::compile() annotator.annotateBindingsToAliases(); } - // Collect imported scripts - const QList<QQmlTypeData::ScriptReference> &scripts = typeData->resolvedScripts(); - compiledData->scripts.reserve(scripts.count()); - for (int scriptIndex = 0; scriptIndex < scripts.count(); ++scriptIndex) { - const QQmlTypeData::ScriptReference &script = scripts.at(scriptIndex); - - QStringRef qualifier(&script.qualifier); - QString enclosingNamespace; - - const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.')); - if (lastDotIndex != -1) { - enclosingNamespace = qualifier.left(lastDotIndex).toString(); - qualifier = qualifier.mid(lastDotIndex+1); - } - - compiledData->importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); - QQmlScriptData *scriptData = script.script->scriptData(); - scriptData->addref(); - compiledData->scripts << scriptData; - } - // Resolve component boundaries and aliases { // Scan for components, determine their scopes and resolve aliases within the scope. QQmlComponentAndAliasResolver resolver(this); if (!resolver.resolve()) - return false; + return nullptr; + } + + { + QQmlDeferredAndCustomParserBindingScanner deferredAndCustomParserBindingScanner(this); + if (!deferredAndCustomParserBindingScanner.scanObject()) + return nullptr; } // Compile JS binding expressions and signal handlers @@ -212,10 +138,10 @@ bool QQmlTypeCompiler::compile() sss.scan(); } - QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, compiledData->importCache, &document->jsGenerator.stringTable); + QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, importCache, &document->jsGenerator.stringTable); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) - return false; + return nullptr; QQmlJavaScriptBindingExpressionSimplificationPass pass(this); pass.reduceTranslationBindings(); @@ -236,64 +162,39 @@ bool QQmlTypeCompiler::compile() // The js unit owns the data and will free the qml unit. document->javaScriptCompilationUnit->data = qmlUnit; - compiledData->compilationUnit = document->javaScriptCompilationUnit; - - // Add to type registry of composites - if (compiledData->isCompositeType()) - engine->registerInternalCompositeType(compiledData); - else { - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(qmlUnit->indexOfRootObject); - QQmlCompiledData::TypeReference *typeRef = compiledData->resolvedTypes.value(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - if (typeRef->component) { - compiledData->metaTypeId = typeRef->component->metaTypeId; - compiledData->listMetaTypeId = typeRef->component->listMetaTypeId; - } else { - compiledData->metaTypeId = typeRef->type->typeId(); - compiledData->listMetaTypeId = typeRef->type->qListTypeId(); - } - } - - // Sanity check property bindings - QQmlPropertyValidator validator(this); - if (!validator.validate()) - return false; + QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit; + compilationUnit = document->javaScriptCompilationUnit; + compilationUnit->importCache = importCache; + compilationUnit->resolvedTypes = resolvedTypes; + compilationUnit->propertyCaches = std::move(m_propertyCaches); + Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->data->nObjects)); - // Collect some data for instantiation later. - int bindingCount = 0; - int parserStatusCount = 0; - int objectCount = 0; - for (quint32 i = 0; i < qmlUnit->nObjects; ++i) { - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i); - bindingCount += obj->nBindings; - if (QQmlCompiledData::TypeReference *typeRef = compiledData->resolvedTypes.value(obj->inheritedTypeNameIndex)) { - if (QQmlType *qmlType = typeRef->type) { - if (qmlType->parserStatusCast() != -1) - ++parserStatusCount; - } - ++objectCount; - if (typeRef->component) { - bindingCount += typeRef->component->totalBindingsCount; - parserStatusCount += typeRef->component->totalParserStatusCount; - objectCount += typeRef->component->totalObjectCount; - } - } - } - compiledData->totalBindingsCount = bindingCount; - compiledData->totalParserStatusCount = parserStatusCount; - compiledData->totalObjectCount = objectCount; + if (errors.isEmpty()) + return compilationUnit; + else + return nullptr; +} - Q_ASSERT(compiledData->propertyCaches.count() == static_cast<int>(compiledData->compilationUnit->data->nObjects)); +void QQmlTypeCompiler::recordError(QQmlError error) +{ + error.setUrl(url()); + errors << error; +} - return errors.isEmpty(); +void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description) +{ + QQmlError error; + error.setLine(location.line); + error.setColumn(location.column); + error.setDescription(description); + error.setUrl(url()); + errors << error; } -void QQmlTypeCompiler::recordError(const QQmlError &error) +void QQmlTypeCompiler::recordError(const QQmlCompileError &error) { - QQmlError e = error; - e.setUrl(url()); - errors << e; + recordError(error.location, error.description); } QString QQmlTypeCompiler::stringAt(int idx) const @@ -313,7 +214,7 @@ QV4::IR::Module *QQmlTypeCompiler::jsIRModule() const const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const { - return compiledData->compilationUnit->data; + return document->javaScriptCompilationUnit->data; } const QQmlImports *QQmlTypeCompiler::imports() const @@ -321,12 +222,7 @@ const QQmlImports *QQmlTypeCompiler::imports() const return &typeData->imports(); } -QHash<int, QQmlCompiledData::TypeReference*> *QQmlTypeCompiler::resolvedTypes() -{ - return &compiledData->resolvedTypes; -} - -QList<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() +QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const { return &document->objects; } @@ -336,45 +232,20 @@ int QQmlTypeCompiler::rootObjectIndex() const return document->indexOfRootObject; } -void QQmlTypeCompiler::setPropertyCaches(const QVector<QQmlPropertyCache *> &caches) +void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches) { - compiledData->propertyCaches = caches; - Q_ASSERT(caches.count() >= document->indexOfRootObject); - if (compiledData->rootPropertyCache) - compiledData->rootPropertyCache->release(); - compiledData->rootPropertyCache = caches.at(document->indexOfRootObject); - compiledData->rootPropertyCache->addref(); + m_propertyCaches = std::move(caches); + Q_ASSERT(m_propertyCaches.count() >= document->indexOfRootObject); } -const QVector<QQmlPropertyCache *> &QQmlTypeCompiler::propertyCaches() const +const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const { - return compiledData->propertyCaches; + return &m_propertyCaches; } -void QQmlTypeCompiler::setVMEMetaObjects(const QVector<QByteArray> &metaObjects) +QQmlPropertyCacheVector &&QQmlTypeCompiler::takePropertyCaches() { - Q_ASSERT(compiledData->metaObjects.isEmpty()); - compiledData->metaObjects = metaObjects; -} - -QVector<QByteArray> *QQmlTypeCompiler::vmeMetaObjects() const -{ - return &compiledData->metaObjects; -} - -QHash<int, int> *QQmlTypeCompiler::objectIndexToIdForRoot() -{ - return &compiledData->objectIndexToIdForRoot; -} - -QHash<int, QHash<int, int> > *QQmlTypeCompiler::objectIndexToIdPerComponent() -{ - return &compiledData->objectIndexToIdPerComponent; -} - -QHash<int, QBitArray> *QQmlTypeCompiler::customParserBindings() -{ - return &compiledData->customParserBindings; + return std::move(m_propertyCaches); } QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool() @@ -392,14 +263,9 @@ const QV4::Compiler::StringTableGenerator *QQmlTypeCompiler::stringPool() const return &document->jsGenerator.stringTable; } -void QQmlTypeCompiler::setDeferredBindingsPerObject(const QHash<int, QBitArray> &deferredBindingsPerObject) -{ - compiledData->deferredBindingsPerObject = deferredBindingsPerObject; -} - void QQmlTypeCompiler::setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData) { - compiledData->compilationUnit->bindingPropertyDataPerObject = propertyData; + document->javaScriptCompilationUnit->bindingPropertyDataPerObject = propertyData; } QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scriptIndex) const @@ -407,499 +273,34 @@ QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scrip return object->bindingAsString(document, scriptIndex); } -QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler) - : compiler(typeCompiler) -{ -} - -void QQmlCompilePass::recordError(const QV4::CompiledData::Location &location, const QString &description) const -{ - QQmlError error; - error.setLine(location.line); - error.setColumn(location.column); - error.setDescription(description); - compiler->recordError(error); -} - -static QAtomicInt classIndexCounter(0); - -QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , enginePrivate(typeCompiler->enginePrivate()) - , qmlObjects(*typeCompiler->qmlObjects()) - , imports(typeCompiler->imports()) - , resolvedTypes(typeCompiler->resolvedTypes()) -{ -} - -QQmlPropertyCacheCreator::~QQmlPropertyCacheCreator() -{ - for (int i = 0; i < propertyCaches.count(); ++i) - if (QQmlPropertyCache *cache = propertyCaches.at(i)) - cache->release(); - propertyCaches.clear(); -} - -bool QQmlPropertyCacheCreator::buildMetaObjects() -{ - propertyCaches.resize(qmlObjects.count()); - vmeMetaObjects.resize(qmlObjects.count()); - - if (!buildMetaObjectRecursively(compiler->rootObjectIndex(), /*referencing object*/-1, /*instantiating binding*/0)) - return false; - - compiler->setVMEMetaObjects(vmeMetaObjects); - compiler->setPropertyCaches(propertyCaches); - propertyCaches.clear(); - - return true; -} - -bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding) +void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion) { - const QmlIR::Object *obj = qmlObjects.at(objectIndex); - - QQmlPropertyCache *baseTypeCache = 0; - QQmlPropertyData *instantiatingProperty = 0; - if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) { - Q_ASSERT(referencingObjectIndex >= 0); - QQmlPropertyCache *parentCache = propertyCaches.at(referencingObjectIndex); - Q_ASSERT(parentCache); - Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); - - bool notInRevision = false; - instantiatingProperty = QmlIR::PropertyResolver(parentCache).property(stringAt(instantiatingBinding->propertyNameIndex), ¬InRevision); - if (instantiatingProperty) { - if (instantiatingProperty->isQObject()) { - baseTypeCache = enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType); - Q_ASSERT(baseTypeCache); - } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(instantiatingProperty->propType)) { - baseTypeCache = enginePrivate->cache(vtmo); - Q_ASSERT(baseTypeCache); - } - } - } - - bool needVMEMetaObject = obj->propertyCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0; - if (!needVMEMetaObject) { - for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { - - // On assignments are implemented using value interceptors, which require a VME meta object. - needVMEMetaObject = true; - - // If the on assignment is inside a group property, we need to distinguish between QObject based - // group properties and value type group properties. For the former the base type is derived from - // the property that references us, for the latter we only need a meta-object on the referencing object - // because interceptors can't go to the shared value type instances. - if (instantiatingProperty && QQmlValueTypeFactory::isValueType(instantiatingProperty->propType)) { - needVMEMetaObject = false; - if (!ensureMetaObject(referencingObjectIndex)) - return false; - } - break; - } - } - } - - if (obj->inheritedTypeNameIndex != 0) { - QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - - if (typeRef->isFullyDynamicType) { - if (obj->propertyCount() > 0) { - recordError(obj->location, tr("Fully dynamic types cannot declare new properties.")); - return false; - } - if (obj->signalCount() > 0) { - recordError(obj->location, tr("Fully dynamic types cannot declare new signals.")); - return false; - } - if (obj->functionCount() > 0) { - recordError(obj->location, tr("Fully Dynamic types cannot declare new functions.")); - return false; - } - } - - baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - Q_ASSERT(baseTypeCache); - } else if (instantiatingBinding && instantiatingBinding->isAttachedProperty()) { - QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(instantiatingBinding->propertyNameIndex); - Q_ASSERT(typeRef); - 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 quint32 moduleIdx = registerString(module); + const quint32 qualifierIdx = registerString(qualifier); - const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0; - if (!attachedMo) { - recordError(instantiatingBinding->location, tr("Non-existent attached object")); - return false; - } - baseTypeCache = enginePrivate->cache(attachedMo); - Q_ASSERT(baseTypeCache); - } - - if (baseTypeCache) { - if (needVMEMetaObject) { - if (!createMetaObject(objectIndex, obj, baseTypeCache)) - return false; - } else { - propertyCaches[objectIndex] = baseTypeCache; - baseTypeCache->addref(); - } - } - - if (propertyCaches.at(objectIndex)) { - for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - if (!buildMetaObjectRecursively(binding->value.objectIndex, objectIndex, binding)) - return false; - } + for (int i = 0, count = document->imports.count(); i < count; ++i) { + const QV4::CompiledData::Import *existingImport = document->imports.at(i); + if (existingImport->type == QV4::CompiledData::Import::ImportLibrary + && existingImport->uriIndex == moduleIdx + && existingImport->qualifierIndex == qualifierIdx) + return; } - - return true; + auto pool = memoryPool(); + QV4::CompiledData::Import *import = pool->New<QV4::CompiledData::Import>(); + import->type = QV4::CompiledData::Import::ImportLibrary; + import->majorVersion = majorVersion; + import->minorVersion = minorVersion; + import->uriIndex = moduleIdx; + import->qualifierIndex = qualifierIdx; + document->imports.append(import); } -bool QQmlPropertyCacheCreator::ensureMetaObject(int objectIndex) +QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler) + : compiler(typeCompiler) { - if (!vmeMetaObjects.at(objectIndex).isEmpty()) - return true; - const QmlIR::Object *obj = qmlObjects.at(objectIndex); - QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - return createMetaObject(objectIndex, obj, baseTypeCache); -} - -bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache) -{ - QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(obj->propertyCount(), - obj->functionCount() + obj->propertyCount() + obj->signalCount(), - obj->signalCount() + obj->propertyCount()); - propertyCaches[objectIndex] = cache; - - struct TypeData { - QV4::CompiledData::Property::Type dtype; - int metaType; - } builtinTypes[] = { - { QV4::CompiledData::Property::Var, QMetaType::QVariant }, - { QV4::CompiledData::Property::Variant, QMetaType::QVariant }, - { QV4::CompiledData::Property::Int, QMetaType::Int }, - { QV4::CompiledData::Property::Bool, QMetaType::Bool }, - { QV4::CompiledData::Property::Real, QMetaType::Double }, - { QV4::CompiledData::Property::String, QMetaType::QString }, - { QV4::CompiledData::Property::Url, QMetaType::QUrl }, - { QV4::CompiledData::Property::Color, QMetaType::QColor }, - { QV4::CompiledData::Property::Font, QMetaType::QFont }, - { QV4::CompiledData::Property::Time, QMetaType::QTime }, - { QV4::CompiledData::Property::Date, QMetaType::QDate }, - { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime }, - { QV4::CompiledData::Property::Rect, QMetaType::QRectF }, - { QV4::CompiledData::Property::Point, QMetaType::QPointF }, - { QV4::CompiledData::Property::Size, QMetaType::QSizeF }, - { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D }, - { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D }, - { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D }, - { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 }, - { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion } - }; - static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); - - QByteArray newClassName; - - if (objectIndex == compiler->rootObjectIndex()) { - QString path = compiler->url().path(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - if (lastSlash > -1) { - const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5); - if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) - newClassName = nameBase.toUtf8() + "_QMLTYPE_" + - QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)); - } - } - if (newClassName.isEmpty()) { - newClassName = QQmlMetaObject(baseTypeCache).className(); - newClassName.append("_QML_"); - newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); - } - - cache->_dynamicClassName = newClassName; - - int aliasCount = 0; - int varPropCount = 0; - - QmlIR::PropertyResolver resolver(baseTypeCache); - - for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) { - if (p->type == QV4::CompiledData::Property::Alias) - aliasCount++; - else if (p->type == QV4::CompiledData::Property::Var) - varPropCount++; - - // No point doing this for both the alias and non alias cases - bool notInRevision = false; - QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); - if (d && d->isFinal()) - COMPILE_EXCEPTION(p, tr("Cannot override FINAL property")); - } - - typedef QQmlVMEMetaData VMD; - - QByteArray &dynamicData = vmeMetaObjects[objectIndex] = QByteArray(sizeof(QQmlVMEMetaData) - + obj->propertyCount() * sizeof(VMD::PropertyData) - + obj->functionCount() * sizeof(VMD::MethodData) - + aliasCount * sizeof(VMD::AliasData), 0); - - int effectivePropertyIndex = cache->propertyIndexCacheStart; - int effectiveMethodIndex = cache->methodIndexCacheStart; - - // For property change signal override detection. - // We prepopulate a set of signal names which already exist in the object, - // and throw an error if there is a signal/method defined as an override. - QSet<QString> seenSignals; - seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged"); - QQmlPropertyCache *parentCache = cache; - while ((parentCache = parentCache->parent())) { - if (int pSigCount = parentCache->signalCount()) { - int pSigOffset = parentCache->signalOffset(); - for (int i = pSigOffset; i < pSigCount; ++i) { - QQmlPropertyData *currPSig = parentCache->signal(i); - // XXX TODO: find a better way to get signal name from the property data :-/ - for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin(); - iter != parentCache->stringCache.end(); ++iter) { - if (currPSig == (*iter).second) { - seenSignals.insert(iter.key()); - break; - } - } - } - } - } - - // First set up notify signals for properties - first normal, then var, then alias - 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_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) || - (ii == NSS_Alias && p->type != QV4::CompiledData::Property::Alias)) - continue; - - quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | - QQmlPropertyData::IsVMESignal; - - QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed"); - seenSignals.insert(changedSigName); - - cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); - } - } - - // Dynamic signals - for (const QmlIR::Signal *s = obj->firstSignal(); s; s = s->next) { - const int paramCount = s->parameters->count; - - QList<QByteArray> names; - names.reserve(paramCount); - QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0); - - if (paramCount) { - paramTypes[0] = paramCount; - - QmlIR::SignalParameter *param = s->parameters->first; - for (int i = 0; i < paramCount; ++i, param = param->next) { - names.append(stringAt(param->nameIndex).toUtf8()); - if (param->type < builtinTypeCount) { - // built-in type - paramTypes[i + 1] = builtinTypes[param->type].metaType; - } else { - // lazily resolved type - Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); - const QString customTypeName = stringAt(param->customTypeNameIndex); - QQmlType *qmltype = 0; - if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0)) - COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(customTypeName)); - - if (qmltype->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - QQmlCompiledData *data = tdata->compiledData(); - - paramTypes[i + 1] = data->metaTypeId; - - tdata->release(); - } else { - paramTypes[i + 1] = qmltype->typeId(); - } - } - } - } - - ((QQmlVMEMetaData *)dynamicData.data())->signalCount++; - - quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | - QQmlPropertyData::IsVMESignal; - if (paramCount) - flags |= QQmlPropertyData::HasArguments; - - QString signalName = stringAt(s->nameIndex); - if (seenSignals.contains(signalName)) - COMPILE_EXCEPTION(s, tr("Duplicate signal name: invalid override of property change signal or superclass signal")); - seenSignals.insert(signalName); - - cache->appendSignal(signalName, flags, effectiveMethodIndex++, - paramCount?paramTypes.constData():0, names); - } - - - // Dynamic slots - for (const QmlIR::Function *s = obj->firstFunction(); s; s = s->next) { - QQmlJS::AST::FunctionDeclaration *astFunction = s->functionDeclaration; - - quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; - - if (astFunction->formals) - flags |= QQmlPropertyData::HasArguments; - - QString slotName = astFunction->name.toString(); - if (seenSignals.contains(slotName)) - COMPILE_EXCEPTION(s, tr("Duplicate method name: invalid override of property change signal or superclass signal")); - // Note: we don't append slotName to the seenSignals list, since we don't - // protect against overriding change signals or methods with properties. - - QList<QByteArray> parameterNames; - QQmlJS::AST::FormalParameterList *param = astFunction->formals; - while (param) { - parameterNames << param->name.toUtf8(); - param = param->next; - } - - cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); - } - - - // Dynamic properties (except var and aliases) - int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; - int propertyIdx = 0; - for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next, ++propertyIdx) { - - if (p->type == QV4::CompiledData::Property::Alias) - continue; - - int propertyType = 0; - int vmePropertyType = 0; - quint32 propertyFlags = 0; - - 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; - - if (p->type == QV4::CompiledData::Property::Variant) - propertyFlags |= QQmlPropertyData::IsQVariant; - } else { - Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || - p->type == QV4::CompiledData::Property::Custom); - - QQmlType *qmltype = 0; - if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) { - COMPILE_EXCEPTION(p, tr("Invalid property type")); - } - - Q_ASSERT(qmltype); - if (qmltype->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - QQmlCompiledData *data = tdata->compiledData(); - - if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = data->metaTypeId; - vmePropertyType = QMetaType::QObjectStar; - } else { - propertyType = data->listMetaTypeId; - vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >(); - } - - tdata->release(); - } else { - if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = qmltype->typeId(); - vmePropertyType = QMetaType::QObjectStar; - } else { - propertyType = qmltype->qListTypeId(); - vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >(); - } - } - - if (p->type == QV4::CompiledData::Property::Custom) - propertyFlags |= QQmlPropertyData::IsQObjectDerived; - else - propertyFlags |= QQmlPropertyData::IsQList; - } - - if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList) - propertyFlags |= QQmlPropertyData::IsWritable; - - - QString propertyName = stringAt(p->nameIndex); - if (propertyIdx == obj->indexOfDefaultProperty) cache->_defaultPropertyName = propertyName; - cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - propertyType, effectiveSignalIndex); - - effectiveSignalIndex++; - - VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); - (vmd->propertyData() + vmd->propertyCount)->propertyType = vmePropertyType; - vmd->propertyCount++; - } - - // Alias property count. Actual data is setup in buildDynamicMetaAliases - ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount; - - // Dynamic slot data - comes after the property data - for (const QmlIR::Function *s = obj->firstFunction(); s; s = s->next) { - QQmlJS::AST::FunctionDeclaration *astFunction = s->functionDeclaration; - int formalsCount = 0; - QQmlJS::AST::FormalParameterList *param = astFunction->formals; - while (param) { - formalsCount++; - param = param->next; - } - - VMD::MethodData methodData = { /* runtimeFunctionIndex*/ 0, // ### - formalsCount, - /* s->location.start.line */0 }; // ### +} - VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); - VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount); - vmd->methodCount++; - md = methodData; - } - return true; -} SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler) : QQmlCompilePass(typeCompiler) @@ -907,7 +308,7 @@ SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler) , qmlObjects(*typeCompiler->qmlObjects()) , imports(typeCompiler->imports()) , customParsers(typeCompiler->customParserCache()) - , resolvedTypes(*typeCompiler->resolvedTypes()) + , resolvedTypes(typeCompiler->resolvedTypes) , illegalNames(QV8Engine::get(QQmlEnginePrivate::get(typeCompiler->enginePrivate()))->illegalNames()) , propertyCaches(typeCompiler->propertyCaches()) { @@ -917,7 +318,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio { for (int objectIndex = 0; objectIndex < qmlObjects.count(); ++objectIndex) { const QmlIR::Object * const obj = qmlObjects.at(objectIndex); - QQmlPropertyCache *cache = propertyCaches.at(objectIndex); + QQmlPropertyCache *cache = propertyCaches->at(objectIndex); if (!cache) continue; if (QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex)) { @@ -941,7 +342,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio // Attached property? if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex); - QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(binding->propertyNameIndex); + auto *typeRef = resolvedTypes.value(binding->propertyNameIndex); QQmlType *type = typeRef ? typeRef->type : 0; if (!type) { if (imports->resolveType(propertyName, &type, 0, 0, 0)) { @@ -950,8 +351,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); - QQmlCompiledData *data = tdata->compiledData(); - type = QQmlMetaType::qmlType(data->metaTypeId); + auto compilationUnit = tdata->compilationUnit(); + type = QQmlMetaType::qmlType(compilationUnit->metaTypeId); tdata->release(); } @@ -1014,7 +415,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio const QString &originalPropertyName = stringAt(binding->propertyNameIndex); - QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); + auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); const QQmlType *type = typeRef ? typeRef->type : 0; if (type) { COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type->module()).arg(type->majorVersion()).arg(type->minorVersion())); @@ -1118,14 +519,14 @@ QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler) , qmlObjects(*typeCompiler->qmlObjects()) , propertyCaches(typeCompiler->propertyCaches()) , imports(typeCompiler->imports()) - , resolvedTypes(typeCompiler->resolvedTypes()) + , resolvedTypes(&typeCompiler->resolvedTypes) { } bool QQmlEnumTypeResolver::resolveEnumBindings() { for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches.at(i); + QQmlPropertyCache *propertyCache = propertyCaches->at(i); if (!propertyCache) continue; const QmlIR::Object *obj = qmlObjects.at(i); @@ -1169,7 +570,7 @@ bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QS COMPILE_EXCEPTION(binding, tr("Invalid property assignment: Enum value \"%1\" cannot start with a lowercase letter").arg(enumName.toString())); } binding->type = QV4::CompiledData::Binding::Type_Number; - binding->value.d = (double)enumValue; + binding->setNumberValueInternal((double)enumValue); binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum; return true; } @@ -1218,7 +619,7 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, int value = 0; bool ok = false; - QQmlCompiledData::TypeReference *tr = resolvedTypes->value(obj->inheritedTypeNameIndex); + auto *tr = resolvedTypes->value(obj->inheritedTypeNameIndex); if (type && tr && tr->type == type) { QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex); @@ -1311,20 +712,20 @@ QQmlAliasAnnotator::QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler) void QQmlAliasAnnotator::annotateBindingsToAliases() { for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches.at(i); + QQmlPropertyCache *propertyCache = propertyCaches->at(i); if (!propertyCache) continue; const QmlIR::Object *obj = qmlObjects.at(i); QmlIR::PropertyResolver resolver(propertyCache); - QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); + QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { if (!binding->isValueBinding()) continue; bool notInRevision = false; - QQmlPropertyData *pd = binding->propertyNameIndex != 0 ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; + QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; if (pd && pd->isAlias()) binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias; } @@ -1343,20 +744,20 @@ void QQmlScriptStringScanner::scan() { const int scriptStringMetaType = qMetaTypeId<QQmlScriptString>(); for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches.at(i); + QQmlPropertyCache *propertyCache = propertyCaches->at(i); if (!propertyCache) continue; const QmlIR::Object *obj = qmlObjects.at(i); QmlIR::PropertyResolver resolver(propertyCache); - QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); + QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { if (binding->type != QV4::CompiledData::Binding::Type_Script) continue; bool notInRevision = false; - QQmlPropertyData *pd = binding->propertyNameIndex != 0 ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; + QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; if (!pd || pd->propType != scriptStringMetaType) continue; @@ -1376,13 +777,8 @@ QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *t , pool(typeCompiler->memoryPool()) , qmlObjects(typeCompiler->qmlObjects()) , indexOfRootObject(typeCompiler->rootObjectIndex()) - , _componentIndex(-1) - , _objectIndexToIdInScope(0) - , resolvedTypes(typeCompiler->resolvedTypes()) - , propertyCaches(typeCompiler->propertyCaches()) - , vmeMetaObjectData(typeCompiler->vmeMetaObjects()) - , objectIndexToIdForRoot(typeCompiler->objectIndexToIdForRoot()) - , objectIndexToIdPerComponent(typeCompiler->objectIndexToIdPerComponent()) + , resolvedTypes(&typeCompiler->resolvedTypes) + , propertyCaches(std::move(typeCompiler->takePropertyCaches())) { } @@ -1390,7 +786,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI { QmlIR::PropertyResolver propertyResolver(propertyCache); - QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); + QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { if (binding->type != QV4::CompiledData::Binding::Type_Object) @@ -1399,18 +795,18 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI continue; const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex); - QQmlCompiledData::TypeReference *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex); + auto *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex); Q_ASSERT(tr); if (QQmlType *targetType = tr->type) { if (targetType->metaObject() == &QQmlComponent::staticMetaObject) continue; - } else if (tr->component) { - if (tr->component->rootPropertyCache->firstCppMetaObject() == &QQmlComponent::staticMetaObject) + } else if (tr->compilationUnit) { + if (tr->compilationUnit->rootPropertyCache()->firstCppMetaObject() == &QQmlComponent::staticMetaObject) continue; } QQmlPropertyData *pd = 0; - if (binding->propertyNameIndex != 0) { + if (binding->propertyNameIndex != quint32(0)) { bool notInRevision = false; pd = propertyResolver.property(stringAt(binding->propertyNameIndex), ¬InRevision); } else { @@ -1430,15 +826,20 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI if (!mo) continue; + // emulate "import Qml 2.0 as QmlInternals" and then wrap the component in "QmlInternals.Component {}" QQmlType *componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject); Q_ASSERT(componentType); + const QString qualifier = QStringLiteral("QmlInternals"); + + compiler->addImport(componentType->module(), qualifier, componentType->majorVersion(), componentType->minorVersion()); QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>(); - syntheticComponent->init(pool, compiler->registerString(QString::fromUtf8(componentType->typeName())), compiler->registerString(QString())); + syntheticComponent->init(pool, compiler->registerString(qualifier + QLatin1Char('.') + componentType->elementName()), compiler->registerString(QString())); syntheticComponent->location = binding->valueLocation; + syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent; if (!resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) { - QQmlCompiledData::TypeReference *typeRef = new QQmlCompiledData::TypeReference; + auto typeRef = new QV4::CompiledData::ResolvedTypeReference; typeRef->type = componentType; typeRef->majorVersion = componentType->majorVersion(); typeRef->minorVersion = componentType->minorVersion(); @@ -1449,7 +850,6 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI const int componentIndex = qmlObjects->count() - 1; // Keep property caches symmetric QQmlPropertyCache *componentCache = enginePrivate->cache(&QQmlComponent::staticMetaObject); - componentCache->addref(); propertyCaches.append(componentCache); QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>(); @@ -1462,7 +862,6 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI binding->value.objectIndex = componentIndex; componentRoots.append(componentIndex); - componentBoundaries.append(syntheticBinding->value.objectIndex); } } @@ -1474,7 +873,7 @@ bool QQmlComponentAndAliasResolver::resolve() // on the left hand side is of QQmlComponent type. const int objCountWithoutSynthesizedComponents = qmlObjects->count(); for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) { - const QmlIR::Object *obj = qmlObjects->at(i); + QmlIR::Object *obj = qmlObjects->at(i); QQmlPropertyCache *cache = propertyCaches.at(i); if (obj->inheritedTypeNameIndex == 0 && !cache) continue; @@ -1482,7 +881,7 @@ bool QQmlComponentAndAliasResolver::resolve() bool isExplicitComponent = false; if (obj->inheritedTypeNameIndex) { - QQmlCompiledData::TypeReference *tref = resolvedTypes->value(obj->inheritedTypeNameIndex); + auto *tref = resolvedTypes->value(obj->inheritedTypeNameIndex); Q_ASSERT(tref); if (tref->type && tref->type->metaObject() == &QQmlComponent::staticMetaObject) isExplicitComponent = true; @@ -1493,11 +892,11 @@ bool QQmlComponentAndAliasResolver::resolve() continue; } - componentRoots.append(i); + obj->flags |= QV4::CompiledData::Object::IsComponent; if (obj->functionCount() > 0) COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions.")); - if (obj->propertyCount() > 0) + if (obj->propertyCount() > 0 || obj->aliasCount() > 0) COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties.")); if (obj->signalCount() > 0) COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals.")); @@ -1515,65 +914,69 @@ bool QQmlComponentAndAliasResolver::resolve() if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object) COMPILE_EXCEPTION(obj, tr("Invalid component body specification")); - componentBoundaries.append(rootBinding->value.objectIndex); - } + // We are going to collect ids/aliases and resolve them for the root object as a separate + // last pass. + if (i != indexOfRootObject) + componentRoots.append(i); - std::sort(componentBoundaries.begin(), componentBoundaries.end()); + } for (int i = 0; i < componentRoots.count(); ++i) { - const QmlIR::Object *component = qmlObjects->at(componentRoots.at(i)); + QmlIR::Object *component = qmlObjects->at(componentRoots.at(i)); const QmlIR::Binding *rootBinding = component->firstBinding(); - _componentIndex = i; _idToObjectIndex.clear(); - _objectIndexToIdInScope = &(*objectIndexToIdPerComponent)[componentRoots.at(i)]; - _objectsWithAliases.clear(); if (!collectIdsAndAliases(rootBinding->value.objectIndex)) return false; - if (!resolveAliases()) + component->namedObjectsInComponent.allocate(pool, _idToObjectIndex); + + if (!resolveAliases(componentRoots.at(i))) return false; } // Collect ids and aliases for root - _componentIndex = -1; _idToObjectIndex.clear(); - _objectIndexToIdInScope = objectIndexToIdForRoot; _objectsWithAliases.clear(); collectIdsAndAliases(indexOfRootObject); - resolveAliases(); + QmlIR::Object *rootComponent = qmlObjects->at(indexOfRootObject); + rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex); + + if (!resolveAliases(indexOfRootObject)) + return false; // Implicit component insertion may have added objects and thus we also need // to extend the symmetric propertyCaches. - compiler->setPropertyCaches(propertyCaches); + compiler->setPropertyCaches(std::move(propertyCaches)); + compiler->setComponentRoots(componentRoots); return true; } bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) { - const QmlIR::Object *obj = qmlObjects->at(objectIndex); + QmlIR::Object *obj = qmlObjects->at(objectIndex); - if (obj->idIndex != 0) { - if (_idToObjectIndex.contains(obj->idIndex)) { + if (obj->idNameIndex != 0) { + if (_idToObjectIndex.contains(obj->idNameIndex)) { recordError(obj->locationOfIdProperty, tr("id is not unique")); return false; } - _idToObjectIndex.insert(obj->idIndex, objectIndex); - _objectIndexToIdInScope->insert(objectIndex, _objectIndexToIdInScope->count()); + obj->id = _idToObjectIndex.count(); + _idToObjectIndex.insert(obj->idNameIndex, objectIndex); } - for (const QmlIR::Property *property = obj->firstProperty(); property; property = property->next) { - if (property->type == QV4::CompiledData::Property::Alias) { - _objectsWithAliases.append(objectIndex); - break; - } - } + if (obj->aliasCount() > 0) + _objectsWithAliases.append(objectIndex); + + // Stop at Component boundary + if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != compiler->rootObjectIndex()) + return true; for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { if (binding->type != QV4::CompiledData::Binding::Type_Object @@ -1581,10 +984,6 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) continue; - // Stop at Component boundary - if (std::binary_search(componentBoundaries.constBegin(), componentBoundaries.constEnd(), binding->value.objectIndex)) - continue; - if (!collectIdsAndAliases(binding->value.objectIndex)) return false; } @@ -1592,257 +991,205 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) return true; } -bool QQmlComponentAndAliasResolver::resolveAliases() +bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex) { - foreach (int objectIndex, _objectsWithAliases) { - const QmlIR::Object *obj = qmlObjects->at(objectIndex); + if (_objectsWithAliases.isEmpty()) + return true; - QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); - Q_ASSERT(propertyCache); + QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> aliasCacheCreator(&propertyCaches, compiler); - int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); - int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count(); - int effectiveAliasIndex = 0; + bool atLeastOneAliasResolved; + do { + atLeastOneAliasResolved = false; + QVector<int> pendingObjects; - const QmlIR::Property *p = obj->firstProperty(); - for (int propertyIndex = 0; propertyIndex < obj->propertyCount(); ++propertyIndex, p = p->next) { - if (p->type != QV4::CompiledData::Property::Alias) - continue; + for (int objectIndex: qAsConst(_objectsWithAliases)) { - const int idIndex = p->aliasIdValueIndex; - const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1); - if (targetObjectIndex == -1) { - recordError(p->aliasLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex))); + QQmlCompileError error; + const auto result = resolveAliasesInObject(objectIndex, &error); + + if (error.isSet()) { + recordError(error); return false; } - const int targetId = _objectIndexToIdInScope->value(targetObjectIndex, -1); - Q_ASSERT(targetId != -1); - - const QString aliasPropertyValue = stringAt(p->aliasPropertyValueIndex); - - QStringRef property; - QStringRef subProperty; - - const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.')); - if (propertySeparator != -1) { - property = aliasPropertyValue.leftRef(propertySeparator); - subProperty = aliasPropertyValue.midRef(propertySeparator + 1); - } else - property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length()); - - int propIdx = -1; - int propType = 0; - int notifySignal = -1; - int flags = 0; - int type = 0; - bool writable = false; - bool resettable = false; - - quint32 propertyFlags = QQmlPropertyData::IsAlias; - - if (property.isEmpty()) { - const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); - QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(targetObject->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - - if (typeRef->type) - type = typeRef->type->typeId(); - else - type = typeRef->component->metaTypeId; - - flags |= QML_ALIAS_FLAG_PTR; - propertyFlags |= QQmlPropertyData::IsQObjectDerived; + + if (result == AllAliasesResolved) { + aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex); + atLeastOneAliasResolved = true; + } else if (result == SomeAliasesResolved) { + atLeastOneAliasResolved = true; + pendingObjects.append(objectIndex); } else { - QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); - Q_ASSERT(targetCache); - QmlIR::PropertyResolver resolver(targetCache); + pendingObjects.append(objectIndex); + } + } + qSwap(_objectsWithAliases, pendingObjects); + } while (!_objectsWithAliases.isEmpty() && atLeastOneAliasResolved); - QQmlPropertyData *targetProperty = resolver.property(property.toString()); - if (!targetProperty || targetProperty->coreIndex > 0x0000FFFF) { - recordError(p->aliasLocation, tr("Invalid alias target location: %1").arg(property.toString())); - return false; - } + if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) { + const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first()); + for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) { + if (!(alias->flags & QV4::CompiledData::Alias::Resolved)) { + recordError(alias->location, tr("Circular alias reference detected")); + return false; + } + } + } - propIdx = targetProperty->coreIndex; - type = targetProperty->propType; + return true; +} - writable = targetProperty->isWritable(); - resettable = targetProperty->isResettable(); - notifySignal = targetProperty->notifyIndex; +QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, QQmlCompileError *error) +{ + const QmlIR::Object * const obj = qmlObjects->at(objectIndex); + if (!obj->aliasCount()) + return AllAliasesResolved; - if (!subProperty.isEmpty()) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type); - if (!valueTypeMetaObject) { - recordError(p->aliasLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); - return false; - } + int numResolvedAliases = 0; + bool seenUnresolvedAlias = false; - propType = type; + for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) { + if (alias->flags & QV4::CompiledData::Alias::Resolved) + continue; - int valueTypeIndex = - valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData()); - if (valueTypeIndex == -1) { - recordError(p->aliasLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); - return false; - } - Q_ASSERT(valueTypeIndex <= 0x0000FFFF); + seenUnresolvedAlias = true; - propIdx = QQmlPropertyData::encodeValueTypePropertyIndex(propIdx, valueTypeIndex); - if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) - type = QVariant::Int; - else - type = valueTypeMetaObject->property(valueTypeIndex).userType(); + const int idIndex = alias->idIndex; + const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1); + if (targetObjectIndex == -1) { + *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex))); + break; + } - } else { - if (targetProperty->isEnum()) { - type = QVariant::Int; - } else { - // Copy type flags - propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask; + const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); + Q_ASSERT(targetObject->id >= 0); + alias->targetObjectId = targetObject->id; + alias->aliasToLocalAlias = false; - if (targetProperty->isVarProperty()) - propertyFlags |= QQmlPropertyData::IsQVariant; + const QString aliasPropertyValue = stringAt(alias->propertyNameIndex); - if (targetProperty->isQObject()) - flags |= QML_ALIAS_FLAG_PTR; + QStringRef property; + QStringRef subProperty; + + const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.')); + if (propertySeparator != -1) { + property = aliasPropertyValue.leftRef(propertySeparator); + subProperty = aliasPropertyValue.midRef(propertySeparator + 1); + } else + property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length()); + + int propIdx = -1; + + if (property.isEmpty()) { + alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; + } else { + QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); + Q_ASSERT(targetCache); + QmlIR::PropertyResolver resolver(targetCache); + + QQmlPropertyData *targetProperty = resolver.property(property.toString()); + + // If it's an alias that we haven't resolved yet, try again later. + if (!targetProperty) { + bool aliasPointsToOtherAlias = false; + int localAliasIndex = 0; + for (auto targetAlias = targetObject->aliasesBegin(), end = targetObject->aliasesEnd(); targetAlias != end; ++targetAlias, ++localAliasIndex) { + if (stringAt(targetAlias->nameIndex) == property) { + aliasPointsToOtherAlias = true; + break; } } - } - - QQmlVMEMetaData::AliasData aliasData = { targetId, propIdx, propType, flags, notifySignal }; + if (aliasPointsToOtherAlias) { + if (targetObjectIndex == objectIndex) { + alias->localAliasIndex = localAliasIndex; + alias->aliasToLocalAlias = true; + alias->flags |= QV4::CompiledData::Alias::Resolved; + ++numResolvedAliases; + continue; + } - typedef QQmlVMEMetaData VMD; - QByteArray &dynamicData = (*vmeMetaObjectData)[objectIndex]; - Q_ASSERT(!dynamicData.isEmpty()); - VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); - *(vmd->aliasData() + effectiveAliasIndex++) = aliasData; + // Try again later and resolve the target alias first. + _objectsWithAliases.append(objectIndex); + // restore + alias->idIndex = idIndex; + break; + } + } - Q_ASSERT(dynamicData.isDetached()); + if (!targetProperty || targetProperty->coreIndex > 0x0000FFFF) { + *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString())); + break; + } - if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && writable) - propertyFlags |= QQmlPropertyData::IsWritable; - else - propertyFlags &= ~QQmlPropertyData::IsWritable; + propIdx = targetProperty->coreIndex; - if (resettable) - propertyFlags |= QQmlPropertyData::IsResettable; - else - propertyFlags &= ~QQmlPropertyData::IsResettable; + if (!subProperty.isEmpty()) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType); + if (!valueTypeMetaObject) { + *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); + break; + } - QString propertyName = stringAt(p->nameIndex); - if (propertyIndex == obj->indexOfDefaultProperty) propertyCache->_defaultPropertyName = propertyName; - propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - type, effectiveSignalIndex++); + int valueTypeIndex = + valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData()); + if (valueTypeIndex == -1) { + *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); + break; + } + Q_ASSERT(valueTypeIndex <= 0x0000FFFF); + propIdx = QQmlPropertyData::encodeValueTypePropertyIndex(propIdx, valueTypeIndex); + } else { + if (targetProperty->isQObject()) + alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; + } } + + alias->encodedMetaPropertyIndex = propIdx; + alias->flags |= QV4::CompiledData::Alias::Resolved; + numResolvedAliases++; } - return true; + + if (numResolvedAliases == 0) + return seenUnresolvedAlias ? NoAliasResolved : AllAliasesResolved; + + return SomeAliasesResolved; } -QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler) +QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler) : QQmlCompilePass(typeCompiler) - , enginePrivate(typeCompiler->enginePrivate()) - , qmlUnit(typeCompiler->qmlUnit()) - , resolvedTypes(*typeCompiler->resolvedTypes()) - , customParsers(typeCompiler->customParserCache()) + , qmlObjects(typeCompiler->qmlObjects()) , propertyCaches(typeCompiler->propertyCaches()) - , objectIndexToIdPerComponent(*typeCompiler->objectIndexToIdPerComponent()) - , customParserBindingsPerObject(typeCompiler->customParserBindings()) + , customParsers(typeCompiler->customParserCache()) , _seenObjectWithId(false) { } -bool QQmlPropertyValidator::validate() -{ - _bindingPropertyDataPerObject.resize(qmlUnit->nObjects); - if (!validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0)) - return false; - compiler->setDeferredBindingsPerObject(_deferredBindingsPerObject); - compiler->setBindingPropertyDataPerObject(_bindingPropertyDataPerObject); - return true; -} - -const QQmlImports &QQmlPropertyValidator::imports() const +bool QQmlDeferredAndCustomParserBindingScanner::scanObject() { - return *compiler->imports(); + return scanObject(compiler->rootObjectIndex()); } -typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector; - -struct BindingFinder -{ - bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const - { - return name < binding->propertyNameIndex; - } - bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const - { - return binding->propertyNameIndex < name; - } - bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const - { - return lhs->propertyNameIndex < rhs->propertyNameIndex; - } -}; - -bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const +bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) { - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex); - if (obj->idIndex != 0) + QmlIR::Object *obj = qmlObjects->at(objectIndex); + if (obj->idNameIndex != 0) _seenObjectWithId = true; - if (isComponent(objectIndex)) { - Q_ASSERT(obj->nBindings == 1); - const QV4::CompiledData::Binding *componentBinding = obj->bindingTable(); + if (obj->flags & QV4::CompiledData::Object::IsComponent) { + Q_ASSERT(obj->bindingCount() == 1); + const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); - return validateObject(componentBinding->value.objectIndex, componentBinding); + return scanObject(componentBinding->value.objectIndex); } - QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); + QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); if (!propertyCache) return true; - QStringList deferredPropertyNames; - { - const QMetaObject *mo = propertyCache->firstCppMetaObject(); - const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); - if (namesIndex != -1) { - QMetaClassInfo classInfo = mo->classInfo(namesIndex); - deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); - } - } - - QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex); - QList<const QV4::CompiledData::Binding*> customBindings; - - // Collect group properties first for sanity checking - // vector values are sorted by property name string index. - GroupPropertyVector groupProperties; - const QV4::CompiledData::Binding *binding = obj->bindingTable(); - for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { - if (!binding->isGroupProperty()) - continue; - - if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) - continue; - - if (populatingValueTypeGroupProperty) { - recordError(binding->location, tr("Property assignment expected")); - return false; - } - - GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); - groupProperties.insert(pos, binding); - } - - QBitArray customParserBindings(obj->nBindings); - QBitArray deferredBindings; - - QmlIR::PropertyResolver propertyResolver(propertyCache); - QString defaultPropertyName; QQmlPropertyData *defaultProperty = 0; - if (obj->indexOfDefaultProperty != -1) { + if (obj->indexOfDefaultPropertyOrAlias != -1) { QQmlPropertyCache *cache = propertyCache->parent(); defaultPropertyName = cache->defaultPropertyName(); defaultProperty = cache->defaultProperty(); @@ -1851,76 +1198,55 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD defaultProperty = propertyCache->defaultProperty(); } - QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings); + QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex); + + QmlIR::PropertyResolver propertyResolver(propertyCache); + + QStringList deferredPropertyNames; + { + const QMetaObject *mo = propertyCache->firstCppMetaObject(); + const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); + if (namesIndex != -1) { + QMetaClassInfo classInfo = mo->classInfo(namesIndex); + deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + } + } - binding = obj->bindingTable(); - for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { + for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + QQmlPropertyData *pd = 0; QString name = stringAt(binding->propertyNameIndex); if (customParser) { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { - customBindings << binding; - customParserBindings.setBit(i); + binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; + obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; continue; } } else if (QmlIR::IRBuilder::isSignalPropertyName(name) && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { - customBindings << binding; - customParserBindings.setBit(i); + obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; + binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; continue; } } - bool bindingToDefaultProperty = false; - bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty; - - bool notInRevision = false; - QQmlPropertyData *pd = 0; - if (!name.isEmpty()) { - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) - pd = propertyResolver.signal(name, ¬InRevision); - else - pd = propertyResolver.property(name, ¬InRevision, isGroupProperty ? QmlIR::PropertyResolver::IgnoreRevision : QmlIR::PropertyResolver::CheckRevision); - - if (notInRevision) { - QString typeName = stringAt(obj->inheritedTypeNameIndex); - QQmlCompiledData::TypeReference *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex); - if (objectType && objectType->type) { - COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type->module()).arg(objectType->majorVersion).arg(objectType->minorVersion)); - } else { - COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name)); - } - } + if (name.isEmpty()) { + pd = defaultProperty; + name = defaultPropertyName; } else { - if (isGroupProperty) - COMPILE_EXCEPTION(binding, tr("Cannot assign a value directly to a grouped property")); - - pd = defaultProperty; - name = defaultPropertyName; - bindingToDefaultProperty = true; - } - - if (pd) - collectedBindingPropertyData[i] = pd; + if (name.constData()->isUpper()) + continue; - if (name.constData()->isUpper() && !binding->isAttachedProperty()) { - QQmlType *type = 0; - QQmlImportNamespace *typeNamespace = 0; - compiler->imports()->resolveType(stringAt(binding->propertyNameIndex), &type, 0, 0, &typeNamespace); - if (typeNamespace) - recordError(binding->location, tr("Invalid use of namespace")); - else - recordError(binding->location, tr("Invalid attached object assignment")); - return false; + bool notInRevision = false; + pd = propertyResolver.property(name, ¬InRevision, QmlIR::PropertyResolver::CheckRevision); } bool seenSubObjectWithId = false; if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { qSwap(_seenObjectWithId, seenSubObjectWithId); - const bool subObjectValid = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)); + const bool subObjectValid = scanObject(binding->value.objectIndex); qSwap(_seenObjectWithId, seenSubObjectWithId); if (!subObjectValid) return false; @@ -1930,538 +1256,28 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD if (!seenSubObjectWithId && !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) { - if (deferredBindings.isEmpty()) - deferredBindings.resize(obj->nBindings); - - deferredBindings.setBit(i); + binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding; + obj->flags |= QV4::CompiledData::Object::HasDeferredBindings; } - // Signal handlers were resolved and checked earlier in the signal handler conversion pass. if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) continue; - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { - if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) { - recordError(binding->location, tr("Attached properties cannot be used here")); - return false; - } - continue; - } - - if (pd) { - GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); - const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex); - - if (!pd->isWritable() - && !pd->isQList() - && !binding->isGroupProperty() - && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration) - ) { - - if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object) - recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property")); - else - recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); - return false; - } - - if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) { - QString error; - if (pd->propType == qMetaTypeId<QQmlScriptString>()) - error = tr( "Cannot assign multiple values to a script property"); - else - error = tr( "Cannot assign multiple values to a singular property"); - recordError(binding->valueLocation, error); - return false; - } - - if (!bindingToDefaultProperty - && !binding->isGroupProperty() - && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) - && assigningToGroupProperty) { - QV4::CompiledData::Location loc = binding->valueLocation; - if (loc < (*assignedGroupProperty)->valueLocation) - loc = (*assignedGroupProperty)->valueLocation; - - if (pd && QQmlValueTypeFactory::isValueType(pd->propType)) - recordError(loc, tr("Property has already been assigned a value")); - else - recordError(loc, tr("Cannot assign a value directly to a grouped property")); - return false; - } - - if (binding->type < QV4::CompiledData::Binding::Type_Script) { - if (!validateLiteralBinding(propertyCache, pd, binding)) - return false; - } else if (binding->type == QV4::CompiledData::Binding::Type_Object) { - if (!validateObjectBinding(pd, name, binding)) - return false; - } else if (binding->isGroupProperty()) { - if (QQmlValueTypeFactory::isValueType(pd->propType)) { - if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)) { - if (!pd->isWritable()) { - recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); - return false; - } - } else { - recordError(binding->location, tr("Invalid grouped property access")); - return false; - } - } else { - if (!enginePrivate->propertyCacheForType(pd->propType)) { - recordError(binding->location, tr("Invalid grouped property access")); - return false; - } - } - } - } else { + if (!pd) { if (customParser) { - customBindings << binding; - customParserBindings.setBit(i); - continue; - } - if (bindingToDefaultProperty) { - COMPILE_EXCEPTION(binding, tr("Cannot assign to non-existent default property")); - } else { - COMPILE_EXCEPTION(binding, tr("Cannot assign to non-existent property \"%1\"").arg(name)); - } - } - } - - if (obj->idIndex) { - bool notInRevision = false; - collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), ¬InRevision); - } - - if (customParser && !customBindings.isEmpty()) { - customParser->clearErrors(); - customParser->validator = this; - customParser->engine = enginePrivate; - customParser->imports = compiler->imports(); - customParser->verifyBindings(qmlUnit, customBindings); - customParser->validator = 0; - customParser->engine = 0; - customParser->imports = (QQmlImports*)0; - customParserBindingsPerObject->insert(objectIndex, customParserBindings); - const QList<QQmlError> parserErrors = customParser->errors(); - if (!parserErrors.isEmpty()) { - foreach (const QQmlError &error, parserErrors) - compiler->recordError(error); - return false; - } - } - - if (!deferredBindings.isEmpty()) - _deferredBindingsPerObject.insert(objectIndex, deferredBindings); - - _bindingPropertyDataPerObject[objectIndex] = collectedBindingPropertyData; - - return true; -} - -bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const -{ - if (property->isQList()) { - recordError(binding->valueLocation, tr("Cannot assign primitives to lists")); - return false; - } - - if (property->isEnum()) { - if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) - return true; - - QString value = binding->valueAsString(qmlUnit); - QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex); - bool ok; - if (p.isFlagType()) { - p.enumerator().keysToValue(value.toUtf8().constData(), &ok); - } else - p.enumerator().keyToValue(value.toUtf8().constData(), &ok); - - if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration")); - return false; - } - return true; - } - - switch (property->propType) { - case QMetaType::QVariant: - break; - case QVariant::String: { - if (!binding->evaluatesToString()) { - recordError(binding->valueLocation, tr("Invalid property assignment: string expected")); - return false; - } - } - break; - case QVariant::StringList: { - if (!binding->evaluatesToString()) { - recordError(binding->valueLocation, tr("Invalid property assignment: string or string list expected")); - return false; - } - } - break; - case QVariant::ByteArray: { - if (binding->type != QV4::CompiledData::Binding::Type_String) { - recordError(binding->valueLocation, tr("Invalid property assignment: byte array expected")); - return false; - } - } - break; - case QVariant::Url: { - if (binding->type != QV4::CompiledData::Binding::Type_String) { - recordError(binding->valueLocation, tr("Invalid property assignment: url expected")); - return false; - } - } - break; - case QVariant::UInt: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double d = binding->valueAsNumber(); - if (double(uint(d)) == d) - return true; - } - recordError(binding->valueLocation, tr("Invalid property assignment: unsigned int expected")); - return false; - } - break; - case QVariant::Int: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double d = binding->valueAsNumber(); - if (double(int(d)) == d) - return true; - } - recordError(binding->valueLocation, tr("Invalid property assignment: int expected")); - return false; - } - break; - case QMetaType::Float: { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { - recordError(binding->valueLocation, tr("Invalid property assignment: number expected")); - return false; - } - } - break; - case QVariant::Double: { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { - recordError(binding->valueLocation, tr("Invalid property assignment: number expected")); - return false; - } - } - break; - case QVariant::Color: { - bool ok = false; - QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: color expected")); - return false; - } - } - break; -#ifndef QT_NO_DATESTRING - case QVariant::Date: { - bool ok = false; - QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: date expected")); - return false; - } - } - break; - case QVariant::Time: { - bool ok = false; - QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: time expected")); - return false; - } - } - break; - case QVariant::DateTime: { - bool ok = false; - QQmlStringConverters::dateTimeFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: datetime expected")); - return false; - } - } - break; -#endif // QT_NO_DATESTRING - case QVariant::Point: { - bool ok = false; - QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: point expected")); - return false; - } - } - break; - case QVariant::PointF: { - bool ok = false; - QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: point expected")); - return false; - } - } - break; - case QVariant::Size: { - bool ok = false; - QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: size expected")); - return false; - } - } - break; - case QVariant::SizeF: { - bool ok = false; - QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: size expected")); - return false; - } - } - break; - case QVariant::Rect: { - bool ok = false; - QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: rect expected")); - return false; - } - } - break; - case QVariant::RectF: { - bool ok = false; - QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok); - if (!ok) { - recordError(binding->valueLocation, tr("Invalid property assignment: point expected")); - return false; - } - } - break; - case QVariant::Bool: { - if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { - recordError(binding->valueLocation, tr("Invalid property assignment: boolean expected")); - return false; - } - } - break; - case QVariant::Vector2D: { - struct { - float xp; - float yp; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { - recordError(binding->valueLocation, tr("Invalid property assignment: 2D vector expected")); - return false; - } - } - break; - case QVariant::Vector3D: { - struct { - float xp; - float yp; - float zy; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { - recordError(binding->valueLocation, tr("Invalid property assignment: 3D vector expected")); - return false; - } - } - break; - case QVariant::Vector4D: { - struct { - float xp; - float yp; - float zy; - float wp; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { - recordError(binding->valueLocation, tr("Invalid property assignment: 4D vector expected")); - return false; - } - } - break; - case QVariant::Quaternion: { - struct { - float wp; - float xp; - float yp; - float zp; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) { - recordError(binding->valueLocation, tr("Invalid property assignment: quaternion expected")); - return false; - } - } - break; - case QVariant::RegExp: - recordError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); - return false; - default: { - // generate single literal value assignment to a list property if required - if (property->propType == qMetaTypeId<QList<qreal> >()) { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { - recordError(binding->valueLocation, tr("Invalid property assignment: number or array of numbers expected")); - return false; - } - break; - } else if (property->propType == qMetaTypeId<QList<int> >()) { - bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number); - if (ok) { - double n = binding->valueAsNumber(); - if (double(int(n)) != n) - ok = false; - } - if (!ok) - recordError(binding->valueLocation, tr("Invalid property assignment: int or array of ints expected")); - break; - } else if (property->propType == qMetaTypeId<QList<bool> >()) { - if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { - recordError(binding->valueLocation, tr("Invalid property assignment: bool or array of bools expected")); - return false; - } - break; - } else if (property->propType == qMetaTypeId<QList<QUrl> >()) { - if (binding->type != QV4::CompiledData::Binding::Type_String) { - recordError(binding->valueLocation, tr("Invalid property assignment: url or array of urls expected")); - return false; + obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; + binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; } - break; - } else if (property->propType == qMetaTypeId<QList<QString> >()) { - if (!binding->evaluatesToString()) { - recordError(binding->valueLocation, tr("Invalid property assignment: string or array of strings expected")); - return false; - } - break; - } else if (property->propType == qMetaTypeId<QJSValue>()) { - break; - } else if (property->propType == qMetaTypeId<QQmlScriptString>()) { - break; - } - - // otherwise, try a custom type assignment - QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType); - if (!converter) { - recordError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType)))); - return false; } } - break; - } - return true; -} - -/*! - Returns true if from can be assigned to a (QObject) property of type - to. -*/ -bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const -{ - QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to); - - while (fromMo) { - if (fromMo == toMo) - return true; - fromMo = fromMo->parent(); - } - return false; -} -bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const -{ - if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object); - - bool isValueSource = false; - bool isPropertyInterceptor = false; - - QQmlType *qmlType = 0; - const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex); - QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex); - if (typeRef) { - QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - const QMetaObject *mo = cache->firstCppMetaObject(); - while (mo && !qmlType) { - qmlType = QQmlMetaType::qmlType(mo); - mo = mo->superClass(); - } - Q_ASSERT(qmlType); - } - - if (qmlType) { - isValueSource = qmlType->propertyValueSourceCast() != -1; - isPropertyInterceptor = qmlType->propertyValueInterceptorCast() != -1; - } - - if (!isValueSource && !isPropertyInterceptor) { - recordError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName)); - return false; - } - - return true; - } - - if (QQmlMetaType::isInterface(property->propType)) { - // Can only check at instantiation time if the created sub-object successfully casts to the - // target interface. - return true; - } else if (property->propType == QMetaType::QVariant) { - // We can convert everything to QVariant :) - return true; - } else if (property->isQList()) { - const int listType = enginePrivate->listType(property->propType); - if (!QQmlMetaType::isInterface(listType)) { - QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); - if (!canCoerce(listType, source)) { - recordError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); - return false; - } - } - return true; - } else if (isComponent(binding->value.objectIndex)) { - return true; - } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { - return true; - } else if (QQmlValueTypeFactory::isValueType(property->propType)) { - recordError(binding->location, tr("Unexpected object assignment")); - return false; - } else if (property->propType == qMetaTypeId<QQmlScriptString>()) { - recordError(binding->valueLocation, tr("Invalid property assignment: script expected")); - return false; - } else { - // We want to raw metaObject here as the raw metaobject is the - // actual property type before we applied any extensions that might - // effect the properties on the type, but don't effect assignability - QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType); - - // Will be true if the assgned type inherits propertyMetaObject - bool isAssignable = false; - // Determine isAssignable value - if (propertyMetaObject) { - QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex); - while (c && !isAssignable) { - isAssignable |= c == propertyMetaObject; - c = c->parent(); - } - } - - if (!isAssignable) { - recordError(binding->valueLocation, tr("Cannot assign object to property")); - return false; - } - } return true; } QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen) : QQmlCompilePass(typeCompiler) - , objectIndexToIdPerComponent(*typeCompiler->objectIndexToIdPerComponent()) - , resolvedTypes(*typeCompiler->resolvedTypes()) + , resolvedTypes(typeCompiler->resolvedTypes) , customParsers(typeCompiler->customParserCache()) , qmlObjects(*typeCompiler->qmlObjects()) , propertyCaches(typeCompiler->propertyCaches()) @@ -2471,48 +1287,42 @@ QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR:: bool QQmlJSCodeGenerator::generateCodeForComponents() { - const QHash<int, QHash<int, int> > &objectIndexToIdPerComponent = *compiler->objectIndexToIdPerComponent(); - for (QHash<int, QHash<int, int> >::ConstIterator component = objectIndexToIdPerComponent.constBegin(), end = objectIndexToIdPerComponent.constEnd(); - component != end; ++component) { - if (!compileComponent(component.key(), component.value())) + const QVector<quint32> &componentRoots = compiler->componentRoots(); + for (int i = 0; i < componentRoots.count(); ++i) { + if (!compileComponent(componentRoots.at(i))) return false; } - return compileComponent(compiler->rootObjectIndex(), *compiler->objectIndexToIdForRoot()); + return compileComponent(compiler->rootObjectIndex()); } -bool QQmlJSCodeGenerator::compileComponent(int contextObject, const QHash<int, int> &objectIndexToId) +bool QQmlJSCodeGenerator::compileComponent(int contextObject) { - if (isComponent(contextObject)) { - const QmlIR::Object *component = qmlObjects.at(contextObject); - Q_ASSERT(component->bindingCount() == 1); - const QV4::CompiledData::Binding *componentBinding = component->firstBinding(); + const QmlIR::Object *obj = qmlObjects.at(contextObject); + if (obj->flags & QV4::CompiledData::Object::IsComponent) { + Q_ASSERT(obj->bindingCount() == 1); + const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); contextObject = componentBinding->value.objectIndex; } QmlIR::JSCodeGen::ObjectIdMapping idMapping; - if (!objectIndexToId.isEmpty()) { - idMapping.reserve(objectIndexToId.count()); + idMapping.reserve(obj->namedObjectsInComponent.count); + for (int i = 0; i < obj->namedObjectsInComponent.count; ++i) { + const int objectIndex = obj->namedObjectsInComponent.at(i); + QmlIR::JSCodeGen::IdMapping m; + const QmlIR::Object *obj = qmlObjects.at(objectIndex); + m.name = stringAt(obj->idNameIndex); + m.idIndex = obj->id; + m.type = propertyCaches->at(objectIndex); - for (QHash<int, int>::ConstIterator idIt = objectIndexToId.constBegin(), end = objectIndexToId.constEnd(); - idIt != end; ++idIt) { + auto *tref = resolvedTypes.value(obj->inheritedTypeNameIndex); + if (tref && tref->isFullyDynamicType) + m.type = 0; - const int objectIndex = idIt.key(); - QmlIR::JSCodeGen::IdMapping m; - const QmlIR::Object *obj = qmlObjects.at(objectIndex); - m.name = stringAt(obj->idIndex); - m.idIndex = idIt.value(); - m.type = propertyCaches.at(objectIndex); - - QQmlCompiledData::TypeReference *tref = resolvedTypes.value(obj->inheritedTypeNameIndex); - if (tref && tref->isFullyDynamicType) - m.type = 0; - - idMapping << m; - } + idMapping << m; } - v4CodeGen->beginContextScope(idMapping, propertyCaches.at(contextObject)); + v4CodeGen->beginContextScope(idMapping, propertyCaches->at(contextObject)); if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject)) return false; @@ -2522,12 +1332,12 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject, const QHash<int, i bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex) { - if (isComponent(objectIndex)) + QmlIR::Object *object = qmlObjects.at(objectIndex); + if (object->flags & QV4::CompiledData::Object::IsComponent) return true; - QmlIR::Object *object = qmlObjects.at(objectIndex); if (object->functionsAndExpressions->count > 0) { - QQmlPropertyCache *scopeObject = propertyCaches.at(scopeObjectIndex); + QQmlPropertyCache *scopeObject = propertyCaches->at(scopeObjectIndex); v4CodeGen->beginObjectScope(scopeObject); QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile; @@ -2546,8 +1356,7 @@ bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIn } QQmlJS::MemoryPool *pool = compiler->memoryPool(); - object->runtimeFunctionIndices = pool->New<QmlIR::FixedPoolArray<int> >(); - object->runtimeFunctionIndices->init(pool, runtimeFunctionIndices); + object->runtimeFunctionIndices.allocate(pool, runtimeFunctionIndices); } for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) { @@ -2580,20 +1389,20 @@ void QQmlDefaultPropertyMerger::mergeDefaultProperties() void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex) { - QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); + QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); if (!propertyCache) return; QmlIR::Object *object = qmlObjects.at(objectIndex); - QString defaultProperty = object->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName(); + QString defaultProperty = object->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName(); QmlIR::Binding *bindingsToReinsert = 0; QmlIR::Binding *tail = 0; QmlIR::Binding *previousBinding = 0; QmlIR::Binding *binding = object->firstBinding(); while (binding) { - if (binding->propertyNameIndex == 0 || stringAt(binding->propertyNameIndex) != defaultProperty) { + if (binding->propertyNameIndex == quint32(0) || stringAt(binding->propertyNameIndex) != defaultProperty) { previousBinding = binding; binding = binding->next; continue; @@ -2646,7 +1455,7 @@ void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBinding if (binding->type != QV4::CompiledData::Binding::Type_Script) continue; - const int irFunctionIndex = obj->runtimeFunctionIndices->at(binding->value.compiledScriptIndex); + const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex); QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex); if (simplifyBinding(irFunction, binding)) { irFunctionsToRemove.append(irFunctionIndex); @@ -2757,7 +1566,7 @@ bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR: for (QV4::IR::BasicBlock *bb : function->basicBlocks()) { for (QV4::IR::Stmt *s : bb->statements()) { - s->accept(this); + visit(s); if (!_canSimplify) return false; } @@ -2927,22 +1736,41 @@ void QQmlIRFunctionCleanser::clean() foreach (QV4::IR::Function *function, module->functions) { for (QV4::IR::BasicBlock *block : function->basicBlocks()) { for (QV4::IR::Stmt *s : block->statements()) { - s->accept(this); + visit(s); } } } foreach (QmlIR::Object *obj, *compiler->qmlObjects()) { - if (!obj->runtimeFunctionIndices) - continue; - for (int i = 0; i < obj->runtimeFunctionIndices->count; ++i) - (*obj->runtimeFunctionIndices)[i] = newFunctionIndices[obj->runtimeFunctionIndices->at(i)]; + for (int i = 0; i < obj->runtimeFunctionIndices.count; ++i) + obj->runtimeFunctionIndices[i] = newFunctionIndices[obj->runtimeFunctionIndices.at(i)]; + } +} + +void QQmlIRFunctionCleanser::visit(QV4::IR::Stmt *s) +{ + + switch (s->stmtKind) { + case QV4::IR::Stmt::PhiStmt: + // nothing to do + break; + default: + STMT_VISIT_ALL_KINDS(s); + break; } } -void QQmlIRFunctionCleanser::visitClosure(QV4::IR::Closure *closure) +void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e) { - closure->value = newFunctionIndices.at(closure->value); + switch (e->exprKind) { + case QV4::IR::Expr::ClosureExpr: { + auto closure = e->asClosure(); + closure->value = newFunctionIndices.at(closure->value); + } break; + default: + EXPR_VISIT_ALL_KINDS(e); + break; + } } QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 273ba01a88..6ad6ad8557 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -53,13 +53,12 @@ #include <qglobal.h> #include <qqmlerror.h> #include <qhash.h> -#include <private/qqmlcompiler_p.h> +#include <private/qqmltypeloader_p.h> #include <private/qqmlirbuilder_p.h> QT_BEGIN_NAMESPACE class QQmlEnginePrivate; -class QQmlCompiledData; class QQmlError; class QQmlTypeData; class QQmlImports; @@ -75,18 +74,40 @@ struct Location; } } +struct QQmlCompileError +{ + QQmlCompileError() {} + QQmlCompileError(const QV4::CompiledData::Location &location, const QString &description) + : location(location), description(description) {} + QV4::CompiledData::Location location; + QString description; + + bool isSet() const { return !description.isEmpty(); } +}; + struct QQmlTypeCompiler { Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) public: - QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlCompiledData *compiledData, QQmlTypeData *typeData, QmlIR::Document *document); + QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &importCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); + + // --- interface used by QQmlPropertyCacheCreator + typedef QmlIR::Object CompiledObject; + const QmlIR::Object *objectAt(int index) const { return document->objects.at(index); } + int objectCount() const { return document->objects.count(); } + QString stringAt(int idx) const; + QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); } + QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); } + QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypes; + // --- - bool compile(); + QV4::CompiledData::CompilationUnit *compile(); QList<QQmlError> compilationErrors() const { return errors; } - void recordError(const QQmlError &error); + void recordError(QQmlError error); + void recordError(const QV4::CompiledData::Location &location, const QString &description); + void recordError(const QQmlCompileError &error); - QString stringAt(int idx) const; int registerString(const QString &str); QV4::IR::Module *jsIRModule() const; @@ -96,70 +117,52 @@ public: QUrl url() const { return typeData->finalUrl(); } QQmlEnginePrivate *enginePrivate() const { return engine; } const QQmlImports *imports() const; - QHash<int, QQmlCompiledData::TypeReference *> *resolvedTypes(); - QList<QmlIR::Object*> *qmlObjects(); + QVector<QmlIR::Object *> *qmlObjects() const; int rootObjectIndex() const; - void setPropertyCaches(const QVector<QQmlPropertyCache *> &caches); - const QVector<QQmlPropertyCache *> &propertyCaches() const; - void setVMEMetaObjects(const QVector<QByteArray> &metaObjects); - QVector<QByteArray> *vmeMetaObjects() const; - QHash<int, int> *objectIndexToIdForRoot(); - QHash<int, QHash<int, int> > *objectIndexToIdPerComponent(); - QHash<int, QBitArray> *customParserBindings(); + void setPropertyCaches(QQmlPropertyCacheVector &&caches); + const QQmlPropertyCacheVector *propertyCaches() const; + QQmlPropertyCacheVector &&takePropertyCaches(); + void setComponentRoots(const QVector<quint32> &roots) { m_componentRoots = roots; } + const QVector<quint32> &componentRoots() const { return m_componentRoots; } QQmlJS::MemoryPool *memoryPool(); QStringRef newStringRef(const QString &string); const QV4::Compiler::StringTableGenerator *stringPool() const; - void setDeferredBindingsPerObject(const QHash<int, QBitArray> &deferredBindingsPerObject); void setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData); const QHash<int, QQmlCustomParser*> &customParserCache() const { return customParsers; } QString bindingAsString(const QmlIR::Object *object, int scriptIndex) const; + void addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion); + private: QList<QQmlError> errors; QQmlEnginePrivate *engine; - QQmlCompiledData *compiledData; QQmlTypeData *typeData; + QQmlRefPointer<QQmlTypeNameCache> importCache; QmlIR::Document *document; // index is string index of type name (use obj->inheritedTypeNameIndex) QHash<int, QQmlCustomParser*> customParsers; + + // index in first hash is component index, vector inside contains object indices of objects with id property + QVector<quint32> m_componentRoots; + QQmlPropertyCacheVector m_propertyCaches; }; struct QQmlCompilePass { - virtual ~QQmlCompilePass() {} - QQmlCompilePass(QQmlTypeCompiler *typeCompiler); QString stringAt(int idx) const { return compiler->stringAt(idx); } protected: - void recordError(const QV4::CompiledData::Location &location, const QString &description) const; + void recordError(const QV4::CompiledData::Location &location, const QString &description) const + { compiler->recordError(location, description); } + void recordError(const QQmlCompileError &error) + { compiler->recordError(error); } QQmlTypeCompiler *compiler; }; -class QQmlPropertyCacheCreator : public QQmlCompilePass -{ - Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreator) -public: - QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler); - ~QQmlPropertyCacheCreator(); - - bool buildMetaObjects(); -protected: - bool buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding); - bool ensureMetaObject(int objectIndex); - bool createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache); - - QQmlEnginePrivate *enginePrivate; - const QList<QmlIR::Object*> &qmlObjects; - const QQmlImports *imports; - QHash<int, QQmlCompiledData::TypeReference*> *resolvedTypes; - QVector<QByteArray> vmeMetaObjects; - QVector<QQmlPropertyCache*> propertyCaches; -}; - // "Converts" signal expressions to full-fleged function declarations with // parameters taken from the signal declarations // It also updates the QV4::CompiledData::Binding objects to set the property name @@ -176,12 +179,12 @@ private: bool convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache); QQmlEnginePrivate *enginePrivate; - const QList<QmlIR::Object*> &qmlObjects; + const QVector<QmlIR::Object*> &qmlObjects; const QQmlImports *imports; const QHash<int, QQmlCustomParser*> &customParsers; - const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes; + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes; const QSet<QString> &illegalNames; - const QVector<QQmlPropertyCache*> &propertyCaches; + const QQmlPropertyCacheVector * const propertyCaches; }; // ### This will go away when the codegen resolves all enums to constant expressions @@ -207,10 +210,10 @@ private: int evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const; - const QList<QmlIR::Object*> &qmlObjects; - const QVector<QQmlPropertyCache *> propertyCaches; + const QVector<QmlIR::Object*> &qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; const QQmlImports *imports; - QHash<int, QQmlCompiledData::TypeReference *> *resolvedTypes; + QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes; }; class QQmlCustomParserScriptIndexer: public QQmlCompilePass @@ -223,7 +226,7 @@ public: private: void scanObjectRecursively(int objectIndex, bool annotateScriptBindings = false); - const QList<QmlIR::Object*> &qmlObjects; + const QVector<QmlIR::Object*> &qmlObjects; const QHash<int, QQmlCustomParser*> &customParsers; }; @@ -235,8 +238,8 @@ public: void annotateBindingsToAliases(); private: - const QList<QmlIR::Object*> &qmlObjects; - const QVector<QQmlPropertyCache *> propertyCaches; + const QVector<QmlIR::Object*> &qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; }; class QQmlScriptStringScanner : public QQmlCompilePass @@ -247,8 +250,8 @@ public: void scan(); private: - const QList<QmlIR::Object*> &qmlObjects; - const QVector<QQmlPropertyCache *> propertyCaches; + const QVector<QmlIR::Object*> &qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; }; class QQmlComponentAndAliasResolver : public QQmlCompilePass @@ -262,64 +265,48 @@ public: protected: void findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache); bool collectIdsAndAliases(int objectIndex); - bool resolveAliases(); + bool resolveAliases(int componentIndex); + void propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags); + + enum AliasResolutionResult { + NoAliasResolved, + SomeAliasesResolved, + AllAliasesResolved + }; + + AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlCompileError *error); QQmlEnginePrivate *enginePrivate; QQmlJS::MemoryPool *pool; - QList<QmlIR::Object*> *qmlObjects; + QVector<QmlIR::Object*> *qmlObjects; const int indexOfRootObject; // indices of the objects that are actually Component {} - QVector<int> componentRoots; - // indices of objects that are the beginning of a new component - // scope. This is sorted and used for binary search. - QVector<quint32> componentBoundaries; + QVector<quint32> componentRoots; - int _componentIndex; QHash<int, int> _idToObjectIndex; - QHash<int, int> *_objectIndexToIdInScope; - QList<int> _objectsWithAliases; - - QHash<int, QQmlCompiledData::TypeReference*> *resolvedTypes; - QVector<QQmlPropertyCache *> propertyCaches; - QVector<QByteArray> *vmeMetaObjectData; - QHash<int, int> *objectIndexToIdForRoot; - QHash<int, QHash<int, int> > *objectIndexToIdPerComponent; + QVector<int> _objectsWithAliases; + + QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes; + QQmlPropertyCacheVector propertyCaches; }; -class QQmlPropertyValidator : public QQmlCompilePass +class QQmlDeferredAndCustomParserBindingScanner : public QQmlCompilePass { - Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) public: - QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler); - - bool validate(); + QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler); - const QQmlImports &imports() const; - QQmlEnginePrivate *engine() const { return enginePrivate; } + bool scanObject(); private: - bool validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const; - bool validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const; - bool validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const; - - bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } - - bool canCoerce(int to, QQmlPropertyCache *fromMo) const; + bool scanObject(int objectIndex); - QQmlEnginePrivate *enginePrivate; - const QV4::CompiledData::Unit *qmlUnit; - const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes; + QVector<QmlIR::Object*> *qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; const QHash<int, QQmlCustomParser*> &customParsers; - const QVector<QQmlPropertyCache *> &propertyCaches; - const QHash<int, QHash<int, int> > objectIndexToIdPerComponent; - QHash<int, QBitArray> *customParserBindingsPerObject; - - // collected state variables, essentially write-only - mutable QHash<int, QBitArray> _deferredBindingsPerObject; - mutable bool _seenObjectWithId; - mutable QVector<QV4::CompiledData::BindingPropertyData> _bindingPropertyDataPerObject; + + bool _seenObjectWithId; }; // ### merge with QtQml::JSCodeGen and operate directly on object->functionsAndExpressions once old compiler is gone. @@ -331,16 +318,13 @@ public: bool generateCodeForComponents(); private: - bool compileComponent(int componentRoot, const QHash<int, int> &objectIndexToId); + bool compileComponent(int componentRoot); bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex); - bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } - - const QHash<int, QHash<int, int> > &objectIndexToIdPerComponent; - const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes; + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes; const QHash<int, QQmlCustomParser*> &customParsers; - const QList<QmlIR::Object*> &qmlObjects; - const QVector<QQmlPropertyCache *> &propertyCaches; + const QVector<QmlIR::Object*> &qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; QmlIR::JSCodeGen * const v4CodeGen; }; @@ -354,11 +338,11 @@ public: private: void mergeDefaultProperties(int objectIndex); - const QList<QmlIR::Object*> &qmlObjects; - const QVector<QQmlPropertyCache*> &propertyCaches; + const QVector<QmlIR::Object*> &qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; }; -class QQmlJavaScriptBindingExpressionSimplificationPass : public QQmlCompilePass, public QV4::IR::StmtVisitor +class QQmlJavaScriptBindingExpressionSimplificationPass : public QQmlCompilePass { public: QQmlJavaScriptBindingExpressionSimplificationPass(QQmlTypeCompiler *typeCompiler); @@ -368,12 +352,30 @@ public: private: void reduceTranslationBindings(int objectIndex); - virtual void visitMove(QV4::IR::Move *move); - virtual void visitJump(QV4::IR::Jump *) {} - virtual void visitCJump(QV4::IR::CJump *) { discard(); } - virtual void visitExp(QV4::IR::Exp *) { discard(); } - virtual void visitPhi(QV4::IR::Phi *) {} - virtual void visitRet(QV4::IR::Ret *ret); + void visit(QV4::IR::Stmt *s) + { + switch (s->stmtKind) { + case QV4::IR::Stmt::MoveStmt: + visitMove(s->asMove()); + break; + case QV4::IR::Stmt::RetStmt: + visitRet(s->asRet()); + break; + case QV4::IR::Stmt::CJumpStmt: + discard(); + break; + case QV4::IR::Stmt::ExpStmt: + discard(); + break; + case QV4::IR::Stmt::JumpStmt: + break; + case QV4::IR::Stmt::PhiStmt: + break; + } + } + + void visitMove(QV4::IR::Move *move); + void visitRet(QV4::IR::Ret *ret); void visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target); @@ -382,7 +384,7 @@ private: bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding); bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding); - const QList<QmlIR::Object*> &qmlObjects; + const QVector<QmlIR::Object*> &qmlObjects; QV4::IR::Module *jsModule; bool _canSimplify; @@ -397,8 +399,7 @@ private: QVector<int> irFunctionsToRemove; }; -class QQmlIRFunctionCleanser : public QQmlCompilePass, public QV4::IR::StmtVisitor, - public QV4::IR::ExprVisitor +class QQmlIRFunctionCleanser : public QQmlCompilePass { public: QQmlIRFunctionCleanser(QQmlTypeCompiler *typeCompiler, const QVector<int> &functionsToRemove); @@ -406,51 +407,13 @@ public: void clean(); private: - virtual void visitClosure(QV4::IR::Closure *closure); - - virtual void visitTemp(QV4::IR::Temp *) {} - virtual void visitArgLocal(QV4::IR::ArgLocal *) {} - virtual void visitMove(QV4::IR::Move *s) { - s->source->accept(this); - s->target->accept(this); - } - - virtual void visitConvert(QV4::IR::Convert *e) { e->expr->accept(this); } - virtual void visitPhi(QV4::IR::Phi *) { } - - virtual void visitExp(QV4::IR::Exp *s) { s->expr->accept(this); } - - virtual void visitJump(QV4::IR::Jump *) {} - virtual void visitCJump(QV4::IR::CJump *s) { s->cond->accept(this); } - virtual void visitRet(QV4::IR::Ret *s) { s->expr->accept(this); } - - virtual void visitConst(QV4::IR::Const *) {} - virtual void visitString(QV4::IR::String *) {} - virtual void visitRegExp(QV4::IR::RegExp *) {} - virtual void visitName(QV4::IR::Name *) {} - virtual void visitUnop(QV4::IR::Unop *e) { e->expr->accept(this); } - virtual void visitBinop(QV4::IR::Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitCall(QV4::IR::Call *e) { - e->base->accept(this); - for (QV4::IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); + visit(s->source); + visit(s->target); } - virtual void visitNew(QV4::IR::New *e) { - e->base->accept(this); - for (QV4::IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitSubscript(QV4::IR::Subscript *e) { - e->base->accept(this); - e->index->accept(this); - } - - virtual void visitMember(QV4::IR::Member *e) { - e->base->accept(this); - } + void visit(QV4::IR::Stmt *s); + void visit(QV4::IR::Expr *e); private: QV4::IR::Module *module; diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index ad28e4d6d1..4130ad08d7 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -2026,6 +2026,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, function->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); function->isStrict = _env->isStrict; function->isNamedExpression = _env->isNamedFunctionExpression; + function->isQmlBinding = _env->compilationMode == QmlBinding; AST::SourceLocation loc = ast->firstSourceLocation(); function->line = loc.startLine; @@ -2046,7 +2047,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, } } else { if (!_env->isStrict) { - foreach (const QString &inheritedLocal, inheritedLocals) { + for (const QString &inheritedLocal : qAsConst(inheritedLocals)) { function->LOCAL(inheritedLocal); unsigned tempIndex = entryBlock->newTemp(); Environment::Member member = { Environment::UndefinedMember, @@ -2088,7 +2089,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, _function->RECEIVE(it->name.toString()); } - foreach (const Environment::Member &member, _env->members) { + for (const Environment::Member &member : qAsConst(_env->members)) { if (member.function) { const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body ? member.function->body->elements : 0); @@ -2932,7 +2933,7 @@ QList<QQmlError> Codegen::qmlErrors() const qmlErrors.reserve(_errors.size()); QUrl url(_fileNameIsUrl ? QUrl(_module->fileName) : QUrl::fromLocalFile(_module->fileName)); - foreach (const QQmlJS::DiagnosticMessage &msg, _errors) { + for (const QQmlJS::DiagnosticMessage &msg: qAsConst(_errors)) { QQmlError e; e.setUrl(url); e.setLine(msg.loc.startLine); diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index a63f35152a..2aab1743cc 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -47,6 +47,14 @@ #include <private/qv4lookup_p.h> #include <private/qv4regexpobject_p.h> #include <private/qqmlpropertycache_p.h> +#include <private/qqmltypeloader_p.h> +#include <private/qqmlengine_p.h> +#include <QQmlPropertyMap> +#include <QDateTime> +#include <QSaveFile> +#include <QFile> +#include <QFileInfo> +#include <QScopedValueRollback> #endif #include <private/qqmlirbuilder_p.h> #include <QCoreApplication> @@ -67,11 +75,20 @@ CompilationUnit::CompilationUnit() , runtimeLookups(0) , runtimeRegularExpressions(0) , runtimeClasses(0) + , totalBindingsCount(0) + , totalParserStatusCount(0) + , totalObjectCount(0) + , metaTypeId(-1) + , listMetaTypeId(-1) + , isRegisteredWithEngine(false) {} CompilationUnit::~CompilationUnit() { unlink(); + if (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) + free(const_cast<Unit *>(data)); + data = 0; } QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) @@ -109,7 +126,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) for (uint i = 0; i < data->lookupTableSize; ++i) { QV4::Lookup *l = runtimeLookups + i; - Lookup::Type type = Lookup::Type(compiledLookups[i].type_and_flags); + Lookup::Type type = Lookup::Type(uint(compiledLookups[i].type_and_flags)); if (type == CompiledData::Lookup::Type_Getter) l->getter = QV4::Lookup::getterGeneric; else if (type == CompiledData::Lookup::Type_Setter) @@ -146,12 +163,6 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) linkBackendToEngine(engine); -#if 0 - runtimeFunctionsSortedByAddress.resize(runtimeFunctions.size()); - memcpy(runtimeFunctionsSortedByAddress.data(), runtimeFunctions.data(), runtimeFunctions.size() * sizeof(QV4::Function*)); - std::sort(runtimeFunctionsSortedByAddress.begin(), runtimeFunctionsSortedByAddress.end(), functionSortHelper); -#endif - if (data->indexOfRootFunction != -1) return runtimeFunctions[data->indexOfRootFunction]; else @@ -162,10 +173,26 @@ void CompilationUnit::unlink() { if (engine) engine->compilationUnits.erase(engine->compilationUnits.find(this)); + + if (isRegisteredWithEngine) { + Q_ASSERT(data && quint32(propertyCaches.count()) > data->indexOfRootObject && propertyCaches.at(data->indexOfRootObject)); + QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(propertyCaches.at(data->indexOfRootObject)->engine); + qmlEngine->unregisterInternalCompositeType(this); + isRegisteredWithEngine = false; + } + + propertyCaches.clear(); + + for (int ii = 0; ii < dependentScripts.count(); ++ii) + dependentScripts.at(ii)->release(); + dependentScripts.clear(); + + importCache = nullptr; + + qDeleteAll(resolvedTypes); + resolvedTypes.clear(); + engine = 0; - if (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) - free(data); - data = 0; free(runtimeStrings); runtimeStrings = 0; delete [] runtimeLookups; @@ -189,6 +216,223 @@ void CompilationUnit::markObjects(QV4::ExecutionEngine *e) } } +void CompilationUnit::destroy() +{ + QQmlEngine *qmlEngine = 0; + if (engine && engine->v8Engine) + qmlEngine = engine->v8Engine->engine(); + if (qmlEngine) + QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this); + else + delete this; +} + +IdentifierHash<int> CompilationUnit::namedObjectsPerComponent(int componentObjectIndex) +{ + auto it = namedObjectsPerComponentCache.find(componentObjectIndex); + if (it == namedObjectsPerComponentCache.end()) { + IdentifierHash<int> namedObjectCache(engine); + const CompiledData::Object *component = data->objectAt(componentObjectIndex); + const LEUInt32 *namedObjectIndexPtr = component->namedObjectsInComponentTable(); + for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) { + const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr); + namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id); + } + it = namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache); + } + return *it; +} + +void CompilationUnit::finalize(QQmlEnginePrivate *engine) +{ + // Add to type registry of composites + if (propertyCaches.needsVMEMetaObject(data->indexOfRootObject)) + engine->registerInternalCompositeType(this); + else { + const QV4::CompiledData::Object *obj = objectAt(data->indexOfRootObject); + auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + if (typeRef->compilationUnit) { + metaTypeId = typeRef->compilationUnit->metaTypeId; + listMetaTypeId = typeRef->compilationUnit->listMetaTypeId; + } else { + metaTypeId = typeRef->type->typeId(); + listMetaTypeId = typeRef->type->qListTypeId(); + } + } + + // Collect some data for instantiation later. + int bindingCount = 0; + int parserStatusCount = 0; + int objectCount = 0; + for (quint32 i = 0; i < data->nObjects; ++i) { + const QV4::CompiledData::Object *obj = data->objectAt(i); + bindingCount += obj->nBindings; + if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { + if (QQmlType *qmlType = typeRef->type) { + if (qmlType->parserStatusCast() != -1) + ++parserStatusCount; + } + ++objectCount; + if (typeRef->compilationUnit) { + bindingCount += typeRef->compilationUnit->totalBindingsCount; + parserStatusCount += typeRef->compilationUnit->totalParserStatusCount; + objectCount += typeRef->compilationUnit->totalObjectCount; + } + } + } + + totalBindingsCount = bindingCount; + totalParserStatusCount = parserStatusCount; + totalObjectCount = objectCount; +} + +bool CompilationUnit::saveToDisk(QString *errorString) +{ + errorString->clear(); + + const QUrl unitUrl = url(); + if (!unitUrl.isLocalFile()) { + *errorString = QStringLiteral("File has to be a local file."); + return false; + } + + // Foo.qml -> Foo.qmlc + QSaveFile cacheFile(unitUrl.toLocalFile() + QLatin1Char('c')); + if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + *errorString = cacheFile.errorString(); + return false; + } + + QByteArray modifiedUnit; + modifiedUnit.resize(data->unitSize); + memcpy(modifiedUnit.data(), data, data->unitSize); + const char *dataPtr = modifiedUnit.data(); + Unit *unitPtr; + memcpy(&unitPtr, &dataPtr, sizeof(unitPtr)); + unitPtr->flags |= Unit::StaticData; + + prepareCodeOffsetsForDiskStorage(unitPtr); + + qint64 headerWritten = cacheFile.write(modifiedUnit); + if (headerWritten != modifiedUnit.size()) { + *errorString = cacheFile.errorString(); + return false; + } + + if (!saveCodeToDisk(&cacheFile, unitPtr, errorString)) + return false; + + if (!cacheFile.commit()) { + *errorString = cacheFile.errorString(); + return false; + } + + return true; +} + +bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) +{ + if (!url.isLocalFile()) { + *errorString = QStringLiteral("File has to be a local file."); + return false; + } + + const QString sourcePath = url.toLocalFile(); + QScopedPointer<QFile> cacheFile(new QFile(sourcePath + QLatin1Char('c'))); + + if (!cacheFile->open(QIODevice::ReadOnly)) { + *errorString = cacheFile->errorString(); + return false; + } + + { + CompiledData::Unit header; + qint64 bytesRead = cacheFile->read(reinterpret_cast<char *>(&header), sizeof(header)); + + if (bytesRead != sizeof(header)) { + *errorString = QStringLiteral("File too small for the header fields"); + return false; + } + + if (strncmp(header.magic, CompiledData::magic_str, sizeof(header.magic))) { + *errorString = QStringLiteral("Magic bytes in the header do not match"); + return false; + } + + if (header.version != quint32(QV4_DATA_STRUCTURE_VERSION)) { + *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(header.version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); + return false; + } + + if (header.qtVersion != quint32(QT_VERSION)) { + *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(header.qtVersion, 0, 16).arg(QT_VERSION, 0, 16); + return false; + } + + { + QFileInfo sourceCode(sourcePath); + if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != header.sourceTimeStamp) { + *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); + return false; + } + } + + } + // Data structure and qt version matched, so now we can access the rest of the file safely. + + uchar *cacheData = cacheFile->map(/*offset*/0, cacheFile->size()); + if (!cacheData) { + *errorString = cacheFile->errorString(); + return false; + } + + QScopedValueRollback<const Unit *> dataPtrChange(data, reinterpret_cast<const Unit *>(cacheData)); + + { + const QString foundArchitecture = stringAt(data->architectureIndex); + const QString expectedArchitecture = QSysInfo::buildAbi(); + if (foundArchitecture != expectedArchitecture) { + *errorString = QString::fromUtf8("Architecture mismatch. Found %1 expected %2").arg(foundArchitecture).arg(expectedArchitecture); + return false; + } + } + + { + const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex); + const QString expectedCodeGenerator = iselFactory->codeGeneratorName; + if (foundCodeGenerator != expectedCodeGenerator) { + *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator); + return false; + } + } + + if (!memoryMapCode(errorString)) + return false; + + dataPtrChange.commit(); + backingFile.reset(cacheFile.take()); + return true; +} + +void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) +{ + Q_UNUSED(unit); +} + +bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QString *errorString) +{ + Q_UNUSED(device); + Q_UNUSED(unit); + *errorString = QStringLiteral("Saving code to disk is not supported in this configuration"); + return false; +} + +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + *errorString = QStringLiteral("Missing code mapping backend"); + return false; +} #endif // V4_BOOTSTRAP Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) @@ -205,7 +449,7 @@ QString Binding::valueAsString(const Unit *unit) const case Type_Boolean: return value.b ? QStringLiteral("true") : QStringLiteral("false"); case Type_Number: - return QString::number(value.d); + return QString::number(valueAsNumber()); case Type_Invalid: return QString(); #ifdef QT_NO_TRANSLATION @@ -287,6 +531,56 @@ QString Binding::valueAsScriptString(const Unit *unit) const return valueAsString(unit); } +#ifndef V4_BOOTSTRAP +/*! +Returns the property cache, if one alread exists. The cache is not referenced. +*/ +QQmlPropertyCache *ResolvedTypeReference::propertyCache() const +{ + if (type) + return typePropertyCache; + else + return compilationUnit->rootPropertyCache(); +} + +/*! +Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. +*/ +QQmlPropertyCache *ResolvedTypeReference::createPropertyCache(QQmlEngine *engine) +{ + if (typePropertyCache) { + return typePropertyCache; + } else if (type) { + typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type->metaObject()); + return typePropertyCache; + } else { + return compilationUnit->rootPropertyCache(); + } +} + +template <typename T> +bool qtTypeInherits(const QMetaObject *mo) { + while (mo) { + if (mo == &T::staticMetaObject) + return true; + mo = mo->superClass(); + } + return false; +} + +void ResolvedTypeReference::doDynamicTypeCheck() +{ + const QMetaObject *mo = 0; + if (typePropertyCache) + mo = typePropertyCache->firstCppMetaObject(); + else if (type) + mo = type->metaObject(); + else if (compilationUnit) + mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); + isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo); +} +#endif + } } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 8c617875e0..82e303f27d 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -60,11 +60,26 @@ #include <private/qv4executableallocator_p.h> #include <private/qqmlrefcount_p.h> #include <private/qqmlnullablevalue_p.h> +#include <private/qv4identifier_p.h> +#include <private/qflagpointer_p.h> +#include <private/qjson_p.h> +#ifndef V4_BOOTSTRAP +#include <private/qqmltypenamecache_p.h> +#include <private/qqmlpropertycache_p.h> +#endif QT_BEGIN_NAMESPACE +// Bump this whenever the compiler data structures change in an incompatible way. +#define QV4_DATA_STRUCTURE_VERSION 0x01 + +class QIODevice; class QQmlPropertyCache; class QQmlPropertyData; +class QQmlTypeNameCache; +class QQmlScriptData; +class QQmlType; +class QQmlEngine; namespace QmlIR { struct Document; @@ -76,25 +91,48 @@ struct Function; } struct Function; +class EvalISelFactory; namespace CompiledData { +typedef QJsonPrivate::q_littleendian<qint16> LEInt16; +typedef QJsonPrivate::q_littleendian<quint16> LEUInt16; +typedef QJsonPrivate::q_littleendian<quint32> LEUInt32; +typedef QJsonPrivate::q_littleendian<qint32> LEInt32; +typedef QJsonPrivate::q_littleendian<quint64> LEUInt64; +typedef QJsonPrivate::q_littleendian<qint64> LEInt64; + struct String; struct Function; struct Lookup; struct RegExp; struct Unit; +template <typename ItemType, typename Container, const ItemType *(Container::*IndexedGetter)(int index) const> +struct TableIterator +{ + TableIterator(const Container *container, int index) : container(container), index(index) {} + const Container *container; + int index; + + const ItemType *operator->() { return (container->*IndexedGetter)(index); } + void operator++() { ++index; } + bool operator==(const TableIterator &rhs) const { return index == rhs.index; } + bool operator!=(const TableIterator &rhs) const { return index != rhs.index; } +}; + #if defined(Q_CC_MSVC) || defined(Q_CC_GNU) #pragma pack(push, 1) #endif struct Location { - qint32 line; - qint32 column; + union { + QJsonPrivate::qle_bitfield<0, 20> line; + QJsonPrivate::qle_bitfield<20, 12> column; + }; - Location(): line(-1), column(-1) {} + Location() { line = 0; column = 0; } inline bool operator<(const Location &other) const { return line < other.line || @@ -104,20 +142,22 @@ struct Location struct RegExp { - enum Flags { + enum Flags : unsigned int { RegExp_Global = 0x01, RegExp_IgnoreCase = 0x02, RegExp_Multiline = 0x04 }; - quint32 flags; - quint32 stringIndex; + union { + QJsonPrivate::qle_bitfield<0, 4> flags; + QJsonPrivate::qle_bitfield<4, 28> stringIndex; + }; - static int calculateSize() { return sizeof(RegExp); } + RegExp() { flags = 0; stringIndex = 0; } }; struct Lookup { - enum Type { + enum Type : unsigned int { Type_Getter = 0x0, Type_Setter = 0x1, Type_GlobalGetter = 2, @@ -125,21 +165,27 @@ struct Lookup Type_IndexedSetter = 4 }; - quint32 type_and_flags; - quint32 nameIndex; + union { + QJsonPrivate::qle_bitfield<0, 4> type_and_flags; + QJsonPrivate::qle_bitfield<4, 28> nameIndex; + }; - static int calculateSize() { return sizeof(Lookup); } + Lookup() { type_and_flags = 0; nameIndex = 0; } }; struct JSClassMember { - uint nameOffset : 31; - uint isAccessor : 1; + union { + QJsonPrivate::qle_bitfield<0, 31> nameOffset; + QJsonPrivate::qle_bitfield<31, 1> isAccessor; + }; + + JSClassMember() { nameOffset = 0; isAccessor = 0; } }; struct JSClass { - uint nMembers; + LEUInt32 nMembers; // JSClassMember[nMembers] static int calculateSize(int nMembers) { return (sizeof(JSClass) + nMembers * sizeof(JSClassMember) + 7) & ~7; } @@ -147,8 +193,7 @@ struct JSClass struct String { - quint32 flags; // isArrayIndex - qint32 size; + LEInt32 size; // uint16 strdata[] static int calculateSize(const QString &str) { @@ -158,7 +203,7 @@ struct String struct Function { - enum Flags { + enum Flags : unsigned int { HasDirectEval = 0x1, UsesArgumentsObject = 0x2, IsStrict = 0x4, @@ -166,36 +211,46 @@ struct Function HasCatchOrWith = 0x10 }; - quint32 index; // in CompilationUnit's function table - quint32 nameIndex; - qint64 flags; - quint32 nFormals; - quint32 formalsOffset; - quint32 nLocals; - quint32 localsOffset; - quint32 nInnerFunctions; - quint32 innerFunctionsOffset; + LEUInt32 nameIndex; + LEUInt32 nFormals; + LEUInt32 formalsOffset; + LEUInt32 nLocals; + LEUInt32 localsOffset; + LEUInt32 nInnerFunctions; Location location; // Qml Extensions Begin - quint32 nDependingIdObjects; - quint32 dependingIdObjectsOffset; // Array of resolved ID objects - quint32 nDependingContextProperties; - quint32 dependingContextPropertiesOffset; // Array of int pairs (property index and notify index) - quint32 nDependingScopeProperties; - quint32 dependingScopePropertiesOffset; // Array of int pairs (property index and notify index) + LEUInt32 nDependingIdObjects; + LEUInt32 dependingIdObjectsOffset; // Array of resolved ID objects + LEUInt32 nDependingContextProperties; + LEUInt32 dependingContextPropertiesOffset; // Array of int pairs (property index and notify index) + LEUInt32 nDependingScopeProperties; + LEUInt32 dependingScopePropertiesOffset; // Array of int pairs (property index and notify index) // Qml Extensions End + // Absolute offset into file where the code for this function is located. Only used when the function + // is serialized. + LEUInt64 codeOffset; + LEUInt64 codeSize; + // quint32 formalsIndex[nFormals] // quint32 localsIndex[nLocals] // quint32 offsetForInnerFunctions[nInnerFunctions] // Function[nInnerFunctions] - const quint32 *formalsTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + formalsOffset); } - const quint32 *localsTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + localsOffset); } - const quint32 *qmlIdObjectDependencyTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset); } - const quint32 *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset); } - const quint32 *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset); } + // Keep all unaligned data at the end + quint8 flags; + + const LEUInt32 *formalsTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + formalsOffset); } + const LEUInt32 *localsTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + localsOffset); } + const LEUInt32 *qmlIdObjectDependencyTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset); } + const LEUInt32 *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset); } + const LEUInt32 *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset); } + + // --- QQmlPropertyCacheCreator interface + const LEUInt32 *formalsBegin() const { return formalsTable(); } + const LEUInt32 *formalsEnd() const { return formalsTable() + nFormals; } + // --- inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; } @@ -207,15 +262,15 @@ struct Function // Qml data structures struct Q_QML_EXPORT TranslationData { - quint32 commentIndex; - int number; + LEUInt32 commentIndex; + LEInt32 number; }; struct Q_QML_PRIVATE_EXPORT Binding { - quint32 propertyNameIndex; + LEUInt32 propertyNameIndex; - enum ValueType { + enum ValueType : unsigned int { Type_Invalid, Type_Boolean, Type_Number, @@ -228,26 +283,30 @@ struct Q_QML_PRIVATE_EXPORT Binding Type_GroupProperty }; - enum Flags { + enum Flags : unsigned int { IsSignalHandlerExpression = 0x1, IsSignalHandlerObject = 0x2, IsOnAssignment = 0x4, InitializerForReadOnlyDeclaration = 0x8, IsResolvedEnum = 0x10, IsListItem = 0x20, - IsBindingToAlias = 0x40 + IsBindingToAlias = 0x40, + IsDeferredBinding = 0x80, + IsCustomParserBinding = 0x100, }; - quint32 flags : 16; - quint32 type : 16; + union { + QJsonPrivate::qle_bitfield<0, 16> flags; + QJsonPrivate::qle_bitfield<16, 16> type; + }; union { bool b; - double d; - quint32 compiledScriptIndex; // used when Type_Script - quint32 objectIndex; + quint64 doubleValue; // do not access directly, needs endian protected access + LEUInt32 compiledScriptIndex; // used when Type_Script + LEUInt32 objectIndex; TranslationData translationData; // used when Type_Translation } value; - quint32 stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings) + LEUInt32 stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings) Location location; Location valueLocation; @@ -307,11 +366,20 @@ struct Q_QML_PRIVATE_EXPORT Binding QString valueAsScriptString(const Unit *unit) const; double valueAsNumber() const { - if (type == Type_Number) - return value.d; - return 0.0; - + if (type != Type_Number) + return 0.0; + quint64 intval = qFromLittleEndian<quint64>(value.doubleValue); + double d; + memcpy(&d, &intval, sizeof(double)); + return d; } + void setNumberValueInternal(double d) + { + quint64 intval; + memcpy(&intval, &d, sizeof(double)); + value.doubleValue = qToLittleEndian<quint64>(intval); + } + bool valueAsBoolean() const { if (type == Type_Boolean) @@ -323,17 +391,16 @@ struct Q_QML_PRIVATE_EXPORT Binding struct Parameter { - quint32 nameIndex; - quint32 type; - quint32 customTypeNameIndex; - quint32 reserved; + LEUInt32 nameIndex; + LEUInt32 type; + LEUInt32 customTypeNameIndex; Location location; }; struct Signal { - quint32 nameIndex; - quint32 nParameters; + LEUInt32 nameIndex; + LEUInt32 nParameters; Location location; // Parameter parameters[1]; @@ -346,47 +413,95 @@ struct Signal + nParameters * sizeof(Parameter) + 7) & ~0x7; } + + // --- QQmlPropertyCacheCceatorInterface + const Parameter *parametersBegin() const { return parameterAt(0); } + const Parameter *parametersEnd() const { return parameterAt(nParameters); } + int parameterCount() const { return nParameters; } + // --- }; struct Property { - enum Type { Var = 0, Variant, Int, Bool, Real, String, Url, Color, + enum Type : unsigned int { Var = 0, Variant, Int, Bool, Real, String, Url, Color, Font, Time, Date, DateTime, Rect, Point, Size, Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, - Alias, Custom, CustomList }; + Custom, CustomList }; - enum Flags { + enum Flags : unsigned int { IsReadOnly = 0x1 }; - quint32 nameIndex; - quint32 type; + LEUInt32 nameIndex; + union { + QJsonPrivate::qle_bitfield<0, 31> type; + QJsonPrivate::qle_bitfield<31, 1> flags; // readonly + }; + LEUInt32 customTypeNameIndex; // If type >= Custom + Location location; +}; + +struct Alias { + enum Flags : unsigned int { + IsReadOnly = 0x1, + Resolved = 0x2, + AliasPointsToPointerObject = 0x4 + }; + union { + QJsonPrivate::qle_bitfield<0, 29> nameIndex; + QJsonPrivate::qle_bitfield<29, 3> flags; + }; union { - quint32 customTypeNameIndex; // If type >= Custom - quint32 aliasIdValueIndex; // If type == Alias + LEUInt32 idIndex; // string index + QJsonPrivate::qle_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues) + QJsonPrivate::qle_bitfield<31, 1> aliasToLocalAlias; + }; + union { + LEUInt32 propertyNameIndex; // string index + LEInt32 encodedMetaPropertyIndex; + LEUInt32 localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId) }; - quint32 aliasPropertyValueIndex; - quint32 flags; // readonly Location location; - Location aliasLocation; // If type == Alias + Location referenceLocation; + + bool isObjectAlias() const { + Q_ASSERT(flags & Resolved); + return encodedMetaPropertyIndex == -1; + } }; struct Object { + enum Flags : unsigned int { + NoFlag = 0x0, + IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary + HasDeferredBindings = 0x2, // any of the bindings are deferred + HasCustomParserBindings = 0x4 + }; + // Depending on the use, this may be the type name to instantiate before instantiating this // object. For grouped properties the type name will be empty and for attached properties // it will be the name of the attached type. - quint32 inheritedTypeNameIndex; - quint32 idIndex; - qint32 indexOfDefaultProperty; // -1 means no default property declared in this object - quint32 nFunctions; - quint32 offsetToFunctions; - quint32 nProperties; - quint32 offsetToProperties; - quint32 nSignals; - quint32 offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects - quint32 nBindings; - quint32 offsetToBindings; + LEUInt32 inheritedTypeNameIndex; + LEUInt32 idNameIndex; + union { + QJsonPrivate::qle_bitfield<0, 15> flags; + QJsonPrivate::qle_bitfield<15, 1> defaultPropertyIsAlias; + QJsonPrivate::qle_signedbitfield<16, 16> id; + }; + LEInt32 indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object + LEUInt32 nFunctions; + LEUInt32 offsetToFunctions; + LEUInt32 nProperties; + LEUInt32 offsetToProperties; + LEUInt32 nAliases; + LEUInt32 offsetToAliases; + LEUInt32 nSignals; + LEUInt32 offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects + LEUInt32 nBindings; + LEUInt32 offsetToBindings; + LEUInt32 nNamedObjectsInComponent; + LEUInt32 offsetToNamedObjectsInComponent; Location location; Location locationOfIdProperty; // Function[] @@ -394,20 +509,22 @@ struct Object // Signal[] // Binding[] - static int calculateSizeExcludingSignals(int nFunctions, int nProperties, int nSignals, int nBindings) + static int calculateSizeExcludingSignals(int nFunctions, int nProperties, int nAliases, int nSignals, int nBindings, int nNamedObjectsInComponent) { return ( sizeof(Object) + nFunctions * sizeof(quint32) + nProperties * sizeof(Property) + + nAliases * sizeof(Alias) + nSignals * sizeof(quint32) + nBindings * sizeof(Binding) + + nNamedObjectsInComponent * sizeof(int) + 0x7 ) & ~0x7; } - const quint32 *functionOffsetTable() const + const LEUInt32 *functionOffsetTable() const { - return reinterpret_cast<const quint32*>(reinterpret_cast<const char *>(this) + offsetToFunctions); + return reinterpret_cast<const LEUInt32*>(reinterpret_cast<const char *>(this) + offsetToFunctions); } const Property *propertyTable() const @@ -415,6 +532,11 @@ struct Object return reinterpret_cast<const Property*>(reinterpret_cast<const char *>(this) + offsetToProperties); } + const Alias *aliasTable() const + { + return reinterpret_cast<const Alias*>(reinterpret_cast<const char *>(this) + offsetToAliases); + } + const Binding *bindingTable() const { return reinterpret_cast<const Binding*>(reinterpret_cast<const char *>(this) + offsetToBindings); @@ -422,78 +544,111 @@ struct Object const Signal *signalAt(int idx) const { - const uint *offsetTable = reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToSignals); - const uint offset = offsetTable[idx]; + const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToSignals); + const LEUInt32 offset = offsetTable[idx]; return reinterpret_cast<const Signal*>(reinterpret_cast<const char*>(this) + offset); } + + const LEUInt32 *namedObjectsInComponentTable() const + { + return reinterpret_cast<const LEUInt32*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent); + } + + // --- QQmlPropertyCacheCreator interface + int propertyCount() const { return nProperties; } + int aliasCount() const { return nAliases; } + int signalCount() const { return nSignals; } + int functionCount() const { return nFunctions; } + + const Binding *bindingsBegin() const { return bindingTable(); } + const Binding *bindingsEnd() const { return bindingTable() + nBindings; } + + const Property *propertiesBegin() const { return propertyTable(); } + const Property *propertiesEnd() const { return propertyTable() + nProperties; } + + const Alias *aliasesBegin() const { return aliasTable(); } + const Alias *aliasesEnd() const { return aliasTable() + nAliases; } + + typedef TableIterator<Signal, Object, &Object::signalAt> SignalIterator; + SignalIterator signalsBegin() const { return SignalIterator(this, 0); } + SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); } + + int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; } + // --- }; struct Import { - enum ImportType { + enum ImportType : unsigned int { ImportLibrary = 0x1, ImportFile = 0x2, ImportScript = 0x3 }; - quint32 type; + quint8 type; - quint32 uriIndex; - quint32 qualifierIndex; + LEUInt32 uriIndex; + LEUInt32 qualifierIndex; - qint32 majorVersion; - qint32 minorVersion; + LEInt32 majorVersion; + LEInt32 minorVersion; Location location; - Import(): type(0), uriIndex(0), qualifierIndex(0), majorVersion(0), minorVersion(0) {} + Import() { type = 0; uriIndex = 0; qualifierIndex = 0; majorVersion = 0; minorVersion = 0; } }; static const char magic_str[] = "qv4cdata"; struct Unit { + // DO NOT CHANGE THESE FIELDS EVER char magic[8]; - qint16 architecture; - qint16 version; - quint32 unitSize; // Size of the Unit and any depending data. + LEUInt32 version; + LEUInt32 qtVersion; + LEInt64 sourceTimeStamp; + LEUInt32 unitSize; // Size of the Unit and any depending data. + // END DO NOT CHANGE THESE FIELDS EVER + + LEUInt32 architectureIndex; // string index to QSysInfo::buildAbi() + LEUInt32 codeGeneratorIndex; - enum { + enum : unsigned int { IsJavascript = 0x1, IsQml = 0x2, StaticData = 0x4, // Unit data persistent in memory? IsSingleton = 0x8, IsSharedLibrary = 0x10 // .pragma shared? }; - quint32 flags; - uint stringTableSize; - uint offsetToStringTable; - uint functionTableSize; - uint offsetToFunctionTable; - uint lookupTableSize; - uint offsetToLookupTable; - uint regexpTableSize; - uint offsetToRegexpTable; - uint constantTableSize; - uint offsetToConstantTable; - uint jsClassTableSize; - uint offsetToJSClassTable; - qint32 indexOfRootFunction; - quint32 sourceFileIndex; + LEUInt32 flags; + LEUInt32 stringTableSize; + LEUInt32 offsetToStringTable; + LEUInt32 functionTableSize; + LEUInt32 offsetToFunctionTable; + LEUInt32 lookupTableSize; + LEUInt32 offsetToLookupTable; + LEUInt32 regexpTableSize; + LEUInt32 offsetToRegexpTable; + LEUInt32 constantTableSize; + LEUInt32 offsetToConstantTable; + LEUInt32 jsClassTableSize; + LEUInt32 offsetToJSClassTable; + LEInt32 indexOfRootFunction; + LEUInt32 sourceFileIndex; /* QML specific fields */ - quint32 nImports; - quint32 offsetToImports; - quint32 nObjects; - quint32 offsetToObjects; - quint32 indexOfRootObject; + LEUInt32 nImports; + LEUInt32 offsetToImports; + LEUInt32 nObjects; + LEUInt32 offsetToObjects; + LEUInt32 indexOfRootObject; const Import *importAt(int idx) const { return reinterpret_cast<const Import*>((reinterpret_cast<const char *>(this)) + offsetToImports + idx * sizeof(Import)); } const Object *objectAt(int idx) const { - const uint *offsetTable = reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToObjects); - const uint offset = offsetTable[idx]; + const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToObjects); + const LEUInt32 offset = offsetTable[idx]; return reinterpret_cast<const Object*>(reinterpret_cast<const char*>(this) + offset); } @@ -503,22 +658,31 @@ struct Unit /* end QML specific fields*/ QString stringAt(int idx) const { - const uint *offsetTable = reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToStringTable); - const uint offset = offsetTable[idx]; + const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToStringTable); + const LEUInt32 offset = offsetTable[idx]; const String *str = reinterpret_cast<const String*>(reinterpret_cast<const char *>(this) + offset); if (str->size == 0) return QString(); +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN const QChar *characters = reinterpret_cast<const QChar *>(str + 1); if (flags & StaticData) return QString::fromRawData(characters, str->size); return QString(characters, str->size); +#else + const LEUInt16 *characters = reinterpret_cast<const LEUInt16 *>(str + 1); + QString qstr(str->size, Qt::Uninitialized); + QChar *ch = qstr.data(); + for (int i = 0; i < str->size; ++i) + ch[i] = QChar(characters[i]); + return qstr; +#endif } - const uint *functionOffsetTable() const { return reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); } + const LEUInt32 *functionOffsetTable() const { return reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); } const Function *functionAt(int idx) const { - const uint *offsetTable = functionOffsetTable(); - const uint offset = offsetTable[idx]; + const LEUInt32 *offsetTable = functionOffsetTable(); + const LEUInt32 offset = offsetTable[idx]; return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset); } @@ -531,22 +695,13 @@ struct Unit } const JSClassMember *jsClassAt(int idx, int *nMembers) const { - const uint *offsetTable = reinterpret_cast<const uint *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable); - const uint offset = offsetTable[idx]; + const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable); + const LEUInt32 offset = offsetTable[idx]; const char *ptr = reinterpret_cast<const char *>(this) + offset; const JSClass *klass = reinterpret_cast<const JSClass *>(ptr); *nMembers = klass->nMembers; return reinterpret_cast<const JSClassMember*>(ptr + sizeof(JSClass)); } - - static int calculateSize(uint nFunctions, uint nRegExps, uint nConstants, - uint nLookups, uint nClasses) { - return (sizeof(Unit) - + (nFunctions + nClasses) * sizeof(uint) - + nRegExps * RegExp::calculateSize() - + nConstants * sizeof(QV4::ReturnedValue) - + nLookups * Lookup::calculateSize() - + 7) & ~7; } }; #if defined(Q_CC_MSVC) || defined(Q_CC_GNU) @@ -574,7 +729,67 @@ struct TypeReferenceMap : QHash<int, TypeReference> return *it; return *insert(nameIndex, loc); } + + template <typename CompiledObject> + void collectFromObject(const CompiledObject *obj) + { + if (obj->inheritedTypeNameIndex != 0) { + TypeReference &r = this->add(obj->inheritedTypeNameIndex, obj->location); + r.needsCreation = true; + r.errorWhenNotFound = true; + } + + for (auto prop = obj->propertiesBegin(), propEnd = obj->propertiesEnd(); prop != propEnd; ++prop) { + if (prop->type >= QV4::CompiledData::Property::Custom) { + // ### FIXME: We could report the more accurate location here by using prop->location, but the old + // compiler can't and the tests expect it to be the object location right now. + TypeReference &r = this->add(prop->customTypeNameIndex, obj->location); + r.errorWhenNotFound = true; + } + } + + for (auto binding = obj->bindingsBegin(), bindingEnd = obj->bindingsEnd(); binding != bindingEnd; ++binding) { + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) + this->add(binding->propertyNameIndex, binding->location); + } + } + + template <typename Iterator> + void collectFromObjects(Iterator it, Iterator end) + { + for (; it != end; ++it) + collectFromObject(*it); + } +}; + +#ifndef V4_BOOTSTRAP +struct ResolvedTypeReference +{ + ResolvedTypeReference() + : type(0) + , majorVersion(0) + , minorVersion(0) + , isFullyDynamicType(false) + {} + + QQmlType *type; + QQmlRefPointer<QQmlPropertyCache> typePropertyCache; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + + int majorVersion; + int minorVersion; + // Types such as QQmlPropertyMap can add properties dynamically at run-time and + // therefore cannot have a property cache installed when instantiated. + bool isFullyDynamicType; + + QQmlPropertyCache *propertyCache() const; + QQmlPropertyCache *createPropertyCache(QQmlEngine *); + + void doDynamicTypeCheck(); }; +// map from name index +typedef QHash<int, ResolvedTypeReference*> ResolvedTypeReferenceMap; +#endif // index is per-object binding index typedef QVector<QQmlPropertyData*> BindingPropertyData; @@ -597,7 +812,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount virtual ~CompilationUnit(); #endif - Unit *data; + const Unit *data; // Called only when building QML, when we build the header for JS first and append QML data virtual QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument); @@ -614,20 +829,75 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount QVector<QV4::Function *> runtimeFunctions; mutable QQmlNullableValue<QUrl> m_url; + // QML specific fields + QQmlPropertyCacheVector propertyCaches; + QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(data->indexOfRootObject); } + + QQmlRefPointer<QQmlTypeNameCache> importCache; + // index is object index. This allows fast access to the // property data when initializing bindings, avoiding expensive // lookups by string (property name). QVector<BindingPropertyData> bindingPropertyDataPerObject; + // mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects + // this is initialized on-demand by QQmlContextData + QHash<int, IdentifierHash<int>> namedObjectsPerComponentCache; + IdentifierHash<int> namedObjectsPerComponent(int componentObjectIndex); + + void finalize(QQmlEnginePrivate *engine); + + int totalBindingsCount; // Number of bindings used in this type + int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses + int totalObjectCount; // Number of objects explicitly instantiated + + QVector<QQmlScriptData *> dependentScripts; + ResolvedTypeReferenceMap resolvedTypes; + + int metaTypeId; + int listMetaTypeId; + bool isRegisteredWithEngine; + + QScopedPointer<QIODevice> backingFile; + + // --- interface for QQmlPropertyCacheCreator + typedef Object CompiledObject; + int objectCount() const { return data->nObjects; } + int rootObjectIndex() const { return data->indexOfRootObject; } + const Object *objectAt(int index) const { return data->objectAt(index); } + QString stringAt(int index) const { return data->stringAt(index); } + + struct FunctionIterator + { + FunctionIterator(const Unit *unit, const Object *object, int index) : unit(unit), object(object), index(index) {} + const Unit *unit; + const Object *object; + int index; + + const Function *operator->() const { return unit->functionAt(object->functionOffsetTable()[index]); } + void operator++() { ++index; } + bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; } + bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; } + }; + FunctionIterator objectFunctionsBegin(const Object *object) const { return FunctionIterator(data, object, 0); } + FunctionIterator objectFunctionsEnd(const Object *object) const { return FunctionIterator(data, object, object->nFunctions); } + // --- + QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); void unlink(); - virtual QV4::ExecutableAllocator::ChunkOfPages *chunkForFunction(int /*functionIndex*/) { return 0; } - void markObjects(QV4::ExecutionEngine *e); + void destroy() Q_DECL_OVERRIDE; + + bool saveToDisk(QString *errorString); + bool loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString); + protected: virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0; + virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit); + virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString); + virtual bool memoryMapCode(QString *errorString); #endif // V4_BOOTSTRAP }; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 3943642146..768a4ffcd3 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -42,6 +42,7 @@ #include <qv4isel_p.h> #include <private/qv4string_p.h> #include <private/qv4value_p.h> +#include <private/qv4alloca_p.h> QV4::Compiler::StringTableGenerator::StringTableGenerator() { @@ -75,16 +76,21 @@ void QV4::Compiler::StringTableGenerator::clear() void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) { char *dataStart = reinterpret_cast<char *>(unit); - uint *stringTable = reinterpret_cast<uint *>(dataStart + unit->offsetToStringTable); + CompiledData::LEUInt32 *stringTable = reinterpret_cast<CompiledData::LEUInt32 *>(dataStart + unit->offsetToStringTable); char *stringData = dataStart + unit->offsetToStringTable + unit->stringTableSize * sizeof(uint); for (int i = 0; i < strings.size(); ++i) { stringTable[i] = stringData - dataStart; const QString &qstr = strings.at(i); - QV4::CompiledData::String *s = (QV4::CompiledData::String*)(stringData); - s->flags = 0; // ### + QV4::CompiledData::String *s = reinterpret_cast<QV4::CompiledData::String *>(stringData); s->size = qstr.length(); +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN memcpy(s + 1, qstr.constData(), qstr.length()*sizeof(ushort)); +#else + ushort *uc = reinterpret_cast<ushort *>(s + 1); + for (int i = 0; i < qstr.length(); ++i) + uc[i] = qToLittleEndian<ushort>(qstr.at(i).unicode()); +#endif stringData += QV4::CompiledData::String::calculateSize(qstr); } @@ -92,7 +98,6 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::IR::Module *module) : irModule(module) - , jsClassDataSize(0) { // Make sure the empty string always gets index 0 registerString(QString()); @@ -174,30 +179,32 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, IR::ExprList *arg { // ### re-use existing class definitions. - QList<CompiledData::JSClassMember> members; - members.reserve(count); + const int size = CompiledData::JSClass::calculateSize(count); + jsClassOffsets.append(jsClassData.size()); + const int oldSize = jsClassData.size(); + jsClassData.resize(jsClassData.size() + size); + memset(jsClassData.data() + oldSize, 0, size); - IR::ExprList *it = args; - for (int i = 0; i < count; ++i, it = it->next) { - CompiledData::JSClassMember member; + CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize); + jsClass->nMembers = count; + CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1); + IR::ExprList *it = args; + for (int i = 0; i < count; ++i, it = it->next, ++member) { QV4::IR::Name *name = it->expr->asName(); it = it->next; const bool isData = it->expr->asConst()->value; it = it->next; - member.nameOffset = registerString(*name->id); - member.isAccessor = !isData; - members << member; + member->nameOffset = registerString(*name->id); + member->isAccessor = !isData; if (!isData) it = it->next; } - jsClasses << members; - jsClassDataSize += CompiledData::JSClass::calculateSize(members.count()); - return jsClasses.size() - 1; + return jsClassOffsets.size() - 1; } QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option) @@ -211,94 +218,45 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO registerString(*f->locals.at(i)); } - int unitSize = QV4::CompiledData::Unit::calculateSize(irModule->functions.size(), regexps.size(), - constants.size(), lookups.size(), jsClasses.count()); - - uint functionDataSize = 0; - for (int i = 0; i < irModule->functions.size(); ++i) { - QV4::IR::Function *f = irModule->functions.at(i); - functionOffsets.insert(f, functionDataSize + unitSize); + CompiledData::LEUInt32 *functionOffsets = reinterpret_cast<CompiledData::LEUInt32*>(alloca(irModule->functions.size() * sizeof(CompiledData::LEUInt32))); + uint jsClassDataOffset = 0; - const int qmlIdDepsCount = f->idObjectDependencies.count(); - const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count(); - functionDataSize += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), qmlIdDepsCount, qmlPropertyDepsCount); + char *dataPtr; + CompiledData::Unit *unit; + { + QV4::CompiledData::Unit tempHeader = generateHeader(option, functionOffsets, &jsClassDataOffset); + dataPtr = reinterpret_cast<char *>(malloc(tempHeader.unitSize)); + memcpy(&unit, &dataPtr, sizeof(CompiledData::Unit*)); + memcpy(unit, &tempHeader, sizeof(tempHeader)); } - const int totalSize = unitSize + functionDataSize + jsClassDataSize + (option == GenerateWithStringTable ? stringTable.sizeOfTableAndData() : 0); - char *data = (char *)malloc(totalSize); - memset(data, 0, totalSize); - QV4::CompiledData::Unit *unit = (QV4::CompiledData::Unit*)data; - - memcpy(unit->magic, QV4::CompiledData::magic_str, sizeof(unit->magic)); - unit->architecture = 0; // ### - unit->flags = QV4::CompiledData::Unit::IsJavascript; - unit->version = 1; - unit->unitSize = totalSize; - unit->functionTableSize = irModule->functions.size(); - unit->offsetToFunctionTable = sizeof(*unit); - unit->lookupTableSize = lookups.count(); - unit->offsetToLookupTable = unit->offsetToFunctionTable + unit->functionTableSize * sizeof(uint); - unit->regexpTableSize = regexps.size(); - unit->offsetToRegexpTable = unit->offsetToLookupTable + unit->lookupTableSize * CompiledData::Lookup::calculateSize(); - unit->constantTableSize = constants.size(); - unit->offsetToConstantTable = unit->offsetToRegexpTable + unit->regexpTableSize * CompiledData::RegExp::calculateSize(); - unit->jsClassTableSize = jsClasses.count(); - unit->offsetToJSClassTable = unit->offsetToConstantTable + unit->constantTableSize * sizeof(ReturnedValue); - if (option == GenerateWithStringTable) { - unit->stringTableSize = stringTable.stringCount(); - unit->offsetToStringTable = unitSize + functionDataSize + jsClassDataSize; - } else { - unit->stringTableSize = 0; - unit->offsetToStringTable = 0; - } - unit->indexOfRootFunction = -1; - unit->sourceFileIndex = getStringId(irModule->fileName); - unit->nImports = 0; - unit->offsetToImports = 0; - unit->nObjects = 0; - unit->offsetToObjects = 0; - unit->indexOfRootObject = 0; - - uint *functionTable = (uint *)(data + unit->offsetToFunctionTable); - for (int i = 0; i < irModule->functions.size(); ++i) - functionTable[i] = functionOffsets.value(irModule->functions.at(i)); - - char *f = data + unitSize; + memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(CompiledData::LEUInt32)); + for (int i = 0; i < irModule->functions.size(); ++i) { QV4::IR::Function *function = irModule->functions.at(i); if (function == irModule->rootFunction) unit->indexOfRootFunction = i; - const int bytes = writeFunction(f, i, function); - f += bytes; + writeFunction(dataPtr + functionOffsets[i], function); } - CompiledData::Lookup *lookupsToWrite = (CompiledData::Lookup*)(data + unit->offsetToLookupTable); + CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable); foreach (const CompiledData::Lookup &l, lookups) *lookupsToWrite++ = l; - CompiledData::RegExp *regexpTable = (CompiledData::RegExp *)(data + unit->offsetToRegexpTable); + CompiledData::RegExp *regexpTable = reinterpret_cast<CompiledData::RegExp *>(dataPtr + unit->offsetToRegexpTable); memcpy(regexpTable, regexps.constData(), regexps.size() * sizeof(*regexpTable)); - ReturnedValue *constantTable = (ReturnedValue *)(data + unit->offsetToConstantTable); + ReturnedValue *constantTable = reinterpret_cast<ReturnedValue *>(dataPtr + unit->offsetToConstantTable); memcpy(constantTable, constants.constData(), constants.size() * sizeof(ReturnedValue)); - // write js classes and js class lookup table - uint *jsClassTable = (uint*)(data + unit->offsetToJSClassTable); - char *jsClass = data + unitSize + functionDataSize; - for (int i = 0; i < jsClasses.count(); ++i) { - jsClassTable[i] = jsClass - data; - - const QList<CompiledData::JSClassMember> members = jsClasses.at(i); - - CompiledData::JSClass *c = reinterpret_cast<CompiledData::JSClass*>(jsClass); - c->nMembers = members.count(); + { + memcpy(dataPtr + jsClassDataOffset, jsClassData.constData(), jsClassData.size()); - CompiledData::JSClassMember *memberToWrite = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + sizeof(CompiledData::JSClass)); - foreach (const CompiledData::JSClassMember &member, members) - *memberToWrite++ = member; - - jsClass += CompiledData::JSClass::calculateSize(members.count()); + // write js classes and js class lookup table + CompiledData::LEUInt32 *jsClassOffsetTable = reinterpret_cast<CompiledData::LEUInt32 *>(dataPtr + unit->offsetToJSClassTable); + for (int i = 0; i < jsClassOffsets.count(); ++i) + jsClassOffsetTable[i] = jsClassDataOffset + jsClassOffsets.at(i); } // write strings and string table @@ -308,13 +266,12 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO return unit; } -int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::Function *irFunction) +void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *irFunction) const { QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f; quint32 currentOffset = sizeof(QV4::CompiledData::Function); - function->index = index; function->nameIndex = getStringId(*irFunction->name); function->flags = 0; if (irFunction->hasDirectEval) @@ -336,8 +293,6 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::F currentOffset += function->nLocals * sizeof(quint32); function->nInnerFunctions = irFunction->nestedFunctions.size(); - function->innerFunctionsOffset = currentOffset; - currentOffset += function->nInnerFunctions * sizeof(quint32); function->nDependingIdObjects = 0; function->nDependingContextProperties = 0; @@ -364,6 +319,9 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::F function->location.line = irFunction->line; function->location.column = irFunction->column; + function->codeOffset = 0; + function->codeSize = 0; + // write formals quint32 *formals = (quint32 *)(f + function->formalsOffset); for (int i = 0; i < irFunction->formals.size(); ++i) @@ -374,15 +332,12 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::F for (int i = 0; i < irFunction->locals.size(); ++i) locals[i] = getStringId(*irFunction->locals.at(i)); - // write inner functions - quint32 *innerFunctions = (quint32 *)(f + function->innerFunctionsOffset); - for (int i = 0; i < irFunction->nestedFunctions.size(); ++i) - innerFunctions[i] = functionOffsets.value(irFunction->nestedFunctions.at(i)); - // write QML dependencies quint32 *writtenDeps = (quint32 *)(f + function->dependingIdObjectsOffset); - foreach (int id, irFunction->idObjectDependencies) - *writtenDeps++ = id; + for (int id : irFunction->idObjectDependencies) { + Q_ASSERT(id >= 0); + *writtenDeps++ = static_cast<quint32>(id); + } writtenDeps = (quint32 *)(f + function->dependingContextPropertiesOffset); for (auto property : irFunction->contextObjectPropertyDependencies) { @@ -395,7 +350,70 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::F *writtenDeps++ = property.key(); // property index *writtenDeps++ = property.value(); // notify index } +} + +QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset) +{ + CompiledData::Unit unit; + memcpy(unit.magic, CompiledData::magic_str, sizeof(unit.magic)); + unit.flags = QV4::CompiledData::Unit::IsJavascript; + unit.version = QV4_DATA_STRUCTURE_VERSION; + unit.qtVersion = QT_VERSION; + unit.architectureIndex = registerString(QSysInfo::buildAbi()); + unit.codeGeneratorIndex = registerString(codeGeneratorName); + + quint32 nextOffset = sizeof(CompiledData::Unit); + + unit.functionTableSize = irModule->functions.size(); + unit.offsetToFunctionTable = nextOffset; + nextOffset += unit.functionTableSize * sizeof(uint); + + unit.lookupTableSize = lookups.count(); + unit.offsetToLookupTable = nextOffset; + nextOffset += unit.lookupTableSize * sizeof(CompiledData::Lookup); + + unit.regexpTableSize = regexps.size(); + unit.offsetToRegexpTable = nextOffset; + nextOffset += unit.regexpTableSize * sizeof(CompiledData::RegExp); + + unit.constantTableSize = constants.size(); + unit.offsetToConstantTable = nextOffset; + nextOffset += unit.constantTableSize * sizeof(ReturnedValue); + + unit.jsClassTableSize = jsClassOffsets.count(); + unit.offsetToJSClassTable = nextOffset; + nextOffset += unit.jsClassTableSize * sizeof(uint); + + *jsClassDataOffset = nextOffset; + nextOffset += jsClassData.size(); + + for (int i = 0; i < irModule->functions.size(); ++i) { + QV4::IR::Function *f = irModule->functions.at(i); + functionOffsets[i] = nextOffset; + + const int qmlIdDepsCount = f->idObjectDependencies.count(); + const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count(); + nextOffset += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), qmlIdDepsCount, qmlPropertyDepsCount); + } + + if (option == GenerateWithStringTable) { + unit.stringTableSize = stringTable.stringCount(); + unit.offsetToStringTable = nextOffset; + nextOffset += stringTable.sizeOfTableAndData(); + } else { + unit.stringTableSize = 0; + unit.offsetToStringTable = 0; + } + unit.indexOfRootFunction = -1; + unit.sourceFileIndex = getStringId(irModule->fileName); + unit.sourceTimeStamp = irModule->sourceTimeStamp; + unit.nImports = 0; + unit.offsetToImports = 0; + unit.nObjects = 0; + unit.offsetToObjects = 0; + unit.indexOfRootObject = 0; - return CompiledData::Function::calculateSize(function->nFormals, function->nLocals, function->nInnerFunctions, - function->nDependingIdObjects, function->nDependingContextProperties + function->nDependingScopeProperties); + unit.unitSize = nextOffset; + + return unit; } diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 0321a83b4f..49b8664513 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -52,6 +52,7 @@ #include <QtCore/qstring.h> #include "qv4jsir_p.h" +#include <private/qjson_p.h> QT_BEGIN_NAMESPACE @@ -114,18 +115,20 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable); // Returns bytes written - int writeFunction(char *f, int index, IR::Function *irFunction); + void writeFunction(char *f, IR::Function *irFunction) const; StringTableGenerator stringTable; + QString codeGeneratorName; private: + CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset); + IR::Module *irModule; - QHash<IR::Function *, uint> functionOffsets; QList<CompiledData::Lookup> lookups; QVector<CompiledData::RegExp> regexps; QVector<ReturnedValue> constants; - QList<QList<CompiledData::JSClassMember> > jsClasses; - uint jsClassDataSize; + QByteArray jsClassData; + QVector<int> jsClassOffsets; }; } diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 90010ccf52..20b871c4e8 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -58,10 +58,17 @@ QT_BEGIN_NAMESPACE +#ifdef QT_NO_QML_DEBUGGER +#define MOTH_DEBUG_INSTR(F) +#else +#define MOTH_DEBUG_INSTR(F) \ + F(Line, line) \ + F(Debug, debug) +#endif + #define FOR_EACH_MOTH_INSTR(F) \ F(Ret, ret) \ - F(Line, line) \ - F(Debug, debug) \ + MOTH_DEBUG_INSTR(F) \ F(LoadRuntimeString, loadRuntimeString) \ F(LoadRegExp, loadRegExp) \ F(LoadClosure, loadClosure) \ @@ -162,7 +169,7 @@ QT_BEGIN_NAMESPACE #define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1) #ifdef MOTH_THREADED_INTERPRETER -# define MOTH_INSTR_HEADER void *code; +# define MOTH_INSTR_HEADER union { quint32 instructionType; void *code; }; #else # define MOTH_INSTR_HEADER quint32 instructionType; #endif @@ -174,6 +181,8 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace Moth { + // When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h + struct Param { // Params are looked up as follows: // Constant: 0 @@ -252,6 +261,8 @@ union Instr MOTH_INSTR_HEADER Param result; }; + +#ifndef QT_NO_QML_DEBUGGING struct instr_line { MOTH_INSTR_HEADER qint32 lineNumber; @@ -260,6 +271,8 @@ union Instr MOTH_INSTR_HEADER qint32 lineNumber; }; +#endif // QT_NO_QML_DEBUGGING + struct instr_loadRuntimeString { MOTH_INSTR_HEADER int stringId; @@ -672,7 +685,7 @@ union Instr }; struct instr_binop { MOTH_INSTR_HEADER - QV4::Runtime::BinaryOperation alu; + uint alu; // offset inside the runtime methods Param lhs; Param rhs; Param result; @@ -757,7 +770,7 @@ union Instr }; struct instr_binopContext { MOTH_INSTR_HEADER - QV4::Runtime::BinaryOperationContext alu; + uint alu; // offset inside the runtime methods Param lhs; Param rhs; Param result; diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index be10d50e9b..41790c04a9 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -46,6 +46,7 @@ #include <private/qv4regexpobject_p.h> #include <private/qv4compileddata_p.h> #include <private/qqmlengine_p.h> +#include <wtf/MathExtras.h> #undef USE_TYPE_INFO @@ -54,7 +55,7 @@ using namespace QV4::Moth; namespace { -inline QV4::Runtime::BinaryOperation aluOpFunction(IR::AluOp op) +inline uint aluOpFunction(IR::AluOp op) { switch (op) { case IR::OpInvalid: @@ -70,43 +71,43 @@ inline QV4::Runtime::BinaryOperation aluOpFunction(IR::AluOp op) case IR::OpCompl: return 0; case IR::OpBitAnd: - return QV4::Runtime::bitAnd; + return offsetof(QV4::Runtime, bitAnd); case IR::OpBitOr: - return QV4::Runtime::bitOr; + return offsetof(QV4::Runtime, bitOr); case IR::OpBitXor: - return QV4::Runtime::bitXor; + return offsetof(QV4::Runtime, bitXor); case IR::OpAdd: return 0; case IR::OpSub: - return QV4::Runtime::sub; + return offsetof(QV4::Runtime, sub); case IR::OpMul: - return QV4::Runtime::mul; + return offsetof(QV4::Runtime, mul); case IR::OpDiv: - return QV4::Runtime::div; + return offsetof(QV4::Runtime, div); case IR::OpMod: - return QV4::Runtime::mod; + return offsetof(QV4::Runtime, mod); case IR::OpLShift: - return QV4::Runtime::shl; + return offsetof(QV4::Runtime, shl); case IR::OpRShift: - return QV4::Runtime::shr; + return offsetof(QV4::Runtime, shr); case IR::OpURShift: - return QV4::Runtime::ushr; + return offsetof(QV4::Runtime, ushr); case IR::OpGt: - return QV4::Runtime::greaterThan; + return offsetof(QV4::Runtime, greaterThan); case IR::OpLt: - return QV4::Runtime::lessThan; + return offsetof(QV4::Runtime, lessThan); case IR::OpGe: - return QV4::Runtime::greaterEqual; + return offsetof(QV4::Runtime, greaterEqual); case IR::OpLe: - return QV4::Runtime::lessEqual; + return offsetof(QV4::Runtime, lessEqual); case IR::OpEqual: - return QV4::Runtime::equal; + return offsetof(QV4::Runtime, equal); case IR::OpNotEqual: - return QV4::Runtime::notEqual; + return offsetof(QV4::Runtime, notEqual); case IR::OpStrictEqual: - return QV4::Runtime::strictEqual; + return offsetof(QV4::Runtime, strictEqual); case IR::OpStrictNotEqual: - return QV4::Runtime::strictNotEqual; + return offsetof(QV4::Runtime, strictNotEqual); case IR::OpInstanceof: return 0; case IR::OpIn: @@ -250,7 +251,7 @@ protected: _unhandled.removeLast(); } - s->accept(this); + visit(s); } if (IR::Jump *jump = s->asJump()) { @@ -273,7 +274,7 @@ protected: moves.order(); QList<IR::Move *> newMoves = moves.insertMoves(_currentBasicBlock, _function, true); foreach (IR::Move *move, newMoves) - move->accept(this); + visit(move); } } @@ -312,8 +313,8 @@ protected: }; } // anonymous namespace -InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - : EvalInstructionSelection(execAllocator, module, jsGenerator) +InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) + : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory) , qmlEngine(qmlEngine) , _block(0) , _codeStart(0) @@ -411,6 +412,7 @@ void InstructionSelection::run(int functionIndex) if (s->location.startLine != currentLine) { blockNeedsDebugInstruction = false; currentLine = s->location.startLine; +#ifndef QT_NO_QML_DEBUGGER if (irModule->debugMode) { Instruction::Debug debug; debug.lineNumber = currentLine; @@ -420,10 +422,11 @@ void InstructionSelection::run(int functionIndex) line.lineNumber = currentLine; addInstruction(line); } +#endif } } - s->accept(this); + visit(s); } } @@ -1040,11 +1043,11 @@ Param InstructionSelection::binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR if (oper == IR::OpInstanceof || oper == IR::OpIn || oper == IR::OpAdd) { Instruction::BinopContext binop; if (oper == IR::OpInstanceof) - binop.alu = QV4::Runtime::instanceof; + binop.alu = offsetof(QV4::Runtime, instanceof); else if (oper == IR::OpIn) - binop.alu = QV4::Runtime::in; + binop.alu = offsetof(QV4::Runtime, in); else - binop.alu = QV4::Runtime::add; + binop.alu = offsetof(QV4::Runtime, add); binop.lhs = getParam(leftSource); binop.rhs = getParam(rightSource); binop.result = getResultParam(target); @@ -1091,6 +1094,17 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint } } +void InstructionSelection::addDebugInstruction() +{ +#ifndef QT_NO_QML_DEBUGGER + if (blockNeedsDebugInstruction) { + Instruction::Debug debug; + debug.lineNumber = -int(currentLine); + addInstruction(debug); + } +#endif +} + void InstructionSelection::visitJump(IR::Jump *s) { if (s->target == _nextBlock) @@ -1098,11 +1112,7 @@ void InstructionSelection::visitJump(IR::Jump *s) if (_removableJumps.contains(s)) return; - if (blockNeedsDebugInstruction) { - Instruction::Debug debug; - debug.lineNumber = -int(currentLine); - addInstruction(debug); - } + addDebugInstruction(); Instruction::Jump jump; jump.offset = 0; @@ -1113,11 +1123,7 @@ void InstructionSelection::visitJump(IR::Jump *s) void InstructionSelection::visitCJump(IR::CJump *s) { - if (blockNeedsDebugInstruction) { - Instruction::Debug debug; - debug.lineNumber = -int(currentLine); - addInstruction(debug); - } + addDebugInstruction(); Param condition; if (IR::Temp *t = s->cond->asTemp()) { @@ -1152,12 +1158,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { - if (blockNeedsDebugInstruction) { - // this is required so stepOut will always be guaranteed to stop in every stack frame - Instruction::Debug debug; - debug.lineNumber = -int(currentLine); - addInstruction(debug); - } + // this is required so stepOut will always be guaranteed to stop in every stack frame + addDebugInstruction(); Instruction::Ret ret; ret.result = getParam(s->expr); @@ -1493,12 +1495,7 @@ void QV4::Moth::InstructionSelection::callBuiltinConvertThisToObject() ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) { - -#ifdef MOTH_THREADED_INTERPRETER - instr.common.code = VME::instructionJumpTable()[static_cast<int>(type)]; -#else instr.common.instructionType = type; -#endif int instructionSize = Instr::size(type); if (_codeEnd - _codeNext < instructionSize) { @@ -1584,6 +1581,29 @@ CompilationUnit::~CompilationUnit() void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) { +#ifdef MOTH_THREADED_INTERPRETER + // link byte code against addresses of instructions + for (int i = 0; i < codeRefs.count(); ++i) { + QByteArray &codeRef = codeRefs[i]; + char *code = codeRef.data(); + int index = 0; + while (index < codeRef.size()) { + Instr *genericInstr = reinterpret_cast<Instr *>(code + index); + + switch (genericInstr->common.instructionType) { +#define LINK_INSTRUCTION(InstructionType, Member) \ + case Instr::InstructionType: \ + genericInstr->common.code = VME::instructionJumpTable()[static_cast<int>(genericInstr->common.instructionType)]; \ + index += InstrMeta<(int)Instr::InstructionType>::Size; \ + break; + + FOR_EACH_MOTH_INSTR(LINK_INSTRUCTION) + + } + } + } +#endif + runtimeFunctions.resize(data->functionTableSize); runtimeFunctions.fill(0); for (int i = 0 ;i < runtimeFunctions.size(); ++i) { @@ -1594,3 +1614,81 @@ void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) runtimeFunctions[i] = runtimeFunction; } } + +void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) +{ + const int codeAlignment = 16; + quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize); + Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); + for (int i = 0; i < codeRefs.size(); ++i) { + CompiledData::Function *compiledFunction = const_cast<CompiledData::Function *>(unit->functionAt(i)); + compiledFunction->codeOffset = offset; + compiledFunction->codeSize = codeRefs.at(i).size(); + offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize); + } +} + +bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) +{ + Q_ASSERT(device->pos() == unit->unitSize); + Q_ASSERT(device->atEnd()); + Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); + + QByteArray padding; + + for (int i = 0; i < codeRefs.size(); ++i) { + const CompiledData::Function *compiledFunction = unit->functionAt(i); + + if (device->pos() > qint64(compiledFunction->codeOffset)) { + *errorString = QStringLiteral("Invalid state of cache file to write."); + return false; + } + + const quint64 paddingSize = compiledFunction->codeOffset - device->pos(); + padding.fill(0, paddingSize); + qint64 written = device->write(padding); + if (written != padding.size()) { + *errorString = device->errorString(); + return false; + } + + const void *codePtr = codeRefs.at(i).constData(); + written = device->write(reinterpret_cast<const char *>(codePtr), compiledFunction->codeSize); + if (written != qint64(compiledFunction->codeSize)) { + *errorString = device->errorString(); + return false; + } + } + return true; +} + +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + Q_UNUSED(errorString); + Q_ASSERT(codeRefs.isEmpty()); + codeRefs.reserve(data->functionTableSize); + + const char *basePtr = reinterpret_cast<const char *>(data); + + for (uint i = 0; i < data->functionTableSize; ++i) { + const CompiledData::Function *compiledFunction = data->functionAt(i); + const char *codePtr = const_cast<const char *>(reinterpret_cast<const char *>(basePtr + compiledFunction->codeOffset)); +#ifdef MOTH_THREADED_INTERPRETER + // for the threaded interpreter we need to make a copy of the data because it needs to be + // modified for the instruction handler addresses. + QByteArray code(codePtr, compiledFunction->codeSize); +#else + QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize); +#endif + codeRefs.append(code); + } + + return true; +} + +QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading() +{ + QQmlRefPointer<CompiledData::CompilationUnit> result; + result.adopt(new Moth::CompilationUnit); + return result; +} diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index 29d117af38..2d2bb91228 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -66,7 +66,10 @@ namespace Moth { struct CompilationUnit : public QV4::CompiledData::CompilationUnit { virtual ~CompilationUnit(); - virtual void linkBackendToEngine(QV4::ExecutionEngine *engine); + void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; + void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; + bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; + bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; QVector<QByteArray> codeRefs; @@ -77,7 +80,7 @@ class Q_QML_EXPORT InstructionSelection: public EvalInstructionSelection { public: - InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); + InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory); ~InstructionSelection(); virtual void run(int functionIndex); @@ -174,6 +177,8 @@ private: template <int Instr> inline ptrdiff_t addInstruction(const InstrData<Instr> &data); + inline void addDebugInstruction(); + ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); void patchJumpAddresses(); QByteArray squeezeCode() const; @@ -202,11 +207,14 @@ private: class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: + ISelFactory() : EvalISelFactory(QStringLiteral("moth")) {} virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); } - virtual bool jitCompileRegexps() const + EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL + { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); } + bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL { return false; } + QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE; + }; template<int InstrT> diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 0ae08160ab..d97eec5e1d 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -52,7 +52,7 @@ using namespace QV4; using namespace QV4::IR; -EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) +EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) : useFastLookups(true) , useTypeInference(true) , executableAllocator(execAllocator) @@ -67,6 +67,7 @@ EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *exe Q_ASSERT(execAllocator); #endif Q_ASSERT(module); + jsGenerator->codeGeneratorName = iselFactory->codeGeneratorName; } EvalInstructionSelection::~EvalInstructionSelection() diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index 88d2071c52..a3fa80b6f0 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -65,13 +65,14 @@ class QQmlEnginePrivate; namespace QV4 { +class EvalISelFactory; class ExecutableAllocator; struct Function; class Q_QML_PRIVATE_EXPORT EvalInstructionSelection { public: - EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); + EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory); virtual ~EvalInstructionSelection() = 0; QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile(bool generateUnitData = true); @@ -104,23 +105,44 @@ protected: class Q_QML_PRIVATE_EXPORT EvalISelFactory { public: + EvalISelFactory(const QString &codeGeneratorName) : codeGeneratorName(codeGeneratorName) {} virtual ~EvalISelFactory() = 0; virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0; virtual bool jitCompileRegexps() const = 0; + virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() = 0; + + const QString codeGeneratorName; }; namespace IR { -class Q_QML_PRIVATE_EXPORT IRDecoder: protected IR::StmtVisitor +class Q_QML_PRIVATE_EXPORT IRDecoder { public: IRDecoder() : _function(0) {} virtual ~IRDecoder() = 0; - virtual void visitPhi(IR::Phi *) {} - -public: // visitor methods for StmtVisitor: - virtual void visitMove(IR::Move *s); - virtual void visitExp(IR::Exp *s); + void visit(Stmt *s) + { + if (auto e = s->asExp()) { + visitExp(e); + } else if (auto m = s->asMove()) { + visitMove(m); + } else if (auto j = s->asJump()) { + visitJump(j); + } else if (auto c = s->asCJump()) { + visitCJump(c); + } else if (auto r = s->asRet()) { + visitRet(r); + } else if (auto p = s->asPhi()) { + visitPhi(p); + } else { + Q_UNREACHABLE(); + } + } + +private: // visitor methods for StmtVisitor: + void visitMove(IR::Move *s); + void visitExp(IR::Exp *s); public: // to implement by subclasses: virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) = 0; @@ -178,6 +200,11 @@ public: // to implement by subclasses: virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) = 0; protected: + virtual void visitJump(IR::Jump *) = 0; + virtual void visitCJump(IR::CJump *) = 0; + virtual void visitRet(IR::Ret *) = 0; + virtual void visitPhi(IR::Phi *) {} + virtual void callBuiltin(IR::Call *c, IR::Expr *result); IR::Function *_function; // subclass needs to set diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h index 674fc01623..1755193d32 100644 --- a/src/qml/compiler/qv4isel_util_p.h +++ b/src/qml/compiler/qv4isel_util_p.h @@ -104,7 +104,7 @@ inline Primitive convertToValue(IR::Const *c) return Primitive::undefinedValue(); } -class ConvertTemps: protected IR::StmtVisitor, protected IR::ExprVisitor +class ConvertTemps { void renumber(IR::Temp *t) { @@ -132,7 +132,7 @@ protected: virtual void process(IR::Stmt *s) { - s->accept(this); + visit(s); } public: @@ -157,34 +157,28 @@ public: } protected: - virtual void visitConst(IR::Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(IR::Name *) {} - virtual void visitTemp(IR::Temp *e) { renumber(e); } - virtual void visitArgLocal(IR::ArgLocal *) {} - virtual void visitClosure(IR::Closure *) {} - virtual void visitConvert(IR::Convert *e) { e->expr->accept(this); } - virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); } - virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitCall(IR::Call *e) { - e->base->accept(this); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); + void visit(IR::Stmt *s) { + switch (s->stmtKind) { + case IR::Stmt::PhiStmt: + visitPhi(s->asPhi()); + break; + default: + STMT_VISIT_ALL_KINDS(s); + break; + } } - virtual void visitNew(IR::New *e) { - e->base->accept(this); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); + + virtual void visitPhi(IR::Phi *) + { Q_UNREACHABLE(); } + +private: + void visit(IR::Expr *e) { + if (auto temp = e->asTemp()) { + renumber(temp); + } else { + EXPR_VISIT_ALL_KINDS(e); + } } - virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(IR::Member *e) { e->base->accept(this); } - virtual void visitExp(IR::Exp *s) { s->expr->accept(this); } - virtual void visitMove(IR::Move *s) { s->target->accept(this); s->source->accept(this); } - virtual void visitJump(IR::Jump *) {} - virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); } - virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } - virtual void visitPhi(IR::Phi *) { Q_UNREACHABLE(); } }; } // namespace QV4 diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index b28db59190..b6c5226894 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -157,12 +157,13 @@ AluOp binaryOperator(int op) } } -struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor +class RemoveSharedExpressions { CloneExpr clone; std::vector<Expr *> subexpressions; // contains all the non-cloned subexpressions in the given function. sorted using std::lower_bound. Expr *uniqueExpr; +public: RemoveSharedExpressions(): uniqueExpr(0) {} void operator()(IR::Function *function) @@ -176,11 +177,12 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor clone.setBasicBlock(block); for (Stmt *s : block->statements()) { - s->accept(this); + visit(s); } } } +private: template <typename Expr_> Expr_ *cleanup(Expr_ *expr) { @@ -189,7 +191,7 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor subexpressions.insert(it, expr); IR::Expr *e = expr; qSwap(uniqueExpr, e); - expr->accept(this); + visit(expr); qSwap(uniqueExpr, e); return static_cast<Expr_ *>(e); } @@ -199,83 +201,45 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor return clone(expr); } - // statements - virtual void visitExp(Exp *s) + void visit(Stmt *s) { - s->expr = cleanup(s->expr); - } - - virtual void visitMove(Move *s) - { - s->target = cleanup(s->target); - s->source = cleanup(s->source); - } - - virtual void visitJump(Jump *) - { - // nothing to do for Jump statements - } - - virtual void visitCJump(CJump *s) - { - s->cond = cleanup(s->cond); - } - - virtual void visitRet(Ret *s) - { - s->expr = cleanup(s->expr); - } - - virtual void visitPhi(IR::Phi *) { Q_UNIMPLEMENTED(); } - - // expressions - virtual void visitConst(Const *) {} - virtual void visitString(String *) {} - virtual void visitRegExp(RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *) {} - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - - virtual void visitConvert(Convert *e) - { - e->expr = cleanup(e->expr); - } - - virtual void visitUnop(Unop *e) - { - e->expr = cleanup(e->expr); - } - - virtual void visitBinop(Binop *e) - { - e->left = cleanup(e->left); - e->right = cleanup(e->right); - } - - virtual void visitCall(Call *e) - { - e->base = cleanup(e->base); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr = cleanup(it->expr); - } - - virtual void visitNew(New *e) - { - e->base = cleanup(e->base); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr = cleanup(it->expr); - } - - virtual void visitSubscript(Subscript *e) - { - e->base = cleanup(e->base); - e->index = cleanup(e->index); + if (auto e = s->asExp()) { + e->expr = cleanup(e->expr); + } else if (auto m = s->asMove()) { + m->target = cleanup(m->target); + m->source = cleanup(m->source); + } else if (auto c = s->asCJump()) { + c->cond = cleanup(c->cond); + } else if (auto r = s->asRet()) { + r->expr = cleanup(r->expr); + } } - virtual void visitMember(Member *e) + void visit(Expr *e) { - e->base = cleanup(e->base); + if (auto c = e->asConvert()) { + c->expr = cleanup(c->expr); + } else if (auto u = e->asUnop()) { + u->expr = cleanup(u->expr); + } else if (auto b = e->asBinop()) { + b->left = cleanup(b->left); + b->right = cleanup(b->right); + } else if (auto c = e->asCall()) { + c->base = cleanup(c->base); + for (IR::ExprList *it = c->args; it; it = it->next) { + it->expr = cleanup(it->expr); + } + } else if (auto n = e->asNew()) { + n->base = cleanup(n->base); + for (IR::ExprList *it = n->args; it; it = it->next) { + it->expr = cleanup(it->expr); + } + } else if (auto s = e->asSubscript()) { + s->base = cleanup(s->base); + s->index = cleanup(s->index); + } else if (auto m = e->asMember()) { + m->base = cleanup(m->base); + } } }; @@ -404,9 +368,10 @@ Function::Function(Module *module, Function *outer, const QString &name) , isNamedExpression(false) , hasTry(false) , hasWith(false) + , isQmlBinding(false) , unused(0) - , line(-1) - , column(-1) + , line(0) + , column(0) , _allBasicBlocks(0) , _statementCount(0) { @@ -548,75 +513,39 @@ ExprList *CloneExpr::clone(ExprList *list) return clonedList; } -void CloneExpr::visitConst(Const *e) -{ - cloned = cloneConst(e, block->function); -} - -void CloneExpr::visitString(String *e) -{ - cloned = block->STRING(e->value); -} - -void CloneExpr::visitRegExp(RegExp *e) -{ - cloned = block->REGEXP(e->value, e->flags); -} - -void CloneExpr::visitName(Name *e) -{ - cloned = cloneName(e, block->function); -} - -void CloneExpr::visitTemp(Temp *e) -{ - cloned = cloneTemp(e, block->function); -} - -void CloneExpr::visitArgLocal(ArgLocal *e) -{ - cloned = cloneArgLocal(e, block->function); -} - -void CloneExpr::visitClosure(Closure *e) -{ - cloned = block->CLOSURE(e->value); -} - -void CloneExpr::visitConvert(Convert *e) -{ - cloned = block->CONVERT(clone(e->expr), e->type); -} - -void CloneExpr::visitUnop(Unop *e) -{ - cloned = block->UNOP(e->op, clone(e->expr)); -} - -void CloneExpr::visitBinop(Binop *e) -{ - cloned = block->BINOP(e->op, clone(e->left), clone(e->right)); -} - -void CloneExpr::visitCall(Call *e) -{ - cloned = block->CALL(clone(e->base), clone(e->args)); -} - -void CloneExpr::visitNew(New *e) -{ - cloned = block->NEW(clone(e->base), clone(e->args)); -} - -void CloneExpr::visitSubscript(Subscript *e) -{ - cloned = block->SUBSCRIPT(clone(e->base), clone(e->index)); -} - -void CloneExpr::visitMember(Member *e) -{ - Expr *clonedBase = clone(e->base); - cloned = block->MEMBER(clonedBase, e->name, e->property, e->kind, e->idIndex); +void CloneExpr::visit(Expr *e) +{ + if (auto c = e->asConst()) { + cloned = cloneConst(c, block->function); + } else if (auto s = e->asString()) { + cloned = block->STRING(s->value); + } else if (auto r = e->asRegExp()) { + cloned = block->REGEXP(r->value, r->flags); + } else if (auto n = e->asName()) { + cloned = cloneName(n, block->function); + } else if (auto t = e->asTemp()) { + cloned = cloneTemp(t, block->function); + } else if (auto a = e->asArgLocal()) { + cloned = cloneArgLocal(a, block->function); + } else if (auto c = e->asClosure()) { + cloned = block->CLOSURE(c->value); + } else if (auto c = e->asConvert()) { + cloned = block->CONVERT(clone(c->expr), c->type); + } else if (auto u = e->asUnop()) { + cloned = block->UNOP(u->op, clone(u->expr)); + } else if (auto b = e->asBinop()) { + cloned = block->BINOP(b->op, clone(b->left), clone(b->right)); + } else if (auto c = e->asCall()) { + cloned = block->CALL(clone(c->base), clone(c->args)); + } else if (auto n = e->asNew()) { + cloned = block->NEW(clone(n->base), clone(n->args)); + } else if (auto s = e->asSubscript()) { + cloned = block->SUBSCRIPT(clone(s->base), clone(s->index)); + } else if (auto m = e->asMember()) { + cloned = block->MEMBER(clone(m->base), m->name, m->property, m->kind, m->idIndex); + } else { + Q_UNREACHABLE(); + } } IRPrinter::IRPrinter(QTextStream *out) @@ -632,17 +561,17 @@ IRPrinter::~IRPrinter() void IRPrinter::print(Stmt *s) { - s->accept(this); + visit(s); } void IRPrinter::print(const Expr &e) { - const_cast<Expr *>(&e)->accept(this); + visit(const_cast<Expr *>(&e)); } void IRPrinter::print(Expr *e) { - e->accept(this); + visit(e); } void IRPrinter::print(Function *f) @@ -696,7 +625,7 @@ void IRPrinter::print(BasicBlock *bb) QTextStream *prevOut = &os; std::swap(out, prevOut); addStmtNr(s); - s->accept(this); + visit(s); if (s->location.startLine) { out->flush(); for (int i = 58 - str.length(); i > 0; --i) @@ -713,10 +642,29 @@ void IRPrinter::print(BasicBlock *bb) std::swap(currentBB, bb); } +void IRPrinter::visit(Stmt *s) +{ + if (auto e = s->asExp()) { + visitExp(e); + } else if (auto m = s->asMove()) { + visitMove(m); + } else if (auto j = s->asJump()) { + visitJump(j); + } else if (auto c = s->asCJump()) { + visitCJump(c); + } else if (auto r = s->asRet()) { + visitRet(r); + } else if (auto p = s->asPhi()) { + visitPhi(p); + } else { + Q_UNREACHABLE(); + } +} + void IRPrinter::visitExp(Exp *s) { *out << "void "; - s->expr->accept(this); + visit(s->expr); } void IRPrinter::visitMove(Move *s) @@ -725,13 +673,13 @@ void IRPrinter::visitMove(Move *s) if (!s->swap && targetTemp->type != UnknownType) *out << typeName(targetTemp->type) << ' '; - s->target->accept(this); + visit(s->target); *out << ' '; if (s->swap) *out << "<=> "; else *out << "= "; - s->source->accept(this); + visit(s->source); } void IRPrinter::visitJump(Jump *s) @@ -742,7 +690,7 @@ void IRPrinter::visitJump(Jump *s) void IRPrinter::visitCJump(CJump *s) { *out << "if "; - s->cond->accept(this); + visit(s->cond); *out << " goto L" << s->iftrue->index() << " else goto L" << s->iffalse->index(); } @@ -752,7 +700,7 @@ void IRPrinter::visitRet(Ret *s) *out << "return"; if (s->expr) { *out << ' '; - s->expr->accept(this); + visit(s->expr); } } @@ -761,7 +709,7 @@ void IRPrinter::visitPhi(Phi *s) if (s->targetTemp->type != UnknownType) *out << typeName(s->targetTemp->type) << ' '; - s->targetTemp->accept(this); + visit(s->targetTemp); *out << " = phi "; for (int i = 0, ei = s->incoming.size(); i < ei; ++i) { if (i > 0) @@ -769,7 +717,42 @@ void IRPrinter::visitPhi(Phi *s) if (currentBB) *out << 'L' << currentBB->in.at(i)->index() << ": "; if (s->incoming[i]) - s->incoming[i]->accept(this); + visit(s->incoming[i]); + } +} + +void IRPrinter::visit(Expr *e) +{ + if (auto c = e->asConst()) { + visitConst(c); + } else if (auto s = e->asString()) { + visitString(s); + } else if (auto r = e->asRegExp()) { + visitRegExp(r); + } else if (auto n = e->asName()) { + visitName(n); + } else if (auto t = e->asTemp()) { + visitTemp(t); + } else if (auto a = e->asArgLocal()) { + visitArgLocal(a); + } else if (auto c = e->asClosure()) { + visitClosure(c); + } else if (auto c = e->asConvert()) { + visitConvert(c); + } else if (auto u = e->asUnop()) { + visitUnop(u); + } else if (auto b = e->asBinop()) { + visitBinop(b); + } else if (auto c = e->asCall()) { + visitCall(c); + } else if (auto n = e->asNew()) { + visitNew(n); + } else if (auto s = e->asSubscript()) { + visitSubscript(s); + } else if (auto m = e->asMember()) { + visitMember(m); + } else { + Q_UNREACHABLE(); } } @@ -867,32 +850,32 @@ void IRPrinter::visitClosure(Closure *e) void IRPrinter::visitConvert(Convert *e) { *out << "convert " << typeName(e->expr->type) << " to " << typeName(e->type) << ' '; - e->expr->accept(this); + visit(e->expr); } void IRPrinter::visitUnop(Unop *e) { *out << opname(e->op) << ' '; - e->expr->accept(this); + visit(e->expr); } void IRPrinter::visitBinop(Binop *e) { *out << opname(e->op) << ' '; - e->left->accept(this); + visit(e->left); *out << ", "; - e->right->accept(this); + visit(e->right); } void IRPrinter::visitCall(Call *e) { *out << "call "; - e->base->accept(this); + visit(e->base); *out << '('; for (ExprList *it = e->args; it; it = it->next) { if (it != e->args) *out << ", "; - it->expr->accept(this); + visit(it->expr); } *out << ')'; } @@ -900,21 +883,21 @@ void IRPrinter::visitCall(Call *e) void IRPrinter::visitNew(New *e) { *out << "new "; - e->base->accept(this); + visit(e->base); *out << '('; for (ExprList *it = e->args; it; it = it->next) { if (it != e->args) *out << ", "; - it->expr->accept(this); + visit(it->expr); } *out << ')'; } void IRPrinter::visitSubscript(Subscript *e) { - e->base->accept(this); + visit(e->base); *out << '['; - e->index->accept(this); + visit(e->index); *out << ']'; } @@ -924,7 +907,7 @@ void IRPrinter::visitMember(Member *e) && e->attachedPropertiesId != 0 && !e->base->asTemp()) *out << "[[attached property from " << e->attachedPropertiesId << "]]"; else - e->base->accept(this); + visit(e->base); *out << '.' << *e->name; #ifndef V4_BOOTSTRAP if (e->property) @@ -942,15 +925,15 @@ QString IRPrinter::escape(const QString &s) for (int i = 0; i < s.length(); ++i) { const QChar ch = s.at(i); if (ch == QLatin1Char('\n')) - r += QStringLiteral("\\n"); + r += QLatin1String("\\n"); else if (ch == QLatin1Char('\r')) - r += QStringLiteral("\\r"); + r += QLatin1String("\\r"); else if (ch == QLatin1Char('\\')) - r += QStringLiteral("\\\\"); + r += QLatin1String("\\\\"); else if (ch == QLatin1Char('"')) - r += QStringLiteral("\\\""); + r += QLatin1String("\\\""); else if (ch == QLatin1Char('\'')) - r += QStringLiteral("\\'"); + r += QLatin1String("\\'"); else r += ch; } diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 94fa65cf71..51b8797862 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -192,7 +192,7 @@ enum AluOp { AluOp binaryOperator(int op); const char *opname(IR::AluOp op); -enum Type { +enum Type : quint16 { UnknownType = 0, MissingType = 1 << 0, @@ -217,34 +217,6 @@ inline bool strictlyEqualTypes(Type t1, Type t2) QString typeName(Type t); -struct ExprVisitor { - virtual ~ExprVisitor() {} - virtual void visitConst(Const *) = 0; - virtual void visitString(String *) = 0; - virtual void visitRegExp(RegExp *) = 0; - virtual void visitName(Name *) = 0; - virtual void visitTemp(Temp *) = 0; - virtual void visitArgLocal(ArgLocal *) = 0; - virtual void visitClosure(Closure *) = 0; - virtual void visitConvert(Convert *) = 0; - virtual void visitUnop(Unop *) = 0; - virtual void visitBinop(Binop *) = 0; - virtual void visitCall(Call *) = 0; - virtual void visitNew(New *) = 0; - virtual void visitSubscript(Subscript *) = 0; - virtual void visitMember(Member *) = 0; -}; - -struct StmtVisitor { - virtual ~StmtVisitor() {} - virtual void visitExp(Exp *) = 0; - virtual void visitMove(Move *) = 0; - virtual void visitJump(Jump *) = 0; - virtual void visitCJump(CJump *) = 0; - virtual void visitRet(Ret *) = 0; - virtual void visitPhi(Phi *) = 0; -}; - struct MemberExpressionResolver; struct DiscoveredType { @@ -288,28 +260,129 @@ struct MemberExpressionResolver }; struct Q_AUTOTEST_EXPORT Expr { + enum ExprKind : quint8 { + NameExpr, + TempExpr, + ArgLocalExpr, + SubscriptExpr, + MemberExpr, + + LastLValue = MemberExpr, + + ConstExpr, + StringExpr, + RegExpExpr, + ClosureExpr, + ConvertExpr, + UnopExpr, + BinopExpr, + CallExpr, + NewExpr + }; + Type type; + const ExprKind exprKind; + + Expr &operator=(const Expr &other) { + Q_ASSERT(exprKind == other.exprKind); + type = other.type; + return *this; + } + + template <typename To> + inline bool isa() const { + return To::classof(this); + } - Expr(): type(UnknownType) {} - virtual ~Expr() {} - virtual void accept(ExprVisitor *) = 0; - virtual bool isLValue() { return false; } - virtual Const *asConst() { return 0; } - virtual String *asString() { return 0; } - virtual RegExp *asRegExp() { return 0; } - virtual Name *asName() { return 0; } - virtual Temp *asTemp() { return 0; } - virtual ArgLocal *asArgLocal() { return 0; } - virtual Closure *asClosure() { return 0; } - virtual Convert *asConvert() { return 0; } - virtual Unop *asUnop() { return 0; } - virtual Binop *asBinop() { return 0; } - virtual Call *asCall() { return 0; } - virtual New *asNew() { return 0; } - virtual Subscript *asSubscript() { return 0; } - virtual Member *asMember() { return 0; } + template <typename To> + inline To *as() { + if (isa<To>()) { + return static_cast<To *>(this); + } else { + return nullptr; + } + } + + template <typename To> + inline const To *as() const { + if (isa<To>()) { + return static_cast<const To *>(this); + } else { + return nullptr; + } + } + + Expr(ExprKind exprKind): type(UnknownType), exprKind(exprKind) {} + bool isLValue() const; + + Const *asConst() { return as<Const>(); } + String *asString() { return as<String>(); } + RegExp *asRegExp() { return as<RegExp>(); } + Name *asName() { return as<Name>(); } + Temp *asTemp() { return as<Temp>(); } + ArgLocal *asArgLocal() { return as<ArgLocal>(); } + Closure *asClosure() { return as<Closure>(); } + Convert *asConvert() { return as<Convert>(); } + Unop *asUnop() { return as<Unop>(); } + Binop *asBinop() { return as<Binop>(); } + Call *asCall() { return as<Call>(); } + New *asNew() { return as<New>(); } + Subscript *asSubscript() { return as<Subscript>(); } + Member *asMember() { return as<Member>(); } }; +#define EXPR_VISIT_ALL_KINDS(e) \ + switch (e->exprKind) { \ + case QV4::IR::Expr::ConstExpr: \ + break; \ + case QV4::IR::Expr::StringExpr: \ + break; \ + case QV4::IR::Expr::RegExpExpr: \ + break; \ + case QV4::IR::Expr::NameExpr: \ + break; \ + case QV4::IR::Expr::TempExpr: \ + break; \ + case QV4::IR::Expr::ArgLocalExpr: \ + break; \ + case QV4::IR::Expr::ClosureExpr: \ + break; \ + case QV4::IR::Expr::ConvertExpr: { \ + auto casted = e->asConvert(); \ + visit(casted->expr); \ + } break; \ + case QV4::IR::Expr::UnopExpr: { \ + auto casted = e->asUnop(); \ + visit(casted->expr); \ + } break; \ + case QV4::IR::Expr::BinopExpr: { \ + auto casted = e->asBinop(); \ + visit(casted->left); \ + visit(casted->right); \ + } break; \ + case QV4::IR::Expr::CallExpr: { \ + auto casted = e->asCall(); \ + visit(casted->base); \ + for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \ + visit(it->expr); \ + } break; \ + case QV4::IR::Expr::NewExpr: { \ + auto casted = e->asNew(); \ + visit(casted->base); \ + for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \ + visit(it->expr); \ + } break; \ + case QV4::IR::Expr::SubscriptExpr: { \ + auto casted = e->asSubscript(); \ + visit(casted->base); \ + visit(casted->index); \ + } break; \ + case QV4::IR::Expr::MemberExpr: { \ + auto casted = e->asMember(); \ + visit(casted->base); \ + } break; \ + } + struct ExprList { Expr *expr; ExprList *next; @@ -326,26 +399,28 @@ struct ExprList { struct Const: Expr { double value; + Const(): Expr(ConstExpr) {} + void init(Type type, double value) { this->type = type; this->value = value; } - virtual void accept(ExprVisitor *v) { v->visitConst(this); } - virtual Const *asConst() { return this; } + static bool classof(const Expr *c) { return c->exprKind == ConstExpr; } }; struct String: Expr { const QString *value; + String(): Expr(StringExpr) {} + void init(const QString *value) { this->value = value; } - virtual void accept(ExprVisitor *v) { v->visitString(this); } - virtual String *asString() { return this; } + static bool classof(const Expr *c) { return c->exprKind == StringExpr; } }; struct RegExp: Expr { @@ -359,14 +434,15 @@ struct RegExp: Expr { const QString *value; int flags; + RegExp(): Expr(RegExpExpr) {} + void init(const QString *value, int flags) { this->value = value; this->flags = flags; } - virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } - virtual RegExp *asRegExp() { return this; } + static bool classof(const Expr *c) { return c->exprKind == RegExpExpr; } }; struct Name: Expr { @@ -399,13 +475,13 @@ struct Name: Expr { quint32 line; quint32 column; + Name(): Expr(NameExpr) {} + void initGlobal(const QString *id, quint32 line, quint32 column); void init(const QString *id, quint32 line, quint32 column); void init(Builtin builtin, quint32 line, quint32 column); - virtual void accept(ExprVisitor *v) { v->visitName(this); } - virtual bool isLValue() { return true; } - virtual Name *asName() { return this; } + static bool classof(const Expr *c) { return c->exprKind == NameExpr; } }; struct Q_AUTOTEST_EXPORT Temp: Expr { @@ -424,7 +500,8 @@ struct Q_AUTOTEST_EXPORT Temp: Expr { MemberExpressionResolver *memberResolver; Temp() - : index((1 << 28) - 1) + : Expr(TempExpr) + , index((1 << 28) - 1) , isReadOnly(0) , kind(Invalid) , memberResolver(0) @@ -438,9 +515,8 @@ struct Q_AUTOTEST_EXPORT Temp: Expr { } bool isInvalid() const { return kind == Invalid; } - virtual void accept(ExprVisitor *v) { v->visitTemp(this); } - virtual bool isLValue() { return !isReadOnly; } - virtual Temp *asTemp() { return this; } + + static bool classof(const Expr *c) { return c->exprKind == TempExpr; } }; inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW @@ -479,44 +555,48 @@ struct Q_AUTOTEST_EXPORT ArgLocal: Expr { this->isArgumentsOrEval = false; } - virtual void accept(ExprVisitor *v) { v->visitArgLocal(this); } - virtual bool isLValue() { return true; } - virtual ArgLocal *asArgLocal() { return this; } + ArgLocal(): Expr(ArgLocalExpr) {} bool operator==(const ArgLocal &other) const { return index == other.index && scope == other.scope && kind == other.kind; } + + static bool classof(const Expr *c) { return c->exprKind == ArgLocalExpr; } }; struct Closure: Expr { int value; // index in _module->functions const QString *functionName; + Closure(): Expr(ClosureExpr) {} + void init(int functionInModule, const QString *functionName) { this->value = functionInModule; this->functionName = functionName; } - virtual void accept(ExprVisitor *v) { v->visitClosure(this); } - virtual Closure *asClosure() { return this; } + static bool classof(const Expr *c) { return c->exprKind == ClosureExpr; } }; struct Convert: Expr { Expr *expr; + Convert(): Expr(ConvertExpr) {} + void init(Expr *expr, Type type) { this->expr = expr; this->type = type; } - virtual void accept(ExprVisitor *v) { v->visitConvert(this); } - virtual Convert *asConvert() { return this; } + static bool classof(const Expr *c) { return c->exprKind == ConvertExpr; } }; struct Unop: Expr { - AluOp op; Expr *expr; + AluOp op; + + Unop(): Expr(UnopExpr) {} void init(AluOp op, Expr *expr) { @@ -524,14 +604,15 @@ struct Unop: Expr { this->expr = expr; } - virtual void accept(ExprVisitor *v) { v->visitUnop(this); } - virtual Unop *asUnop() { return this; } + static bool classof(const Expr *c) { return c->exprKind == UnopExpr; } }; struct Binop: Expr { - AluOp op; Expr *left; // Temp or Const Expr *right; // Temp or Const + AluOp op; + + Binop(): Expr(BinopExpr) {} void init(AluOp op, Expr *left, Expr *right) { @@ -540,14 +621,15 @@ struct Binop: Expr { this->right = right; } - virtual void accept(ExprVisitor *v) { v->visitBinop(this); } - virtual Binop *asBinop() { return this; } + static bool classof(const Expr *c) { return c->exprKind == BinopExpr; } }; struct Call: Expr { Expr *base; // Name, Member, Temp ExprList *args; // List of Temps + Call(): Expr(CallExpr) {} + void init(Expr *base, ExprList *args) { this->base = base; @@ -560,14 +642,15 @@ struct Call: Expr { return 0; } - virtual void accept(ExprVisitor *v) { v->visitCall(this); } - virtual Call *asCall() { return this; } + static bool classof(const Expr *c) { return c->exprKind == CallExpr; } }; struct New: Expr { Expr *base; // Name, Member, Temp ExprList *args; // List of Temps + New(): Expr(NewExpr) {} + void init(Expr *base, ExprList *args) { this->base = base; @@ -580,23 +663,22 @@ struct New: Expr { return 0; } - virtual void accept(ExprVisitor *v) { v->visitNew(this); } - virtual New *asNew() { return this; } + static bool classof(const Expr *c) { return c->exprKind == NewExpr; } }; struct Subscript: Expr { Expr *base; Expr *index; + Subscript(): Expr(SubscriptExpr) {} + void init(Expr *base, Expr *index) { this->base = base; this->index = index; } - virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } - virtual bool isLValue() { return true; } - virtual Subscript *asSubscript() { return this; } + static bool classof(const Expr *c) { return c->exprKind == SubscriptExpr; } }; struct Member: Expr { @@ -628,6 +710,8 @@ struct Member: Expr { uchar kind: 3; // MemberKind + Member(): Expr(MemberExpr) {} + void setEnumValue(int value) { kind = MemberOfEnum; enumValue = value; @@ -649,35 +733,52 @@ struct Member: Expr { this->kind = kind; } - virtual void accept(ExprVisitor *v) { v->visitMember(this); } - virtual bool isLValue() { return true; } - virtual Member *asMember() { return this; } + static bool classof(const Expr *c) { return c->exprKind == MemberExpr; } }; +inline bool Expr::isLValue() const { + if (auto t = as<Temp>()) + return !t->isReadOnly; + return exprKind <= LastLValue; +} + struct Stmt { + enum StmtKind: quint8 { + MoveStmt, + ExpStmt, + JumpStmt, + CJumpStmt, + RetStmt, + PhiStmt + }; + + template <typename To> + inline bool isa() const { + return To::classof(this); + } + + template <typename To> + inline To *as() { + if (isa<To>()) + return static_cast<To *>(this); + else + return nullptr; + } + enum { InvalidId = -1 }; QQmlJS::AST::SourceLocation location; - explicit Stmt(int id): _id(id) {} + explicit Stmt(int id, StmtKind stmtKind): _id(id), stmtKind(stmtKind) {} - virtual ~Stmt() - { -#ifdef Q_CC_MSVC - // MSVC complains about potential memory leaks if a destructor never returns. -#else - Q_UNREACHABLE(); -#endif - } - virtual Stmt *asTerminator() { return 0; } + Stmt *asTerminator(); - virtual void accept(StmtVisitor *) = 0; - virtual Exp *asExp() { return 0; } - virtual Move *asMove() { return 0; } - virtual Jump *asJump() { return 0; } - virtual CJump *asCJump() { return 0; } - virtual Ret *asRet() { return 0; } - virtual Phi *asPhi() { return 0; } + Exp *asExp() { return as<Exp>(); } + Move *asMove() { return as<Move>(); } + Jump *asJump() { return as<Jump>(); } + CJump *asCJump() { return as<CJump>(); } + Ret *asRet() { return as<Ret>(); } + Phi *asPhi() { return as<Phi>(); } int id() const { return _id; } @@ -687,21 +788,52 @@ private: // For memory management in BasicBlock private: friend struct Function; int _id; + +public: + const StmtKind stmtKind; }; +#define STMT_VISIT_ALL_KINDS(s) \ + switch (s->stmtKind) { \ + case QV4::IR::Stmt::MoveStmt: { \ + auto casted = s->asMove(); \ + visit(casted->target); \ + visit(casted->source); \ + } break; \ + case QV4::IR::Stmt::ExpStmt: { \ + auto casted = s->asExp(); \ + visit(casted->expr); \ + } break; \ + case QV4::IR::Stmt::JumpStmt: \ + break; \ + case QV4::IR::Stmt::CJumpStmt: { \ + auto casted = s->asCJump(); \ + visit(casted->cond); \ + } break; \ + case QV4::IR::Stmt::RetStmt: { \ + auto casted = s->asRet(); \ + visit(casted->expr); \ + } break; \ + case QV4::IR::Stmt::PhiStmt: { \ + auto casted = s->asPhi(); \ + visit(casted->targetTemp); \ + for (auto *e : casted->incoming) { \ + visit(e); \ + } \ + } break; \ + } + struct Exp: Stmt { Expr *expr; - Exp(int id): Stmt(id) {} + Exp(int id): Stmt(id, ExpStmt) {} void init(Expr *expr) { this->expr = expr; } - virtual void accept(StmtVisitor *v) { v->visitExp(this); } - virtual Exp *asExp() { return this; } - + static bool classof(const Stmt *c) { return c->stmtKind == ExpStmt; } }; struct Move: Stmt { @@ -709,7 +841,7 @@ struct Move: Stmt { Expr *source; bool swap; - Move(int id): Stmt(id) {} + Move(int id): Stmt(id, MoveStmt) {} void init(Expr *target, Expr *source) { @@ -718,25 +850,20 @@ struct Move: Stmt { this->swap = false; } - virtual void accept(StmtVisitor *v) { v->visitMove(this); } - virtual Move *asMove() { return this; } - + static bool classof(const Stmt *c) { return c->stmtKind == MoveStmt; } }; struct Jump: Stmt { BasicBlock *target; - Jump(int id): Stmt(id) {} + Jump(int id): Stmt(id, JumpStmt) {} void init(BasicBlock *target) { this->target = target; } - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitJump(this); } - virtual Jump *asJump() { return this; } + static bool classof(const Stmt *c) { return c->stmtKind == JumpStmt; } }; struct CJump: Stmt { @@ -745,7 +872,7 @@ struct CJump: Stmt { BasicBlock *iffalse; BasicBlock *parent; - CJump(int id): Stmt(id) {} + CJump(int id): Stmt(id, CJumpStmt) {} void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse, BasicBlock *parent) { @@ -755,26 +882,20 @@ struct CJump: Stmt { this->parent = parent; } - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitCJump(this); } - virtual CJump *asCJump() { return this; } + static bool classof(const Stmt *c) { return c->stmtKind == CJumpStmt; } }; struct Ret: Stmt { Expr *expr; - Ret(int id): Stmt(id) {} + Ret(int id): Stmt(id, RetStmt) {} void init(Expr *expr) { this->expr = expr; } - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitRet(this); } - virtual Ret *asRet() { return this; } + static bool classof(const Stmt *c) { return c->stmtKind == RetStmt; } }; // Phi nodes can only occur at the start of a basic block. If there are any, they need to be @@ -785,30 +906,52 @@ struct Phi: Stmt { Temp *targetTemp; VarLengthArray<Expr *, 4> incoming; - Phi(int id): Stmt(id) {} + Phi(int id): Stmt(id, PhiStmt) {} - virtual void accept(StmtVisitor *v) { v->visitPhi(this); } - virtual Phi *asPhi() { return this; } + static bool classof(const Stmt *c) { return c->stmtKind == PhiStmt; } void destroyData() { incoming.~VarLengthArray(); } }; +inline Stmt *Stmt::asTerminator() +{ + if (auto s = asJump()) { + return s; + } else if (auto s = asCJump()) { + return s; + } else if (auto s = asRet()) { + return s; + } else { + return nullptr; + } +} + struct Q_QML_PRIVATE_EXPORT Module { QQmlJS::MemoryPool pool; QVector<Function *> functions; Function *rootFunction; QString fileName; + qint64 sourceTimeStamp; bool isQmlModule; // implies rootFunction is always 0 +#ifdef QT_NO_QML_DEBUGGER + static const bool debugMode = false; +#else bool debugMode; +#endif Function *newFunction(const QString &name, Function *outer); Module(bool debugMode) : rootFunction(0) + , sourceTimeStamp(0) , isQmlModule(false) +#ifndef QT_NO_QML_DEBUGGER , debugMode(debugMode) {} +#else + { Q_UNUSED(debugMode); } +#endif ~Module(); void setFileName(const QString &name); @@ -1060,6 +1203,20 @@ private: unsigned _isRemoved : 1; }; +template <typename T> +class SmallSet: public QVarLengthArray<T, 8> +{ +public: + void insert(int value) + { + for (auto it : *this) { + if (it == value) + return; + } + this->append(value); + } +}; + // Map from meta property index (existence implies dependency) to notify signal index struct KeyValuePair { @@ -1125,14 +1282,15 @@ struct Function { uint isNamedExpression : 1; uint hasTry: 1; uint hasWith: 1; - uint unused : 25; + uint isQmlBinding: 1; + uint unused : 24; - // Location of declaration in source code (-1 if not specified) - int line; - int column; + // Location of declaration in source code (0 if not specified) + uint line; + uint column; // Qml extension: - QSet<int> idObjectDependencies; + SmallSet<int> idObjectDependencies; PropertyDependencyMap contextObjectPropertyDependencies; PropertyDependencyMap scopeObjectPropertyDependencies; @@ -1192,7 +1350,7 @@ private: int _statementCount; }; -class CloneExpr: protected IR::ExprVisitor +class CloneExpr { public: explicit CloneExpr(IR::BasicBlock *block = 0); @@ -1210,7 +1368,7 @@ public: { Expr *c = expr; qSwap(cloned, c); - expr->accept(this); + visit(expr); qSwap(cloned, c); return static_cast<ExprSubclass *>(c); } @@ -1253,23 +1411,10 @@ public: return newArgLocal; } -protected: +private: IR::ExprList *clone(IR::ExprList *list); - virtual void visitConst(Const *); - virtual void visitString(String *); - virtual void visitRegExp(RegExp *); - virtual void visitName(Name *); - virtual void visitTemp(Temp *); - virtual void visitArgLocal(ArgLocal *); - virtual void visitClosure(Closure *); - virtual void visitConvert(Convert *); - virtual void visitUnop(Unop *); - virtual void visitBinop(Binop *); - virtual void visitCall(Call *); - virtual void visitNew(New *); - virtual void visitSubscript(Subscript *); - virtual void visitMember(Member *); + void visit(Expr *e); protected: IR::BasicBlock *block; @@ -1278,7 +1423,7 @@ private: IR::Expr *cloned; }; -class Q_AUTOTEST_EXPORT IRPrinter: public StmtVisitor, public ExprVisitor +class Q_AUTOTEST_EXPORT IRPrinter { public: IRPrinter(QTextStream *out); @@ -1291,6 +1436,7 @@ public: virtual void print(Function *f); virtual void print(BasicBlock *bb); + void visit(Stmt *s); virtual void visitExp(Exp *s); virtual void visitMove(Move *s); virtual void visitJump(Jump *s); @@ -1298,6 +1444,7 @@ public: virtual void visitRet(Ret *s); virtual void visitPhi(Phi *s); + void visit(Expr *e); virtual void visitConst(Const *e); virtual void visitString(String *e); virtual void visitRegExp(RegExp *e); diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index f021e1f760..283fb24897 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -642,7 +642,7 @@ public: qout << from; else qout << "(none)"; - qout << " -> " << to->index() << endl; + qout << " dominates " << to->index() << endl; } qDebug("%s", buf.data().constData()); } @@ -740,6 +740,21 @@ public: return order; } + void mergeIntoPredecessor(BasicBlock *successor) + { + int succIdx = successor->index(); + if (succIdx == InvalidBasicBlockIndex) { + return; + } + + int succDom = idom[unsigned(succIdx)]; + for (BasicBlockIndex &idx : idom) { + if (idx == succIdx) { + idx = succDom; + } + } + } + private: bool dominates(BasicBlockIndex dominator, BasicBlockIndex dominated) const { // dominator can be Invalid when the dominated block has no dominator (i.e. the start node) @@ -898,7 +913,7 @@ private: } }; -class VariableCollector: public StmtVisitor, ExprVisitor { +class VariableCollector { std::vector<Temp> _allTemps; std::vector<BasicBlockSet> _defsites; std::vector<std::vector<int> > A_orig; @@ -946,7 +961,7 @@ public: currentBB = bb; killed.assign(function->tempCount, false); for (Stmt *s : bb->statements()) - s->accept(this); + visit(s); } } @@ -971,62 +986,45 @@ public: return nonLocals.at(var.index); } -protected: - virtual void visitPhi(Phi *) {} - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(Member *e) { e->base->accept(this); } - virtual void visitExp(Exp *s) { s->expr->accept(this); } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } +private: + void visit(Stmt *s) + { + if (s->asPhi()) { + // nothing to do + } else if (auto move = s->asMove()) { + visit(move->source); - virtual void visitMove(Move *s) { - s->source->accept(this); + if (Temp *t = move->target->asTemp()) { + addTemp(t); - if (Temp *t = s->target->asTemp()) { - addTemp(t); + if (isCollectable(t)) { + _defsites[t->index].insert(currentBB); + addDefInCurrentBlock(t); - if (isCollectable(t)) { - _defsites[t->index].insert(currentBB); - addDefInCurrentBlock(t); - - // For semi-pruned SSA: - killed.setBit(t->index); + // For semi-pruned SSA: + killed.setBit(t->index); + } + } else { + visit(move->target); } } else { - s->target->accept(this); + STMT_VISIT_ALL_KINDS(s) } } - virtual void visitTemp(Temp *t) + void visit(Expr *e) { - addTemp(t); + if (auto t = e->asTemp()) { + addTemp(t); - if (isCollectable(t)) - if (!killed.at(t->index)) - nonLocals.setBit(t->index); + if (isCollectable(t)) { + if (!killed.at(t->index)) { + nonLocals.setBit(t->index); + } + } + } else { + EXPR_VISIT_ALL_KINDS(e); + } } }; @@ -1192,6 +1190,15 @@ public: return _defUses[variable.index].blockOfStatement; } + void replaceBasicBlock(BasicBlock *from, BasicBlock *to) + { + for (auto &du : _defUses) { + if (du.blockOfStatement == from) { + du.blockOfStatement = to; + } + } + } + void removeUse(Stmt *usingStmt, const Temp &var) { Q_ASSERT(static_cast<unsigned>(var.index) < _defUses.size()); @@ -1345,7 +1352,7 @@ void insertPhiNode(const Temp &a, BasicBlock *y, IR::Function *f) { // // Undo(t, c) = // mapping[t] = c -class VariableRenamer: public StmtVisitor, public ExprVisitor +class VariableRenamer { Q_DISABLE_COPY(VariableRenamer) @@ -1466,7 +1473,7 @@ private: for (Stmt *s : bb->statements()) { currentStmt = s; - s->accept(this); + visit(s); } for (BasicBlock *Y : bb->out) { @@ -1532,23 +1539,35 @@ private: return newIndex; } -protected: - virtual void visitTemp(Temp *e) { // only called for uses, not defs -// qDebug()<<"I: replacing use of"<<e->index<<"with"<<stack[e->index].top(); - e->index = currentNumber(*e); - e->kind = Temp::VirtualRegister; - defUses.addUse(*e, currentStmt); - } +private: + void visit(Stmt *s) + { + if (auto move = s->asMove()) { + // uses: + visit(move->source); - virtual void visitMove(Move *s) { - // uses: - s->source->accept(this); + // defs: + if (Temp *t = move->target->asTemp()) { + renameTemp(t); + } else { + visit(move->target); + } + } else if (auto phi = s->asPhi()) { + renameTemp(phi->targetTemp); + } else { + STMT_VISIT_ALL_KINDS(s); + } + } - // defs: - if (Temp *t = s->target->asTemp()) - renameTemp(t); - else - s->target->accept(this); + void visit(Expr *e) + { + if (auto temp = e->asTemp()) { + temp->index = currentNumber(*temp); + temp->kind = Temp::VirtualRegister; + defUses.addUse(*temp, currentStmt); + } else { + EXPR_VISIT_ALL_KINDS(e); + } } void renameTemp(Temp *t) { // only called for defs, not uses @@ -1558,44 +1577,6 @@ protected: t->index = newIdx; defUses.addDef(t, currentStmt, currentBB); } - - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - virtual void visitPhi(Phi *s) { renameTemp(s->targetTemp); } - - virtual void visitExp(Exp *s) { s->expr->accept(this); } - - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitSubscript(Subscript *e) { - e->base->accept(this); - e->index->accept(this); - } - - virtual void visitMember(Member *e) { - e->base->accept(this); - } }; // This function converts the IR to semi-pruned SSA form. For details about SSA and the algorightm, @@ -1922,7 +1903,7 @@ private: } }; -class SideEffectsChecker: public ExprVisitor +class SideEffectsChecker { bool _sideEffect; @@ -1931,11 +1912,14 @@ public: : _sideEffect(false) {} + ~SideEffectsChecker() + {} + bool hasSideEffects(Expr *expr) { bool sideEffect = false; qSwap(_sideEffect, sideEffect); - expr->accept(this); + visit(expr); qSwap(_sideEffect, sideEffect); return sideEffect; } @@ -1948,12 +1932,35 @@ protected: bool seenSideEffects() const { return _sideEffect; } -protected: - void visitConst(Const *) Q_DECL_OVERRIDE {} - void visitString(IR::String *) Q_DECL_OVERRIDE {} - void visitRegExp(IR::RegExp *) Q_DECL_OVERRIDE {} + void visit(Expr *e) + { + if (auto n = e->asName()) { + visitName(n); + } else if (auto t = e->asTemp()) { + visitTemp(t); + } else if (auto c = e->asClosure()) { + visitClosure(c); + } else if (auto c = e->asConvert()) { + visitConvert(c); + } else if (auto u = e->asUnop()) { + visitUnop(u); + } else if (auto b = e->asBinop()) { + visitBinop(b); + } else if (auto c = e->asCall()) { + visitCall(c); + } else if (auto n = e->asNew()) { + visitNew(n); + } else if (auto s = e->asSubscript()) { + visitSubscript(s); + } else if (auto m = e->asMember()) { + visitMember(m); + } + } + + virtual void visitTemp(Temp *) {} - void visitName(Name *e) Q_DECL_OVERRIDE { +private: + void visitName(Name *e) { if (e->freeOfSideEffects) return; // TODO: maybe we can distinguish between built-ins of which we know that they do not have @@ -1962,15 +1969,12 @@ protected: markAsSideEffect(); } - void visitTemp(Temp *) Q_DECL_OVERRIDE {} - void visitArgLocal(ArgLocal *) Q_DECL_OVERRIDE {} - - void visitClosure(Closure *) Q_DECL_OVERRIDE { + void visitClosure(Closure *) { markAsSideEffect(); } - void visitConvert(Convert *e) Q_DECL_OVERRIDE { - e->expr->accept(this); + void visitConvert(Convert *e) { + visit(e->expr); switch (e->expr->type) { case QObjectType: @@ -1983,8 +1987,8 @@ protected: } } - void visitUnop(Unop *e) Q_DECL_OVERRIDE { - e->expr->accept(this); + void visitUnop(Unop *e) { + visit(e->expr); switch (e->op) { case OpUPlus: @@ -2001,7 +2005,7 @@ protected: } } - void visitBinop(Binop *e) Q_DECL_OVERRIDE { + void visitBinop(Binop *e) { // 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. @@ -2013,30 +2017,30 @@ protected: markAsSideEffect(); } - void visitSubscript(Subscript *e) Q_DECL_OVERRIDE { - e->base->accept(this); - e->index->accept(this); + void visitSubscript(Subscript *e) { + visit(e->base); + visit(e->index); markAsSideEffect(); } - void visitMember(Member *e) Q_DECL_OVERRIDE { - e->base->accept(this); + void visitMember(Member *e) { + visit(e->base); if (e->freeOfSideEffects) return; markAsSideEffect(); } - void visitCall(Call *e) Q_DECL_OVERRIDE { - e->base->accept(this); + void visitCall(Call *e) { + visit(e->base); for (ExprList *args = e->args; args; args = args->next) - args->expr->accept(this); + visit(args->expr); markAsSideEffect(); // TODO: there are built-in functions that have no side effect. } - void visitNew(New *e) Q_DECL_OVERRIDE { - e->base->accept(this); + void visitNew(New *e) { + visit(e->base); for (ExprList *args = e->args; args; args = args->next) - args->expr->accept(this); + visit(args->expr); markAsSideEffect(); // TODO: there are built-in types that have no side effect. } }; @@ -2045,21 +2049,19 @@ class EliminateDeadCode: public SideEffectsChecker { DefUses &_defUses; StatementWorklist &_worklist; - QVector<Temp *> _collectedTemps; + QVarLengthArray<Temp *, 8> _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) { + for (Temp *t : _collectedTemps) { _defUses.removeUse(stmt, *t); _worklist += _defUses.defStmt(*t); } @@ -2067,13 +2069,13 @@ public: } protected: - void visitTemp(Temp *e) Q_DECL_OVERRIDE + void visitTemp(Temp *e) Q_DECL_OVERRIDE Q_DECL_FINAL { _collectedTemps.append(e); } }; -class PropagateTempTypes: public StmtVisitor, ExprVisitor +class PropagateTempTypes { const DefUses &defUses; UntypedTemp theTemp; @@ -2089,64 +2091,31 @@ public: newType = type; theTemp = temp; if (Stmt *defStmt = defUses.defStmt(temp.temp)) - defStmt->accept(this); + visit(defStmt); foreach (Stmt *use, defUses.uses(temp.temp)) - use->accept(this); + visit(use); } -protected: - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *e) { - if (theTemp == UntypedTemp(*e)) { - e->type = static_cast<Type>(newType.type); - e->memberResolver = newType.memberResolver; - } - } - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitSubscript(Subscript *e) { - e->base->accept(this); - e->index->accept(this); - } - - virtual void visitMember(Member *e) { - e->base->accept(this); - } - - virtual void visitExp(Exp *s) {s->expr->accept(this);} - virtual void visitMove(Move *s) { - s->source->accept(this); - s->target->accept(this); +private: + void visit(Stmt *s) + { + STMT_VISIT_ALL_KINDS(s); } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitPhi(Phi *s) { - s->targetTemp->accept(this); - foreach (Expr *e, s->incoming) - e->accept(this); + void visit(Expr *e) + { + if (auto temp = e->asTemp()) { + if (theTemp == UntypedTemp(*temp)) { + temp->type = static_cast<Type>(newType.type); + temp->memberResolver = newType.memberResolver; + } + } else { + EXPR_VISIT_ALL_KINDS(e); + } } }; -class TypeInference: public StmtVisitor, public ExprVisitor +class TypeInference { enum { DebugTypeInference = 0 }; @@ -2248,7 +2217,7 @@ private: TypingResult ty; std::swap(_ty, ty); std::swap(_currentStmt, s); - _currentStmt->accept(this); + visit(_currentStmt); std::swap(_currentStmt, s); std::swap(_ty, ty); return ty.fullyTyped; @@ -2257,7 +2226,7 @@ private: TypingResult run(Expr *e) { TypingResult ty; std::swap(_ty, ty); - e->accept(this); + visit(e); std::swap(_ty, ty); if (ty.type != UnknownType) @@ -2299,39 +2268,74 @@ private: } } -protected: - virtual void visitConst(Const *e) { - if (e->type & NumberType) { - if (canConvertToSignedInteger(e->value)) +private: + void visit(Expr *e) + { + if (auto c = e->asConst()) { + visitConst(c); + } else if (auto s = e->asString()) { + visitString(s); + } else if (auto r = e->asRegExp()) { + visitRegExp(r); + } else if (auto n = e->asName()) { + visitName(n); + } else if (auto t = e->asTemp()) { + visitTemp(t); + } else if (auto a = e->asArgLocal()) { + visitArgLocal(a); + } else if (auto c = e->asClosure()) { + visitClosure(c); + } else if (auto c = e->asConvert()) { + visitConvert(c); + } else if (auto u = e->asUnop()) { + visitUnop(u); + } else if (auto b = e->asBinop()) { + visitBinop(b); + } else if (auto c = e->asCall()) { + visitCall(c); + } else if (auto n = e->asNew()) { + visitNew(n); + } else if (auto s = e->asSubscript()) { + visitSubscript(s); + } else if (auto m = e->asMember()) { + visitMember(m); + } else { + Q_UNREACHABLE(); + } + } + + void visitConst(Const *c) { + if (c->type & NumberType) { + if (canConvertToSignedInteger(c->value)) _ty = TypingResult(SInt32Type); - else if (canConvertToUnsignedInteger(e->value)) + else if (canConvertToUnsignedInteger(c->value)) _ty = TypingResult(UInt32Type); else - _ty = TypingResult(e->type); + _ty = TypingResult(c->type); } else - _ty = TypingResult(e->type); + _ty = TypingResult(c->type); } - virtual void visitString(IR::String *) { _ty = TypingResult(StringType); } - virtual void visitRegExp(IR::RegExp *) { _ty = TypingResult(VarType); } - virtual void visitName(Name *) { _ty = TypingResult(VarType); } - virtual void visitTemp(Temp *e) { + void visitString(IR::String *) { _ty = TypingResult(StringType); } + void visitRegExp(IR::RegExp *) { _ty = TypingResult(VarType); } + void visitName(Name *) { _ty = TypingResult(VarType); } + void visitTemp(Temp *e) { if (e->memberResolver && e->memberResolver->isValid()) _ty = TypingResult(e->memberResolver); else _ty = TypingResult(_tempTypes[e->index]); setType(e, _ty.type); } - virtual void visitArgLocal(ArgLocal *e) { + void visitArgLocal(ArgLocal *e) { _ty = TypingResult(VarType); setType(e, _ty.type); } - virtual void visitClosure(Closure *) { _ty = TypingResult(VarType); } - virtual void visitConvert(Convert *e) { + void visitClosure(Closure *) { _ty = TypingResult(VarType); } + void visitConvert(Convert *e) { _ty = TypingResult(e->type); } - virtual void visitUnop(Unop *e) { + void visitUnop(Unop *e) { _ty = run(e->expr); switch (e->op) { case OpUPlus: _ty.type = DoubleType; return; @@ -2348,7 +2352,7 @@ protected: } } - virtual void visitBinop(Binop *e) { + void visitBinop(Binop *e) { TypingResult leftTy = run(e->left); TypingResult rightTy = run(e->right); _ty.fullyTyped = leftTy.fullyTyped && rightTy.fullyTyped; @@ -2406,24 +2410,24 @@ protected: } } - virtual void visitCall(Call *e) { + void visitCall(Call *e) { _ty = run(e->base); for (ExprList *it = e->args; it; it = it->next) _ty.fullyTyped &= run(it->expr).fullyTyped; _ty.type = VarType; } - virtual void visitNew(New *e) { + void visitNew(New *e) { _ty = run(e->base); for (ExprList *it = e->args; it; it = it->next) _ty.fullyTyped &= run(it->expr).fullyTyped; _ty.type = VarType; } - virtual void visitSubscript(Subscript *e) { + void visitSubscript(Subscript *e) { _ty.fullyTyped = run(e->base).fullyTyped && run(e->index).fullyTyped; _ty.type = VarType; } - virtual void visitMember(Member *e) { + void visitMember(Member *e) { _ty = run(e->base); if (_ty.fullyTyped && _ty.type.memberResolver && _ty.type.memberResolver->isValid()) { @@ -2433,8 +2437,27 @@ protected: _ty.type = VarType; } - virtual void visitExp(Exp *s) { _ty = run(s->expr); } - virtual void visitMove(Move *s) { + void visit(Stmt *s) + { + if (auto e = s->asExp()) { + visitExp(e); + } else if (auto m = s->asMove()) { + visitMove(m); + } else if (auto j = s->asJump()) { + visitJump(j); + } else if (auto c = s->asCJump()) { + visitCJump(c); + } else if (auto r = s->asRet()) { + visitRet(r); + } else if (auto p = s->asPhi()) { + visitPhi(p); + } else { + Q_UNREACHABLE(); + } + } + + void visitExp(Exp *s) { _ty = run(s->expr); } + void visitMove(Move *s) { if (Temp *t = s->target->asTemp()) { if (Name *n = s->source->asName()) { if (n->builtin == Name::builtin_qml_context) { @@ -2455,10 +2478,10 @@ protected: _ty.fullyTyped &= sourceTy.fullyTyped; } - virtual void visitJump(Jump *) { _ty = TypingResult(MissingType); } - virtual void visitCJump(CJump *s) { _ty = run(s->cond); } - virtual void visitRet(Ret *s) { _ty = run(s->expr); } - virtual void visitPhi(Phi *s) { + void visitJump(Jump *) { _ty = TypingResult(MissingType); } + void visitCJump(CJump *s) { _ty = run(s->cond); } + void visitRet(Ret *s) { _ty = run(s->expr); } + void visitPhi(Phi *s) { _ty = run(s->incoming[0]); for (int i = 1, ei = s->incoming.size(); i != ei; ++i) { TypingResult ty = run(s->incoming[i]); @@ -2677,14 +2700,15 @@ void convertConst(Const *c, Type targetType) c->type = targetType; } -class TypePropagation: public StmtVisitor, public ExprVisitor { +class TypePropagation +{ DefUses &_defUses; Type _ty; IR::Function *_f; bool run(Expr *&e, Type requestedType = UnknownType, bool insertConversion = true) { qSwap(_ty, requestedType); - e->accept(this); + visit(e); qSwap(_ty, requestedType); if (requestedType != UnknownType) { @@ -2731,7 +2755,7 @@ public: for (Stmt *s : bb->statements()) { _currStmt = s; - s->accept(this); + visit(s); } foreach (const Conversion &conversion, _conversions) { @@ -2817,8 +2841,29 @@ public: } } -protected: - virtual void visitConst(Const *c) { +private: + void visit(Expr *e) + { + if (auto c = e->asConst()) { + visitConst(c); + } else if (auto c = e->asConvert()) { + run(c->expr, c->type); + } else if (auto u = e->asUnop()) { + run(u->expr, u->type); + } else if (auto b = e->asBinop()) { + visitBinop(b); + } else if (auto c = e->asCall()) { + visitCall(c); + } else if (auto n = e->asNew()) { + visitNew(n); + } else if (auto s = e->asSubscript()) { + visitSubscript(s); + } else if (auto m = e->asMember()) { + visitMember(m); + } + } + + void visitConst(Const *c) { if (_ty & NumberType && c->type & NumberType) { if (_ty == SInt32Type) c->value = QV4::Primitive::toInt32(c->value); @@ -2828,15 +2873,7 @@ protected: } } - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *) {} - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { run(e->expr, e->type); } - virtual void visitUnop(Unop *e) { run(e->expr, e->type); } - virtual void visitBinop(Binop *e) { + void visitBinop(Binop *e) { // FIXME: This routine needs more tuning! switch (e->op) { case OpAdd: @@ -2887,20 +2924,36 @@ protected: Q_UNREACHABLE(); } } - virtual void visitCall(Call *e) { + void visitCall(Call *e) { run(e->base); for (ExprList *it = e->args; it; it = it->next) run(it->expr); } - virtual void visitNew(New *e) { + void visitNew(New *e) { run(e->base); for (ExprList *it = e->args; it; it = it->next) run(it->expr); } - virtual void visitSubscript(Subscript *e) { run(e->base); run(e->index); } - virtual void visitMember(Member *e) { run(e->base); } - virtual void visitExp(Exp *s) { run(s->expr); } - virtual void visitMove(Move *s) { + void visitSubscript(Subscript *e) { run(e->base); run(e->index); } + void visitMember(Member *e) { run(e->base); } + + void visit(Stmt *s) + { + if (auto e = s->asExp()) { + visitExp(e); + } else if (auto m = s->asMove()) { + visitMove(m); + } else if (auto c = s->asCJump()) { + visitCJump(c); + } else if (auto r = s->asRet()) { + visitRet(r); + } else if (auto p = s->asPhi()) { + visitPhi(p); + } + } + + void visitExp(Exp *s) { run(s->expr); } + void visitMove(Move *s) { if (s->source->asConvert()) return; // this statement got inserted for a phi-node type conversion @@ -2925,12 +2978,11 @@ protected: run(s->source, s->target->type, !inhibitConversion); } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { + void visitCJump(CJump *s) { run(s->cond, BoolType); } - virtual void visitRet(Ret *s) { run(s->expr); } - virtual void visitPhi(Phi *s) { + void visitRet(Ret *s) { run(s->expr); } + void visitPhi(Phi *s) { Type ty = s->targetTemp->type; for (int i = 0, ei = s->incoming.size(); i != ei; ++i) run(s->incoming[i], ty); @@ -3299,6 +3351,15 @@ class BlockScheduler // this is a loop, where there in -> candidate edge is the jump back to the top of the loop. continue; + if (in == candidate) + // this is a very tight loop, e.g.: + // L1: ... + // goto L1 + // This can happen when, for example, the basic-block merging gets rid of the empty + // body block. In this case, we can safely schedule this block (if all other + // incoming edges are either loop-back edges, or have been scheduled already). + continue; + return false; // an incoming edge that is not yet emitted, and is not a back-edge } @@ -3504,7 +3565,7 @@ static Expr *clone(Expr *e, IR::Function *function) { } } -class ExprReplacer: public StmtVisitor, public ExprVisitor +class ExprReplacer { DefUses &_defUses; IR::Function* _function; @@ -3535,7 +3596,7 @@ public: // qout << " " << uses.size() << " uses:"<<endl; foreach (Stmt *use, uses) { // qout<<" ";use->dump(qout);qout<<"\n"; - use->accept(this); + visit(use); // qout<<" -> ";use->dump(qout);qout<<"\n"; W += use; if (newUses) @@ -3546,45 +3607,101 @@ public: qSwap(_toReplace, toReplace); } -protected: - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *) {} - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { check(e->expr); } - virtual void visitUnop(Unop *e) { check(e->expr); } - virtual void visitBinop(Binop *e) { check(e->left); check(e->right); } - virtual void visitCall(Call *e) { +private: + void visit(Expr *e) + { + if (auto c = e->asConst()) { + visitConst(c); + } else if (auto s = e->asString()) { + visitString(s); + } else if (auto r = e->asRegExp()) { + visitRegExp(r); + } else if (auto n = e->asName()) { + visitName(n); + } else if (auto t = e->asTemp()) { + visitTemp(t); + } else if (auto a = e->asArgLocal()) { + visitArgLocal(a); + } else if (auto c = e->asClosure()) { + visitClosure(c); + } else if (auto c = e->asConvert()) { + visitConvert(c); + } else if (auto u = e->asUnop()) { + visitUnop(u); + } else if (auto b = e->asBinop()) { + visitBinop(b); + } else if (auto c = e->asCall()) { + visitCall(c); + } else if (auto n = e->asNew()) { + visitNew(n); + } else if (auto s = e->asSubscript()) { + visitSubscript(s); + } else if (auto m = e->asMember()) { + visitMember(m); + } else { + Q_UNREACHABLE(); + } + } + + void visitConst(Const *) {} + void visitString(IR::String *) {} + void visitRegExp(IR::RegExp *) {} + void visitName(Name *) {} + void visitTemp(Temp *) {} + void visitArgLocal(ArgLocal *) {} + void visitClosure(Closure *) {} + void visitConvert(Convert *e) { check(e->expr); } + void visitUnop(Unop *e) { check(e->expr); } + void visitBinop(Binop *e) { check(e->left); check(e->right); } + void visitCall(Call *e) { check(e->base); for (ExprList *it = e->args; it; it = it->next) check(it->expr); } - virtual void visitNew(New *e) { + void visitNew(New *e) { check(e->base); for (ExprList *it = e->args; it; it = it->next) check(it->expr); } - virtual void visitSubscript(Subscript *e) { check(e->base); check(e->index); } - virtual void visitMember(Member *e) { check(e->base); } - virtual void visitExp(Exp *s) { check(s->expr); } - virtual void visitMove(Move *s) { check(s->target); check(s->source); } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { check(s->cond); } - virtual void visitRet(Ret *s) { check(s->expr); } - virtual void visitPhi(Phi *s) { + void visitSubscript(Subscript *e) { check(e->base); check(e->index); } + void visitMember(Member *e) { check(e->base); } + + void visit(Stmt *s) + { + if (auto e = s->asExp()) { + visitExp(e); + } else if (auto m = s->asMove()) { + visitMove(m); + } else if (auto j = s->asJump()) { + visitJump(j); + } else if (auto c = s->asCJump()) { + visitCJump(c); + } else if (auto r = s->asRet()) { + visitRet(r); + } else if (auto p = s->asPhi()) { + visitPhi(p); + } else { + Q_UNREACHABLE(); + } + } + + void visitExp(Exp *s) { check(s->expr); } + void visitMove(Move *s) { check(s->target); check(s->source); } + void visitJump(Jump *) {} + void visitCJump(CJump *s) { check(s->cond); } + void visitRet(Ret *s) { check(s->expr); } + void visitPhi(Phi *s) { for (int i = 0, ei = s->incoming.size(); i != ei; ++i) check(s->incoming[i]); } private: void check(Expr *&e) { - if (equals(e, _toReplace)) + if (equals(e, _toReplace)) { e = clone(_replacement, _function); - else - e->accept(this); + } else { + visit(e); + } } // This only calculates equality for everything needed by constant propagation @@ -3625,6 +3742,8 @@ namespace { void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUses, StatementWorklist &W, DominatorTree &dt) { + enum { DebugUnlinking = 0 }; + struct Util { static void removeIncomingEdge(BasicBlock *from, BasicBlock *to, DefUses &defUses, StatementWorklist &W) { @@ -3663,11 +3782,17 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs } }; + Q_ASSERT(!from->isRemoved()); + Q_ASSERT(!to->isRemoved()); + // don't purge blocks that are entry points for catch statements. They might not be directly // connected, but are required anyway if (to->isExceptionHandler()) return; + if (DebugUnlinking) + qDebug("Unlinking L%d -> L%d...", from->index(), to->index()); + // First, unlink the edge from->out.removeOne(to); Util::removeIncomingEdge(from, to, defUses, W); @@ -3677,8 +3802,12 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs // Check if the target is still reachable... if (Util::isReachable(to, dt)) { // yes, recalculate the immediate dominator, and we're done. + if (DebugUnlinking) + qDebug(".. L%d is still reachable, recalulate idom.", to->index()); dt.collectSiblings(to, siblings); } else { + if (DebugUnlinking) + qDebug(".. L%d is unreachable, purging it:", to->index()); // The target is unreachable, so purge it: QVector<BasicBlock *> toPurge; toPurge.reserve(8); @@ -3686,6 +3815,8 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs while (!toPurge.isEmpty()) { BasicBlock *bb = toPurge.first(); toPurge.removeFirst(); + if (DebugUnlinking) + qDebug("... purging L%d", bb->index()); if (bb->isRemoved()) continue; @@ -3729,6 +3860,8 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs } dt.recalculateIDoms(siblings); + if (DebugUnlinking) + qDebug("Unlinking done."); } bool tryOptimizingComparison(Expr *&expr) @@ -3748,42 +3881,42 @@ bool tryOptimizingComparison(Expr *&expr) switch (b->op) { case OpGt: - leftConst->value = Runtime::compareGreaterThan(l, r); + leftConst->value = Runtime::method_compareGreaterThan(l, r); leftConst->type = BoolType; expr = leftConst; return true; case OpLt: - leftConst->value = Runtime::compareLessThan(l, r); + leftConst->value = Runtime::method_compareLessThan(l, r); leftConst->type = BoolType; expr = leftConst; return true; case OpGe: - leftConst->value = Runtime::compareGreaterEqual(l, r); + leftConst->value = Runtime::method_compareGreaterEqual(l, r); leftConst->type = BoolType; expr = leftConst; return true; case OpLe: - leftConst->value = Runtime::compareLessEqual(l, r); + leftConst->value = Runtime::method_compareLessEqual(l, r); leftConst->type = BoolType; expr = leftConst; return true; case OpStrictEqual: - leftConst->value = Runtime::compareStrictEqual(l, r); + leftConst->value = Runtime::method_compareStrictEqual(l, r); leftConst->type = BoolType; expr = leftConst; return true; case OpEqual: - leftConst->value = Runtime::compareEqual(l, r); + leftConst->value = Runtime::method_compareEqual(l, r); leftConst->type = BoolType; expr = leftConst; return true; case OpStrictNotEqual: - leftConst->value = Runtime::compareStrictNotEqual(l, r); + leftConst->value = Runtime::method_compareStrictNotEqual(l, r); leftConst->type = BoolType; expr = leftConst; return true; case OpNotEqual: - leftConst->value = Runtime::compareNotEqual(l, r); + leftConst->value = Runtime::method_compareNotEqual(l, r); leftConst->type = BoolType; expr = leftConst; return true; @@ -4153,7 +4286,8 @@ void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df) } //### TODO: use DefUses from the optimizer, because it already has all this information -class InputOutputCollector: protected StmtVisitor, protected ExprVisitor { +class InputOutputCollector +{ void setOutput(Temp *out) { Q_ASSERT(!output); @@ -4170,48 +4304,33 @@ public: void collect(Stmt *s) { inputs.resize(0); output = 0; - s->accept(this); + visit(s); } -protected: - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *e) { - inputs.push_back(e); - } - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(Member *e) { e->base->accept(this); } - virtual void visitExp(Exp *s) { s->expr->accept(this); } - virtual void visitMove(Move *s) { - s->source->accept(this); - if (Temp *t = s->target->asTemp()) { - setOutput(t); +private: + void visit(Expr *e) + { + if (auto t = e->asTemp()) { + inputs.push_back(t); } else { - s->target->accept(this); + EXPR_VISIT_ALL_KINDS(e); } } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitPhi(Phi *) { - // Handled separately + + void visit(Stmt *s) + { + if (auto m = s->asMove()) { + visit(m->source); + if (Temp *t = m->target->asTemp()) { + setOutput(t); + } else { + visit(m->target); + } + } else if (s->asPhi()) { + // Handled separately + } else { + STMT_VISIT_ALL_KINDS(s); + } } }; @@ -4381,7 +4500,7 @@ void removeUnreachleBlocks(IR::Function *function) function->renumberBasicBlocks(); } -class ConvertArgLocals: protected StmtVisitor, protected ExprVisitor +class ConvertArgLocals { public: ConvertArgLocals(IR::Function *function) @@ -4419,10 +4538,13 @@ public: } } - for (BasicBlock *bb : function->basicBlocks()) - if (!bb->isRemoved()) - for (Stmt *s : bb->statements()) - s->accept(this); + for (BasicBlock *bb : function->basicBlocks()) { + if (!bb->isRemoved()) { + for (Stmt *s : bb->statements()) { + visit(s); + } + } + } if (convertArgs && function->formals.size() > 0) function->basicBlock(0)->prependStatements(extraMoves); @@ -4430,39 +4552,45 @@ public: function->locals.clear(); } -protected: - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *) {} - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { check(e->expr); } - virtual void visitUnop(Unop *e) { check(e->expr); } - virtual void visitBinop(Binop *e) { check(e->left); check(e->right); } - virtual void visitCall(Call *e) { - check(e->base); - for (ExprList *it = e->args; it; it = it->next) - check(it->expr); - } - virtual void visitNew(New *e) { - check(e->base); - for (ExprList *it = e->args; it; it = it->next) - check(it->expr); +private: + void visit(Stmt *s) + { + if (auto e = s->asExp()) { + check(e->expr); + } else if (auto m = s->asMove()) { + check(m->target); check(m->source); + } else if (auto c = s->asCJump()) { + check(c->cond); + } else if (auto r = s->asRet()) { + check(r->expr); + } } - virtual void visitSubscript(Subscript *e) { check(e->base); check(e->index); } - virtual void visitMember(Member *e) { check(e->base); } - virtual void visitExp(Exp *s) { check(s->expr); } - virtual void visitMove(Move *s) { check(s->target); check(s->source); } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { check(s->cond); } - virtual void visitRet(Ret *s) { check(s->expr); } - virtual void visitPhi(Phi *) { - Q_UNREACHABLE(); + + void visit(Expr *e) + { + if (auto c = e->asConvert()) { + check(c->expr); + } else if (auto u = e->asUnop()) { + check(u->expr); + } else if (auto b = e->asBinop()) { + check(b->left); check(b->right); + } else if (auto c = e->asCall()) { + check(c->base); + for (ExprList *it = c->args; it; it = it->next) { + check(it->expr); + } + } else if (auto n = e->asNew()) { + check(n->base); + for (ExprList *it = n->args; it; it = it->next) { + check(it->expr); + } + } else if (auto s = e->asSubscript()) { + check(s->base); check(s->index); + } else if (auto m = e->asMember()) { + check(m->base); + } } -private: void check(Expr *&e) { if (ArgLocal *al = e->asArgLocal()) { if (al->kind == ArgLocal::Local) { @@ -4475,7 +4603,7 @@ private: e = t; } } else { - e->accept(this); + visit(e); } } @@ -4498,7 +4626,7 @@ private: std::vector<int> tempForLocal; }; -class CloneBasicBlock: protected IR::StmtVisitor, protected CloneExpr +class CloneBasicBlock: protected CloneExpr { public: BasicBlock *operator()(IR::BasicBlock *originalBlock) @@ -4506,38 +4634,37 @@ public: block = new BasicBlock(originalBlock->function, 0); for (Stmt *s : originalBlock->statements()) { - s->accept(this); + visit(s); clonedStmt->location = s->location; } return block; } -protected: - virtual void visitExp(Exp *stmt) - { clonedStmt = block->EXP(clone(stmt->expr)); } - - virtual void visitMove(Move *stmt) - { clonedStmt = block->MOVE(clone(stmt->target), clone(stmt->source)); } - - virtual void visitJump(Jump *stmt) - { clonedStmt = block->JUMP(stmt->target); } - - virtual void visitCJump(CJump *stmt) - { clonedStmt = block->CJUMP(clone(stmt->cond), stmt->iftrue, stmt->iffalse); } - - virtual void visitRet(Ret *stmt) - { clonedStmt = block->RET(clone(stmt->expr)); } - - virtual void visitPhi(Phi *stmt) +private: + void visit(Stmt *s) { - Phi *phi = block->function->NewStmt<Phi>(); - clonedStmt = phi; - - phi->targetTemp = clone(stmt->targetTemp); - foreach (Expr *in, stmt->incoming) - phi->incoming.append(clone(in)); - block->appendStatement(phi); + if (auto e = s->asExp()) { + clonedStmt = block->EXP(clone(e->expr)); + } else if (auto m = s->asMove()) { + clonedStmt = block->MOVE(clone(m->target), clone(m->source)); + } else if (auto j = s->asJump()) { + clonedStmt = block->JUMP(j->target); + } else if (auto c = s->asCJump()) { + clonedStmt = block->CJUMP(clone(c->cond), c->iftrue, c->iffalse); + } else if (auto r = s->asRet()) { + clonedStmt = block->RET(clone(r->expr)); + } else if (auto p = s->asPhi()) { + Phi *phi = block->function->NewStmt<Phi>(); + clonedStmt = phi; + + phi->targetTemp = clone(p->targetTemp); + foreach (Expr *in, p->incoming) + phi->incoming.append(clone(in)); + block->appendStatement(phi); + } else { + Q_UNREACHABLE(); + } } private: @@ -4706,13 +4833,20 @@ static void verifyCFG(IR::Function *function) Q_ASSERT(function->basicBlock(bb->index()) == bb); // Check the terminators: - if (Jump *jump = bb->terminator()->asJump()) { + Stmt *terminator = bb->terminator(); + if (terminator == nullptr) { + Stmt *last = bb->statements().last(); + Call *call = last->asExp()->expr->asCall(); + Name *baseName = call->base->asName(); + Q_ASSERT(baseName->builtin == Name::builtin_rethrow); + Q_UNUSED(baseName); + } else if (Jump *jump = terminator->asJump()) { Q_UNUSED(jump); Q_ASSERT(jump->target); Q_ASSERT(!jump->target->isRemoved()); Q_ASSERT(bb->out.size() == 1); Q_ASSERT(bb->out.first() == jump->target); - } else if (CJump *cjump = bb->terminator()->asCJump()) { + } else if (CJump *cjump = terminator->asCJump()) { Q_UNUSED(cjump); Q_ASSERT(bb->out.size() == 2); Q_ASSERT(cjump->iftrue); @@ -4721,7 +4855,7 @@ static void verifyCFG(IR::Function *function) Q_ASSERT(cjump->iffalse); Q_ASSERT(!cjump->iffalse->isRemoved()); Q_ASSERT(cjump->iffalse == bb->out[1]); - } else if (bb->terminator()->asRet()) { + } else if (terminator->asRet()) { Q_ASSERT(bb->out.size() == 0); } else { Q_UNREACHABLE(); @@ -4769,7 +4903,7 @@ static void verifyNoPointerSharing(IR::Function *function) if (!DoVerification) return; - class : public StmtVisitor, public ExprVisitor { + class { public: void operator()(IR::Function *f) { @@ -4777,44 +4911,23 @@ static void verifyNoPointerSharing(IR::Function *function) if (bb->isRemoved()) continue; - for (Stmt *s : bb->statements()) - s->accept(this); + for (Stmt *s : bb->statements()) { + visit(s); + } } } - protected: - virtual void visitExp(Exp *s) { check(s); s->expr->accept(this); } - virtual void visitMove(Move *s) { check(s); s->target->accept(this); s->source->accept(this); } - virtual void visitJump(Jump *s) { check(s); } - virtual void visitCJump(CJump *s) { check(s); s->cond->accept(this); } - virtual void visitRet(Ret *s) { check(s); s->expr->accept(this); } - virtual void visitPhi(Phi *s) + private: + void visit(Stmt *s) { check(s); - s->targetTemp->accept(this); - foreach (Expr *e, s->incoming) - e->accept(this); - } - - virtual void visitConst(Const *e) { check(e); } - virtual void visitString(IR::String *e) { check(e); } - virtual void visitRegExp(IR::RegExp *e) { check(e); } - virtual void visitName(Name *e) { check(e); } - virtual void visitTemp(Temp *e) { check(e); } - virtual void visitArgLocal(ArgLocal *e) { check(e); } - virtual void visitClosure(Closure *e) { check(e); } - virtual void visitConvert(Convert *e) { check(e); e->expr->accept(this); } - virtual void visitUnop(Unop *e) { check(e); e->expr->accept(this); } - virtual void visitBinop(Binop *e) { check(e); e->left->accept(this); e->right->accept(this); } - virtual void visitCall(Call *e) { check(e); e->base->accept(this); check(e->args); } - virtual void visitNew(New *e) { check(e); e->base->accept(this); check(e->args); } - virtual void visitSubscript(Subscript *e) { check(e); e->base->accept(this); e->index->accept(this); } - virtual void visitMember(Member *e) { check(e); e->base->accept(this); } - - void check(ExprList *l) + STMT_VISIT_ALL_KINDS(s); + } + + void visit(Expr *e) { - for (ExprList *it = l; it; it = it->next) - check(it->expr); + check(e); + EXPR_VISIT_ALL_KINDS(e); } private: @@ -4836,7 +4949,7 @@ static void verifyNoPointerSharing(IR::Function *function) V(function); } -class RemoveLineNumbers: public SideEffectsChecker, public StmtVisitor +class RemoveLineNumbers: private SideEffectsChecker { public: static void run(IR::Function *function) @@ -4854,21 +4967,88 @@ public: } private: + ~RemoveLineNumbers() {} + static bool hasSideEffects(Stmt *stmt) { RemoveLineNumbers checker; - stmt->accept(&checker); + if (auto e = stmt->asExp()) { + checker.visit(e->expr); + } else if (auto m = stmt->asMove()) { + checker.visit(m->source); + if (!checker.seenSideEffects()) { + checker.visit(m->target); + } + } else if (auto c = stmt->asCJump()) { + checker.visit(c->cond); + } else if (auto r = stmt->asRet()) { + checker.visit(r->expr); + } 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 {} + void visitTemp(Temp *) Q_DECL_OVERRIDE Q_DECL_FINAL {} }; +void mergeBasicBlocks(IR::Function *function, DefUses *du, DominatorTree *dt) +{ + enum { DebugBlockMerging = 0 }; + + if (function->hasTry) + return; + + showMeTheCode(function, "Before basic block merging"); + + // Now merge a basic block with its successor when there is one outgoing edge, and the + // successor has one incoming edge. + for (int i = 0, ei = function->basicBlockCount(); i != ei; ++i) { + BasicBlock *bb = function->basicBlock(i); + + bb->nextLocation = QQmlJS::AST::SourceLocation(); // make sure appendStatement doesn't mess with the line info + + if (bb->isRemoved()) continue; // the block has been removed, so ignore it + if (bb->out.size() != 1) continue; // more than one outgoing edge + BasicBlock *successor = bb->out.first(); + if (successor->in.size() != 1) continue; // more than one incoming edge + + // Ok, we can merge the two basic blocks. + if (DebugBlockMerging) { + qDebug("Merging L%d into L%d", successor->index(), bb->index()); + } + Q_ASSERT(bb->terminator()->asJump()); + bb->removeStatement(bb->statementCount() - 1); // remove the terminator, and replace it with: + for (Stmt *s : successor->statements()) { + bb->appendStatement(s); // add all statements from the successor to the current basic block + if (auto cjump = s->asCJump()) + cjump->parent = bb; + } + bb->out = successor->out; // set the outgoing edges to the successor's so they're now in sync with our new terminator + for (auto newSuccessor : bb->out) { + for (auto &backlink : newSuccessor->in) { + if (backlink == successor) { + backlink = bb; // for all successors of our successor: set the incoming edges to come from bb, because we'll now jump there. + } + } + } + if (du) { + // all statements in successor have moved to bb, so make sure that the containing blocks + // stored in DefUses get updated (meaning: point to bb) + du->replaceBasicBlock(successor, bb); + } + if (dt) { + // update the immediate dominators to: any block that was dominated by the successor + // will now need to point to bb's immediate dominator. The reason is that bb itself + // won't be anyones immediate dominator, because it had just one outgoing edge. + dt->mergeIntoPredecessor(successor); + } + function->removeBasicBlock(successor); + --i; // re-run on the current basic-block, so any chain gets collapsed. + } + + showMeTheCode(function, "After basic block merging"); + verifyCFG(function); +} + } // anonymous namespace void LifeTimeInterval::setFrom(int from) { @@ -5103,6 +5283,8 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee if (!function->hasTry && !function->hasWith && !function->module->debugMode && doSSA && statementCount <= 300) { // qout << "SSA for " << (function->name ? qPrintable(*function->name) : "<anonymous>") << endl; + mergeBasicBlocks(function, nullptr, nullptr); + ConvertArgLocals(function).toTemps(); showMeTheCode(function, "After converting arguments to locals"); @@ -5178,6 +5360,7 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee } verifyNoPointerSharing(function); + mergeBasicBlocks(function, &defUses, &df); // Basic-block cycles that are unreachable (i.e. for loops in a then-part where the // condition is calculated to be always false) are not yet removed. This will choke the diff --git a/src/qml/debugger/debugger.pri b/src/qml/debugger/debugger.pri index 30a44eedd1..74dcb250a8 100644 --- a/src/qml/debugger/debugger.pri +++ b/src/qml/debugger/debugger.pri @@ -1,21 +1,27 @@ -contains(QT_CONFIG, no-qml-debug):DEFINES += QT_NO_QML_DEBUGGER +contains(QT_CONFIG, no-qml-debug) { + DEFINES += QT_NO_QML_DEBUGGER +} else { + HEADERS += \ + $$PWD/qqmldebugpluginmanager_p.h \ + $$PWD/qqmldebugservicefactory_p.h -SOURCES += \ - $$PWD/qqmldebug.cpp \ - $$PWD/qqmldebugconnector.cpp \ - $$PWD/qqmldebugservice.cpp \ - $$PWD/qqmldebugserviceinterfaces.cpp \ - $$PWD/qqmlabstractprofileradapter.cpp \ - $$PWD/qqmlprofiler.cpp + SOURCES += \ + $$PWD/qqmldebug.cpp \ + $$PWD/qqmldebugconnector.cpp \ + $$PWD/qqmldebugservice.cpp \ + $$PWD/qqmlabstractprofileradapter.cpp \ + $$PWD/qqmlmemoryprofiler.cpp \ + $$PWD/qqmlprofiler.cpp \ + $$PWD/qqmldebugserviceinterfaces.cpp +} HEADERS += \ $$PWD/qqmldebugconnector_p.h \ - $$PWD/qqmldebugpluginmanager_p.h \ $$PWD/qqmldebugservice_p.h \ - $$PWD/qqmldebugservicefactory_p.h \ $$PWD/qqmldebugserviceinterfaces_p.h \ $$PWD/qqmldebugstatesdelegate_p.h \ $$PWD/qqmldebug.h \ + $$PWD/qqmlmemoryprofiler_p.h \ $$PWD/qqmlprofilerdefinitions_p.h \ $$PWD/qqmlabstractprofileradapter_p.h \ $$PWD/qqmlprofiler_p.h diff --git a/src/qml/debugger/qqmlabstractprofileradapter_p.h b/src/qml/debugger/qqmlabstractprofileradapter_p.h index 1104608055..6a05a80f37 100644 --- a/src/qml/debugger/qqmlabstractprofileradapter_p.h +++ b/src/qml/debugger/qqmlabstractprofileradapter_p.h @@ -59,6 +59,8 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_QML_DEBUGGER + class QQmlProfilerService; class Q_QML_PRIVATE_EXPORT QQmlAbstractProfilerAdapter : public QObject, public QQmlProfilerDefinitions { Q_OBJECT @@ -71,13 +73,13 @@ public: virtual ~QQmlAbstractProfilerAdapter() {} void setService(QQmlProfilerService *new_service) { service = new_service; } - virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages) = 0; + virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages, bool trackLocations) = 0; void startProfiling(quint64 features); void stopProfiling(); - void reportData() { emit dataRequested(); } + void reportData(bool trackLocations) { emit dataRequested(trackLocations); } void stopWaiting() { waiting = false; } void startWaiting() { waiting = true; } @@ -94,7 +96,7 @@ signals: void profilingDisabled(); void profilingDisabledWhileWaiting(); - void dataRequested(); + void dataRequested(bool trackLocations); void referenceTimeKnown(const QElapsedTimer &timer); protected: @@ -114,6 +116,8 @@ public: #define QQmlAbstractProfilerAdapterFactory_iid "org.qt-project.Qt.QQmlAbstractProfilerAdapterFactory" +#endif // QT_NO_QML_DEBUGGER + QT_END_NAMESPACE #endif // QQMLABSTRACTPROFILERADAPTER_P_H diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp index 9276bd0544..386fb60b3a 100644 --- a/src/qml/debugger/qqmldebug.cpp +++ b/src/qml/debugger/qqmldebug.cpp @@ -47,15 +47,11 @@ 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 } /*! @@ -105,11 +101,7 @@ QStringList QQmlDebuggingEnabler::profilerServices() */ void QQmlDebuggingEnabler::setServices(const QStringList &services) { -#ifndef QQML_NO_DEBUG_PROTOCOL QQmlDebugConnector::setServices(services); -#else - Q_UNUSED(services); -#endif } /*! @@ -172,16 +164,9 @@ bool QQmlDebuggingEnabler::connectToLocalDebugger(const QString &socketFileName, bool QQmlDebuggingEnabler::startDebugConnector(const QString &pluginName, const QVariantHash &configuration) { -#ifndef QQML_NO_DEBUG_PROTOCOL QQmlDebugConnector::setPluginKey(pluginName); QQmlDebugConnector *connector = QQmlDebugConnector::instance(); - if (connector) - return connector->open(configuration); -#else - Q_UNUSED(pluginName); - Q_UNUSED(configuration); -#endif - return false; + return connector ? connector->open(configuration) : false; } QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmldebug.h b/src/qml/debugger/qqmldebug.h index 660b9e4d46..fb41039867 100644 --- a/src/qml/debugger/qqmldebug.h +++ b/src/qml/debugger/qqmldebug.h @@ -46,6 +46,7 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_QML_DEBUGGER struct Q_QML_EXPORT QQmlDebuggingEnabler { @@ -77,6 +78,8 @@ static QQmlDebuggingEnabler qQmlEnableDebuggingHelper(false); static QQmlDebuggingEnabler qQmlEnableDebuggingHelper(true); #endif +#endif + QT_END_NAMESPACE #endif // QQMLDEBUG_H diff --git a/src/qml/debugger/qqmldebugconnector_p.h b/src/qml/debugger/qqmldebugconnector_p.h index 05755250bd..0d3e2e2e47 100644 --- a/src/qml/debugger/qqmldebugconnector_p.h +++ b/src/qml/debugger/qqmldebugconnector_p.h @@ -59,6 +59,29 @@ QT_BEGIN_NAMESPACE +#ifdef QT_NO_QML_DEBUGGER + +class Q_QML_PRIVATE_EXPORT QQmlDebugConnector +{ +public: + static QQmlDebugConnector *instance() { return nullptr; } + + template<class Service> + static Service *service() { return nullptr; } + + bool hasEngine(QJSEngine *) const { return false; } + void addEngine(QJSEngine *) {} + void removeEngine(QJSEngine *) {} + + bool open(const QVariantHash &configuration = QVariantHash()) + { + Q_UNUSED(configuration); + return false; + } +}; + +#else + class QQmlDebugService; class Q_QML_PRIVATE_EXPORT QQmlDebugConnector : public QObject { @@ -106,6 +129,8 @@ public: #define QQmlDebugConnectorFactory_iid "org.qt-project.Qt.QQmlDebugConnectorFactory" +#endif + QT_END_NAMESPACE #endif // QQMLDEBUGCONNECTOR_H diff --git a/src/qml/debugger/qqmldebugservice.cpp b/src/qml/debugger/qqmldebugservice.cpp index b780735f48..b576c3bb85 100644 --- a/src/qml/debugger/qqmldebugservice.cpp +++ b/src/qml/debugger/qqmldebugservice.cpp @@ -132,7 +132,6 @@ public: int nextId; -private slots: void remove(QObject *obj); }; } @@ -163,7 +162,7 @@ int QQmlDebugService::idForObject(QObject *object) int id = hash->nextId++; hash->ids.insert(id, object); iter = hash->objects.insert(object, id); - connect(object, SIGNAL(destroyed(QObject*)), hash, SLOT(remove(QObject*))); + connect(object, &QObject::destroyed, hash, &ObjectReferenceHash::remove); } return iter.value(); } @@ -176,36 +175,6 @@ const QHash<int, QObject *> &QQmlDebugService::objectsForIds() return objectReferenceHash()->ids; } -void QQmlDebugService::stateAboutToBeChanged(State) -{ -} - -void QQmlDebugService::stateChanged(State) -{ -} - -void QQmlDebugService::messageReceived(const QByteArray &) -{ -} - -void QQmlDebugService::engineAboutToBeAdded(QJSEngine *engine) -{ - emit attachedToEngine(engine); -} - -void QQmlDebugService::engineAboutToBeRemoved(QJSEngine *engine) -{ - emit detachedFromEngine(engine); -} - -void QQmlDebugService::engineAdded(QJSEngine *) -{ -} - -void QQmlDebugService::engineRemoved(QJSEngine *) -{ -} - QT_END_NAMESPACE #include "qqmldebugservice.moc" diff --git a/src/qml/debugger/qqmldebugservice_p.h b/src/qml/debugger/qqmldebugservice_p.h index 9ddc692ecc..42a57a39f2 100644 --- a/src/qml/debugger/qqmldebugservice_p.h +++ b/src/qml/debugger/qqmldebugservice_p.h @@ -58,6 +58,8 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_QML_DEBUGGER + class QJSEngine; class QQmlDebugServicePrivate; @@ -65,7 +67,6 @@ class Q_QML_PRIVATE_EXPORT QQmlDebugService : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QQmlDebugService) - Q_DISABLE_COPY(QQmlDebugService) public: ~QQmlDebugService(); @@ -77,14 +78,15 @@ public: State state() const; void setState(State newState); - virtual void stateAboutToBeChanged(State); - virtual void stateChanged(State); - virtual void messageReceived(const QByteArray &); + virtual void stateAboutToBeChanged(State) {} + virtual void stateChanged(State) {} + virtual void messageReceived(const QByteArray &) {} + + virtual void engineAboutToBeAdded(QJSEngine *engine) { emit attachedToEngine(engine); } + virtual void engineAboutToBeRemoved(QJSEngine *engine) { emit detachedFromEngine(engine); } - virtual void engineAboutToBeAdded(QJSEngine *); - virtual void engineAboutToBeRemoved(QJSEngine *); - virtual void engineAdded(QJSEngine *); - virtual void engineRemoved(QJSEngine *); + virtual void engineAdded(QJSEngine *) {} + virtual void engineRemoved(QJSEngine *) {} static const QHash<int, QObject *> &objectsForIds(); static int idForObject(QObject *); @@ -101,6 +103,8 @@ signals: void messagesToClient(const QString &name, const QList<QByteArray> &messages); }; +#endif + QT_END_NAMESPACE #endif // QQMLDEBUGSERVICE_H diff --git a/src/qml/debugger/qqmldebugserviceinterfaces_p.h b/src/qml/debugger/qqmldebugserviceinterfaces_p.h index 8f66779872..ca6293c3ec 100644 --- a/src/qml/debugger/qqmldebugserviceinterfaces_p.h +++ b/src/qml/debugger/qqmldebugserviceinterfaces_p.h @@ -62,7 +62,46 @@ QT_BEGIN_NAMESPACE -class Q_QML_PRIVATE_EXPORT QV4DebugService : protected QQmlDebugService +class QWindow; +class QQuickWindow; + +#ifdef QT_NO_QML_DEBUGGER + +struct QV4DebugService +{ + void signalEmitted(const QString &) {} +}; + +struct QQmlProfilerService +{ + void startProfiling(QJSEngine *engine, quint64 features = std::numeric_limits<quint64>::max()) + { + Q_UNUSED(engine); + Q_UNUSED(features); + } + + void stopProfiling(QJSEngine *) {} +}; + +struct QQmlEngineDebugService +{ + void objectCreated(QJSEngine *, QObject *) {} + virtual void setStatesDelegate(QQmlDebugStatesDelegate *) {} +}; + +struct QQmlInspectorService { + void addWindow(QQuickWindow *) {} + void setParentWindow(QQuickWindow *, QWindow *) {} + void removeWindow(QQuickWindow *) {} +}; + +struct QDebugMessageService {}; +struct QQmlEngineControlService {}; +struct QQmlNativeDebugService {}; + +#else + +class Q_QML_PRIVATE_EXPORT QV4DebugService : public QQmlDebugService { Q_OBJECT public: @@ -77,7 +116,7 @@ protected: QQmlDebugService(s_key, version, parent) {} }; -class Q_QML_PRIVATE_EXPORT QQmlProfilerService : protected QQmlDebugService +class Q_QML_PRIVATE_EXPORT QQmlProfilerService : public QQmlDebugService { Q_OBJECT public: @@ -99,7 +138,7 @@ protected: QQmlDebugService(s_key, version, parent) {} }; -class Q_QML_PRIVATE_EXPORT QQmlEngineDebugService : protected QQmlDebugService +class Q_QML_PRIVATE_EXPORT QQmlEngineDebugService : public QQmlDebugService { Q_OBJECT public: @@ -117,9 +156,7 @@ protected: QQmlBoundSignal *nextSignal(QQmlBoundSignal *prev) { return prev->m_nextSignal; } }; -class QWindow; -class QQuickWindow; -class Q_QML_PRIVATE_EXPORT QQmlInspectorService : protected QQmlDebugService +class Q_QML_PRIVATE_EXPORT QQmlInspectorService : public QQmlDebugService { Q_OBJECT public: @@ -136,7 +173,7 @@ protected: QQmlDebugService(s_key, version, parent) {} }; -class Q_QML_PRIVATE_EXPORT QDebugMessageService : protected QQmlDebugService +class Q_QML_PRIVATE_EXPORT QDebugMessageService : public QQmlDebugService { Q_OBJECT public: @@ -151,7 +188,7 @@ protected: QQmlDebugService(s_key, version, parent) {} }; -class Q_QML_PRIVATE_EXPORT QQmlEngineControlService : protected QQmlDebugService +class Q_QML_PRIVATE_EXPORT QQmlEngineControlService : public QQmlDebugService { Q_OBJECT public: @@ -165,7 +202,7 @@ protected: }; -class Q_QML_PRIVATE_EXPORT QQmlNativeDebugService : protected QQmlDebugService +class Q_QML_PRIVATE_EXPORT QQmlNativeDebugService : public QQmlDebugService { Q_OBJECT @@ -178,6 +215,8 @@ protected: static const QString s_key; }; +#endif + QT_END_NAMESPACE #endif // QQMLDEBUGSERVICEINTERFACES_P_H diff --git a/src/qml/debugger/qqmldebugstatesdelegate_p.h b/src/qml/debugger/qqmldebugstatesdelegate_p.h index 42c4e94b50..95f727fb2d 100644 --- a/src/qml/debugger/qqmldebugstatesdelegate_p.h +++ b/src/qml/debugger/qqmldebugstatesdelegate_p.h @@ -57,6 +57,11 @@ QT_BEGIN_NAMESPACE +#ifdef QT_NO_QML_DEBUGGER + +class QQmlDebugStatesDelegate {}; + +#else class QQmlContext; class QQmlProperty; @@ -90,6 +95,8 @@ private: Q_DISABLE_COPY(QQmlDebugStatesDelegate) }; +#endif + QT_END_NAMESPACE #endif // QQMLDEBUGSTATESDELEGATE_P_H diff --git a/src/qml/qml/qqmlmemoryprofiler.cpp b/src/qml/debugger/qqmlmemoryprofiler.cpp index d9e121bb1b..60f6d96eaf 100644 --- a/src/qml/qml/qqmlmemoryprofiler.cpp +++ b/src/qml/debugger/qqmlmemoryprofiler.cpp @@ -97,12 +97,9 @@ static bool openLibrary() return state == Loaded; } -QQmlMemoryScope::QQmlMemoryScope(const QUrl &url) : pushed(false) +QQmlMemoryScope::QQmlMemoryScope(const QUrl &url) + : QQmlMemoryScope(url.path().toUtf8().constData()) { - if (openLibrary() && memprofile_is_enabled()) { - memprofile_push_location(url.path().toUtf8().constData(), 0); - pushed = true; - } } QQmlMemoryScope::QQmlMemoryScope(const char *string) : pushed(false) diff --git a/src/qml/qml/qqmlmemoryprofiler_p.h b/src/qml/debugger/qqmlmemoryprofiler_p.h index 4b0ba823ba..59f08704ca 100644 --- a/src/qml/qml/qqmlmemoryprofiler_p.h +++ b/src/qml/debugger/qqmlmemoryprofiler_p.h @@ -55,6 +55,13 @@ QT_BEGIN_NAMESPACE +#ifdef QT_NO_QML_DEBUGGER + +#define QML_MEMORY_SCOPE_URL(url) +#define QML_MEMORY_SCOPE_STRING(s) + +#else + class QUrl; class Q_QML_PRIVATE_EXPORT QQmlMemoryScope @@ -83,5 +90,7 @@ public: #define QML_MEMORY_SCOPE_URL(url) QQmlMemoryScope _qml_memory_scope(url) #define QML_MEMORY_SCOPE_STRING(s) QQmlMemoryScope _qml_memory_scope(s) +#endif + QT_END_NAMESPACE #endif // QQMLMEMORYPROFILER_H diff --git a/src/qml/debugger/qqmlprofiler.cpp b/src/qml/debugger/qqmlprofiler.cpp index 629d5cb7b8..ffba731b13 100644 --- a/src/qml/debugger/qqmlprofiler.cpp +++ b/src/qml/debugger/qqmlprofiler.cpp @@ -59,19 +59,21 @@ void QQmlProfiler::startProfiling(quint64 features) void QQmlProfiler::stopProfiling() { featuresEnabled = false; - reportData(); + reportData(true); + m_locations.clear(); } -void QQmlProfiler::reportData() +void QQmlProfiler::reportData(bool trackLocations) { LocationHash resolved; resolved.reserve(m_locations.size()); - for (auto it = m_locations.constBegin(), end = m_locations.constEnd(); it != end; ++it) - resolved.insert(it.key(), it.value()); - - // This unrefs all the objects. We have to make sure we do this in the GUI thread. Also, it's - // a good idea to release the memory before creating the packets to be sent. - m_locations.clear(); + for (auto it = m_locations.begin(), end = m_locations.end(); it != end; ++it) { + if (!trackLocations || !it->sent) { + resolved.insert(it.key(), it.value()); + if (trackLocations) + it->sent = true; + } + } QVector<QQmlProfilerData> data; data.swap(m_data); diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 1380599fb7..6643695d11 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -55,7 +55,6 @@ #include <private/qqmlboundsignal_p.h> #include <private/qfinitestack_p.h> #include <private/qqmlbinding_p.h> -#include <private/qqmlcompiler_p.h> #include "qqmlprofilerdefinitions_p.h" #include "qqmlabstractprofileradapter_p.h" @@ -64,6 +63,54 @@ QT_BEGIN_NAMESPACE +#ifdef QT_NO_QML_DEBUGGER + +#define Q_QML_PROFILE_IF_ENABLED(feature, profiler, Code) +#define Q_QML_PROFILE(feature, profiler, Method) +#define Q_QML_OC_PROFILE(member, Code) + +struct QQmlProfiler {}; + +struct QQmlBindingProfiler +{ + QQmlBindingProfiler(quintptr, QQmlBinding *, QV4::FunctionObject *) {} +}; + +struct QQmlHandlingSignalProfiler +{ + QQmlHandlingSignalProfiler(quintptr, QQmlBoundSignalExpression *) {} +}; + +struct QQmlCompilingProfiler +{ + QQmlCompilingProfiler(quintptr, QQmlDataBlob *) {} +}; + +struct QQmlVmeProfiler { + QQmlVmeProfiler() {} + + void init(quintptr, int) {} + + const QV4::CompiledData::Object *pop() { return nullptr; } + void push(const QV4::CompiledData::Object *) {} + + static const quintptr profiler = 0; +}; + +struct QQmlObjectCreationProfiler +{ + QQmlObjectCreationProfiler(quintptr, const QV4::CompiledData::Object *) {} + void update(QV4::CompiledData::CompilationUnit *, const QV4::CompiledData::Object *, + const QString &, const QUrl &) {} +}; + +struct QQmlObjectCompletionProfiler +{ + QQmlObjectCompletionProfiler(QQmlVmeProfiler *) {} +}; + +#else + #define Q_QML_PROFILE_IF_ENABLED(feature, profiler, Code)\ if (profiler && (profiler->featuresEnabled & (1 << feature))) {\ Code;\ @@ -73,6 +120,9 @@ QT_BEGIN_NAMESPACE #define Q_QML_PROFILE(feature, profiler, Method)\ Q_QML_PROFILE_IF_ENABLED(feature, profiler, profiler->Method) +#define Q_QML_OC_PROFILE(member, Code)\ + Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, member.profiler, Code) + // 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 @@ -95,7 +145,7 @@ struct Q_AUTOTEST_EXPORT QQmlProfilerData : public QQmlProfilerDefinitions Q_DECLARE_TYPEINFO(QQmlProfilerData, Q_MOVABLE_TYPE); -class QQmlProfiler : public QObject, public QQmlProfilerDefinitions { +class Q_QML_PRIVATE_EXPORT QQmlProfiler : public QObject, public QQmlProfilerDefinitions { Q_OBJECT public: @@ -146,26 +196,27 @@ public: // Unfortunately we have to resolve the locations right away because the QML context might not // be available anymore when we send the data. struct RefLocation : public Location { - RefLocation() : Location(), locationType(MaximumRangeType), ref(nullptr) + RefLocation() : Location(), locationType(MaximumRangeType), ref(nullptr), sent(false) {} RefLocation(QQmlBinding *binding, QV4::FunctionObject *function) : Location(function->sourceLocation()), locationType(Binding), - ref(new BindingRefCount(binding), QQmlRefPointer<QQmlRefCount>::Adopt) + ref(new BindingRefCount(binding), QQmlRefPointer<QQmlRefCount>::Adopt), sent(false) {} - RefLocation(QQmlCompiledData *ref, const QUrl &url, const QV4::CompiledData::Object *obj, + RefLocation(QV4::CompiledData::CompilationUnit *ref, const QUrl &url, const QV4::CompiledData::Object *obj, const QString &type) : Location(QQmlSourceLocation(type, obj->location.line, obj->location.column), url), - locationType(Creating), ref(ref) + locationType(Creating), ref(ref), sent(false) {} RefLocation(QQmlBoundSignalExpression *ref) : - Location(ref->sourceLocation()), locationType(HandlingSignal), ref(ref) + Location(ref->sourceLocation()), locationType(HandlingSignal), ref(ref), sent(false) {} RefLocation(QQmlDataBlob *ref) : - Location(QQmlSourceLocation(), ref->url()), locationType(Compiling), ref(ref) + Location(QQmlSourceLocation(), ref->url()), locationType(Compiling), ref(ref), + sent(false) {} bool isValid() const @@ -175,6 +226,7 @@ public: RangeType locationType; QQmlRefPointer<QQmlRefCount> ref; + bool sent; }; typedef QHash<quintptr, Location> LocationHash; @@ -217,11 +269,6 @@ public: location = RefLocation(expression); } - void startCreating() - { - m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(), 1 << RangeStart, Creating)); - } - void startCreating(const QV4::CompiledData::Object *obj) { m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(), @@ -229,14 +276,10 @@ public: Creating, id(obj))); } - void updateCreating(const QV4::CompiledData::Object *obj, QQmlCompiledData *ref, + void updateCreating(const QV4::CompiledData::Object *obj, QV4::CompiledData::CompilationUnit *ref, const QUrl &url, const QString &type) { quintptr locationId(id(obj)); - m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(), - (1 << RangeLocation | 1 << RangeData), - Creating, locationId)); - RefLocation &location = m_locations[locationId]; if (!location.isValid()) location = RefLocation(ref, url, obj, type); @@ -258,10 +301,9 @@ public: return reinterpret_cast<quintptr>(pointer); } -public slots: void startProfiling(quint64 features); void stopProfiling(); - void reportData(); + void reportData(bool trackLocations); void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: @@ -357,15 +399,13 @@ private: QFiniteStack<const QV4::CompiledData::Object *> ranges; }; -#define Q_QML_OC_PROFILE(member, Code)\ - Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, member.profiler, Code) - class QQmlObjectCreationProfiler { public: - QQmlObjectCreationProfiler(QQmlProfiler *profiler) : profiler(profiler) + QQmlObjectCreationProfiler(QQmlProfiler *profiler, const QV4::CompiledData::Object *obj) + : profiler(profiler) { - Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler, startCreating()); + Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler, startCreating(obj)); } ~QQmlObjectCreationProfiler() @@ -373,7 +413,7 @@ public: Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler, endRange<QQmlProfilerDefinitions::Creating>()); } - void update(QQmlCompiledData *ref, const QV4::CompiledData::Object *obj, + void update(QV4::CompiledData::CompilationUnit *ref, const QV4::CompiledData::Object *obj, const QString &typeName, const QUrl &url) { profiler->updateCreating(obj, ref, url, typeName); @@ -406,4 +446,6 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(QVector<QQmlProfilerData>) Q_DECLARE_METATYPE(QQmlProfiler::LocationHash) +#endif // QT_NO_QML_DEBUGGER + #endif // QQMLPROFILER_P_H diff --git a/src/qml/debugger/qqmlprofilerdefinitions_p.h b/src/qml/debugger/qqmlprofilerdefinitions_p.h index 2b2eda22e1..c6ae4593a9 100644 --- a/src/qml/debugger/qqmlprofilerdefinitions_p.h +++ b/src/qml/debugger/qqmlprofilerdefinitions_p.h @@ -56,6 +56,8 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_QML_DEBUGGER + struct QQmlProfilerDefinitions { enum Message { Event, @@ -161,6 +163,8 @@ struct QQmlProfilerDefinitions { }; }; +#endif // QT_NO_QML_DEBUGGER + QT_END_NAMESPACE #endif diff --git a/src/qml/doc/snippets/qml/qtLater.qml b/src/qml/doc/snippets/qml/qtLater.qml new file mode 100644 index 0000000000..e2bc02edb4 --- /dev/null +++ b/src/qml/doc/snippets/qml/qtLater.qml @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +//![0] +import QtQuick 2.0 + +Rectangle { + width: 480 + height: 320 + + property int callsToUpdateMinimumWidth: 0 + property bool optimize: true + + property int currentTextModel: 0 + property var columnTexts: [ + ["Click on either", "rectangle above", "and note how the counter", "below updates", "significantly faster using the", "regular (non-optimized)", "implementation"], + ["The width", "of this column", "is", "no wider than the", "widest item"], + ["Note how using Qt.callLater()", "the minimum width is", "calculated a bare-minimum", "number", "of times"] + ] + + Text { + x: 20; y: 280 + text: "Times minimum width has been calculated: " + callsToUpdateMinimumWidth + } + + Row { + y: 25; spacing: 30; anchors.horizontalCenter: parent.horizontalCenter + Rectangle { + width: 200; height: 50; color: "lightgreen" + Text { text: "Optimized behavior\nusing Qt.callLater()"; anchors.centerIn: parent } + MouseArea { anchors.fill: parent; onClicked: { optimize = true; currentTextModel++ } } + } + Rectangle { + width: 200; height: 50; color: "lightblue" + Text { text: "Regular behavior"; anchors.centerIn: parent} + MouseArea { anchors.fill: parent; onClicked: { optimize = false; currentTextModel++ } } + } + } + + Column { + id: column + anchors.centerIn: parent + + onChildrenChanged: optimize ? Qt.callLater(updateMinimumWidth) : updateMinimumWidth() + + property int widestChild + function updateMinimumWidth() { + callsToUpdateMinimumWidth++ + var w = 0; + for (var i in children) { + var child = children[i]; + if (child.implicitWidth > w) { + w = child.implicitWidth; + } + } + + widestChild = w; + } + + Repeater { + id: repeater + model: columnTexts[currentTextModel%3] + delegate: Text { + color: "white" + text: modelData + width: column.widestChild + horizontalAlignment: Text.Center + Rectangle { anchors.fill: parent; z: -1; color: index%2 ? "gray" : "darkgray" } + } + } + } +} +//![0] diff --git a/src/qml/doc/src/javascript/functionlist.qdoc b/src/qml/doc/src/javascript/functionlist.qdoc index 7f0e844b65..b9a25a2440 100644 --- a/src/qml/doc/src/javascript/functionlist.qdoc +++ b/src/qml/doc/src/javascript/functionlist.qdoc @@ -174,6 +174,8 @@ \li charAt(pos) \li charCodeAt(pos) \li concat([string1 [, string2 [, ...]]]) + \li endsWith(searchString [, endPosition ]) // ECMAScript 6: Added in Qt 5.8 + \li includes(searchString [, position ]) // ECMAScript 6: Added in 5.8 \li indexOf(searchString ,position) \li lastIndexOf(searchString, position) \li localeCompare(that) @@ -182,6 +184,7 @@ \li search(regexp) \li slice(start, end) \li split(separator, limit) + \li startsWith(searchString [, position ]) // ECMAScript 6: Added in Qt 5.8 \li substring(start, end) \li toLowerCase() \li toLocaleLowerCase() @@ -228,6 +231,26 @@ \li \l {Number::toLocaleString}{toLocaleString(locale, format, precision)} \endlist + \section2 The Number Object + + \section3 Value Properties + + \list + \li NaN + \li NEGATIVE_INFINITY + \li POSITIVE_INFINITY + \li MAX_VALUE + \li MIN_VALUE + \li EPSILON // ECMAScript 6: Added in Qt 5.8 + \endlist + + \section3 Function Properties + + \list + \li isFinite(x) // ECMAScript 6: Added in Qt 5.8 + \li isNaN(x) // ECMAScript 6: Added in Qt 5.8 + \endlist + \section1 The Math Object \section2 Value Properties @@ -261,6 +284,7 @@ \li pow(x, y) \li random() \li round(x) + \li sign(x) // ECMAScript 6: Added in Qt 5.8 \li sin(x) \li sqrt(x) \li tan(x) diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 3da1aaa010..08e4f0a8c0 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -79,14 +79,70 @@ void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) } } -QV4::ExecutableAllocator::ChunkOfPages *CompilationUnit::chunkForFunction(int functionIndex) +void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) { - if (functionIndex < 0 || functionIndex >= codeRefs.count()) - return 0; - JSC::ExecutableMemoryHandle *handle = codeRefs[functionIndex].executableMemory(); - if (!handle) - return 0; - return handle->chunk(); + const int codeAlignment = 16; + quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize); + Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); + for (int i = 0; i < codeRefs.size(); ++i) { + CompiledData::Function *compiledFunction = const_cast<CompiledData::Function *>(unit->functionAt(i)); + compiledFunction->codeOffset = offset; + compiledFunction->codeSize = codeRefs.at(i).size(); + offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize); + } +} + +bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) +{ + Q_ASSERT(device->pos() == unit->unitSize); + Q_ASSERT(device->atEnd()); + Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); + + QByteArray padding; + + for (int i = 0; i < codeRefs.size(); ++i) { + const CompiledData::Function *compiledFunction = unit->functionAt(i); + + if (device->pos() > qint64(compiledFunction->codeOffset)) { + *errorString = QStringLiteral("Invalid state of cache file to write."); + return false; + } + + const quint64 paddingSize = compiledFunction->codeOffset - device->pos(); + padding.fill(0, paddingSize); + qint64 written = device->write(padding); + if (written != padding.size()) { + *errorString = device->errorString(); + return false; + } + + const void *undecoratedCodePtr = codeRefs.at(i).code().dataLocation(); + written = device->write(reinterpret_cast<const char *>(undecoratedCodePtr), compiledFunction->codeSize); + if (written != qint64(compiledFunction->codeSize)) { + *errorString = device->errorString(); + return false; + } + } + return true; +} + +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + Q_UNUSED(errorString); + Q_ASSERT(codeRefs.isEmpty()); + codeRefs.reserve(data->functionTableSize); + + const char *basePtr = reinterpret_cast<const char *>(data); + + for (uint i = 0; i < data->functionTableSize; ++i) { + const CompiledData::Function *compiledFunction = data->functionAt(i); + void *codePtr = const_cast<void *>(reinterpret_cast<const void *>(basePtr + compiledFunction->codeOffset)); + JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr)); + JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize); + codeRefs.append(codeRef); + } + + return true; } const Assembler::VoidType Assembler::Void; diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index a5028bc683..669b0b2ff4 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -63,6 +63,8 @@ #include <config.h> #include <wtf/Vector.h> +#include <climits> + #if ENABLE(ASSEMBLER) #include <assembler/MacroAssembler.h> @@ -73,20 +75,16 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { -#define OP(op) \ - { isel_stringIfy(op), op, 0, 0, 0 } -#define OPCONTEXT(op) \ - { isel_stringIfy(op), 0, op, 0, 0 } - class InstructionSelection; struct CompilationUnit : public QV4::CompiledData::CompilationUnit { virtual ~CompilationUnit(); - virtual void linkBackendToEngine(QV4::ExecutionEngine *engine); - - virtual QV4::ExecutableAllocator::ChunkOfPages *chunkForFunction(int functionIndex); + void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; + void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; + bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; + bool memoryMapCode(QString *errorString); // Coderef + execution engine @@ -94,14 +92,6 @@ struct CompilationUnit : public QV4::CompiledData::CompilationUnit QList<QVector<QV4::Primitive> > constantValues; }; -struct RelativeCall { - JSC::MacroAssembler::Address addr; - - explicit RelativeCall(const JSC::MacroAssembler::Address &addr) - : addr(addr) - {} -}; - struct LookupCall { JSC::MacroAssembler::Address addr; uint getterSetterOffset; @@ -112,6 +102,13 @@ struct LookupCall { {} }; +struct RuntimeCall { + JSC::MacroAssembler::Address addr; + + inline RuntimeCall(uint offset = uint(INT_MIN)); + bool isValid() const { return addr.offset >= 0; } +}; + template <typename T> struct ExceptionCheck { enum { NeedsCheck = 1 }; @@ -321,12 +318,12 @@ public: typedef JSC::FunctionPtr FunctionPtr; - struct CallToLink { - Call call; - FunctionPtr externalFunction; +#ifndef QT_NO_DEBUG + struct CallInfo { Label label; const char* functionName; }; +#endif struct PointerToValue { PointerToValue(IR::Expr *value) : value(value) @@ -344,32 +341,23 @@ public: IR::Expr *value; }; - struct ReentryBlock { - ReentryBlock(IR::BasicBlock *b) : block(b) {} - IR::BasicBlock *block; - }; - - void callAbsolute(const char* functionName, FunctionPtr function) { - CallToLink ctl; - ctl.call = call(); - ctl.externalFunction = function; - ctl.functionName = functionName; - ctl.label = label(); - _callsToLink.append(ctl); - } - - void callAbsolute(const char* /*functionName*/, Address addr) { - call(addr); - } - - void callAbsolute(const char* /*functionName*/, const RelativeCall &relativeCall) + void callAbsolute(const char* /*functionName*/, const LookupCall &lookupCall) { - call(relativeCall.addr); + call(lookupCall.addr); } - void callAbsolute(const char* /*functionName*/, const LookupCall &lookupCall) + void callAbsolute(const char *functionName, const RuntimeCall &runtimeCall) { - call(lookupCall.addr); + call(runtimeCall.addr); +#ifndef QT_NO_DEBUG + // the code below is to get proper function names in the disassembly + CallInfo info; + info.functionName = functionName; + info.label = label(); + _callInfos.append(info); +#else + Q_UNUSED(functionName) +#endif } void registerBlock(IR::BasicBlock*, IR::BasicBlock *nextBlock); @@ -441,13 +429,6 @@ public: move(source, dest); } - void loadArgumentInRegister(TrustedImmPtr ptr, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - move(TrustedImmPtr(ptr), dest); - } - void loadArgumentInRegister(const Pointer& ptr, RegisterID dest, int argumentNumber) { Q_UNUSED(argumentNumber); @@ -457,7 +438,7 @@ public: void loadArgumentInRegister(PointerToValue temp, RegisterID dest, int argumentNumber) { if (!temp.value) { - loadArgumentInRegister(TrustedImmPtr(0), dest, argumentNumber); + move(TrustedImmPtr(0), dest); } else { Pointer addr = toAddress(dest, temp.value, argumentNumber); loadArgumentInRegister(addr, dest, argumentNumber); @@ -476,15 +457,6 @@ public: loadArgumentInRegister(addr, dest, argumentNumber); } - void loadArgumentInRegister(ReentryBlock block, RegisterID dest, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - Q_ASSERT(block.block); - DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), dest); - addPatch(patch, block.block); - } - #ifdef VALUE_FITS_IN_REGISTER void loadArgumentInRegister(IR::Temp* temp, RegisterID dest, int argumentNumber) { @@ -544,11 +516,6 @@ public: } #endif - void loadArgumentInRegister(QV4::String* string, RegisterID dest, int argumentNumber) - { - loadArgumentInRegister(TrustedImmPtr(string), dest, argumentNumber); - } - void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest, int argumentNumber) { Q_UNUSED(argumentNumber); @@ -705,34 +672,6 @@ public: loadArgumentOnStack<StackSlot>(ptr, argumentNumber); } - template <int StackSlot> - void loadArgumentOnStack(ReentryBlock block, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - Q_ASSERT(block.block); - DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), ScratchRegister); - poke(ScratchRegister, StackSlot); - addPatch(patch, block.block); - } - - template <int StackSlot> - void loadArgumentOnStack(TrustedImmPtr ptr, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - move(TrustedImmPtr(ptr), ScratchRegister); - poke(ScratchRegister, StackSlot); - } - - template <int StackSlot> - void loadArgumentOnStack(QV4::String* name, int argumentNumber) - { - Q_UNUSED(argumentNumber); - - poke(TrustedImmPtr(name), StackSlot); - } - void loadDouble(IR::Expr *source, FPRegisterID dest) { IR::Temp *sourceTemp = source->asTemp(); @@ -1154,9 +1093,8 @@ public: // it's not in signed int range, so load it as a double, and truncate it down loadDouble(addr, FPGpr0); - static const double magic = double(INT_MAX) + 1; - move(TrustedImmPtr(&magic), scratchReg); - subDouble(Address(scratchReg, 0), FPGpr0); + Address inversionAddress = constantTable().loadValueAddress(QV4::Primitive::fromDouble(double(INT_MAX) + 1), scratchReg); + subDouble(inversionAddress, FPGpr0); Jump canNeverHappen = branchTruncateDoubleToUint32(FPGpr0, scratchReg); canNeverHappen.link(this); or32(TrustedImm32(1 << 31), scratchReg); @@ -1184,7 +1122,9 @@ private: IR::Function *_function; QHash<IR::BasicBlock *, Label> _addrs; QHash<IR::BasicBlock *, QVector<Jump> > _patches; - QList<CallToLink> _callsToLink; +#ifndef QT_NO_DEBUG + QVector<CallInfo> _callInfos; +#endif struct DataLabelPatch { DataLabelPtr dataLabel; @@ -1245,24 +1185,21 @@ void Assembler::copyValue(Result result, IR::Expr* source) } } +inline RuntimeCall::RuntimeCall(uint offset) + : addr(Assembler::EngineRegister, offset + qOffsetOf(QV4::ExecutionEngine, runtime)) +{ +} + template <typename T> inline bool prepareCall(T &, Assembler *) { return true; } -template <> inline bool prepareCall(RelativeCall &relativeCall, Assembler *as) -{ - as->loadPtr(Assembler::Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - as->loadPtr(Assembler::Address(Assembler::ScratchRegister, qOffsetOf(QV4::Heap::ExecutionContext, lookups)), - relativeCall.addr.base); - return true; -} - template <> inline bool prepareCall(LookupCall &lookupCall, Assembler *as) { // IMPORTANT! See generateLookupCall in qv4isel_masm_p.h for details! - // same as prepareCall(RelativeCall ....) : load the table from the context + // load the table from the context as->loadPtr(Assembler::Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); as->loadPtr(Assembler::Address(Assembler::ScratchRegister, qOffsetOf(QV4::Heap::ExecutionContext, lookups)), lookupCall.addr.base); diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp index 50b6cec975..c09fc6fdca 100644 --- a/src/qml/jit/qv4binop.cpp +++ b/src/qml/jit/qv4binop.cpp @@ -45,14 +45,14 @@ using namespace QV4; using namespace JIT; #define OP(op) \ - { isel_stringIfy(op), op, 0, 0, 0 } + { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, 0, 0 } #define OPCONTEXT(op) \ - { isel_stringIfy(op), 0, op, 0, 0 } + { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), 0, 0 } #define INLINE_OP(op, memOp, immOp) \ - { isel_stringIfy(op), op, 0, memOp, immOp } + { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, memOp, immOp } #define INLINE_OPCONTEXT(op, memOp, immOp) \ - { isel_stringIfy(op), 0, op, memOp, immOp } + { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), memOp, immOp } #define NULL_OP \ { 0, 0, 0, 0, 0 } @@ -67,32 +67,32 @@ const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = { NULL_OP, // OpIncrement NULL_OP, // OpDecrement - INLINE_OP(Runtime::bitAnd, &Binop::inline_and32, &Binop::inline_and32), // OpBitAnd - INLINE_OP(Runtime::bitOr, &Binop::inline_or32, &Binop::inline_or32), // OpBitOr - INLINE_OP(Runtime::bitXor, &Binop::inline_xor32, &Binop::inline_xor32), // OpBitXor + INLINE_OP(bitAnd, &Binop::inline_and32, &Binop::inline_and32), // OpBitAnd + INLINE_OP(bitOr, &Binop::inline_or32, &Binop::inline_or32), // OpBitOr + INLINE_OP(bitXor, &Binop::inline_xor32, &Binop::inline_xor32), // OpBitXor - INLINE_OPCONTEXT(Runtime::add, &Binop::inline_add32, &Binop::inline_add32), // OpAdd - INLINE_OP(Runtime::sub, &Binop::inline_sub32, &Binop::inline_sub32), // OpSub - INLINE_OP(Runtime::mul, &Binop::inline_mul32, &Binop::inline_mul32), // OpMul + INLINE_OPCONTEXT(add, &Binop::inline_add32, &Binop::inline_add32), // OpAdd + INLINE_OP(sub, &Binop::inline_sub32, &Binop::inline_sub32), // OpSub + INLINE_OP(mul, &Binop::inline_mul32, &Binop::inline_mul32), // OpMul - OP(Runtime::div), // OpDiv - OP(Runtime::mod), // OpMod + OP(div), // OpDiv + OP(mod), // OpMod - INLINE_OP(Runtime::shl, &Binop::inline_shl32, &Binop::inline_shl32), // OpLShift - INLINE_OP(Runtime::shr, &Binop::inline_shr32, &Binop::inline_shr32), // OpRShift - INLINE_OP(Runtime::ushr, &Binop::inline_ushr32, &Binop::inline_ushr32), // OpURShift + INLINE_OP(shl, &Binop::inline_shl32, &Binop::inline_shl32), // OpLShift + INLINE_OP(shr, &Binop::inline_shr32, &Binop::inline_shr32), // OpRShift + INLINE_OP(ushr, &Binop::inline_ushr32, &Binop::inline_ushr32), // OpURShift - OP(Runtime::greaterThan), // OpGt - OP(Runtime::lessThan), // OpLt - OP(Runtime::greaterEqual), // OpGe - OP(Runtime::lessEqual), // OpLe - OP(Runtime::equal), // OpEqual - OP(Runtime::notEqual), // OpNotEqual - OP(Runtime::strictEqual), // OpStrictEqual - OP(Runtime::strictNotEqual), // OpStrictNotEqual + OP(greaterThan), // OpGt + OP(lessThan), // OpLt + OP(greaterEqual), // OpGe + OP(lessEqual), // OpLe + OP(equal), // OpEqual + OP(notEqual), // OpNotEqual + OP(strictEqual), // OpStrictEqual + OP(strictNotEqual), // OpStrictNotEqual - OPCONTEXT(Runtime::instanceof), // OpInstanceof - OPCONTEXT(Runtime::in), // OpIn + OPCONTEXT(instanceof), // OpInstanceof + OPCONTEXT(in), // OpIn NULL_OP, // OpAnd NULL_OP // OpOr @@ -121,16 +121,18 @@ void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) if (op == IR::OpAdd && (lhs->type == IR::StringType || rhs->type == IR::StringType)) { - const Binop::OpInfo stringAdd = OPCONTEXT(Runtime::addString); + const Binop::OpInfo stringAdd = OPCONTEXT(addString); info = stringAdd; } - if (info.fallbackImplementation) { - as->generateFunctionCallImp(target, info.name, info.fallbackImplementation, + RuntimeCall fallBack(info.fallbackImplementation); + RuntimeCall context(info.contextImplementation); + if (fallBack.isValid()) { + as->generateFunctionCallImp(target, info.name, fallBack, Assembler::PointerToValue(lhs), Assembler::PointerToValue(rhs)); - } else if (info.contextImplementation) { - as->generateFunctionCallImp(target, info.name, info.contextImplementation, + } else if (context.isValid()) { + as->generateFunctionCallImp(target, info.name, context, Assembler::EngineRegister, Assembler::PointerToValue(lhs), Assembler::PointerToValue(rhs)); diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h index 791e335970..c246ee43b0 100644 --- a/src/qml/jit/qv4binop_p.h +++ b/src/qml/jit/qv4binop_p.h @@ -77,8 +77,8 @@ struct Binop { struct OpInfo { const char *name; - QV4::Runtime::BinaryOperation fallbackImplementation; - QV4::Runtime::BinaryOperationContext contextImplementation; + int fallbackImplementation; // offsetOf(Runtime,...) + int contextImplementation; // offsetOf(Runtime,...) MemRegOp inlineMemRegOp; ImmRegOp inlineImmRegOp; }; diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index bf658fe689..4066ab213c 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -159,12 +159,6 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) JSC::JSGlobalData dummy(_executableAllocator); JSC::LinkBuffer linkBuffer(dummy, this, 0); - QHash<void*, const char*> functions; - foreach (CallToLink ctl, _callsToLink) { - linkBuffer.link(ctl.call, ctl.externalFunction); - functions[linkBuffer.locationOf(ctl.label).dataLocation()] = ctl.functionName; - } - foreach (const DataLabelPatch &p, _dataLabelPatches) linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); @@ -193,6 +187,12 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM"); if (showCode) { + QHash<void*, const char*> functions; +#ifndef QT_NO_DEBUG + foreach (CallInfo call, _callInfos) + functions[linkBuffer.locationOf(call.label).dataLocation()] = call.functionName; +#endif + QBuffer buf; buf.open(QIODevice::WriteOnly); WTF::setDataFile(new QIODevicePrintStream(&buf)); @@ -258,8 +258,8 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) return codeRef; } -InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator) - : EvalInstructionSelection(execAllocator, module, jsGenerator) +InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) + : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory) , _block(0) , _as(0) , compilationUnit(new CompilationUnit) @@ -353,7 +353,7 @@ void InstructionSelection::run(int functionIndex) lastLine = s->location.startLine; } } - s->accept(this); + visit(s); } } @@ -393,12 +393,12 @@ void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args if (useFastLookups && func->global) { uint index = registerGlobalGetterLookup(*func->id); - generateFunctionCall(result, Runtime::callGlobalLookup, + generateRuntimeCall(result, callGlobalLookup, Assembler::EngineRegister, Assembler::TrustedImm32(index), baseAddressForCallData()); } else { - generateFunctionCall(result, Runtime::callActivationProperty, + generateRuntimeCall(result, callActivationProperty, Assembler::EngineRegister, Assembler::StringToIndex(*func->id), baseAddressForCallData()); @@ -410,11 +410,11 @@ void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base, int propertyIndex, IR::Expr *result) { if (kind == IR::Member::MemberOfQmlScopeObject) { - generateFunctionCall(result, Runtime::typeofScopeObjectProperty, Assembler::EngineRegister, + generateRuntimeCall(result, typeofScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex)); } else if (kind == IR::Member::MemberOfQmlContextObject) { - generateFunctionCall(result, Runtime::typeofContextObjectProperty, + generateRuntimeCall(result, typeofContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex)); } else { @@ -425,46 +425,46 @@ void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base, void InstructionSelection::callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) { - generateFunctionCall(result, Runtime::typeofMember, Assembler::EngineRegister, + generateRuntimeCall(result, typeofMember, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::StringToIndex(name)); } void InstructionSelection::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) { - generateFunctionCall(result, Runtime::typeofElement, + generateRuntimeCall(result, typeofElement, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::PointerToValue(index)); } void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Expr *result) { - generateFunctionCall(result, Runtime::typeofName, Assembler::EngineRegister, + generateRuntimeCall(result, typeofName, Assembler::EngineRegister, Assembler::StringToIndex(name)); } void InstructionSelection::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) { - generateFunctionCall(result, Runtime::typeofValue, Assembler::EngineRegister, + generateRuntimeCall(result, typeofValue, Assembler::EngineRegister, Assembler::PointerToValue(value)); } void InstructionSelection::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) { - generateFunctionCall(result, Runtime::deleteMember, Assembler::EngineRegister, + generateRuntimeCall(result, deleteMember, Assembler::EngineRegister, Assembler::Reference(base), Assembler::StringToIndex(name)); } void InstructionSelection::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) { - generateFunctionCall(result, Runtime::deleteElement, Assembler::EngineRegister, + generateRuntimeCall(result, deleteElement, Assembler::EngineRegister, Assembler::Reference(base), Assembler::PointerToValue(index)); } void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Expr *result) { - generateFunctionCall(result, Runtime::deleteName, Assembler::EngineRegister, + generateRuntimeCall(result, deleteName, Assembler::EngineRegister, Assembler::StringToIndex(name)); } @@ -475,7 +475,7 @@ void InstructionSelection::callBuiltinDeleteValue(IR::Expr *result) void InstructionSelection::callBuiltinThrow(IR::Expr *arg) { - generateFunctionCall(Assembler::ReturnValueRegister, Runtime::throwException, Assembler::EngineRegister, + generateRuntimeCall(Assembler::ReturnValueRegister, throwException, Assembler::EngineRegister, Assembler::PointerToValue(arg)); } @@ -486,13 +486,13 @@ void InstructionSelection::callBuiltinReThrow() void InstructionSelection::callBuiltinUnwindException(IR::Expr *result) { - generateFunctionCall(result, Runtime::unwindException, Assembler::EngineRegister); + generateRuntimeCall(result, unwindException, Assembler::EngineRegister); } void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName) { - generateFunctionCall(Assembler::Void, Runtime::pushCatchScope, Assembler::EngineRegister, Assembler::StringToIndex(exceptionName)); + generateRuntimeCall(Assembler::Void, pushCatchScope, Assembler::EngineRegister, Assembler::StringToIndex(exceptionName)); } void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) @@ -500,7 +500,7 @@ void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::E Q_ASSERT(arg); Q_ASSERT(result); - generateFunctionCall(result, Runtime::foreachIterator, Assembler::EngineRegister, Assembler::PointerToValue(arg)); + generateRuntimeCall(result, foreachIterator, Assembler::EngineRegister, Assembler::PointerToValue(arg)); } void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) @@ -508,24 +508,24 @@ void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR: Q_ASSERT(arg); Q_ASSERT(result); - generateFunctionCall(result, Runtime::foreachNextPropertyName, Assembler::Reference(arg)); + generateRuntimeCall(result, foreachNextPropertyName, Assembler::Reference(arg)); } void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg) { Q_ASSERT(arg); - generateFunctionCall(Assembler::Void, Runtime::pushWithScope, Assembler::Reference(arg), Assembler::EngineRegister); + generateRuntimeCall(Assembler::Void, pushWithScope, Assembler::Reference(arg), Assembler::EngineRegister); } void InstructionSelection::callBuiltinPopScope() { - generateFunctionCall(Assembler::Void, Runtime::popScope, Assembler::EngineRegister); + generateRuntimeCall(Assembler::Void, popScope, Assembler::EngineRegister); } void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) { - generateFunctionCall(Assembler::Void, Runtime::declareVar, Assembler::EngineRegister, + generateRuntimeCall(Assembler::Void, declareVar, Assembler::EngineRegister, Assembler::TrustedImm32(deletable), Assembler::StringToIndex(name)); } @@ -534,7 +534,7 @@ void InstructionSelection::callBuiltinDefineArray(IR::Expr *result, IR::ExprList Q_ASSERT(result); int length = prepareVariableArguments(args); - generateFunctionCall(result, Runtime::arrayLiteral, Assembler::EngineRegister, + generateRuntimeCall(result, arrayLiteral, Assembler::EngineRegister, baseAddressForCallArguments(), Assembler::TrustedImm32(length)); } @@ -614,19 +614,19 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int it = it->next; } - generateFunctionCall(result, Runtime::objectLiteral, Assembler::EngineRegister, + generateRuntimeCall(result, objectLiteral, Assembler::EngineRegister, baseAddressForCallArguments(), Assembler::TrustedImm32(classId), Assembler::TrustedImm32(arrayValueCount), Assembler::TrustedImm32(arrayGetterSetterCount | (needSparseArray << 30))); } void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result) { - generateFunctionCall(result, Runtime::setupArgumentsObject, Assembler::EngineRegister); + generateRuntimeCall(result, setupArgumentsObject, Assembler::EngineRegister); } void InstructionSelection::callBuiltinConvertThisToObject() { - generateFunctionCall(Assembler::Void, Runtime::convertThisToObject, Assembler::EngineRegister); + generateRuntimeCall(Assembler::Void, convertThisToObject, Assembler::EngineRegister); } void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) @@ -635,11 +635,11 @@ void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Ex prepareCallData(args, 0); if (value->asConst()) - generateFunctionCall(result, Runtime::callValue, Assembler::EngineRegister, + generateRuntimeCall(result, callValue, Assembler::EngineRegister, Assembler::PointerToValue(value), baseAddressForCallData()); else - generateFunctionCall(result, Runtime::callValue, Assembler::EngineRegister, + generateRuntimeCall(result, callValue, Assembler::EngineRegister, Assembler::Reference(value), baseAddressForCallData()); } @@ -659,17 +659,17 @@ void InstructionSelection::loadThisObject(IR::Expr *temp) void InstructionSelection::loadQmlContext(IR::Expr *temp) { - generateFunctionCall(temp, Runtime::getQmlContext, Assembler::EngineRegister); + generateRuntimeCall(temp, getQmlContext, Assembler::EngineRegister); } void InstructionSelection::loadQmlImportedScripts(IR::Expr *temp) { - generateFunctionCall(temp, Runtime::getQmlImportedScripts, Assembler::EngineRegister); + generateRuntimeCall(temp, getQmlImportedScripts, Assembler::EngineRegister); } void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *temp) { - generateFunctionCall(temp, Runtime::getQmlSingleton, Assembler::EngineRegister, Assembler::StringToIndex(name)); + generateRuntimeCall(temp, getQmlSingleton, Assembler::EngineRegister, Assembler::StringToIndex(name)); } void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *target) @@ -716,7 +716,7 @@ void InstructionSelection::loadString(const QString &str, IR::Expr *target) void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) { int id = registerRegExp(sourceRegexp); - generateFunctionCall(target, Runtime::regexpLiteral, Assembler::EngineRegister, Assembler::TrustedImm32(id)); + generateRuntimeCall(target, regexpLiteral, Assembler::EngineRegister, Assembler::TrustedImm32(id)); } void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr *target) @@ -726,20 +726,20 @@ void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr generateLookupCall(target, index, qOffsetOf(QV4::Lookup, globalGetter), Assembler::EngineRegister, Assembler::Void); return; } - generateFunctionCall(target, Runtime::getActivationProperty, Assembler::EngineRegister, Assembler::StringToIndex(*name->id)); + generateRuntimeCall(target, getActivationProperty, Assembler::EngineRegister, Assembler::StringToIndex(*name->id)); } void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) { // ### should use a lookup call here - generateFunctionCall(Assembler::Void, Runtime::setActivationProperty, + generateRuntimeCall(Assembler::Void, setActivationProperty, Assembler::EngineRegister, Assembler::StringToIndex(targetName), Assembler::PointerToValue(source)); } void InstructionSelection::initClosure(IR::Closure *closure, IR::Expr *target) { int id = closure->value; - generateFunctionCall(target, Runtime::closure, Assembler::EngineRegister, Assembler::TrustedImm32(id)); + generateRuntimeCall(target, closure, Assembler::EngineRegister, Assembler::TrustedImm32(id)); } void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::Expr *target) @@ -748,7 +748,7 @@ void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR:: uint index = registerGetterLookup(name); generateLookupCall(target, index, qOffsetOf(QV4::Lookup, getter), Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::Void); } else { - generateFunctionCall(target, Runtime::getProperty, Assembler::EngineRegister, + generateRuntimeCall(target, getProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::StringToIndex(name)); } } @@ -756,11 +756,11 @@ 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)); + generateRuntimeCall(target, 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)); + generateRuntimeCall(target, 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)); + generateRuntimeCall(target, getQmlIdObject, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index)); else Q_ASSERT(false); } @@ -768,12 +768,12 @@ void InstructionSelection::getQmlContextProperty(IR::Expr *base, IR::Member::Mem void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) { if (attachedPropertiesId != 0) - generateFunctionCall(target, Runtime::getQmlAttachedProperty, Assembler::EngineRegister, Assembler::TrustedImm32(attachedPropertiesId), Assembler::TrustedImm32(propertyIndex)); + generateRuntimeCall(target, getQmlAttachedProperty, Assembler::EngineRegister, Assembler::TrustedImm32(attachedPropertiesId), Assembler::TrustedImm32(propertyIndex)); else if (isSingleton) - generateFunctionCall(target, Runtime::getQmlSingletonQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex), + generateRuntimeCall(target, getQmlSingletonQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex), Assembler::TrustedImm32(captureRequired)); else - generateFunctionCall(target, Runtime::getQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex), + generateRuntimeCall(target, getQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex), Assembler::TrustedImm32(captureRequired)); } @@ -787,7 +787,7 @@ void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase, Assembler::PointerToValue(targetBase), Assembler::PointerToValue(source)); } else { - generateFunctionCall(Assembler::Void, Runtime::setProperty, Assembler::EngineRegister, + generateRuntimeCall(Assembler::Void, setProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), Assembler::StringToIndex(targetName), Assembler::PointerToValue(source)); } @@ -796,10 +796,10 @@ 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), + generateRuntimeCall(Assembler::Void, 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), + generateRuntimeCall(Assembler::Void, setQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); else Q_ASSERT(false); @@ -807,7 +807,7 @@ void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *tar void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) { - generateFunctionCall(Assembler::Void, Runtime::setQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), + generateRuntimeCall(Assembler::Void, setQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); } @@ -821,7 +821,7 @@ void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr return; } - generateFunctionCall(target, Runtime::getElement, Assembler::EngineRegister, + generateRuntimeCall(target, getElement, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::PointerToValue(index)); } @@ -834,7 +834,7 @@ void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase, IR Assembler::PointerToValue(source)); return; } - generateFunctionCall(Assembler::Void, Runtime::setElement, Assembler::EngineRegister, + generateRuntimeCall(Assembler::Void, setElement, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex), Assembler::PointerToValue(source)); } @@ -981,9 +981,9 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) } #define setOp(op, opName, operation) \ - do { op = operation; opName = isel_stringIfy(operation); } while (0) + do { op = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); } while (0) #define setOpContext(op, opName, operation) \ - do { opContext = operation; opName = isel_stringIfy(operation); } while (0) + do { opContext = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); } while (0) void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) { @@ -1003,12 +1003,12 @@ void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::Me prepareCallData(args, base); if (kind == IR::Member::MemberOfQmlScopeObject) - generateFunctionCall(result, Runtime::callQmlScopeObjectProperty, + generateRuntimeCall(result, callQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::TrustedImm32(propertyIndex), baseAddressForCallData()); else if (kind == IR::Member::MemberOfQmlContextObject) - generateFunctionCall(result, Runtime::callQmlContextObjectProperty, + generateRuntimeCall(result, callQmlContextObjectProperty, Assembler::EngineRegister, Assembler::TrustedImm32(propertyIndex), baseAddressForCallData()); @@ -1025,12 +1025,12 @@ void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR: if (useFastLookups) { uint index = registerGetterLookup(name); - generateFunctionCall(result, Runtime::callPropertyLookup, + generateRuntimeCall(result, callPropertyLookup, Assembler::EngineRegister, Assembler::TrustedImm32(index), baseAddressForCallData()); } else { - generateFunctionCall(result, Runtime::callProperty, Assembler::EngineRegister, + generateRuntimeCall(result, callProperty, Assembler::EngineRegister, Assembler::StringToIndex(name), baseAddressForCallData()); } @@ -1042,7 +1042,7 @@ void InstructionSelection::callSubscript(IR::Expr *base, IR::Expr *index, IR::Ex Q_ASSERT(base != 0); prepareCallData(args, base); - generateFunctionCall(result, Runtime::callElement, Assembler::EngineRegister, + generateRuntimeCall(result, callElement, Assembler::EngineRegister, Assembler::PointerToValue(index), baseAddressForCallData()); } @@ -1118,7 +1118,7 @@ void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *targe Assembler::TrustedImm32(Value::NotDouble_Mask)); #endif - generateFunctionCall(target, Runtime::toDouble, Assembler::PointerToValue(source)); + generateRuntimeCall(target, toDouble, Assembler::PointerToValue(source)); Assembler::Jump noDoubleDone = _as->jump(); // it is a double: @@ -1183,7 +1183,7 @@ void InstructionSelection::convertTypeToBool(IR::Expr *source, IR::Expr *target) case IR::StringType: case IR::VarType: default: - generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toBoolean, + generateRuntimeCall(Assembler::ReturnValueRegister, toBoolean, Assembler::PointerToValue(source)); _as->storeBool(Assembler::ReturnValueRegister, target); break; @@ -1215,7 +1215,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe // not an int: fallback.link(_as); - generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toInt, + generateRuntimeCall(Assembler::ReturnValueRegister, toInt, _as->loadAddress(Assembler::ScratchRegister, source)); isInt.link(_as); @@ -1253,7 +1253,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe // not an int: fallback.link(_as); - generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toInt, + generateRuntimeCall(Assembler::ReturnValueRegister, toInt, _as->loadAddress(Assembler::ScratchRegister, source)); _as->storeInt32(Assembler::ReturnValueRegister, target); @@ -1266,7 +1266,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe _as->branchTruncateDoubleToInt32(_as->toDoubleRegister(source), Assembler::ReturnValueRegister, Assembler::BranchIfTruncateSuccessful); - generateFunctionCall(Assembler::ReturnValueRegister, Runtime::doubleToInt, + generateRuntimeCall(Assembler::ReturnValueRegister, doubleToInt, Assembler::PointerToValue(source)); success.link(_as); _as->storeInt32(Assembler::ReturnValueRegister, target); @@ -1284,7 +1284,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe break; case IR::StringType: default: - generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toInt, + generateRuntimeCall(Assembler::ReturnValueRegister, toInt, _as->loadAddress(Assembler::ScratchRegister, source)); _as->storeInt32(Assembler::ReturnValueRegister, target); break; @@ -1309,7 +1309,7 @@ void InstructionSelection::convertTypeToUInt32(IR::Expr *source, IR::Expr *targe // not an int: isNoInt.link(_as); - generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toUInt, + generateRuntimeCall(Assembler::ReturnValueRegister, toUInt, _as->loadAddress(Assembler::ScratchRegister, source)); _as->storeInt32(Assembler::ReturnValueRegister, target); @@ -1320,7 +1320,7 @@ void InstructionSelection::convertTypeToUInt32(IR::Expr *source, IR::Expr *targe Assembler::Jump success = _as->branchTruncateDoubleToUint32(reg, Assembler::ReturnValueRegister, Assembler::BranchIfTruncateSuccessful); - generateFunctionCall(Assembler::ReturnValueRegister, Runtime::doubleToUInt, + generateRuntimeCall(Assembler::ReturnValueRegister, doubleToUInt, Assembler::PointerToValue(source)); success.link(_as); _as->storeUInt32(Assembler::ReturnValueRegister, target); @@ -1331,7 +1331,7 @@ void InstructionSelection::convertTypeToUInt32(IR::Expr *source, IR::Expr *targe _as->storeUInt32(Assembler::ReturnValueRegister, target); break; case IR::StringType: - generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toUInt, + generateRuntimeCall(Assembler::ReturnValueRegister, toUInt, Assembler::PointerToValue(source)); _as->storeUInt32(Assembler::ReturnValueRegister, target); break; @@ -1351,13 +1351,13 @@ void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprL if (useFastLookups && func->global) { uint index = registerGlobalGetterLookup(*func->id); - generateFunctionCall(result, Runtime::constructGlobalLookup, + generateRuntimeCall(result, constructGlobalLookup, Assembler::EngineRegister, Assembler::TrustedImm32(index), baseAddressForCallData()); return; } - generateFunctionCall(result, Runtime::constructActivationProperty, + generateRuntimeCall(result, constructActivationProperty, Assembler::EngineRegister, Assembler::StringToIndex(*func->id), baseAddressForCallData()); @@ -1369,14 +1369,14 @@ void InstructionSelection::constructProperty(IR::Expr *base, const QString &name prepareCallData(args, base); if (useFastLookups) { uint index = registerGetterLookup(name); - generateFunctionCall(result, Runtime::constructPropertyLookup, + generateRuntimeCall(result, constructPropertyLookup, Assembler::EngineRegister, Assembler::TrustedImm32(index), baseAddressForCallData()); return; } - generateFunctionCall(result, Runtime::constructProperty, Assembler::EngineRegister, + generateRuntimeCall(result, constructProperty, Assembler::EngineRegister, Assembler::StringToIndex(name), baseAddressForCallData()); } @@ -1386,7 +1386,7 @@ void InstructionSelection::constructValue(IR::Expr *value, IR::ExprList *args, I Q_ASSERT(value != 0); prepareCallData(args, 0); - generateFunctionCall(result, Runtime::constructValue, + generateRuntimeCall(result, constructValue, Assembler::EngineRegister, Assembler::Reference(value), baseAddressForCallData()); @@ -1422,7 +1422,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) booleanConversion.link(_as); reg = Assembler::ReturnValueRegister; - generateFunctionCall(reg, Runtime::toBoolean, Assembler::Reference(s->cond)); + generateRuntimeCall(reg, toBoolean, Assembler::Reference(s->cond)); testBoolean.link(_as); } @@ -1432,7 +1432,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) } else if (IR::Const *c = s->cond->asConst()) { // TODO: SSA optimization for constant condition evaluation should remove this. // See also visitCJump() in RegAllocInfo. - generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toBoolean, + generateRuntimeCall(Assembler::ReturnValueRegister, toBoolean, Assembler::PointerToValue(c)); _as->generateCJumpOnNonZero(Assembler::ReturnValueRegister, _block, s->iftrue, s->iffalse); return; @@ -1454,8 +1454,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) return; } - Runtime::CompareOperation op = 0; - Runtime::CompareOperationContext opContext = 0; + RuntimeCall op; + RuntimeCall opContext; const char *opName = 0; switch (b->op) { default: Q_UNREACHABLE(); Q_ASSERT(!"todo"); break; @@ -1476,7 +1476,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) // if (true === true) ..... // Of course, after folding the CJUMP to a JUMP, dead-code (dead-basic-block) // elimination (which isn't there either) would remove the whole else block. - if (opContext) + if (opContext.isValid()) _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, opContext, Assembler::EngineRegister, Assembler::PointerToValue(b->left), @@ -1800,7 +1800,7 @@ void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *tr IR::Expr *left = binop->left; IR::Expr *right = binop->right; - _as->generateFunctionCallImp(Assembler::ReturnValueRegister, "Runtime::compareStrictEqual", Runtime::compareStrictEqual, + generateRuntimeCall(Assembler::ReturnValueRegister, compareStrictEqual, Assembler::PointerToValue(left), Assembler::PointerToValue(right)); _as->generateCJumpOnCompare(binop->op == IR::OpStrictEqual ? Assembler::NotEqual : Assembler::Equal, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0), @@ -1954,12 +1954,18 @@ void InstructionSelection::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *tru IR::Expr *left = binop->left; IR::Expr *right = binop->right; - _as->generateFunctionCallImp(Assembler::ReturnValueRegister, "Runtime::compareEqual", Runtime::compareEqual, + generateRuntimeCall(Assembler::ReturnValueRegister, compareEqual, Assembler::PointerToValue(left), Assembler::PointerToValue(right)); _as->generateCJumpOnCompare(binop->op == IR::OpEqual ? Assembler::NotEqual : Assembler::Equal, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0), _block, trueBlock, falseBlock); } +QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading() +{ + QQmlRefPointer<CompiledData::CompilationUnit> result; + result.adopt(new JIT::CompilationUnit); + return result; +} #endif // ENABLE(ASSEMBLER) diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 366d510072..4b35a72e01 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -76,7 +76,7 @@ class Q_QML_EXPORT InstructionSelection: public EvalInstructionSelection { public: - InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); + InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory); ~InstructionSelection(); virtual void run(int functionIndex); @@ -244,8 +244,8 @@ private: #define isel_stringIfyx(s) #s #define isel_stringIfy(s) isel_stringIfyx(s) - #define generateFunctionCall(t, function, ...) \ - _as->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) + #define generateRuntimeCall(t, function, ...) \ + _as->generateFunctionCallImp(t, "Runtime::" isel_stringIfy(function), RuntimeCall(qOffsetOf(QV4::Runtime, function)), __VA_ARGS__) int prepareVariableArguments(IR::ExprList* args); int prepareCallData(IR::ExprList* args, IR::Expr *thisObject); @@ -285,11 +285,13 @@ private: class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: + ISelFactory() : EvalISelFactory(QStringLiteral("jit")) {} virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); } - virtual bool jitCompileRegexps() const + EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL + { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); } + bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL { return true; } + QQmlRefPointer<CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE Q_DECL_FINAL; }; } // end of namespace JIT diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index c21f52ecd3..deef719b67 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -87,7 +87,7 @@ public: {} protected: - void addStmtNr(Stmt *s) + void addStmtNr(Stmt *s) Q_DECL_OVERRIDE Q_DECL_FINAL { addJustifiedNr(intervals->positionForStatement(s)); } @@ -115,7 +115,7 @@ public: } protected: - void visitTemp(Temp *e) + void visitTemp(Temp *e) Q_DECL_OVERRIDE Q_DECL_FINAL { switch (e->kind) { case Temp::PhysicalRegister: { @@ -184,7 +184,7 @@ public: _currentBB = bb; for (Stmt *s : bb->statements()) { _currentStmt = s; - s->accept(this); + visit(s); } } } @@ -809,7 +809,8 @@ using namespace QT_PREPEND_NAMESPACE(QV4::IR); using namespace QT_PREPEND_NAMESPACE(QV4); namespace { -class ResolutionPhase: protected StmtVisitor, protected ExprVisitor { +class ResolutionPhase +{ Q_DISABLE_COPY(ResolutionPhase) LifeTimeIntervals::Ptr _intervals; @@ -892,7 +893,7 @@ private: addNewIntervals(usePosition(_currentStmt)); else addNewIntervals(defPosition(_currentStmt)); - _currentStmt->accept(this); + visit(_currentStmt); for (Move *load : _loads) newStatements.append(load); if (_currentStmt->asPhi()) @@ -1179,8 +1180,20 @@ private: return load; } -protected: - virtual void visitTemp(Temp *t) +private: + void visit(Expr *e) + { + switch (e->exprKind) { + case Expr::TempExpr: + visitTemp(e->asTemp()); + break; + default: + EXPR_VISIT_ALL_KINDS(e); + break; + } + } + + void visitTemp(Temp *t) { if (t->kind != Temp::VirtualRegister) return; @@ -1210,47 +1223,25 @@ protected: } } - virtual void visitArgLocal(ArgLocal *) {} - virtual void visitConst(Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(Member *e) { e->base->accept(this); } - - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitExp(Exp *s) { s->expr->accept(this); } - - virtual void visitMove(Move *s) + void visit(Stmt *s) { - if (Temp *t = s->target->asTemp()) - maybeGenerateSpill(t); + switch (s->stmtKind) { + case Stmt::MoveStmt: { + auto m = s->asMove(); + if (Temp *t = m->target->asTemp()) + maybeGenerateSpill(t); - s->source->accept(this); - s->target->accept(this); - } - - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitPhi(Phi *s) - { - maybeGenerateSpill(s->targetTemp); + visit(m->source); + visit(m->target); + } break; + case Stmt::PhiStmt: { + auto p = s->asPhi(); + maybeGenerateSpill(p->targetTemp); + } break; + default: + STMT_VISIT_ALL_KINDS(s); + break; + } } }; } // anonymous namespace diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp index cb9131d731..6a32069ac4 100644 --- a/src/qml/jit/qv4unop.cpp +++ b/src/qml/jit/qv4unop.cpp @@ -47,11 +47,11 @@ using namespace JIT; #define stringIfyx(s) #s #define stringIfy(s) stringIfyx(s) #define setOp(operation) \ - do { call = operation; name = stringIfy(operation); } while (0) + do { call = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); name = "Runtime::" stringIfy(operation); } while (0) void Unop::generate(IR::Expr *source, IR::Expr *target) { - Runtime::UnaryOperation call = 0; + RuntimeCall call; const char *name = 0; switch (op) { case IR::OpNot: @@ -60,19 +60,18 @@ void Unop::generate(IR::Expr *source, IR::Expr *target) case IR::OpUMinus: generateUMinus(source, target); return; - case IR::OpUPlus: setOp(Runtime::uPlus); break; + case IR::OpUPlus: setOp(uPlus); break; case IR::OpCompl: generateCompl(source, target); return; - case IR::OpIncrement: setOp(Runtime::increment); break; - case IR::OpDecrement: setOp(Runtime::decrement); break; + case IR::OpIncrement: setOp(increment); break; + case IR::OpDecrement: setOp(decrement); break; default: Q_UNREACHABLE(); } // switch - if (call) { - as->generateFunctionCallImp(target, name, call, Assembler::PointerToValue(source)); - } + Q_ASSERT(call.isValid()); + _as->generateFunctionCallImp(target, name, call, Assembler::PointerToValue(source)); } void Unop::generateUMinus(IR::Expr *source, IR::Expr *target) @@ -82,15 +81,15 @@ void Unop::generateUMinus(IR::Expr *source, IR::Expr *target) Assembler::RegisterID tReg = Assembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) tReg = (Assembler::RegisterID) targetTemp->index; - Assembler::RegisterID sReg = as->toInt32Register(source, tReg); - as->move(sReg, tReg); - as->neg32(tReg); + Assembler::RegisterID sReg = _as->toInt32Register(source, tReg); + _as->move(sReg, tReg); + _as->neg32(tReg); if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - as->storeInt32(tReg, target); + _as->storeInt32(tReg, target); return; } - as->generateFunctionCallImp(target, "Runtime::uMinus", Runtime::uMinus, Assembler::PointerToValue(source)); + generateRuntimeCall(target, uMinus, Assembler::PointerToValue(source)); } void Unop::generateNot(IR::Expr *source, IR::Expr *target) @@ -100,26 +99,26 @@ void Unop::generateNot(IR::Expr *source, IR::Expr *target) Assembler::RegisterID tReg = Assembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) tReg = (Assembler::RegisterID) targetTemp->index; - as->xor32(Assembler::TrustedImm32(0x1), as->toInt32Register(source, tReg), tReg); + _as->xor32(Assembler::TrustedImm32(0x1), _as->toInt32Register(source, tReg), tReg); if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - as->storeBool(tReg, target); + _as->storeBool(tReg, target); return; } else if (source->type == IR::SInt32Type) { Assembler::RegisterID tReg = Assembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) tReg = (Assembler::RegisterID) targetTemp->index; - as->compare32(Assembler::Equal, - as->toInt32Register(source, Assembler::ScratchRegister), Assembler::TrustedImm32(0), + _as->compare32(Assembler::Equal, + _as->toInt32Register(source, Assembler::ScratchRegister), Assembler::TrustedImm32(0), tReg); if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - as->storeBool(tReg, target); + _as->storeBool(tReg, target); return; } else if (source->type == IR::DoubleType) { // ### } // ## generic implementation testing for int/bool - as->generateFunctionCallImp(target, "Runtime::uNot", Runtime::uNot, Assembler::PointerToValue(source)); + generateRuntimeCall(target, uNot, Assembler::PointerToValue(source)); } void Unop::generateCompl(IR::Expr *source, IR::Expr *target) @@ -129,12 +128,12 @@ void Unop::generateCompl(IR::Expr *source, IR::Expr *target) Assembler::RegisterID tReg = Assembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) tReg = (Assembler::RegisterID) targetTemp->index; - as->xor32(Assembler::TrustedImm32(0xffffffff), as->toInt32Register(source, tReg), tReg); + _as->xor32(Assembler::TrustedImm32(0xffffffff), _as->toInt32Register(source, tReg), tReg); if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) - as->storeInt32(tReg, target); + _as->storeInt32(tReg, target); return; } - as->generateFunctionCallImp(target, "Runtime::complement", Runtime::complement, Assembler::PointerToValue(source)); + generateRuntimeCall(target, complement, Assembler::PointerToValue(source)); } #endif diff --git a/src/qml/jit/qv4unop_p.h b/src/qml/jit/qv4unop_p.h index f0b5b9c223..1141a84913 100644 --- a/src/qml/jit/qv4unop_p.h +++ b/src/qml/jit/qv4unop_p.h @@ -64,7 +64,7 @@ class Assembler; struct Unop { Unop(Assembler *assembler, IR::AluOp operation) - : as(assembler) + : _as(assembler) , op(operation) {} @@ -74,7 +74,7 @@ struct Unop { void generateNot(IR::Expr *source, IR::Expr *target); void generateCompl(IR::Expr *source, IR::Expr *target); - Assembler *as; + Assembler *_as; IR::AluOp op; }; diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 9c952f0d42..4404a5d79f 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -166,6 +166,17 @@ Q_DECLARE_METATYPE(QList<int>) properties of the proxy object. No binding code is needed because it is done dynamically using the Qt meta object system. + Use newQMetaObject() to wrap a QMetaObject; this gives you a + "script representation" of a QObject-based class. newQMetaObject() + returns a proxy script object; enum values of the class are available + as properties of the proxy object. + + Constructors exposed to the meta-object system ( using Q_INVOKABLE ) can be + called from the script to create a new QObject instance with + JavaScriptOwnership. + + + \snippet code/src_script_qjsengine.cpp 5 \section1 Extensions @@ -261,12 +272,8 @@ static void checkForApplicationInstance() \l{ECMA-262}, Section 15.1. */ QJSEngine::QJSEngine() - : QObject(*new QJSEnginePrivate, 0) - , d(new QV8Engine(this)) + : QJSEngine(nullptr) { - checkForApplicationInstance(); - - QJSEnginePrivate::addToDebugServer(this); } /*! @@ -515,6 +522,38 @@ QJSValue QJSEngine::newQObject(QObject *object) } /*! + \since 5.8 + + Creates a JavaScript object that wraps the given QMetaObject + The metaObject must outlive the script engine. It is recommended to only + use this method with static metaobjects. + + + When called as a constructor, a new instance of the class will be created. + Only constructors exposed by Q_INVOKABLE will be visible from the script engine. + + \sa newQObject() +*/ + +QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) { + Q_D(QJSEngine); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(d); + QV4::Scope scope(v4); + QV4::ScopedValue v(scope, QV4::QMetaObjectWrapper::create(v4, metaObject)); + return QJSValue(v4, v->asReturnedValue()); +} + +/*! \fn QJSValue QJSEngine::newQMetaObject<T>() + + \since 5.8 + Creates a JavaScript object that wraps the static QMetaObject associated + with class \c{T}. + + \sa newQObject() +*/ + + +/*! Returns this engine's Global Object. By default, the Global Object contains the built-in objects that are diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h index 6ecd0c7ec0..41c4b81270 100644 --- a/src/qml/jsapi/qjsengine.h +++ b/src/qml/jsapi/qjsengine.h @@ -74,6 +74,14 @@ public: QJSValue newQObject(QObject *object); + QJSValue newQMetaObject(const QMetaObject* metaObject); + + template <typename T> + QJSValue newQMetaObject() + { + return newQMetaObject(&T::staticMetaObject); + } + template <typename T> inline QJSValue toScriptValue(const T &value) { diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index ec7848aba2..e5c1dcdb80 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -663,11 +663,11 @@ QJSValue QJSValue::call(const QJSValueList &args) callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); } - ScopedValue result(scope, f->call(callData)); + f->call(scope, callData); if (engine->hasException) - result = engine->catchException(); + scope.result = engine->catchException(); - return QJSValue(engine, result->asReturnedValue()); + return QJSValue(engine, scope.result.asReturnedValue()); } /*! @@ -719,11 +719,11 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); } - ScopedValue result(scope, f->call(callData)); + f->call(scope, callData); if (engine->hasException) - result = engine->catchException(); + scope.result = engine->catchException(); - return QJSValue(engine, result->asReturnedValue()); + return QJSValue(engine, scope.result.asReturnedValue()); } /*! @@ -767,11 +767,11 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); } - ScopedValue result(scope, f->construct(callData)); + f->construct(scope, callData); if (engine->hasException) - result = engine->catchException(); + scope.result = engine->catchException(); - return QJSValue(engine, result->asReturnedValue()); + return QJSValue(engine, scope.result.asReturnedValue()); } #ifdef QT_DEPRECATED @@ -938,7 +938,7 @@ bool QJSValue::equals(const QJSValue& other) const if (!ov) return other.equals(*this); - return Runtime::compareEqual(*v, *ov); + return Runtime::method_compareEqual(*v, *ov); } /*! @@ -1234,6 +1234,28 @@ QObject *QJSValue::toQObject() const } /*! + \since 5.8 + + * If this QJSValue is a QMetaObject, returns the QMetaObject pointer + * that the QJSValue represents; otherwise, returns 0. + * + * \sa isQMetaObject() + */ +const QMetaObject *QJSValue::toQMetaObject() const +{ + QV4::ExecutionEngine *engine = QJSValuePrivate::engine(this); + if (!engine) + return 0; + QV4::Scope scope(engine); + QV4::Scoped<QV4::QMetaObjectWrapper> wrapper(scope, QJSValuePrivate::getValue(this)); + if (!wrapper) + return 0; + + return wrapper->metaObject(); +} + + +/*! Returns a QDateTime representation of this value, in local time. If this QJSValue is not a date, or the value of the date is NaN (Not-a-Number), an invalid QDateTime is returned. @@ -1286,4 +1308,18 @@ bool QJSValue::isQObject() const return val && val->as<QV4::QObjectWrapper>() != 0; } +/*! + \since 5.8 + + Returns true if this QJSValue is a QMetaObject; otherwise returns + false. + + \sa toQMetaObject(), QJSEngine::newQMetaObject() +*/ +bool QJSValue::isQMetaObject() const +{ + QV4::Value *val = QJSValuePrivate::getValue(this); + return val && val->as<QV4::QMetaObjectWrapper>() != 0; +} + QT_END_NAMESPACE diff --git a/src/qml/jsapi/qjsvalue.h b/src/qml/jsapi/qjsvalue.h index e207e1b099..ab20a2607d 100644 --- a/src/qml/jsapi/qjsvalue.h +++ b/src/qml/jsapi/qjsvalue.h @@ -98,6 +98,7 @@ public: bool isUndefined() const; bool isVariant() const; bool isQObject() const; + bool isQMetaObject() const; bool isObject() const; bool isDate() const; bool isRegExp() const; @@ -111,6 +112,7 @@ public: bool toBool() const; QVariant toVariant() const; QObject *toQObject() const; + const QMetaObject *toQMetaObject() const; QDateTime toDateTime() const; bool equals(const QJSValue &other) const; diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 038b23e8d3..cf434ee2ed 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -6,7 +6,6 @@ SOURCES += \ $$PWD/qv4engine.cpp \ $$PWD/qv4context.cpp \ $$PWD/qv4persistent.cpp \ - $$PWD/qv4debugging.cpp \ $$PWD/qv4lookup.cpp \ $$PWD/qv4identifier.cpp \ $$PWD/qv4identifiertable.cpp \ @@ -40,11 +39,16 @@ SOURCES += \ $$PWD/qv4include.cpp \ $$PWD/qv4qobjectwrapper.cpp \ $$PWD/qv4vme_moth.cpp \ - $$PWD/qv4profiling.cpp \ $$PWD/qv4arraybuffer.cpp \ $$PWD/qv4typedarray.cpp \ $$PWD/qv4dataview.cpp +!contains(QT_CONFIG, no-qml-debug) { + SOURCES += $$PWD/qv4profiling.cpp +} else { + DEFINES += QT_NO_QML_DEBUGGER +} + HEADERS += \ $$PWD/qv4global_p.h \ $$PWD/qv4engine_p.h \ @@ -99,6 +103,7 @@ HEADERS += \ HEADERS += \ $$PWD/qv4runtime_p.h \ + $$PWD/qv4runtimeapi_p.h \ $$PWD/qv4value_p.h \ $$PWD/qv4string_p.h \ $$PWD/qv4value_p.h @@ -111,3 +116,7 @@ SOURCES += \ valgrind { DEFINES += V4_USE_VALGRIND } + +heaptrack { + DEFINES += V4_USE_HEAPTRACK +} diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 94f418cae1..b55494823c 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -134,7 +134,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con ScopedCallData callData(scope, 1); callData->thisObject = this->asReturnedValue(); callData->args[0] = desc->value; - setter->call(callData); + setter->call(scope, callData); if (attrs.isWritable()) { setArrayAttributes(index, mapAttrs); @@ -203,33 +203,35 @@ PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) DEFINE_OBJECT_VTABLE(ArgumentsGetterFunction); -ReturnedValue ArgumentsGetterFunction::call(const Managed *getter, CallData *callData) +void ArgumentsGetterFunction::call(const Managed *getter, Scope &scope, CallData *callData) { ExecutionEngine *v4 = static_cast<const ArgumentsGetterFunction *>(getter)->engine(); - Scope scope(v4); Scoped<ArgumentsGetterFunction> g(scope, static_cast<const ArgumentsGetterFunction *>(getter)); Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>()); - if (!o) - return v4->throwTypeError(); + if (!o) { + scope.result = v4->throwTypeError(); + return; + } Q_ASSERT(g->index() < static_cast<unsigned>(o->context()->callData->argc)); - return o->context()->callData->args[g->index()].asReturnedValue(); + scope.result = o->context()->callData->args[g->index()]; } DEFINE_OBJECT_VTABLE(ArgumentsSetterFunction); -ReturnedValue ArgumentsSetterFunction::call(const Managed *setter, CallData *callData) +void ArgumentsSetterFunction::call(const Managed *setter, Scope &scope, CallData *callData) { ExecutionEngine *v4 = static_cast<const ArgumentsSetterFunction *>(setter)->engine(); - Scope scope(v4); Scoped<ArgumentsSetterFunction> s(scope, static_cast<const ArgumentsSetterFunction *>(setter)); Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>()); - if (!o) - return v4->throwTypeError(); + if (!o) { + scope.result = v4->throwTypeError(); + return; + } Q_ASSERT(s->index() < static_cast<unsigned>(o->context()->callData->argc)); o->context()->callData->args[s->index()] = callData->argc ? callData->args[0].asReturnedValue() : Encode::undefined(); - return Encode::undefined(); + scope.result = Encode::undefined(); } void ArgumentsObject::markObjects(Heap::Base *that, ExecutionEngine *e) diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index eeedcaf995..6ccfd89fd4 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -88,7 +88,7 @@ struct ArgumentsGetterFunction: FunctionObject V4_OBJECT2(ArgumentsGetterFunction, FunctionObject) uint index() const { return d()->index; } - static ReturnedValue call(const Managed *that, CallData *d); + static void call(const Managed *that, Scope &scope, CallData *d); }; inline @@ -103,7 +103,7 @@ struct ArgumentsSetterFunction: FunctionObject V4_OBJECT2(ArgumentsSetterFunction, FunctionObject) uint index() const { return d()->index; } - static ReturnedValue call(const Managed *that, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; inline diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index d170bde0e8..e006773c9e 100644 --- a/src/qml/jsruntime/qv4arraybuffer.cpp +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -51,29 +51,34 @@ Heap::ArrayBufferCtor::ArrayBufferCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ArrayBufferCtor::construct(const Managed *m, CallData *callData) +void ArrayBufferCtor::construct(const Managed *m, Scope &scope, CallData *callData) { ExecutionEngine *v4 = static_cast<const Object *>(m)->engine(); - Scope scope(v4); ScopedValue l(scope, callData->argument(0)); double dl = l->toInteger(); - if (v4->hasException) - return Encode::undefined(); + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } uint len = (uint)qBound(0., dl, (double)UINT_MAX); - if (len != dl) - return v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length")); + if (len != dl) { + scope.result = v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length")); + return; + } Scoped<ArrayBuffer> a(scope, v4->newArrayBuffer(len)); - if (scope.engine->hasException) - return Encode::undefined(); - return a.asReturnedValue(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + } else { + scope.result = a->asReturnedValue(); + } } -ReturnedValue ArrayBufferCtor::call(const Managed *that, CallData *callData) +void ArrayBufferCtor::call(const Managed *that, Scope &scope, CallData *callData) { - return construct(that, callData); + construct(that, scope, callData); } ReturnedValue ArrayBufferCtor::method_isView(CallContext *ctx) @@ -184,7 +189,8 @@ ReturnedValue ArrayBufferPrototype::method_slice(CallContext *ctx) ScopedCallData callData(scope, 1); double newLen = qMax(final - first, 0.); callData->args[0] = QV4::Encode(newLen); - QV4::Scoped<ArrayBuffer> newBuffer(scope, constructor->construct(callData)); + constructor->construct(scope, callData); + QV4::Scoped<ArrayBuffer> newBuffer(scope, scope.result.asReturnedValue()); if (!newBuffer || newBuffer->d()->data->size < (int)newLen) return scope.engine->throwTypeError(); diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h index 0413d2f28d..d079aeb9f7 100644 --- a/src/qml/jsruntime/qv4arraybuffer_p.h +++ b/src/qml/jsruntime/qv4arraybuffer_p.h @@ -78,8 +78,8 @@ struct ArrayBufferCtor: FunctionObject { V4_OBJECT2(ArrayBufferCtor, FunctionObject) - static ReturnedValue construct(const Managed *m, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); static ReturnedValue method_isView(CallContext *ctx); diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index 62ece57c41..f4afe46fcb 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -682,7 +682,7 @@ bool ArrayElementLessThan::operator()(Value v1, Value v2) const callData->thisObject = Primitive::undefinedValue(); callData->args[0] = v1; callData->args[1] = v2; - result = Runtime::callValue(scope.engine, m_comparefn, callData); + result = scope.engine->runtime.callValue(scope.engine, m_comparefn, callData); return result->toNumber() < 0; } diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 3521673461..4d15c6c137 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -54,18 +54,19 @@ Heap::ArrayCtor::ArrayCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ArrayCtor::construct(const Managed *m, CallData *callData) +void ArrayCtor::construct(const Managed *m, Scope &scope, CallData *callData) { ExecutionEngine *v4 = static_cast<const ArrayCtor *>(m)->engine(); - Scope scope(v4); ScopedArrayObject a(scope, v4->newArrayObject()); uint len; if (callData->argc == 1 && callData->args[0].isNumber()) { bool ok; len = callData->args[0].asArrayLength(&ok); - if (!ok) - return v4->throwRangeError(callData->args[0]); + if (!ok) { + scope.result = v4->throwRangeError(callData->args[0]); + return; + } if (len < 0x1000) a->arrayReserve(len); @@ -76,12 +77,12 @@ ReturnedValue ArrayCtor::construct(const Managed *m, CallData *callData) } a->setArrayLengthUnchecked(len); - return a.asReturnedValue(); + scope.result = a.asReturnedValue(); } -ReturnedValue ArrayCtor::call(const Managed *that, CallData *callData) +void ArrayCtor::call(const Managed *that, Scope &scope, CallData *callData) { - return construct(that, callData); + construct(that, scope, callData); } void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -132,7 +133,8 @@ ReturnedValue ArrayPrototype::method_toString(CallContext *ctx) if (!!f) { ScopedCallData d(scope, 0); d->thisObject = ctx->thisObject(); - return f->call(d); + f->call(scope, d); + return scope.result.asReturnedValue(); } return ObjectPrototype::method_toString(ctx); } @@ -704,7 +706,6 @@ ReturnedValue ArrayPrototype::method_every(CallContext *ctx) ScopedCallData callData(scope, 3); callData->args[2] = instance; callData->thisObject = ctx->argument(1); - ScopedValue r(scope); ScopedValue v(scope); bool ok = true; @@ -716,8 +717,8 @@ ReturnedValue ArrayPrototype::method_every(CallContext *ctx) callData->args[0] = v; callData->args[1] = Primitive::fromDouble(k); - r = callback->call(callData); - ok = r->toBoolean(); + callback->call(scope, callData); + ok = scope.result.toBoolean(); } return Encode(ok); } @@ -740,7 +741,6 @@ ReturnedValue ArrayPrototype::method_some(CallContext *ctx) callData->args[2] = instance; ScopedValue v(scope); - ScopedValue r(scope); for (uint k = 0; k < len; ++k) { bool exists; v = instance->getIndexed(k, &exists); @@ -749,8 +749,8 @@ ReturnedValue ArrayPrototype::method_some(CallContext *ctx) callData->args[0] = v; callData->args[1] = Primitive::fromDouble(k); - r = callback->call(callData); - if (r->toBoolean()) + callback->call(scope, callData); + if (scope.result.toBoolean()) return Encode(true); } return Encode(false); @@ -782,7 +782,7 @@ ReturnedValue ArrayPrototype::method_forEach(CallContext *ctx) callData->args[0] = v; callData->args[1] = Primitive::fromDouble(k); - callback->call(callData); + callback->call(scope, callData); } return Encode::undefined(); } @@ -804,7 +804,6 @@ ReturnedValue ArrayPrototype::method_map(CallContext *ctx) a->arrayReserve(len); a->setArrayLengthUnchecked(len); - ScopedValue mapped(scope); ScopedCallData callData(scope, 3); callData->thisObject = ctx->argument(1); callData->args[2] = instance; @@ -818,8 +817,8 @@ ReturnedValue ArrayPrototype::method_map(CallContext *ctx) callData->args[0] = v; callData->args[1] = Primitive::fromDouble(k); - mapped = callback->call(callData); - a->arraySet(k, mapped); + callback->call(scope, callData); + a->arraySet(k, scope.result); } return a.asReturnedValue(); } @@ -840,7 +839,6 @@ ReturnedValue ArrayPrototype::method_filter(CallContext *ctx) ScopedArrayObject a(scope, ctx->d()->engine->newArrayObject()); a->arrayReserve(len); - ScopedValue selected(scope); ScopedCallData callData(scope, 3); callData->thisObject = ctx->argument(1); callData->args[2] = instance; @@ -856,8 +854,8 @@ ReturnedValue ArrayPrototype::method_filter(CallContext *ctx) callData->args[0] = v; callData->args[1] = Primitive::fromDouble(k); - selected = callback->call(callData); - if (selected->toBoolean()) { + callback->call(scope, callData); + if (scope.result.toBoolean()) { a->arraySet(to, v); ++to; } @@ -879,17 +877,16 @@ ReturnedValue ArrayPrototype::method_reduce(CallContext *ctx) return ctx->engine()->throwTypeError(); uint k = 0; - ScopedValue acc(scope); ScopedValue v(scope); if (ctx->argc() > 1) { - acc = ctx->argument(1); + scope.result = ctx->argument(1); } else { bool kPresent = false; while (k < len && !kPresent) { v = instance->getIndexed(k, &kPresent); if (kPresent) - acc = v; + scope.result = v; ++k; } if (!kPresent) @@ -898,21 +895,21 @@ ReturnedValue ArrayPrototype::method_reduce(CallContext *ctx) ScopedCallData callData(scope, 4); callData->thisObject = Primitive::undefinedValue(); - callData->args[0] = acc; + callData->args[0] = scope.result; callData->args[3] = instance; while (k < len) { bool kPresent; v = instance->getIndexed(k, &kPresent); if (kPresent) { - callData->args[0] = acc; + callData->args[0] = scope.result; callData->args[1] = v; callData->args[2] = Primitive::fromDouble(k); - acc = callback->call(callData); + callback->call(scope, callData); } ++k; } - return acc->asReturnedValue(); + return scope.result.asReturnedValue(); } ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx) @@ -935,16 +932,15 @@ ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx) } uint k = len; - ScopedValue acc(scope); ScopedValue v(scope); if (ctx->argc() > 1) { - acc = ctx->argument(1); + scope.result = ctx->argument(1); } else { bool kPresent = false; while (k > 0 && !kPresent) { v = instance->getIndexed(k - 1, &kPresent); if (kPresent) - acc = v; + scope.result = v; --k; } if (!kPresent) @@ -959,13 +955,13 @@ ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx) bool kPresent; v = instance->getIndexed(k - 1, &kPresent); if (kPresent) { - callData->args[0] = acc; + callData->args[0] = scope.result; callData->args[1] = v; callData->args[2] = Primitive::fromDouble(k - 1); - acc = callback->call(callData); + callback->call(scope, callData); } --k; } - return acc->asReturnedValue(); + return scope.result.asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index bae5f9e0da..f49ed76b02 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -70,8 +70,8 @@ struct ArrayCtor: FunctionObject { V4_OBJECT2(ArrayCtor, FunctionObject) - static ReturnedValue construct(const Managed *m, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct ArrayPrototype: ArrayObject diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index d9da7d7754..57c54e15c4 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -50,17 +50,16 @@ Heap::BooleanCtor::BooleanCtor(QV4::ExecutionContext *scope) { } -ReturnedValue BooleanCtor::construct(const Managed *m, CallData *callData) +void BooleanCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast<const BooleanCtor *>(m)->engine()); bool n = callData->argc ? callData->args[0].toBoolean() : false; - return Encode(scope.engine->newBooleanObject(n)); + scope.result = Encode(scope.engine->newBooleanObject(n)); } -ReturnedValue BooleanCtor::call(const Managed *, CallData *callData) +void BooleanCtor::call(const Managed *, Scope &scope, CallData *callData) { bool value = callData->argc ? callData->args[0].toBoolean() : 0; - return Encode(value); + scope.result = Encode(value); } void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor) diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h index eedafa6126..17543e33e0 100644 --- a/src/qml/jsruntime/qv4booleanobject_p.h +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -70,8 +70,8 @@ struct BooleanCtor: FunctionObject { V4_OBJECT2(BooleanCtor, FunctionObject) - static ReturnedValue construct(const Managed *, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct BooleanPrototype: BooleanObject diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index f296ffd71e..fac3d5316c 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -54,32 +54,34 @@ Heap::DataViewCtor::DataViewCtor(QV4::ExecutionContext *scope) { } -ReturnedValue DataViewCtor::construct(const Managed *m, CallData *callData) +void DataViewCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast<const Object *>(m)->engine()); Scoped<ArrayBuffer> buffer(scope, callData->argument(0)); - if (!buffer) - return scope.engine->throwTypeError(); + if (!buffer) { + scope.result = scope.engine->throwTypeError(); + return; + } double bo = callData->argc > 1 ? callData->args[1].toNumber() : 0; uint byteOffset = (uint)bo; uint bufferLength = buffer->d()->data->size; double bl = callData->argc < 3 || callData->args[2].isUndefined() ? (bufferLength - bo) : callData->args[2].toNumber(); uint byteLength = (uint)bl; - if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) - return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); + if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) { + scope.result = scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); + return; + } Scoped<DataView> a(scope, scope.engine->memoryManager->allocObject<DataView>()); a->d()->buffer = buffer->d(); a->d()->byteLength = byteLength; a->d()->byteOffset = byteOffset; - return a.asReturnedValue(); - + scope.result = a.asReturnedValue(); } -ReturnedValue DataViewCtor::call(const Managed *that, CallData *callData) +void DataViewCtor::call(const Managed *that, Scope &scope, CallData *callData) { - return construct(that, callData); + construct(that, scope, callData); } diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 2e8e94cecd..f996a4c2f1 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -76,8 +76,8 @@ struct DataViewCtor: FunctionObject { V4_OBJECT2(DataViewCtor, FunctionObject) - static ReturnedValue construct(const Managed *m, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct DataView : Object diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 5397ad43c5..df648ba9ee 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -466,7 +466,7 @@ static inline double ParseString(const QString &s) if (format < Minute || format >= TimezoneHour) error = true; format = TimezoneHour; - } else if (*ch == 'Z' || *ch == 0) { + } else if (*ch == 'Z' || ch->unicode() == 0) { format = Done; } current = 0; @@ -565,7 +565,7 @@ static inline QString ToString(double t) { if (std::isnan(t)) return QStringLiteral("Invalid Date"); - QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT"); + QString str = ToDateTime(t, Qt::LocalTime).toString() + QLatin1String(" GMT"); double tzoffset = LocalTZA + DaylightSavingTA(t); if (tzoffset) { int hours = static_cast<int>(::fabs(tzoffset) / 1000 / 60 / 60); @@ -651,9 +651,8 @@ Heap::DateCtor::DateCtor(QV4::ExecutionContext *scope) { } -ReturnedValue DateCtor::construct(const Managed *m, CallData *callData) +void DateCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast<const DateCtor *>(m)->engine()); double t = 0; if (callData->argc == 0) @@ -687,13 +686,13 @@ ReturnedValue DateCtor::construct(const Managed *m, CallData *callData) t = TimeClip(UTC(t)); } - return Encode(scope.engine->newDateObject(Primitive::fromDouble(t))); + scope.result = Encode(scope.engine->newDateObject(Primitive::fromDouble(t))); } -ReturnedValue DateCtor::call(const Managed *m, CallData *) +void DateCtor::call(const Managed *m, Scope &scope, CallData *) { double t = currentTime(); - return static_cast<const DateCtor *>(m)->engine()->newString(ToString(t))->asReturnedValue(); + scope.result = static_cast<const DateCtor *>(m)->engine()->newString(ToString(t)); } void DatePrototype::init(ExecutionEngine *engine, Object *ctor) @@ -1311,7 +1310,8 @@ ReturnedValue DatePrototype::method_toJSON(CallContext *ctx) ScopedCallData callData(scope); callData->thisObject = ctx->thisObject(); - return toIso->call(callData); + toIso->call(scope, callData); + return scope.result.asReturnedValue(); } void DatePrototype::timezoneUpdated() diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index e3615d76a7..13e9e04040 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -104,8 +104,8 @@ struct DateCtor: FunctionObject { V4_OBJECT2(DateCtor, FunctionObject) - static ReturnedValue construct(const Managed *, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *); + static void construct(const Managed *, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *); }; struct DatePrototype: DateObject diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h index 9dca7e9979..3b589a41f1 100644 --- a/src/qml/jsruntime/qv4debugging_p.h +++ b/src/qml/jsruntime/qv4debugging_p.h @@ -59,6 +59,19 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace Debugging { +#ifdef QT_NO_QML_DEBUGGER + +struct Debugger +{ + bool pauseAtNextOpportunity() const { return false; } + void maybeBreakAtInstruction() {} + void enteringFunction() {} + void leavingFunction(const ReturnedValue &) {} + void aboutToThrow() {} +}; + +#else + class Q_QML_EXPORT Debugger : public QObject { Q_OBJECT @@ -72,6 +85,8 @@ public: virtual void aboutToThrow() = 0; }; +#endif // QT_NO_QML_DEBUGGING + } // namespace Debugging } // namespace QV4 diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 15a4ed2e56..f5bae4b258 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -136,8 +136,6 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , currentContext(0) , bumperPointerAllocator(new WTF::BumpPointerAllocator) , jsStack(new WTF::PageAllocation) - , debugger(0) - , profiler(0) , globalCode(0) , v8Engine(0) , argumentsAccessors(0) @@ -145,6 +143,10 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , m_engineId(engineSerial.fetchAndAddOrdered(1)) , regExpCache(0) , m_multiplyWrappedQObjects(0) +#ifndef QT_NO_QML_DEBUGGER + , m_debugger(0) + , m_profiler(0) +#endif { if (maxCallDepth == -1) { bool ok = false; @@ -442,10 +444,12 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) ExecutionEngine::~ExecutionEngine() { - delete debugger; - debugger = 0; - delete profiler; - profiler = 0; +#ifndef QT_NO_QML_DEBUGGER + delete m_debugger; + m_debugger = 0; + delete m_profiler; + m_profiler = 0; +#endif delete m_multiplyWrappedQObjects; m_multiplyWrappedQObjects = 0; delete identifierTable; @@ -467,17 +471,19 @@ ExecutionEngine::~ExecutionEngine() delete [] argumentsAccessors; } -void ExecutionEngine::setDebugger(Debugging::Debugger *debugger_) +#ifndef QT_NO_QML_DEBUGGER +void ExecutionEngine::setDebugger(Debugging::Debugger *debugger) { - Q_ASSERT(!debugger); - debugger = debugger_; + Q_ASSERT(!m_debugger); + m_debugger = debugger; } -void ExecutionEngine::enableProfiler() +void ExecutionEngine::setProfiler(Profiling::Profiler *profiler) { - Q_ASSERT(!profiler); - profiler = new QV4::Profiling::Profiler(this); + Q_ASSERT(!m_profiler); + m_profiler = profiler; } +#endif // QT_NO_QML_DEBUGGER void ExecutionEngine::initRootContext() { @@ -910,8 +916,8 @@ ReturnedValue ExecutionEngine::throwError(const Value &value) else exceptionStackTrace = stackTrace(); - if (debugger) - debugger->aboutToThrow(); + if (QV4::Debugging::Debugger *debug = debugger()) + debug->aboutToThrow(); return Encode::undefined(); } @@ -969,7 +975,7 @@ ReturnedValue ExecutionEngine::throwReferenceError(const Value &value) { Scope scope(this); ScopedString s(scope, value.toString(this)); - QString msg = s->toQString() + QStringLiteral(" is not defined"); + QString msg = s->toQString() + QLatin1String(" is not defined"); ScopedObject error(scope, newReferenceErrorObject(msg)); return throwError(error); } @@ -993,7 +999,7 @@ ReturnedValue ExecutionEngine::throwRangeError(const Value &value) { Scope scope(this); ScopedString s(scope, value.toString(this)); - QString msg = s->toQString() + QStringLiteral(" out of range"); + QString msg = s->toQString() + QLatin1String(" out of range"); ScopedObject error(scope, newRangeErrorObject(msg)); return throwError(error); } @@ -1137,7 +1143,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return ld->d()->locale; if (const QV4::DateObject *d = value.as<DateObject>()) return d->toQDateTime(); - if (const QV4::ArrayBuffer *d = value.as<ArrayBuffer>()) + if (const 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)! @@ -1265,6 +1271,8 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::Encode(*reinterpret_cast<const double*>(ptr)); case QMetaType::QString: return newString(*reinterpret_cast<const QString*>(ptr))->asReturnedValue(); + case QMetaType::QByteArray: + return newArrayBuffer(*reinterpret_cast<const QByteArray*>(ptr))->asReturnedValue(); case QMetaType::Float: return QV4::Encode(*reinterpret_cast<const float*>(ptr)); case QMetaType::Short: @@ -1441,6 +1449,8 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) return QV4::Encode(*reinterpret_cast<const double*>(data)); case QMetaType::QString: return newString(*reinterpret_cast<const QString*>(data))->asReturnedValue(); + case QMetaType::QByteArray: + return newArrayBuffer(*reinterpret_cast<const QByteArray*>(data))->asReturnedValue(); case QMetaType::Float: return QV4::Encode(*reinterpret_cast<const float*>(data)); case QMetaType::Short: @@ -1502,6 +1512,11 @@ void ExecutionEngine::assertObjectBelongsToEngine(const Heap::Base &baseObject) Q_UNUSED(baseObject); } +void ExecutionEngine::failStackLimitCheck(Scope &scope) +{ + scope.result = throwRangeError(QStringLiteral("Maximum call stack size exceeded.")); +} + // 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. @@ -1533,6 +1548,12 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) else *reinterpret_cast<QString*>(data) = value->toQString(); return true; + case QMetaType::QByteArray: + if (const ArrayBuffer *ab = value->as<ArrayBuffer>()) + *reinterpret_cast<QByteArray*>(data) = ab->asByteArray(); + else + *reinterpret_cast<QByteArray*>(data) = QByteArray(); + return true; case QMetaType::Float: *reinterpret_cast<float*>(data) = value->toNumber(); return true; diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index aeb2533d35..f42f727295 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -54,7 +54,7 @@ #include "private/qv4isel_p.h" #include "qv4managed_p.h" #include "qv4context_p.h" -#include "qv4internalclass_p.h" +#include "qv4runtimeapi_p.h" #include <private/qintrusivelist_p.h> #ifndef V4_BOOTSTRAP @@ -85,6 +85,9 @@ namespace CompiledData { struct CompilationUnit; } +struct InternalClass; +struct InternalClassPool; + struct Q_QML_EXPORT ExecutionEngine { private: @@ -109,6 +112,8 @@ public: Value *jsStackLimit; + Runtime runtime; + WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. enum { JSStackLimit = 4*1024*1024 }; @@ -132,9 +137,6 @@ public: IdentifierTable *identifierTable; - QV4::Debugging::Debugger *debugger; - QV4::Profiling::Profiler *profiler; - Object *globalObject; Function *globalCode; @@ -377,8 +379,19 @@ public: ExecutionEngine(EvalISelFactory *iselFactory = 0); ~ExecutionEngine(); +#ifdef QT_NO_QML_DEBUGGER + QV4::Debugging::Debugger *debugger() const { return nullptr; } + QV4::Profiling::Profiler *profiler() const { return nullptr; } + + void setDebugger(Debugging::Debugger *) {} + void setProfiler(Profiling::Profiler *) {} +#else + QV4::Debugging::Debugger *debugger() const { return m_debugger; } + QV4::Profiling::Profiler *profiler() const { return m_profiler; } + void setDebugger(Debugging::Debugger *debugger); - void enableProfiler(); + void setProfiler(Profiling::Profiler *profiler); +#endif // QT_NO_QML_DEBUGGER ExecutionContext *pushGlobalContext(); void pushContext(Heap::ExecutionContext *context); @@ -475,9 +488,28 @@ public: void assertObjectBelongsToEngine(const Heap::Base &baseObject); - bool checkStackLimits(ReturnedValue &exception); + bool checkStackLimits(Scope &scope); + +private: + void failStackLimitCheck(Scope &scope); + +#ifndef QT_NO_QML_DEBUGGER + QV4::Debugging::Debugger *m_debugger; + QV4::Profiling::Profiler *m_profiler; +#endif }; +// This is a trick to tell the code generators that functions taking a NoThrowContext won't +// throw exceptions and therefore don't need a check after the call. +#ifndef V4_BOOTSTRAP +struct NoThrowEngine : public ExecutionEngine +{ +}; +#else +struct NoThrowEngine; +#endif + + inline void ExecutionEngine::pushContext(Heap::ExecutionContext *context) { Q_ASSERT(currentContext && context); @@ -557,7 +589,7 @@ inline void Value::mark(ExecutionEngine *e) o->mark(e); } -#define CHECK_STACK_LIMITS(v4) { ReturnedValue e; if ((v4)->checkStackLimits(e)) return e; } \ +#define CHECK_STACK_LIMITS(v4, scope) if ((v4)->checkStackLimits(scope)) return; \ ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4); struct ExecutionEngineCallDepthRecorder @@ -568,10 +600,10 @@ struct ExecutionEngineCallDepthRecorder ~ExecutionEngineCallDepthRecorder() { --ee->callDepth; } }; -inline bool ExecutionEngine::checkStackLimits(ReturnedValue &exception) +inline bool ExecutionEngine::checkStackLimits(Scope &scope) { if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= maxCallDepth))) { - exception = throwRangeError(QStringLiteral("Maximum call stack size exceeded.")); + failStackLimitCheck(scope); return true; } diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index 9f1e6b613b..3763bf2613 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -160,13 +160,9 @@ ReturnedValue ErrorObject::method_get_stack(CallContext *ctx) if (i > 0) trace += QLatin1Char('\n'); const StackFrame &frame = This->d()->stackTrace[i]; - trace += frame.function; - trace += QLatin1Char('@'); - trace += frame.source; - if (frame.line >= 0) { - trace += QLatin1Char(':'); - trace += QString::number(frame.line); - } + trace += frame.function + QLatin1Char('@') + frame.source; + if (frame.line >= 0) + trace += QLatin1Char(':') + QString::number(frame.line); } This->d()->stack = ctx->d()->engine->newString(trace); } @@ -243,16 +239,15 @@ Heap::ErrorCtor::ErrorCtor(QV4::ExecutionContext *scope, const QString &name) { } -ReturnedValue ErrorCtor::construct(const Managed *m, CallData *callData) +void ErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast<const ErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create<ErrorObject>(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create<ErrorObject>(scope.engine, v); } -ReturnedValue ErrorCtor::call(const Managed *that, CallData *callData) +void ErrorCtor::call(const Managed *that, Scope &scope, CallData *callData) { - return static_cast<const Object *>(that)->construct(callData); + static_cast<const Object *>(that)->construct(scope, callData); } Heap::EvalErrorCtor::EvalErrorCtor(QV4::ExecutionContext *scope) @@ -260,11 +255,10 @@ Heap::EvalErrorCtor::EvalErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue EvalErrorCtor::construct(const Managed *m, CallData *callData) +void EvalErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast<const EvalErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create<EvalErrorObject>(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create<EvalErrorObject>(scope.engine, v); } Heap::RangeErrorCtor::RangeErrorCtor(QV4::ExecutionContext *scope) @@ -272,11 +266,10 @@ Heap::RangeErrorCtor::RangeErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue RangeErrorCtor::construct(const Managed *m, CallData *callData) +void RangeErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast<const RangeErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create<RangeErrorObject>(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create<RangeErrorObject>(scope.engine, v); } Heap::ReferenceErrorCtor::ReferenceErrorCtor(QV4::ExecutionContext *scope) @@ -284,11 +277,10 @@ Heap::ReferenceErrorCtor::ReferenceErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ReferenceErrorCtor::construct(const Managed *m, CallData *callData) +void ReferenceErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast<const ReferenceErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create<ReferenceErrorObject>(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create<ReferenceErrorObject>(scope.engine, v); } Heap::SyntaxErrorCtor::SyntaxErrorCtor(QV4::ExecutionContext *scope) @@ -296,11 +288,10 @@ Heap::SyntaxErrorCtor::SyntaxErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue SyntaxErrorCtor::construct(const Managed *m, CallData *callData) +void SyntaxErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast<const SyntaxErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create<SyntaxErrorObject>(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create<SyntaxErrorObject>(scope.engine, v); } Heap::TypeErrorCtor::TypeErrorCtor(QV4::ExecutionContext *scope) @@ -308,11 +299,10 @@ Heap::TypeErrorCtor::TypeErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue TypeErrorCtor::construct(const Managed *m, CallData *callData) +void TypeErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast<const TypeErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create<TypeErrorObject>(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create<TypeErrorObject>(scope.engine, v); } Heap::URIErrorCtor::URIErrorCtor(QV4::ExecutionContext *scope) @@ -320,11 +310,10 @@ Heap::URIErrorCtor::URIErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue URIErrorCtor::construct(const Managed *m, CallData *callData) +void URIErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(static_cast<const URIErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); - return ErrorObject::create<URIErrorObject>(scope.engine, v)->asReturnedValue(); + scope.result = ErrorObject::create<URIErrorObject>(scope.engine, v); } void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, Heap::ErrorObject::ErrorType t) diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 1ca2fedd7b..42a3d05d9f 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -221,50 +221,50 @@ struct ErrorCtor: FunctionObject { V4_OBJECT2(ErrorCtor, FunctionObject) - static ReturnedValue construct(const Managed *, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct EvalErrorCtor: ErrorCtor { V4_OBJECT2(EvalErrorCtor, ErrorCtor) - static ReturnedValue construct(const Managed *m, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); }; struct RangeErrorCtor: ErrorCtor { V4_OBJECT2(RangeErrorCtor, ErrorCtor) - static ReturnedValue construct(const Managed *m, CallData *callData); + static void construct(const Managed *, Scope &scope, CallData *callData); }; struct ReferenceErrorCtor: ErrorCtor { V4_OBJECT2(ReferenceErrorCtor, ErrorCtor) - static ReturnedValue construct(const Managed *m, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); }; struct SyntaxErrorCtor: ErrorCtor { V4_OBJECT2(SyntaxErrorCtor, ErrorCtor) - static ReturnedValue construct(const Managed *m, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); }; struct TypeErrorCtor: ErrorCtor { V4_OBJECT2(TypeErrorCtor, ErrorCtor) - static ReturnedValue construct(const Managed *m, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); }; struct URIErrorCtor: ErrorCtor { V4_OBJECT2(URIErrorCtor, ErrorCtor) - static ReturnedValue construct(const Managed *m, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); }; diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index f314e20863..caabee322a 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -59,7 +59,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, Q_UNUSED(engine); internalClass = engine->emptyClass; - const quint32 *formalsIndices = compiledFunction->formalsTable(); + const CompiledData::LEUInt32 *formalsIndices = compiledFunction->formalsTable(); // iterate backwards, so we get the right ordering for duplicate names Scope scope(engine); ScopedString arg(scope); @@ -78,7 +78,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, } nFormals = compiledFunction->nFormals; - const quint32 *localsIndices = compiledFunction->localsTable(); + const CompiledData::LEUInt32 *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable); @@ -110,7 +110,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr } nFormals = parameters.size(); - const quint32 *localsIndices = compiledFunction->localsTable(); + const CompiledData::LEUInt32 *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable); diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 5d2c57a2ba..6b9c552350 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -166,14 +166,6 @@ ReturnedValue FunctionObject::name() const return get(scope()->engine->id_name()); } - -ReturnedValue FunctionObject::newInstance() -{ - Scope scope(internalClass()->engine); - ScopedCallData callData(scope); - return construct(callData); -} - ReturnedValue FunctionObject::construct(const Managed *that, CallData *) { return static_cast<const FunctionObject *>(that)->engine()->throwTypeError(); @@ -251,9 +243,8 @@ Heap::FunctionCtor::FunctionCtor(QV4::ExecutionContext *scope) } // 15.3.2 -ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData) +void FunctionCtor::construct(const Managed *that, Scope &scope, CallData *callData) { - Scope scope(static_cast<const Object *>(that)->engine()); Scoped<FunctionCtor> f(scope, static_cast<const FunctionCtor *>(that)); QString arguments; @@ -266,8 +257,10 @@ ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData) } body = callData->args[callData->argc - 1].toQString(); } - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1Char('}'); @@ -278,15 +271,19 @@ ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData) const bool parsed = parser.parseExpression(); - if (!parsed) - return scope.engine->throwSyntaxError(QLatin1String("Parse error")); + if (!parsed) { + scope.result = scope.engine->throwSyntaxError(QLatin1String("Parse error")); + return; + } using namespace QQmlJS::AST; FunctionExpression *fe = QQmlJS::AST::cast<FunctionExpression *>(parser.rootNode()); - if (!fe) - return scope.engine->throwSyntaxError(QLatin1String("Parse error")); + if (!fe) { + scope.result = scope.engine->throwSyntaxError(QLatin1String("Parse error")); + return; + } - IR::Module module(scope.engine->debugger != 0); + IR::Module module(scope.engine->debugger() != 0); QQmlJS::RuntimeCodegen cg(scope.engine, f->strictMode()); cg.generateFromFunctionExpression(QString(), function, fe, &module); @@ -297,13 +294,13 @@ ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData) Function *vmf = compilationUnit->linkToEngine(scope.engine); ExecutionContext *global = scope.engine->rootContext(); - return FunctionObject::createScriptFunction(global, vmf)->asReturnedValue(); + scope.result = FunctionObject::createScriptFunction(global, vmf); } // 15.3.1: This is equivalent to new Function(...) -ReturnedValue FunctionCtor::call(const Managed *that, CallData *callData) +void FunctionCtor::call(const Managed *that, Scope &scope, CallData *callData) { - return construct(that, callData); + construct(that, scope, callData); } DEFINE_OBJECT_VTABLE(FunctionPrototype); @@ -376,7 +373,8 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx) } callData->thisObject = ctx->argument(0); - return o->call(callData); + o->call(scope, callData); + return scope.result.asReturnedValue(); } ReturnedValue FunctionPrototype::method_call(CallContext *ctx) @@ -393,7 +391,9 @@ ReturnedValue FunctionPrototype::method_call(CallContext *ctx) callData->args[i - 1] = ctx->args()[i]; } callData->thisObject = ctx->argument(0); - return o->call(callData); + + o->call(scope, callData); + return scope.result.asReturnedValue(); } ReturnedValue FunctionPrototype::method_bind(CallContext *ctx) @@ -422,19 +422,20 @@ Heap::ScriptFunction::ScriptFunction(QV4::ExecutionContext *scope, Function *fun { } -ReturnedValue ScriptFunction::construct(const Managed *that, CallData *callData) +void ScriptFunction::construct(const Managed *that, Scope &scope, CallData *callData) { - ExecutionEngine *v4 = static_cast<const Object *>(that)->engine(); - if (v4->hasException) - return Encode::undefined(); - CHECK_STACK_LIMITS(v4); + ExecutionEngine *v4 = scope.engine; + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that)); - InternalClass *ic = scope.engine->emptyClass; + InternalClass *ic = v4->emptyClass; ScopedObject proto(scope, f->protoForConstructor()); ScopedObject obj(scope, v4->newObject(ic, proto)); @@ -442,39 +443,37 @@ ReturnedValue ScriptFunction::construct(const Managed *that, CallData *callData) Scoped<CallContext> ctx(scope, v4->currentContext->newCallContext(f, callData)); v4->pushContext(ctx); - ScopedValue result(scope, Q_V4_PROFILE(v4, f->function())); + scope.result = Q_V4_PROFILE(v4, f->function()); if (f->function()->compiledFunction->hasQmlDependencies()) - QQmlPropertyCapture::registerQmlDependencies(v4, f->function()->compiledFunction); - - if (v4->hasException) - return Encode::undefined(); + QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope); - if (result->isObject()) - return result->asReturnedValue(); - return obj.asReturnedValue(); + if (v4->hasException) { + scope.result = Encode::undefined(); + } else if (!scope.result.isObject()) { + scope.result = obj.asReturnedValue(); + } } -ReturnedValue ScriptFunction::call(const Managed *that, CallData *callData) +void ScriptFunction::call(const Managed *that, Scope &scope, CallData *callData) { - ExecutionEngine *v4 = static_cast<const Object *>(that)->engine(); - if (v4->hasException) - return Encode::undefined(); - CHECK_STACK_LIMITS(v4); + ExecutionEngine *v4 = scope.engine; + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that)); Scoped<CallContext> ctx(scope, v4->currentContext->newCallContext(f, callData)); v4->pushContext(ctx); - ScopedValue result(scope, Q_V4_PROFILE(v4, f->function())); + scope.result = Q_V4_PROFILE(v4, f->function()); if (f->function()->compiledFunction->hasQmlDependencies()) - QQmlPropertyCapture::registerQmlDependencies(scope.engine, f->function()->compiledFunction); - - return result->asReturnedValue(); + QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope); } DEFINE_OBJECT_VTABLE(SimpleScriptFunction); @@ -511,14 +510,15 @@ Heap::SimpleScriptFunction::SimpleScriptFunction(QV4::ExecutionContext *scope, F } } -ReturnedValue SimpleScriptFunction::construct(const Managed *that, CallData *callData) +void SimpleScriptFunction::construct(const Managed *that, Scope &scope, CallData *callData) { - ExecutionEngine *v4 = static_cast<const Object *>(that)->engine(); - if (v4->hasException) - return Encode::undefined(); - CHECK_STACK_LIMITS(v4); + ExecutionEngine *v4 = scope.engine; + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); Scoped<SimpleScriptFunction> f(scope, static_cast<const SimpleScriptFunction *>(that)); @@ -542,24 +542,24 @@ ReturnedValue SimpleScriptFunction::construct(const Managed *that, CallData *cal v4->pushContext(&ctx); Q_ASSERT(v4->current == &ctx); - ScopedObject result(scope, Q_V4_PROFILE(v4, f->function())); + scope.result = Q_V4_PROFILE(v4, f->function()); if (f->function()->compiledFunction->hasQmlDependencies()) - QQmlPropertyCapture::registerQmlDependencies(v4, f->function()->compiledFunction); + QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope); - if (!result) - return callData->thisObject.asReturnedValue(); - return result.asReturnedValue(); + if (!scope.result.isManaged() || !scope.result.managed()) + scope.result = callData->thisObject; } -ReturnedValue SimpleScriptFunction::call(const Managed *that, CallData *callData) +void SimpleScriptFunction::call(const Managed *that, Scope &scope, CallData *callData) { - ExecutionEngine *v4 = static_cast<const SimpleScriptFunction *>(that)->internalClass()->engine; - if (v4->hasException) - return Encode::undefined(); - CHECK_STACK_LIMITS(v4); + ExecutionEngine *v4 = scope.engine; + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); Scoped<SimpleScriptFunction> f(scope, static_cast<const SimpleScriptFunction *>(that)); @@ -579,12 +579,10 @@ ReturnedValue SimpleScriptFunction::call(const Managed *that, CallData *callData v4->pushContext(&ctx); Q_ASSERT(v4->current == &ctx); - ScopedValue result(scope, Q_V4_PROFILE(v4, f->function())); + scope.result = Q_V4_PROFILE(v4, f->function()); if (f->function()->compiledFunction->hasQmlDependencies()) - QQmlPropertyCapture::registerQmlDependencies(v4, f->function()->compiledFunction); - - return result->asReturnedValue(); + QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope); } Heap::Object *SimpleScriptFunction::protoForConstructor() @@ -606,20 +604,21 @@ Heap::BuiltinFunction::BuiltinFunction(QV4::ExecutionContext *scope, QV4::String { } -ReturnedValue BuiltinFunction::construct(const Managed *f, CallData *) +void BuiltinFunction::construct(const Managed *f, Scope &scope, CallData *) { - return static_cast<const BuiltinFunction *>(f)->internalClass()->engine->throwTypeError(); + scope.result = static_cast<const BuiltinFunction *>(f)->internalClass()->engine->throwTypeError(); } -ReturnedValue BuiltinFunction::call(const Managed *that, CallData *callData) +void BuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) { const BuiltinFunction *f = static_cast<const BuiltinFunction *>(that); - ExecutionEngine *v4 = f->internalClass()->engine; - if (v4->hasException) - return Encode::undefined(); - CHECK_STACK_LIMITS(v4); + ExecutionEngine *v4 = scope.engine; + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); CallContext::Data ctx(v4); @@ -630,18 +629,19 @@ ReturnedValue BuiltinFunction::call(const Managed *that, CallData *callData) v4->pushContext(&ctx); Q_ASSERT(v4->current == &ctx); - return f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext)); + scope.result = f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext)); } -ReturnedValue IndexedBuiltinFunction::call(const Managed *that, CallData *callData) +void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) { const IndexedBuiltinFunction *f = static_cast<const IndexedBuiltinFunction *>(that); - ExecutionEngine *v4 = f->internalClass()->engine; - if (v4->hasException) - return Encode::undefined(); - CHECK_STACK_LIMITS(v4); + ExecutionEngine *v4 = scope.engine; + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); CallContext::Data ctx(v4); @@ -652,7 +652,7 @@ ReturnedValue IndexedBuiltinFunction::call(const Managed *that, CallData *callDa v4->pushContext(&ctx); Q_ASSERT(v4->current == &ctx); - return f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext), f->d()->index); + scope.result = f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext), f->d()->index); } DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); @@ -685,12 +685,13 @@ Heap::BoundFunction::BoundFunction(QV4::ExecutionContext *scope, QV4::FunctionOb f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } -ReturnedValue BoundFunction::call(const Managed *that, CallData *dd) +void BoundFunction::call(const Managed *that, Scope &scope, CallData *dd) { const BoundFunction *f = static_cast<const BoundFunction *>(that); - Scope scope(f->engine()); - if (scope.hasException()) - return Encode::undefined(); + if (scope.hasException()) { + scope.result = Encode::undefined(); + return; + } Scoped<MemberData> boundArgs(scope, f->boundArgs()); ScopedCallData callData(scope, (boundArgs ? boundArgs->size() : 0) + dd->argc); @@ -702,15 +703,16 @@ ReturnedValue BoundFunction::call(const Managed *that, CallData *dd) } memcpy(argp, dd->args, dd->argc*sizeof(Value)); ScopedFunctionObject t(scope, f->target()); - return t->call(callData); + t->call(scope, callData); } -ReturnedValue BoundFunction::construct(const Managed *that, CallData *dd) +void BoundFunction::construct(const Managed *that, Scope &scope, CallData *dd) { const BoundFunction *f = static_cast<const BoundFunction *>(that); - Scope scope(f->engine()); - if (scope.hasException()) - return Encode::undefined(); + if (scope.hasException()) { + scope.result = Encode::undefined(); + return; + } Scoped<MemberData> boundArgs(scope, f->boundArgs()); ScopedCallData callData(scope, (boundArgs ? boundArgs->size() : 0) + dd->argc); @@ -721,7 +723,7 @@ ReturnedValue BoundFunction::construct(const Managed *that, CallData *dd) } memcpy(argp, dd->args, dd->argc*sizeof(Value)); ScopedFunctionObject t(scope, f->target()); - return t->construct(callData); + t->construct(scope, callData); } void BoundFunction::markObjects(Heap::Base *that, ExecutionEngine *e) diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 4a4545eca4..be80b87873 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -145,8 +145,6 @@ struct Q_QML_EXPORT FunctionObject: Object { void init(String *name, bool createProto); - ReturnedValue newInstance(); - using Object::construct; using Object::call; static ReturnedValue construct(const Managed *that, CallData *); @@ -178,8 +176,8 @@ struct FunctionCtor: FunctionObject { V4_OBJECT2(FunctionCtor, FunctionObject) - static ReturnedValue construct(const Managed *that, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *that, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct FunctionPrototype: FunctionObject @@ -202,20 +200,20 @@ struct Q_QML_EXPORT BuiltinFunction: FunctionObject { return scope->engine()->memoryManager->allocObject<BuiltinFunction>(scope, name, code); } - static ReturnedValue construct(const Managed *, CallData *); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *, Scope &scope, CallData *); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct IndexedBuiltinFunction: FunctionObject { V4_OBJECT2(IndexedBuiltinFunction, FunctionObject) - static ReturnedValue construct(const Managed *m, CallData *) + static void construct(const Managed *m, Scope &scope, CallData *) { - return static_cast<const IndexedBuiltinFunction *>(m)->engine()->throwTypeError(); + scope.result = static_cast<const IndexedBuiltinFunction *>(m)->engine()->throwTypeError(); } - static ReturnedValue call(const Managed *that, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; Heap::IndexedBuiltinFunction::IndexedBuiltinFunction(QV4::ExecutionContext *scope, uint index, @@ -231,8 +229,8 @@ struct SimpleScriptFunction: FunctionObject { V4_OBJECT2(SimpleScriptFunction, FunctionObject) V4_INTERNALCLASS(simpleScriptFunctionClass) - static ReturnedValue construct(const Managed *, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); Heap::Object *protoForConstructor(); }; @@ -240,8 +238,8 @@ struct SimpleScriptFunction: FunctionObject { struct ScriptFunction: SimpleScriptFunction { V4_OBJECT2(ScriptFunction, FunctionObject) - static ReturnedValue construct(const Managed *, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; @@ -257,8 +255,8 @@ struct BoundFunction: FunctionObject { Value boundThis() const { return d()->boundThis; } Heap::MemberData *boundArgs() const { return d()->boundArgs; } - static ReturnedValue construct(const Managed *, CallData *d); - static ReturnedValue call(const Managed *that, CallData *dd); + static void construct(const Managed *, Scope &scope, CallData *d); + static void call(const Managed *that, Scope &scope, CallData *dd); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 2c767e3302..b88e271a26 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -338,13 +338,14 @@ Heap::EvalFunction::EvalFunction(QV4::ExecutionContext *scope) f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1)); } -ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const +void EvalFunction::evalCall(Scope &scope, CallData *callData, bool directCall) const { - if (callData->argc < 1) - return Encode::undefined(); + if (callData->argc < 1) { + scope.result = Encode::undefined(); + return; + } ExecutionEngine *v4 = engine(); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); ExecutionContext *currentContext = v4->currentContext; @@ -356,8 +357,10 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const ctx = v4->pushGlobalContext(); } - if (!callData->args[0].isString()) - return callData->args[0].asReturnedValue(); + if (!callData->args[0].isString()) { + scope.result = callData->args[0].asReturnedValue(); + return; + } const QString code = callData->args[0].stringValue()->toQString(); bool inheritContext = !ctx->d()->strictMode; @@ -366,18 +369,23 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const script.strictMode = (directCall && currentContext->d()->strictMode); script.inheritContext = inheritContext; script.parse(); - if (v4->hasException) - return Encode::undefined(); + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } Function *function = script.function(); - if (!function) - return Encode::undefined(); + if (!function) { + scope.result = Encode::undefined(); + return; + } if (function->isStrict() || (ctx->d()->strictMode)) { ScopedFunctionObject e(scope, FunctionObject::createScriptFunction(ctx, function)); ScopedCallData callData(scope, 0); callData->thisObject = ctx->thisObject(); - return e->call(callData); + e->call(scope, callData); + return; } ContextStateSaver stateSaver(scope, ctx); @@ -386,14 +394,14 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const ctx->d()->strictMode = false; ctx->d()->compilationUnit = function->compilationUnit; - return Q_V4_PROFILE(ctx->engine(), function); + scope.result = Q_V4_PROFILE(ctx->engine(), function); } -ReturnedValue EvalFunction::call(const Managed *that, CallData *callData) +void EvalFunction::call(const Managed *that, Scope &scope, CallData *callData) { // indirect call - return static_cast<const EvalFunction *>(that)->evalCall(callData, false); + static_cast<const EvalFunction *>(that)->evalCall(scope, callData, false); } @@ -512,7 +520,7 @@ ReturnedValue GlobalFunctions::method_parseFloat(CallContext *ctx) if (trimmed.startsWith(QLatin1String("Infinity")) || trimmed.startsWith(QLatin1String("+Infinity"))) return Encode(Q_INFINITY); - if (trimmed.startsWith(QStringLiteral("-Infinity"))) + if (trimmed.startsWith(QLatin1String("-Infinity"))) return Encode(-Q_INFINITY); QByteArray ba = trimmed.toLatin1(); bool ok; diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h index ea7a3b06ce..403639f8c1 100644 --- a/src/qml/jsruntime/qv4globalobject_p.h +++ b/src/qml/jsruntime/qv4globalobject_p.h @@ -69,9 +69,9 @@ struct Q_QML_EXPORT EvalFunction : FunctionObject { V4_OBJECT2(EvalFunction, FunctionObject) - ReturnedValue evalCall(CallData *callData, bool directCall) const; + void evalCall(Scope &scope, CallData *callData, bool directCall) const; - static ReturnedValue call(const Managed *that, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct GlobalFunctions diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp index c8d66b1254..6260fd0cc8 100644 --- a/src/qml/jsruntime/qv4identifier.cpp +++ b/src/qml/jsruntime/qv4identifier.cpp @@ -64,12 +64,33 @@ IdentifierHashData::IdentifierHashData(int numBits) memset(entries, 0, alloc*sizeof(IdentifierHashEntry)); } +IdentifierHashData::IdentifierHashData(IdentifierHashData *other) + : size(other->size) + , numBits(other->numBits) + , identifierTable(other->identifierTable) +{ + refCount.store(1); + alloc = other->alloc; + entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); + memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry)); +} + IdentifierHashBase::IdentifierHashBase(ExecutionEngine *engine) { d = new IdentifierHashData(3); d->identifierTable = engine->identifierTable; } +void IdentifierHashBase::detach() +{ + if (!d || d->refCount == 1) + return; + IdentifierHashData *newData = new IdentifierHashData(d); + if (d && !d->refCount.deref()) + delete d; + d = newData; +} + IdentifierHashEntry *IdentifierHashBase::addEntry(const Identifier *identifier) { @@ -131,7 +152,7 @@ const IdentifierHashEntry *IdentifierHashBase::lookup(const QString &str) const return 0; Q_ASSERT(d->entries); - uint hash = String::createHashValue(str.constData(), str.length()); + uint hash = String::createHashValue(str.constData(), str.length(), Q_NULLPTR); uint idx = hash % d->alloc; while (1) { if (!d->entries[idx].identifier) diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h index a3abfd8e13..2695bbc875 100644 --- a/src/qml/jsruntime/qv4identifier_p.h +++ b/src/qml/jsruntime/qv4identifier_p.h @@ -85,6 +85,7 @@ struct IdentifierHashEntry { struct IdentifierHashData { IdentifierHashData(int numBits); + explicit IdentifierHashData(IdentifierHashData *other); ~IdentifierHashData() { free(entries); } @@ -115,6 +116,8 @@ struct IdentifierHashBase bool contains(const QString &str) const; bool contains(String *str) const; + void detach(); + protected: IdentifierHashEntry *addEntry(const Identifier *i); const IdentifierHashEntry *lookup(const Identifier *identifier) const; @@ -141,6 +144,7 @@ struct IdentifierHash : public IdentifierHashBase } void add(const QString &str, const T &value); + void add(Heap::String *str, const T &value); inline T value(const QString &str) const; inline T value(String *str) const; @@ -198,6 +202,13 @@ void IdentifierHash<T>::add(const QString &str, const T &value) } template<typename T> +void IdentifierHash<T>::add(Heap::String *str, const T &value) +{ + IdentifierHashEntry *e = addEntry(toIdentifier(str)); + e->value = value; +} + +template<typename T> inline T IdentifierHash<T>::value(const QString &str) const { return IdentifierHashEntry::get(lookup(str), (T*)0); @@ -223,7 +234,6 @@ QString IdentifierHash<T>::findId(T value) const return QString(); } - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index 5adb17b4ea..3def6defbf 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -118,7 +118,8 @@ void IdentifierTable::addEntry(Heap::String *str) Heap::String *IdentifierTable::insertString(const QString &s) { - uint hash = String::createHashValue(s.constData(), s.length()); + uint subtype; + uint hash = String::createHashValue(s.constData(), s.length(), &subtype); uint idx = hash % alloc; while (Heap::String *e = entries[idx]) { if (e->stringHash == hash && e->toQString() == s) @@ -128,6 +129,8 @@ Heap::String *IdentifierTable::insertString(const QString &s) } Heap::String *str = engine->newString(s); + str->stringHash = hash; + str->subtype = subtype; addEntry(str); return str; } @@ -178,7 +181,8 @@ Identifier *IdentifierTable::identifier(const QString &s) Identifier *IdentifierTable::identifier(const char *s, int len) { - uint hash = String::createHashValue(s, len); + uint subtype; + uint hash = String::createHashValue(s, len, &subtype); if (hash == UINT_MAX) return identifier(QString::fromUtf8(s, len)); @@ -192,6 +196,8 @@ Identifier *IdentifierTable::identifier(const char *s, int len) } Heap::String *str = engine->newString(QString::fromLatin1(s, len)); + str->stringHash = hash; + str->subtype = subtype; addEntry(str); return str->identifier; } diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index 29f83da522..c33d2cad11 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -41,8 +41,10 @@ #include "qv4scopedvalue_p.h" #include <QtQml/qjsengine.h> +#ifndef QT_NO_NETWORK #include <QtNetwork/qnetworkrequest.h> #include <QtNetwork/qnetworkreply.h> +#endif #include <QtCore/qfile.h> #include <QtQml/qqmlfile.h> @@ -57,7 +59,10 @@ QT_BEGIN_NAMESPACE 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) + : v4(engine), m_url(url) +#ifndef QT_NO_NETWORK + , m_redirectCount(0), m_network(0) , m_reply(0) +#endif { if (qmlContext) m_qmlContext.set(engine, *qmlContext); @@ -66,6 +71,7 @@ QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, m_resultObject.set(v4, resultValue(v4)); +#ifndef QT_NO_NETWORK m_network = engine->v8Engine->networkAccessManager(); QNetworkRequest request; @@ -73,11 +79,17 @@ QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, m_reply = m_network->get(request); QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); +#else + finished(); +#endif } QV4Include::~QV4Include() { - delete m_reply; m_reply = 0; +#ifndef QT_NO_NETWORK + delete m_reply; + m_reply = 0; +#endif } QV4::ReturnedValue QV4Include::resultValue(QV4::ExecutionEngine *v4, Status status) @@ -110,7 +122,7 @@ void QV4Include::callback(const QV4::Value &callback, const QV4::Value &status) QV4::ScopedCallData callData(scope, 1); callData->thisObject = v4->globalObject->asReturnedValue(); callData->args[0] = status; - f->call(callData); + f->call(scope, callData); if (scope.hasException()) scope.engine->catchException(); } @@ -123,6 +135,7 @@ QV4::ReturnedValue QV4Include::result() #define INCLUDE_MAXIMUM_REDIRECT_RECURSION 15 void QV4Include::finished() { +#ifndef QT_NO_NETWORK m_redirectCount++; if (m_redirectCount < INCLUDE_MAXIMUM_REDIRECT_RECURSION) { @@ -166,6 +179,12 @@ void QV4Include::finished() } else { resultObj->put(status, QV4::ScopedValue(scope, QV4::Primitive::fromInt32(NetworkError))); } +#else + QV4::Scope scope(v4); + QV4::ScopedObject resultObj(scope, m_resultObject.value()); + QV4::ScopedString status(scope, v4->newString(QStringLiteral("status"))); + resultObj->put(status, QV4::ScopedValue(scope, QV4::Primitive::fromInt32(NetworkError))); +#endif //QT_NO_NETWORK QV4::ScopedValue cb(scope, m_callbackFunction.value()); callback(cb, resultObj); @@ -188,14 +207,15 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) if (!context || !context->isJSContext) V4THROW_ERROR("Qt.include(): Can only be called from JavaScript files"); - QUrl url(scope.engine->resolvedUrl(ctx->args()[0].toQStringNoThrow())); - if (scope.engine->qmlEngine() && scope.engine->qmlEngine()->urlInterceptor()) - url = scope.engine->qmlEngine()->urlInterceptor()->intercept(url, QQmlAbstractUrlInterceptor::JavaScriptFile); - QV4::ScopedValue callbackFunction(scope, QV4::Primitive::undefinedValue()); if (ctx->argc() >= 2 && ctx->args()[1].as<QV4::FunctionObject>()) callbackFunction = ctx->args()[1]; +#ifndef QT_NO_NETWORK + QUrl url(scope.engine->resolvedUrl(ctx->args()[0].toQStringNoThrow())); + if (scope.engine->qmlEngine() && scope.engine->qmlEngine()->urlInterceptor()) + url = scope.engine->qmlEngine()->urlInterceptor()->intercept(url, QQmlAbstractUrlInterceptor::JavaScriptFile); + QString localFile = QQmlFile::urlToLocalFileOrQrc(url); QV4::ScopedValue result(scope); @@ -243,6 +263,12 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) } return result->asReturnedValue(); +#else + QV4::ScopedValue result(scope); + result = resultValue(scope.engine, NetworkError); + callback(callbackFunction, result); + return result->asReturnedValue(); +#endif } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h index 257dc05e65..1750e6a7e1 100644 --- a/src/qml/jsruntime/qv4include_p.h +++ b/src/qml/jsruntime/qv4include_p.h @@ -62,7 +62,9 @@ QT_BEGIN_NAMESPACE class QQmlEngine; +#ifndef QT_NO_NETWORK class QNetworkAccessManager; +#endif class QNetworkReply; class QV4Include : public QObject { @@ -90,15 +92,16 @@ private: static void callback(const QV4::Value &callback, const QV4::Value &status); QV4::ExecutionEngine *v4; - QNetworkAccessManager *m_network; - QPointer<QNetworkReply> m_reply; - QUrl m_url; + +#ifndef QT_NO_NETWORK int m_redirectCount; + QNetworkAccessManager *m_network; + QPointer<QNetworkReply> m_reply; +#endif QV4::PersistentValue m_callbackFunction; QV4::PersistentValue m_resultObject; - QV4::PersistentValue m_qmlContext; }; diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index af359bc0d3..bac45e18c8 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -101,20 +101,6 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) ++d->size; } -uint PropertyHash::lookup(const Identifier *identifier) const -{ - Q_ASSERT(d->entries); - - uint idx = identifier->hashValue % d->alloc; - while (1) { - if (d->entries[idx].identifier == identifier) - return d->entries[idx].index; - if (!d->entries[idx].identifier) - return UINT_MAX; - ++idx; - idx %= d->alloc; - } -} InternalClass::InternalClass(ExecutionEngine *engine) : engine(engine) @@ -360,18 +346,6 @@ void InternalClass::removeMember(Object *object, Identifier *id) Q_ASSERT(t.lookup); } -uint InternalClass::find(const String *string) -{ - engine->identifierTable->identifier(string); - const Identifier *id = string->d()->identifier; - - uint index = propertyTable.lookup(id); - if (index < size) - return index; - - return UINT_MAX; -} - uint InternalClass::find(const Identifier *id) { uint index = propertyTable.lookup(id); @@ -429,11 +403,13 @@ InternalClass *InternalClass::propertiesFrozen() const void InternalClass::destroy() { - QList<InternalClass *> destroyStack; - destroyStack.append(this); + std::vector<InternalClass *> destroyStack; + destroyStack.reserve(64); + destroyStack.push_back(this); - while (!destroyStack.isEmpty()) { - InternalClass *next = destroyStack.takeLast(); + while (!destroyStack.empty()) { + InternalClass *next = destroyStack.back(); + destroyStack.pop_back(); if (!next->engine) continue; next->engine = 0; @@ -441,13 +417,13 @@ void InternalClass::destroy() next->nameMap.~SharedInternalClassData<Identifier *>(); next->propertyData.~SharedInternalClassData<PropertyAttributes>(); if (next->m_sealed) - destroyStack.append(next->m_sealed); + destroyStack.push_back(next->m_sealed); if (next->m_frozen) - destroyStack.append(next->m_frozen); + destroyStack.push_back(next->m_frozen); for (size_t i = 0; i < next->transitions.size(); ++i) { Q_ASSERT(next->transitions.at(i).lookup); - destroyStack.append(next->transitions.at(i).lookup); + destroyStack.push_back(next->transitions.at(i).lookup); } next->transitions.~vector<Transition>(); diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index c10af4ce01..dcda949c97 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -54,6 +54,8 @@ #include <QHash> #include <private/qqmljsmemorypool_p.h> +#include <private/qv4engine_p.h> +#include <private/qv4identifiertable_p.h> QT_BEGIN_NAMESPACE @@ -117,6 +119,20 @@ inline PropertyHash::~PropertyHash() delete d; } +inline uint PropertyHash::lookup(const Identifier *identifier) const +{ + Q_ASSERT(d->entries); + + uint idx = identifier->hashValue % d->alloc; + while (1) { + if (d->entries[idx].identifier == identifier) + return d->entries[idx].index; + if (!d->entries[idx].identifier) + return UINT_MAX; + ++idx; + idx %= d->alloc; + } +} template <typename T> struct SharedInternalClassData { @@ -245,7 +261,7 @@ struct InternalClass : public QQmlJS::Managed { InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = 0); static void changeMember(Object *object, String *string, PropertyAttributes data, uint *index = 0); static void removeMember(Object *object, Identifier *id); - uint find(const String *s); + uint find(const String *string); uint find(const Identifier *id); InternalClass *sealed(); @@ -261,6 +277,18 @@ private: InternalClass(const InternalClass &other); }; +inline uint InternalClass::find(const String *string) +{ + engine->identifierTable->identifier(string); + const Identifier *id = string->d()->identifier; + + uint index = propertyTable.lookup(id); + if (index < size) + return index; + + return UINT_MAX; +} + struct InternalClassPool : public QQmlJS::MemoryPool { void markObjects(ExecutionEngine *engine); diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index a211d46153..5b665d8836 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -590,8 +590,7 @@ bool JsonParser::parseString(QString *string) return false; } if (QChar::requiresSurrogates(ch)) { - *string += QChar(QChar::highSurrogate(ch)); - *string += QChar(QChar::lowSurrogate(ch)); + *string += QChar(QChar::highSurrogate(ch)) + QChar(QChar::lowSurrogate(ch)); } else { *string += QChar(ch); } @@ -644,36 +643,39 @@ struct Stringify static QString quote(const QString &str) { - QString product = QStringLiteral("\""); - for (int i = 0; i < str.length(); ++i) { + QString product; + const int length = str.length(); + product.reserve(length + 2); + product += QLatin1Char('"'); + for (int i = 0; i < length; ++i) { QChar c = str.at(i); switch (c.unicode()) { case '"': - product += QStringLiteral("\\\""); + product += QLatin1String("\\\""); break; case '\\': - product += QStringLiteral("\\\\"); + product += QLatin1String("\\\\"); break; case '\b': - product += QStringLiteral("\\b"); + product += QLatin1String("\\b"); break; case '\f': - product += QStringLiteral("\\f"); + product += QLatin1String("\\f"); break; case '\n': - product += QStringLiteral("\\n"); + product += QLatin1String("\\n"); break; case '\r': - product += QStringLiteral("\\r"); + product += QLatin1String("\\r"); break; case '\t': - product += QStringLiteral("\\t"); + product += QLatin1String("\\t"); break; default: if (c.unicode() <= 0x1f) { - product += QStringLiteral("\\u00"); - product += c.unicode() > 0xf ? QLatin1Char('1') : QLatin1Char('0'); - product += QLatin1Char("0123456789abcdef"[c.unicode() & 0xf]); + product += QLatin1String("\\u00"); + product += (c.unicode() > 0xf ? QLatin1Char('1') : QLatin1Char('0')) + + QLatin1Char("0123456789abcdef"[c.unicode() & 0xf]); } else { product += c; } @@ -686,53 +688,53 @@ static QString quote(const QString &str) QString Stringify::Str(const QString &key, const Value &v) { Scope scope(v4); + scope.result = v; - ScopedValue value(scope, v); - ScopedObject o(scope, value); + ScopedObject o(scope, scope.result); if (o) { ScopedString s(scope, v4->newString(QStringLiteral("toJSON"))); ScopedFunctionObject toJSON(scope, o->get(s)); if (!!toJSON) { ScopedCallData callData(scope, 1); - callData->thisObject = value; + callData->thisObject = scope.result; callData->args[0] = v4->newString(key); - value = toJSON->call(callData); + toJSON->call(scope, callData); } } if (replacerFunction) { ScopedObject holder(scope, v4->newObject()); - holder->put(scope.engine, QString(), value); + holder->put(scope.engine, QString(), scope.result); ScopedCallData callData(scope, 2); callData->args[0] = v4->newString(key); - callData->args[1] = value; + callData->args[1] = scope.result; callData->thisObject = holder; - value = replacerFunction->call(callData); + replacerFunction->call(scope, callData); } - o = value->asReturnedValue(); + o = scope.result.asReturnedValue(); if (o) { if (NumberObject *n = o->as<NumberObject>()) - value = Encode(n->value()); + scope.result = Encode(n->value()); else if (StringObject *so = o->as<StringObject>()) - value = so->d()->string; + scope.result = so->d()->string; else if (BooleanObject *b = o->as<BooleanObject>()) - value = Encode(b->value()); + scope.result = Encode(b->value()); } - if (value->isNull()) + if (scope.result.isNull()) return QStringLiteral("null"); - if (value->isBoolean()) - return value->booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); - if (value->isString()) - return quote(value->stringValue()->toQString()); - - if (value->isNumber()) { - double d = value->toNumber(); - return std::isfinite(d) ? value->toQString() : QStringLiteral("null"); + if (scope.result.isBoolean()) + return scope.result.booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); + if (scope.result.isString()) + return quote(scope.result.stringValue()->toQString()); + + if (scope.result.isNumber()) { + double d = scope.result.toNumber(); + return std::isfinite(d) ? scope.result.toQString() : QStringLiteral("null"); } - o = value->asReturnedValue(); + o = scope.result.asReturnedValue(); if (o) { if (!o->as<FunctionObject>()) { if (o->as<ArrayObject>()) { @@ -807,10 +809,10 @@ QString Stringify::JO(Object *o) if (partial.isEmpty()) { result = QStringLiteral("{}"); } else if (gap.isEmpty()) { - result = QStringLiteral("{") + partial.join(QLatin1Char(',')) + QLatin1Char('}'); + result = QLatin1Char('{') + partial.join(QLatin1Char(',')) + QLatin1Char('}'); } else { - QString separator = QStringLiteral(",\n") + indent; - result = QStringLiteral("{\n") + indent + partial.join(separator) + QLatin1Char('\n') + QString separator = QLatin1String(",\n") + indent; + result = QLatin1String("{\n") + indent + partial.join(separator) + QLatin1Char('\n') + stepback + QLatin1Char('}'); } @@ -853,10 +855,10 @@ QString Stringify::JA(ArrayObject *a) if (partial.isEmpty()) { result = QStringLiteral("[]"); } else if (gap.isEmpty()) { - result = QStringLiteral("[") + partial.join(QLatin1Char(',')) + QStringLiteral("]"); + result = QLatin1Char('[') + partial.join(QLatin1Char(',')) + QLatin1Char(']'); } else { - QString separator = QStringLiteral(",\n") + indent; - result = QStringLiteral("[\n") + indent + partial.join(separator) + QStringLiteral("\n") + stepback + QStringLiteral("]"); + QString separator = QLatin1String(",\n") + indent; + result = QLatin1String("[\n") + indent + partial.join(separator) + QLatin1Char('\n') + stepback + QLatin1Char(']'); } indent = stepback; diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 46e47307ef..42e561bc7c 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -451,7 +451,8 @@ ReturnedValue Lookup::getterAccessor0(Lookup *l, ExecutionEngine *engine, const ScopedCallData callData(scope, 0); callData->thisObject = object; - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } } l->getter = getterFallback; @@ -473,7 +474,8 @@ ReturnedValue Lookup::getterAccessor1(Lookup *l, ExecutionEngine *engine, const ScopedCallData callData(scope, 0); callData->thisObject = object; - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } } l->getter = getterFallback; @@ -498,7 +500,8 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const ScopedCallData callData(scope, 0); callData->thisObject = object; - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } } } @@ -542,7 +545,8 @@ ReturnedValue Lookup::primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engin ScopedCallData callData(scope, 0); callData->thisObject = object; - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } } l->getter = getterGeneric; @@ -562,7 +566,8 @@ ReturnedValue Lookup::primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engin ScopedCallData callData(scope, 0); callData->thisObject = object; - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } } l->getter = getterGeneric; @@ -665,7 +670,8 @@ ReturnedValue Lookup::globalGetterAccessor0(Lookup *l, ExecutionEngine *engine) ScopedCallData callData(scope, 0); callData->thisObject = Primitive::undefinedValue(); - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -683,7 +689,8 @@ ReturnedValue Lookup::globalGetterAccessor1(Lookup *l, ExecutionEngine *engine) ScopedCallData callData(scope, 0); callData->thisObject = Primitive::undefinedValue(); - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -704,7 +711,8 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine) ScopedCallData callData(scope, 0); callData->thisObject = Primitive::undefinedValue(); - return getter->call(callData); + getter->call(scope, callData); + return scope.result.asReturnedValue(); } } } diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index cb17583b98..9fe8224736 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -80,6 +80,7 @@ Heap::MathObject::MathObject() m->defineDefaultProperty(QStringLiteral("pow"), QV4::MathObject::method_pow, 2); m->defineDefaultProperty(QStringLiteral("random"), QV4::MathObject::method_random, 0); m->defineDefaultProperty(QStringLiteral("round"), QV4::MathObject::method_round, 1); + m->defineDefaultProperty(QStringLiteral("sign"), QV4::MathObject::method_sign, 1); m->defineDefaultProperty(QStringLiteral("sin"), QV4::MathObject::method_sin, 1); m->defineDefaultProperty(QStringLiteral("sqrt"), QV4::MathObject::method_sqrt, 1); m->defineDefaultProperty(QStringLiteral("tan"), QV4::MathObject::method_tan, 1); @@ -292,6 +293,19 @@ ReturnedValue MathObject::method_round(CallContext *context) return Encode(v); } +ReturnedValue MathObject::method_sign(CallContext *context) +{ + double v = context->argc() ? context->args()[0].toNumber() : qt_qnan(); + + if (std::isnan(v)) + return Encode(qt_qnan()); + + if (qIsNull(v)) + return v; + + return Encode(std::signbit(v) ? -1 : 1); +} + ReturnedValue MathObject::method_sin(CallContext *context) { double v = context->argc() ? context->args()[0].toNumber() : qt_qnan(); diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h index 2b842a312f..01e778d9ec 100644 --- a/src/qml/jsruntime/qv4mathobject_p.h +++ b/src/qml/jsruntime/qv4mathobject_p.h @@ -84,6 +84,7 @@ struct MathObject: Object static ReturnedValue method_pow(CallContext *context); static ReturnedValue method_random(CallContext *context); static ReturnedValue method_round(CallContext *context); + static ReturnedValue method_sign(CallContext *context); static ReturnedValue method_sin(CallContext *context); static ReturnedValue method_sqrt(CallContext *context); static ReturnedValue method_tan(CallContext *context); diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index ab3e03b183..d27eef1d79 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -45,6 +45,7 @@ #include <QtCore/qmath.h> #include <QtCore/QDebug> #include <cassert> +#include <limits> using namespace QV4; @@ -75,17 +76,16 @@ Heap::NumberCtor::NumberCtor(QV4::ExecutionContext *scope) { } -ReturnedValue NumberCtor::construct(const Managed *m, CallData *callData) +void NumberCtor::construct(const Managed *, Scope &scope, CallData *callData) { - Scope scope(m->cast<NumberCtor>()->engine()); double dbl = callData->argc ? callData->args[0].toNumber() : 0.; - return Encode(scope.engine->newNumberObject(dbl)); + scope.result = Encode(scope.engine->newNumberObject(dbl)); } -ReturnedValue NumberCtor::call(const Managed *, CallData *callData) +void NumberCtor::call(const Managed *, Scope &scope, CallData *callData) { double dbl = callData->argc ? callData->args[0].toNumber() : 0.; - return Encode(dbl); + scope.result = Encode(dbl); } void NumberPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -99,12 +99,16 @@ void NumberPrototype::init(ExecutionEngine *engine, Object *ctor) ctor->defineReadonlyProperty(QStringLiteral("NEGATIVE_INFINITY"), Primitive::fromDouble(-qInf())); ctor->defineReadonlyProperty(QStringLiteral("POSITIVE_INFINITY"), Primitive::fromDouble(qInf())); ctor->defineReadonlyProperty(QStringLiteral("MAX_VALUE"), Primitive::fromDouble(1.7976931348623158e+308)); + ctor->defineReadonlyProperty(QStringLiteral("EPSILON"), Primitive::fromDouble(std::numeric_limits<double>::epsilon())); QT_WARNING_PUSH QT_WARNING_DISABLE_INTEL(239) ctor->defineReadonlyProperty(QStringLiteral("MIN_VALUE"), Primitive::fromDouble(5e-324)); QT_WARNING_POP + ctor->defineDefaultProperty(QStringLiteral("isFinite"), method_isFinite, 1); + ctor->defineDefaultProperty(QStringLiteral("isNaN"), method_isNaN, 1); + defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); @@ -134,6 +138,24 @@ inline double thisNumber(ExecutionContext *ctx) return n->value(); } +ReturnedValue NumberPrototype::method_isFinite(CallContext *ctx) +{ + if (!ctx->argc()) + return Encode(false); + + double v = ctx->args()[0].toNumber(); + return Encode(!std::isnan(v) && !qt_is_inf(v)); +} + +ReturnedValue NumberPrototype::method_isNaN(CallContext *ctx) +{ + if (!ctx->argc()) + return Encode(false); + + double v = ctx->args()[0].toNumber(); + return Encode(std::isnan(v)); +} + ReturnedValue NumberPrototype::method_toString(CallContext *ctx) { Scope scope(ctx); diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index ca6f686304..2416165c78 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -79,14 +79,16 @@ struct NumberCtor: FunctionObject { V4_OBJECT2(NumberCtor, FunctionObject) - static ReturnedValue construct(const Managed *that, CallData *callData); - static ReturnedValue call(const Managed *, CallData *callData); + static void construct(const Managed *that, Scope &scope, CallData *callData); + static void call(const Managed *, Scope &scope, CallData *callData); }; struct NumberPrototype: NumberObject { void init(ExecutionEngine *engine, Object *ctor); + static ReturnedValue method_isFinite(CallContext *ctx); + static ReturnedValue method_isNaN(CallContext *ctx); static ReturnedValue method_toString(CallContext *ctx); static ReturnedValue method_toLocaleString(CallContext *ctx); static ReturnedValue method_valueOf(CallContext *ctx); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 0727f35edd..aeb185049f 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -109,7 +109,8 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property Scope scope(f->engine()); ScopedCallData callData(scope); callData->thisObject = thisObject; - return f->call(callData); + f->call(scope, callData); + return scope.result.asReturnedValue(); } void Object::putValue(uint memberIndex, const Value &value) @@ -128,7 +129,7 @@ void Object::putValue(uint memberIndex, const Value &value) ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; - setter->call(callData); + setter->call(scope, callData); return; } goto reject; @@ -389,14 +390,14 @@ bool Object::hasOwnProperty(uint index) const return false; } -ReturnedValue Object::construct(const Managed *m, CallData *) +void Object::construct(const Managed *m, Scope &scope, CallData *) { - return static_cast<const Object *>(m)->engine()->throwTypeError(); + scope.result = static_cast<const Object *>(m)->engine()->throwTypeError(); } -ReturnedValue Object::call(const Managed *m, CallData *) +void Object::call(const Managed *m, Scope &scope, CallData *) { - return static_cast<const Object *>(m)->engine()->throwTypeError(); + scope.result = static_cast<const Object *>(m)->engine()->throwTypeError(); } ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty) @@ -744,7 +745,7 @@ void Object::internalPut(String *name, const Value &value) ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; - setter->call(callData); + setter->call(scope, callData); return; } @@ -753,9 +754,8 @@ void Object::internalPut(String *name, const Value &value) reject: if (engine()->current->strictMode) { - QString message = QStringLiteral("Cannot assign to read-only property \""); - message += name->toQString(); - message += QLatin1Char('\"'); + QString message = QLatin1String("Cannot assign to read-only property \"") + + name->toQString() + QLatin1Char('\"'); engine()->throwTypeError(message); } } @@ -815,7 +815,7 @@ void Object::internalPutIndexed(uint index, const Value &value) ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; - setter->call(callData); + setter->call(scope, callData); return; } diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index e4431a9fc9..b68dbc27b7 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -56,6 +56,7 @@ #include "qv4engine_p.h" #include "qv4scopedvalue_p.h" #include "qv4value_p.h" +#include "qv4internalclass_p.h" QT_BEGIN_NAMESPACE @@ -112,8 +113,8 @@ struct Object : Base { struct ObjectVTable { VTable vTable; - ReturnedValue (*call)(const Managed *, CallData *data); - ReturnedValue (*construct)(const Managed *, CallData *data); + void (*call)(const Managed *, Scope &scope, CallData *data); + void (*construct)(const Managed *, Scope &scope, 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); @@ -324,14 +325,14 @@ public: { vtable()->advanceIterator(this, it, name, index, p, attributes); } uint getLength() const { return vtable()->getLength(this); } - inline ReturnedValue construct(CallData *d) const - { return vtable()->construct(this, d); } - inline ReturnedValue call(CallData *d) const - { return vtable()->call(this, d); } + inline void construct(Scope &scope, CallData *d) const + { return vtable()->construct(this, scope, d); } + inline void call(Scope &scope, CallData *d) const + { vtable()->call(this, scope, d); } protected: static void markObjects(Heap::Base *that, ExecutionEngine *e); - static ReturnedValue construct(const Managed *m, CallData *); - static ReturnedValue call(const Managed *m, CallData *); + static void construct(const Managed *m, Scope &scope, CallData *); + static void call(const Managed *m, Scope &scope, 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); diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 015294e48a..cfd76166f2 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -59,28 +59,30 @@ Heap::ObjectCtor::ObjectCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ObjectCtor::construct(const Managed *that, CallData *callData) +void ObjectCtor::construct(const Managed *that, Scope &scope, CallData *callData) { 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())); if (!!proto) obj->setPrototype(proto); - return obj.asReturnedValue(); + scope.result = obj.asReturnedValue(); + } else { + scope.result = RuntimeHelpers::toObject(scope.engine, callData->args[0]); } - return RuntimeHelpers::toObject(scope.engine, callData->args[0]); } -ReturnedValue ObjectCtor::call(const Managed *m, CallData *callData) +void ObjectCtor::call(const Managed *m, Scope &scope, CallData *callData) { 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(); - return RuntimeHelpers::toObject(v4, callData->args[0]); + if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) { + scope.result = v4->newObject()->asReturnedValue(); + } else { + scope.result = RuntimeHelpers::toObject(v4, callData->args[0]); + } } void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) @@ -413,7 +415,8 @@ ReturnedValue ObjectPrototype::method_toLocaleString(CallContext *ctx) return ctx->engine()->throwTypeError(); ScopedCallData callData(scope); callData->thisObject = o; - return f->call(callData); + f->call(scope, callData); + return scope.result.asReturnedValue(); } ReturnedValue ObjectPrototype::method_valueOf(CallContext *ctx) diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index ac1964103e..47a37b196f 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -70,8 +70,8 @@ struct ObjectCtor: FunctionObject { V4_OBJECT2(ObjectCtor, FunctionObject) - static ReturnedValue construct(const Managed *that, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *that, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; struct ObjectPrototype: Object diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h index 5b1926468a..a0ade2068f 100644 --- a/src/qml/jsruntime/qv4persistent_p.h +++ b/src/qml/jsruntime/qv4persistent_p.h @@ -118,7 +118,7 @@ public: Managed *asManaged() const { if (!val) return 0; - return val->as<Managed>(); + return val->managed(); } template<typename T> T *as() const { @@ -167,7 +167,7 @@ public: Managed *asManaged() const { if (!val) return 0; - return val->as<Managed>(); + return val->managed(); } template <typename T> T *as() const { diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index a59190b846..349ec48e06 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -48,13 +48,10 @@ namespace Profiling { FunctionLocation FunctionCall::resolveLocation() const { - FunctionLocation location = { - m_function->name()->toQString(), - m_function->compilationUnit->fileName(), - m_function->compiledFunction->location.line, - m_function->compiledFunction->location.column - }; - return location; + return FunctionLocation(m_function->name()->toQString(), + m_function->compilationUnit->fileName(), + m_function->compiledFunction->location.line, + m_function->compiledFunction->location.column); } FunctionCallProperties FunctionCall::properties() const @@ -81,7 +78,8 @@ Profiler::Profiler(QV4::ExecutionEngine *engine) : featuresEnabled(0), m_engine( void Profiler::stopProfiling() { featuresEnabled = 0; - reportData(); + reportData(true); + m_sentLocations.clear(); } bool operator<(const FunctionCall &call1, const FunctionCall &call2) @@ -91,16 +89,24 @@ bool operator<(const FunctionCall &call1, const FunctionCall &call2) (call1.m_end == call2.m_end && call1.m_function < call2.m_function))); } -void Profiler::reportData() +void Profiler::reportData(bool trackLocations) { std::sort(m_data.begin(), m_data.end()); QVector<FunctionCallProperties> properties; - QHash<qint64, FunctionLocation> locations; + FunctionLocationHash locations; properties.reserve(m_data.size()); foreach (const FunctionCall &call, m_data) { properties.append(call.properties()); - locations[properties.constLast().id] = call.resolveLocation(); + Function *function = call.function(); + SentMarker &marker = m_sentLocations[reinterpret_cast<quintptr>(function)]; + if (!trackLocations || !marker.isValid()) { + FunctionLocation &location = locations[properties.constLast().id]; + if (!location.isValid()) + location = call.resolveLocation(); + if (trackLocations) + marker.setFunction(function); + } } emit dataReady(locations, properties, m_memory_data); diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index 0b4193204f..e06cb64a61 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -57,6 +57,40 @@ #include <QElapsedTimer> +#ifdef QT_NO_QML_DEBUGGER + +#define Q_V4_PROFILE_ALLOC(engine, size, type) (!engine) +#define Q_V4_PROFILE_DEALLOC(engine, size, type) (!engine) +#define Q_V4_PROFILE(engine, function) (function->code(engine, function->codeData)) + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace Profiling { +struct Profiler {}; +} +} + +QT_END_NAMESPACE + +#else + +#define Q_V4_PROFILE_ALLOC(engine, size, type)\ + (engine->profiler() &&\ + (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ + engine->profiler()->trackAlloc(size, type) : false) + +#define Q_V4_PROFILE_DEALLOC(engine, size, type) \ + (engine->profiler() &&\ + (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ + engine->profiler()->trackDealloc(size, type) : false) + +#define Q_V4_PROFILE(engine, function)\ + (engine->profiler() &&\ + (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureFunctionCall)) ?\ + Profiling::FunctionCallProfiler::profileCall(engine->profiler(), engine, function) :\ + function->code(engine, function->codeData)) + QT_BEGIN_NAMESPACE namespace QV4 { @@ -81,13 +115,23 @@ struct FunctionCallProperties { }; struct FunctionLocation { + FunctionLocation(const QString &name = QString(), const QString &file = QString(), + int line = -1, int column = -1) : + name(name), file(file), line(line), column(column) + {} + + bool isValid() + { + return !name.isEmpty(); + } + QString name; QString file; int line; int column; }; -typedef QHash<qint64, QV4::Profiling::FunctionLocation> FunctionLocationHash; +typedef QHash<quintptr, QV4::Profiling::FunctionLocation> FunctionLocationHash; struct MemoryAllocationProperties { qint64 timestamp; @@ -124,6 +168,11 @@ public: return *this; } + Function *function() const + { + return m_function; + } + FunctionLocation resolveLocation() const; FunctionCallProperties properties() const; @@ -135,48 +184,70 @@ private: qint64 m_end; }; -#define Q_V4_PROFILE_ALLOC(engine, size, type)\ - (engine->profiler &&\ - (engine->profiler->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ - engine->profiler->trackAlloc(size, type) : size) - -#define Q_V4_PROFILE_DEALLOC(engine, pointer, size, type) \ - (engine->profiler &&\ - (engine->profiler->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ - engine->profiler->trackDealloc(pointer, size, type) : pointer) - -#define Q_V4_PROFILE(engine, function)\ - (engine->profiler &&\ - (engine->profiler->featuresEnabled & (1 << Profiling::FeatureFunctionCall)) ?\ - Profiling::FunctionCallProfiler::profileCall(engine->profiler, engine, function) :\ - function->code(engine, function->codeData)) - class Q_QML_EXPORT Profiler : public QObject { Q_OBJECT - Q_DISABLE_COPY(Profiler) public: + struct SentMarker { + SentMarker() : m_function(nullptr) {} + + SentMarker(const SentMarker &other) : m_function(other.m_function) + { + if (m_function) + m_function->compilationUnit->addref(); + } + + ~SentMarker() + { + if (m_function) + m_function->compilationUnit->release(); + } + + SentMarker &operator=(const SentMarker &other) + { + if (&other != this) { + if (m_function) + m_function->compilationUnit->release(); + m_function = other.m_function; + m_function->compilationUnit->addref(); + } + return *this; + } + + void setFunction(Function *function) + { + Q_ASSERT(m_function == nullptr); + m_function = function; + m_function->compilationUnit->addref(); + } + + bool isValid() const + { return m_function != nullptr; } + + private: + Function *m_function; + }; + Profiler(QV4::ExecutionEngine *engine); - size_t trackAlloc(size_t size, MemoryType type) + bool trackAlloc(size_t size, MemoryType type) { MemoryAllocationProperties allocation = {m_timer.nsecsElapsed(), (qint64)size, type}; m_memory_data.append(allocation); - return size; + return true; } - void *trackDealloc(void *pointer, size_t size, MemoryType type) + bool trackDealloc(size_t size, MemoryType type) { MemoryAllocationProperties allocation = {m_timer.nsecsElapsed(), -(qint64)size, type}; m_memory_data.append(allocation); - return pointer; + return true; } quint64 featuresEnabled; -public slots: void stopProfiling(); void startProfiling(quint64 features); - void reportData(); + void reportData(bool trackLocations); void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: @@ -189,6 +260,7 @@ private: QElapsedTimer m_timer; QVector<FunctionCall> m_data; QVector<MemoryAllocationProperties> m_memory_data; + QHash<quintptr, SentMarker> m_sentLocations; friend class FunctionCallProfiler; }; @@ -227,10 +299,13 @@ Q_DECLARE_TYPEINFO(QV4::Profiling::MemoryAllocationProperties, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCallProperties, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCall, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionLocation, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QV4::Profiling::Profiler::SentMarker, Q_MOVABLE_TYPE); QT_END_NAMESPACE Q_DECLARE_METATYPE(QV4::Profiling::FunctionLocationHash) Q_DECLARE_METATYPE(QVector<QV4::Profiling::FunctionCallProperties>) Q_DECLARE_METATYPE(QVector<QV4::Profiling::MemoryAllocationProperties>) +#endif // QT_NO_QML_DEBUGGER + #endif // QV4PROFILING_H diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 462c4f3171..20179643fd 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -54,6 +54,7 @@ #include <private/qqmlbuiltinfunctions_p.h> #include <private/qv8engine_p.h> +#include <private/qv4arraybuffer_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4runtime_p.h> #include <private/qv4variantobject_p.h> @@ -74,6 +75,7 @@ #include <QtCore/qvarlengtharray.h> #include <QtCore/qtimer.h> #include <QtCore/qatomic.h> +#include <QtCore/qmetaobject.h> QT_BEGIN_NAMESPACE @@ -83,7 +85,7 @@ QT_WARNING_DISABLE_GCC("-Wstrict-aliasing") using namespace QV4; -static QPair<QObject *, int> extractQtMethod(QV4::FunctionObject *function) +QPair<QObject *, int> QObjectMethod::extractQtMethod(const QV4::FunctionObject *function) { QV4::ExecutionEngine *v4 = function->engine(); if (v4) { @@ -103,7 +105,7 @@ static QPair<QObject *, int> extractQtSignal(const Value &value) QV4::Scope scope(v4); QV4::ScopedFunctionObject function(scope, value); if (function) - return extractQtMethod(function); + return QObjectMethod::extractQtMethod(function); QV4::Scoped<QV4::QmlSignalHandler> handler(scope, value); if (handler) @@ -360,7 +362,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje Scope scope(engine); QV4::ScopedValue rv(scope, LoadProperty<ReadAccessor::Accessor>(engine, object, *property, nptr)); - if (captureRequired) { + if (captureRequired && !property->isConstant()) { if (property->accessors->notifier) { if (n && ep->propertyCapture) ep->propertyCapture->captureProperty(n); @@ -463,7 +465,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); bindingFunction->initBindingLocation(); - newBinding = new QQmlBinding(value, object, callingQmlContext); + newBinding = QQmlBinding::create(property, value, object, callingQmlContext); newBinding->setTarget(object, *property); } } @@ -795,8 +797,8 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase if (!v4) break; - QVarLengthArray<int, 9> dummy; - int *argsTypes = QQmlMetaObject(r).methodParameterTypes(This->signalIndex, dummy, 0); + QQmlMetaObject::ArgTypeStorage storage; + int *argsTypes = QQmlMetaObject(r).methodParameterTypes(This->signalIndex, &storage, 0); int argCount = argsTypes ? argsTypes[0]:0; @@ -814,7 +816,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase } } - f->call(callData); + f->call(scope, callData); if (scope.hasException()) { QQmlError error = v4->catchExceptionAsQmlError(); if (error.description().isEmpty()) { @@ -859,7 +861,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) { QV4::ScopedFunctionObject f(scope, connection->function.value()); - QPair<QObject *, int> connectedFunctionData = extractQtMethod(f); + QPair<QObject *, int> connectedFunctionData = QObjectMethod::extractQtMethod(f); if (connectedFunctionData.first == receiverToDisconnect && connectedFunctionData.second == slotIndexToDisconnect) { *ret = true; @@ -974,7 +976,7 @@ ReturnedValue QObjectWrapper::method_disconnect(CallContext *ctx) if (!functionThisValue->isUndefined() && !functionThisValue->isObject()) V4THROW_ERROR("Function.prototype.disconnect: target this is not an object"); - QPair<QObject *, int> functionData = extractQtMethod(functionValue); + QPair<QObject *, int> functionData = QObjectMethod::extractQtMethod(functionValue); void *a[] = { ctx->d()->engine, @@ -1098,6 +1100,7 @@ private: // Pointers to allocData union { QString *qstringPtr; + QByteArray *qbyteArrayPtr; QVariant *qvariantPtr; QList<QObject *> *qlistPtr; QJSValue *qjsValuePtr; @@ -1112,7 +1115,8 @@ private: } static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, int returnType, int argCount, - int *argTypes, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) + int *argTypes, QV4::ExecutionEngine *engine, QV4::CallData *callArgs, + QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) { if (argCount > 0) { // Convert all arguments. @@ -1124,7 +1128,7 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index for (int ii = 0; ii < args.count(); ++ii) argData[ii] = args[ii].dataPtr(); - object.metacall(QMetaObject::InvokeMetaMethod, index, argData.data()); + object.metacall(callType, index, argData.data()); return args[0].toValue(engine); @@ -1135,14 +1139,14 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index void *args[] = { arg.dataPtr() }; - object.metacall(QMetaObject::InvokeMetaMethod, index, args); + object.metacall(callType, index, args); return arg.toValue(engine); } else { void *args[] = { 0 }; - object.metacall(QMetaObject::InvokeMetaMethod, index, args); + object.metacall(callType, index, args); return Encode::undefined(); } @@ -1219,6 +1223,13 @@ static int MatchScore(const QV4::Value &actual, int conversionType) default: return 10; } + } else if (actual.as<ArrayBuffer>()) { + switch (conversionType) { + case QMetaType::QByteArray: + return 0; + default: + return 10; + } } else if (actual.as<ArrayObject>()) { switch (conversionType) { case QMetaType::QJsonArray: @@ -1345,7 +1356,8 @@ static const QQmlPropertyData * RelatedMethod(const QQmlObjectOrGadget &object, } static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPropertyData &data, - QV4::ExecutionEngine *engine, QV4::CallData *callArgs) + QV4::ExecutionEngine *engine, QV4::CallData *callArgs, + QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) { QByteArray unknownTypeError; @@ -1360,9 +1372,13 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ if (data.hasArguments()) { int *args = 0; - QVarLengthArray<int, 9> dummy; + QQmlMetaObject::ArgTypeStorage storage; - args = object.methodParameterTypes(data.coreIndex, dummy, &unknownTypeError); + if (data.isConstructor()) + args = static_cast<const QQmlStaticMetaObject&>(object).constructorParameterTypes( + data.coreIndex, &storage, &unknownTypeError); + else + args = object.methodParameterTypes(data.coreIndex, &storage, &unknownTypeError); if (!args) { QString typeName = QString::fromLatin1(unknownTypeError); @@ -1375,11 +1391,11 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ return engine->throwError(error); } - return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs); + return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs, callType); } else { - return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs); + return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs, callType); } } @@ -1398,7 +1414,8 @@ Resolve the overloaded method to call. The algorithm works conceptually like th score is constructed by adding the matchScore() result for each of the parameters. */ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QQmlPropertyData &data, - QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache) + QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache, + QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) { int argumentCount = callArgs->argc; @@ -1413,11 +1430,11 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QV4::ScopedValue v(scope); do { - QVarLengthArray<int, 9> dummy; + QQmlMetaObject::ArgTypeStorage storage; int methodArgumentCount = 0; int *methodArgTypes = 0; if (attempt->hasArguments()) { - int *args = object.methodParameterTypes(attempt->coreIndex, dummy, 0); + int *args = object.methodParameterTypes(attempt->coreIndex, &storage, 0); if (!args) // Must be an unknown argument continue; @@ -1448,7 +1465,7 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const } while ((attempt = RelatedMethod(object, attempt, dummy, propertyCache)) != 0); if (best.isValid()) { - return CallPrecise(object, best, engine, callArgs); + return CallPrecise(object, best, engine, callArgs, callType); } else { QString error = QLatin1String("Unable to determine callable overload. Candidates are:"); const QQmlPropertyData *candidate = &data; @@ -1477,6 +1494,8 @@ void CallArgument::cleanup() { if (type == QMetaType::QString) { qstringPtr->~QString(); + } else if (type == QMetaType::QByteArray) { + qbyteArrayPtr->~QByteArray(); } else if (type == -1 || type == QMetaType::QVariant) { qvariantPtr->~QVariant(); } else if (type == qMetaTypeId<QJSValue>()) { @@ -1676,6 +1695,8 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine) return QV4::Encode(floatValue); } else if (type == QMetaType::QString) { return QV4::Encode(engine->newString(*qstringPtr)); + } else if (type == QMetaType::QByteArray) { + return QV4::Encode(engine->newArrayBuffer(*qbyteArrayPtr)); } else if (type == QMetaType::QObjectStar) { QObject *object = qobjectPtr; if (object) @@ -1730,7 +1751,7 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index) { Scope valueScope(scope); - Scoped<QObjectMethod> method(valueScope, scope->d()->engine->memoryManager->allocObject<QObjectMethod>(scope)); + Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope)); method->d()->propertyCache = valueType->d()->propertyCache; method->d()->index = index; method->d()->valueTypeWrapper = valueType->d(); @@ -1754,17 +1775,13 @@ QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) co QString result; if (const QMetaObject *metaObject = d()->metaObject()) { - result += QString::fromUtf8(metaObject->className()); - result += QLatin1String("(0x"); - result += QString::number((quintptr)d()->object.data(),16); + result += QString::fromUtf8(metaObject->className()) + + QLatin1String("(0x") + QString::number((quintptr)d()->object.data(),16); if (d()->object) { QString objectName = d()->object->objectName(); - if (!objectName.isEmpty()) { - result += QLatin1String(", \""); - result += objectName; - result += QLatin1Char('\"'); - } + if (!objectName.isEmpty()) + result += QLatin1String(", \"") + objectName + QLatin1Char('\"'); } result += QLatin1Char(')'); @@ -1794,25 +1811,32 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, con return Encode::undefined(); } -ReturnedValue QObjectMethod::call(const Managed *m, CallData *callData) +void QObjectMethod::call(const Managed *m, Scope &scope, CallData *callData) { const QObjectMethod *This = static_cast<const QObjectMethod*>(m); - return This->callInternal(callData); + This->callInternal(callData, scope); } -ReturnedValue QObjectMethod::callInternal(CallData *callData) const +void QObjectMethod::callInternal(CallData *callData, Scope &scope) const { ExecutionEngine *v4 = engine(); ExecutionContext *context = v4->currentContext; - if (d()->index == DestroyMethod) - return method_destroy(context, callData->args, callData->argc); - else if (d()->index == ToStringMethod) - return method_toString(context); + if (d()->index == DestroyMethod) { + scope.result = method_destroy(context, callData->args, callData->argc); + return; + } + + else if (d()->index == ToStringMethod) { + scope.result = method_toString(context); + return; + } QQmlObjectOrGadget object(d()->object.data()); if (!d()->object) { - if (!d()->valueTypeWrapper) - return Encode::undefined(); + if (!d()->valueTypeWrapper) { + scope.result = Encode::undefined(); + return; + } object = QQmlObjectOrGadget(d()->propertyCache.data(), d()->valueTypeWrapper->gadgetPtr); } @@ -1821,16 +1845,20 @@ ReturnedValue QObjectMethod::callInternal(CallData *callData) const if (d()->propertyCache) { QQmlPropertyData *data = d()->propertyCache->method(d()->index); - if (!data) - return QV4::Encode::undefined(); + if (!data) { + scope.result = QV4::Encode::undefined(); + return; + } method = *data; } else { const QMetaObject *mo = d()->object->metaObject(); const QMetaMethod moMethod = mo->method(d()->index); method.load(moMethod); - if (method.coreIndex == -1) - return QV4::Encode::undefined(); + if (method.coreIndex == -1) { + scope.result = QV4::Encode::undefined(); + return; + } // Look for overloaded methods QByteArray methodName = moMethod.name(); @@ -1846,21 +1874,20 @@ ReturnedValue QObjectMethod::callInternal(CallData *callData) const } if (method.isV4Function()) { - Scope scope(v4); - QV4::ScopedValue rv(scope, QV4::Primitive::undefinedValue()); - QQmlV4Function func(callData, rv, v4); + scope.result = QV4::Encode::undefined(); + QQmlV4Function func(callData, &scope.result, v4); QQmlV4Function *funcptr = &func; void *args[] = { 0, &funcptr }; object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex, args); - return rv->asReturnedValue(); + return; } if (!method.isOverload()) { - return CallPrecise(object, method, v4, callData); + scope.result = CallPrecise(object, method, v4, callData); } else { - return CallOverloaded(object, method, v4, callData, d()->propertyCache); + scope.result = CallOverloaded(object, method, v4, callData, d()->propertyCache); } } @@ -1875,6 +1902,169 @@ void QObjectMethod::markObjects(Heap::Base *that, ExecutionEngine *e) DEFINE_OBJECT_VTABLE(QObjectMethod); + +Heap::QMetaObjectWrapper::QMetaObjectWrapper(const QMetaObject *metaObject) + : metaObject(metaObject) { + +} + +void Heap::QMetaObjectWrapper::ensureConstructorsCache() { + + const int count = metaObject->constructorCount(); + if (constructors.size() != count) { + constructors.clear(); + constructors.reserve(count); + + for (int i = 0; i < count; ++i) { + QMetaMethod method = metaObject->constructor(i); + QQmlPropertyData d; + d.load(method); + d.coreIndex = i; + constructors << d; + } + } +} + + +ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) { + + QV4::Scope scope(engine); + Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocObject<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue()); + mo->init(engine); + return mo->asReturnedValue(); +} + +void QMetaObjectWrapper::init(ExecutionEngine *) { + const QMetaObject & mo = *d()->metaObject; + + for (int i = 0; i < mo.enumeratorCount(); i++) { + QMetaEnum Enum = mo.enumerator(i); + for (int k = 0; k < Enum.keyCount(); k++) { + const char* key = Enum.key(k); + const int value = Enum.value(k); + defineReadonlyProperty(QLatin1String(key), Primitive::fromInt32(value)); + } + } +} + +void QMetaObjectWrapper::construct(const Managed *m, Scope &scope, CallData *callData) +{ + const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(m); + scope.result = This->constructInternal(callData); +} + +ReturnedValue QMetaObjectWrapper::constructInternal(CallData * callData) const { + + d()->ensureConstructorsCache(); + + ExecutionEngine *v4 = engine(); + const QMetaObject* mo = d()->metaObject; + if (d()->constructors.isEmpty()) { + return v4->throwTypeError(QStringLiteral("%1 has no invokable constructor") + .arg(QLatin1String(mo->className()))); + } + + Scope scope(v4); + Scoped<QObjectWrapper> object(scope); + + if (d()->constructors.size() == 1) { + object = callConstructor(d()->constructors.first(), v4, callData); + } + else { + object = callOverloadedConstructor(v4, callData); + } + Scoped<QMetaObjectWrapper> metaObject(scope, this); + object->defineDefaultProperty(v4->id_constructor(), metaObject); + object->setPrototype(const_cast<QMetaObjectWrapper*>(this)); + return object.asReturnedValue(); + +} + +ReturnedValue QMetaObjectWrapper::callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const { + + const QMetaObject* mo = d()->metaObject; + const QQmlStaticMetaObject object(mo); + return CallPrecise(object, data, engine, callArgs, QMetaObject::InvokeMetaMethod); +} + + +ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const { + const int numberOfConstructors = d()->constructors.size(); + const int argumentCount = callArgs->argc; + const QQmlStaticMetaObject object(d()->metaObject); + + QQmlPropertyData best; + int bestParameterScore = INT_MAX; + int bestMatchScore = INT_MAX; + + QV4::Scope scope(engine); + QV4::ScopedValue v(scope); + + for (int i = 0; i < numberOfConstructors; i++) { + const QQmlPropertyData & attempt = d()->constructors.at(i); + int methodArgumentCount = 0; + int *methodArgTypes = 0; + if (attempt.hasArguments()) { + QQmlMetaObject::ArgTypeStorage storage; + int *args = object.constructorParameterTypes(attempt.coreIndex, &storage, 0); + if (!args) // Must be an unknown argument + continue; + + methodArgumentCount = args[0]; + methodArgTypes = args + 1; + } + + if (methodArgumentCount > argumentCount) + continue; // We don't have sufficient arguments to call this method + + int methodParameterScore = argumentCount - methodArgumentCount; + if (methodParameterScore > bestParameterScore) + continue; // We already have a better option + + int methodMatchScore = 0; + for (int ii = 0; ii < methodArgumentCount; ++ii) + methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]); + + if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { + best = attempt; + bestParameterScore = methodParameterScore; + bestMatchScore = methodMatchScore; + } + + if (bestParameterScore == 0 && bestMatchScore == 0) + break; // We can't get better than that + }; + + if (best.isValid()) { + return CallPrecise(object, best, engine, callArgs, QMetaObject::CreateInstance); + } else { + QString error = QLatin1String("Unable to determine callable overload. Candidates are:"); + for (int i = 0; i < numberOfConstructors; i++) { + const QQmlPropertyData & candidate = d()->constructors.at(i); + error += QLatin1String("\n ") + + QString::fromUtf8(d()->metaObject->constructor(candidate.coreIndex) + .methodSignature()); + } + + return engine->throwError(error); + } +} + +bool QMetaObjectWrapper::isEqualTo(Managed *a, Managed *b) +{ + Q_ASSERT(a->as<QMetaObjectWrapper>()); + QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>(); + QMetaObjectWrapper *bMetaObject = b->as<QMetaObjectWrapper>(); + if (!bMetaObject) + return true; + return aMetaObject->metaObject() == bMetaObject->metaObject(); +} + +DEFINE_OBJECT_VTABLE(QMetaObjectWrapper); + + + + Heap::QmlSignalHandler::QmlSignalHandler(QObject *object, int signalIndex) : object(object) , signalIndex(signalIndex) diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index f101f352f1..8ab5c96c3a 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -93,6 +93,14 @@ struct QObjectMethod : FunctionObject { const QMetaObject *metaObject(); }; +struct QMetaObjectWrapper : FunctionObject { + QMetaObjectWrapper(const QMetaObject* metaObject); + const QMetaObject* metaObject; + QVector<QQmlPropertyData> constructors; + + void ensureConstructorsCache(); +}; + struct QmlSignalHandler : Object { QmlSignalHandler(QObject *object, int signalIndex); QPointer<QObject> object; @@ -184,11 +192,33 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject QV4::ReturnedValue method_toString(QV4::ExecutionContext *ctx) const; QV4::ReturnedValue method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const; - static ReturnedValue call(const Managed *, CallData *callData); + static void call(const Managed *, Scope &scope, CallData *callData); - ReturnedValue callInternal(CallData *callData) const; + void callInternal(CallData *callData, Scope &scope) const; static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); + + static QPair<QObject *, int> extractQtMethod(const QV4::FunctionObject *function); +}; + + +struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject +{ + V4_OBJECT2(QMetaObjectWrapper, QV4::FunctionObject) + V4_NEEDS_DESTROY + + static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject); + static void construct(const Managed *, Scope &scope, CallData *callData); + static bool isEqualTo(Managed *a, Managed *b); + + const QMetaObject *metaObject() const { return d()->metaObject; } + +private: + void init(ExecutionEngine *engine); + ReturnedValue constructInternal(CallData *callData) const; + ReturnedValue callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const; + ReturnedValue callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const; + }; struct Q_QML_EXPORT QmlSignalHandler : public QV4::Object diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 71e2bd1a06..ab00859b3b 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -185,8 +185,7 @@ QRegExp RegExpObject::toQRegExp() const QString RegExpObject::toString() const { - QString result = QLatin1Char('/') + source(); - result += QLatin1Char('/'); + QString result = QLatin1Char('/') + source() + QLatin1Char('/'); if (global()) result += QLatin1Char('g'); if (value()->ignoreCase) @@ -232,34 +231,39 @@ void Heap::RegExpCtor::clearLastMatch() lastMatchEnd = 0; } -ReturnedValue RegExpCtor::construct(const Managed *m, CallData *callData) +void RegExpCtor::construct(const Managed *, Scope &scope, CallData *callData) { - 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 scope.engine->throwTypeError(); + if (!f->isUndefined()) { + scope.result = scope.engine->throwTypeError(); + return; + } Scoped<RegExp> regexp(scope, re->value()); - return Encode(scope.engine->newRegExpObject(regexp, re->global())); + scope.result = Encode(scope.engine->newRegExpObject(regexp, re->global())); + return; } QString pattern; if (!r->isUndefined()) pattern = r->toQString(); - if (scope.hasException()) - return Encode::undefined(); + if (scope.hasException()) { + scope.result = Encode::undefined(); + return; + } bool global = false; bool ignoreCase = false; bool multiLine = false; if (!f->isUndefined()) { f = RuntimeHelpers::toString(scope.engine, f); - if (scope.hasException()) - return Encode::undefined(); + if (scope.hasException()) { + scope.result = Encode::undefined(); + return; + } QString str = f->stringValue()->toQString(); for (int i = 0; i < str.length(); ++i) { if (str.at(i) == QLatin1Char('g') && !global) { @@ -269,26 +273,31 @@ ReturnedValue RegExpCtor::construct(const Managed *m, CallData *callData) } else if (str.at(i) == QLatin1Char('m') && !multiLine) { multiLine = true; } else { - return scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor")); + scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor")); + return; } } } Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, ignoreCase, multiLine)); - if (!regexp->isValid()) - return scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression")); + if (!regexp->isValid()) { + scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression")); + return; + } - return Encode(scope.engine->newRegExpObject(regexp, global)); + scope.result = Encode(scope.engine->newRegExpObject(regexp, global)); } -ReturnedValue RegExpCtor::call(const Managed *that, CallData *callData) +void RegExpCtor::call(const Managed *that, Scope &scope, CallData *callData) { if (callData->argc > 0 && callData->args[0].as<RegExpObject>()) { - if (callData->argc == 1 || callData->args[1].isUndefined()) - return callData->args[0].asReturnedValue(); + if (callData->argc == 1 || callData->args[1].isUndefined()) { + scope.result = callData->args[0]; + return; + } } - return construct(that, callData); + construct(that, scope, callData); } void RegExpCtor::markObjects(Heap::Base *that, ExecutionEngine *e) @@ -420,7 +429,8 @@ 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()->as<FunctionObject>()->construct(callData)); + ctx->d()->engine->regExpCtor()->as<FunctionObject>()->construct(scope, callData); + Scoped<RegExpObject> re(scope, scope.result.asReturnedValue()); r->d()->value = re->value(); r->d()->global = re->global(); diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 4bd91bbedd..655e120c16 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -140,8 +140,8 @@ struct RegExpCtor: FunctionObject int lastMatchStart() { return d()->lastMatchStart; } int lastMatchEnd() { return d()->lastMatchEnd; } - static ReturnedValue construct(const Managed *m, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 0e10f7699e..a69874cacb 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -252,11 +252,11 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) result->append(QLatin1Char('+')); result->append(QString::number(decpt - 1)); } else if (decpt <= 0) { - result->prepend(QString::fromLatin1("0.%1").arg(QString().fill(zero, -decpt))); + result->prepend(QLatin1String("0.") + QString(-decpt, zero)); } else if (decpt < result->length()) { result->insert(decpt, dot); } else { - result->append(QString().fill(zero, decpt - result->length())); + result->append(QString(decpt - result->length(), zero)); } if (sign) @@ -298,14 +298,14 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) result->prepend(QLatin1Char('-')); } -ReturnedValue Runtime::closure(ExecutionEngine *engine, int functionId) +ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId) { QV4::Function *clos = engine->current->compilationUnit->runtimeFunctions[functionId]; Q_ASSERT(clos); return FunctionObject::createScriptFunction(engine->currentContext, clos)->asReturnedValue(); } -ReturnedValue Runtime::deleteElement(ExecutionEngine *engine, const Value &base, const Value &index) +ReturnedValue Runtime::method_deleteElement(ExecutionEngine *engine, const Value &base, const Value &index) { Scope scope(engine); ScopedObject o(scope, base); @@ -317,17 +317,17 @@ ReturnedValue Runtime::deleteElement(ExecutionEngine *engine, const Value &base, } ScopedString name(scope, index.toString(engine)); - return Runtime::deleteMemberString(engine, base, name); + return method_deleteMemberString(engine, base, name); } -ReturnedValue Runtime::deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex) +ReturnedValue Runtime::method_deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - return deleteMemberString(engine, base, name); + return method_deleteMemberString(engine, base, name); } -ReturnedValue Runtime::deleteMemberString(ExecutionEngine *engine, const Value &base, String *name) +ReturnedValue Runtime::method_deleteMemberString(ExecutionEngine *engine, const Value &base, String *name) { Scope scope(engine); ScopedObject obj(scope, base.toObject(engine)); @@ -336,14 +336,14 @@ ReturnedValue Runtime::deleteMemberString(ExecutionEngine *engine, const Value & return Encode(obj->deleteProperty(name)); } -ReturnedValue Runtime::deleteName(ExecutionEngine *engine, int nameIndex) +ReturnedValue Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); 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) +QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &left, const Value &right) { Scope scope(engine); ScopedFunctionObject f(scope, right.as<FunctionObject>()); @@ -373,7 +373,7 @@ QV4::ReturnedValue Runtime::instanceof(ExecutionEngine *engine, const Value &lef return Encode(false); } -QV4::ReturnedValue Runtime::in(ExecutionEngine *engine, const Value &left, const Value &right) +QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right) { if (!right.isObject()) return engine->throwTypeError(); @@ -387,7 +387,7 @@ QV4::ReturnedValue Runtime::in(ExecutionEngine *engine, const Value &left, const double RuntimeHelpers::stringToNumber(const QString &string) { - QString s = string.trimmed(); + const QStringRef s = QStringRef(&string).trimmed(); if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) return s.toLong(0, 16); bool ok; @@ -438,9 +438,9 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH ScopedValue conv(scope, object->get(meth1)); if (FunctionObject *o = conv->as<FunctionObject>()) { - ScopedValue r(scope, o->call(callData)); - if (r->isPrimitive()) - return r->asReturnedValue(); + o->call(scope, callData); + if (scope.result.isPrimitive()) + return scope.result.asReturnedValue(); } if (engine->hasException) @@ -448,9 +448,9 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH conv = object->get(meth2); if (FunctionObject *o = conv->as<FunctionObject>()) { - ScopedValue r(scope, o->call(callData)); - if (r->isPrimitive()) - return r->asReturnedValue(); + o->call(scope, callData); + if (scope.result.isPrimitive()) + return scope.result.asReturnedValue(); } return engine->throwTypeError(); @@ -562,7 +562,7 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu return Encode(x + y); } -QV4::ReturnedValue Runtime::addString(ExecutionEngine *engine, const Value &left, const Value &right) +QV4::ReturnedValue Runtime::method_addString(ExecutionEngine *engine, const Value &left, const Value &right) { Q_ASSERT(left.isString() || right.isString()); @@ -593,7 +593,7 @@ QV4::ReturnedValue Runtime::addString(ExecutionEngine *engine, const Value &left return (mm->alloc<String>(mm, pleft->stringValue()->d(), pright->stringValue()->d()))->asReturnedValue(); } -void Runtime::setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) +void Runtime::method_setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) { Scope scope(engine); ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); @@ -603,7 +603,7 @@ void Runtime::setProperty(ExecutionEngine *engine, const Value &object, int name o->put(name, value); } -ReturnedValue Runtime::getElement(ExecutionEngine *engine, const Value &object, const Value &index) +ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &object, const Value &index) { Scope scope(engine); uint idx = index.asArrayIndex(); @@ -646,7 +646,7 @@ ReturnedValue Runtime::getElement(ExecutionEngine *engine, const Value &object, return o->get(name); } -void Runtime::setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { Scope scope(engine); ScopedObject o(scope, object.toObject(engine)); @@ -670,7 +670,7 @@ void Runtime::setElement(ExecutionEngine *engine, const Value &object, const Val o->put(name, value); } -ReturnedValue Runtime::foreachIterator(ExecutionEngine *engine, const Value &in) +ReturnedValue Runtime::method_foreachIterator(ExecutionEngine *engine, const Value &in) { Scope scope(engine); ScopedObject o(scope, (Object *)0); @@ -679,7 +679,7 @@ ReturnedValue Runtime::foreachIterator(ExecutionEngine *engine, const Value &in) return engine->newForEachIteratorObject(o)->asReturnedValue(); } -ReturnedValue Runtime::foreachNextPropertyName(const Value &foreach_iterator) +ReturnedValue Runtime::method_foreachNextPropertyName(const Value &foreach_iterator) { Q_ASSERT(foreach_iterator.isObject()); @@ -690,14 +690,14 @@ ReturnedValue Runtime::foreachNextPropertyName(const Value &foreach_iterator) } -void Runtime::setActivationProperty(ExecutionEngine *engine, int nameIndex, const Value &value) +void Runtime::method_setActivationProperty(ExecutionEngine *engine, int nameIndex, const Value &value) { Scope scope(engine); ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); engine->currentContext->setProperty(name, value); } -ReturnedValue Runtime::getProperty(ExecutionEngine *engine, const Value &object, int nameIndex) +ReturnedValue Runtime::method_getProperty(ExecutionEngine *engine, const Value &object, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); @@ -717,7 +717,7 @@ ReturnedValue Runtime::getProperty(ExecutionEngine *engine, const Value &object, return o->get(name); } -ReturnedValue Runtime::getActivationProperty(ExecutionEngine *engine, int nameIndex) +ReturnedValue Runtime::method_getActivationProperty(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); @@ -743,9 +743,9 @@ uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) double dx = RuntimeHelpers::toNumber(x); return dx == y.asDouble(); } else if (x.isBoolean()) { - return Runtime::compareEqual(Primitive::fromDouble((double) x.booleanValue()), y); + return Runtime::method_compareEqual(Primitive::fromDouble((double) x.booleanValue()), y); } else if (y.isBoolean()) { - return Runtime::compareEqual(x, Primitive::fromDouble((double) y.booleanValue())); + return Runtime::method_compareEqual(x, Primitive::fromDouble((double) y.booleanValue())); } else { #ifdef V4_BOOTSTRAP Q_UNIMPLEMENTED(); @@ -753,11 +753,11 @@ uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) if ((x.isNumber() || x.isString()) && y.isObject()) { Scope scope(y.objectValue()->engine()); ScopedValue py(scope, RuntimeHelpers::toPrimitive(y, PREFERREDTYPE_HINT)); - return Runtime::compareEqual(x, py); + return Runtime::method_compareEqual(x, py); } else if (x.isObject() && (y.isNumber() || y.isString())) { Scope scope(x.objectValue()->engine()); ScopedValue px(scope, RuntimeHelpers::toPrimitive(x, PREFERREDTYPE_HINT)); - return Runtime::compareEqual(px, y); + return Runtime::method_compareEqual(px, y); } #endif } @@ -780,7 +780,7 @@ Bool RuntimeHelpers::strictEqual(const Value &x, const Value &y) return false; } -QV4::Bool Runtime::compareGreaterThan(const Value &l, const Value &r) +QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -804,7 +804,7 @@ QV4::Bool Runtime::compareGreaterThan(const Value &l, const Value &r) QV4::Scope scope(e); QV4::ScopedValue pl(scope, RuntimeHelpers::toPrimitive(l, QV4::NUMBER_HINT)); QV4::ScopedValue pr(scope, RuntimeHelpers::toPrimitive(r, QV4::NUMBER_HINT)); - return Runtime::compareGreaterThan(pl, pr); + return Runtime::method_compareGreaterThan(pl, pr); #endif } @@ -813,7 +813,7 @@ QV4::Bool Runtime::compareGreaterThan(const Value &l, const Value &r) return dl > dr; } -QV4::Bool Runtime::compareLessThan(const Value &l, const Value &r) +QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -837,7 +837,7 @@ QV4::Bool Runtime::compareLessThan(const Value &l, const Value &r) QV4::Scope scope(e); QV4::ScopedValue pl(scope, RuntimeHelpers::toPrimitive(l, QV4::NUMBER_HINT)); QV4::ScopedValue pr(scope, RuntimeHelpers::toPrimitive(r, QV4::NUMBER_HINT)); - return Runtime::compareLessThan(pl, pr); + return Runtime::method_compareLessThan(pl, pr); #endif } @@ -846,7 +846,7 @@ QV4::Bool Runtime::compareLessThan(const Value &l, const Value &r) return dl < dr; } -QV4::Bool Runtime::compareGreaterEqual(const Value &l, const Value &r) +QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -870,7 +870,7 @@ QV4::Bool Runtime::compareGreaterEqual(const Value &l, const Value &r) QV4::Scope scope(e); QV4::ScopedValue pl(scope, RuntimeHelpers::toPrimitive(l, QV4::NUMBER_HINT)); QV4::ScopedValue pr(scope, RuntimeHelpers::toPrimitive(r, QV4::NUMBER_HINT)); - return Runtime::compareGreaterEqual(pl, pr); + return Runtime::method_compareGreaterEqual(pl, pr); #endif } @@ -879,7 +879,7 @@ QV4::Bool Runtime::compareGreaterEqual(const Value &l, const Value &r) return dl >= dr; } -QV4::Bool Runtime::compareLessEqual(const Value &l, const Value &r) +QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -903,7 +903,7 @@ QV4::Bool Runtime::compareLessEqual(const Value &l, const Value &r) QV4::Scope scope(e); QV4::ScopedValue pl(scope, RuntimeHelpers::toPrimitive(l, QV4::NUMBER_HINT)); QV4::ScopedValue pr(scope, RuntimeHelpers::toPrimitive(r, QV4::NUMBER_HINT)); - return Runtime::compareLessEqual(pl, pr); + return Runtime::method_compareLessEqual(pl, pr); #endif } @@ -913,26 +913,26 @@ QV4::Bool Runtime::compareLessEqual(const Value &l, const Value &r) } #ifndef V4_BOOTSTRAP -Bool Runtime::compareInstanceof(ExecutionEngine *engine, const Value &left, const Value &right) +Bool Runtime::method_compareInstanceof(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); Scope scope(engine); - ScopedValue v(scope, Runtime::instanceof(engine, left, right)); + ScopedValue v(scope, method_instanceof(engine, left, right)); return v->booleanValue(); } -uint Runtime::compareIn(ExecutionEngine *engine, const Value &left, const Value &right) +uint Runtime::method_compareIn(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); Scope scope(engine); - ScopedValue v(scope, Runtime::in(engine, left, right)); + ScopedValue v(scope, method_in(engine, left, right)); return v->booleanValue(); } -ReturnedValue Runtime::callGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData) +ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData) { Scope scope(engine); Q_ASSERT(callData->thisObject.isUndefined()); @@ -943,14 +943,17 @@ ReturnedValue Runtime::callGlobalLookup(ExecutionEngine *engine, uint index, Cal return engine->throwTypeError(); 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); + if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) { + static_cast<EvalFunction *>(o.getPointer())->evalCall(scope, callData, true); + } else { + o->call(scope, callData); + } - return o->call(callData); + return scope.result.asReturnedValue(); } -ReturnedValue Runtime::callActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) +ReturnedValue Runtime::method_callActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) { Q_ASSERT(callData->thisObject.isUndefined()); Scope scope(engine); @@ -974,36 +977,41 @@ ReturnedValue Runtime::callActivationProperty(ExecutionEngine *engine, int nameI } if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) { - return static_cast<EvalFunction *>(o)->evalCall(callData, true); + static_cast<EvalFunction *>(o)->evalCall(scope, callData, true); + } else { + o->call(scope, callData); } - return o->call(callData); + return scope.result.asReturnedValue(); } -ReturnedValue Runtime::callQmlScopeObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) +ReturnedValue Runtime::method_callQmlScopeObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) { Scope scope(engine); - ScopedFunctionObject o(scope, getQmlScopeObjectProperty(engine, callData->thisObject, propertyIndex)); + ScopedFunctionObject o(scope, method_getQmlScopeObjectProperty(engine, callData->thisObject, propertyIndex)); if (!o) { QString error = QStringLiteral("Property '%1' of scope object is not a function").arg(propertyIndex); return engine->throwTypeError(error); } - return o->call(callData); + + o->call(scope, callData); + return scope.result.asReturnedValue(); } -ReturnedValue Runtime::callQmlContextObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) +ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) { Scope scope(engine); - ScopedFunctionObject o(scope, getQmlContextObjectProperty(engine, callData->thisObject, propertyIndex)); + ScopedFunctionObject o(scope, method_getQmlContextObjectProperty(engine, callData->thisObject, propertyIndex)); if (!o) { QString error = QStringLiteral("Property '%1' of context object is not a function").arg(propertyIndex); return engine->throwTypeError(error); } - return o->call(callData); + o->call(scope, callData); + return scope.result.asReturnedValue(); } -ReturnedValue Runtime::callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) +ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) { Scope scope(engine); ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); @@ -1022,26 +1030,31 @@ ReturnedValue Runtime::callProperty(ExecutionEngine *engine, int nameIndex, Call } ScopedFunctionObject o(scope, baseObject->get(name)); - if (!o) { + if (o) { + o->call(scope, callData); + return scope.result.asReturnedValue(); + } else { QString error = QStringLiteral("Property '%1' of object %2 is not a function").arg(name->toQString(), callData->thisObject.toQStringNoThrow()); return engine->throwTypeError(error); } - return o->call(callData); } -ReturnedValue Runtime::callPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData) +ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData) { Lookup *l = engine->current->lookups + index; Value v; v = l->getter(l, engine, callData->thisObject); - if (!v.isObject()) + if (v.isObject()) { + Scope scope(engine); + v.objectValue()->call(scope, callData); + return scope.result.asReturnedValue(); + } else { return engine->throwTypeError(); - - return v.objectValue()->call(callData); + } } -ReturnedValue Runtime::callElement(ExecutionEngine *engine, const Value &index, CallData *callData) +ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, const Value &index, CallData *callData) { Scope scope(engine); ScopedObject baseObject(scope, callData->thisObject.toObject(engine)); @@ -1055,33 +1068,38 @@ ReturnedValue Runtime::callElement(ExecutionEngine *engine, const Value &index, if (!o) return engine->throwTypeError(); - return o->call(callData); + o->call(scope, callData); + return scope.result.asReturnedValue(); } -ReturnedValue Runtime::callValue(ExecutionEngine *engine, const Value &func, CallData *callData) +ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &func, CallData *callData) { if (!func.isObject()) return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); - return func.objectValue()->call(callData); + Scope scope(engine); + func.objectValue()->call(scope, callData); + return scope.result.asReturnedValue(); } -ReturnedValue Runtime::constructGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData) +ReturnedValue Runtime::method_constructGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData) { Scope scope(engine); Q_ASSERT(callData->thisObject.isUndefined()); Lookup *l = engine->current->lookups + index; ScopedObject f(scope, l->globalGetter(l, engine)); - if (!f) + if (f) { + f->construct(scope, callData); + return scope.result.asReturnedValue(); + } else { return engine->throwTypeError(); - - return f->construct(callData); + } } -ReturnedValue Runtime::constructActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) +ReturnedValue Runtime::method_constructActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) { Scope scope(engine); ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); @@ -1093,19 +1111,22 @@ ReturnedValue Runtime::constructActivationProperty(ExecutionEngine *engine, int if (!f) return engine->throwTypeError(); - return f->construct(callData); + f->construct(scope, callData); + return scope.result.asReturnedValue(); } -ReturnedValue Runtime::constructValue(ExecutionEngine *engine, const Value &func, CallData *callData) +ReturnedValue Runtime::method_constructValue(ExecutionEngine *engine, const Value &func, CallData *callData) { const Object *f = func.as<Object>(); if (!f) return engine->throwTypeError(); - return f->construct(callData); + Scope scope(engine); + f->construct(scope, callData); + return scope.result.asReturnedValue(); } -ReturnedValue Runtime::constructProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) +ReturnedValue Runtime::method_constructProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) { Scope scope(engine); ScopedObject thisObject(scope, callData->thisObject.toObject(engine)); @@ -1114,31 +1135,38 @@ ReturnedValue Runtime::constructProperty(ExecutionEngine *engine, int nameIndex, return Encode::undefined(); ScopedObject f(scope, thisObject->get(name)); - if (!f) + if (f) { + Scope scope(engine); + f->construct(scope, callData); + return scope.result.asReturnedValue(); + } else { return engine->throwTypeError(); - - return f->construct(callData); + } } -ReturnedValue Runtime::constructPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData) +ReturnedValue Runtime::method_constructPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData) { Lookup *l = engine->current->lookups + index; Value v; v = l->getter(l, engine, callData->thisObject); - if (!v.isObject()) + if (v.isObject()) { + Scope scope(engine); + ScopedValue result(scope); + v.objectValue()->construct(scope, callData); + return scope.result.asReturnedValue(); + } else { return engine->throwTypeError(); - - return v.objectValue()->construct(callData); + } } -void Runtime::throwException(ExecutionEngine *engine, const Value &value) +void Runtime::method_throwException(ExecutionEngine *engine, const Value &value) { if (!value.isEmpty()) engine->throwError(value); } -ReturnedValue Runtime::typeofValue(ExecutionEngine *engine, const Value &value) +ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value &value) { Scope scope(engine); ScopedString res(scope); @@ -1167,39 +1195,37 @@ ReturnedValue Runtime::typeofValue(ExecutionEngine *engine, const Value &value) return res.asReturnedValue(); } -QV4::ReturnedValue Runtime::typeofName(ExecutionEngine *engine, int nameIndex) +QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); 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); + return method_typeofValue(engine, prop); } #ifndef V4_BOOTSTRAP -ReturnedValue Runtime::typeofScopeObjectProperty(ExecutionEngine *engine, const Value &context, - int propertyIndex) +ReturnedValue Runtime::method_typeofScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex) { Scope scope(engine); - ScopedValue prop(scope, getQmlScopeObjectProperty(engine, context, propertyIndex)); + ScopedValue prop(scope, method_getQmlScopeObjectProperty(engine, context, propertyIndex)); if (scope.engine->hasException) return Encode::undefined(); - return Runtime::typeofValue(engine, prop); + return method_typeofValue(engine, prop); } -ReturnedValue Runtime::typeofContextObjectProperty(ExecutionEngine *engine, const Value &context, - int propertyIndex) +ReturnedValue Runtime::method_typeofContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex) { Scope scope(engine); - ScopedValue prop(scope, getQmlContextObjectProperty(engine, context, propertyIndex)); + ScopedValue prop(scope, method_getQmlContextObjectProperty(engine, context, propertyIndex)); if (scope.engine->hasException) return Encode::undefined(); - return Runtime::typeofValue(engine, prop); + return method_typeofValue(engine, prop); } #endif // V4_BOOTSTRAP -QV4::ReturnedValue Runtime::typeofMember(ExecutionEngine *engine, const Value &base, int nameIndex) +QV4::ReturnedValue Runtime::method_typeofMember(ExecutionEngine *engine, const Value &base, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); @@ -1207,10 +1233,10 @@ QV4::ReturnedValue Runtime::typeofMember(ExecutionEngine *engine, const Value &b if (scope.engine->hasException) return Encode::undefined(); ScopedValue prop(scope, obj->get(name)); - return Runtime::typeofValue(engine, prop); + return method_typeofValue(engine, prop); } -QV4::ReturnedValue Runtime::typeofElement(ExecutionEngine *engine, const Value &base, const Value &index) +QV4::ReturnedValue Runtime::method_typeofElement(ExecutionEngine *engine, const Value &base, const Value &index) { Scope scope(engine); ScopedString name(scope, index.toString(engine)); @@ -1218,10 +1244,10 @@ QV4::ReturnedValue Runtime::typeofElement(ExecutionEngine *engine, const Value & if (scope.engine->hasException) return Encode::undefined(); ScopedValue prop(scope, obj->get(name)); - return Runtime::typeofValue(engine, prop); + return method_typeofValue(engine, prop); } -ReturnedValue Runtime::unwindException(ExecutionEngine *engine) +ReturnedValue Runtime::method_unwindException(ExecutionEngine *engine) { if (!engine->hasException) return Primitive::emptyValue().asReturnedValue(); @@ -1233,39 +1259,39 @@ ReturnedValue Runtime::unwindException(ExecutionEngine *engine) * * Instead the push/pop pair acts as a non local scope. */ -void Runtime::pushWithScope(const Value &o, ExecutionEngine *engine) +void Runtime::method_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) +void Runtime::method_pushCatchScope(NoThrowEngine *engine, int exceptionVarNameIndex) { 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) +void Runtime::method_popScope(ExecutionEngine *engine) { Q_ASSERT(engine->jsStackTop = engine->currentContext + 2); engine->popContext(); engine->jsStackTop -= 2; } -void Runtime::declareVar(ExecutionEngine *engine, bool deletable, int nameIndex) +void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); engine->currentContext->createMutableBinding(name, deletable); } -ReturnedValue Runtime::arrayLiteral(ExecutionEngine *engine, Value *values, uint length) +ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *values, uint length) { return engine->newArrayObject(values, length)->asReturnedValue(); } -ReturnedValue Runtime::objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags) +ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags) { Scope scope(engine); QV4::InternalClass *klass = engine->current->compilationUnit->runtimeClasses[classId]; @@ -1307,7 +1333,7 @@ ReturnedValue Runtime::objectLiteral(ExecutionEngine *engine, const QV4::Value * return o.asReturnedValue(); } -QV4::ReturnedValue Runtime::setupArgumentsObject(ExecutionEngine *engine) +QV4::ReturnedValue Runtime::method_setupArgumentsObject(ExecutionEngine *engine) { Q_ASSERT(engine->current->type == Heap::ExecutionContext::Type_CallContext); QV4::CallContext *c = static_cast<QV4::CallContext *>(engine->currentContext); @@ -1317,7 +1343,7 @@ QV4::ReturnedValue Runtime::setupArgumentsObject(ExecutionEngine *engine) #endif // V4_BOOTSTRAP -QV4::ReturnedValue Runtime::increment(const Value &value) +QV4::ReturnedValue Runtime::method_increment(const Value &value) { TRACE1(value); @@ -1329,7 +1355,7 @@ QV4::ReturnedValue Runtime::increment(const Value &value) } } -QV4::ReturnedValue Runtime::decrement(const Value &value) +QV4::ReturnedValue Runtime::method_decrement(const Value &value) { TRACE1(value); @@ -1364,31 +1390,31 @@ QV4::ReturnedValue RuntimeHelpers::toObject(ExecutionEngine *engine, const Value #endif // V4_BOOTSTRAP -ReturnedValue Runtime::toDouble(const Value &value) +ReturnedValue Runtime::method_toDouble(const Value &value) { TRACE1(value); return Encode(value.toNumber()); } -int Runtime::toInt(const Value &value) +int Runtime::method_toInt(const Value &value) { TRACE1(value); return value.toInt32(); } -int Runtime::doubleToInt(const double &d) +int Runtime::method_doubleToInt(const double &d) { TRACE0(); return Primitive::toInt32(d); } -unsigned Runtime::toUInt(const Value &value) +unsigned Runtime::method_toUInt(const Value &value) { TRACE1(value); return value.toUInt32(); } -unsigned Runtime::doubleToUInt(const double &d) +unsigned Runtime::method_doubleToUInt(const double &d) { TRACE0(); return Primitive::toUInt32(d); @@ -1396,17 +1422,17 @@ unsigned Runtime::doubleToUInt(const double &d) #ifndef V4_BOOTSTRAP -ReturnedValue Runtime::getQmlContext(NoThrowEngine *engine) +ReturnedValue Runtime::method_getQmlContext(NoThrowEngine *engine) { return engine->qmlContext()->asReturnedValue(); } -ReturnedValue Runtime::regexpLiteral(ExecutionEngine *engine, int id) +ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id) { return engine->current->compilationUnit->runtimeRegularExpressions[id].asReturnedValue(); } -ReturnedValue Runtime::getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) +ReturnedValue Runtime::method_getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) { Scope scope(engine); QV4::Scoped<QObjectWrapper> wrapper(scope, object); @@ -1417,7 +1443,7 @@ ReturnedValue Runtime::getQmlQObjectProperty(ExecutionEngine *engine, const Valu return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->object(), propertyIndex, captureRequired); } -QV4::ReturnedValue Runtime::getQmlAttachedProperty(ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex) +QV4::ReturnedValue Runtime::method_getQmlAttachedProperty(ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex) { QObject *scopeObject = engine->qmlScopeObject(); QObject *attachedObject = qmlAttachedPropertiesObjectById(attachedPropertiesId, scopeObject); @@ -1427,19 +1453,19 @@ QV4::ReturnedValue Runtime::getQmlAttachedProperty(ExecutionEngine *engine, int return QV4::QObjectWrapper::getProperty(engine, attachedObject, propertyIndex, /*captureRequired*/true); } -ReturnedValue Runtime::getQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex) +ReturnedValue Runtime::method_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) +ReturnedValue Runtime::method_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) +ReturnedValue Runtime::method_getQmlSingletonQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) { Scope scope(engine); QV4::Scoped<QmlTypeWrapper> wrapper(scope, object); @@ -1450,7 +1476,7 @@ ReturnedValue Runtime::getQmlSingletonQObjectProperty(ExecutionEngine *engine, c return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->singletonObject(), propertyIndex, captureRequired); } -ReturnedValue Runtime::getQmlIdObject(ExecutionEngine *engine, const Value &c, uint index) +ReturnedValue Runtime::method_getQmlIdObject(ExecutionEngine *engine, const Value &c, uint index) { Scope scope(engine); const QmlContext &qmlContext = static_cast<const QmlContext &>(c); @@ -1465,19 +1491,19 @@ ReturnedValue Runtime::getQmlIdObject(ExecutionEngine *engine, const Value &c, u return QObjectWrapper::wrap(engine, context->idValues[index].data()); } -void Runtime::setQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) +void Runtime::method_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) +void Runtime::method_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) +void Runtime::method_setQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value) { Scope scope(engine); QV4::Scoped<QObjectWrapper> wrapper(scope, object); @@ -1488,7 +1514,7 @@ void Runtime::setQmlQObjectProperty(ExecutionEngine *engine, const Value &object wrapper->setProperty(engine, propertyIndex, value); } -ReturnedValue Runtime::getQmlImportedScripts(NoThrowEngine *engine) +ReturnedValue Runtime::method_getQmlImportedScripts(NoThrowEngine *engine) { QQmlContextData *context = engine->callingQmlContext(); if (!context) @@ -1496,14 +1522,14 @@ ReturnedValue Runtime::getQmlImportedScripts(NoThrowEngine *engine) return context->importedScripts.value(); } -QV4::ReturnedValue Runtime::getQmlSingleton(QV4::NoThrowEngine *engine, int nameIndex) +QV4::ReturnedValue Runtime::method_getQmlSingleton(QV4::NoThrowEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); return engine->qmlSingletonWrapper(name); } -void Runtime::convertThisToObject(ExecutionEngine *engine) +void Runtime::method_convertThisToObject(ExecutionEngine *engine) { Value *t = &engine->current->callData->thisObject; if (t->isObject()) @@ -1515,6 +1541,289 @@ void Runtime::convertThisToObject(ExecutionEngine *engine) } } +ReturnedValue Runtime::method_uPlus(const Value &value) +{ + TRACE1(value); + + if (value.isNumber()) + return value.asReturnedValue(); + if (value.integerCompatible()) + return Encode(value.int_32()); + + double n = value.toNumberImpl(); + return Encode(n); +} + +ReturnedValue Runtime::method_uMinus(const Value &value) +{ + TRACE1(value); + + // +0 != -0, so we need to convert to double when negating 0 + if (value.isInteger() && value.integerValue()) + return Encode(-value.integerValue()); + else { + double n = RuntimeHelpers::toNumber(value); + return Encode(-n); + } +} + +ReturnedValue Runtime::method_complement(const Value &value) +{ + TRACE1(value); + + int n = value.toInt32(); + return Encode((int)~n); +} + +ReturnedValue Runtime::method_uNot(const Value &value) +{ + TRACE1(value); + + bool b = value.toBoolean(); + return Encode(!b); +} + +// binary operators +ReturnedValue Runtime::method_bitOr(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + int rval = right.toInt32(); + return Encode(lval | rval); +} + +ReturnedValue Runtime::method_bitXor(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + int rval = right.toInt32(); + return Encode(lval ^ rval); +} + +ReturnedValue Runtime::method_bitAnd(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + int rval = right.toInt32(); + return Encode(lval & rval); +} + +#ifndef V4_BOOTSTRAP +ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, const Value &right) +{ + TRACE2(left, right); + + if (Q_LIKELY(left.isInteger() && right.isInteger())) + return add_int32(left.integerValue(), right.integerValue()); + if (left.isNumber() && right.isNumber()) + return Primitive::fromDouble(left.asDouble() + right.asDouble()).asReturnedValue(); + + return RuntimeHelpers::addHelper(engine, left, right); +} +#endif // V4_BOOTSTRAP + +ReturnedValue Runtime::method_sub(const Value &left, const Value &right) +{ + TRACE2(left, right); + + if (Q_LIKELY(left.isInteger() && right.isInteger())) + return sub_int32(left.integerValue(), right.integerValue()); + + double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl(); + double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl(); + + return Primitive::fromDouble(lval - rval).asReturnedValue(); +} + +ReturnedValue Runtime::method_mul(const Value &left, const Value &right) +{ + TRACE2(left, right); + + if (Q_LIKELY(left.isInteger() && right.isInteger())) + return mul_int32(left.integerValue(), right.integerValue()); + + double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl(); + double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl(); + + return Primitive::fromDouble(lval * rval).asReturnedValue(); +} + +ReturnedValue Runtime::method_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(); +} + +ReturnedValue Runtime::method_mod(const Value &left, const Value &right) +{ + TRACE2(left, right); + + if (Value::integerCompatible(left, right) && right.integerValue() != 0) { + int intRes = left.integerValue() % right.integerValue(); + if (intRes != 0 || left.integerValue() >= 0) + return Encode(intRes); + } + + double lval = RuntimeHelpers::toNumber(left); + double rval = RuntimeHelpers::toNumber(right); +#ifdef fmod +# undef fmod +#endif + return Primitive::fromDouble(std::fmod(lval, rval)).asReturnedValue(); +} + +ReturnedValue Runtime::method_shl(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + int rval = right.toInt32() & 0x1f; + return Encode((int)(lval << rval)); +} + +ReturnedValue Runtime::method_shr(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + unsigned rval = right.toUInt32() & 0x1f; + return Encode((int)(lval >> rval)); +} + +ReturnedValue Runtime::method_ushr(const Value &left, const Value &right) +{ + TRACE2(left, right); + + unsigned lval = left.toUInt32(); + unsigned rval = right.toUInt32() & 0x1f; + uint res = lval >> rval; + + return Encode(res); +} + +ReturnedValue Runtime::method_greaterThan(const Value &left, const Value &right) +{ + TRACE2(left, right); + + bool r = method_compareGreaterThan(left, right); + return Encode(r); +} + +ReturnedValue Runtime::method_lessThan(const Value &left, const Value &right) +{ + TRACE2(left, right); + + bool r = method_compareLessThan(left, right); + return Encode(r); +} + +ReturnedValue Runtime::method_greaterEqual(const Value &left, const Value &right) +{ + TRACE2(left, right); + + bool r = method_compareGreaterEqual(left, right); + return Encode(r); +} + +ReturnedValue Runtime::method_lessEqual(const Value &left, const Value &right) +{ + TRACE2(left, right); + + bool r = method_compareLessEqual(left, right); + return Encode(r); +} + +Bool Runtime::method_compareEqual(const Value &left, const Value &right) +{ + TRACE2(left, right); + + if (left.rawValue() == right.rawValue()) + // NaN != NaN + return !left.isNaN(); + + if (left.type() == right.type()) { + if (!left.isManaged()) + return false; + if (left.isString() == right.isString()) + return left.cast<Managed>()->isEqualTo(right.cast<Managed>()); + } + + return RuntimeHelpers::equalHelper(left, right); +} + +ReturnedValue Runtime::method_equal(const Value &left, const Value &right) +{ + TRACE2(left, right); + + bool r = method_compareEqual(left, right); + return Encode(r); +} + +ReturnedValue Runtime::method_notEqual(const Value &left, const Value &right) +{ + TRACE2(left, right); + + bool r = !method_compareEqual(left, right); + return Encode(r); +} + +ReturnedValue Runtime::method_strictEqual(const Value &left, const Value &right) +{ + TRACE2(left, right); + + bool r = RuntimeHelpers::strictEqual(left, right); + return Encode(r); +} + +ReturnedValue Runtime::method_strictNotEqual(const Value &left, const Value &right) +{ + TRACE2(left, right); + + bool r = ! RuntimeHelpers::strictEqual(left, right); + return Encode(r); +} + +Bool Runtime::method_compareNotEqual(const Value &left, const Value &right) +{ + TRACE2(left, right); + + return !Runtime::method_compareEqual(left, right); +} + +Bool Runtime::method_compareStrictEqual(const Value &left, const Value &right) +{ + TRACE2(left, right); + + return RuntimeHelpers::strictEqual(left, right); +} + +Bool Runtime::method_compareStrictNotEqual(const Value &left, const Value &right) +{ + TRACE2(left, right); + + return ! RuntimeHelpers::strictEqual(left, right); +} + +Bool Runtime::method_toBoolean(const Value &value) +{ + return value.toBoolean(); +} + #endif // V4_BOOTSTRAP } // namespace QV4 diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index b63777e164..88c09ec5f9 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -55,10 +55,9 @@ #include "qv4context_p.h" #include "qv4engine_p.h" #include "qv4math_p.h" - +#include "qv4runtimeapi_p.h" #include <QtCore/qnumeric.h> - QT_BEGIN_NAMESPACE class QQmlAccessors; @@ -100,156 +99,6 @@ enum TypeHint { STRING_HINT }; -// This is a trick to tell the code generators that functions taking a NoThrowContext won't -// throw exceptions and therefore don't need a check after the call. - -#ifndef V4_BOOTSTRAP -struct NoThrowEngine : public ExecutionEngine -{ -}; -#else -struct NoThrowEngine; -#endif - -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); - static ReturnedValue callValue(ExecutionEngine *engine, const Value &func, CallData *callData); - - // construct - static ReturnedValue constructGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData); - static ReturnedValue constructActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData); - static ReturnedValue constructProperty(ExecutionEngine *engine, int nameIndex, CallData *callData); - static ReturnedValue constructPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData); - static ReturnedValue constructValue(ExecutionEngine *engine, const Value &func, CallData *callData); - - // set & get - static void setActivationProperty(ExecutionEngine *engine, int nameIndex, const Value &value); - static void setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value); - static void setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value); - static ReturnedValue getProperty(ExecutionEngine *engine, const Value &object, int nameIndex); - static ReturnedValue getActivationProperty(ExecutionEngine *engine, int nameIndex); - static ReturnedValue getElement(ExecutionEngine *engine, const Value &object, const Value &index); - - // 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); - - // delete - static ReturnedValue deleteElement(ExecutionEngine *engine, const Value &base, const Value &index); - static ReturnedValue deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex); - static ReturnedValue deleteMemberString(ExecutionEngine *engine, const Value &base, String *name); - static ReturnedValue deleteName(ExecutionEngine *engine, int nameIndex); - - // exceptions & scopes - static void throwException(ExecutionEngine *engine, const Value &value); - static ReturnedValue unwindException(ExecutionEngine *engine); - static void pushWithScope(const Value &o, ExecutionEngine *engine); - static void pushCatchScope(NoThrowEngine *engine, int exceptionVarNameIndex); - static void popScope(ExecutionEngine *engine); - - // closures - static ReturnedValue closure(ExecutionEngine *engine, int functionId); - - // function header - static void declareVar(ExecutionEngine *engine, bool deletable, int nameIndex); - static ReturnedValue setupArgumentsObject(ExecutionEngine *engine); - static void convertThisToObject(ExecutionEngine *engine); - - // literals - static ReturnedValue arrayLiteral(ExecutionEngine *engine, Value *values, uint length); - static ReturnedValue objectLiteral(ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags); - static ReturnedValue regexpLiteral(ExecutionEngine *engine, int id); - - // foreach - static ReturnedValue foreachIterator(ExecutionEngine *engine, const Value &in); - static ReturnedValue foreachNextPropertyName(const Value &foreach_iterator); - - // unary operators - typedef ReturnedValue (*UnaryOperation)(const Value &value); - static ReturnedValue uPlus(const Value &value); - static ReturnedValue uMinus(const Value &value); - static ReturnedValue uNot(const Value &value); - static ReturnedValue complement(const Value &value); - static ReturnedValue increment(const Value &value); - static ReturnedValue decrement(const Value &value); - - // binary operators - typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right); - typedef ReturnedValue (*BinaryOperationContext)(ExecutionEngine *engine, const Value &left, const Value &right); - - static ReturnedValue instanceof(ExecutionEngine *engine, const Value &left, const Value &right); - static ReturnedValue in(ExecutionEngine *engine, const Value &left, const Value &right); - static ReturnedValue add(ExecutionEngine *engine, const Value &left, const Value &right); - static ReturnedValue addString(ExecutionEngine *engine, const Value &left, const Value &right); - static ReturnedValue bitOr(const Value &left, const Value &right); - static ReturnedValue bitXor(const Value &left, const Value &right); - static ReturnedValue bitAnd(const Value &left, const Value &right); - static ReturnedValue sub(const Value &left, const Value &right); - static ReturnedValue mul(const Value &left, const Value &right); - static ReturnedValue div(const Value &left, const Value &right); - static ReturnedValue mod(const Value &left, const Value &right); - static ReturnedValue shl(const Value &left, const Value &right); - static ReturnedValue shr(const Value &left, const Value &right); - static ReturnedValue ushr(const Value &left, const Value &right); - static ReturnedValue greaterThan(const Value &left, const Value &right); - static ReturnedValue lessThan(const Value &left, const Value &right); - static ReturnedValue greaterEqual(const Value &left, const Value &right); - static ReturnedValue lessEqual(const Value &left, const Value &right); - static ReturnedValue equal(const Value &left, const Value &right); - static ReturnedValue notEqual(const Value &left, const Value &right); - static ReturnedValue strictEqual(const Value &left, const Value &right); - static ReturnedValue strictNotEqual(const Value &left, const Value &right); - - // comparisons - typedef Bool (*CompareOperation)(const Value &left, const Value &right); - static Bool compareGreaterThan(const Value &l, const Value &r); - static Bool compareLessThan(const Value &l, const Value &r); - static Bool compareGreaterEqual(const Value &l, const Value &r); - static Bool compareLessEqual(const Value &l, const Value &r); - static Bool compareEqual(const Value &left, const Value &right); - static Bool compareNotEqual(const Value &left, const Value &right); - static Bool compareStrictEqual(const Value &left, const Value &right); - static Bool compareStrictNotEqual(const Value &left, const Value &right); - - typedef Bool (*CompareOperationContext)(ExecutionEngine *engine, const Value &left, const Value &right); - static Bool compareInstanceof(ExecutionEngine *engine, const Value &left, const Value &right); - static Bool compareIn(ExecutionEngine *engine, const Value &left, const Value &right); - - // conversions - static Bool toBoolean(const Value &value); - static ReturnedValue toDouble(const Value &value); - static int toInt(const Value &value); - static int doubleToInt(const double &d); - static unsigned toUInt(const Value &value); - static unsigned doubleToUInt(const double &d); - - // qml - 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(const Object *object, int typeHint); static ReturnedValue toPrimitive(const Value &value, int typeHint); @@ -287,287 +136,6 @@ inline double RuntimeHelpers::toNumber(const Value &value) { return value.toNumber(); } - -inline ReturnedValue Runtime::uPlus(const Value &value) -{ - TRACE1(value); - - if (value.isNumber()) - return value.asReturnedValue(); - if (value.integerCompatible()) - return Encode(value.int_32()); - - double n = value.toNumberImpl(); - return Encode(n); -} - -inline ReturnedValue Runtime::uMinus(const Value &value) -{ - TRACE1(value); - - // +0 != -0, so we need to convert to double when negating 0 - if (value.isInteger() && value.integerValue()) - return Encode(-value.integerValue()); - else { - double n = RuntimeHelpers::toNumber(value); - return Encode(-n); - } -} - -inline ReturnedValue Runtime::complement(const Value &value) -{ - TRACE1(value); - - int n = value.toInt32(); - return Encode((int)~n); -} - -inline ReturnedValue Runtime::uNot(const Value &value) -{ - TRACE1(value); - - bool b = value.toBoolean(); - return Encode(!b); -} - -// binary operators -inline ReturnedValue Runtime::bitOr(const Value &left, const Value &right) -{ - TRACE2(left, right); - - int lval = left.toInt32(); - int rval = right.toInt32(); - return Encode(lval | rval); -} - -inline ReturnedValue Runtime::bitXor(const Value &left, const Value &right) -{ - TRACE2(left, right); - - int lval = left.toInt32(); - int rval = right.toInt32(); - return Encode(lval ^ rval); -} - -inline ReturnedValue Runtime::bitAnd(const Value &left, const Value &right) -{ - TRACE2(left, right); - - int lval = left.toInt32(); - int rval = right.toInt32(); - return Encode(lval & rval); -} - -#ifndef V4_BOOTSTRAP -inline ReturnedValue Runtime::add(ExecutionEngine *engine, const Value &left, const Value &right) -{ - TRACE2(left, right); - - if (Q_LIKELY(left.isInteger() && right.isInteger())) - return add_int32(left.integerValue(), right.integerValue()); - if (left.isNumber() && right.isNumber()) - return Primitive::fromDouble(left.asDouble() + right.asDouble()).asReturnedValue(); - - return RuntimeHelpers::addHelper(engine, left, right); -} -#endif // V4_BOOTSTRAP - -inline ReturnedValue Runtime::sub(const Value &left, const Value &right) -{ - TRACE2(left, right); - - if (Q_LIKELY(left.isInteger() && right.isInteger())) - return sub_int32(left.integerValue(), right.integerValue()); - - double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl(); - double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl(); - - return Primitive::fromDouble(lval - rval).asReturnedValue(); -} - -inline ReturnedValue Runtime::mul(const Value &left, const Value &right) -{ - TRACE2(left, right); - - if (Q_LIKELY(left.isInteger() && right.isInteger())) - return mul_int32(left.integerValue(), right.integerValue()); - - double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl(); - double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl(); - - return Primitive::fromDouble(lval * rval).asReturnedValue(); -} - -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(); -} - -inline ReturnedValue Runtime::mod(const Value &left, const Value &right) -{ - TRACE2(left, right); - - if (Value::integerCompatible(left, right) && right.integerValue() != 0) { - int intRes = left.integerValue() % right.integerValue(); - if (intRes != 0 || left.integerValue() >= 0) - return Encode(intRes); - } - - double lval = RuntimeHelpers::toNumber(left); - double rval = RuntimeHelpers::toNumber(right); - return Primitive::fromDouble(std::fmod(lval, rval)).asReturnedValue(); -} - -inline ReturnedValue Runtime::shl(const Value &left, const Value &right) -{ - TRACE2(left, right); - - int lval = left.toInt32(); - int rval = right.toInt32() & 0x1f; - return Encode((int)(lval << rval)); -} - -inline ReturnedValue Runtime::shr(const Value &left, const Value &right) -{ - TRACE2(left, right); - - int lval = left.toInt32(); - unsigned rval = right.toUInt32() & 0x1f; - return Encode((int)(lval >> rval)); -} - -inline ReturnedValue Runtime::ushr(const Value &left, const Value &right) -{ - TRACE2(left, right); - - unsigned lval = left.toUInt32(); - unsigned rval = right.toUInt32() & 0x1f; - uint res = lval >> rval; - - return Encode(res); -} - -inline ReturnedValue Runtime::greaterThan(const Value &left, const Value &right) -{ - TRACE2(left, right); - - bool r = Runtime::compareGreaterThan(left, right); - return Encode(r); -} - -inline ReturnedValue Runtime::lessThan(const Value &left, const Value &right) -{ - TRACE2(left, right); - - bool r = Runtime::compareLessThan(left, right); - return Encode(r); -} - -inline ReturnedValue Runtime::greaterEqual(const Value &left, const Value &right) -{ - TRACE2(left, right); - - bool r = Runtime::compareGreaterEqual(left, right); - return Encode(r); -} - -inline ReturnedValue Runtime::lessEqual(const Value &left, const Value &right) -{ - TRACE2(left, right); - - bool r = Runtime::compareLessEqual(left, right); - return Encode(r); -} - -inline Bool Runtime::compareEqual(const Value &left, const Value &right) -{ - TRACE2(left, right); - - if (left.rawValue() == right.rawValue()) - // NaN != NaN - return !left.isNaN(); - - if (left.type() == right.type()) { - if (!left.isManaged()) - return false; - if (left.isString() == right.isString()) - return left.cast<Managed>()->isEqualTo(right.cast<Managed>()); - } - - return RuntimeHelpers::equalHelper(left, right); -} - -inline ReturnedValue Runtime::equal(const Value &left, const Value &right) -{ - TRACE2(left, right); - - bool r = Runtime::compareEqual(left, right); - return Encode(r); -} - -inline ReturnedValue Runtime::notEqual(const Value &left, const Value &right) -{ - TRACE2(left, right); - - bool r = !Runtime::compareEqual(left, right); - return Encode(r); -} - -inline ReturnedValue Runtime::strictEqual(const Value &left, const Value &right) -{ - TRACE2(left, right); - - bool r = RuntimeHelpers::strictEqual(left, right); - return Encode(r); -} - -inline ReturnedValue Runtime::strictNotEqual(const Value &left, const Value &right) -{ - TRACE2(left, right); - - bool r = ! RuntimeHelpers::strictEqual(left, right); - return Encode(r); -} - -inline Bool Runtime::compareNotEqual(const Value &left, const Value &right) -{ - TRACE2(left, right); - - return !Runtime::compareEqual(left, right); -} - -inline Bool Runtime::compareStrictEqual(const Value &left, const Value &right) -{ - TRACE2(left, right); - - return RuntimeHelpers::strictEqual(left, right); -} - -inline Bool Runtime::compareStrictNotEqual(const Value &left, const Value &right) -{ - TRACE2(left, right); - - return ! RuntimeHelpers::strictEqual(left, right); -} - -inline Bool Runtime::toBoolean(const Value &value) -{ - return value.toBoolean(); -} - } // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h new file mode 100644 index 0000000000..cbc7a2ddc1 --- /dev/null +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -0,0 +1,315 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4RUNTIMEAPI_P_H +#define QV4RUNTIMEAPI_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> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct NoThrowEngine; + +#define RUNTIME_METHOD(returnvalue, name, args) \ + typedef returnvalue (*Method_##name)args; \ + static returnvalue method_##name args; \ + const Method_##name name + +#define INIT_RUNTIME_METHOD(name) \ + name(method_##name) + +struct Q_QML_PRIVATE_EXPORT Runtime { + Runtime() + : INIT_RUNTIME_METHOD(callGlobalLookup) + , INIT_RUNTIME_METHOD(callActivationProperty) + , INIT_RUNTIME_METHOD(callQmlScopeObjectProperty) + , INIT_RUNTIME_METHOD(callQmlContextObjectProperty) + , INIT_RUNTIME_METHOD(callProperty) + , INIT_RUNTIME_METHOD(callPropertyLookup) + , INIT_RUNTIME_METHOD(callElement) + , INIT_RUNTIME_METHOD(callValue) + , INIT_RUNTIME_METHOD(constructGlobalLookup) + , INIT_RUNTIME_METHOD(constructActivationProperty) + , INIT_RUNTIME_METHOD(constructProperty) + , INIT_RUNTIME_METHOD(constructPropertyLookup) + , INIT_RUNTIME_METHOD(constructValue) + , INIT_RUNTIME_METHOD(setActivationProperty) + , INIT_RUNTIME_METHOD(setProperty) + , INIT_RUNTIME_METHOD(setElement) + , INIT_RUNTIME_METHOD(getProperty) + , INIT_RUNTIME_METHOD(getActivationProperty) + , INIT_RUNTIME_METHOD(getElement) + , INIT_RUNTIME_METHOD(typeofValue) + , INIT_RUNTIME_METHOD(typeofName) + , INIT_RUNTIME_METHOD(typeofScopeObjectProperty) + , INIT_RUNTIME_METHOD(typeofContextObjectProperty) + , INIT_RUNTIME_METHOD(typeofMember) + , INIT_RUNTIME_METHOD(typeofElement) + , INIT_RUNTIME_METHOD(deleteElement) + , INIT_RUNTIME_METHOD(deleteMember) + , INIT_RUNTIME_METHOD(deleteMemberString) + , INIT_RUNTIME_METHOD(deleteName) + , INIT_RUNTIME_METHOD(throwException) + , INIT_RUNTIME_METHOD(unwindException) + , INIT_RUNTIME_METHOD(pushWithScope) + , INIT_RUNTIME_METHOD(pushCatchScope) + , INIT_RUNTIME_METHOD(popScope) + , INIT_RUNTIME_METHOD(closure) + , INIT_RUNTIME_METHOD(declareVar) + , INIT_RUNTIME_METHOD(setupArgumentsObject) + , INIT_RUNTIME_METHOD(convertThisToObject) + , INIT_RUNTIME_METHOD(arrayLiteral) + , INIT_RUNTIME_METHOD(objectLiteral) + , INIT_RUNTIME_METHOD(regexpLiteral) + , INIT_RUNTIME_METHOD(foreachIterator) + , INIT_RUNTIME_METHOD(foreachNextPropertyName) + , INIT_RUNTIME_METHOD(uPlus) + , INIT_RUNTIME_METHOD(uMinus) + , INIT_RUNTIME_METHOD(uNot) + , INIT_RUNTIME_METHOD(complement) + , INIT_RUNTIME_METHOD(increment) + , INIT_RUNTIME_METHOD(decrement) + , INIT_RUNTIME_METHOD(instanceof) + , INIT_RUNTIME_METHOD(in) + , INIT_RUNTIME_METHOD(add) + , INIT_RUNTIME_METHOD(addString) + , INIT_RUNTIME_METHOD(bitOr) + , INIT_RUNTIME_METHOD(bitXor) + , INIT_RUNTIME_METHOD(bitAnd) + , INIT_RUNTIME_METHOD(sub) + , INIT_RUNTIME_METHOD(mul) + , INIT_RUNTIME_METHOD(div) + , INIT_RUNTIME_METHOD(mod) + , INIT_RUNTIME_METHOD(shl) + , INIT_RUNTIME_METHOD(shr) + , INIT_RUNTIME_METHOD(ushr) + , INIT_RUNTIME_METHOD(greaterThan) + , INIT_RUNTIME_METHOD(lessThan) + , INIT_RUNTIME_METHOD(greaterEqual) + , INIT_RUNTIME_METHOD(lessEqual) + , INIT_RUNTIME_METHOD(equal) + , INIT_RUNTIME_METHOD(notEqual) + , INIT_RUNTIME_METHOD(strictEqual) + , INIT_RUNTIME_METHOD(strictNotEqual) + , INIT_RUNTIME_METHOD(compareGreaterThan) + , INIT_RUNTIME_METHOD(compareLessThan) + , INIT_RUNTIME_METHOD(compareGreaterEqual) + , INIT_RUNTIME_METHOD(compareLessEqual) + , INIT_RUNTIME_METHOD(compareEqual) + , INIT_RUNTIME_METHOD(compareNotEqual) + , INIT_RUNTIME_METHOD(compareStrictEqual) + , INIT_RUNTIME_METHOD(compareStrictNotEqual) + , INIT_RUNTIME_METHOD(compareInstanceof) + , INIT_RUNTIME_METHOD(compareIn) + , INIT_RUNTIME_METHOD(toBoolean) + , INIT_RUNTIME_METHOD(toDouble) + , INIT_RUNTIME_METHOD(toInt) + , INIT_RUNTIME_METHOD(doubleToInt) + , INIT_RUNTIME_METHOD(toUInt) + , INIT_RUNTIME_METHOD(doubleToUInt) + , INIT_RUNTIME_METHOD(getQmlContext) + , INIT_RUNTIME_METHOD(getQmlImportedScripts) + , INIT_RUNTIME_METHOD(getQmlSingleton) + , INIT_RUNTIME_METHOD(getQmlAttachedProperty) + , INIT_RUNTIME_METHOD(getQmlScopeObjectProperty) + , INIT_RUNTIME_METHOD(getQmlContextObjectProperty) + , INIT_RUNTIME_METHOD(getQmlQObjectProperty) + , INIT_RUNTIME_METHOD(getQmlSingletonQObjectProperty) + , INIT_RUNTIME_METHOD(getQmlIdObject) + , INIT_RUNTIME_METHOD(setQmlScopeObjectProperty) + , INIT_RUNTIME_METHOD(setQmlContextObjectProperty) + , INIT_RUNTIME_METHOD(setQmlQObjectProperty) + { } + + // call + RUNTIME_METHOD(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData)); + RUNTIME_METHOD(ReturnedValue, callActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)); + RUNTIME_METHOD(ReturnedValue, callQmlScopeObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData)); + RUNTIME_METHOD(ReturnedValue, callQmlContextObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData)); + RUNTIME_METHOD(ReturnedValue, callProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)); + RUNTIME_METHOD(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData)); + RUNTIME_METHOD(ReturnedValue, callElement, (ExecutionEngine *engine, const Value &index, CallData *callData)); + RUNTIME_METHOD(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, CallData *callData)); + + // construct + RUNTIME_METHOD(ReturnedValue, constructGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData)); + RUNTIME_METHOD(ReturnedValue, constructActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)); + RUNTIME_METHOD(ReturnedValue, constructProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)); + RUNTIME_METHOD(ReturnedValue, constructPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData)); + RUNTIME_METHOD(ReturnedValue, constructValue, (ExecutionEngine *engine, const Value &func, CallData *callData)); + + // set & get + RUNTIME_METHOD(void, setActivationProperty, (ExecutionEngine *engine, int nameIndex, const Value &value)); + RUNTIME_METHOD(void, setProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)); + RUNTIME_METHOD(void, setElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)); + RUNTIME_METHOD(ReturnedValue, getProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)); + RUNTIME_METHOD(ReturnedValue, getActivationProperty, (ExecutionEngine *engine, int nameIndex)); + RUNTIME_METHOD(ReturnedValue, getElement, (ExecutionEngine *engine, const Value &object, const Value &index)); + + // typeof + RUNTIME_METHOD(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)); + RUNTIME_METHOD(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)); + RUNTIME_METHOD(ReturnedValue, typeofScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)); + RUNTIME_METHOD(ReturnedValue, typeofContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)); + RUNTIME_METHOD(ReturnedValue, typeofMember, (ExecutionEngine *engine, const Value &base, int nameIndex)); + RUNTIME_METHOD(ReturnedValue, typeofElement, (ExecutionEngine *engine, const Value &base, const Value &index)); + + // delete + RUNTIME_METHOD(ReturnedValue, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index)); + RUNTIME_METHOD(ReturnedValue, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex)); + RUNTIME_METHOD(ReturnedValue, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name)); + RUNTIME_METHOD(ReturnedValue, deleteName, (ExecutionEngine *engine, int nameIndex)); + + // exceptions & scopes + RUNTIME_METHOD(void, throwException, (ExecutionEngine *engine, const Value &value)); + RUNTIME_METHOD(ReturnedValue, unwindException, (ExecutionEngine *engine)); + RUNTIME_METHOD(void, pushWithScope, (const Value &o, ExecutionEngine *engine)); + RUNTIME_METHOD(void, pushCatchScope, (NoThrowEngine *engine, int exceptionVarNameIndex)); + RUNTIME_METHOD(void, popScope, (ExecutionEngine *engine)); + + // closures + RUNTIME_METHOD(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)); + + // function header + RUNTIME_METHOD(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)); + RUNTIME_METHOD(ReturnedValue, setupArgumentsObject, (ExecutionEngine *engine)); + RUNTIME_METHOD(void, convertThisToObject, (ExecutionEngine *engine)); + + // literals + RUNTIME_METHOD(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)); + RUNTIME_METHOD(ReturnedValue, objectLiteral, (ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)); + RUNTIME_METHOD(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id)); + + // foreach + RUNTIME_METHOD(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in)); + RUNTIME_METHOD(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator)); + + // unary operators + typedef ReturnedValue (*UnaryOperation)(const Value &value); + RUNTIME_METHOD(ReturnedValue, uPlus, (const Value &value)); + RUNTIME_METHOD(ReturnedValue, uMinus, (const Value &value)); + RUNTIME_METHOD(ReturnedValue, uNot, (const Value &value)); + RUNTIME_METHOD(ReturnedValue, complement, (const Value &value)); + RUNTIME_METHOD(ReturnedValue, increment, (const Value &value)); + RUNTIME_METHOD(ReturnedValue, decrement, (const Value &value)); + + // binary operators + typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right); + typedef ReturnedValue (*BinaryOperationContext)(ExecutionEngine *engine, const Value &left, const Value &right); + + RUNTIME_METHOD(ReturnedValue, instanceof, (ExecutionEngine *engine, const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, in, (ExecutionEngine *engine, const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, add, (ExecutionEngine *engine, const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, addString, (ExecutionEngine *engine, const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, bitOr, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, bitXor, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, bitAnd, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, sub, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, mul, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, div, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, mod, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, shl, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, shr, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, ushr, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, greaterThan, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, lessThan, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, greaterEqual, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, lessEqual, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, equal, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, notEqual, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, strictEqual, (const Value &left, const Value &right)); + RUNTIME_METHOD(ReturnedValue, strictNotEqual, (const Value &left, const Value &right)); + + // comparisons + RUNTIME_METHOD(Bool, compareGreaterThan, (const Value &l, const Value &r)); + RUNTIME_METHOD(Bool, compareLessThan, (const Value &l, const Value &r)); + RUNTIME_METHOD(Bool, compareGreaterEqual, (const Value &l, const Value &r)); + RUNTIME_METHOD(Bool, compareLessEqual, (const Value &l, const Value &r)); + RUNTIME_METHOD(Bool, compareEqual, (const Value &left, const Value &right)); + RUNTIME_METHOD(Bool, compareNotEqual, (const Value &left, const Value &right)); + RUNTIME_METHOD(Bool, compareStrictEqual, (const Value &left, const Value &right)); + RUNTIME_METHOD(Bool, compareStrictNotEqual, (const Value &left, const Value &right)); + + RUNTIME_METHOD(Bool, compareInstanceof, (ExecutionEngine *engine, const Value &left, const Value &right)); + RUNTIME_METHOD(Bool, compareIn, (ExecutionEngine *engine, const Value &left, const Value &right)); + + // conversions + RUNTIME_METHOD(Bool, toBoolean, (const Value &value)); + RUNTIME_METHOD(ReturnedValue, toDouble, (const Value &value)); + RUNTIME_METHOD(int, toInt, (const Value &value)); + RUNTIME_METHOD(int, doubleToInt, (const double &d)); + RUNTIME_METHOD(unsigned, toUInt, (const Value &value)); + RUNTIME_METHOD(unsigned, doubleToUInt, (const double &d)); + + // qml + RUNTIME_METHOD(ReturnedValue, getQmlContext, (NoThrowEngine *engine)); + RUNTIME_METHOD(ReturnedValue, getQmlImportedScripts, (NoThrowEngine *engine)); + RUNTIME_METHOD(ReturnedValue, getQmlSingleton, (NoThrowEngine *engine, int nameIndex)); + RUNTIME_METHOD(ReturnedValue, getQmlAttachedProperty, (ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex)); + RUNTIME_METHOD(ReturnedValue, getQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)); + RUNTIME_METHOD(ReturnedValue, getQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)); + RUNTIME_METHOD(ReturnedValue, getQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)); + RUNTIME_METHOD(ReturnedValue, getQmlSingletonQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)); + RUNTIME_METHOD(ReturnedValue, getQmlIdObject, (ExecutionEngine *engine, const Value &context, uint index)); + + RUNTIME_METHOD(void, setQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)); + RUNTIME_METHOD(void, setQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)); + RUNTIME_METHOD(void, setQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value)); +}; + +#undef RUNTIME_METHOD +#undef INIT_RUNTIME_METHOD + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4RUNTIMEAPI_P_H diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index a6f7f8e0e7..44d171e087 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -71,14 +71,18 @@ struct ScopedValue; struct Scope { inline Scope(ExecutionContext *ctx) : engine(ctx->d()->engine) + , mark(engine->jsStackTop) + , result(*engine->jsAlloca(1)) { - mark = engine->jsStackTop; + result = Encode::undefined(); } explicit Scope(ExecutionEngine *e) : engine(e) + , mark(engine->jsStackTop) + , result(*engine->jsAlloca(1)) { - mark = engine->jsStackTop; + result = Encode::undefined(); } ~Scope() { @@ -93,7 +97,7 @@ struct Scope { engine->jsStackTop = mark; } - Value *alloc(int nValues) { + Value *alloc(int nValues) const { return engine->jsAlloca(nValues); } @@ -103,6 +107,7 @@ struct Scope { ExecutionEngine *engine; Value *mark; + Value &result; private: Q_DISABLE_COPY(Scope) @@ -190,59 +195,59 @@ struct Scoped Scoped(const Scope &scope) { - ptr = scope.engine->jsStackTop++; - ptr->setM(0); + ptr = scope.engine->jsAlloca(1); } Scoped(const Scope &scope, const Value &v) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); setPointer(v.as<T>()); } Scoped(const Scope &scope, Heap::Base *o) { Value v; v = o; - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); setPointer(v.as<T>()); } Scoped(const Scope &scope, const ScopedValue &v) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); setPointer(v.ptr->as<T>()); } Scoped(const Scope &scope, const Value &v, ConvertType) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); ptr->setRawValue(value_convert<T>(scope.engine, v)); } Scoped(const Scope &scope, const Value *v) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); setPointer(v ? v->as<T>() : 0); } Scoped(const Scope &scope, T *t) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); setPointer(t); } Scoped(const Scope &scope, typename T::Data *t) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); *ptr = t; } Scoped(const Scope &scope, const ReturnedValue &v) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); setPointer(QV4::Value::fromReturnedValue(v).as<T>()); } + Scoped(const Scope &scope, const ReturnedValue &v, ConvertType) { - ptr = scope.engine->jsStackTop++; + ptr = scope.engine->jsAlloca(1); ptr->setRawValue(value_convert<T>(scope.engine, QV4::Value::fromReturnedValue(v))); } @@ -289,6 +294,10 @@ struct Scoped return ptr->cast<T>(); } + const T *operator->() const { + return ptr->cast<T>(); + } + bool operator!() const { return !ptr->m(); } @@ -311,7 +320,7 @@ struct Scoped }; struct ScopedCallData { - ScopedCallData(Scope &scope, int argc = 0) + ScopedCallData(const Scope &scope, int argc = 0) { int size = qMax(argc, (int)QV4::Global::ReservedArgumentCount) + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value); ptr = reinterpret_cast<CallData *>(scope.alloc(size)); @@ -362,19 +371,19 @@ struct ScopedProperty struct ExecutionContextSaver { - ExecutionEngine *engine; + Scope scope; // this makes sure that a reference to context on the JS stack goes out of scope as soon as the context is not used anymore. ExecutionContext *savedContext; - ExecutionContextSaver(Scope &scope) - : engine(scope.engine) + ExecutionContextSaver(const Scope &scope) + : scope(scope.engine) { - savedContext = engine->currentContext; + savedContext = scope.engine->currentContext; } ~ExecutionContextSaver() { - Q_ASSERT(engine->jsStackTop > engine->currentContext); - engine->currentContext = savedContext; - engine->current = savedContext->d(); + Q_ASSERT(scope.engine->jsStackTop > scope.engine->currentContext); + scope.engine->currentContext = savedContext; + scope.engine->current = savedContext->d(); } }; diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 4b847600b4..a2e379ec1a 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -64,34 +64,16 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace Heap { -struct CompilationUnitHolder : Object { - inline CompilationUnitHolder(CompiledData::CompilationUnit *unit); - - QQmlRefPointer<CompiledData::CompilationUnit> unit; -}; - struct QmlBindingWrapper : FunctionObject { QmlBindingWrapper(QV4::QmlContext *scope, Function *f); }; } -struct CompilationUnitHolder : public Object -{ - V4_OBJECT2(CompilationUnitHolder, Object) - V4_NEEDS_DESTROY -}; - -inline -Heap::CompilationUnitHolder::CompilationUnitHolder(CompiledData::CompilationUnit *unit) - : unit(unit) -{ -} - struct QmlBindingWrapper : FunctionObject { V4_OBJECT2(QmlBindingWrapper, FunctionObject) - static ReturnedValue call(const Managed *that, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; } @@ -101,7 +83,6 @@ QT_END_NAMESPACE using namespace QV4; DEFINE_OBJECT_VTABLE(QmlBindingWrapper); -DEFINE_OBJECT_VTABLE(CompilationUnitHolder); Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::QmlContext *scope, Function *f) : Heap::FunctionObject(scope, scope->d()->engine->id_eval(), /*createProto = */ false) @@ -113,32 +94,33 @@ Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::QmlContext *scope, Function *f) function->compilationUnit->addref(); } -ReturnedValue QmlBindingWrapper::call(const Managed *that, CallData *callData) +void QmlBindingWrapper::call(const Managed *that, Scope &scope, CallData *callData) { 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); + if (v4->hasException) { + scope.result = Encode::undefined(); + return; + } + CHECK_STACK_LIMITS(v4, scope); - Scope scope(v4); ExecutionContextSaver ctxSaver(scope); QV4::Function *f = This->function(); - if (!f) - return QV4::Encode::undefined(); + if (!f) { + scope.result = QV4::Encode::undefined(); + return; + } Scoped<CallContext> ctx(scope, v4->currentContext->newCallContext(This, callData)); v4->pushContext(ctx); - ScopedValue result(scope, Q_V4_PROFILE(v4, f)); - - return result->asReturnedValue(); + scope.result = Q_V4_PROFILE(v4, f); } Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit) : line(0), column(0), scope(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false) - , vmFunction(0), parseAsBinding(true) + , compilationUnit(compilationUnit), vmFunction(0), parseAsBinding(true) { if (qml) qmlContext.set(v4, *qml); @@ -146,11 +128,6 @@ Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUn parsed = true; vmFunction = compilationUnit ? compilationUnit->linkToEngine(v4) : 0; - if (vmFunction) { - Scope valueScope(v4); - ScopedObject holder(valueScope, v4->memoryManager->allocObject<CompilationUnitHolder>(compilationUnit)); - compilationUnitHolder.set(v4, holder); - } } Script::~Script() @@ -171,7 +148,7 @@ void Script::parse() MemoryManager::GCBlocker gcBlocker(v4->memoryManager); - IR::Module module(v4->debugger != 0); + IR::Module module(v4->debugger() != 0); QQmlJS::Engine ee, *engine = ⅇ Lexer lexer(engine); @@ -217,10 +194,8 @@ void Script::parse() QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(QQmlEnginePrivate::get(v4), v4->executableAllocator, &module, &jsGenerator)); if (inheritContext) isel->setUseFastLookups(false); - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = isel->compile(); + compilationUnit = isel->compile(); vmFunction = compilationUnit->linkToEngine(v4); - ScopedObject holder(valueScope, v4->memoryManager->allocObject<CompilationUnitHolder>(compilationUnit)); - compilationUnitHolder.set(v4, holder); } if (!vmFunction) { @@ -255,7 +230,8 @@ ReturnedValue Script::run() ScopedFunctionObject f(valueScope, engine->memoryManager->allocObject<QmlBindingWrapper>(qml, vmFunction)); ScopedCallData callData(valueScope); callData->thisObject = Primitive::undefinedValue(); - return f->call(callData); + f->call(valueScope, callData); + return valueScope.result.asReturnedValue(); } } diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index d7b82218e7..e81bc3049c 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -74,7 +74,7 @@ struct ContextStateSaver { CompiledData::CompilationUnit *compilationUnit; int lineNumber; - ContextStateSaver(Scope &scope, ExecutionContext *context) + ContextStateSaver(const Scope &scope, ExecutionContext *context) : savedContext(scope.alloc(1)) , strictMode(context->d()->strictMode) , lookups(context->d()->lookups) @@ -83,7 +83,7 @@ struct ContextStateSaver { { savedContext->setM(context->d()); } - ContextStateSaver(Scope &scope, Heap::ExecutionContext *context) + ContextStateSaver(const Scope &scope, Heap::ExecutionContext *context) : savedContext(scope.alloc(1)) , strictMode(context->strictMode) , lookups(context->lookups) @@ -126,7 +126,7 @@ struct Q_QML_EXPORT Script { bool inheritContext; bool parsed; QV4::PersistentValue qmlContext; - QV4::PersistentValue compilationUnitHolder; + QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit; Function *vmFunction; bool parseAsBinding; diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index fa2409a85c..b15ac7eb5e 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -411,8 +411,8 @@ public: 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; - QV4::ScopedValue result(scope, compare->call(callData)); - return result->toNumber() < 0; + compare->call(scope, callData); + return scope.result.toNumber() < 0; } private: @@ -531,7 +531,7 @@ public: Q_ASSERT(d()->object); Q_ASSERT(d()->isReference); int status = -1; - QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding; + QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding; void *a[] = { &d()->container, 0, &status, &flags }; QMetaObject::metacall(d()->object, QMetaObject::WriteProperty, d()->propertyIndex, a); } diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index abef885249..3901514326 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -49,57 +49,8 @@ using namespace QV4; -static uint toArrayIndex(const QChar *ch, const QChar *end) -{ - uint i = ch->unicode() - '0'; - if (i > 9) - return UINT_MAX; - ++ch; - // reject "01", "001", ... - if (i == 0 && ch != end) - return UINT_MAX; - - while (ch < end) { - uint x = ch->unicode() - '0'; - if (x > 9) - return UINT_MAX; - uint n = i*10 + x; - if (n < i) - // overflow - return UINT_MAX; - i = n; - ++ch; - } - return i; -} - #ifndef V4_BOOTSTRAP -static uint toArrayIndex(const char *ch, const char *end) -{ - uint i = *ch - '0'; - if (i > 9) - return UINT_MAX; - ++ch; - // reject "01", "001", ... - if (i == 0 && ch != end) - return UINT_MAX; - - while (ch < end) { - uint x = *ch - '0'; - if (x > 9) - return UINT_MAX; - uint n = i*10 + x; - if (n < i) - // overflow - return UINT_MAX; - i = n; - ++ch; - } - return i; -} - - DEFINE_MANAGED_VTABLE(String); void String::markObjects(Heap::Base *that, ExecutionEngine *e) @@ -198,31 +149,6 @@ void Heap::String::simplifyString() const mm->growUnmanagedHeapSizeUsage(size_t(text->size) * sizeof(QChar)); } -void Heap::String::createHashValue() const -{ - if (largestSubLength) - simplifyString(); - Q_ASSERT(!largestSubLength); - const QChar *ch = reinterpret_cast<const QChar *>(text->data()); - const QChar *end = ch + text->size; - - // array indices get their number as hash value - stringHash = ::toArrayIndex(ch, end); - if (stringHash != UINT_MAX) { - subtype = Heap::String::StringType_ArrayIndex; - return; - } - - uint h = 0xffffffff; - while (ch < end) { - h = 31 * h + ch->unicode(); - ++ch; - } - - stringHash = h; - subtype = Heap::String::StringType_Regular; -} - void Heap::String::append(const String *data, QChar *ch) { std::vector<const String *> worklist; @@ -243,45 +169,14 @@ void Heap::String::append(const String *data, QChar *ch) } } - - - -uint String::createHashValue(const QChar *ch, int length) -{ - const QChar *end = ch + length; - - // array indices get their number as hash value - uint stringHash = ::toArrayIndex(ch, end); - if (stringHash != UINT_MAX) - return stringHash; - - uint h = 0xffffffff; - while (ch < end) { - h = 31 * h + ch->unicode(); - ++ch; - } - - return h; -} - -uint String::createHashValue(const char *ch, int length) +void Heap::String::createHashValue() const { - const char *end = ch + length; - - // array indices get their number as hash value - uint stringHash = ::toArrayIndex(ch, end); - if (stringHash != UINT_MAX) - return stringHash; - - uint h = 0xffffffff; - while (ch < end) { - if ((uchar)(*ch) >= 0x80) - return UINT_MAX; - h = 31 * h + *ch; - ++ch; - } - - return h; + if (largestSubLength) + simplifyString(); + Q_ASSERT(!largestSubLength); + const QChar *ch = reinterpret_cast<const QChar *>(text->data()); + const QChar *end = ch + text->size; + stringHash = QV4::String::calculateHashValue(ch, end, &subtype); } uint String::getLength(const Managed *m) @@ -293,6 +188,6 @@ uint String::getLength(const Managed *m) uint String::toArrayIndex(const QString &str) { - return ::toArrayIndex(str.constData(), str.constData() + str.length()); + return QV4::String::toArrayIndex(str.constData(), str.constData() + str.length()); } diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 3196f49896..ff42ab6471 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -52,6 +52,7 @@ #include <QtCore/qstring.h> #include "qv4managed_p.h" +#include <QtCore/private/qnumeric_p.h> QT_BEGIN_NAMESPACE @@ -62,7 +63,6 @@ struct Identifier; namespace Heap { -#ifndef V4_BOOTSTRAP struct Q_QML_PRIVATE_EXPORT String : Base { enum StringType { StringType_Unknown, @@ -70,6 +70,7 @@ struct Q_QML_PRIVATE_EXPORT String : Base { StringType_ArrayIndex }; +#ifndef V4_BOOTSTRAP String(MemoryManager *mm, const QString &text); String(MemoryManager *mm, String *l, String *n); ~String() { @@ -130,8 +131,8 @@ struct Q_QML_PRIVATE_EXPORT String : Base { MemoryManager *mm; private: static void append(const String *data, QChar *ch); -}; #endif +}; } @@ -183,8 +184,17 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { void makeIdentifierImpl(ExecutionEngine *e) const; - static uint createHashValue(const QChar *ch, int length); - static uint createHashValue(const char *ch, int length); + static uint createHashValue(const QChar *ch, int length, uint *subtype) + { + const QChar *end = ch + length; + return calculateHashValue(ch, end, subtype); + } + + static uint createHashValue(const char *ch, int length, uint *subtype) + { + const char *end = ch + length; + return calculateHashValue(ch, end, subtype); + } bool startsWithUpper() const { const String::Data *l = d(); @@ -203,6 +213,54 @@ protected: public: static uint toArrayIndex(const QString &str); + +private: + static inline uint toUInt(const QChar *ch) { return ch->unicode(); } + static inline uint toUInt(const char *ch) { return *ch; } + + template <typename T> + static inline uint toArrayIndex(const T *ch, const T *end) + { + uint i = toUInt(ch) - '0'; + if (i > 9) + return UINT_MAX; + ++ch; + // reject "01", "001", ... + if (i == 0 && ch != end) + return UINT_MAX; + + while (ch < end) { + uint x = toUInt(ch) - '0'; + if (x > 9) + return UINT_MAX; + if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) // i = i * 10 + x + return UINT_MAX; + ++ch; + } + return i; + } + +public: + template <typename T> + static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype) + { + // array indices get their number as hash value + uint h = toArrayIndex(ch, end); + if (h != UINT_MAX) { + if (subtype) + *subtype = Heap::String::StringType_ArrayIndex; + return h; + } + + while (ch < end) { + h = 31 * h + toUInt(ch); + ++ch; + } + + if (subtype) + *subtype = Heap::String::StringType_Regular; + return h; + } }; template<> diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index b874766655..1989f747e9 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -157,28 +157,24 @@ Heap::StringCtor::StringCtor(QV4::ExecutionContext *scope) { } -ReturnedValue StringCtor::construct(const Managed *m, CallData *callData) +void StringCtor::construct(const Managed *m, Scope &scope, CallData *callData) { ExecutionEngine *v4 = static_cast<const Object *>(m)->engine(); - Scope scope(v4); ScopedString value(scope); if (callData->argc) value = callData->args[0].toString(v4); else value = v4->newString(); - return Encode(v4->newStringObject(value)); + scope.result = Encode(v4->newStringObject(value)); } -ReturnedValue StringCtor::call(const Managed *m, CallData *callData) +void StringCtor::call(const Managed *, Scope &scope, CallData *callData) { - ExecutionEngine *v4 = static_cast<const Object *>(m)->engine(); - Scope scope(v4); - ScopedValue value(scope); + ExecutionEngine *v4 = scope.engine; if (callData->argc) - value = callData->args[0].toString(v4); + scope.result = callData->args[0].toString(v4); else - value = v4->newString(); - return value->asReturnedValue(); + scope.result = v4->newString(); } void StringPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -196,7 +192,9 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("charAt"), method_charAt, 1); defineDefaultProperty(QStringLiteral("charCodeAt"), method_charCodeAt, 1); defineDefaultProperty(QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(QStringLiteral("endsWith"), method_endsWith, 1); defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(QStringLiteral("includes"), method_includes, 1); defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare, 1); defineDefaultProperty(QStringLiteral("match"), method_match, 1); @@ -204,6 +202,7 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("search"), method_search, 1); defineDefaultProperty(QStringLiteral("slice"), method_slice, 2); defineDefaultProperty(QStringLiteral("split"), method_split, 2); + defineDefaultProperty(QStringLiteral("startsWith"), method_startsWith, 1); defineDefaultProperty(QStringLiteral("substr"), method_substr, 2); defineDefaultProperty(QStringLiteral("substring"), method_substring, 2); defineDefaultProperty(QStringLiteral("toLowerCase"), method_toLowerCase); @@ -293,6 +292,30 @@ ReturnedValue StringPrototype::method_concat(CallContext *context) return context->d()->engine->newString(value)->asReturnedValue(); } +ReturnedValue StringPrototype::method_endsWith(CallContext *context) +{ + QString value = getThisString(context); + if (context->d()->engine->hasException) + return Encode::undefined(); + + QString searchString; + if (context->argc()) { + if (context->args()[0].as<RegExpObject>()) + return context->engine()->throwTypeError(); + searchString = context->args()[0].toQString(); + } + + int pos = value.length(); + if (context->argc() > 1) + pos = (int) context->args()[1].toInteger(); + + if (pos == value.length()) + return Encode(value.endsWith(searchString)); + + QStringRef stringToSearch = value.leftRef(pos); + return Encode(stringToSearch.endsWith(searchString)); +} + ReturnedValue StringPrototype::method_indexOf(CallContext *context) { QString value = getThisString(context); @@ -314,6 +337,35 @@ ReturnedValue StringPrototype::method_indexOf(CallContext *context) return Encode(index); } +ReturnedValue StringPrototype::method_includes(CallContext *context) +{ + QString value = getThisString(context); + if (context->d()->engine->hasException) + return Encode::undefined(); + + QString searchString; + if (context->argc()) { + if (context->args()[0].as<RegExpObject>()) + return context->engine()->throwTypeError(); + searchString = context->args()[0].toQString(); + } + + int pos = 0; + if (context->argc() > 1) { + Scope scope(context); + ScopedValue posArg(scope, context->argument(1)); + pos = (int) posArg->toInteger(); + if (!posArg->isInteger() && posArg->isNumber() && qIsInf(posArg->toNumber())) + pos = value.length(); + } + + if (pos == 0) + return Encode(value.contains(searchString)); + + QStringRef stringToSearch = value.midRef(pos); + return Encode(stringToSearch.contains(searchString)); +} + ReturnedValue StringPrototype::method_lastIndexOf(CallContext *context) { Scope scope(context); @@ -367,7 +419,8 @@ ReturnedValue StringPrototype::method_match(CallContext *context) if (!rx) { ScopedCallData callData(scope, 1); callData->args[0] = regexp; - rx = context->d()->engine->regExpCtor()->construct(callData); + context->d()->engine->regExpCtor()->construct(scope, callData); + rx = scope.result.asReturnedValue(); } if (!rx) @@ -383,8 +436,10 @@ ReturnedValue StringPrototype::method_match(CallContext *context) ScopedCallData callData(scope, 1); callData->thisObject = rx; callData->args[0] = s; - if (!global) - return exec->call(callData); + if (!global) { + exec->call(scope, callData); + return scope.result.asReturnedValue(); + } ScopedString lastIndex(scope, context->d()->engine->newString(QStringLiteral("lastIndex"))); rx->put(lastIndex, ScopedValue(scope, Primitive::fromInt32(0))); @@ -392,14 +447,13 @@ ReturnedValue StringPrototype::method_match(CallContext *context) double previousLastIndex = 0; uint n = 0; - ScopedValue result(scope); ScopedValue matchStr(scope); ScopedValue index(scope); while (1) { - result = exec->call(callData); - if (result->isNull()) + exec->call(scope, callData); + if (scope.result.isNull()) break; - assert(result->isObject()); + assert(scope.result.isObject()); index = rx->get(lastIndex, 0); double thisIndex = index->toInteger(); if (previousLastIndex == thisIndex) { @@ -408,7 +462,7 @@ ReturnedValue StringPrototype::method_match(CallContext *context) } else { previousLastIndex = thisIndex; } - matchStr = result->objectValue()->getIndexed(0); + matchStr = scope.result.objectValue()->getIndexed(0); a->arraySet(n, matchStr); ++n; } @@ -524,7 +578,6 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) } QString result; - ScopedValue replacement(scope); ScopedValue replaceValue(scope, ctx->argument(1)); ScopedFunctionObject searchCallback(scope, replaceValue); if (!!searchCallback) { @@ -549,9 +602,9 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) callData->args[numCaptures] = Primitive::fromUInt32(matchStart); callData->args[numCaptures + 1] = ctx->d()->engine->newString(string); - replacement = searchCallback->call(callData); + searchCallback->call(scope, callData); result += string.midRef(lastEnd, matchStart - lastEnd); - result += replacement->toQString(); + result += scope.result.toQString(); lastEnd = matchEnd; } result += string.midRef(lastEnd); @@ -584,17 +637,17 @@ ReturnedValue StringPrototype::method_search(CallContext *ctx) { Scope scope(ctx); QString string = getThisString(ctx); - ScopedValue regExpValue(scope, ctx->argument(0)); + scope.result = ctx->argument(0); if (scope.engine->hasException) return Encode::undefined(); - Scoped<RegExpObject> regExp(scope, regExpValue->as<RegExpObject>()); + Scoped<RegExpObject> regExp(scope, scope.result.as<RegExpObject>()); if (!regExp) { ScopedCallData callData(scope, 1); - callData->args[0] = regExpValue; - regExpValue = ctx->d()->engine->regExpCtor()->construct(callData); + callData->args[0] = scope.result; + ctx->d()->engine->regExpCtor()->construct(scope, callData); if (scope.engine->hasException) return Encode::undefined(); - regExp = regExpValue->as<RegExpObject>(); + regExp = scope.result.as<RegExpObject>(); Q_ASSERT(regExp); } Scoped<RegExp> re(scope, regExp->value()); @@ -716,6 +769,30 @@ ReturnedValue StringPrototype::method_split(CallContext *ctx) return array.asReturnedValue(); } +ReturnedValue StringPrototype::method_startsWith(CallContext *context) +{ + QString value = getThisString(context); + if (context->d()->engine->hasException) + return Encode::undefined(); + + QString searchString; + if (context->argc()) { + if (context->args()[0].as<RegExpObject>()) + return context->engine()->throwTypeError(); + searchString = context->args()[0].toQString(); + } + + int pos = 0; + if (context->argc() > 1) + pos = (int) context->args()[1].toInteger(); + + if (pos == 0) + return Encode(value.startsWith(searchString)); + + QStringRef stringToSearch = value.midRef(pos); + return Encode(stringToSearch.startsWith(searchString)); +} + ReturnedValue StringPrototype::method_substr(CallContext *context) { const QString value = getThisString(context); diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index 3930a011e6..54425b0909 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -103,8 +103,8 @@ struct StringCtor: FunctionObject { V4_OBJECT2(StringCtor, FunctionObject) - static ReturnedValue construct(const Managed *m, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); + static void call(const Managed *, Scope &scope, CallData *callData); }; struct StringPrototype: StringObject @@ -115,7 +115,9 @@ struct StringPrototype: StringObject static ReturnedValue method_charAt(CallContext *context); static ReturnedValue method_charCodeAt(CallContext *context); static ReturnedValue method_concat(CallContext *context); + static ReturnedValue method_endsWith(CallContext *ctx); static ReturnedValue method_indexOf(CallContext *context); + static ReturnedValue method_includes(CallContext *context); static ReturnedValue method_lastIndexOf(CallContext *context); static ReturnedValue method_localeCompare(CallContext *context); static ReturnedValue method_match(CallContext *context); @@ -123,6 +125,7 @@ struct StringPrototype: StringObject static ReturnedValue method_search(CallContext *ctx); static ReturnedValue method_slice(CallContext *ctx); static ReturnedValue method_split(CallContext *ctx); + static ReturnedValue method_startsWith(CallContext *ctx); static ReturnedValue method_substr(CallContext *context); static ReturnedValue method_substring(CallContext *context); static ReturnedValue method_toLowerCase(CallContext *ctx); diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index c86f252353..ae5ebcad1b 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -208,30 +208,34 @@ Heap::TypedArrayCtor::TypedArrayCtor(QV4::ExecutionContext *scope, TypedArray::T { } -ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) +void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callData) { - 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 double l = callData->argc ? callData->args[0].toNumber() : 0; - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } uint len = (uint)l; 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->newArrayBuffer(byteLength)); - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer = buffer->d(); array->d()->byteLength = byteLength; array->d()->byteOffset = 0; - return array.asReturnedValue(); + scope.result = array.asReturnedValue(); + return; } Scoped<TypedArray> typedArray(scope, callData->argument(0)); if (!!typedArray) { @@ -243,8 +247,10 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) uint destByteLength = byteLength*destElementSize/srcElementSize; Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(destByteLength)); - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer = newBuffer->d(); @@ -269,7 +275,8 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) } } - return array.asReturnedValue(); + scope.result = array.asReturnedValue(); + return; } Scoped<ArrayBuffer> buffer(scope, callData->argument(0)); if (!!buffer) { @@ -278,21 +285,29 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) double dbyteOffset = callData->argc > 1 ? callData->args[1].toInteger() : 0; uint byteOffset = (uint)dbyteOffset; uint elementSize = operations[that->d()->type].bytesPerElement; - if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength()) - return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset")); + if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength()) { + scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset")); + return; + } uint byteLength; if (callData->argc < 3 || callData->args[2].isUndefined()) { byteLength = buffer->byteLength() - byteOffset; - if (buffer->byteLength() < byteOffset || byteLength % elementSize) - return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); + if (buffer->byteLength() < byteOffset || byteLength % elementSize) { + scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); + return; + } } else { double l = qBound(0., callData->args[2].toInteger(), (double)UINT_MAX); - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } l *= elementSize; - if (buffer->byteLength() - byteOffset < l) - return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); + if (buffer->byteLength() - byteOffset < l) { + scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); + return; + } byteLength = (uint)l; } @@ -300,20 +315,25 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) array->d()->buffer = buffer->d(); array->d()->byteLength = byteLength; array->d()->byteOffset = byteOffset; - return array.asReturnedValue(); + scope.result = array.asReturnedValue(); + return; } // 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); - if (scope.engine->hasException) - return scope.engine->throwTypeError(); + if (scope.engine->hasException) { + scope.result = scope.engine->throwTypeError(); + return; + } uint elementSize = operations[that->d()->type].bytesPerElement; Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(l * elementSize)); - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer = newBuffer->d(); @@ -326,19 +346,21 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) while (idx < l) { val = o->getIndexed(idx); array->d()->type->write(scope.engine, b, 0, val); - if (scope.engine->hasException) - return Encode::undefined(); + if (scope.engine->hasException) { + scope.result = Encode::undefined(); + return; + } ++idx; b += elementSize; } - return array.asReturnedValue(); + scope.result = array.asReturnedValue(); } -ReturnedValue TypedArrayCtor::call(const Managed *that, CallData *callData) +void TypedArrayCtor::call(const Managed *that, Scope &scope, CallData *callData) { - return construct(that, callData); + construct(that, scope, callData); } Heap::TypedArray::TypedArray(Type t) @@ -582,5 +604,6 @@ ReturnedValue TypedArrayPrototype::method_subarray(CallContext *ctx) callData->args[0] = buffer; callData->args[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement); callData->args[2] = Encode(newLen); - return constructor->construct(callData); + constructor->construct(scope, callData); + return scope.result.asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index 757273e4ed..d96027b96a 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -140,8 +140,8 @@ struct TypedArrayCtor: FunctionObject { V4_OBJECT2(TypedArrayCtor, FunctionObject) - static ReturnedValue construct(const Managed *m, CallData *callData); - static ReturnedValue call(const Managed *that, CallData *callData); + static void construct(const Managed *m, Scope &scope, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); }; diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index b2d64213f5..dae4d11767 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -322,16 +322,22 @@ public: } Q_ALWAYS_INLINE String *stringValue() const { + if (!isString()) + return nullptr; return m() ? reinterpret_cast<String*>(const_cast<Value *>(this)) : 0; } Q_ALWAYS_INLINE Object *objectValue() const { + if (!isObject()) + return nullptr; return m() ? reinterpret_cast<Object*>(const_cast<Value *>(this)) : 0; } Q_ALWAYS_INLINE Managed *managed() const { + if (!isManaged()) + return nullptr; return m() ? reinterpret_cast<Managed*>(const_cast<Value *>(this)) : 0; } Q_ALWAYS_INLINE Heap::Base *heapObject() const { - return m(); + return isManaged() ? m() : nullptr; } static inline Value fromHeapObject(Heap::Base *m) @@ -381,7 +387,10 @@ public: } template <typename T> T *as() { - return const_cast<T *>(const_cast<const Value *>(this)->as<T>()); + if (isManaged()) + return const_cast<T *>(const_cast<const Value *>(this)->as<T>()); + else + return nullptr; } template<typename T> inline T *cast() { diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index ecbc243baa..b6dc7716ba 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -142,6 +142,7 @@ Q_QML_EXPORT int qt_v4DebuggerHook(const char *json); } // extern "C" +#ifndef QT_NO_QML_DEBUGGER static int qt_v4BreakpointCount = 0; static bool qt_v4IsDebugging = true; static bool qt_v4IsStepping = false; @@ -203,7 +204,7 @@ int qt_v4DebuggerHook(const char *json) QJsonDocument doc = QJsonDocument::fromJson(json); QJsonObject ob = doc.object(); - QByteArray command = ob.value(QStringLiteral("command")).toString().toUtf8(); + QByteArray command = ob.value(QLatin1String("command")).toString().toUtf8(); if (command == "protocolVersion") { return ProtocolVersion; // Version number. @@ -217,17 +218,17 @@ int qt_v4DebuggerHook(const char *json) if (command == "insertBreakpoint") { Breakpoint bp; bp.bpNumber = ++qt_v4BreakpointCount; - bp.lineNumber = ob.value(QStringLiteral("lineNumber")).toString().toInt(); - bp.engineName = ob.value(QStringLiteral("engineName")).toString(); - bp.fullName = ob.value(QStringLiteral("fullName")).toString(); - bp.condition = ob.value(QStringLiteral("condition")).toString(); + bp.lineNumber = ob.value(QLatin1String("lineNumber")).toString().toInt(); + bp.engineName = ob.value(QLatin1String("engineName")).toString(); + bp.fullName = ob.value(QLatin1String("fullName")).toString(); + bp.condition = ob.value(QLatin1String("condition")).toString(); qt_v4Breakpoints.append(bp); return bp.bpNumber; } if (command == "removeBreakpoint") { - int lineNumber = ob.value(QStringLiteral("lineNumber")).toString().toInt(); - QString fullName = ob.value(QStringLiteral("fullName")).toString(); + int lineNumber = ob.value(QLatin1String("lineNumber")).toString().toInt(); + QString fullName = ob.value(QLatin1String("fullName")).toString(); if (qt_v4Breakpoints.last().matches(fullName, lineNumber)) { qt_v4Breakpoints.removeLast(); return Success; @@ -285,6 +286,7 @@ static void qt_v4CheckForBreak(QV4::ExecutionContext *context, QV4::Value **scop } } +#endif // QT_NO_QML_DEBUGGER // End of debugger interface using namespace QV4; @@ -451,12 +453,12 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(LoadRegExp) MOTH_BEGIN_INSTR(LoadClosure) - STOREVALUE(instr.result, Runtime::closure(engine, instr.value)); + STOREVALUE(instr.result, engine->runtime.closure(engine, instr.value)); MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) TRACE(inline, "property name = %s", runtimeStrings[instr.name]->toQString().toUtf8().constData()); - STOREVALUE(instr.result, Runtime::getActivationProperty(engine, instr.name)); + STOREVALUE(instr.result, engine->runtime.getActivationProperty(engine, instr.name)); MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(GetGlobalLookup) @@ -466,12 +468,12 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(StoreName) TRACE(inline, "property name = %s", runtimeStrings[instr.name]->toQString().toUtf8().constData()); - Runtime::setActivationProperty(engine, instr.name, VALUE(instr.source)); + engine->runtime.setActivationProperty(engine, instr.name, VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreName) MOTH_BEGIN_INSTR(LoadElement) - STOREVALUE(instr.result, Runtime::getElement(engine, VALUE(instr.base), VALUE(instr.index))); + STOREVALUE(instr.result, engine->runtime.getElement(engine, VALUE(instr.base), VALUE(instr.index))); MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(LoadElementLookup) @@ -480,7 +482,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(LoadElementLookup) MOTH_BEGIN_INSTR(StoreElement) - Runtime::setElement(engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); + engine->runtime.setElement(engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElement) @@ -491,7 +493,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(StoreElementLookup) MOTH_BEGIN_INSTR(LoadProperty) - STOREVALUE(instr.result, Runtime::getProperty(engine, VALUE(instr.base), instr.name)); + STOREVALUE(instr.result, engine->runtime.getProperty(engine, VALUE(instr.base), instr.name)); MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) @@ -500,7 +502,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) - Runtime::setProperty(engine, VALUE(instr.base), instr.name, VALUE(instr.source)); + engine->runtime.setProperty(engine, VALUE(instr.base), instr.name, VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreProperty) @@ -511,42 +513,42 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(SetLookup) MOTH_BEGIN_INSTR(StoreQObjectProperty) - Runtime::setQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); + engine->runtime.setQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreQObjectProperty) MOTH_BEGIN_INSTR(LoadQObjectProperty) - STOREVALUE(instr.result, Runtime::getQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); + STOREVALUE(instr.result, engine->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)); + engine->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)); + STOREVALUE(instr.result, engine->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)); + engine->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)); + STOREVALUE(instr.result, engine->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)); + STOREVALUE(instr.result, engine->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)); + STOREVALUE(instr.result, engine->runtime.getQmlAttachedProperty(engine, instr.attachedPropertiesId, instr.propertyIndex)); MOTH_END_INSTR(LoadAttachedQObjectProperty) MOTH_BEGIN_INSTR(LoadSingletonQObjectProperty) - STOREVALUE(instr.result, Runtime::getQmlSingletonQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); + STOREVALUE(instr.result, engine->runtime.getQmlSingletonQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); MOTH_END_INSTR(LoadSingletonQObjectProperty) MOTH_BEGIN_INSTR(Push) @@ -572,7 +574,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::callValue(engine, VALUE(instr.dest), callData)); + STOREVALUE(instr.result, engine->runtime.callValue(engine, VALUE(instr.dest), callData)); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) @@ -582,7 +584,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::callProperty(engine, instr.name, callData)); + STOREVALUE(instr.result, engine->runtime.callProperty(engine, instr.name, callData)); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) @@ -591,7 +593,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::callPropertyLookup(engine, instr.lookupIndex, callData)); + STOREVALUE(instr.result, engine->runtime.callPropertyLookup(engine, instr.lookupIndex, callData)); MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallScopeObjectProperty) @@ -601,7 +603,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::callQmlScopeObjectProperty(engine, instr.index, callData)); + STOREVALUE(instr.result, engine->runtime.callQmlScopeObjectProperty(engine, instr.index, callData)); MOTH_END_INSTR(CallScopeObjectProperty) MOTH_BEGIN_INSTR(CallContextObjectProperty) @@ -611,7 +613,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::callQmlContextObjectProperty(engine, instr.index, callData)); + STOREVALUE(instr.result, engine->runtime.callQmlContextObjectProperty(engine, instr.index, callData)); MOTH_END_INSTR(CallContextObjectProperty) MOTH_BEGIN_INSTR(CallElement) @@ -620,7 +622,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::callElement(engine, VALUE(instr.index), callData)); + STOREVALUE(instr.result, engine->runtime.callElement(engine, VALUE(instr.index), callData)); MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallActivationProperty) @@ -629,7 +631,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::callActivationProperty(engine, instr.name, callData)); + STOREVALUE(instr.result, engine->runtime.callActivationProperty(engine, instr.name, callData)); MOTH_END_INSTR(CallActivationProperty) MOTH_BEGIN_INSTR(CallGlobalLookup) @@ -638,7 +640,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::callGlobalLookup(engine, instr.index, callData)); + STOREVALUE(instr.result, Runtime::method_callGlobalLookup(engine, instr.index, callData)); MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(SetExceptionHandler) @@ -646,95 +648,95 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(SetExceptionHandler) MOTH_BEGIN_INSTR(CallBuiltinThrow) - Runtime::throwException(engine, VALUE(instr.arg)); + engine->runtime.throwException(engine, VALUE(instr.arg)); CHECK_EXCEPTION; MOTH_END_INSTR(CallBuiltinThrow) MOTH_BEGIN_INSTR(CallBuiltinUnwindException) - STOREVALUE(instr.result, Runtime::unwindException(engine)); + STOREVALUE(instr.result, engine->runtime.unwindException(engine)); MOTH_END_INSTR(CallBuiltinUnwindException) MOTH_BEGIN_INSTR(CallBuiltinPushCatchScope) - Runtime::pushCatchScope(static_cast<QV4::NoThrowEngine*>(engine), instr.name); + engine->runtime.pushCatchScope(static_cast<QV4::NoThrowEngine*>(engine), instr.name); context = engine->currentContext; MOTH_END_INSTR(CallBuiltinPushCatchScope) MOTH_BEGIN_INSTR(CallBuiltinPushScope) - Runtime::pushWithScope(VALUE(instr.arg), engine); + engine->runtime.pushWithScope(VALUE(instr.arg), engine); context = engine->currentContext; CHECK_EXCEPTION; MOTH_END_INSTR(CallBuiltinPushScope) MOTH_BEGIN_INSTR(CallBuiltinPopScope) - Runtime::popScope(engine); + engine->runtime.popScope(engine); context = engine->currentContext; MOTH_END_INSTR(CallBuiltinPopScope) MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) - STOREVALUE(instr.result, Runtime::foreachIterator(engine, VALUE(instr.arg))); + STOREVALUE(instr.result, engine->runtime.foreachIterator(engine, VALUE(instr.arg))); MOTH_END_INSTR(CallBuiltinForeachIteratorObject) MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) - STOREVALUE(instr.result, Runtime::foreachNextPropertyName(VALUE(instr.arg))); + STOREVALUE(instr.result, engine->runtime.foreachNextPropertyName(VALUE(instr.arg))); MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) - STOREVALUE(instr.result, Runtime::deleteMember(engine, VALUE(instr.base), instr.member)); + STOREVALUE(instr.result, engine->runtime.deleteMember(engine, VALUE(instr.base), instr.member)); MOTH_END_INSTR(CallBuiltinDeleteMember) MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) - STOREVALUE(instr.result, Runtime::deleteElement(engine, VALUE(instr.base), VALUE(instr.index))); + STOREVALUE(instr.result, engine->runtime.deleteElement(engine, VALUE(instr.base), VALUE(instr.index))); MOTH_END_INSTR(CallBuiltinDeleteSubscript) MOTH_BEGIN_INSTR(CallBuiltinDeleteName) - STOREVALUE(instr.result, Runtime::deleteName(engine, instr.name)); + STOREVALUE(instr.result, engine->runtime.deleteName(engine, instr.name)); MOTH_END_INSTR(CallBuiltinDeleteName) MOTH_BEGIN_INSTR(CallBuiltinTypeofScopeObjectProperty) - STOREVALUE(instr.result, Runtime::typeofScopeObjectProperty(engine, VALUE(instr.base), instr.index)); + STOREVALUE(instr.result, engine->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)); + STOREVALUE(instr.result, engine->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)); + STOREVALUE(instr.result, engine->runtime.typeofMember(engine, VALUE(instr.base), instr.member)); MOTH_END_INSTR(CallBuiltinTypeofMember) MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) - STOREVALUE(instr.result, Runtime::typeofElement(engine, VALUE(instr.base), VALUE(instr.index))); + STOREVALUE(instr.result, engine->runtime.typeofElement(engine, VALUE(instr.base), VALUE(instr.index))); MOTH_END_INSTR(CallBuiltinTypeofSubscript) MOTH_BEGIN_INSTR(CallBuiltinTypeofName) - STOREVALUE(instr.result, Runtime::typeofName(engine, instr.name)); + STOREVALUE(instr.result, engine->runtime.typeofName(engine, instr.name)); MOTH_END_INSTR(CallBuiltinTypeofName) MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) - STOREVALUE(instr.result, Runtime::typeofValue(engine, VALUE(instr.value))); + STOREVALUE(instr.result, engine->runtime.typeofValue(engine, VALUE(instr.value))); MOTH_END_INSTR(CallBuiltinTypeofValue) MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) - Runtime::declareVar(engine, instr.isDeletable, instr.varName); + engine->runtime.declareVar(engine, instr.isDeletable, instr.varName); MOTH_END_INSTR(CallBuiltinDeclareVar) MOTH_BEGIN_INSTR(CallBuiltinDefineArray) Q_ASSERT(instr.args + instr.argc <= stackSize); QV4::Value *args = stack + instr.args; - STOREVALUE(instr.result, Runtime::arrayLiteral(engine, args, instr.argc)); + STOREVALUE(instr.result, engine->runtime.arrayLiteral(engine, args, instr.argc)); MOTH_END_INSTR(CallBuiltinDefineArray) MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral) QV4::Value *args = stack + instr.args; - STOREVALUE(instr.result, Runtime::objectLiteral(engine, args, instr.internalClassId, instr.arrayValueCount, instr.arrayGetterSetterCountAndFlags)); + STOREVALUE(instr.result, engine->runtime.objectLiteral(engine, args, instr.internalClassId, instr.arrayValueCount, instr.arrayGetterSetterCountAndFlags)); MOTH_END_INSTR(CallBuiltinDefineObjectLiteral) MOTH_BEGIN_INSTR(CallBuiltinSetupArgumentsObject) - STOREVALUE(instr.result, Runtime::setupArgumentsObject(engine)); + STOREVALUE(instr.result, engine->runtime.setupArgumentsObject(engine)); MOTH_END_INSTR(CallBuiltinSetupArgumentsObject) MOTH_BEGIN_INSTR(CallBuiltinConvertThisToObject) - Runtime::convertThisToObject(engine); + engine->runtime.convertThisToObject(engine); CHECK_EXCEPTION; MOTH_END_INSTR(CallBuiltinConvertThisToObject) @@ -744,7 +746,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::constructValue(engine, VALUE(instr.func), callData)); + STOREVALUE(instr.result, engine->runtime.constructValue(engine, VALUE(instr.func), callData)); MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) @@ -753,7 +755,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::constructProperty(engine, instr.name, callData)); + STOREVALUE(instr.result, engine->runtime.constructProperty(engine, instr.name, callData)); MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(ConstructPropertyLookup) @@ -762,7 +764,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::constructPropertyLookup(engine, instr.index, callData)); + STOREVALUE(instr.result, engine->runtime.constructPropertyLookup(engine, instr.index, callData)); MOTH_END_INSTR(ConstructPropertyLookup) MOTH_BEGIN_INSTR(CreateActivationProperty) @@ -771,7 +773,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::constructActivationProperty(engine, instr.name, callData)); + STOREVALUE(instr.result, engine->runtime.constructActivationProperty(engine, instr.name, callData)); MOTH_END_INSTR(CreateActivationProperty) MOTH_BEGIN_INSTR(ConstructGlobalLookup) @@ -780,7 +782,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::constructGlobalLookup(engine, instr.index, callData)); + STOREVALUE(instr.result, engine->runtime.constructGlobalLookup(engine, instr.index, callData)); MOTH_END_INSTR(ConstructGlobalLookup) MOTH_BEGIN_INSTR(Jump) @@ -802,7 +804,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(JumpNe) MOTH_BEGIN_INSTR(UNot) - STOREVALUE(instr.result, Runtime::uNot(VALUE(instr.source))); + STOREVALUE(instr.result, engine->runtime.uNot(VALUE(instr.source))); MOTH_END_INSTR(UNot) MOTH_BEGIN_INSTR(UNotBool) @@ -811,15 +813,15 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(UNotBool) MOTH_BEGIN_INSTR(UPlus) - STOREVALUE(instr.result, Runtime::uPlus(VALUE(instr.source))); + STOREVALUE(instr.result, engine->runtime.uPlus(VALUE(instr.source))); MOTH_END_INSTR(UPlus) MOTH_BEGIN_INSTR(UMinus) - STOREVALUE(instr.result, Runtime::uMinus(VALUE(instr.source))); + STOREVALUE(instr.result, engine->runtime.uMinus(VALUE(instr.source))); MOTH_END_INSTR(UMinus) MOTH_BEGIN_INSTR(UCompl) - STOREVALUE(instr.result, Runtime::complement(VALUE(instr.source))); + STOREVALUE(instr.result, engine->runtime.complement(VALUE(instr.source))); MOTH_END_INSTR(UCompl) MOTH_BEGIN_INSTR(UComplInt) @@ -827,31 +829,32 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(UComplInt) MOTH_BEGIN_INSTR(Increment) - STOREVALUE(instr.result, Runtime::increment(VALUE(instr.source))); + STOREVALUE(instr.result, engine->runtime.increment(VALUE(instr.source))); MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) - STOREVALUE(instr.result, Runtime::decrement(VALUE(instr.source))); + STOREVALUE(instr.result, engine->runtime.decrement(VALUE(instr.source))); MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Binop) - STOREVALUE(instr.result, instr.alu(VALUE(instr.lhs), VALUE(instr.rhs))); + QV4::Runtime::BinaryOperation op = *reinterpret_cast<QV4::Runtime::BinaryOperation *>(reinterpret_cast<char *>(&engine->runtime) + instr.alu); + STOREVALUE(instr.result, op(VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(Binop) MOTH_BEGIN_INSTR(Add) - STOREVALUE(instr.result, Runtime::add(engine, VALUE(instr.lhs), VALUE(instr.rhs))); + STOREVALUE(instr.result, engine->runtime.add(engine, VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(BitAnd) - STOREVALUE(instr.result, Runtime::bitAnd(VALUE(instr.lhs), VALUE(instr.rhs))); + STOREVALUE(instr.result, engine->runtime.bitAnd(VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(BitAnd) MOTH_BEGIN_INSTR(BitOr) - STOREVALUE(instr.result, Runtime::bitOr(VALUE(instr.lhs), VALUE(instr.rhs))); + STOREVALUE(instr.result, engine->runtime.bitOr(VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(BitOr) MOTH_BEGIN_INSTR(BitXor) - STOREVALUE(instr.result, Runtime::bitXor(VALUE(instr.lhs), VALUE(instr.rhs))); + STOREVALUE(instr.result, engine->runtime.bitXor(VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(BitXor) MOTH_BEGIN_INSTR(Shr) @@ -886,15 +889,16 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(ShlConst) MOTH_BEGIN_INSTR(Mul) - STOREVALUE(instr.result, Runtime::mul(VALUE(instr.lhs), VALUE(instr.rhs))); + STOREVALUE(instr.result, engine->runtime.mul(VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(Mul) MOTH_BEGIN_INSTR(Sub) - STOREVALUE(instr.result, Runtime::sub(VALUE(instr.lhs), VALUE(instr.rhs))); + STOREVALUE(instr.result, engine->runtime.sub(VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(Sub) MOTH_BEGIN_INSTR(BinopContext) - STOREVALUE(instr.result, instr.alu(engine, VALUE(instr.lhs), VALUE(instr.rhs))); + QV4::Runtime::BinaryOperationContext op = *reinterpret_cast<QV4::Runtime::BinaryOperationContext *>(reinterpret_cast<char *>(&engine->runtime) + instr.alu); + STOREVALUE(instr.result, op(engine, VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(BinopContext) MOTH_BEGIN_INSTR(Ret) @@ -902,9 +906,10 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code return VALUE(instr.result).asReturnedValue(); MOTH_END_INSTR(Ret) +#ifndef QT_NO_QML_DEBUGGER MOTH_BEGIN_INSTR(Debug) engine->current->lineNumber = instr.lineNumber; - QV4::Debugging::Debugger *debugger = context->engine()->debugger; + QV4::Debugging::Debugger *debugger = context->engine()->debugger(); if (debugger && debugger->pauseAtNextOpportunity()) debugger->maybeBreakAtInstruction(); if (qt_v4IsDebugging) @@ -916,21 +921,22 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code if (qt_v4IsDebugging) qt_v4CheckForBreak(context, scopes, scopeDepth); MOTH_END_INSTR(Line) +#endif // QT_NO_QML_DEBUGGER MOTH_BEGIN_INSTR(LoadThis) VALUE(instr.result) = context->thisObject(); MOTH_END_INSTR(LoadThis) MOTH_BEGIN_INSTR(LoadQmlContext) - VALUE(instr.result) = Runtime::getQmlContext(static_cast<QV4::NoThrowEngine*>(engine)); + VALUE(instr.result) = engine->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)); + VALUE(instr.result) = engine->runtime.getQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine)); MOTH_END_INSTR(LoadQmlImportedScripts) MOTH_BEGIN_INSTR(LoadQmlSingleton) - VALUE(instr.result) = Runtime::getQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), instr.name); + VALUE(instr.result) = engine->runtime.getQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), instr.name); MOTH_END_INSTR(LoadQmlSingleton) #ifdef MOTH_THREADED_INTERPRETER @@ -968,7 +974,7 @@ void **VME::instructionJumpTable() QV4::ReturnedValue VME::exec(ExecutionEngine *engine, const uchar *code) { VME vme; - QV4::Debugging::Debugger *debugger = engine->debugger; + QV4::Debugging::Debugger *debugger = engine->debugger(); if (debugger) debugger->enteringFunction(); QV4::ReturnedValue retVal = vme.run(engine, code); diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index fe94a11082..a6d7c3b1ed 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -60,6 +60,10 @@ #include <valgrind/memcheck.h> #endif +#ifdef V4_USE_HEAPTRACK +#include <heaptrack_api.h> +#endif + #if OS(QNX) #include <sys/storage.h> // __tls() #endif @@ -68,7 +72,7 @@ #include <pthread_np.h> #endif -#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT (std::size_t)128*1024 +#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT std::size_t(128 * 1024) using namespace WTF; @@ -79,9 +83,9 @@ static uint maxShiftValue() static uint result = 0; if (!result) { result = 6; - if (Q_UNLIKELY(qEnvironmentVariableIsSet("QV4_MM_MAXBLOCK_SHIFT"))) { + if (Q_UNLIKELY(qEnvironmentVariableIsSet(QV4_MM_MAXBLOCK_SHIFT))) { bool ok; - const uint overrideValue = qgetenv("QV4_MM_MAXBLOCK_SHIFT").toUInt(&ok); + const uint overrideValue = qgetenv(QV4_MM_MAXBLOCK_SHIFT).toUInt(&ok); if (ok && overrideValue <= 11 && overrideValue > 0) result = overrideValue; } @@ -94,9 +98,9 @@ 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"))) { + if (Q_UNLIKELY(qEnvironmentVariableIsSet(QV4_MM_MAX_CHUNK_SIZE))) { bool ok; - const std::size_t overrideValue = qgetenv("QV4_MM_MAX_CHUNK_SIZE").toUInt(&ok); + const std::size_t overrideValue = qgetenv(QV4_MM_MAX_CHUNK_SIZE).toUInt(&ok); if (ok) result = overrideValue; } @@ -108,29 +112,20 @@ using namespace QV4; struct MemoryManager::Data { + const size_t pageSize; + struct ChunkHeader { Heap::Base freeItems; ChunkHeader *nextNonFull; char *itemStart; char *itemEnd; - int itemSize; + unsigned itemSize; }; - bool gcBlocked; - bool aggressiveGC; - bool gcStats; ExecutionEngine *engine; - enum { MaxItemSize = 512 }; - ChunkHeader *nonFullChunks[MaxItemSize/16]; - uint nChunks[MaxItemSize/16]; - uint availableItems[MaxItemSize/16]; - uint allocCount[MaxItemSize/16]; - int totalItems; - int totalAlloc; - uint maxShift; std::size_t maxChunkSize; - QVector<PageAllocation> heapChunks; + std::vector<PageAllocation> heapChunks; std::size_t unmanagedHeapSize; // the amount of bytes of heap that is not managed by the memory manager, but which is held onto by managed items. std::size_t unmanagedHeapSizeGCLimit; @@ -147,24 +142,39 @@ struct MemoryManager::Data LargeItem *largeItems; std::size_t totalLargeItemsAllocated; + enum { MaxItemSize = 512 }; + ChunkHeader *nonFullChunks[MaxItemSize/16]; + uint nChunks[MaxItemSize/16]; + uint availableItems[MaxItemSize/16]; + uint allocCount[MaxItemSize/16]; + int totalItems; + int totalAlloc; + uint maxShift; + + bool gcBlocked; + bool aggressiveGC; + bool gcStats; + bool unused; // suppress padding warning + // statistics: #ifdef DETAILED_MM_STATS QVector<unsigned> allocSizeCounters; #endif // DETAILED_MM_STATS Data() - : gcBlocked(false) - , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC")) - , gcStats(!qEnvironmentVariableIsEmpty("QV4_MM_STATS")) + : pageSize(WTF::pageSize()) , engine(0) - , totalItems(0) - , totalAlloc(0) - , maxShift(maxShiftValue()) , maxChunkSize(maxChunkSizeValue()) , unmanagedHeapSize(0) , unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT) , largeItems(0) , totalLargeItemsAllocated(0) + , totalItems(0) + , totalAlloc(0) + , maxShift(maxShiftValue()) + , gcBlocked(false) + , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC")) + , gcStats(!qEnvironmentVariableIsEmpty(QV4_MM_STATS)) { memset(nonFullChunks, 0, sizeof(nonFullChunks)); memset(nChunks, 0, sizeof(nChunks)); @@ -174,8 +184,8 @@ struct MemoryManager::Data ~Data() { - for (QVector<PageAllocation>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) { - Q_V4_PROFILE_DEALLOC(engine, 0, i->size(), Profiling::HeapPage); + for (std::vector<PageAllocation>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) { + Q_V4_PROFILE_DEALLOC(engine, i->size(), Profiling::HeapPage); i->deallocate(); } } @@ -198,7 +208,7 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec // qDebug("chunk @ %p, in use: %s, mark bit: %s", // item, (m->inUse() ? "yes" : "no"), (m->isMarked() ? "true" : "false")); - Q_ASSERT((qintptr) item % 16 == 0); + Q_ASSERT(qintptr(item) % 16 == 0); if (m->isMarked()) { Q_ASSERT(m->inUse()); @@ -226,7 +236,10 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec VALGRIND_DISABLE_ERROR_REPORTING; VALGRIND_MEMPOOL_FREE(engine->memoryManager, m); #endif - Q_V4_PROFILE_DEALLOC(engine, m, header->itemSize, Profiling::SmallItem); +#ifdef V4_USE_HEAPTRACK + heaptrack_report_free(m); +#endif + Q_V4_PROFILE_DEALLOC(engine, header->itemSize, Profiling::SmallItem); ++(*itemsInUse); } // Relink all free blocks to rewrite references to any released chunk. @@ -289,10 +302,11 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize runGC(); // we use malloc for this - MemoryManager::Data::LargeItem *item = static_cast<MemoryManager::Data::LargeItem *>( - malloc(Q_V4_PROFILE_ALLOC(engine, size + sizeof(MemoryManager::Data::LargeItem), - Profiling::LargeItem))); - memset(item, 0, size + sizeof(MemoryManager::Data::LargeItem)); + const size_t totalSize = size + sizeof(MemoryManager::Data::LargeItem); + Q_V4_PROFILE_ALLOC(engine, totalSize, Profiling::LargeItem); + MemoryManager::Data::LargeItem *item = + static_cast<MemoryManager::Data::LargeItem *>(malloc(totalSize)); + memset(item, 0, totalSize); item->next = m_d->largeItems; item->size = size; m_d->largeItems = item; @@ -324,14 +338,14 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize if (shift > m_d->maxShift) shift = m_d->maxShift; std::size_t allocSize = m_d->maxChunkSize*(size_t(1) << shift); - allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); - PageAllocation allocation = PageAllocation::allocate( - Q_V4_PROFILE_ALLOC(engine, allocSize, Profiling::HeapPage), - OSAllocator::JSGCHeapPages); - m_d->heapChunks.append(allocation); + allocSize = roundUpToMultipleOf(m_d->pageSize, allocSize); + Q_V4_PROFILE_ALLOC(engine, allocSize, Profiling::HeapPage); + PageAllocation allocation = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); + m_d->heapChunks.push_back(allocation); header = reinterpret_cast<Data::ChunkHeader *>(allocation.base()); - header->itemSize = int(size); + Q_ASSERT(size <= UINT_MAX); + header->itemSize = unsigned(size); header->itemStart = reinterpret_cast<char *>(allocation.base()) + roundUpToMultipleOf(16, sizeof(Data::ChunkHeader)); header->itemEnd = reinterpret_cast<char *>(allocation.base()) + allocation.size() - header->itemSize; @@ -347,19 +361,26 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize } last->setNextFree(0); m = header->freeItems.nextFree(); - const size_t increase = (header->itemEnd - header->itemStart) / header->itemSize; + Q_ASSERT(header->itemEnd >= header->itemStart); + const size_t increase = quintptr(header->itemEnd - header->itemStart) / header->itemSize; m_d->availableItems[pos] += uint(increase); m_d->totalItems += int(increase); #ifdef V4_USE_VALGRIND VALGRIND_MAKE_MEM_NOACCESS(allocation.base(), allocSize); VALGRIND_MEMPOOL_ALLOC(this, header, sizeof(Data::ChunkHeader)); #endif +#ifdef V4_USE_HEAPTRACK + heaptrack_report_alloc(header, sizeof(Data::ChunkHeader)); +#endif } found: #ifdef V4_USE_VALGRIND VALGRIND_MEMPOOL_ALLOC(this, m, size); #endif +#ifdef V4_USE_HEAPTRACK + heaptrack_report_alloc(m, size); +#endif Q_V4_PROFILE_ALLOC(engine, size, Profiling::SmallItem); ++m_d->allocCount[pos]; @@ -374,6 +395,7 @@ static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase) { while (engine->jsStackTop > markBase) { Heap::Base *h = engine->popForGC(); + Q_ASSERT(h); // at this point we should only have Heap::Base objects in this area on the stack. If not, weird things might happen. Q_ASSERT (h->vtable()->markObjects); h->vtable()->markObjects(h, engine); } @@ -430,7 +452,7 @@ void MemoryManager::sweep(bool lastSweep) for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { if (!(*it).isManaged()) continue; - Managed *m = (*it).as<Managed>(); + Managed *m = (*it).managed(); if (m->markBit()) continue; // we need to call detroyObject on qobjectwrappers now, so that they can emit the destroyed @@ -465,29 +487,33 @@ void MemoryManager::sweep(bool lastSweep) } } - bool *chunkIsEmpty = (bool *)alloca(m_d->heapChunks.size() * sizeof(bool)); + bool *chunkIsEmpty = static_cast<bool *>(alloca(m_d->heapChunks.size() * sizeof(bool))); uint itemsInUse[MemoryManager::Data::MaxItemSize/16]; memset(itemsInUse, 0, sizeof(itemsInUse)); memset(m_d->nonFullChunks, 0, sizeof(m_d->nonFullChunks)); - for (int i = 0; i < m_d->heapChunks.size(); ++i) { + for (size_t 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], engine, &m_d->unmanagedHeapSize); } - QVector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin(); - for (int i = 0; i < m_d->heapChunks.size(); ++i) { + std::vector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin(); + for (size_t i = 0; i < m_d->heapChunks.size(); ++i) { Q_ASSERT(chunkIter != m_d->heapChunks.end()); Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(chunkIter->base()); const size_t pos = header->itemSize >> 4; - const size_t decrease = (header->itemEnd - header->itemStart) / header->itemSize; + Q_ASSERT(header->itemEnd >= header->itemStart); + const size_t decrease = quintptr(header->itemEnd - header->itemStart) / header->itemSize; // 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(engine, 0, chunkIter->size(), Profiling::HeapPage); + Q_V4_PROFILE_DEALLOC(engine, chunkIter->size(), Profiling::HeapPage); #ifdef V4_USE_VALGRIND VALGRIND_MEMPOOL_FREE(this, header); #endif +#ifdef V4_USE_HEAPTRACK + heaptrack_report_free(header); +#endif --m_d->nChunks[pos]; m_d->availableItems[pos] -= uint(decrease); m_d->totalItems -= int(decrease); @@ -516,8 +542,8 @@ void MemoryManager::sweep(bool lastSweep) m->vtable()->destroy(m); *last = i->next; - free(Q_V4_PROFILE_DEALLOC(engine, i, i->size + sizeof(Data::LargeItem), - Profiling::LargeItem)); + Q_V4_PROFILE_DEALLOC(engine, i->size + sizeof(Data::LargeItem), Profiling::LargeItem); + free(i); i = *last; } @@ -561,7 +587,7 @@ void MemoryManager::runGC() t.restart(); const size_t usedBefore = getUsedMem(); const size_t largeItemsBefore = getLargeItemsMem(); - int chunksBefore = m_d->heapChunks.size(); + size_t chunksBefore = m_d->heapChunks.size(); sweep(); const size_t usedAfter = getUsedMem(); const size_t largeItemsAfter = getLargeItemsMem(); @@ -589,11 +615,11 @@ void MemoryManager::runGC() size_t MemoryManager::getUsedMem() const { size_t usedMem = 0; - for (QVector<PageAllocation>::const_iterator i = m_d->heapChunks.cbegin(), ei = m_d->heapChunks.cend(); i != ei; ++i) { + for (std::vector<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); - Q_ASSERT((qintptr) item % 16 == 0); + Q_ASSERT(qintptr(item) % 16 == 0); if (m->inUse()) usedMem += header->itemSize; } @@ -604,7 +630,7 @@ size_t MemoryManager::getUsedMem() const size_t MemoryManager::getAllocatedMem() const { size_t total = 0; - for (int i = 0; i < m_d->heapChunks.size(); ++i) + for (size_t i = 0; i < m_d->heapChunks.size(); ++i) total += m_d->heapChunks.at(i).size(); return total; } @@ -667,7 +693,7 @@ void MemoryManager::collectFromJSStack() const Value *v = engine->jsStackBase; Value *top = engine->jsStackTop; while (v < top) { - Managed *m = v->as<Managed>(); + Managed *m = v->managed(); if (m && m->inUse()) // Skip pointers to already freed objects, they are bogus as well m->mark(engine); diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index e169675f7d..026cbd8c6b 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -59,6 +59,10 @@ //#define DETAILED_MM_STATS +#define QV4_MM_MAXBLOCK_SHIFT "QV4_MM_MAXBLOCK_SHIFT" +#define QV4_MM_MAX_CHUNK_SIZE "QV4_MM_MAX_CHUNK_SIZE" +#define QV4_MM_STATS "QV4_MM_STATS" + QT_BEGIN_NAMESPACE namespace QV4 { diff --git a/src/qml/parser/qqmljsengine_p.cpp b/src/qml/parser/qqmljsengine_p.cpp index 07064a4889..7a6d9c3826 100644 --- a/src/qml/parser/qqmljsengine_p.cpp +++ b/src/qml/parser/qqmljsengine_p.cpp @@ -114,7 +114,7 @@ double integerFromString(const char *buf, int size, int radix) double integerFromString(const QString &str, int radix) { - QByteArray ba = str.trimmed().toLatin1(); + QByteArray ba = QStringRef(&str).trimmed().toLatin1(); return integerFromString(ba.constData(), ba.size(), radix); } diff --git a/src/qml/qml.pro b/src/qml/qml.pro index f4862a17a6..cc5023f12a 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -1,11 +1,17 @@ TARGET = QtQml -QT = core-private network +QT = core-private + +no_network { + DEFINES += QT_NO_NETWORK +} else { + QT += network +} DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000 win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS -win32:!wince*:!winrt:LIBS += -lshell32 +win32:!winrt:LIBS += -lshell32 solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 # Ensure this gcc optimization is switched off for mips platforms to avoid trouble with JIT. @@ -16,7 +22,7 @@ exists("qqml_enable_gcov") { LIBS_PRIVATE += -lgcov } -greaterThan(QT_GCC_MAJOR_VERSION, 5) { +gcc:!intel_icc:greaterThan(QT_GCC_MAJOR_VERSION, 5) { # Our code is bad. Temporary workaround. QMAKE_CXXFLAGS += -fno-delete-null-pointer-checks } diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri index a671cfa12d..addf1d9ff8 100644 --- a/src/qml/qml/ftw/ftw.pri +++ b/src/qml/qml/ftw/ftw.pri @@ -11,7 +11,6 @@ HEADERS += \ $$PWD/qdeletewatcher_p.h \ $$PWD/qrecyclepool_p.h \ $$PWD/qflagpointer_p.h \ - $$PWD/qpointervaluepair_p.h \ $$PWD/qlazilyallocated_p.h \ $$PWD/qqmlnullablevalue_p.h \ diff --git a/src/qml/qml/ftw/qhashedstring.cpp b/src/qml/qml/ftw/qhashedstring.cpp index 37c1003748..117670dbfc 100644 --- a/src/qml/qml/ftw/qhashedstring.cpp +++ b/src/qml/qml/ftw/qhashedstring.cpp @@ -39,30 +39,7 @@ #include "qhashedstring_p.h" -inline quint32 stringHash(const QChar* data, int length) -{ - return QV4::String::createHashValue(data, length); -} -inline quint32 stringHash(const char *data, int length) -{ - return QV4::String::createHashValue(data, length); -} - -void QHashedString::computeHash() const -{ - m_hash = stringHash(constData(), length()); -} - -void QHashedStringRef::computeHash() const -{ - m_hash = stringHash(m_data, m_length); -} - -void QHashedCStringRef::computeHash() const -{ - m_hash = stringHash(m_data, m_length); -} /* A QHash has initially around pow(2, MinNumBits) buckets. For diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h index 6ff3e4a11b..9ee50ec931 100644 --- a/src/qml/qml/ftw/qhashedstring_p.h +++ b/src/qml/qml/ftw/qhashedstring_p.h @@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE // #define QSTRINGHASH_LINK_DEBUG class QHashedStringRef; -class Q_AUTOTEST_EXPORT QHashedString : public QString +class Q_QML_PRIVATE_EXPORT QHashedString : public QString { public: inline QHashedString(); @@ -85,16 +85,20 @@ public: static bool compare(const QChar *lhs, const QChar *rhs, int length); static inline bool compare(const QChar *lhs, const char *rhs, int length); static inline bool compare(const char *lhs, const char *rhs, int length); + + static inline quint32 stringHash(const QChar* data, int length); + static inline quint32 stringHash(const char *data, int length); + private: friend class QHashedStringRef; friend class QStringHashNode; - void computeHash() const; + inline void computeHash() const; mutable quint32 m_hash; }; class QHashedCStringRef; -class Q_AUTOTEST_EXPORT QHashedStringRef +class Q_QML_PRIVATE_EXPORT QHashedStringRef { public: inline QHashedStringRef(); @@ -136,7 +140,7 @@ public: private: friend class QHashedString; - void computeHash() const; + inline void computeHash() const; const QChar *m_data; int m_length; @@ -163,7 +167,7 @@ public: private: friend class QHashedStringRef; - void computeHash() const; + inline void computeHash() const; const char *m_data; int m_length; @@ -1214,6 +1218,11 @@ bool QHashedStringRef::isLatin1() const return true; } +void QHashedStringRef::computeHash() const +{ + m_hash = QHashedString::stringHash(m_data, m_length); +} + bool QHashedStringRef::startsWithUpper() const { if (m_length < 1) return false; @@ -1280,6 +1289,11 @@ void QHashedCStringRef::writeUtf16(quint16 *output) const *output++ = *d++; } +void QHashedCStringRef::computeHash() const +{ + m_hash = QHashedString::stringHash(m_data, m_length); +} + bool QHashedString::compare(const QChar *lhs, const char *rhs, int length) { Q_ASSERT(lhs && rhs); @@ -1295,6 +1309,21 @@ bool QHashedString::compare(const char *lhs, const char *rhs, int length) return 0 == ::memcmp(lhs, rhs, length); } +quint32 QHashedString::stringHash(const QChar *data, int length) +{ + return QV4::String::createHashValue(data, length, Q_NULLPTR); +} + +quint32 QHashedString::stringHash(const char *data, int length) +{ + return QV4::String::createHashValue(data, length, Q_NULLPTR); +} + +void QHashedString::computeHash() const +{ + m_hash = stringHash(constData(), length()); +} + QT_END_NAMESPACE #endif // QHASHEDSTRING_P_H diff --git a/src/qml/qml/ftw/qpointervaluepair_p.h b/src/qml/qml/ftw/qpointervaluepair_p.h deleted file mode 100644 index 3d0644039f..0000000000 --- a/src/qml/qml/ftw/qpointervaluepair_p.h +++ /dev/null @@ -1,194 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPOINTERVALUEPAIR_P_H -#define QPOINTERVALUEPAIR_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/qflagpointer_p.h> - -QT_BEGIN_NAMESPACE - -// QPointerValuePair is intended to help reduce the memory consumption of a class. -// In the common case, QPointerValuePair behaves like a pointer. In this mode, it -// consumes the same memory as a regular pointer. -// Additionally, QPointerValuePair can store an arbitrary value type in *addition* -// to the pointer. In this case, it uses slightly more memory than the pointer and -// value type combined. -// Consequently, this class is most useful in cases where a pointer is always stored -// and a value type is rarely stored. -template<typename P, typename V> -class QPointerValuePair { -public: - inline QPointerValuePair(); - inline QPointerValuePair(P *); - inline ~QPointerValuePair(); - - inline bool isNull() const; - - inline bool flag() const; - inline void setFlag(); - inline void clearFlag(); - inline void setFlagValue(bool); - - inline QPointerValuePair<P, V> &operator=(P *); - - inline P *operator->() const; - inline P *operator*() const; - - inline bool hasValue() const; - inline V &value(); - inline const V *constValue() const; - -private: - struct Value { P *pointer; V value; }; - QBiPointer<P, Value> d; -}; - -template<typename P, typename V> -QPointerValuePair<P, V>::QPointerValuePair() -{ -} - -template<typename P, typename V> -QPointerValuePair<P, V>::QPointerValuePair(P *p) -: d(p) -{ -} - -template<typename P, typename V> -QPointerValuePair<P, V>::~QPointerValuePair() -{ - if (d.isT2()) delete d.asT2(); -} - -template<typename P, typename V> -bool QPointerValuePair<P, V>::isNull() const -{ - if (d.isT1()) return 0 == d.asT1(); - else return d.asT2()->pointer == 0; -} - -template<typename P, typename V> -bool QPointerValuePair<P, V>::flag() const -{ - return d.flag(); -} - -template<typename P, typename V> -void QPointerValuePair<P, V>::setFlag() -{ - d.setFlag(); -} - -template<typename P, typename V> -void QPointerValuePair<P, V>::clearFlag() -{ - d.clearFlag(); -} - -template<typename P, typename V> -void QPointerValuePair<P, V>::setFlagValue(bool v) -{ - d.setFlagValue(v); -} - -template<typename P, typename V> -QPointerValuePair<P, V> &QPointerValuePair<P, V>::operator=(P *o) -{ - if (d.isT1()) d = o; - else d.asT2()->pointer = o; - return *this; -} - -template<typename P, typename V> -P *QPointerValuePair<P, V>::operator->() const -{ - if (d.isT1()) return d.asT1(); - else return d.asT2()->pointer; -} - -template<typename P, typename V> -P *QPointerValuePair<P, V>::operator*() const -{ - if (d.isT1()) return d.asT1(); - else return d.asT2()->pointer; -} - -template<typename P, typename V> -bool QPointerValuePair<P, V>::hasValue() const -{ - return d.isT2(); -} - -template<typename P, typename V> -V &QPointerValuePair<P, V>::value() -{ - if (d.isT1()) { - P *p = d.asT1(); - Value *value = new Value; - value->pointer = p; - d = value; - } - - return d.asT2()->value; -} - -// Will return null if hasValue() == false -template<typename P, typename V> -const V *QPointerValuePair<P, V>::constValue() const -{ - if (d.isT2()) return &d.asT2()->value; - else return 0; -} - -QT_END_NAMESPACE - -#endif // QPOINTERVALUEPAIR_P_H diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 4d84cc82ae..cc394b78cb 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -12,7 +12,6 @@ SOURCES += \ $$PWD/qqmlpropertyvalueinterceptor.cpp \ $$PWD/qqmlproxymetaobject.cpp \ $$PWD/qqmlvme.cpp \ - $$PWD/qqmlcompileddata.cpp \ $$PWD/qqmlboundsignal.cpp \ $$PWD/qqmlmetatype.cpp \ $$PWD/qqmlstringconverters.cpp \ @@ -39,7 +38,6 @@ SOURCES += \ $$PWD/qqmlvaluetypeproxybinding.cpp \ $$PWD/qqmlglobal.cpp \ $$PWD/qqmlfile.cpp \ - $$PWD/qqmlmemoryprofiler.cpp \ $$PWD/qqmlplatform.cpp \ $$PWD/qqmlbinding.cpp \ $$PWD/qqmlabstracturlinterceptor.cpp \ @@ -50,7 +48,8 @@ SOURCES += \ $$PWD/qqmltypewrapper.cpp \ $$PWD/qqmlfileselector.cpp \ $$PWD/qqmlobjectcreator.cpp \ - $$PWD/qqmldirparser.cpp + $$PWD/qqmldirparser.cpp \ + $$PWD/qqmldelayedcallqueue.cpp HEADERS += \ $$PWD/qqmlglobal_p.h \ @@ -70,7 +69,6 @@ HEADERS += \ $$PWD/qqmlparserstatus.h \ $$PWD/qqmlproxymetaobject_p.h \ $$PWD/qqmlvme_p.h \ - $$PWD/qqmlcompiler_p.h \ $$PWD/qqmlengine_p.h \ $$PWD/qqmlexpression_p.h \ $$PWD/qqmlprivate.h \ @@ -108,7 +106,6 @@ HEADERS += \ $$PWD/qqmlabstractbinding_p.h \ $$PWD/qqmlvaluetypeproxybinding_p.h \ $$PWD/qqmlfile.h \ - $$PWD/qqmlmemoryprofiler_p.h \ $$PWD/qqmlplatform_p.h \ $$PWD/qqmlbinding_p.h \ $$PWD/qqmlextensionplugin_p.h \ @@ -122,7 +119,8 @@ HEADERS += \ $$PWD/qqmlfileselector_p.h \ $$PWD/qqmlfileselector.h \ $$PWD/qqmlobjectcreator_p.h \ - $$PWD/qqmldirparser_p.h + $$PWD/qqmldirparser_p.h \ + $$PWD/qqmldelayedcallqueue_p.h include(ftw/ftw.pri) include(v8/v8.pri) diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h index 674178153a..0ccfae4610 100644 --- a/src/qml/qml/qqmlabstractbinding_p.h +++ b/src/qml/qml/qqmlabstractbinding_p.h @@ -55,7 +55,6 @@ #include <QtCore/qshareddata.h> #include <private/qtqmlglobal_p.h> #include <private/qqmlproperty_p.h> -#include <private/qpointervaluepair_p.h> QT_BEGIN_NAMESPACE @@ -83,7 +82,7 @@ public: // binding is not enabled or added to the object. QObject *targetObject() const { return m_target.data(); } - virtual void setEnabled(bool e, QQmlPropertyPrivate::WriteFlags f = QQmlPropertyPrivate::DontRemoveBinding) = 0; + virtual void setEnabled(bool e, QQmlPropertyData::WriteFlags f = QQmlPropertyData::DontRemoveBinding) = 0; void addToObject(); void removeFromObject(); @@ -92,6 +91,8 @@ public: inline QQmlAbstractBinding *nextBinding() const; + inline bool canUseAccessor() const + { return m_nextBinding.flag2(); } struct RefCount { RefCount() : refCount(0) {} @@ -113,8 +114,15 @@ protected: inline void setNextBinding(QQmlAbstractBinding *); int m_targetIndex; + + // Pointer is the target object to which the binding binds + // flag1 is the updating flag + // flag2 is the enabled flag QFlagPointer<QObject> m_target; + // Pointer to the next binding in the linked list of bindings. + // flag1 is used for addedToObject + // flag2 indicates if an accessor is can be used (i.e. there is no interceptor on the target) QFlagPointer<QQmlAbstractBinding> m_nextBinding; }; diff --git a/src/qml/qml/qqmlaccessors_p.h b/src/qml/qml/qqmlaccessors_p.h index 55562a5307..e98663adfe 100644 --- a/src/qml/qml/qqmlaccessors_p.h +++ b/src/qml/qml/qqmlaccessors_p.h @@ -102,11 +102,15 @@ class QQmlNotifier; } \ } while (false); -#define QML_PRIVATE_ACCESSOR(clazz, cpptype, name, variable) \ +#define QML_PRIVATE_ACCESSOR(clazz, cpptype, name, variable, setter) \ static void clazz ## _ ## name ## Read(QObject *o, void *rv) \ { \ clazz ## Private *d = clazz ## Private::get(static_cast<clazz *>(o)); \ *static_cast<cpptype *>(rv) = d->variable; \ + } \ + static void clazz ## _ ## name ## Write(QObject *o, void *rv) \ + { \ + static_cast<clazz *>(o)->setter(*static_cast<cpptype *>(rv)); \ } #define QML_PROPERTY_NAME(name) #name, sizeof #name - 1 @@ -115,6 +119,7 @@ class QQmlAccessors { public: void (*read)(QObject *object, void *output); + void (*write)(QObject *object, void *output); void (*notifier)(QObject *object, QQmlNotifier **notifier); }; diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 57cdd3f47f..8c342e0592 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -211,11 +211,8 @@ QQmlApplicationEngine::QQmlApplicationEngine(QObject *parent) This is provided as a convenience, and is the same as using the empty constructor and calling load afterwards. */ QQmlApplicationEngine::QQmlApplicationEngine(const QUrl &url, QObject *parent) - : QQmlEngine(*(new QQmlApplicationEnginePrivate(this)), parent) + : QQmlApplicationEngine(parent) { - Q_D(QQmlApplicationEngine); - d->init(); - QJSEnginePrivate::addToDebugServer(this); load(url); } @@ -228,12 +225,8 @@ QQmlApplicationEngine::QQmlApplicationEngine(const QUrl &url, QObject *parent) This is provided as a convenience, and is the same as using the empty constructor and calling load afterwards. */ QQmlApplicationEngine::QQmlApplicationEngine(const QString &filePath, QObject *parent) - : QQmlEngine(*(new QQmlApplicationEnginePrivate(this)), parent) + : QQmlApplicationEngine(QUrl::fromLocalFile(filePath), parent) { - Q_D(QQmlApplicationEngine); - d->init(); - QJSEnginePrivate::addToDebugServer(this); - load(QUrl::fromLocalFile(filePath)); } /*! diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 1249e1b6c8..10d16a8a12 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -42,8 +42,8 @@ #include "qqml.h" #include "qqmlcontext.h" #include "qqmlinfo.h" -#include "qqmlcompiler_p.h" #include "qqmldata_p.h" +#include "qqmlaccessors_p.h" #include <private/qqmlprofiler_p.h> #include <private/qqmlexpression_p.h> #include <private/qqmlscriptstring_p.h> @@ -57,27 +57,28 @@ QT_BEGIN_NAMESPACE -QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt) - : QQmlJavaScriptExpression(), - QQmlAbstractBinding() +QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContext *ctxt) { - setNotifyOnValueChanged(true); - QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt)); - setScopeObject(obj); + QQmlBinding *b = newBinding(property); + b->setNotifyOnValueChanged(true); + b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt)); + b->setScopeObject(obj); - createQmlBinding(context(), obj, str, QString(), 0); + b->createQmlBinding(b->context(), obj, str, QString(), 0); + + return b; } -QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt) - : QQmlJavaScriptExpression(), - QQmlAbstractBinding() +QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt) { + QQmlBinding *b = newBinding(property); + if (ctxt && !ctxt->isValid()) - return; + return b; const QQmlScriptStringPrivate *scriptPrivate = script.d.data(); if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid())) - return; + return b; QString url; QV4::Function *runtimeFunction = 0; @@ -90,53 +91,61 @@ QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlConte runtimeFunction = ctxtdata->typeCompilationUnit->runtimeFunctions.at(scriptPrivate->bindingId); } - setNotifyOnValueChanged(true); - QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context)); - setScopeObject(obj ? obj : scriptPrivate->scope); + b->setNotifyOnValueChanged(true); + b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context)); + b->setScopeObject(obj ? obj : scriptPrivate->scope); - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(context()->engine)->v4engine(); + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(b->context()->engine)->v4engine(); if (runtimeFunction) { - m_function.set(v4, QV4::FunctionObject::createQmlFunction(ctxtdata, scopeObject(), runtimeFunction)); + b->m_function.set(v4, QV4::FunctionObject::createQmlFunction(ctxtdata, b->scopeObject(), runtimeFunction)); } else { QString code = scriptPrivate->script; - createQmlBinding(context(), scopeObject(), code, url, scriptPrivate->lineNumber); + b->createQmlBinding(b->context(), b->scopeObject(), code, url, scriptPrivate->lineNumber); } + + return b; } -QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt) - : QQmlJavaScriptExpression(), - QQmlAbstractBinding() +QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContextData *ctxt) { - setNotifyOnValueChanged(true); - QQmlJavaScriptExpression::setContext(ctxt); - setScopeObject(obj); + QQmlBinding *b = newBinding(property); + + b->setNotifyOnValueChanged(true); + b->QQmlJavaScriptExpression::setContext(ctxt); + b->setScopeObject(obj); - createQmlBinding(ctxt, obj, str, QString(), 0); + b->createQmlBinding(ctxt, obj, str, QString(), 0); + + return b; } -QQmlBinding::QQmlBinding(const QString &str, QObject *obj, - QQmlContextData *ctxt, - const QString &url, quint16 lineNumber, quint16 columnNumber) - : QQmlJavaScriptExpression(), - QQmlAbstractBinding() +QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, + QQmlContextData *ctxt, const QString &url, quint16 lineNumber, + quint16 columnNumber) { + QQmlBinding *b = newBinding(property); + Q_UNUSED(columnNumber); - setNotifyOnValueChanged(true); - QQmlJavaScriptExpression::setContext(ctxt); - setScopeObject(obj); + b->setNotifyOnValueChanged(true); + b->QQmlJavaScriptExpression::setContext(ctxt); + b->setScopeObject(obj); - createQmlBinding(ctxt, obj, str, url, lineNumber); + b->createQmlBinding(ctxt, obj, str, url, lineNumber); + + return b; } -QQmlBinding::QQmlBinding(const QV4::Value &functionPtr, QObject *obj, QQmlContextData *ctxt) - : QQmlJavaScriptExpression(), - QQmlAbstractBinding() +QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QV4::Value &functionPtr, QObject *obj, QQmlContextData *ctxt) { - setNotifyOnValueChanged(true); - QQmlJavaScriptExpression::setContext(ctxt); - setScopeObject(obj); + QQmlBinding *b = newBinding(property); + + b->setNotifyOnValueChanged(true); + b->QQmlJavaScriptExpression::setContext(ctxt); + b->setScopeObject(obj); - m_function.set(functionPtr.as<QV4::Object>()->engine(), functionPtr); + b->m_function.set(functionPtr.as<QV4::Object>()->engine(), functionPtr); + + return b; } QQmlBinding::~QQmlBinding() @@ -148,7 +157,7 @@ void QQmlBinding::setNotifyOnValueChanged(bool v) QQmlJavaScriptExpression::setNotifyOnValueChanged(v); } -void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) +void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) { if (!enabledFlag() || !context() || !context()->isValid()) return; @@ -157,44 +166,73 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) if (QQmlData::wasDeleted(targetObject())) return; + // Check for a binding update loop + if (Q_UNLIKELY(updatingFlag())) { + QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), getPropertyData(), 0); + QQmlAbstractBinding::printBindingLoopError(p); + return; + } + setUpdatingFlag(true); + + DeleteWatcher watcher(this); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); QV4::Scope scope(ep->v4engine()); QV4::ScopedFunctionObject f(scope, m_function.value()); Q_ASSERT(f); - if (updatingFlag()) { - QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), getPropertyData(), 0); - QQmlAbstractBinding::printBindingLoopError(p); - return; - } + if (canUseAccessor()) + flags.setFlag(QQmlPropertyData::BypassInterceptor); QQmlBindingProfiler prof(ep->profiler, this, f); - setUpdatingFlag(true); - - QQmlJavaScriptExpression::DeleteWatcher watcher(this); + doUpdate(this, watcher, flags, scope, f); - QQmlPropertyData pd = getPropertyData(); + if (!watcher.wasDeleted()) + setUpdatingFlag(false); +} - if (pd.propType == qMetaTypeId<QQmlBinding *>()) { +// QQmlBindingBinding is for target properties which are of type "binding" (instead of, say, int or +// double). The reason for being is that GenericBinding::fastWrite needs a compile-time constant +// expression for the switch for the compiler to generate the optimal code, but +// qMetaTypeId<QQmlBinding *>() needs to be used for the ID. So QQmlBinding::newBinding uses that +// to instantiate this class. +class QQmlBindingBinding: public QQmlBinding +{ +protected: + void doUpdate(QQmlBinding *binding, const DeleteWatcher &, + QQmlPropertyData::WriteFlags flags, QV4::Scope &, + const QV4::ScopedFunctionObject &) Q_DECL_OVERRIDE Q_DECL_FINAL + { + QQmlPropertyData pd = getPropertyData(); int idx = pd.coreIndex; Q_ASSERT(idx != -1); - QQmlBinding *t = this; int status = -1; - void *a[] = { &t, 0, &status, &flags }; + void *a[] = { &binding, 0, &status, &flags }; QMetaObject::metacall(*m_target, QMetaObject::WriteProperty, idx, a); + } +}; - } else { +template<int StaticPropType> +class GenericBinding: public QQmlBinding +{ +protected: + void doUpdate(QQmlBinding *binding, const DeleteWatcher &watcher, + QQmlPropertyData::WriteFlags flags, QV4::Scope &scope, + const QV4::ScopedFunctionObject &f) Q_DECL_OVERRIDE Q_DECL_FINAL + { + auto ep = QQmlEnginePrivate::get(scope.engine); ep->referenceScarceResources(); bool isUndefined = false; - QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); + QV4::ScopedCallData callData(scope); + binding->QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope); bool error = false; if (!watcher.wasDeleted() && isAddedToObject() && !hasError()) - error = !write(pd, result, isUndefined, flags); + error = !write(scope.result, isUndefined, flags); if (!watcher.wasDeleted()) { @@ -214,59 +252,65 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) 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); - -#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 (Q_LIKELY(!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); + // Returns true if successful, false if an error description was set on expression + Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined, + QQmlPropertyData::WriteFlags flags) + { + QQmlPropertyData pd = getPropertyData(); + int propertyType = StaticPropType; // If the binding is specialized to a type, the if and switch below will be constant-folded. + if (propertyType == QMetaType::UnknownType) + propertyType = pd.propType; + Q_ASSERT(targetObject()); + + if (Q_LIKELY(!isUndefined && !pd.isValueTypeVirtual())) { + switch (propertyType) { + case QMetaType::Bool: + if (result.isBoolean()) + return doStore<bool>(result.booleanValue(), pd, flags); + else + return doStore<bool>(result.toBoolean(), pd, flags); + case QMetaType::Int: + if (result.isInteger()) + return doStore<int>(result.integerValue(), pd, flags); + else if (result.isNumber()) + return doStore<int>(result.doubleValue(), pd, flags); + break; + case QMetaType::Double: + if (result.isNumber()) + return doStore<double>(result.asDouble(), pd, flags); + break; + case QMetaType::Float: + if (result.isNumber()) + return doStore<float>(result.asDouble(), pd, flags); + break; + case QMetaType::QString: + if (result.isString()) + return doStore<QString>(result.toQStringNoThrow(), pd, flags); + break; + default: + if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) { + if (vtw->d()->valueType->typeId == pd.propType) { + return vtw->write(m_target.data(), pd.coreIndex); + } } + break; } - break; } + + return slowWrite(pd, result, isUndefined, flags); + } + + template <typename T> + Q_ALWAYS_INLINE bool doStore(T value, const QQmlPropertyData &pd, QQmlPropertyData::WriteFlags flags) const + { + void *o = &value; + return pd.writeProperty(targetObject(), o, flags); } -#undef QUICK_STORE +}; +Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const QV4::Value &result, + bool isUndefined, QQmlPropertyData::WriteFlags flags) +{ QQmlEngine *engine = context()->engine; QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); @@ -378,11 +422,12 @@ QVariant QQmlBinding::evaluate() bool isUndefined = false; QV4::Scope scope(ep->v4engine()); - QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); + QV4::ScopedCallData callData(scope); + QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope); ep->dereferenceScarceResources(); - return scope.engine->toVariant(result, qMetaTypeId<QList<QObject*> >()); + return scope.engine->toVariant(scope.result, qMetaTypeId<QList<QObject*> >()); } QString QQmlBinding::expressionIdentifier() @@ -409,11 +454,18 @@ void QQmlBinding::refresh() update(); } -void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) +void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) { setEnabledFlag(e); setNotifyOnValueChanged(e); + m_nextBinding.setFlag2(); // Always use accessors, only not when: + if (auto interceptorMetaObject = QQmlInterceptorMetaObject::get(targetObject())) { + int coreIndex = getPropertyCoreIndex(); + if (coreIndex == -1 || interceptorMetaObject->intercepts(coreIndex)) + m_nextBinding.clearFlag2(); + } + if (e) update(flags); } @@ -509,4 +561,39 @@ QQmlPropertyData QQmlBinding::getPropertyData() const return d; } +Q_ALWAYS_INLINE int QQmlBinding::getPropertyCoreIndex() const +{ + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(m_targetIndex, &coreIndex); + if (valueTypeIndex != -1) { + return -1; + } else { + return coreIndex; + } +} + +QQmlBinding *QQmlBinding::newBinding(const QQmlPropertyData *property) +{ + const int type = (property && property->isFullyResolved()) ? property->propType : QMetaType::UnknownType; + + if (type == qMetaTypeId<QQmlBinding *>()) { + return new QQmlBindingBinding; + } + + switch (type) { + case QMetaType::Bool: + return new GenericBinding<QMetaType::Bool>; + case QMetaType::Int: + return new GenericBinding<QMetaType::Int>; + case QMetaType::Double: + return new GenericBinding<QMetaType::Double>; + case QMetaType::Float: + return new GenericBinding<QMetaType::Float>; + case QMetaType::QString: + return new GenericBinding<QMetaType::QString>; + default: + return new GenericBinding<QMetaType::UnknownType>; + } +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 7814b9dee8..0c797d7df2 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -61,7 +61,6 @@ #include <QtCore/QObject> #include <QtCore/QMetaProperty> -#include <private/qpointervaluepair_p.h> #include <private/qqmlabstractbinding_p.h> #include <private/qqmljavascriptexpression_p.h> @@ -73,12 +72,12 @@ class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlJavaScriptExpression, { friend class QQmlAbstractBinding; public: - QQmlBinding(const QString &, QObject *, QQmlContext *); - QQmlBinding(const QQmlScriptString &, QObject *, QQmlContext *); - QQmlBinding(const QString &, QObject *, QQmlContextData *); - QQmlBinding(const QString &, QObject *, QQmlContextData *, - const QString &url, quint16 lineNumber, quint16 columnNumber); - QQmlBinding(const QV4::Value &, QObject *, QQmlContextData *); + static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContext *); + static QQmlBinding *create(const QQmlPropertyData *, const QQmlScriptString &, QObject *, QQmlContext *); + static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContextData *); + static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContextData *, + const QString &url, quint16 lineNumber, quint16 columnNumber); + static QQmlBinding *create(const QQmlPropertyData *, const QV4::Value &, QObject *, QQmlContextData *); ~QQmlBinding(); void setTarget(const QQmlProperty &); @@ -86,13 +85,11 @@ public: void setNotifyOnValueChanged(bool); - // Inherited from QQmlJavaScriptExpression - virtual void refresh(); + void refresh() Q_DECL_OVERRIDE; - // Inherited from QQmlAbstractBinding - virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding); - virtual QString expression() const; - void update(QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding); + void setEnabled(bool, QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding) Q_DECL_OVERRIDE; + QString expression() const Q_DECL_OVERRIDE; + void update(QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding); typedef int Identifier; enum { @@ -101,20 +98,28 @@ public: QVariant evaluate(); - virtual QString expressionIdentifier(); - virtual void expressionChanged(); + QString expressionIdentifier() Q_DECL_OVERRIDE; + void expressionChanged() Q_DECL_OVERRIDE; + +protected: + virtual void doUpdate(QQmlBinding *binding, const DeleteWatcher &watcher, + QQmlPropertyData::WriteFlags flags, QV4::Scope &scope, + const QV4::ScopedFunctionObject &f) = 0; + + QQmlPropertyData getPropertyData() const; + int getPropertyCoreIndex() const; + int getPropertyType() const; + + bool slowWrite(const QQmlPropertyData &core, const QV4::Value &result, bool isUndefined, + QQmlPropertyData::WriteFlags flags); private: inline bool updatingFlag() const; inline void setUpdatingFlag(bool); inline bool enabledFlag() const; inline void setEnabledFlag(bool); - QQmlPropertyData getPropertyData() const; - - bool write(const QQmlPropertyData &core, - const QV4::Value &result, bool isUndefined, - QQmlPropertyPrivate::WriteFlags flags); + static QQmlBinding *newBinding(const QQmlPropertyData *property); }; bool QQmlBinding::updatingFlag() const diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index c6a7cde240..ef837183db 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -51,14 +51,12 @@ #include <private/qqmlprofiler_p.h> #include <private/qqmldebugconnector_p.h> #include <private/qqmldebugserviceinterfaces_p.h> -#include <private/qqmlcompiler_p.h> #include "qqmlinfo.h" #include <private/qjsvalue_p.h> #include <private/qv4value_p.h> #include <private/qv4qobjectwrapper_p.h> -#include <QtCore/qstringbuilder.h> #include <QtCore/qdebug.h> @@ -83,9 +81,7 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, // 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('('); + function += QLatin1String("(function ") + handlerName + QLatin1Char('('); if (parameterString.isEmpty()) { QString error; @@ -101,10 +97,7 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, } else function += parameterString; - function += QStringLiteral(") { "); - function += expression; - function += QStringLiteral(" })"); - + function += QLatin1String(") { ") + expression + QLatin1String(" })"); m_function.set(v4, evalFunction(context(), scopeObject(), function, fileName, line)); if (m_function.isNullOrUndefined()) @@ -213,10 +206,10 @@ void QQmlBoundSignalExpression::evaluate(void **a) ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. - QVarLengthArray<int, 9> dummy; + QQmlMetaObject::ArgTypeStorage storage; //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 *argsTypes = QQmlMetaObject(m_target).methodParameterTypes(methodIndex, &storage, 0); int argCount = argsTypes ? *argsTypes : 0; QV4::ScopedCallData callData(scope, argCount); @@ -244,7 +237,7 @@ void QQmlBoundSignalExpression::evaluate(void **a) } } - QQmlJavaScriptExpression::evaluate(callData, 0); + QQmlJavaScriptExpression::evaluate(callData, 0, scope); ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. } @@ -266,7 +259,7 @@ void QQmlBoundSignalExpression::evaluate(const QList<QVariant> &args) callData->args[ii] = scope.engine->fromVariant(args[ii]); } - QQmlJavaScriptExpression::evaluate(callData, 0); + QQmlJavaScriptExpression::evaluate(callData, 0, scope); ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. } diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp deleted file mode 100644 index 28d599a593..0000000000 --- a/src/qml/qml/qqmlcompileddata.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlcompiler_p.h" -#include "qqmlengine.h" -#include "qqmlcomponent.h" -#include "qqmlcomponent_p.h" -#include "qqmlcontext.h" -#include "qqmlcontext_p.h" -#include "qqmlpropertymap.h" -#ifdef QML_THREADED_VME_INTERPRETER -#include "qqmlvme_p.h" -#endif - -#include <QtCore/qdebug.h> - -#include <private/qobject_p.h> - -QT_BEGIN_NAMESPACE - -QQmlCompiledData::QQmlCompiledData(QQmlEngine *engine) -: engine(engine), importCache(0), metaTypeId(-1), listMetaTypeId(-1), isRegisteredWithEngine(false), - rootPropertyCache(0), totalBindingsCount(0), totalParserStatusCount(0) -{ - Q_ASSERT(engine); -} - -void QQmlCompiledData::destroy() -{ - if (engine && hasEngine()) - QQmlEnginePrivate::deleteInEngineThread(engine, this); - else - delete this; -} - -QQmlCompiledData::~QQmlCompiledData() -{ - if (isRegisteredWithEngine) - QQmlEnginePrivate::get(engine)->unregisterInternalCompositeType(this); - - clear(); - - for (QHash<int, TypeReference*>::Iterator resolvedType = resolvedTypes.begin(), end = resolvedTypes.end(); - resolvedType != end; ++resolvedType) { - if ((*resolvedType)->component) - (*resolvedType)->component->release(); - if ((*resolvedType)->typePropertyCache) - (*resolvedType)->typePropertyCache->release(); - } - qDeleteAll(resolvedTypes); - resolvedTypes.clear(); - - for (int ii = 0; ii < propertyCaches.count(); ++ii) - if (propertyCaches.at(ii)) - propertyCaches.at(ii)->release(); - - for (int ii = 0; ii < scripts.count(); ++ii) - scripts.at(ii)->release(); - - if (importCache) - importCache->release(); - - if (rootPropertyCache) - rootPropertyCache->release(); -} - -void QQmlCompiledData::clear() -{ -} - -/*! -Returns the property cache, if one alread exists. The cache is not referenced. -*/ -QQmlPropertyCache *QQmlCompiledData::TypeReference::propertyCache() const -{ - if (type) - return typePropertyCache; - else - return component->rootPropertyCache; -} - -/*! -Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. -*/ -QQmlPropertyCache *QQmlCompiledData::TypeReference::createPropertyCache(QQmlEngine *engine) -{ - if (typePropertyCache) { - return typePropertyCache; - } else if (type) { - typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type->metaObject()); - typePropertyCache->addref(); - return typePropertyCache; - } else { - return component->rootPropertyCache; - } -} - -template <typename T> -bool qtTypeInherits(const QMetaObject *mo) { - while (mo) { - if (mo == &T::staticMetaObject) - return true; - mo = mo->superClass(); - } - return false; -} - -void QQmlCompiledData::TypeReference::doDynamicTypeCheck() -{ - const QMetaObject *mo = 0; - if (typePropertyCache) - mo = typePropertyCache->firstCppMetaObject(); - else if (type) - mo = type->metaObject(); - else if (component) - mo = component->rootPropertyCache->firstCppMetaObject(); - isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo); -} - -void QQmlCompiledData::initialize(QQmlEngine *engine) -{ - Q_ASSERT(!hasEngine()); - QQmlCleanup::addToEngine(engine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - if (compilationUnit && !compilationUnit->engine) - compilationUnit->linkToEngine(v4); -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h deleted file mode 100644 index 1c43a85de3..0000000000 --- a/src/qml/qml/qqmlcompiler_p.h +++ /dev/null @@ -1,160 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLCOMPILER_P_H -#define QQMLCOMPILER_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 "qqml.h" -#include "qqmlerror.h" -#include "qqmlengine_p.h" -#include <private/qbitfield_p.h> -#include "qqmlpropertycache_p.h" -#include "qqmltypenamecache_p.h" -#include "qqmltypeloader_p.h" -#include "private/qv4identifier_p.h" -#include <private/qqmljsastfwd_p.h> -#include "qqmlcustomparser_p.h" - -#include <QtCore/qbytearray.h> -#include <QtCore/qset.h> -#include <QtCore/QCoreApplication> - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace CompiledData { -struct CompilationUnit; -struct Unit; -} -} - -class QQmlEngine; -class QQmlComponent; -class QQmlContext; -class QQmlContextData; - -// ### Merge with QV4::CompiledData::CompilationUnit -class Q_AUTOTEST_EXPORT QQmlCompiledData : public QQmlRefCount, public QQmlCleanup -{ -public: - QQmlCompiledData(QQmlEngine *engine); - virtual ~QQmlCompiledData(); - - QQmlEngine *engine; - - QString fileName() const { return compilationUnit->fileName(); } - QUrl url() const { return compilationUnit->url(); } - QQmlTypeNameCache *importCache; - - int metaTypeId; - int listMetaTypeId; - bool isRegisteredWithEngine; - - struct TypeReference - { - TypeReference() - : type(0), typePropertyCache(0), component(0) - , majorVersion(0) - , minorVersion(0) - , isFullyDynamicType(false) - {} - - QQmlType *type; - QQmlPropertyCache *typePropertyCache; - QQmlCompiledData *component; - - int majorVersion; - int minorVersion; - // Types such as QQmlPropertyMap can add properties dynamically at run-time and - // therefore cannot have a property cache installed when instantiated. - bool isFullyDynamicType; - - QQmlPropertyCache *propertyCache() const; - QQmlPropertyCache *createPropertyCache(QQmlEngine *); - - void doDynamicTypeCheck(); - }; - // map from name index - QHash<int, TypeReference*> resolvedTypes; - - QQmlPropertyCache *rootPropertyCache; - QVector<QByteArray> metaObjects; - QVector<QQmlPropertyCache *> propertyCaches; - QList<QQmlScriptData *> scripts; - - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; - // index in first hash is component index, hash inside maps from object index in that scope to integer id - QHash<int, QHash<int, int> > objectIndexToIdPerComponent; - QHash<int, int> objectIndexToIdForRoot; - // hash key is object index, value is indicies of bindings covered by custom parser - QHash<int, QBitArray> customParserBindings; - QHash<int, QBitArray> deferredBindingsPerObject; // index is object index - int totalBindingsCount; // Number of bindings used in this type - int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses - int totalObjectCount; // Number of objects explicitly instantiated - - bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } - bool isCompositeType() const { return !metaObjects.at(compilationUnit->data->indexOfRootObject).isEmpty(); } - - bool isInitialized() const { return hasEngine(); } - void initialize(QQmlEngine *); - -protected: - virtual void destroy(); // From QQmlRefCount - virtual void clear(); // From QQmlCleanup - -private: - QQmlCompiledData(const QQmlCompiledData &other); - QQmlCompiledData &operator=(const QQmlCompiledData &other); -}; - -QT_END_NAMESPACE - -#endif // QQMLCOMPILER_P_H diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index c64851cac5..9c7b4fe1c0 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -41,7 +41,6 @@ #include "qqmlcomponent_p.h" #include "qqmlcomponentattached_p.h" -#include "qqmlcompiler_p.h" #include "qqmlcontext_p.h" #include "qqmlengine_p.h" #include "qqmlvme_p.h" @@ -335,14 +334,11 @@ void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p) void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data) { url = data->finalUrl(); - QQmlCompiledData *c = data->compiledData(); + compilationUnit = data->compilationUnit(); - if (!c) { + if (!compilationUnit) { Q_ASSERT(data->isError()); state.errors = data->errors(); - } else { - cc = c; - cc->addref(); } data->release(); @@ -356,10 +352,7 @@ void QQmlComponentPrivate::clear() typeData = 0; } - if (cc) { - cc->release(); - cc = 0; - } + compilationUnit = nullptr; } /*! @@ -393,8 +386,6 @@ QQmlComponent::~QQmlComponent() d->typeData->unregisterCallback(d); d->typeData->release(); } - if (d->cc) - d->cc->release(); } /*! @@ -422,7 +413,7 @@ QQmlComponent::Status QQmlComponent::status() const return Loading; else if (!d->state.errors.isEmpty()) return Error; - else if (d->engine && d->cc) + else if (d->engine && d->compilationUnit) return Ready; else return Null; @@ -512,11 +503,8 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent) \sa loadUrl() */ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *parent) -: QObject(*(new QQmlComponentPrivate), parent) + : QQmlComponent(engine, url, QQmlComponent::PreferSynchronous, parent) { - Q_D(QQmlComponent); - d->engine = engine; - d->loadUrl(url); } /*! @@ -531,10 +519,9 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *paren */ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMode mode, QObject *parent) -: QObject(*(new QQmlComponentPrivate), parent) + : QQmlComponent(engine, parent) { Q_D(QQmlComponent); - d->engine = engine; d->loadUrl(url, mode); } @@ -546,11 +533,8 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMod */ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, QObject *parent) -: QObject(*(new QQmlComponentPrivate), parent) + : QQmlComponent(engine, fileName, QQmlComponent::PreferSynchronous, parent) { - Q_D(QQmlComponent); - d->engine = engine; - d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName))); } /*! @@ -562,25 +546,22 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, */ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, CompilationMode mode, QObject *parent) -: QObject(*(new QQmlComponentPrivate), parent) + : QQmlComponent(engine, parent) { Q_D(QQmlComponent); - d->engine = engine; d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)), mode); } /*! \internal */ -QQmlComponent::QQmlComponent(QQmlEngine *engine, QQmlCompiledData *cc, int start, QObject *parent) - : QObject(*(new QQmlComponentPrivate), parent) +QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *compilationUnit, int start, QObject *parent) + : QQmlComponent(engine, parent) { Q_D(QQmlComponent); - d->engine = engine; - d->cc = cc; - cc->addref(); + d->compilationUnit = compilationUnit; d->start = start; - d->url = cc->url(); + d->url = compilationUnit->url(); d->progress = 1.0; } @@ -873,7 +854,7 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) enginePriv->referenceScarceResources(); QObject *rv = 0; - state.creator.reset(new QQmlObjectCreator(context, cc, creationContext)); + state.creator.reset(new QQmlObjectCreator(context, compilationUnit, creationContext)); rv = state.creator->create(start); if (!rv) state.errors = state.creator->errors; @@ -893,11 +874,13 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) depthIncreased = false; } - QQmlEngineDebugService *service = QQmlDebugConnector::service<QQmlEngineDebugService>(); - if (service && rv) { - if (!context->isInternal) - context->asQQmlContextPrivate()->instances.append(rv); - service->objectCreated(engine, rv); + if (rv) { + if (QQmlEngineDebugService *service = + QQmlDebugConnector::service<QQmlEngineDebugService>()) { + if (!context->isInternal) + context->asQQmlContextPrivate()->instances.append(rv); + service->objectCreated(engine, rv); + } } return rv; @@ -914,7 +897,7 @@ void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv, Q_ASSERT(ddata->deferredData); QQmlData::DeferredData *deferredData = ddata->deferredData; QQmlContextData *creationContext = 0; - state->creator.reset(new QQmlObjectCreator(deferredData->context->parent, deferredData->compiledData, creationContext)); + state->creator.reset(new QQmlObjectCreator(deferredData->context->parent, deferredData->compilationUnit, creationContext)); if (!state->creator->populateDeferredProperties(object)) state->errors << state->creator->errors; } @@ -1058,9 +1041,9 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(d->engine); - p->compiledData = d->cc; - p->compiledData->addref(); - p->creator.reset(new QQmlObjectCreator(contextData, d->cc, d->creationContext, p.data())); + p->compilationUnit = d->compilationUnit; + p->enginePriv = enginePriv; + p->creator.reset(new QQmlObjectCreator(contextData, d->compilationUnit, d->creationContext, p.data())); p->subComponentToCreate = d->start; enginePriv->incubate(incubator, forContextData); @@ -1193,7 +1176,7 @@ static void QQmlComponent_setQmlParent(QObject *me, QObject *parent) */ -static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v) +void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v) { QV4::Scope scope(engine); QV4::ScopedObject object(scope); @@ -1282,7 +1265,7 @@ void QQmlComponent::createObject(QQmlV4Function *args) if (!valuemap->isUndefined()) { QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext()); - setInitialProperties(v4, qmlContext, object, valuemap); + QQmlComponentPrivate::setInitialProperties(v4, qmlContext, object, valuemap); } d->completeCreate(); @@ -1515,7 +1498,7 @@ void QV4::QmlIncubatorObject::setInitialState(QObject *o) QV4::Scope scope(v4); QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(v4, o)); QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext); - setInitialProperties(v4, qmlCtxt, obj, d()->valuemap); + QQmlComponentPrivate::setInitialProperties(v4, qmlCtxt, obj, d()->valuemap); } } @@ -1546,7 +1529,7 @@ void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s) QV4::ScopedCallData callData(scope, 1); callData->thisObject = this; callData->args[0] = QV4::Primitive::fromUInt32(s); - f->call(callData); + f->call(scope, callData); if (scope.hasException()) { QQmlError error = scope.engine->catchExceptionAsQmlError(); QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error); diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h index aefbf20aff..ca60f01eb5 100644 --- a/src/qml/qml/qqmlcomponent.h +++ b/src/qml/qml/qqmlcomponent.h @@ -55,10 +55,15 @@ class QQmlEngine; class QQmlComponent; class QQmlIncubator; class QQmlV4Function; -class QQmlCompiledData; class QQmlComponentPrivate; class QQmlComponentAttached; +namespace QV4 { +namespace CompiledData { +struct CompilationUnit; +} +} + class Q_QML_EXPORT QQmlComponent : public QObject { Q_OBJECT @@ -122,7 +127,7 @@ protected: Q_INVOKABLE void incubateObject(QQmlV4Function *); private: - QQmlComponent(QQmlEngine *, QQmlCompiledData *, int, QObject *parent); + QQmlComponent(QQmlEngine *, QV4::CompiledData::CompilationUnit *compilationUnit, int, QObject *parent); Q_DISABLE_COPY(QQmlComponent) friend class QQmlTypeData; diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index 039b267433..3a84e724da 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -71,7 +71,6 @@ QT_BEGIN_NAMESPACE class QQmlComponent; class QQmlEngine; -class QQmlCompiledData; class QQmlComponentAttached; class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public QQmlTypeData::TypeDataCallback @@ -80,13 +79,14 @@ class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public public: QQmlComponentPrivate() - : typeData(0), progress(0.), start(-1), cc(0), engine(0), creationContext(0), depthIncreased(false) {} + : typeData(0), progress(0.), start(-1), engine(0), creationContext(0), depthIncreased(false) {} void loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous); QObject *beginCreate(QQmlContextData *); void completeCreate(); void initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate); + static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v); QQmlTypeData *typeData; virtual void typeDataReady(QQmlTypeData *); @@ -98,7 +98,7 @@ public: qreal progress; int start; - QQmlCompiledData *cc; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; struct ConstructionState { ConstructionState() diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index 65a337f4e5..6621f48e20 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -310,7 +310,7 @@ void QQmlContext::setContextProperty(const QString &name, const QVariant &value) } } - QV4::IdentifierHash<int> &properties = data->propertyNames(); + QV4::IdentifierHash<int> &properties = data->detachedPropertyNames(); int idx = properties.value(name); if (idx == -1) { properties.add(name, data->idValueCount + d->propertyValues.count()); @@ -346,7 +346,7 @@ void QQmlContext::setContextProperty(const QString &name, QObject *value) return; } - QV4::IdentifierHash<int> &properties = data->propertyNames(); + QV4::IdentifierHash<int> &properties = data->detachedPropertyNames(); int idx = properties.value(name); if (idx == -1) { @@ -516,20 +516,15 @@ QObject *QQmlContextPrivate::context_at(QQmlListProperty<QObject> *prop, int ind QQmlContextData::QQmlContextData() -: parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false), - isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false), - publicContext(0), activeVMEData(0), - contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0), - expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0), - componentAttached(0) + : QQmlContextData(nullptr) { } QQmlContextData::QQmlContextData(QQmlContext *ctxt) : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false), isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false), - publicContext(ctxt), activeVMEData(0), - contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0), + publicContext(ctxt), activeVMEData(0), componentObjectIndex(-1), + contextObject(0), childContexts(0), nextChild(0), prevChild(0), expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0), componentAttached(0) { @@ -633,9 +628,6 @@ void QQmlContextData::destroy() } contextGuards = 0; - if (imports) - imports->release(); - delete [] idValues; if (isInternal) @@ -765,15 +757,6 @@ void QQmlContextData::setIdProperty(int idx, QObject *obj) idValues[idx].context = this; } -void QQmlContextData::setIdPropertyData(const QHash<int, int> &data) -{ - Q_ASSERT(objectIndexToId.isEmpty()); - objectIndexToId = data; - Q_ASSERT(propertyNameCache.isEmpty()); - idValueCount = data.count(); - idValues = new ContextGuard[idValueCount]; -} - QString QQmlContextData::findObjectId(const QObject *obj) const { const QV4::IdentifierHash<int> &properties = propertyNames(); @@ -809,21 +792,33 @@ QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate() return QQmlContextPrivate::get(asQQmlContext()); } -QV4::IdentifierHash<int> &QQmlContextData::propertyNames() const +void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, int subComponentIndex) +{ + typeCompilationUnit = unit; + componentObjectIndex = subComponentIndex == -1 ? typeCompilationUnit->data->indexOfRootObject : subComponentIndex; + Q_ASSERT(!idValues); + idValueCount = typeCompilationUnit->data->objectAt(componentObjectIndex)->nNamedObjectsInComponent; + idValues = new ContextGuard[idValueCount]; +} + +const QV4::IdentifierHash<int> &QQmlContextData::propertyNames() const { if (propertyNameCache.isEmpty()) { - propertyNameCache = QV4::IdentifierHash<int>(QV8Engine::getV4(engine->handle())); - 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); - propertyNameCache.add(name, it.value()); - } - objectIndexToId.clear(); + if (typeCompilationUnit) + propertyNameCache = typeCompilationUnit->namedObjectsPerComponent(componentObjectIndex); + else + propertyNameCache = QV4::IdentifierHash<int>(QV8Engine::getV4(engine)); } return propertyNameCache; } +QV4::IdentifierHash<int> &QQmlContextData::detachedPropertyNames() +{ + propertyNames(); + propertyNameCache.detach(); + return propertyNameCache; +} + QUrl QQmlContextData::url() const { if (typeCompilationUnit) diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index 48d596418d..62cd3d4877 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -151,9 +151,15 @@ public: // Compilation unit for contexts that belong to a compiled type. QQmlRefPointer<QV4::CompiledData::CompilationUnit> typeCompilationUnit; - mutable QHash<int, int> objectIndexToId; + // object index in CompiledData::Unit to component that created this context + int componentObjectIndex; + + void initFromTypeCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, int subComponentIndex); + + // flag indicates whether the context owns the cache (after mutation) or not. mutable QV4::IdentifierHash<int> propertyNameCache; - QV4::IdentifierHash<int> &propertyNames() const; + const QV4::IdentifierHash<int> &propertyNames() const; + QV4::IdentifierHash<int> &detachedPropertyNames(); // Context object QObject *contextObject; @@ -168,7 +174,7 @@ public: QString urlString() const; // List of imports that apply to this context - QQmlTypeNameCache *imports; + QQmlRefPointer<QQmlTypeNameCache> imports; // My children QQmlContextData *childContexts; @@ -201,7 +207,6 @@ public: ContextGuard *idValues; int idValueCount; void setIdProperty(int, QObject *); - void setIdPropertyData(const QHash<int, int> &); // Linked contexts. this owns linkedContext. QQmlContextData *linkedContext; diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index 134002cf33..85c91a592a 100644 --- a/src/qml/qml/qqmlcustomparser.cpp +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -39,7 +39,6 @@ #include "qqmlcustomparser_p.h" -#include "qqmlcompiler_p.h" #include <private/qqmltypecompiler_p.h> #include <QtCore/qdebug.h> @@ -101,11 +100,7 @@ void QQmlCustomParser::clearErrors() */ void QQmlCustomParser::error(const QV4::CompiledData::Location &location, const QString &description) { - QQmlError error; - error.setLine(location.line); - error.setColumn(location.column); - error.setDescription(description); - exceptions << error; + exceptions << QQmlCompileError(location, description); } struct StaticQtMetaObject : public QObject @@ -166,11 +161,13 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const */ const QMetaObject *QQmlCustomParser::resolveType(const QString& name) const { + if (!imports.isT1()) + return nullptr; QQmlType *qmltype = 0; - if (!validator->imports().resolveType(name, &qmltype, 0, 0, 0)) - return 0; + if (!imports.asT1()->resolveType(name, &qmltype, 0, 0, 0)) + return nullptr; if (!qmltype) - return 0; + return nullptr; return qmltype->metaObject(); } diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h index d8e25c55fd..5eb409990d 100644 --- a/src/qml/qml/qqmlcustomparser_p.h +++ b/src/qml/qml/qqmlcustomparser_p.h @@ -54,13 +54,13 @@ #include "qqmlmetatype_p.h" #include "qqmlerror.h" #include "qqmlbinding_p.h" +#include <private/qqmltypecompiler_p.h> #include <QtCore/qbytearray.h> #include <QtCore/qxmlstream.h> QT_BEGIN_NAMESPACE -class QQmlCompiledData; class QQmlPropertyValidator; class QQmlEnginePrivate; @@ -82,9 +82,9 @@ public: Flags flags() const { return m_flags; } virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &) = 0; - virtual void applyBindings(QObject *, QQmlCompiledData *, const QList<const QV4::CompiledData::Binding *> &) = 0; + virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &) = 0; - QList<QQmlError> errors() const { return exceptions; } + QVector<QQmlCompileError> errors() const { return exceptions; } protected: void error(const QV4::CompiledData::Binding *binding, const QString& description) @@ -98,7 +98,7 @@ protected: const QMetaObject *resolveType(const QString&) const; private: - QList<QQmlError> exceptions; + QVector<QQmlCompileError> exceptions; QQmlEnginePrivate *engine; const QQmlPropertyValidator *validator; Flags m_flags; diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index ad2456a68d..a9f2acdb06 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -63,7 +63,6 @@ QT_BEGIN_NAMESPACE template <class Key, class T> class QHash; class QQmlEngine; class QQmlGuardImpl; -class QQmlCompiledData; class QQmlAbstractBinding; class QQmlBoundSignal; class QQmlContext; @@ -72,6 +71,13 @@ class QQmlContextData; class QQmlNotifier; class QQmlDataExtended; class QQmlNotifierEndpoint; + +namespace QV4 { +namespace CompiledData { +struct CompilationUnit; +} +} + // This class is structured in such a way, that simply zero'ing it is the // default state for elemental object allocations. This is crucial in the // workings of the QQmlInstruction::CreateSimpleObject instruction. @@ -179,10 +185,10 @@ public: struct DeferredData { unsigned int deferredIdx; - QQmlCompiledData *compiledData;//Not always the same as the other compiledData + QV4::CompiledData::CompilationUnit *compilationUnit;//Not always the same as the other compilation unit QQmlContextData *context;//Could be either context or outerContext }; - QQmlCompiledData *compiledData; + QV4::CompiledData::CompilationUnit *compilationUnit; DeferredData *deferredData; QV4::WeakValue jsWrapper; @@ -223,7 +229,7 @@ public: static inline void flushPendingBinding(QObject *, int coreIndex); - static void ensurePropertyCache(QJSEngine *engine, QObject *object); + static QQmlPropertyCache *ensurePropertyCache(QJSEngine *engine, QObject *object); private: // For attachedProperties diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp new file mode 100644 index 0000000000..d10a8c7718 --- /dev/null +++ b/src/qml/qml/qqmldelayedcallqueue.cpp @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldelayedcallqueue_p.h" +#include <private/qv8engine_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmljavascriptexpression_p.h> +#include <private/qv4value_p.h> +#include <private/qv4qobjectwrapper_p.h> +#include <private/qqmlcontextwrapper_p.h> + +#include <QQmlError> + +QT_BEGIN_NAMESPACE + +// +// struct QQmlDelayedCallQueue::DelayedFunctionCall +// + +void QQmlDelayedCallQueue::DelayedFunctionCall::execute(QV4::ExecutionEngine *engine) const +{ + if (!m_guarded || + (!m_objectGuard.isNull() && + !QQmlData::wasDeleted(m_objectGuard) && + QQmlData::get(m_objectGuard) && + !QQmlData::get(m_objectGuard)->isQueuedForDeletion)) { + + QV4::Scope scope(engine); + + QV4::ArrayObject *array = m_args.as<QV4::ArrayObject>(); + const int argCount = array ? array->getLength() : 0; + QV4::ScopedCallData callData(scope, argCount); + callData->thisObject = QV4::Encode::undefined(); + + for (int i = 0; i < argCount; i++) { + callData->args[i] = array->getIndexed(i); + } + + const QV4::FunctionObject *callback = m_function.as<QV4::FunctionObject>(); + Q_ASSERT(callback); + callback->call(scope, callData); + + if (scope.engine->hasException) { + QQmlError error = scope.engine->catchExceptionAsQmlError(); + error.setDescription(error.description() + QLatin1String(" (exception occurred during delayed function evaluation)")); + QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error); + } + } +} + +// +// class QQmlDelayedCallQueue +// + +QQmlDelayedCallQueue::QQmlDelayedCallQueue() + : QObject(0), m_engine(0), m_callbackOutstanding(false) +{ +} + +QQmlDelayedCallQueue::~QQmlDelayedCallQueue() +{ +} + +void QQmlDelayedCallQueue::init(QV4::ExecutionEngine* engine) +{ + m_engine = engine; + + const QMetaObject &metaObject = QQmlDelayedCallQueue::staticMetaObject; + int methodIndex = metaObject.indexOfSlot("ticked()"); + m_tickedMethod = metaObject.method(methodIndex); +} + +QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(QV4::CallContext *ctx) +{ + const QV4::CallData *callData = ctx->d()->callData; + + if (callData->argc == 0) + V4THROW_ERROR("Qt.callLater: no arguments given"); + + const QV4::FunctionObject *func = callData->args[0].as<QV4::FunctionObject>(); + + if (!func) + V4THROW_ERROR("Qt.callLater: first argument not a function or signal"); + + QPair<QObject *, int> functionData = QV4::QObjectMethod::extractQtMethod(func); + + QVector<DelayedFunctionCall>::Iterator iter; + if (functionData.second != -1) { + // This is a QObject function wrapper + iter = m_delayedFunctionCalls.begin(); + while (iter != m_delayedFunctionCalls.end()) { + DelayedFunctionCall& dfc = *iter; + QPair<QObject *, int> storedFunctionData = QV4::QObjectMethod::extractQtMethod(dfc.m_function.as<QV4::FunctionObject>()); + if (storedFunctionData == functionData) { + break; // Already stored! + } + ++iter; + } + } else { + // This is a JavaScript function (dynamic slot on VMEMO) + iter = m_delayedFunctionCalls.begin(); + while (iter != m_delayedFunctionCalls.end()) { + DelayedFunctionCall& dfc = *iter; + if (callData->argument(0) == dfc.m_function.value()) { + break; // Already stored! + } + ++iter; + } + } + + const bool functionAlreadyStored = (iter != m_delayedFunctionCalls.end()); + if (functionAlreadyStored) { + DelayedFunctionCall dfc = *iter; + m_delayedFunctionCalls.erase(iter); + m_delayedFunctionCalls.append(dfc); + } else { + m_delayedFunctionCalls.append(QV4::PersistentValue(m_engine, callData->argument(0))); + } + + DelayedFunctionCall& dfc = m_delayedFunctionCalls.last(); + if (dfc.m_objectGuard.isNull()) { + if (functionData.second != -1) { + // if it's a qobject function wrapper, guard against qobject deletion + dfc.m_objectGuard = QQmlGuard<QObject>(functionData.first); + dfc.m_guarded = true; + } else if (func->scope()->type == QV4::Heap::ExecutionContext::Type_QmlContext) { + QV4::QmlContext::Data *g = static_cast<QV4::QmlContext::Data *>(func->scope()); + Q_ASSERT(g->qml->scopeObject); + dfc.m_objectGuard = QQmlGuard<QObject>(g->qml->scopeObject); + dfc.m_guarded = true; + } + } + storeAnyArguments(dfc, callData, 1, m_engine); + + if (!m_callbackOutstanding) { + m_tickedMethod.invoke(this, Qt::QueuedConnection); + m_callbackOutstanding = true; + } + return QV4::Encode::undefined(); +} + +void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, const QV4::CallData *callData, int offset, QV4::ExecutionEngine *engine) +{ + const int length = callData->argc - offset; + if (length == 0) { + dfc.m_args.clear(); + return; + } + QV4::Scope scope(engine); + QV4::ScopedArrayObject array(scope, engine->newArrayObject(length)); + int i = 0; + for (int j = offset; j < callData->argc; ++i, ++j) { + array->putIndexed(i, callData->args[j]); + } + dfc.m_args.set(engine, array); +} + +void QQmlDelayedCallQueue::executeAllExpired_Later() +{ + // Make a local copy of the list and clear m_delayedFunctionCalls + // This ensures correct behavior in the case of recursive calls to Qt.callLater() + QVector<DelayedFunctionCall> delayedCalls = m_delayedFunctionCalls; + m_delayedFunctionCalls.clear(); + + QVector<DelayedFunctionCall>::Iterator iter = delayedCalls.begin(); + while (iter != delayedCalls.end()) { + DelayedFunctionCall& dfc = *iter; + dfc.execute(m_engine); + ++iter; + } +} + +void QQmlDelayedCallQueue::ticked() +{ + m_callbackOutstanding = false; + executeAllExpired_Later(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmldelayedcallqueue_p.h b/src/qml/qml/qqmldelayedcallqueue_p.h new file mode 100644 index 0000000000..ef899170a2 --- /dev/null +++ b/src/qml/qml/qqmldelayedcallqueue_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDELAYEDCALLQUEUE_P_H +#define QQMLDELAYEDCALLQUEUE_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 <QtCore/qobject.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qmetatype.h> +#include <private/qqmlguard_p.h> +#include <private/qv4context_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; +class QQmlDelayedCallQueue : public QObject +{ + Q_OBJECT +public: + QQmlDelayedCallQueue(); + ~QQmlDelayedCallQueue(); + + void init(QV4::ExecutionEngine *); + + QV4::ReturnedValue addUniquelyAndExecuteLater(QV4::CallContext *ctx); + +public Q_SLOTS: + void ticked(); + +private: + struct DelayedFunctionCall + { + DelayedFunctionCall() {} + DelayedFunctionCall(QV4::PersistentValue function) + : m_function(function), m_guarded(false) { } + + void execute(QV4::ExecutionEngine *engine) const; + + QV4::PersistentValue m_function; + QV4::PersistentValue m_args; + QQmlGuard<QObject> m_objectGuard; + bool m_guarded; + }; + + void storeAnyArguments(DelayedFunctionCall& dfc, const QV4::CallData *callData, int offset, QV4::ExecutionEngine *engine); + void executeAllExpired_Later(); + + QV4::ExecutionEngine *m_engine; + QVector<DelayedFunctionCall> m_delayedFunctionCalls; + QMetaMethod m_tickedMethod; + bool m_callbackOutstanding; +}; + +QT_END_NAMESPACE + +#endif // QQMLDELAYEDCALLQUEUE_P_H diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 28f8bf4855..e4e7530af7 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -42,7 +42,6 @@ #include "qqmlcomponentattached_p.h" #include "qqmlcontext_p.h" -#include "qqmlcompiler_p.h" #include "qqml.h" #include "qqmlcontext.h" #include "qqmlexpression.h" @@ -53,35 +52,33 @@ #include "qqmlscriptstring.h" #include "qqmlglobal_p.h" #include "qqmlcomponent_p.h" -#include "qqmlnetworkaccessmanagerfactory.h" #include "qqmldirparser_p.h" #include "qqmlextensioninterface.h" #include "qqmllist_p.h" #include "qqmltypenamecache_p.h" #include "qqmlnotifier_p.h" -#include <private/qqmldebugconnector_p.h> #include "qqmlincubator.h" #include "qqmlabstracturlinterceptor.h" #include <private/qqmlboundsignal_p.h> - #include <QtCore/qstandardpaths.h> #include <QtCore/qsettings.h> - #include <QtCore/qmetaobject.h> -#include <QNetworkAccessManager> #include <QDebug> #include <QtCore/qcoreapplication.h> #include <QtCore/qdir.h> #include <QtCore/qmutex.h> #include <QtCore/qthread.h> #include <private/qthread_p.h> + +#ifndef QT_NO_NETWORK +#include "qqmlnetworkaccessmanagerfactory.h" +#include <QNetworkAccessManager> #include <QtNetwork/qnetworkconfigmanager.h> +#endif #include <private/qobject_p.h> #include <private/qmetaobject_p.h> - #include <private/qqmllocale_p.h> - #include <private/qqmlbind_p.h> #include <private/qqmlconnections_p.h> #include <private/qqmltimer_p.h> @@ -94,17 +91,15 @@ #include <private/qqmlinstantiator_p.h> #ifdef Q_OS_WIN // for %APPDATA% -#include <qt_windows.h> -# if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) +# include <qt_windows.h> +# ifndef Q_OS_WINRT # include <shlobj.h> # endif -#include <qlibrary.h> -#include <windows.h> - -#ifndef CSIDL_APPDATA -# define CSIDL_APPDATA 0x001a // <username>\Application Data -#endif -#endif +# include <qlibrary.h> +# ifndef CSIDL_APPDATA +# define CSIDL_APPDATA 0x001a // <username>\Application Data +# endif +#endif // Q_OS_WIN Q_DECLARE_METATYPE(QQmlProperty) @@ -182,6 +177,7 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int qmlRegisterType<QQmlComponent>(uri,versionMajor,versionMinor,"Component"); qmlRegisterType<QObject>(uri,versionMajor,versionMinor,"QtObject"); qmlRegisterType<QQmlBind>(uri, versionMajor, versionMinor,"Binding"); + qmlRegisterType<QQmlBind,8>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "Binding"); //Only available in >=2.8 qmlRegisterType<QQmlConnections,1>(uri, versionMajor, (versionMinor < 3 ? 3 : versionMinor), "Connections"); //Only available in >=2.3 qmlRegisterType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections"); qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer"); @@ -493,6 +489,10 @@ The following functions are also on the Qt object. from right to left. \endlist \row + \li \c application.font + \li This read-only property holds the default application font as + returned by \l QGuiApplication::font(). + \row \li \c application.arguments \li This is a string list of the arguments the executable was invoked with. \row @@ -531,6 +531,7 @@ The following functions are also on the Qt object. \li application.active \li application.state \li application.layoutDirection + \li application.font \endlist */ @@ -601,12 +602,17 @@ the same object as is returned from the Qt.include() call. QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) : propertyCapture(0), rootContext(0), - profiler(0), outputWarningsToMsgLog(true), +#ifndef QT_NO_QML_DEBUGGER + profiler(0), +#endif + outputWarningsToMsgLog(true), cleanup(0), erroredBindings(0), inProgressCreations(0), workerScriptEngine(0), activeObjectCreator(0), - networkAccessManager(0), networkAccessManagerFactory(0), urlInterceptor(0), - scarceResourcesRefCount(0), importDatabase(e), typeLoader(e), +#ifndef QT_NO_NETWORK + networkAccessManager(0), networkAccessManagerFactory(0), +#endif + urlInterceptor(0), scarceResourcesRefCount(0), importDatabase(e), typeLoader(e), uniqueId(1), incubatorCount(0), incubationController(0) { } @@ -614,7 +620,6 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) QQmlEnginePrivate::~QQmlEnginePrivate() { 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); @@ -635,7 +640,7 @@ QQmlEnginePrivate::~QQmlEnginePrivate() for (TypePropertyCacheIt iter = typePropertyCache.cbegin(), end = typePropertyCache.cend(); iter != end; ++iter) (*iter)->release(); - for (CompositeTypesIt iter = m_compositeTypes.cbegin(), end = m_compositeTypes.cend(); iter != end; ++iter) { + for (auto iter = m_compositeTypes.cbegin(), end = m_compositeTypes.cend(); iter != end; ++iter) { iter.value()->isRegisteredWithEngine = false; // since unregisterInternalCompositeType() will not be called in this @@ -643,12 +648,9 @@ QQmlEnginePrivate::~QQmlEnginePrivate() QMetaType::unregisterType(iter.value()->metaTypeId); QMetaType::unregisterType(iter.value()->listMetaTypeId); } +#ifndef QT_NO_QML_DEBUGGER delete profiler; -} - -void QQmlEnginePrivate::enableProfiler() -{ - profiler = new QQmlProfiler(); +#endif } void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) @@ -677,7 +679,7 @@ QQmlData::QQmlData() 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), + lineNumber(0), columnNumber(0), jsEngineId(0), compilationUnit(0), deferredData(0), propertyCache(0), guards(0), extendedData(0) { init(); @@ -845,8 +847,8 @@ void QQmlData::flushPendingBindingImpl(int coreIndex) b = b->nextBinding(); if (b && b->targetPropertyIndex() == coreIndex) - b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | - QQmlPropertyPrivate::DontRemoveBinding); + b->setEnabled(true, QQmlPropertyData::BypassInterceptor | + QQmlPropertyData::DontRemoveBinding); } bool QQmlEnginePrivate::baseModulesUninitialized = true; @@ -1066,7 +1068,17 @@ QQmlAbstractUrlInterceptor *QQmlEngine::urlInterceptor() const return d->urlInterceptor; } +void QQmlEnginePrivate::registerFinalizeCallback(QObject *obj, int index) +{ + if (activeObjectCreator) { + activeObjectCreator->finalizeCallbacks()->append(qMakePair(QPointer<QObject>(obj), index)); + } else { + void *args[] = { 0 }; + QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, args); + } +} +#ifndef QT_NO_NETWORK /*! Sets the \a factory to use for creating QNetworkAccessManager(s). @@ -1095,16 +1107,6 @@ QQmlNetworkAccessManagerFactory *QQmlEngine::networkAccessManagerFactory() const return d->networkAccessManagerFactory; } -void QQmlEnginePrivate::registerFinalizeCallback(QObject *obj, int index) -{ - if (activeObjectCreator) { - activeObjectCreator->finalizeCallbacks()->append(qMakePair(QPointer<QObject>(obj), index)); - } else { - void *args[] = { 0 }; - QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, args); - } -} - QNetworkAccessManager *QQmlEnginePrivate::createNetworkAccessManager(QObject *parent) const { QMutexLocker locker(&networkAccessManagerMutex); @@ -1143,6 +1145,7 @@ QNetworkAccessManager *QQmlEngine::networkAccessManager() const Q_D(const QQmlEngine); return d->getNetworkAccessManager(); } +#endif // QT_NO_NETWORK /*! @@ -1405,7 +1408,7 @@ void qmlExecuteDeferred(QObject *object) QQmlComponentPrivate::beginDeferred(ep, object, &state); // Release the reference for the deferral action (we still have one from construction) - data->deferredData->compiledData->release(); + data->deferredData->compilationUnit->release(); delete data->deferredData; data->deferredData = 0; @@ -1636,13 +1639,13 @@ void QQmlData::destroyed(QObject *object) if (bindings && !bindings->ref.deref()) delete bindings; - if (compiledData) { - compiledData->release(); - compiledData = 0; + if (compilationUnit) { + compilationUnit->release(); + compilationUnit = 0; } if (deferredData) { - deferredData->compiledData->release(); + deferredData->compilationUnit->release(); delete deferredData; deferredData = 0; } @@ -1665,7 +1668,7 @@ void QQmlData::destroyed(QObject *object) QString source = expr->expression(); if (source.size() > 100) { source.truncate(96); - source.append(QStringLiteral(" ...")); + source.append(QLatin1String(" ...")); } locationString.append(source); } else { @@ -1801,14 +1804,16 @@ void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex) QQmlData_setBit(this, obj, coreIndex * 2 + 1); } -void QQmlData::ensurePropertyCache(QJSEngine *engine, QObject *object) +QQmlPropertyCache *QQmlData::ensurePropertyCache(QJSEngine *engine, QObject *object) { Q_ASSERT(engine); QQmlData *ddata = QQmlData::get(object, /*create*/true); - if (ddata->propertyCache) - return; - ddata->propertyCache = QJSEnginePrivate::get(engine)->cache(object); - if (ddata->propertyCache) ddata->propertyCache->addref(); + if (!ddata->propertyCache) { + ddata->propertyCache = QJSEnginePrivate::get(engine)->cache(object); + if (ddata->propertyCache) + ddata->propertyCache->addref(); + } + return ddata->propertyCache; } void QQmlEnginePrivate::sendQuit() @@ -2219,9 +2224,9 @@ int QQmlEnginePrivate::listType(int t) const QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const { Locker locker(this); - QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.constFind(t); + auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return QQmlMetaObject((*iter)->rootPropertyCache); + return QQmlMetaObject((*iter)->rootPropertyCache()); } else { QQmlType *type = QQmlMetaType::qmlType(t); return QQmlMetaObject(type?type->baseMetaObject():0); @@ -2231,9 +2236,9 @@ QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const { Locker locker(this); - QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.constFind(t); + auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return QQmlMetaObject((*iter)->rootPropertyCache); + return QQmlMetaObject((*iter)->rootPropertyCache()); } else { QQmlType *type = QQmlMetaType::qmlType(t); return QQmlMetaObject(type?type->metaObject():0); @@ -2243,9 +2248,9 @@ QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t) { Locker locker(this); - QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.constFind(t); + auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return (*iter)->rootPropertyCache; + return (*iter)->rootPropertyCache(); } else { QQmlType *type = QQmlMetaType::qmlType(t); locker.unlock(); @@ -2256,9 +2261,9 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t) QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t) { Locker locker(this); - QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.constFind(t); + auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return (*iter)->rootPropertyCache; + return (*iter)->rootPropertyCache(); } else { QQmlType *type = QQmlMetaType::qmlType(t); locker.unlock(); @@ -2266,9 +2271,9 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t) } } -void QQmlEnginePrivate::registerInternalCompositeType(QQmlCompiledData *data) +void QQmlEnginePrivate::registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) { - QByteArray name = data->rootPropertyCache->className(); + QByteArray name = compilationUnit->rootPropertyCache()->className(); QByteArray ptr = name + '*'; QByteArray lst = "QQmlListProperty<" + name + '>'; @@ -2286,21 +2291,21 @@ void QQmlEnginePrivate::registerInternalCompositeType(QQmlCompiledData *data) static_cast<QFlags<QMetaType::TypeFlag> >(QtPrivate::QMetaTypeTypeFlags<QQmlListProperty<QObject> >::Flags), static_cast<QMetaObject*>(0)); - data->metaTypeId = ptr_type; - data->listMetaTypeId = lst_type; - data->isRegisteredWithEngine = true; + compilationUnit->metaTypeId = ptr_type; + compilationUnit->listMetaTypeId = lst_type; + compilationUnit->isRegisteredWithEngine = true; Locker locker(this); m_qmlLists.insert(lst_type, ptr_type); // The QQmlCompiledData is not referenced here, but it is removed from this // hash in the QQmlCompiledData destructor - m_compositeTypes.insert(ptr_type, data); + m_compositeTypes.insert(ptr_type, compilationUnit); } -void QQmlEnginePrivate::unregisterInternalCompositeType(QQmlCompiledData *data) +void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) { - int ptr_type = data->metaTypeId; - int lst_type = data->listMetaTypeId; + int ptr_type = compilationUnit->metaTypeId; + int lst_type = compilationUnit->listMetaTypeId; Locker locker(this); m_qmlLists.remove(lst_type); @@ -2320,7 +2325,7 @@ bool QQmlEnginePrivate::isScriptLoaded(const QUrl &url) const return typeLoader.isScriptLoaded(url); } -#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) // Normalize a file name using Shell API. As opposed to converting it // to a short 8.3 name and back, this also works for drives where 8.3 notation // is disabled (see 8dot3name options of fsutil.exe). @@ -2352,7 +2357,7 @@ static inline QString shellNormalizeFileName(const QString &name) canonicalName[0] = canonicalName.at(0).toUpper(); return QDir::cleanPath(canonicalName); } -#endif // Q_OS_WIN && !Q_OS_WINCE && !Q_OS_WINRT +#endif // Q_OS_WIN && !Q_OS_WINRT bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */) { @@ -2360,7 +2365,7 @@ bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */) QFileInfo info(fileName); const QString absolute = info.absoluteFilePath(); -#if defined(Q_OS_MAC) || defined(Q_OS_WINCE) || defined(Q_OS_WINRT) +#if defined(Q_OS_DARWIN) || defined(Q_OS_WINRT) const QString canonical = info.canonicalFilePath(); #elif defined(Q_OS_WIN) // No difference if the path is qrc based diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index bd2a3cfc48..132af78f80 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -87,8 +87,10 @@ class QQmlExpression; class QQmlContext; class QQmlType; class QUrl; +#ifndef QT_NO_NETWORK class QNetworkAccessManager; class QQmlNetworkAccessManagerFactory; +#endif class QQmlIncubationController; class Q_QML_EXPORT QQmlEngine : public QJSEngine { @@ -115,10 +117,12 @@ public: bool importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors); +#ifndef QT_NO_NETWORK void setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *); QQmlNetworkAccessManagerFactory *networkAccessManagerFactory() const; QNetworkAccessManager *networkAccessManager() const; +#endif void setUrlInterceptor(QQmlAbstractUrlInterceptor* urlInterceptor); QQmlAbstractUrlInterceptor* urlInterceptor() const; diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 003cfa0112..949060f395 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -134,8 +134,12 @@ public: QRecyclePool<QQmlJavaScriptExpressionGuard> jsExpressionGuardPool; QQmlContext *rootContext; + +#ifdef QT_NO_QML_DEBUGGER + static const quintptr profiler = 0; +#else QQmlProfiler *profiler; - void enableProfiler(); +#endif bool outputWarningsToMsgLog; @@ -158,12 +162,12 @@ public: void registerFinalizeCallback(QObject *obj, int index); QQmlObjectCreator *activeObjectCreator; - +#ifndef QT_NO_NETWORK QNetworkAccessManager *createNetworkAccessManager(QObject *parent) const; QNetworkAccessManager *getNetworkAccessManager() const; mutable QNetworkAccessManager *networkAccessManager; mutable QQmlNetworkAccessManagerFactory *networkAccessManagerFactory; - +#endif QHash<QString,QSharedPointer<QQmlImageProviderBase> > imageProviders; QQmlAbstractUrlInterceptor* urlInterceptor; @@ -216,8 +220,8 @@ public: QQmlMetaObject metaObjectForType(int) const; QQmlPropertyCache *propertyCacheForType(int); QQmlPropertyCache *rawPropertyCacheForType(int); - void registerInternalCompositeType(QQmlCompiledData *); - void unregisterInternalCompositeType(QQmlCompiledData *); + void registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); + void unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); bool isTypeLoaded(const QUrl &url) const; bool isScriptLoaded(const QUrl &url) const; @@ -260,7 +264,7 @@ private: // the threaded loader. Only access them through their respective accessor methods. QHash<QPair<QQmlType *, int>, QQmlPropertyCache *> typePropertyCache; QHash<int, int> m_qmlLists; - QHash<int, QQmlCompiledData *> m_compositeTypes; + QHash<int, QV4::CompiledData::CompilationUnit *> m_compositeTypes; static bool s_designerMode; // These members is protected by the full QQmlEnginePrivate::mutex mutex diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index 50880e70ea..6afbd05e3e 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -44,7 +44,7 @@ #include "qqmlengine_p.h" #include "qqmlcontext_p.h" #include "qqmlscriptstring_p.h" -#include "qqmlcompiler_p.h" +#include "qqmlbinding_p.h" #include <private/qv8engine_p.h> #include <QtCore/qdebug.h> @@ -245,14 +245,15 @@ void QQmlExpression::setExpression(const QString &expression) } // Must be called with a valid handle scope -QV4::ReturnedValue QQmlExpressionPrivate::v4value(bool *isUndefined) +void QQmlExpressionPrivate::v4value(bool *isUndefined, QV4::Scope &scope) { if (!expressionFunctionValid) { createQmlBinding(context(), scopeObject(), expression, url, line); expressionFunctionValid = true; } - return evaluate(isUndefined); + QV4::ScopedCallData callData(scope); + evaluate(callData, isUndefined, scope); } QVariant QQmlExpressionPrivate::value(bool *isUndefined) @@ -271,9 +272,9 @@ QVariant QQmlExpressionPrivate::value(bool *isUndefined) { QV4::Scope scope(QV8Engine::getV4(ep->v8engine())); - QV4::ScopedValue result(scope, v4value(isUndefined)); + v4value(isUndefined, scope); if (!hasError()) - rv = scope.engine->toVariant(result, -1); + rv = scope.engine->toVariant(scope.result, -1); } ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h index 3d1df55a2d..741c25e206 100644 --- a/src/qml/qml/qqmlexpression_p.h +++ b/src/qml/qml/qqmlexpression_p.h @@ -57,7 +57,6 @@ #include <private/qfieldlist_p.h> #include <private/qflagpointer_p.h> #include <private/qdeletewatcher_p.h> -#include <private/qpointervaluepair_p.h> #include <private/qqmljavascriptexpression_p.h> QT_BEGIN_NAMESPACE @@ -77,7 +76,7 @@ public: QVariant value(bool *isUndefined = 0); - QV4::ReturnedValue v4value(bool *isUndefined = 0); + void v4value(bool *isUndefined, QV4::Scope &scope); static inline QQmlExpressionPrivate *get(QQmlExpression *expr); static inline QQmlExpression *get(QQmlExpressionPrivate *expr); diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp index 5fe3cba5c3..4769402855 100644 --- a/src/qml/qml/qqmlfile.cpp +++ b/src/qml/qml/qqmlfile.cpp @@ -67,6 +67,8 @@ static char assets_string[] = "assets"; #endif class QQmlFilePrivate; + +#ifndef QT_NO_NETWORK class QQmlFileNetworkReply : public QObject { Q_OBJECT @@ -97,6 +99,7 @@ private: int m_redirectCount; QNetworkReply *m_reply; }; +#endif class QQmlFilePrivate { @@ -114,10 +117,12 @@ public: Error error; QString errorString; - +#ifndef QT_NO_NETWORK QQmlFileNetworkReply *reply; +#endif }; +#ifndef QT_NO_NETWORK int QQmlFileNetworkReply::finishedIndex = -1; int QQmlFileNetworkReply::downloadProgressIndex = -1; int QQmlFileNetworkReply::networkFinishedIndex = -1; @@ -200,9 +205,13 @@ void QQmlFileNetworkReply::networkDownloadProgress(qint64 a, qint64 b) { emit downloadProgress(a, b); } +#endif // QT_NO_NETWORK QQmlFilePrivate::QQmlFilePrivate() -: error(None), reply(0) +: error(None) +#ifndef QT_NO_NETWORK +, reply(0) +#endif { } @@ -218,14 +227,15 @@ QQmlFile::QQmlFile(QQmlEngine *e, const QUrl &url) } QQmlFile::QQmlFile(QQmlEngine *e, const QString &url) -: d(new QQmlFilePrivate) + : QQmlFile(e, QUrl(url)) { - load(e, url); } QQmlFile::~QQmlFile() { +#ifndef QT_NO_NETWORK delete d->reply; +#endif delete d; d = 0; } @@ -263,8 +273,10 @@ QQmlFile::Status QQmlFile::status() const { if (d->url.isEmpty() && d->urlString.isEmpty()) return Null; +#ifndef QT_NO_NETWORK else if (d->reply) return Loading; +#endif else if (d->error != QQmlFilePrivate::None) return Error; else @@ -321,7 +333,11 @@ void QQmlFile::load(QQmlEngine *engine, const QUrl &url) d->error = QQmlFilePrivate::NotFound; } } else { +#ifndef QT_NO_NETWORK d->reply = new QQmlFileNetworkReply(engine, d, url); +#else + d->error = QQmlFilePrivate::NotFound; +#endif } } @@ -348,10 +364,14 @@ void QQmlFile::load(QQmlEngine *engine, const QString &url) d->error = QQmlFilePrivate::NotFound; } } else { +#ifndef QT_NO_NETWORK QUrl qurl(url); d->url = qurl; d->urlString = QString(); d->reply = new QQmlFileNetworkReply(engine, d, qurl); +#else + d->error = QQmlFilePrivate::NotFound; +#endif } } @@ -368,6 +388,7 @@ void QQmlFile::clear(QObject *) clear(); } +#ifndef QT_NO_NETWORK bool QQmlFile::connectFinished(QObject *object, const char *method) { if (!d || !d->reply) { @@ -411,6 +432,7 @@ bool QQmlFile::connectDownloadProgress(QObject *object, int method) return QMetaObject::connect(d->reply, QQmlFileNetworkReply::downloadProgressIndex, object, method); } +#endif /*! Returns true if QQmlFile will open \a url synchronously. diff --git a/src/qml/qml/qqmlfile.h b/src/qml/qml/qqmlfile.h index b0910cc0f4..3dd683a2cd 100644 --- a/src/qml/qml/qqmlfile.h +++ b/src/qml/qml/qqmlfile.h @@ -80,10 +80,12 @@ public: void clear(); void clear(QObject *); +#ifndef QT_NO_NETWORK bool connectFinished(QObject *, const char *); bool connectFinished(QObject *, int); bool connectDownloadProgress(QObject *, const char *); bool connectDownloadProgress(QObject *, int); +#endif static bool isSynchronous(const QString &url); static bool isSynchronous(const QUrl &url); diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index b51c78b8d4..9393f74a8d 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -191,7 +191,7 @@ void qmlClearEnginePlugins() { StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes(); QMutexLocker lock(&plugins->mutex); - foreach (RegisteredPlugin plugin, plugins->values()) { + for (auto &plugin : qAsConst(*plugins)) { QPluginLoader* loader = plugin.loader; if (loader && !loader->unload()) qWarning("Unloading %s failed: %s", qPrintable(plugin.uri), qPrintable(loader->errorString())); @@ -913,7 +913,7 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res // To avoid traversing all static plugins for all imports, we cut down // the list the first time called to only contain QML plugins: foreach (const QStaticPlugin &plugin, QPluginLoader::staticPlugins()) { - if (qobject_cast<QQmlExtensionPlugin *>(plugin.instance())) + if (plugin.metaData().value(QLatin1String("IID")).toString() == QLatin1String(QQmlExtensionInterface_iid)) plugins.append(plugin); } } @@ -921,7 +921,7 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res foreach (const QStaticPlugin &plugin, plugins) { // Since a module can list more than one plugin, we keep iterating even after we found a match. if (QQmlExtensionPlugin *instance = qobject_cast<QQmlExtensionPlugin *>(plugin.instance())) { - const QJsonArray metaTagsUriList = plugin.metaData().value(QStringLiteral("uri")).toArray(); + const QJsonArray metaTagsUriList = plugin.metaData().value(QLatin1String("uri")).toArray(); if (metaTagsUriList.isEmpty()) { if (errors) { QQmlError error; @@ -1702,10 +1702,7 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, resolvedPath += Slash; foreach (const QString &suffix, suffixes) { - QString pluginFileName = prefix; - - pluginFileName += baseName; - pluginFileName += suffix; + QString pluginFileName = prefix + baseName + suffix; QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName); if (!absolutePath.isEmpty()) @@ -1820,7 +1817,7 @@ void QQmlImportDatabase::addImportPath(const QString& path) } else if (path.startsWith(QLatin1Char(':'))) { // qrc directory, e.g. :/foo // need to convert to a qrc url, e.g. qrc:/foo - cPath = QStringLiteral("qrc") + path; + cPath = QLatin1String("qrc") + path; cPath.replace(Backslash, Slash); } else if (url.isRelative() || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index f0f160e6f6..0ce769aaea 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -41,7 +41,6 @@ #include "qqmlcomponent.h" #include "qqmlincubator_p.h" -#include "qqmlcompiler_p.h" #include "qqmlexpression_p.h" #include "qqmlmemoryprofiler_p.h" #include "qqmlobjectcreator_p.h" @@ -132,7 +131,7 @@ QQmlIncubationController *QQmlEngine::incubationController() const QQmlIncubatorPrivate::QQmlIncubatorPrivate(QQmlIncubator *q, QQmlIncubator::IncubationMode m) : q(q), status(QQmlIncubator::Null), mode(m), isAsynchronous(false), progress(Execute), - result(0), compiledData(0), waitingOnMe(0) + result(0), enginePriv(0), waitingOnMe(0) { } @@ -143,20 +142,15 @@ QQmlIncubatorPrivate::~QQmlIncubatorPrivate() void QQmlIncubatorPrivate::clear() { + compilationUnit = nullptr; if (next.isInList()) { next.remove(); - Q_ASSERT(compiledData); - QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(compiledData->engine); - compiledData->release(); - compiledData = 0; enginePriv->incubatorCount--; QQmlIncubationController *controller = enginePriv->incubationController; if (controller) controller->incubatingObjectCountChanged(enginePriv->incubatorCount); - } else if (compiledData) { - compiledData->release(); - compiledData = 0; } + enginePriv = 0; if (!rootContext.isNull()) { rootContext->activeVMEData = 0; rootContext = 0; @@ -278,21 +272,20 @@ void QQmlIncubatorPrivate::forceCompletion(QQmlInstantiationInterrupt &i) void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) { - if (!compiledData) + if (!compilationUnit) return; - QML_MEMORY_SCOPE_URL(compiledData->url()); + QML_MEMORY_SCOPE_URL(compilationUnit->url()); QExplicitlySharedDataPointer<QQmlIncubatorPrivate> protectThis(this); QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(this); - - QQmlEngine *engine = compiledData->engine; - QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); + // get a copy of the engine pointer as it might get reset; + QQmlEnginePrivate *enginePriv = this->enginePriv; if (!vmeGuard.isOK()) { QQmlError error; - error.setUrl(compiledData->url()); + error.setUrl(compilationUnit->url()); error.setDescription(QQmlComponent::tr("Object destroyed during incubation")); errors << error; progress = QQmlIncubatorPrivate::Completed; @@ -563,17 +556,16 @@ void QQmlIncubator::clear() if (s == Null) return; - QQmlEnginePrivate *enginePriv = 0; + QQmlEnginePrivate *enginePriv = d->enginePriv; if (s == Loading) { - Q_ASSERT(d->compiledData); - enginePriv = QQmlEnginePrivate::get(d->compiledData->engine); + Q_ASSERT(d->compilationUnit); if (d->result) d->result->deleteLater(); d->result = 0; } d->clear(); - Q_ASSERT(d->compiledData == 0); + Q_ASSERT(d->compilationUnit.isNull()); Q_ASSERT(d->waitingOnMe.data() == 0); Q_ASSERT(d->waitingFor.isEmpty()); @@ -713,7 +705,7 @@ QQmlIncubator::Status QQmlIncubatorPrivate::calculateStatus() const return QQmlIncubator::Error; else if (result && progress == QQmlIncubatorPrivate::Completed && waitingFor.isEmpty()) return QQmlIncubator::Ready; - else if (compiledData) + else if (compilationUnit) return QQmlIncubator::Loading; else return QQmlIncubator::Null; diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h index a12ff9c5e2..ecf3b6d2ca 100644 --- a/src/qml/qml/qqmlincubator_p.h +++ b/src/qml/qml/qqmlincubator_p.h @@ -59,7 +59,6 @@ QT_BEGIN_NAMESPACE -class QQmlCompiledData; class QQmlIncubator; class QQmlIncubatorPrivate : public QQmlEnginePrivate::Incubator { @@ -85,7 +84,8 @@ public: QPointer<QObject> result; QQmlGuardedContextData rootContext; - QQmlCompiledData *compiledData; + QQmlEnginePrivate *enginePriv; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; QScopedPointer<QQmlObjectCreator> creator; int subComponentToCreate; QQmlVMEGuard vmeGuard; diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 8ba4b5eba1..5d96240e5b 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -106,7 +106,8 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() m_nextExpression->m_prevExpression = m_prevExpression; } - clearGuards(); + clearActiveGuards(); + clearPermanentGuards(); if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion. m_scopeObject.asT2()->_s = 0; } @@ -114,12 +115,12 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v) { activeGuards.setFlagValue(v); - if (!v) clearGuards(); + if (!v) clearActiveGuards(); } void QQmlJavaScriptExpression::resetNotifyOnValueChanged() { - clearGuards(); + clearActiveGuards(); } void QQmlJavaScriptExpression::setContext(QQmlContextData *context) @@ -147,16 +148,9 @@ 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(callData, isUndefined); -} -QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined) +void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined, QV4::Scope &scope) { Q_ASSERT(m_context && m_context->engine); @@ -164,7 +158,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b if (!f || f->isUndefined()) { if (isUndefined) *isUndefined = true; - return QV4::Encode::undefined(); + return; } QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_context->engine); @@ -184,8 +178,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b capture.guards.copyAndClearPrepend(activeGuards); QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); - QV4::Scope scope(v4); - QV4::ScopedValue result(scope, QV4::Primitive::undefinedValue()); + scope.result = QV4::Primitive::undefinedValue(); callData->thisObject = v4->globalObject; if (scopeObject()) { QV4::ScopedValue value(scope, QV4::QObjectWrapper::wrap(v4, scopeObject())); @@ -193,7 +186,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b callData->thisObject = value; } - result = f->as<QV4::FunctionObject>()->call(callData); + f->as<QV4::FunctionObject>()->call(scope, callData); if (scope.hasException()) { if (watcher.wasDeleted()) scope.engine->catchException(); // ignore exception @@ -203,7 +196,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b *isUndefined = true; } else { if (isUndefined) - *isUndefined = result->isUndefined(); + *isUndefined = scope.result.isUndefined(); if (!watcher.wasDeleted() && hasDelayedError()) delayedError()->clearError(); @@ -220,11 +213,9 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b g->Delete(); ep->propertyCapture = lastPropertyCapture; - - return result->asReturnedValue(); } -void QQmlPropertyCapture::captureProperty(QQmlNotifier *n) +void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration) { if (watcher->wasDeleted()) return; @@ -244,14 +235,17 @@ void QQmlPropertyCapture::captureProperty(QQmlNotifier *n) g->connect(n); } - expression->activeGuards.prepend(g); + if (duration == OnlyOnce) + expression->permanentGuards.prepend(g); + else + expression->activeGuards.prepend(g); } /*! \internal \a n is in the signal index range (see QObjectPrivate::signalIndex()). */ -void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n) +void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration duration) { if (watcher->wasDeleted()) return; @@ -290,15 +284,19 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n) g->connect(o, n, engine); } - expression->activeGuards.prepend(g); + if (duration == Permanently) + expression->permanentGuards.prepend(g); + else + expression->activeGuards.prepend(g); } } -void QQmlPropertyCapture::registerQmlDependencies(QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction) +void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Function *compiledFunction, const QV4::Scope &scope) { // Let the caller check and avoid the function call :) Q_ASSERT(compiledFunction->hasQmlDependencies()); + QV4::ExecutionEngine *engine = scope.engine; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine()); if (!ep) return; @@ -306,33 +304,40 @@ void QQmlPropertyCapture::registerQmlDependencies(QV4::ExecutionEngine *engine, if (!capture) return; - QV4::Scope scope(engine); + if (capture->expression->m_permanentDependenciesRegistered) + return; + + capture->expression->m_permanentDependenciesRegistered = true; + QV4::Scoped<QV4::QmlContext> context(scope, engine->qmlContext()); QQmlContextData *qmlContext = context->qmlContext(); - const quint32 *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable(); + const QV4::CompiledData::LEUInt32 *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); + capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings, + QQmlPropertyCapture::Permanently); } Q_ASSERT(qmlContext->contextObject); - const quint32 *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable(); + const QV4::CompiledData::LEUInt32 *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); + capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex, + QQmlPropertyCapture::Permanently); } QObject *scopeObject = context->qmlScope(); - const quint32 *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable(); + const QV4::CompiledData::LEUInt32 *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); + capture->captureProperty(scopeObject, propertyIndex, notifyIndex, + QQmlPropertyCapture::Permanently); } } @@ -416,12 +421,18 @@ void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject * } -void QQmlJavaScriptExpression::clearGuards() +void QQmlJavaScriptExpression::clearActiveGuards() { while (QQmlJavaScriptExpressionGuard *g = activeGuards.takeFirst()) g->Delete(); } +void QQmlJavaScriptExpression::clearPermanentGuards() +{ + while (QQmlJavaScriptExpressionGuard *g = permanentGuards.takeFirst()) + g->Delete(); +} + void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **) { QQmlJavaScriptExpression *expression = diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 64cb1bb242..a5c7a80153 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -54,7 +54,6 @@ #include <QtCore/qglobal.h> #include <QtQml/qqmlerror.h> #include <private/qqmlengine_p.h> -#include <private/qpointervaluepair_p.h> QT_BEGIN_NAMESPACE @@ -104,8 +103,7 @@ public: virtual QString expressionIdentifier() = 0; virtual void expressionChanged() = 0; - QV4::ReturnedValue evaluate(bool *isUndefined); - QV4::ReturnedValue evaluate(QV4::CallData *callData, bool *isUndefined); + void evaluate(QV4::CallData *callData, bool *isUndefined, QV4::Scope &scope); inline bool notifyOnValueChanged() const; @@ -138,7 +136,8 @@ public: inline bool hasDelayedError() const; QQmlError error(QQmlEngine *) const; void clearError(); - void clearGuards(); + void clearActiveGuards(); + void clearPermanentGuards(); QQmlDelayedError *delayedError(); static QV4::ReturnedValue evalFunction(QQmlContextData *ctxt, QObject *scope, @@ -159,10 +158,12 @@ private: // activeGuards:flag2 - useSharedContext QBiPointer<QObject, DeleteWatcher> m_scopeObject; QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> activeGuards; + QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> permanentGuards; QQmlContextData *m_context; QQmlJavaScriptExpression **m_prevExpression; QQmlJavaScriptExpression *m_nextExpression; + bool m_permanentDependenciesRegistered = false; protected: QV4::PersistentValue m_function; @@ -179,10 +180,14 @@ public: Q_ASSERT(errorString == 0); } - void captureProperty(QQmlNotifier *); - void captureProperty(QObject *, int, int); + enum Duration { + OnlyOnce, + Permanently + }; - static void registerQmlDependencies(QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction); + static void registerQmlDependencies(const QV4::CompiledData::Function *compiledFunction, const QV4::Scope &scope); + void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce); + void captureProperty(QObject *, int, int, Duration duration = OnlyOnce); QQmlEngine *engine; QQmlJavaScriptExpression *expression; diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 7b129e2b57..271b4f1b31 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -44,7 +44,6 @@ #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> @@ -493,8 +492,8 @@ QQmlType *QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()), QQmlRefPointer<QQmlTypeData>::Adopt); if (td.isNull() || !td->isComplete()) return 0; - QQmlCompiledData *cd = td->compiledData(); - const QMetaObject *mo = cd->rootPropertyCache->firstCppMetaObject(); + QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit(); + const QMetaObject *mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); return QQmlMetaType::qmlType(mo); } @@ -1914,14 +1913,11 @@ QList<QQmlType*> QQmlMetaType::qmlSingletonTypes() QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - QList<QQmlType*> alltypes = data->nameToType.values(); QList<QQmlType*> retn; - foreach (QQmlType* t, alltypes) { - if (t->isSingleton()) { - retn.append(t); - } + for (const auto type : qAsConst(data->nameToType)) { + if (type->isSingleton()) + retn.append(type); } - return retn; } @@ -1929,9 +1925,9 @@ const QQmlPrivate::CachedQmlUnit *QQmlMetaType::findCachedCompilationUnit(const { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - for (QVector<QQmlPrivate::QmlUnitCacheLookupFunction>::ConstIterator it = data->lookupCachedQmlUnit.constBegin(), end = data->lookupCachedQmlUnit.constEnd(); - it != end; ++it) { - if (const QQmlPrivate::CachedQmlUnit *unit = (*it)(uri)) + + for (const auto lookup : qAsConst(data->lookupCachedQmlUnit)) { + if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) return unit; } return 0; @@ -1961,8 +1957,7 @@ QString QQmlMetaType::prettyTypeName(const QObject *object) marker = typeName.indexOf(QLatin1String("_QML_")); if (marker != -1) { - typeName = typeName.left(marker); - typeName += QLatin1Char('*'); + typeName = typeName.left(marker) + QLatin1Char('*'); type = QQmlMetaType::qmlType(QMetaType::type(typeName.toLatin1())); if (type) { typeName = type->qmlTypeName(); diff --git a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp index f9fea0279e..c94db8e168 100644 --- a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp +++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp @@ -41,6 +41,8 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_NETWORK + /*! \class QQmlNetworkAccessManagerFactory \since 5.0 @@ -101,4 +103,6 @@ QQmlNetworkAccessManagerFactory::~QQmlNetworkAccessManagerFactory() implementation of this method is reentrant. */ +#endif //QT_NO_NETWORK + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlnetworkaccessmanagerfactory.h b/src/qml/qml/qqmlnetworkaccessmanagerfactory.h index 8e3b94fad3..ba3561b9f4 100644 --- a/src/qml/qml/qqmlnetworkaccessmanagerfactory.h +++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.h @@ -45,6 +45,7 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_NETWORK class QNetworkAccessManager; class Q_QML_EXPORT QQmlNetworkAccessManagerFactory @@ -55,6 +56,8 @@ public: }; +#endif //QT_NO_NETWORK + QT_END_NAMESPACE #endif // QQMLNETWORKACCESSMANAGERFACTORY_H diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 831965f36a..3000f3e695 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -54,6 +54,7 @@ #include <private/qqmlscriptstring_p.h> #include <private/qqmlpropertyvalueinterceptor_p.h> #include <private/qqmlvaluetypeproxybinding_p.h> +#include <private/qqmlaccessors_p.h> QT_USE_NAMESPACE @@ -69,12 +70,11 @@ struct ActiveOCRestorer }; } -QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlContextData *creationContext, void *activeVMEDataForRootContext) +QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *creationContext, void *activeVMEDataForRootContext) : phase(Startup) - , compiledData(compiledData) - , resolvedTypes(compiledData->resolvedTypes) - , propertyCaches(compiledData->propertyCaches) - , vmeMetaObjectData(compiledData->metaObjects) + , compilationUnit(compilationUnit) + , resolvedTypes(compilationUnit->resolvedTypes) + , propertyCaches(&compilationUnit->propertyCaches) , activeVMEDataForRootContext(activeVMEDataForRootContext) { init(parentContext); @@ -82,24 +82,26 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompile sharedState = new QQmlObjectCreatorSharedState; topLevelCreator = true; sharedState->componentAttached = 0; - sharedState->allCreatedBindings.allocate(compiledData->totalBindingsCount); - sharedState->allParserStatusCallbacks.allocate(compiledData->totalParserStatusCount); - sharedState->allCreatedObjects.allocate(compiledData->totalObjectCount); + sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount); + sharedState->allParserStatusCallbacks.allocate(compilationUnit->totalParserStatusCount); + sharedState->allCreatedObjects.allocate(compilationUnit->totalObjectCount); sharedState->allJavaScriptObjects = 0; sharedState->creationContext = creationContext; sharedState->rootContext = 0; - QQmlProfiler *profiler = QQmlEnginePrivate::get(engine)->profiler; - Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler, - sharedState->profiler.init(profiler, compiledData->totalParserStatusCount)); + if (auto profiler = QQmlEnginePrivate::get(engine)->profiler) { + Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler, + sharedState->profiler.init(profiler, compilationUnit->totalParserStatusCount)); + } else { + Q_UNUSED(profiler); + } } -QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlObjectCreatorSharedState *inheritedSharedState) +QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState) : phase(Startup) - , compiledData(compiledData) - , resolvedTypes(compiledData->resolvedTypes) - , propertyCaches(compiledData->propertyCaches) - , vmeMetaObjectData(compiledData->metaObjects) + , compilationUnit(compilationUnit) + , resolvedTypes(compilationUnit->resolvedTypes) + , propertyCaches(&compilationUnit->propertyCaches) , activeVMEDataForRootContext(0) { init(parentContext); @@ -114,10 +116,10 @@ void QQmlObjectCreator::init(QQmlContextData *providedParentContext) engine = parentContext->engine; v4 = QV8Engine::getV4(engine); - if (!compiledData->isInitialized()) - compiledData->initialize(engine); + if (compilationUnit && !compilationUnit->engine) + compilationUnit->linkToEngine(v4); - qmlUnit = compiledData->compilationUnit->data; + qmlUnit = compilationUnit->data; context = 0; _qobject = 0; _scopeObject = 0; @@ -160,19 +162,16 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI int objectToCreate; if (subComponentIndex == -1) { - objectIndexToId = compiledData->objectIndexToIdForRoot; objectToCreate = qmlUnit->indexOfRootObject; } else { - objectIndexToId = compiledData->objectIndexToIdPerComponent[subComponentIndex]; const QV4::CompiledData::Object *compObj = qmlUnit->objectAt(subComponentIndex); objectToCreate = compObj->bindingTable()->value.objectIndex; } context = new QQmlContextData; context->isInternal = true; - context->imports = compiledData->importCache; - context->imports->addref(); - context->typeCompilationUnit = compiledData->compilationUnit; + context->imports = compilationUnit->importCache; + context->initFromTypeCompilationUnit(compilationUnit, subComponentIndex); context->setParent(parentContext); if (!sharedState->rootContext) { @@ -185,16 +184,14 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI Q_ASSERT(sharedState->allJavaScriptObjects || topLevelCreator); if (topLevelCreator) - sharedState->allJavaScriptObjects = scope.alloc(compiledData->totalObjectCount); + sharedState->allJavaScriptObjects = scope.alloc(compilationUnit->totalObjectCount); - context->setIdPropertyData(objectIndexToId); - - if (subComponentIndex == -1 && compiledData->scripts.count()) { - QV4::ScopedObject scripts(scope, v4->newArrayObject(compiledData->scripts.count())); + if (subComponentIndex == -1 && compilationUnit->dependentScripts.count()) { + QV4::ScopedObject scripts(scope, v4->newArrayObject(compilationUnit->dependentScripts.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); + for (int i = 0; i < compilationUnit->dependentScripts.count(); ++i) { + QQmlScriptData *s = compilationUnit->dependentScripts.at(i); scripts->putIndexed(i, (v = s->scriptValueForContext(context))); } } else if (sharedState->creationContext) { @@ -205,10 +202,10 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI if (instance) { QQmlData *ddata = QQmlData::get(instance); Q_ASSERT(ddata); - if (ddata->compiledData) - ddata->compiledData->release(); - ddata->compiledData = compiledData; - ddata->compiledData->addref(); + if (ddata->compilationUnit) + ddata->compilationUnit->release(); + ddata->compilationUnit = compilationUnit; + ddata->compilationUnit->addref(); } if (topLevelCreator) @@ -242,7 +239,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance) Q_ASSERT(topLevelCreator); Q_ASSERT(!sharedState->allJavaScriptObjects); - sharedState->allJavaScriptObjects = valueScope.alloc(compiledData->totalObjectCount); + sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount); QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1)); @@ -261,11 +258,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance) qSwap(_bindingTarget, bindingTarget); qSwap(_vmeMetaObject, vmeMetaObject); - QBitArray bindingSkipList = compiledData->deferredBindingsPerObject.value(_compiledObjectIndex); - for (int i = 0; i < bindingSkipList.count(); ++i) - bindingSkipList.setBit(i, !bindingSkipList.testBit(i)); - - setupBindings(bindingSkipList); + setupBindings(/*applyDeferredBindings=*/true); qSwap(_vmeMetaObject, vmeMetaObject); qSwap(_bindingTarget, bindingTarget); @@ -285,11 +278,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance) void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) { - QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | - QQmlPropertyPrivate::RemoveBindingOnAliasWrite; - int propertyWriteStatus = -1; - void *argv[] = { 0, 0, &propertyWriteStatus, &propertyWriteFlags }; - + QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | QQmlPropertyData::RemoveBindingOnAliasWrite; QV4::Scope scope(v4); int propertyType = property->propType; @@ -317,16 +306,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } else { int i = int(n); QVariant value(i); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } } else { if (property->isVarProperty()) { _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Primitive::fromDouble(n)); } else { QVariant value(n); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } } } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { @@ -334,8 +321,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Primitive::fromBoolean(binding->valueAsBoolean())); } else { QVariant value(binding->valueAsBoolean()); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } } else { QString stringValue = binding->valueAsString(qmlUnit); @@ -344,8 +330,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const _vmeMetaObject->setVMEProperty(property->coreIndex, s); } else { QVariant value = QQmlStringConverters::variantFromString(stringValue); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } } } @@ -353,22 +338,19 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const case QVariant::String: { Q_ASSERT(binding->evaluatesToString()); QString value = binding->valueAsString(qmlUnit); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::StringList: { Q_ASSERT(binding->evaluatesToString()); QStringList value(binding->valueAsString(qmlUnit)); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::ByteArray: { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); QByteArray value(binding->valueAsString(qmlUnit).toUtf8()); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Url: { @@ -376,20 +358,18 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QString string = binding->valueAsString(qmlUnit); // Encoded dir-separators defeat QUrl processing - decode them first string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); - QUrl value = string.isEmpty() ? QUrl() : compiledData->url().resolved(QUrl(string)); + QUrl value = string.isEmpty() ? QUrl() : compilationUnit->url().resolved(QUrl(string)); // Apply URL interceptor if (engine->urlInterceptor()) value = engine->urlInterceptor()->intercept(value, QQmlAbstractUrlInterceptor::UrlString); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::UInt: { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); double d = binding->valueAsNumber(); uint value = uint(d); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } break; @@ -397,23 +377,20 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); double d = binding->valueAsNumber(); int value = int(d); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } break; case QMetaType::Float: { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); float value = float(binding->valueAsNumber()); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Double: { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); double value = binding->valueAsNumber(); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Color: { @@ -422,8 +399,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const Q_ASSERT(ok); struct { void *data[4]; } buffer; if (QQml_valueTypeProvider()->storeValueType(property->propType, &colorValue, &buffer, sizeof(buffer))) { - argv[0] = reinterpret_cast<void *>(&buffer); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &buffer, propertyWriteFlags); } } break; @@ -432,16 +408,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const bool ok = false; QDate value = QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Time: { bool ok = false; QTime value = QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::DateTime: { @@ -454,8 +428,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const value = QDateTime(QDate::fromJulianDay(date), QTime::fromMSecsSinceStartOfDay(msecsSinceStartOfDay)); } Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; #endif // QT_NO_DATESTRING @@ -463,55 +436,48 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const bool ok = false; QPoint value = QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok).toPoint(); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::PointF: { bool ok = false; QPointF value = QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Size: { bool ok = false; QSize value = QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok).toSize(); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::SizeF: { bool ok = false; QSizeF value = QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Rect: { bool ok = false; QRect value = QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok).toRect(); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::RectF: { bool ok = false; QRectF value = QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok); Q_ASSERT(ok); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Bool: { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Boolean); bool value = binding->valueAsBoolean(); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Vector2D: { @@ -522,8 +488,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const bool ok = QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(qmlUnit), &vec, sizeof(vec)); Q_ASSERT(ok); Q_UNUSED(ok); - argv[0] = reinterpret_cast<void *>(&vec); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &vec, propertyWriteFlags); } break; case QVariant::Vector3D: { @@ -535,8 +500,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec)); Q_ASSERT(ok); Q_UNUSED(ok); - argv[0] = reinterpret_cast<void *>(&vec); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &vec, propertyWriteFlags); } break; case QVariant::Vector4D: { @@ -549,8 +513,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec)); Q_ASSERT(ok); Q_UNUSED(ok); - argv[0] = reinterpret_cast<void *>(&vec); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &vec, propertyWriteFlags); } break; case QVariant::Quaternion: { @@ -563,8 +526,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const bool ok = QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(qmlUnit), &vec, sizeof(vec)); Q_ASSERT(ok); Q_UNUSED(ok); - argv[0] = reinterpret_cast<void *>(&vec); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &vec, propertyWriteFlags); } break; case QVariant::RegExp: @@ -576,39 +538,34 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); QList<qreal> value; value.append(binding->valueAsNumber()); - argv[0] = reinterpret_cast<void *>(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType == qMetaTypeId<QList<int> >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); double n = binding->valueAsNumber(); QList<int> value; value.append(int(n)); - argv[0] = reinterpret_cast<void *>(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType == qMetaTypeId<QList<bool> >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Boolean); QList<bool> value; value.append(binding->valueAsBoolean()); - argv[0] = reinterpret_cast<void *>(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType == qMetaTypeId<QList<QUrl> >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); QString urlString = binding->valueAsString(qmlUnit); - QUrl u = urlString.isEmpty() ? QUrl() : compiledData->url().resolved(QUrl(urlString)); + QUrl u = urlString.isEmpty() ? QUrl() : compilationUnit->url().resolved(QUrl(urlString)); QList<QUrl> value; value.append(u); - argv[0] = reinterpret_cast<void *>(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType == qMetaTypeId<QList<QString> >()) { Q_ASSERT(binding->evaluatesToString()); QList<QString> value; value.append(binding->valueAsString(qmlUnit)); - argv[0] = reinterpret_cast<void *>(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType == qMetaTypeId<QJSValue>()) { QJSValue value; @@ -623,8 +580,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } else { value = QJSValue(binding->valueAsString(qmlUnit)); } - argv[0] = reinterpret_cast<void *>(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, &value, propertyWriteFlags); break; } @@ -640,8 +596,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; } - argv[0] = value.data(); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + property->writeProperty(_qobject, value.data(), propertyWriteFlags); } break; } @@ -658,14 +613,14 @@ static QQmlType *qmlTypeForObject(QObject *object) return type; } -void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip) +void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) { QQmlListProperty<void> savedList; qSwap(_currentList, savedList); - const QV4::CompiledData::BindingPropertyData &propertyData = compiledData->compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex); + const QV4::CompiledData::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex); - if (_compiledObject->idIndex) { + if (_compiledObject->idNameIndex) { const QQmlPropertyData *idProperty = propertyData.last(); Q_ASSERT(!idProperty || !idProperty->isValid() || idProperty->name(_qobject) == QLatin1String("id")); if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType == QMetaType::QString) { @@ -673,7 +628,7 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip) idBinding.propertyNameIndex = 0; // Not used idBinding.flags = 0; idBinding.type = QV4::CompiledData::Binding::Type_String; - idBinding.stringIndex = _compiledObject->idIndex; + idBinding.stringIndex = _compiledObject->idNameIndex; idBinding.location = _compiledObject->location; // ### setPropertyValue(idProperty, &idBinding); } @@ -691,7 +646,7 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip) if (qmlTypeForObject(_bindingTarget)) { quint32 bindingSkipList = 0; - QQmlPropertyData *defaultProperty = _compiledObject->indexOfDefaultProperty != -1 ? _propertyCache->parent()->defaultProperty() : _propertyCache->defaultProperty(); + QQmlPropertyData *defaultProperty = _compiledObject->indexOfDefaultPropertyOrAlias != -1 ? _propertyCache->parent()->defaultProperty() : _propertyCache->defaultProperty(); const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable(); for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) { @@ -709,9 +664,17 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip) const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable(); for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) { - if (static_cast<int>(i) < bindingsToSkip.size() && bindingsToSkip.testBit(i)) + if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding) continue; + if (binding->flags & QV4::CompiledData::Binding::IsDeferredBinding) { + if (!applyDeferredBindings) + continue; + } else { + if (applyDeferredBindings) + continue; + } + const QQmlPropertyData *property = propertyData.at(i); if (property && property->isQList()) { @@ -736,7 +699,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { Q_ASSERT(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty()); - QQmlCompiledData::TypeReference *tr = resolvedTypes.value(binding->propertyNameIndex); + QV4::CompiledData::ResolvedTypeReference *tr = resolvedTypes.value(binding->propertyNameIndex); Q_ASSERT(tr); QQmlType *attachedType = tr->type; if (!attachedType) { @@ -763,8 +726,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con ss.d.data()->isNumberLiteral = binding->type == QV4::CompiledData::Binding::Type_Number; ss.d.data()->numberValue = binding->valueAsNumber(); - QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | - QQmlPropertyPrivate::RemoveBindingOnAliasWrite; + QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | + QQmlPropertyData::RemoveBindingOnAliasWrite; int propertyWriteStatus = -1; void *argv[] = { &ss, 0, &propertyWriteStatus, &propertyWriteFlags }; QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); @@ -816,7 +779,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; if (valueType) - valueType->write(_qobject, property->coreIndex, QQmlPropertyPrivate::BypassInterceptor); + valueType->write(_qobject, property->coreIndex, QQmlPropertyData::BypassInterceptor); return true; } @@ -828,7 +791,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlPropertyPrivate::removeBinding(_bindingTarget, property->coreIndex); if (binding->type == QV4::CompiledData::Binding::Type_Script) { - QV4::Function *runtimeFunction = compiledData->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; + QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; QV4::Scope scope(v4); QV4::ScopedContext qmlContext(scope, currentQmlContext()); @@ -842,8 +805,6 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con bs->takeExpression(expr); } else { - QQmlBinding *qmlBinding = new QQmlBinding(function, _scopeObject, context); - // When writing bindings to grouped properties implemented as value types, // such as point.x: { someExpression; }, then the binding is installed on // the point property (_qobjectForBindings) and after evaluating the expression, @@ -853,6 +814,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con if (_valueTypeProperty) targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine); + QQmlBinding *qmlBinding = QQmlBinding::create(&targetCorePropertyData, function, _scopeObject, context); sharedState->allCreatedBindings.push(QQmlAbstractBinding::Ptr(qmlBinding)); qmlBinding->setTarget(_bindingTarget, targetCorePropertyData); @@ -943,8 +905,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return true; } - QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | - QQmlPropertyPrivate::RemoveBindingOnAliasWrite; + QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | + QQmlPropertyData::RemoveBindingOnAliasWrite; int propertyWriteStatus = -1; void *argv[] = { 0, 0, &propertyWriteStatus, &propertyWriteFlags }; @@ -1009,9 +971,9 @@ void QQmlObjectCreator::setupFunctions() QV4::ScopedValue function(scope); QV4::ScopedContext qmlContext(scope, currentQmlContext()); - const quint32 *functionIdx = _compiledObject->functionOffsetTable(); + const QV4::CompiledData::LEUInt32 *functionIdx = _compiledObject->functionOffsetTable(); for (quint32 i = 0; i < _compiledObject->nFunctions; ++i, ++functionIdx) { - QV4::Function *runtimeFunction = compiledData->compilationUnit->runtimeFunctions[*functionIdx]; + QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[*functionIdx]; const QString name = runtimeFunction->name()->toQString(); QQmlPropertyData *property = _propertyCache->property(name, _qobject, context); @@ -1026,18 +988,17 @@ void QQmlObjectCreator::setupFunctions() void QQmlObjectCreator::recordError(const QV4::CompiledData::Location &location, const QString &description) { QQmlError error; - error.setUrl(compiledData->url()); + error.setUrl(compilationUnit->url()); error.setLine(location.line); error.setColumn(location.column); error.setDescription(description); errors << error; } -void QQmlObjectCreator::registerObjectWithContextById(int objectIndex, QObject *instance) const +void QQmlObjectCreator::registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const { - QHash<int, int>::ConstIterator idEntry = objectIndexToId.find(objectIndex); - if (idEntry != objectIndexToId.constEnd()) - context->setIdProperty(idEntry.value(), instance); + if (object->id >= 0) + context->setIdProperty(object->id, instance); } QV4::Heap::QmlContext *QQmlObjectCreator::currentQmlContext() @@ -1050,7 +1011,9 @@ QV4::Heap::QmlContext *QQmlObjectCreator::currentQmlContext() QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isContextObject) { - QQmlObjectCreationProfiler profiler(sharedState->profiler.profiler); + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); + QQmlObjectCreationProfiler profiler(sharedState->profiler.profiler, obj); + ActiveOCRestorer ocRestorer(this, QQmlEnginePrivate::get(engine)); bool isComponent = false; @@ -1060,23 +1023,22 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo QQmlParserStatus *parserStatus = 0; bool installPropertyCache = true; - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); - if (compiledData->isComponent(index)) { + if (obj->flags & QV4::CompiledData::Object::IsComponent) { isComponent = true; - QQmlComponent *component = new QQmlComponent(engine, compiledData, index, parent); + QQmlComponent *component = new QQmlComponent(engine, compilationUnit, index, parent); Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( - compiledData, obj, QStringLiteral("<component>"), context->url())); + compilationUnit, obj, QStringLiteral("<component>"), context->url())); QQmlComponentPrivate::get(component)->creationContext = context; instance = component; ddata = QQmlData::get(instance, /*create*/true); } else { - QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); + QV4::CompiledData::ResolvedTypeReference *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); installPropertyCache = !typeRef->isFullyDynamicType; QQmlType *type = typeRef->type; if (type) { Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( - compiledData, obj, type->qmlTypeName(), context->url())); + compilationUnit, obj, type->qmlTypeName(), context->url())); instance = type->create(); if (!instance) { recordError(obj->location, tr("Unable to create object of type %1").arg(stringAt(obj->inheritedTypeNameIndex))); @@ -1097,17 +1059,17 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo sharedState->allCreatedObjects.push(instance); } else { - Q_ASSERT(typeRef->component); + Q_ASSERT(typeRef->compilationUnit); Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( - compiledData, obj, typeRef->component->fileName(), + compilationUnit, obj, typeRef->compilationUnit->fileName(), context->url())); - if (typeRef->component->compilationUnit->data->isSingleton()) + if (typeRef->compilationUnit->data->isSingleton()) { recordError(obj->location, tr("Composite Singleton Type %1 is not creatable").arg(stringAt(obj->inheritedTypeNameIndex))); return 0; } - QQmlObjectCreator subCreator(context, typeRef->component, sharedState.data()); + QQmlObjectCreator subCreator(context, typeRef->compilationUnit, sharedState.data()); instance = subCreator.create(); if (!instance) { errors += subCreator.errors; @@ -1153,32 +1115,30 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (isContextObject) context->contextObject = instance; - QBitArray bindingsToSkip; - if (customParser) { - 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; - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); - for (int i = 0; i < customParserBindings->count(); ++i) - if (customParserBindings->testBit(i)) - bindings << obj->bindingTable() + i; - customParser->applyBindings(instance, compiledData, bindings); - - customParser->engine = 0; - customParser->imports = (QQmlTypeNameCache*)0; - bindingsToSkip = *customParserBindings; + if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) { + customParser->engine = QQmlEnginePrivate::get(engine); + customParser->imports = compilationUnit->importCache; + + QList<const QV4::CompiledData::Binding *> bindings; + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); + const QV4::CompiledData::Binding *binding = obj->bindingTable(); + for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { + if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding) { + bindings << binding; + } } + customParser->applyBindings(instance, compilationUnit, bindings); + + customParser->engine = 0; + customParser->imports = (QQmlTypeNameCache*)0; } if (isComponent) { - registerObjectWithContextById(index, instance); + registerObjectWithContextById(obj, instance); return instance; } - QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.at(index); + QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches->at(index); Q_ASSERT(!cache.isNull()); if (installPropertyCache) { if (ddata->propertyCache) @@ -1199,7 +1159,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo qSwap(_qmlContext, qmlContext); - bool result = populateInstance(index, instance, /*binding target*/instance, /*value type property*/0, bindingsToSkip); + bool result = populateInstance(index, instance, /*binding target*/instance, /*value type property*/0); qSwap(_qmlContext, qmlContext); qSwap(_scopeObject, scopeObject); @@ -1224,8 +1184,8 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru QQmlData *data = QQmlData::get(b->targetObject()); Q_ASSERT(data); data->clearPendingBindingBit(b->targetPropertyIndex()); - b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | - QQmlPropertyPrivate::DontRemoveBinding); + b->setEnabled(true, QQmlPropertyData::BypassInterceptor | + QQmlPropertyData::DontRemoveBinding); if (watcher.hasRecursed() || interrupt.shouldInterrupt()) return 0; @@ -1294,7 +1254,7 @@ void QQmlObjectCreator::clear() phase = Done; } -bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty, const QBitArray &bindingsToSkip) +bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty) { QQmlData *declarativeData = QQmlData::get(instance, /*create*/true); @@ -1309,14 +1269,13 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * QV4::Scope valueScope(v4); QV4::ScopedValue scopeObjectProtector(valueScope); - QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.at(_compiledObjectIndex); + QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches->at(_compiledObjectIndex); QQmlVMEMetaObject *vmeMetaObject = 0; - const QByteArray data = vmeMetaObjectData.value(_compiledObjectIndex); - if (!data.isEmpty()) { + if (propertyCaches->needsVMEMetaObject(_compiledObjectIndex)) { Q_ASSERT(!cache.isNull()); // install on _object - vmeMetaObject = new QQmlVMEMetaObject(_qobject, cache, reinterpret_cast<const QQmlVMEMetaData*>(data.constData())); + vmeMetaObject = new QQmlVMEMetaObject(_qobject, cache, compilationUnit, _compiledObjectIndex); if (_ddata->propertyCache) _ddata->propertyCache->release(); _ddata->propertyCache = cache; @@ -1326,33 +1285,23 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * vmeMetaObject = QQmlVMEMetaObject::get(_qobject); } - registerObjectWithContextById(_compiledObjectIndex, _qobject); + registerObjectWithContextById(_compiledObject, _qobject); qSwap(_propertyCache, cache); qSwap(_vmeMetaObject, vmeMetaObject); - QBitArray bindingSkipList = bindingsToSkip; - { - QHash<int, QBitArray>::ConstIterator deferredBindings = compiledData->deferredBindingsPerObject.constFind(_compiledObjectIndex); - if (deferredBindings != compiledData->deferredBindingsPerObject.constEnd()) { - if (bindingSkipList.isEmpty()) - bindingSkipList.resize(deferredBindings->count()); - - for (int i = 0; i < deferredBindings->count(); ++i) - if (deferredBindings->testBit(i)) - bindingSkipList.setBit(i); - QQmlData::DeferredData *deferData = new QQmlData::DeferredData; - deferData->deferredIdx = _compiledObjectIndex; - deferData->compiledData = compiledData; - deferData->compiledData->addref(); - deferData->context = context; - _ddata->deferredData = deferData; - } + if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings) { + QQmlData::DeferredData *deferData = new QQmlData::DeferredData; + deferData->deferredIdx = _compiledObjectIndex; + deferData->compilationUnit = compilationUnit; + deferData->compilationUnit->addref(); + deferData->context = context; + _ddata->deferredData = deferData; } if (_compiledObject->nFunctions > 0) setupFunctions(); - setupBindings(bindingSkipList); + setupBindings(); qSwap(_vmeMetaObject, vmeMetaObject); qSwap(_bindingTarget, bindingTarget); diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 3d743954c9..e3312f9df5 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -53,7 +53,6 @@ #include <private/qqmlimport_p.h> #include <private/qqmltypenamecache_p.h> #include <private/qv4compileddata_p.h> -#include <private/qqmlcompiler_p.h> #include <private/qqmltypecompiler_p.h> #include <private/qfinitestack_p.h> #include <private/qrecursionwatcher_p.h> @@ -66,7 +65,6 @@ QT_BEGIN_NAMESPACE class QQmlAbstractBinding; struct QQmlTypeCompiler; class QQmlInstantiationInterrupt; -struct QQmlVmeProfiler; struct QQmlObjectCreatorSharedState : public QSharedData { @@ -86,7 +84,7 @@ class QQmlObjectCreator { Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator) public: - QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlContextData *creationContext, void *activeVMEDataForRootContext = 0); + QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *creationContext, void *activeVMEDataForRootContext = 0); ~QQmlObjectCreator(); QObject *create(int subComponentIndex = -1, QObject *parent = 0, QQmlInstantiationInterrupt *interrupt = 0); @@ -104,17 +102,16 @@ public: QFiniteStack<QPointer<QObject> > &allCreatedObjects() const { return sharedState->allCreatedObjects; } private: - QQmlObjectCreator(QQmlContextData *contextData, QQmlCompiledData *compiledData, QQmlObjectCreatorSharedState *inheritedSharedState); + QQmlObjectCreator(QQmlContextData *contextData, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState); void init(QQmlContextData *parentContext); QObject *createInstance(int index, QObject *parent = 0, bool isContextObject = false); bool populateInstance(int index, QObject *instance, - QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty, - const QBitArray &bindingsToSkip = QBitArray()); + QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty); - void setupBindings(const QBitArray &bindingsToSkip); + void setupBindings(bool applyDeferredBindings = false); bool setPropertyBinding(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding); void setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding); void setupFunctions(); @@ -122,7 +119,7 @@ private: QString stringAt(int idx) const { return qmlUnit->stringAt(idx); } void recordError(const QV4::CompiledData::Location &location, const QString &description); - void registerObjectWithContextById(int objectIndex, QObject *instance) const; + void registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const; QV4::Heap::QmlContext *currentQmlContext(); @@ -137,14 +134,12 @@ private: QQmlEngine *engine; QV4::ExecutionEngine *v4; - QQmlCompiledData *compiledData; + QV4::CompiledData::CompilationUnit *compilationUnit; const QV4::CompiledData::Unit *qmlUnit; QQmlGuardedContextData parentContext; QQmlContextData *context; - const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes; - const QVector<QQmlPropertyCache *> &propertyCaches; - const QVector<QByteArray> &vmeMetaObjectData; - QHash<int, int> objectIndexToId; + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes; + const QQmlPropertyCacheVector *propertyCaches; QExplicitlySharedDataPointer<QQmlObjectCreatorSharedState> sharedState; bool topLevelCreator; void *activeVMEDataForRootContext; diff --git a/src/qml/qml/qqmlplatform.cpp b/src/qml/qml/qqmlplatform.cpp index 37d8d4748a..a47a0ab4a4 100644 --- a/src/qml/qml/qqmlplatform.cpp +++ b/src/qml/qml/qqmlplatform.cpp @@ -67,8 +67,6 @@ QString QQmlPlatform::os() return QStringLiteral("tvos"); #elif defined(Q_OS_MAC) return QStringLiteral("osx"); -#elif defined(Q_OS_WINCE) - return QStringLiteral("wince"); #elif defined(Q_OS_WINPHONE) return QStringLiteral("winphone"); #elif defined(Q_OS_WINRT) diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index df2ff05de1..b3eb0a5619 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -42,6 +42,7 @@ #include "qqml.h" #include "qqmlbinding_p.h" +#include "qqmlboundsignal_p.h" #include "qqmlcontext.h" #include "qqmlcontext_p.h" #include "qqmlboundsignal_p.h" @@ -50,7 +51,6 @@ #include "qqmldata_p.h" #include "qqmlstringconverters_p.h" #include "qqmllist_p.h" -#include "qqmlcompiler_p.h" #include "qqmlvmemetaobject_p.h" #include "qqmlexpression_p.h" #include "qqmlvaluetypeproxybinding_p.h" @@ -317,8 +317,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) if (!property->isQObject()) return; // Not an object property - void *args[] = { ¤tObject, 0 }; - QMetaObject::metacall(currentObject, QMetaObject::ReadProperty, property->coreIndex, args); + property->readProperty(currentObject, ¤tObject); if (!currentObject) return; // No value } @@ -859,7 +858,7 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, } -void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, WriteFlags writeFlags) +void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, QQmlPropertyData::WriteFlags writeFlags) { Q_ASSERT(binding); @@ -1035,15 +1034,13 @@ QVariant QQmlPropertyPrivate::readValueProperty() } else if (core.isQList()) { QQmlListProperty<QObject> prop; - void *args[] = { &prop, 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args); + core.readProperty(object, &prop); return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType, engine)); } else if (core.isQObject()) { QObject *rv = 0; - void *args[] = { &rv, 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args); + core.readProperty(object, &rv); return QVariant::fromValue(rv); } else { @@ -1060,7 +1057,7 @@ QVariant QQmlPropertyPrivate::readValueProperty() value = QVariant(core.propType, (void*)0); args[0] = value.data(); } - QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args); + core.readPropertyWithArgs(object, args); if (core.propType != QMetaType::QVariant && args[0] != value.data()) return QVariant((QVariant::Type)core.propType, args[0]); @@ -1148,7 +1145,7 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, return status; } -bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, WriteFlags flags) +bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, QQmlPropertyData::WriteFlags flags) { return writeValueProperty(object, core, value, effectiveContext(), flags); } @@ -1157,10 +1154,10 @@ bool QQmlPropertyPrivate::writeValueProperty(QObject *object, const QQmlPropertyData &core, const QVariant &value, - QQmlContextData *context, WriteFlags flags) + QQmlContextData *context,QQmlPropertyData::WriteFlags flags) { // Remove any existing bindings on this property - if (!(flags & DontRemoveBinding) && object) + if (!(flags & QQmlPropertyData::DontRemoveBinding) && object) removeBinding(object, core.encodedIndex()); bool rv = false; @@ -1190,145 +1187,151 @@ QQmlPropertyPrivate::writeValueProperty(QObject *object, bool QQmlPropertyPrivate::write(QObject *object, const QQmlPropertyData &property, const QVariant &value, QQmlContextData *context, - WriteFlags flags) + QQmlPropertyData::WriteFlags flags) { - int coreIdx = property.coreIndex; - int status = -1; //for dbus + const int propertyType = property.propType; + const int variantType = value.userType(); if (property.isEnum()) { QMetaProperty prop = object->metaObject()->property(property.coreIndex); QVariant v = value; // Enum values come through the script engine as doubles - if (value.userType() == QVariant::Double) { + if (variantType == QVariant::Double) { double integral; double fractional = std::modf(value.toDouble(), &integral); if (qFuzzyIsNull(fractional)) v.convert(QVariant::Int); } - return writeEnumProperty(prop, coreIdx, object, v, flags); + return writeEnumProperty(prop, property.coreIndex, object, v, flags); } - int propertyType = property.propType; - int variantType = value.userType(); - QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(context); + const bool isUrl = propertyType == QVariant::Url; // handled separately + + // The cases below are in approximate order of likelyhood: + if (propertyType == variantType && !isUrl && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) { + return property.writeProperty(object, const_cast<void *>(value.constData()), flags); + } else if (property.isQObject()) { + QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, variantType); + if (valMo.isNull()) + return false; + QObject *o = *static_cast<QObject *const *>(value.constData()); + QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType); - if (propertyType == QVariant::Url) { + if (o) + valMo = o; + if (QQmlMetaObject::canConvert(valMo, propMo)) { + return property.writeProperty(object, &o, flags); + } else if (!o && QQmlMetaObject::canConvert(propMo, valMo)) { + // In the case of a null QObject, we assign the null if there is + // any change that the null variant type could be up or down cast to + // the property type. + return property.writeProperty(object, &o, flags); + } else { + return false; + } + } else if (value.canConvert(propertyType) && !isUrl && variantType != QVariant::String && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) { + // common cases: + switch (propertyType) { + case QMetaType::Bool: { + bool b = value.toBool(); + return property.writeProperty(object, &b, flags); + } + case QMetaType::Int: { + int i = value.toInt(); + return property.writeProperty(object, &i, flags); + } + case QMetaType::Double: { + double d = value.toDouble(); + return property.writeProperty(object, &d, flags); + } + case QMetaType::Float: { + float f = value.toFloat(); + return property.writeProperty(object, &f, flags); + } + case QMetaType::QString: { + QString s = value.toString(); + return property.writeProperty(object, &s, flags); + } + default: { // "fallback": + QVariant v = value; + v.convert(propertyType); + return property.writeProperty(object, const_cast<void *>(v.constData()), flags); + } + } + } else if (propertyType == qMetaTypeId<QVariant>()) { + return property.writeProperty(object, const_cast<QVariant *>(&value), flags); + } else if (isUrl) { QUrl u; - bool found = false; if (variantType == QVariant::Url) { u = value.toUrl(); - found = true; } else if (variantType == QVariant::ByteArray) { QString input(QString::fromUtf8(value.toByteArray())); // Encoded dir-separators defeat QUrl processing - decode them first input.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); u = QUrl(input); - found = true; } else if (variantType == QVariant::String) { QString input(value.toString()); // Encoded dir-separators defeat QUrl processing - decode them first input.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); u = QUrl(input); - found = true; - } - - if (!found) - return false; - - if (context && u.isRelative() && !u.isEmpty()) - u = context->resolvedUrl(u); - int status = -1; - void *argv[] = { &u, 0, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, argv); - - } else if (propertyType == qMetaTypeId<QList<QUrl> >()) { - QList<QUrl> urlSeq = resolvedUrlSequence(value, context).value<QList<QUrl> >(); - int status = -1; - void *argv[] = { &urlSeq, 0, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, argv); - } else if (variantType == propertyType) { - - void *a[] = { const_cast<void *>(value.constData()), 0, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); - - } else if (qMetaTypeId<QVariant>() == propertyType) { - - void *a[] = { const_cast<QVariant *>(&value), 0, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); - - } else if (property.isQObject()) { - - QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, value.userType()); - - if (valMo.isNull()) - return false; - - QObject *o = *(QObject *const *)value.constData(); - QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType); - - if (o) valMo = o; - - if (QQmlMetaObject::canConvert(valMo, propMo)) { - void *args[] = { &o, 0, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, args); - } else if (!o && QQmlMetaObject::canConvert(propMo, valMo)) { - // In the case of a null QObject, we assign the null if there is - // any change that the null variant type could be up or down cast to - // the property type. - void *args[] = { &o, 0, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, args); } else { return false; } + if (context && u.isRelative() && !u.isEmpty()) + u = context->resolvedUrl(u); + return property.writeProperty(object, &u, flags); + } else if (propertyType == qMetaTypeId<QList<QUrl>>()) { + QList<QUrl> urlSeq = resolvedUrlSequence(value, context).value<QList<QUrl>>(); + return property.writeProperty(object, &urlSeq, flags); } else if (property.isQList()) { - QQmlMetaObject listType; if (enginePriv) { listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType)); } else { QQmlType *type = QQmlMetaType::qmlType(QQmlMetaType::listType(property.propType)); - if (!type) return false; + if (!type) + return false; listType = type->baseMetaObject(); } - if (listType.isNull()) return false; + if (listType.isNull()) + return false; QQmlListProperty<void> prop; - void *args[] = { &prop, 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, coreIdx, args); + property.readProperty(object, &prop); - if (!prop.clear) return false; + if (!prop.clear) + return false; prop.clear(&prop); - if (value.userType() == qMetaTypeId<QQmlListReference>()) { + if (variantType == qMetaTypeId<QQmlListReference>()) { QQmlListReference qdlr = value.value<QQmlListReference>(); for (int ii = 0; ii < qdlr.count(); ++ii) { QObject *o = qdlr.at(ii); if (o && !QQmlMetaObject::canConvert(o, listType)) - o = 0; - prop.append(&prop, (void *)o); + o = nullptr; + prop.append(&prop, o); } - } else if (value.userType() == qMetaTypeId<QList<QObject *> >()) { + } else if (variantType == qMetaTypeId<QList<QObject *> >()) { const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(value); for (int ii = 0; ii < list.count(); ++ii) { QObject *o = list.at(ii); if (o && !QQmlMetaObject::canConvert(o, listType)) - o = 0; - prop.append(&prop, (void *)o); + o = nullptr; + prop.append(&prop, o); } } else { QObject *o = enginePriv?enginePriv->toQObject(value):QQmlMetaType::toQObject(value); if (o && !QQmlMetaObject::canConvert(o, listType)) - o = 0; - prop.append(&prop, (void *)o); + o = nullptr; + prop.append(&prop, o); } - } else { Q_ASSERT(variantType != propertyType); @@ -1368,7 +1371,8 @@ bool QQmlPropertyPrivate::write(QObject *object, // successful conversion. Q_ASSERT(v.userType() == propertyType); ok = true; - } else if ((uint)propertyType >= QVariant::UserType && variantType == QVariant::String) { + } else if (static_cast<uint>(propertyType) >= QVariant::UserType && + variantType == QVariant::String) { QQmlMetaType::StringConverter con = QQmlMetaType::customStringConverter(propertyType); if (con) { v = con(value.toString()); @@ -1411,8 +1415,7 @@ bool QQmlPropertyPrivate::write(QObject *object, } if (ok) { - void *a[] = { const_cast<void *>(v.constData()), 0, &status, &flags}; - QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); + return property.writeProperty(object, const_cast<void *>(v.constData()), flags); } else { return false; } @@ -1428,10 +1431,9 @@ QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate *engi return metaType.metaObject(); if (engine) return engine->rawMetaObjectForType(userType); - QQmlType *type = QQmlMetaType::qmlType(userType); - if (type) + if (QQmlType *type = QQmlMetaType::qmlType(userType)) return QQmlMetaObject(type->baseMetaObject()); - return QQmlMetaObject((QObject*)0); + return QQmlMetaObject(); } /*! @@ -1513,7 +1515,7 @@ bool QQmlProperty::reset() const } bool QQmlPropertyPrivate::write(const QQmlProperty &that, - const QVariant &value, WriteFlags flags) + const QVariant &value, QQmlPropertyData::WriteFlags flags) { if (!that.d) return false; diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index 58fea9c239..9398c74621 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -68,13 +68,6 @@ class QQmlJavaScriptExpression; class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount { public: - enum WriteFlag { - BypassInterceptor = 0x01, - DontRemoveBinding = 0x02, - RemoveBindingOnAliasWrite = 0x04 - }; - Q_DECLARE_FLAGS(WriteFlags, WriteFlag) - QQmlContextData *context; QPointer<QQmlEngine> engine; QPointer<QObject> object; @@ -97,7 +90,7 @@ public: QQmlProperty::PropertyTypeCategory propertyTypeCategory() const; QVariant readValueProperty(); - bool writeValueProperty(const QVariant &, WriteFlags); + bool writeValueProperty(const QVariant &, QQmlPropertyData::WriteFlags); static QQmlMetaObject rawMetaObjectForType(QQmlEnginePrivate *, int); static bool writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, @@ -105,9 +98,9 @@ public: static bool writeValueProperty(QObject *, const QQmlPropertyData &, const QVariant &, QQmlContextData *, - WriteFlags flags = 0); + QQmlPropertyData::WriteFlags flags = 0); static bool write(QObject *, const QQmlPropertyData &, const QVariant &, - QQmlContextData *, WriteFlags flags = 0); + QQmlContextData *, QQmlPropertyData::WriteFlags flags = 0); static void findAliasTarget(QObject *, int, QObject **, int *); enum BindingFlag { @@ -116,7 +109,7 @@ public: }; Q_DECLARE_FLAGS(BindingFlags, BindingFlag) - static void setBinding(QQmlAbstractBinding *binding, BindingFlags flags = None, WriteFlags writeFlags = DontRemoveBinding); + static void setBinding(QQmlAbstractBinding *binding, BindingFlags flags = None, QQmlPropertyData::WriteFlags writeFlags = QQmlPropertyData::DontRemoveBinding); static void removeBinding(const QQmlProperty &that); static void removeBinding(QObject *o, int index); @@ -144,7 +137,7 @@ public: QQmlBoundSignalExpression *); static void takeSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *); - static bool write(const QQmlProperty &that, const QVariant &, WriteFlags); + static bool write(const QQmlProperty &that, const QVariant &, QQmlPropertyData::WriteFlags); static int valueTypeCoreIndex(const QQmlProperty &that); static int bindingIndex(const QQmlProperty &that); static int bindingIndex(const QQmlPropertyData &that); @@ -157,7 +150,6 @@ public: 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 0522aa93ee..322e519706 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -184,10 +184,16 @@ void QQmlPropertyData::load(const QMetaMethod &m) { coreIndex = m.methodIndex(); arguments = 0; + + propType = m.returnType(); + flags |= IsFunction; if (m.methodType() == QMetaMethod::Signal) flags |= IsSignal; - propType = m.returnType(); + else if (m.methodType() == QMetaMethod::Constructor) { + flags |= IsConstructor; + propType = QMetaType::QObjectStar; + } if (m.parameterCount()) { flags |= HasArguments; @@ -206,11 +212,15 @@ void QQmlPropertyData::load(const QMetaMethod &m) void QQmlPropertyData::lazyLoad(const QMetaMethod &m) { coreIndex = m.methodIndex(); + propType = QMetaType::Void; arguments = 0; flags |= IsFunction; if (m.methodType() == QMetaMethod::Signal) flags |= IsSignal; - propType = QMetaType::Void; + else if (m.methodType() == QMetaMethod::Constructor) { + flags |= IsConstructor; + propType = QMetaType::QObjectStar; + } const char *returnType = m.typeName(); if (!returnType) @@ -220,9 +230,10 @@ void QQmlPropertyData::lazyLoad(const QMetaMethod &m) flags |= NotFullyResolved; } - if (m.parameterCount()) { + const int paramCount = m.parameterCount(); + if (paramCount) { flags |= HasArguments; - if ((m.parameterCount() == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) { + if ((paramCount == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) { flags |= IsV4Function; } } @@ -249,11 +260,8 @@ QQmlPropertyCache::QQmlPropertyCache(QV4::ExecutionEngine *e) Creates a new QQmlPropertyCache of \a metaObject. */ 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) + : QQmlPropertyCache(e) { - Q_ASSERT(engine); Q_ASSERT(metaObject); update(metaObject); @@ -416,12 +424,6 @@ void QQmlPropertyCache::appendMethod(const QString &name, quint32 flags, int cor 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 -{ - return _metaObject; -} - // Returns this property cache's metaObject, creating it if necessary. const QMetaObject *QQmlPropertyCache::createMetaObject() { @@ -437,34 +439,19 @@ const QMetaObject *QQmlPropertyCache::createMetaObject() return _metaObject; } -// Returns the name of the default property for this cache -QString QQmlPropertyCache::defaultPropertyName() const -{ - return _defaultPropertyName; -} - QQmlPropertyData *QQmlPropertyCache::defaultProperty() const { return property(defaultPropertyName(), 0, 0); } -QQmlPropertyCache *QQmlPropertyCache::parent() const -{ - return _parent; -} - void QQmlPropertyCache::setParent(QQmlPropertyCache *newParent) { + if (_parent == newParent) + return; + if (_parent) + _parent->release(); _parent = newParent; -} - -// Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by -// QML -const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const -{ - while (_parent && (_metaObject == 0 || _ownMetaObject)) - return _parent->firstCppMetaObject(); - return _metaObject; + _parent->addref(); } QQmlPropertyCache * @@ -523,11 +510,12 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, bool hasFastProperty = false; for (int ii = 0; ii < classInfoCount; ++ii) { int idx = ii + classInfoOffset; - - if (0 == qstrcmp(metaObject->classInfo(idx).name(), "qt_HasQmlAccessors")) { + QMetaClassInfo mci = metaObject->classInfo(idx); + const char *name = mci.name(); + if (0 == qstrcmp(name, "qt_HasQmlAccessors")) { hasFastProperty = true; - } else if (0 == qstrcmp(metaObject->classInfo(idx).name(), "DefaultProperty")) { - _defaultPropertyName = QString::fromUtf8(metaObject->classInfo(idx).value()); + } else if (0 == qstrcmp(name, "DefaultProperty")) { + _defaultPropertyName = QString::fromUtf8(mci.value()); } } @@ -610,7 +598,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, setNamedProperty(methodName, ii, data, (old != 0)); if (data->isSignal()) { - QHashedString on(QStringLiteral("on") % methodName.at(0).toUpper() % methodName.midRef(1)); + QHashedString on(QLatin1String("on") % methodName.at(0).toUpper() % methodName.midRef(1)); setNamedProperty(on, ii, sigdata, (old != 0)); ++signalHandlerIndex; } @@ -802,48 +790,6 @@ void QQmlPropertyCache::invalidate(const QMetaObject *metaObject) } } -/*! \internal - \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()). - This is different from QMetaMethod::methodIndex(). -*/ -QQmlPropertyData * -QQmlPropertyCache::signal(int index) const -{ - if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.count())) - return 0; - - if (index < signalHandlerIndexCacheStart) - return _parent->signal(index); - - QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - signalHandlerIndexCacheStart)); - Q_ASSERT(rv->isSignal() || rv->coreIndex == -1); - return ensureResolved(rv); -} - -int QQmlPropertyCache::methodIndexToSignalIndex(int index) const -{ - if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count())) - return index; - - if (index < methodIndexCacheStart) - return _parent->methodIndexToSignalIndex(index); - - return index - methodIndexCacheStart + signalHandlerIndexCacheStart; -} - -QQmlPropertyData * -QQmlPropertyCache::method(int index) const -{ - if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count())) - return 0; - - if (index < methodIndexCacheStart) - return _parent->method(index); - - QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - methodIndexCacheStart)); - return ensureResolved(rv); -} - QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, QObject *object, QQmlContextData *context) const { QQmlData *data = (object ? QQmlData::get(object) : 0); @@ -860,11 +806,11 @@ inline bool contextHasNoExtensions(QQmlContextData *context) return (!context->parent || !context->parent->imports); } -inline int maximumIndexForProperty(QQmlPropertyData *prop, const QQmlVMEMetaObject *vmemo) +inline int maximumIndexForProperty(QQmlPropertyData *prop, const int methodCount, const int signalCount, const int propertyCount) { - return (prop->isFunction() ? vmemo->methodCount() - : prop->isSignalHandler() ? vmemo->signalCount() - : vmemo->propertyCount()); + return prop->isFunction() ? methodCount + : prop->isSignalHandler() ? signalCount + : propertyCount; } } @@ -891,11 +837,15 @@ QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, } if (vmemo) { + const int methodCount = vmemo->methodCount(); + const int signalCount = vmemo->signalCount(); + const int propertyCount = vmemo->propertyCount(); + // Ensure that the property we resolve to is accessible from this meta-object do { const StringCache::mapped_type &property(it.value()); - if (property.first < maximumIndexForProperty(property.second, vmemo)) { + if (property.first < maximumIndexForProperty(property.second, methodCount, signalCount, propertyCount)) { // This property is available in the specified context if (property.second->isFunction() || property.second->isSignalHandler()) { // Prefer the earlier resolution @@ -1296,6 +1246,215 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) } } +namespace { +template <typename StringVisitor, typename TypeInfoVisitor> +int visitMethods(const QMetaObject &mo, int methodOffset, int methodCount, + StringVisitor visitString, TypeInfoVisitor visitTypeInfo) +{ + const int intsPerMethod = 5; + + int fieldsForParameterData = 0; + + bool hasRevisionedMethods = false; + + for (int i = 0; i < methodCount; ++i) { + const int handle = methodOffset + i * intsPerMethod; + + const uint flags = mo.d.data[handle + 4]; + if (flags & MethodRevisioned) + hasRevisionedMethods = true; + + visitString(mo.d.data[handle + 0]); // name + visitString(mo.d.data[handle + 3]); // tag + + const int argc = mo.d.data[handle + 1]; + const int paramIndex = mo.d.data[handle + 2]; + + fieldsForParameterData += argc * 2; // type and name + fieldsForParameterData += 1; // + return type + + // return type + args + for (int i = 0; i < 1 + argc; ++i) { + // type name (maybe) + visitTypeInfo(mo.d.data[paramIndex + i]); + + // parameter name + if (i > 0) + visitString(mo.d.data[paramIndex + argc + i]); + } + } + + int fieldsForRevisions = 0; + if (hasRevisionedMethods) + fieldsForRevisions = methodCount; + + return methodCount * intsPerMethod + fieldsForRevisions + fieldsForParameterData; +} + +template <typename StringVisitor, typename TypeInfoVisitor> +int visitProperties(const QMetaObject &mo, StringVisitor visitString, TypeInfoVisitor visitTypeInfo) +{ + const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data); + const int intsPerProperty = 3; + + bool hasRevisionedProperties = false; + bool hasNotifySignals = false; + + for (int i = 0; i < priv->propertyCount; ++i) { + const int handle = priv->propertyData + i * intsPerProperty; + + const auto flags = mo.d.data[handle + 2]; + if (flags & Revisioned) { + hasRevisionedProperties = true; + } + if (flags & Notify) + hasNotifySignals = true; + + visitString(mo.d.data[handle]); // name + visitTypeInfo(mo.d.data[handle + 1]); + } + + int fieldsForPropertyRevisions = 0; + if (hasRevisionedProperties) + fieldsForPropertyRevisions = priv->propertyCount; + + int fieldsForNotifySignals = 0; + if (hasNotifySignals) + fieldsForNotifySignals = priv->propertyCount; + + return priv->propertyCount * intsPerProperty + fieldsForPropertyRevisions + + fieldsForNotifySignals; +} + +template <typename StringVisitor> +int visitClassInfo(const QMetaObject &mo, StringVisitor visitString) +{ + const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data); + const int intsPerClassInfo = 2; + + for (int i = 0; i < priv->classInfoCount; ++i) { + const int handle = priv->classInfoData + i * intsPerClassInfo; + + visitString(mo.d.data[handle]); // key + visitString(mo.d.data[handle + 1]); // value + } + + return priv->classInfoCount * intsPerClassInfo; +} + +template <typename StringVisitor> +int visitEnumerations(const QMetaObject &mo, StringVisitor visitString) +{ + const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data); + const int intsPerEnumerator = 4; + + int fieldCount = priv->enumeratorCount * intsPerEnumerator; + + for (int i = 0; i < priv->enumeratorCount; ++i) { + const uint *enumeratorData = mo.d.data + priv->enumeratorData + i * intsPerEnumerator; + + const uint keyCount = enumeratorData[2]; + fieldCount += keyCount * 2; + + visitString(enumeratorData[0]); // name + + const uint keyOffset = enumeratorData[3]; + + for (uint j = 0; j < keyCount; ++j) { + visitString(mo.d.data[keyOffset + 2 * j]); + } + } + + return fieldCount; +} + +template <typename StringVisitor> +int countMetaObjectFields(const QMetaObject &mo, StringVisitor stringVisitor) +{ + const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data); + + const auto typeInfoVisitor = [&stringVisitor](uint typeInfo) { + if (typeInfo & IsUnresolvedType) + stringVisitor(typeInfo & TypeNameIndexMask); + }; + + int fieldCount = MetaObjectPrivateFieldCount; + + fieldCount += visitMethods(mo, priv->methodData, priv->methodCount, stringVisitor, + typeInfoVisitor); + fieldCount += visitMethods(mo, priv->constructorData, priv->constructorCount, stringVisitor, + typeInfoVisitor); + + fieldCount += visitProperties(mo, stringVisitor, typeInfoVisitor); + fieldCount += visitClassInfo(mo, stringVisitor); + fieldCount += visitEnumerations(mo, stringVisitor); + + return fieldCount; +} + +} // anonymous namespace + +bool QQmlPropertyCache::determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, + int *stringCount) +{ + const QMetaObjectPrivate *priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data); + if (priv->revision != 7) { + return false; + } + + uint highestStringIndex = 0; + const auto stringIndexVisitor = [&highestStringIndex](uint index) { + highestStringIndex = qMax(highestStringIndex, index); + }; + + *fieldCount = countMetaObjectFields(mo, stringIndexVisitor); + *stringCount = highestStringIndex + 1; + + return true; +} + +bool QQmlPropertyCache::addToHash(QCryptographicHash &hash, const QMetaObject &mo) +{ + int fieldCount = 0; + int stringCount = 0; + if (!determineMetaObjectSizes(mo, &fieldCount, &stringCount)) { + return false; + } + + hash.addData(reinterpret_cast<const char *>(mo.d.data), fieldCount * sizeof(uint)); + for (int i = 0; i < stringCount; ++i) { + const QByteArrayDataPtr data = { const_cast<QByteArrayData*>(&mo.d.stringdata[i]) }; + hash.addData(QByteArray(data)); + } + + return true; +} + +QByteArray QQmlPropertyCache::checksum(bool *ok) +{ + if (!_checksum.isEmpty()) { + *ok = true; + return _checksum; + } + + QCryptographicHash hash(QCryptographicHash::Md5); + + if (_parent) { + hash.addData(_parent->checksum(ok)); + if (!*ok) + return QByteArray(); + } + + if (!addToHash(hash, *createMetaObject())) { + *ok = false; + return QByteArray(); + } + + _checksum = hash.result(); + *ok = !_checksum.isEmpty(); + return _checksum; +} + /*! \internal \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()). This is different from QMetaMethod::methodIndex(). @@ -1451,7 +1610,8 @@ int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *u return type; } -int *QQmlMetaObject::methodParameterTypes(int index, QVarLengthArray<int, 9> &dummy, QByteArray *unknownTypeError) const +int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const { Q_ASSERT(!_m.isNull() && index >= 0); @@ -1506,39 +1666,53 @@ int *QQmlMetaObject::methodParameterTypes(int index, QVarLengthArray<int, 9> &du } else { QMetaMethod m = _m.asT2()->method(index); - int argc = m.parameterCount(); - dummy.resize(argc + 1); - dummy[0] = argc; - QList<QByteArray> argTypeNames; // Only loaded if needed + return methodParameterTypes(m, argStorage, unknownTypeError); - for (int ii = 0; ii < argc; ++ii) { - int type = m.parameterType(ii); - QMetaType::TypeFlags flags = QMetaType::typeFlags(type); - if (flags & QMetaType::IsEnumeration) - type = QVariant::Int; - else if (type == QMetaType::UnknownType || - (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) && - type != qMetaTypeId<QJSValue>())) { - //the UserType clause is to catch registered QFlags) - if (argTypeNames.isEmpty()) - argTypeNames = m.parameterTypes(); - type = EnumType(_m.asT2(), argTypeNames.at(ii), type); - } - if (type == QMetaType::UnknownType) { - if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); - return 0; - } - dummy[ii + 1] = type; - } + } +} - return dummy.data(); +int *QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const +{ + Q_ASSERT(argStorage); + + int argc = m.parameterCount(); + argStorage->resize(argc + 1); + argStorage->operator[](0) = argc; + QList<QByteArray> argTypeNames; // Only loaded if needed + + for (int ii = 0; ii < argc; ++ii) { + int type = m.parameterType(ii); + QMetaType::TypeFlags flags = QMetaType::typeFlags(type); + if (flags & QMetaType::IsEnumeration) + type = QVariant::Int; + else if (type == QMetaType::UnknownType || + (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) && + type != qMetaTypeId<QJSValue>())) { + //the UserType clause is to catch registered QFlags) + if (argTypeNames.isEmpty()) + argTypeNames = m.parameterTypes(); + type = EnumType(_m.asT2(), argTypeNames.at(ii), type); + } + if (type == QMetaType::UnknownType) { + if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); + return 0; + } + argStorage->operator[](ii + 1) = type; } + + return argStorage->data(); } void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv) const { - if (ptr.isT1()) + if (ptr.isNull()) { + const QMetaObject *metaObject = _m.asT2(); + metaObject->d.static_metacall(0, type, index, argv); + } + else if (ptr.isT1()) { QMetaObject::metacall(ptr.asT1(), type, index, argv); + } else { const QMetaObject *metaObject = _m.asT1()->metaObject(); QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type, &metaObject, &index); @@ -1546,4 +1720,11 @@ void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv } } +int *QQmlStaticMetaObject::constructorParameterTypes(int index, ArgTypeStorage *dummy, + QByteArray *unknownTypeError) const +{ + QMetaMethod m = _m.asT2()->constructor(index); + return methodParameterTypes(m, dummy, unknownTypeError); +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 96dbc72f32..750537e707 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -61,9 +61,11 @@ #include <QtCore/qvector.h> #include <private/qv4value_p.h> +#include <private/qqmlaccessors_p.h> QT_BEGIN_NAMESPACE +class QCryptographicHash; class QMetaProperty; class QQmlEngine; class QJSEngine; @@ -72,7 +74,8 @@ class QQmlAccessors; class QMetaObjectBuilder; class QQmlPropertyCacheMethodArguments; class QQmlVMEMetaObject; -class QQmlPropertyCacheCreator; +template <typename T> class QQmlPropertyCacheCreator; +template <typename T> class QQmlPropertyCacheAliasCreator; // We have this somewhat awful split between RawData and Data so that RawData can be // used in unions. In normal code, you should always use Data which initializes RawData @@ -115,9 +118,10 @@ public: IsSignalHandler = 0x00800000, // Function is a signal handler IsOverload = 0x01000000, // Function is an overload of another function IsCloned = 0x02000000, // The function was marked as cloned + IsConstructor = 0x04000000, // The function was marked is a constructor // Internal QQmlPropertyCache flags - NotFullyResolved = 0x04000000, // True if the type data is to be lazily resolved + NotFullyResolved = 0x08000000, // True if the type data is to be lazily resolved // Flags that are set based on the propType field PropTypeFlagMask = IsQObjectDerived | IsEnumType | IsQList | IsQmlBinding | IsQJSValue | @@ -156,12 +160,15 @@ public: bool isSignalHandler() const { return flags & IsSignalHandler; } bool isOverload() const { return flags & IsOverload; } bool isCloned() const { return flags & IsCloned; } + bool isConstructor() const { return flags & IsConstructor; } bool hasOverride() const { return !(flags & IsValueTypeVirtual) && !(flags & HasAccessors) && overrideIndex >= 0; } bool hasRevision() const { return !(flags & HasAccessors) && revision != 0; } + bool isFullyResolved() const { return !(flags & NotFullyResolved); } + // Returns -1 if not a value type virtual property inline int getValueTypeCoreIndex() const; @@ -223,6 +230,13 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyRawData::Flags) class QQmlPropertyData : public QQmlPropertyRawData { public: + enum WriteFlag { + BypassInterceptor = 0x01, + DontRemoveBinding = 0x02, + RemoveBindingOnAliasWrite = 0x04 + }; + Q_DECLARE_FLAGS(WriteFlags, WriteFlag) + inline QQmlPropertyData(); inline QQmlPropertyData(const QQmlPropertyRawData &); @@ -236,6 +250,33 @@ public: void markAsOverrideOf(QQmlPropertyData *predecessor); + inline void readProperty(QObject *target, void *property) const + { + void *args[] = { property, 0 }; + readPropertyWithArgs(target, args); + } + + inline void readPropertyWithArgs(QObject *target, void *args[]) const + { + if (hasAccessors()) { + accessors->read(target, args[0]); + } else { + QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, args); + } + } + + bool writeProperty(QObject *target, void *value, WriteFlags flags) const + { + if (flags.testFlag(BypassInterceptor) && hasAccessors() && accessors->write) { + accessors->write(target, value); + } else { + int status = -1; + void *argv[] = { value, 0, &status, &flags }; + QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex, argv); + } + return true; + } + private: friend class QQmlPropertyCache; void lazyLoad(const QMetaProperty &); @@ -326,6 +367,11 @@ public: void toMetaObjectBuilder(QMetaObjectBuilder &); + static bool determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, int *stringCount); + static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo); + + QByteArray checksum(bool *ok); + protected: virtual void destroy(); virtual void clear(); @@ -333,7 +379,8 @@ protected: private: friend class QQmlEnginePrivate; friend class QQmlCompiler; - friend class QQmlPropertyCacheCreator; + template <typename T> friend class QQmlPropertyCacheCreator; + template <typename T> friend class QQmlPropertyCacheAliasCreator; friend class QQmlComponentAndAliasResolver; friend class QQmlMetaObject; @@ -394,6 +441,7 @@ private: QByteArray _dynamicStringData; QString _defaultPropertyName; QQmlPropertyCacheMethodArguments *argumentsCache; + QByteArray _checksum; }; // QQmlMetaObject serves as a wrapper around either QMetaObject or QQmlPropertyCache. @@ -406,6 +454,8 @@ class QQmlEnginePrivate; class Q_QML_EXPORT QQmlMetaObject { public: + typedef QVarLengthArray<int, 9> ArgTypeStorage; + inline QQmlMetaObject(); inline QQmlMetaObject(QObject *); inline QQmlMetaObject(const QMetaObject *); @@ -425,7 +475,8 @@ public: QQmlPropertyCache *propertyCache(QQmlEnginePrivate *) const; int methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const; - int *methodParameterTypes(int index, QVarLengthArray<int, 9> &dummy, QByteArray *unknownTypeError) const; + int *methodParameterTypes(int index, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const; static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to); @@ -435,6 +486,9 @@ public: protected: QBiPointer<QQmlPropertyCache, const QMetaObject> _m; + int *methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const; + }; class QQmlObjectOrGadget: public QQmlMetaObject @@ -453,6 +507,20 @@ public: private: QBiPointer<QObject, void> ptr; + +protected: + QQmlObjectOrGadget(const QMetaObject* metaObject) + : QQmlMetaObject(metaObject) + {} + +}; + +class QQmlStaticMetaObject : public QQmlObjectOrGadget { +public: + QQmlStaticMetaObject(const QMetaObject* metaObject) + : QQmlObjectOrGadget(metaObject) + {} + int *constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const; }; QQmlPropertyData::QQmlPropertyData() @@ -502,6 +570,21 @@ inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) return p; } +// Returns this property cache's metaObject. May be null if it hasn't been created yet. +inline const QMetaObject *QQmlPropertyCache::metaObject() const +{ + return _metaObject; +} + +// Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by +// QML +inline const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const +{ + while (_parent && (_metaObject == 0 || _ownMetaObject)) + return _parent->firstCppMetaObject(); + return _metaObject; +} + inline QQmlPropertyData *QQmlPropertyCache::property(int index) const { if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count())) @@ -514,6 +597,57 @@ inline QQmlPropertyData *QQmlPropertyCache::property(int index) const return ensureResolved(rv); } +inline QQmlPropertyData *QQmlPropertyCache::method(int index) const +{ + if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count())) + return 0; + + if (index < methodIndexCacheStart) + return _parent->method(index); + + QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - methodIndexCacheStart)); + return ensureResolved(rv); +} + +/*! \internal + \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()). + This is different from QMetaMethod::methodIndex(). +*/ +inline QQmlPropertyData *QQmlPropertyCache::signal(int index) const +{ + if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.count())) + return 0; + + if (index < signalHandlerIndexCacheStart) + return _parent->signal(index); + + QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - signalHandlerIndexCacheStart)); + Q_ASSERT(rv->isSignal() || rv->coreIndex == -1); + return ensureResolved(rv); +} + +inline int QQmlPropertyCache::methodIndexToSignalIndex(int index) const +{ + if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count())) + return index; + + if (index < methodIndexCacheStart) + return _parent->methodIndexToSignalIndex(index); + + return index - methodIndexCacheStart + signalHandlerIndexCacheStart; +} + +// Returns the name of the default property for this cache +inline QString QQmlPropertyCache::defaultPropertyName() const +{ + return _defaultPropertyName; +} + +inline QQmlPropertyCache *QQmlPropertyCache::parent() const +{ + return _parent; +} + QQmlPropertyData * QQmlPropertyCache::overrideData(QQmlPropertyData *data) const { @@ -635,6 +769,51 @@ const QMetaObject *QQmlMetaObject::metaObject() const else return _m.asT2(); } +class QQmlPropertyCacheVector +{ +public: + QQmlPropertyCacheVector() {} + QQmlPropertyCacheVector(QQmlPropertyCacheVector &&other) + : data(std::move(other.data)) {} + QQmlPropertyCacheVector &operator=(QQmlPropertyCacheVector &&other) { + QVector<QFlagPointer<QQmlPropertyCache>> moved(std::move(other.data)); + data.swap(moved); + return *this; + } + + ~QQmlPropertyCacheVector() { clear(); } + void resize(int size) { return data.resize(size); } + int count() const { return data.count(); } + void clear() + { + for (int i = 0; i < data.count(); ++i) { + if (QQmlPropertyCache *cache = data.at(i).data()) + cache->release(); + } + data.clear(); + } + + void append(QQmlPropertyCache *cache) { cache->addref(); data.append(cache); } + QQmlPropertyCache *at(int index) const { return data.at(index).data(); } + void set(int index, QQmlPropertyCache *replacement) { + if (QQmlPropertyCache *oldCache = data.at(index).data()) { + if (replacement == oldCache) + return; + oldCache->release(); + } + data[index] = replacement; + replacement->addref(); + } + + void setNeedsVMEMetaObject(int index) { data[index].setFlag(); } + bool needsVMEMetaObject(int index) const { return data.at(index).flag(); } +private: + Q_DISABLE_COPY(QQmlPropertyCacheVector) + QVector<QFlagPointer<QQmlPropertyCache>> data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyData::WriteFlags) + QT_END_NAMESPACE #endif // QQMLPROPERTYCACHE_P_H diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 739a833a30..c54af43a6f 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -45,14 +45,16 @@ #include <private/qqmlengine_p.h> #include <private/qqmlglobal_p.h> #include <private/qqmlthread_p.h> -#include <private/qqmlcompiler_p.h> #include <private/qqmlcomponent_p.h> #include <private/qqmlprofiler_p.h> #include <private/qqmlmemoryprofiler_p.h> #include <private/qqmltypecompiler_p.h> +#include <private/qqmlpropertyvalidator_p.h> +#include <private/qqmlpropertycachecreator_p.h> #include <QtCore/qdir.h> #include <QtCore/qfile.h> +#include <QtCore/qdatetime.h> #include <QtCore/qdebug.h> #include <QtCore/qmutex.h> #include <QtCore/qthread.h> @@ -62,6 +64,8 @@ #include <QtCore/qwaitcondition.h> #include <QtQml/qqmlextensioninterface.h> +#include <functional> + #if defined (Q_OS_UNIX) #include <sys/types.h> #include <sys/stat.h> @@ -98,6 +102,8 @@ #endif DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS); +DEFINE_BOOL_CONFIG_OPTION(diskCache, QML_DISK_CACHE); +DEFINE_BOOL_CONFIG_OPTION(forceDiskCacheRefresh, QML_FORCE_DISK_CACHE_REFRESH); QT_BEGIN_NAMESPACE @@ -110,8 +116,23 @@ namespace { LockHolder(LockType *l) : lock(*l) { lock.lock(); } ~LockHolder() { lock.unlock(); } }; + + struct DeferredCall + { + std::function<void()> callback; + ~DeferredCall() { callback(); } + }; + + template <typename Callback> + DeferredCall defer(Callback &&cb) + { + DeferredCall c; + c.callback = std::move(cb); + return c; + } } +#ifndef QT_NO_NETWORK // This is a lame object that we need to ensure that slots connected to // QNetworkReply get called in the correct thread (the loader thread). // As QQmlTypeLoader lives in the main thread, and we can't use @@ -131,6 +152,7 @@ public slots: private: QQmlTypeLoader *l; }; +#endif // QT_NO_NETWORK class QQmlTypeLoaderThread : public QQmlThread { @@ -138,9 +160,10 @@ class QQmlTypeLoaderThread : public QQmlThread public: QQmlTypeLoaderThread(QQmlTypeLoader *loader); +#ifndef QT_NO_NETWORK QNetworkAccessManager *networkAccessManager() const; QQmlTypeLoaderNetworkReplyProxy *networkReplyProxy() const; - +#endif // QT_NO_NETWORK void load(QQmlDataBlob *b); void loadAsync(QQmlDataBlob *b); void loadWithStaticData(QQmlDataBlob *b, const QByteArray &); @@ -163,11 +186,13 @@ private: void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri); QQmlTypeLoader *m_loader; +#ifndef QT_NO_NETWORK mutable QNetworkAccessManager *m_networkAccessManager; mutable QQmlTypeLoaderNetworkReplyProxy *m_networkReplyProxy; +#endif // QT_NO_NETWORK }; - +#ifndef QT_NO_NETWORK QQmlTypeLoaderNetworkReplyProxy::QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l) : l(l) { @@ -196,7 +221,7 @@ void QQmlTypeLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply) l->networkReplyProgress(reply, replySize, replySize); l->networkReplyFinished(reply); } - +#endif // QT_NO_NETWORK /*! \class QQmlDataBlob @@ -430,6 +455,39 @@ void QQmlDataBlob::setError(const QList<QQmlError> &errors) tryDone(); } +void QQmlDataBlob::setError(const QQmlCompileError &error) +{ + QQmlError e; + e.setColumn(error.location.column); + e.setLine(error.location.line); + e.setDescription(error.description); + e.setUrl(url()); + setError(e); +} + +void QQmlDataBlob::setError(const QVector<QQmlCompileError> &errors) +{ + QList<QQmlError> finalErrors; + finalErrors.reserve(errors.count()); + for (const QQmlCompileError &error: errors) { + QQmlError e; + e.setColumn(error.location.column); + e.setLine(error.location.line); + e.setDescription(error.description); + e.setUrl(url()); + finalErrors << e; + } + setError(finalErrors); +} + +void QQmlDataBlob::setError(const QString &description) +{ + QQmlError e; + e.setDescription(description); + e.setUrl(finalUrl()); + setError(e); +} + /*! Wait for \a blob to become complete or to error. If \a blob is already complete or in error, or this blob is already complete, this has no effect. @@ -480,6 +538,7 @@ void QQmlDataBlob::done() { } +#ifndef QT_NO_NETWORK /*! Invoked if there is a network error while fetching this blob. @@ -532,6 +591,7 @@ void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError) setError(error); } +#endif // QT_NO_NETWORK /*! Called if \a blob, which was previously waited for, has an error. @@ -730,12 +790,16 @@ void QQmlDataBlob::ThreadData::setProgress(quint8 v) } QQmlTypeLoaderThread::QQmlTypeLoaderThread(QQmlTypeLoader *loader) -: m_loader(loader), m_networkAccessManager(0), m_networkReplyProxy(0) +: m_loader(loader) +#ifndef QT_NO_NETWORK +, m_networkAccessManager(0), m_networkReplyProxy(0) +#endif // QT_NO_NETWORK { // Do that after initializing all the members. startup(); } +#ifndef QT_NO_NETWORK QNetworkAccessManager *QQmlTypeLoaderThread::networkAccessManager() const { Q_ASSERT(isThisThread()); @@ -753,6 +817,7 @@ QQmlTypeLoaderNetworkReplyProxy *QQmlTypeLoaderThread::networkReplyProxy() const Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first return m_networkReplyProxy; } +#endif // QT_NO_NETWORK void QQmlTypeLoaderThread::load(QQmlDataBlob *b) { @@ -810,10 +875,12 @@ void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface, void QQmlTypeLoaderThread::shutdownThread() { +#ifndef QT_NO_NETWORK delete m_networkAccessManager; m_networkAccessManager = 0; delete m_networkReplyProxy; m_networkReplyProxy = 0; +#endif // QT_NO_NETWORK } void QQmlTypeLoaderThread::loadThread(QQmlDataBlob *b) @@ -899,12 +966,14 @@ void QQmlTypeLoader::invalidate() m_thread = 0; } +#ifndef QT_NO_NETWORK // Need to delete the network replies after // the loader thread is shutdown as it could be // getting new replies while we clear them for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter) (*iter)->release(); m_networkReplies.clear(); +#endif // QT_NO_NETWORK } void QQmlTypeLoader::lock() @@ -1065,13 +1134,9 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob) QML_MEMORY_SCOPE_URL(blob->m_url); if (QQmlFile::isSynchronous(blob->m_url)) { - QQmlFile file(m_engine, blob->m_url); - - if (file.isError()) { - QQmlError error; - error.setUrl(blob->m_url); - error.setDescription(file.error()); - blob->setError(error); + const QString fileName = QQmlFile::urlToLocalFileOrQrc(blob->m_url); + if (!QQml_isFileCaseCorrect(fileName)) { + blob->setError(QLatin1String("File name case mismatch")); return; } @@ -1079,10 +1144,10 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob) if (blob->m_data.isAsync()) m_thread->callDownloadProgressChanged(blob, 1.); - setData(blob, &file); + setData(blob, fileName); } else { - +#ifndef QT_NO_NETWORK QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url)); QQmlTypeLoaderNetworkReplyProxy *nrp = m_thread->networkReplyProxy(); blob->addref(); @@ -1099,14 +1164,15 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob) #ifdef DATABLOB_DEBUG qWarning("QQmlDataBlob: requested %s", qPrintable(blob->url().toString())); -#endif - +#endif // DATABLOB_DEBUG +#endif // QT_NO_NETWORK } } #define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16 #define TYPELOADER_MINIMUM_TRIM_THRESHOLD 64 +#ifndef QT_NO_NETWORK void QQmlTypeLoader::networkReplyFinished(QNetworkReply *reply) { Q_ASSERT(m_thread->isThisThread()); @@ -1162,6 +1228,7 @@ void QQmlTypeLoader::networkReplyProgress(QNetworkReply *reply, m_thread->callDownloadProgressChanged(blob, blob->m_data.progress()); } } +#endif // QT_NO_NETWORK /*! Return the QQmlEngine associated with this loader @@ -1197,11 +1264,11 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) setData(blob, d); } -void QQmlTypeLoader::setData(QQmlDataBlob *blob, QQmlFile *file) +void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName) { QML_MEMORY_SCOPE_URL(blob->url()); QQmlDataBlob::Data d; - d.d = file; + d.d = &fileName; setData(blob, d); } @@ -1252,7 +1319,7 @@ void QQmlTypeLoader::shutdownThread() } QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader) - : QQmlDataBlob(url, type, loader), m_importCache(loader), m_isSingleton(false) + : QQmlDataBlob(url, type, loader), m_importCache(loader) { } @@ -1416,51 +1483,6 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL return true; } -bool QQmlTypeLoader::Blob::addPragma(const QmlIR::Pragma &pragma, QList<QQmlError> *errors) -{ - Q_ASSERT(errors); - - if (pragma.type == QmlIR::Pragma::PragmaSingleton) { - QUrl myUrl = finalUrl(); - - QQmlType *ret = QQmlMetaType::qmlType(myUrl, true); - if (!ret) { - QQmlError error; - error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent.")); - error.setUrl(myUrl); - error.setLine(pragma.location.line); - error.setColumn(pragma.location.column); - errors->prepend(error); - return false; - } - - if (!ret->isCompositeSingleton()) { - QQmlError error; - error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(ret->qmlTypeName())); - error.setUrl(myUrl); - error.setLine(pragma.location.line); - error.setColumn(pragma.location.column); - errors->prepend(error); - return false; - } - // This flag is used for error checking when a qmldir file marks a type as - // composite singleton, but there is no pragma Singleton defined in QML. - m_isSingleton = true; - } else { - QQmlError error; - error.setDescription(QLatin1String("Invalid pragma")); - error.setUrl(finalUrl()); - error.setLine(pragma.location.line); - error.setColumn(pragma.location.column); - errors->prepend(error); - return false; - } - - return true; -} - - - void QQmlTypeLoader::Blob::dependencyError(QQmlDataBlob *blob) { if (blob->type() == QQmlDataBlob::QmldirFile) { @@ -1950,7 +1972,9 @@ void QQmlTypeLoader::trimCache() QList<TypeCache::Iterator> unneededTypes; for (TypeCache::Iterator iter = m_typeCache.begin(), end = m_typeCache.end(); iter != end; ++iter) { QQmlTypeData *typeData = iter.value(); - if (typeData->m_compiledData && typeData->count() == 1 + // typeData->m_compiledData may be set early on in the proccess of loading a file, so it's important + // to check the general loading status of the typeData before making any other decisions. + if (typeData->isComplete() && typeData->m_compiledData && typeData->count() == 1 && typeData->m_compiledData->count() == 1) { // There are no live objects of this type unneededTypes.append(iter); @@ -1992,7 +2016,7 @@ QQmlTypeData::TypeDataCallback::~TypeDataCallback() QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager) : QQmlTypeLoader::Blob(url, QmlFile, manager), - m_typesResolved(false), m_compiledData(0), m_implicitImport(0), m_implicitImportLoaded(false) + m_typesResolved(false), m_implicitImportLoaded(false) { } @@ -2005,14 +2029,11 @@ QQmlTypeData::~QQmlTypeData() if (QQmlTypeData *tdata = m_compositeSingletons.at(ii).typeData) tdata->release(); } - for (QHash<int, TypeReference>::ConstIterator it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); + for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end; ++it) { if (QQmlTypeData *tdata = it->typeData) tdata->release(); } - - if (m_compiledData) - m_compiledData->release(); } const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const @@ -2020,19 +2041,9 @@ const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() cons return m_scripts; } -const QSet<QString> &QQmlTypeData::namespaces() const -{ - return m_namespaces; -} - -const QList<QQmlTypeData::TypeReference> &QQmlTypeData::compositeSingletons() const +QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const { - return m_compositeSingletons; -} - -QQmlCompiledData *QQmlTypeData::compiledData() const -{ - return m_compiledData; + return m_compiledData.data(); } void QQmlTypeData::registerCallback(TypeDataCallback *callback) @@ -2048,10 +2059,120 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback) Q_ASSERT(!m_callbacks.contains(callback)); } +bool QQmlTypeData::tryLoadFromDiskCache() +{ + if (!diskCache()) + return false; + + if (forceDiskCacheRefresh()) + return false; + + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine()); + if (!v4) + return false; + + QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading(); + { + QString error; + if (!unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) { + qDebug() << "Error loading" << url().toString() << "from disk cache:" << error; + return false; + } + } + + m_compiledData = unit; + + for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) + m_typeReferences.collectFromObject(m_compiledData->objectAt(i)); + + m_importCache.setBaseUrl(finalUrl(), finalUrlString()); + + // For remote URLs, we don't delay the loading of the implicit import + // because the loading probably requires an asynchronous fetch of the + // qmldir (so we can't load it just in time). + if (!finalUrl().scheme().isEmpty()) { + QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir"))); + if (!QQmlImports::isLocal(qmldirUrl)) { + if (!loadImplicitImport()) + return false; + + // find the implicit import + for (quint32 i = 0; i < m_compiledData->data->nImports; ++i) { + const QV4::CompiledData::Import *import = m_compiledData->data->importAt(i); + if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".") + && import->qualifierIndex == 0 + && import->majorVersion == -1 + && import->minorVersion == -1) { + QList<QQmlError> errors; + if (!fetchQmldir(qmldirUrl, import, 1, &errors)) { + setError(errors); + return false; + } + break; + } + } + } + } + + for (int i = 0, count = m_compiledData->data->nImports; i < count; ++i) { + const QV4::CompiledData::Import *import = m_compiledData->data->importAt(i); + QList<QQmlError> errors; + if (!addImport(import, &errors)) { + Q_ASSERT(errors.size()); + QQmlError error(errors.takeFirst()); + error.setUrl(m_importCache.baseUrl()); + error.setLine(import->location.line); + error.setColumn(import->location.column); + errors.prepend(error); // put it back on the list after filling out information. + setError(errors); + return false; + } + } + + return true; +} + +void QQmlTypeData::rebuildTypeAndPropertyCaches() +{ + Q_ASSERT(m_compiledData); + + QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); + + { + QQmlCompileError error = buildTypeResolutionCaches(&m_compiledData->importCache, &m_compiledData->resolvedTypes); + if (error.isSet()) { + setError(error); + return; + } + } + + { + QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(&m_compiledData->propertyCaches, engine, m_compiledData, &m_importCache); + QQmlCompileError error = propertyCacheCreator.buildMetaObjects(); + if (error.isSet()) { + setError(error); + return; + } + } + + QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> aliasCreator(&m_compiledData->propertyCaches, m_compiledData); + aliasCreator.appendAliasPropertiesToMetaObjects(); +} + void QQmlTypeData::done() { + auto cleanup = defer([this]{ + m_document.reset(); + m_typeReferences.clear(); + if (isError()) + m_compiledData = nullptr; + }); + + if (isError()) + return; + // Check all script dependencies for errors - for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) { + for (int ii = 0; ii < m_scripts.count(); ++ii) { const ScriptReference &script = m_scripts.at(ii); Q_ASSERT(script.script->isCompleteOrError()); if (script.script->isError()) { @@ -2063,16 +2184,17 @@ void QQmlTypeData::done() error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString())); errors.prepend(error); setError(errors); + return; } } // Check all type dependencies for errors - for (QHash<int, TypeReference>::ConstIterator it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); - !isError() && it != end; ++it) { + for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end; + ++it) { const TypeReference &type = *it; Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); if (type.typeData && type.typeData->isError()) { - QString typeName = m_document->stringAt(it.key()); + const QString typeName = stringAt(it.key()); QList<QQmlError> errors = type.typeData->errors(); QQmlError error; @@ -2082,11 +2204,12 @@ void QQmlTypeData::done() error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); errors.prepend(error); setError(errors); + return; } } // Check all composite singleton type dependencies for errors - for (int ii = 0; !isError() && ii < m_compositeSingletons.count(); ++ii) { + for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) { const TypeReference &type = m_compositeSingletons.at(ii); Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); if (type.typeData && type.typeData->isError()) { @@ -2100,27 +2223,81 @@ void QQmlTypeData::done() error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); errors.prepend(error); setError(errors); + return; } } - // If the type is CompositeSingleton but there was no pragma Singleton in the - // QML file, lets report an error. - QQmlType *type = QQmlMetaType::qmlType(url(), true); - if (!isError() && type && type->isCompositeSingleton() && !m_isSingleton) { - QString typeName = type->qmlTypeName(); + if (!m_document.isNull()) { + // Compile component + compile(); + } else { + rebuildTypeAndPropertyCaches(); + } - QQmlError error; - error.setDescription(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName)); - error.setUrl(finalUrl()); - setError(error); + if (isError()) + return; + + { + QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); + { + // Sanity check property bindings + QQmlPropertyValidator validator(engine, m_importCache, m_compiledData); + QVector<QQmlCompileError> errors = validator.validate(); + if (!errors.isEmpty()) { + setError(errors); + return; + } + } + + m_compiledData->finalize(engine); } - // Compile component - if (!isError()) - compile(); + { + QQmlType *type = QQmlMetaType::qmlType(finalUrl(), true); + if (m_compiledData && m_compiledData->data->flags & QV4::CompiledData::Unit::IsSingleton) { + if (!type) { + QQmlError error; + error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent.")); + setError(error); + return; + } else if (!type->isCompositeSingleton()) { + QQmlError error; + error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type->qmlTypeName())); + setError(error); + return; + } + } else { + // If the type is CompositeSingleton but there was no pragma Singleton in the + // QML file, lets report an error. + if (type && type->isCompositeSingleton()) { + QString typeName = type->qmlTypeName(); + setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName)); + return; + } + } + } + + { + // Collect imported scripts + m_compiledData->dependentScripts.reserve(m_scripts.count()); + for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { + const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex); + + QStringRef qualifier(&script.qualifier); + QString enclosingNamespace; + + const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.')); + if (lastDotIndex != -1) { + enclosingNamespace = qualifier.left(lastDotIndex).toString(); + qualifier = qualifier.mid(lastDotIndex+1); + } - m_document.reset(); - m_implicitImport = 0; + m_compiledData->importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); + QQmlScriptData *scriptData = script.script->scriptData(); + scriptData->addref(); + m_compiledData->dependentScripts << scriptData; + } + } } void QQmlTypeData::completed() @@ -2155,9 +2332,19 @@ bool QQmlTypeData::loadImplicitImport() void QQmlTypeData::dataReceived(const Data &data) { - QString code = QString::fromUtf8(data.data(), data.size()); + if (tryLoadFromDiskCache()) + return; + + qint64 sourceTimeStamp; + QString error; + QString code = QString::fromUtf8(data.readAll(&error, &sourceTimeStamp)); + if (!error.isEmpty()) { + setError(error); + return; + } QQmlEngine *qmlEngine = typeLoader()->engine(); - m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger != 0)); + m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger() != 0)); + m_document->jsModule.sourceTimeStamp = sourceTimeStamp; QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames()); if (!compiler.generateFromQml(code, finalUrlString(), m_document.data())) { QList<QQmlError> errors; @@ -2180,14 +2367,14 @@ void QQmlTypeData::dataReceived(const Data &data) void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit) { QQmlEngine *qmlEngine = typeLoader()->engine(); - m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger != 0)); + m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger() != 0)); unit->loadIR(m_document.data(), unit); continueLoadFromIR(); } void QQmlTypeData::continueLoadFromIR() { - m_document->collectTypeReferences(); + m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd()); m_importCache.setBaseUrl(finalUrl(), finalUrlString()); // For remote URLs, we don't delay the loading of the implicit import @@ -2200,14 +2387,14 @@ void QQmlTypeData::continueLoadFromIR() return; // This qmldir is for the implicit import QQmlJS::MemoryPool *pool = m_document->jsParserEngine.pool(); - m_implicitImport = pool->New<QV4::CompiledData::Import>(); - m_implicitImport->uriIndex = m_document->registerString(QLatin1String(".")); - m_implicitImport->qualifierIndex = 0; // empty string - m_implicitImport->majorVersion = -1; - m_implicitImport->minorVersion = -1; + auto implicitImport = pool->New<QV4::CompiledData::Import>(); + implicitImport->uriIndex = m_document->registerString(QLatin1String(".")); + implicitImport->qualifierIndex = 0; // empty string + implicitImport->majorVersion = -1; + implicitImport->minorVersion = -1; QList<QQmlError> errors; - if (!fetchQmldir(qmldirUrl, m_implicitImport, 1, &errors)) { + if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) { setError(errors); return; } @@ -2228,14 +2415,6 @@ void QQmlTypeData::continueLoadFromIR() return; } } - - foreach (QmlIR::Pragma *pragma, m_document->pragmas) { - if (!addPragma(*pragma, &errors)) { - Q_ASSERT(errors.size()); - setError(errors); - return; - } - } } void QQmlTypeData::allDependenciesDone() @@ -2280,20 +2459,34 @@ void QQmlTypeData::downloadProgressChanged(qreal p) QString QQmlTypeData::stringAt(int index) const { + if (m_compiledData) + return m_compiledData->stringAt(index); return m_document->jsGenerator.stringTable.stringForIndex(index); } void QQmlTypeData::compile() { - Q_ASSERT(m_compiledData == 0); + Q_ASSERT(m_compiledData.isNull()); - m_compiledData = new QQmlCompiledData(typeLoader()->engine()); + QQmlRefPointer<QQmlTypeNameCache> importCache; + QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache; + QQmlCompileError error = buildTypeResolutionCaches(&importCache, &resolvedTypeCache); + if (error.isSet()) { + setError(error); + return; + } - QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), m_compiledData, this, m_document.data()); - if (!compiler.compile()) { + QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), this, m_document.data(), importCache, resolvedTypeCache); + m_compiledData = compiler.compile(); + if (!m_compiledData) { setError(compiler.compilationErrors()); - m_compiledData->release(); - m_compiledData = 0; + return; + } + if (diskCache() || forceDiskCacheRefresh()) { + QString errorString; + if (!m_compiledData->saveToDisk(&errorString)) { + qDebug() << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString; + } } } @@ -2346,7 +2539,7 @@ void QQmlTypeData::resolveTypes() } } - for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_document->typeReferences.constBegin(), end = m_document->typeReferences.constEnd(); + for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd(); unresolvedRef != end; ++unresolvedRef) { TypeReference ref; // resolved reference @@ -2416,6 +2609,57 @@ void QQmlTypeData::resolveTypes() } } +QQmlCompileError QQmlTypeData::buildTypeResolutionCaches( + QQmlRefPointer<QQmlTypeNameCache> *importCache, + QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache + ) const +{ + importCache->adopt(new QQmlTypeNameCache); + + for (const QString &ns: m_namespaces) + (*importCache)->add(ns); + + // Add any Composite Singletons that were used to the import cache + for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons) + (*importCache)->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); + + m_importCache.populateCache(*importCache); + + QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); + + for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) { + QScopedPointer<QV4::CompiledData::ResolvedTypeReference> ref(new QV4::CompiledData::ResolvedTypeReference); + QQmlType *qmlType = resolvedType->type; + if (resolvedType->typeData) { + if (resolvedType->needsCreation && qmlType->isCompositeSingleton()) { + return QQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType->qmlTypeName())); + } + ref->compilationUnit = resolvedType->typeData->compilationUnit(); + } else if (qmlType) { + ref->type = qmlType; + Q_ASSERT(ref->type); + + if (resolvedType->needsCreation && !ref->type->isCreatable()) { + QString reason = ref->type->noCreationReason(); + if (reason.isEmpty()) + reason = tr("Element is not creatable."); + return QQmlCompileError(resolvedType->location, reason); + } + + if (ref->type->containsRevisionedAttributes()) { + ref->typePropertyCache = engine->cache(ref->type, + resolvedType->minorVersion); + } + } + ref->majorVersion = resolvedType->majorVersion; + ref->minorVersion = resolvedType->minorVersion; + ref->doDynamicTypeCheck(); + resolvedTypeCache->insert(resolvedType.key(), ref.take()); + } + QQmlCompileError noError; + return noError; +} + bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref) { QQmlImportNamespace *typeNamespace = 0; @@ -2537,10 +2781,6 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent ctxt->importedScripts = effectiveCtxt->importedScripts; } - if (ctxt->imports) { - ctxt->imports->addref(); - } - if (effectiveCtxt) { ctxt->setParent(effectiveCtxt, true); } else { @@ -2628,10 +2868,29 @@ struct EmptyCompilationUnit : public QV4::CompiledData::CompilationUnit void QQmlScriptBlob::dataReceived(const Data &data) { - QString source = QString::fromUtf8(data.data(), data.size()); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); - QmlIR::Document irUnit(v4->debugger != 0); + + if (diskCache() && !forceDiskCacheRefresh()) { + QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading(); + QString error; + if (unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) { + initializeFromCompilationUnit(unit); + return; + } else { + qDebug() << "Error loading" << url().toString() << "from disk cache:" << error; + } + } + + + QmlIR::Document irUnit(v4->debugger() != 0); + + QString error; + QString source = QString::fromUtf8(data.readAll(&error, &irUnit.jsModule.sourceTimeStamp)); + if (!error.isEmpty()) { + setError(error); + return; + } + QmlIR::ScriptDirectivesCollector collector(&irUnit.jsParserEngine, &irUnit.jsGenerator); QList<QQmlError> errors; @@ -2656,6 +2915,13 @@ void QQmlScriptBlob::dataReceived(const Data &data) // The js unit owns the data and will free the qml unit. unit->data = unitData; + if (diskCache() || forceDiskCacheRefresh()) { + QString errorString; + if (!unit->saveToDisk(&errorString)) { + qDebug() << "Error saving cached version of" << unit->url().toString() << "to disk:" << errorString; + } + } + initializeFromCompilationUnit(unit); } @@ -2666,8 +2932,11 @@ void QQmlScriptBlob::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit * void QQmlScriptBlob::done() { + if (isError()) + return; + // Check all script dependencies for errors - for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) { + for (int ii = 0; ii < m_scripts.count(); ++ii) { const ScriptReference &script = m_scripts.at(ii); Q_ASSERT(script.script->isCompleteOrError()); if (script.script->isError()) { @@ -2679,17 +2948,15 @@ void QQmlScriptBlob::done() error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString())); errors.prepend(error); setError(errors); + return; } } - if (isError()) - return; - m_scriptData->importCache = new QQmlTypeNameCache(); QSet<QString> ns; - for (int scriptIndex = 0; !isError() && scriptIndex < m_scripts.count(); ++scriptIndex) { + for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { const ScriptReference &script = m_scripts.at(scriptIndex); m_scriptData->scripts.append(script.script); @@ -2783,7 +3050,12 @@ void QQmlQmldirData::setPriority(int priority) void QQmlQmldirData::dataReceived(const Data &data) { - m_content = QString::fromUtf8(data.data(), data.size()); + QString error; + m_content = QString::fromUtf8(data.readAll(&error)); + if (!error.isEmpty()) { + setError(error); + return; + } } void QQmlQmldirData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *) @@ -2791,6 +3063,30 @@ void QQmlQmldirData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit * Q_UNIMPLEMENTED(); } +QByteArray QQmlDataBlob::Data::readAll(QString *error, qint64 *sourceTimeStamp) const +{ + Q_ASSERT(!d.isNull()); + error->clear(); + if (d.isT1()) { + if (sourceTimeStamp) + *sourceTimeStamp = 0; + return *d.asT1(); + } + QFile f(*d.asT2()); + if (!f.open(QIODevice::ReadOnly)) { + *error = f.errorString(); + return QByteArray(); + } + if (sourceTimeStamp) + *sourceTimeStamp = QFileInfo(f).lastModified().toMSecsSinceEpoch(); + QByteArray data(f.size(), Qt::Uninitialized); + if (f.read(data.data(), data.length()) != data.length()) { + *error = f.errorString(); + return QByteArray(); + } + return data; +} + QT_END_NAMESPACE #include "qqmltypeloader.moc" diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 51228eacc7..ab6b046fcf 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -53,7 +53,9 @@ #include <QtCore/qobject.h> #include <QtCore/qatomic.h> +#ifndef QT_NO_NETWORK #include <QtNetwork/qnetworkreply.h> +#endif #include <QtQml/qqmlerror.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlfile.h> @@ -75,11 +77,11 @@ class QQmlScriptData; class QQmlScriptBlob; class QQmlQmldirData; class QQmlTypeLoader; -class QQmlCompiledData; class QQmlComponentPrivate; class QQmlTypeData; class QQmlTypeLoader; class QQmlExtensionInterface; +struct QQmlCompileError; namespace QmlIR { struct Document; @@ -129,34 +131,32 @@ public: class Data { public: - inline const char *data() const; - inline int size() const; - - inline QByteArray asByteArray() const; - - inline bool isFile() const; - inline QQmlFile *asFile() const; - + QByteArray readAll(QString *error, qint64 *sourceTimeStamp = 0) const; private: friend class QQmlDataBlob; friend class QQmlTypeLoader; inline Data(); Data(const Data &); Data &operator=(const Data &); - QBiPointer<const QByteArray, QQmlFile> d; + QBiPointer<const QByteArray, const QString> d; }; protected: // Can be called from within callbacks void setError(const QQmlError &); void setError(const QList<QQmlError> &errors); + void setError(const QQmlCompileError &error); + void setError(const QVector<QQmlCompileError> &errors); + void setError(const QString &description); void addDependency(QQmlDataBlob *); // Callbacks made in load thread virtual void dataReceived(const Data &) = 0; virtual void initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit*) = 0; virtual void done(); +#ifndef QT_NO_NETWORK virtual void networkError(QNetworkReply::NetworkError); +#endif virtual void dependencyError(QQmlDataBlob *); virtual void dependencyComplete(QQmlDataBlob *); virtual void allDependenciesDone(); @@ -233,7 +233,6 @@ public: protected: bool addImport(const QV4::CompiledData::Import *import, QList<QQmlError> *errors); - bool addPragma(const QmlIR::Pragma &pragma, QList<QQmlError> *errors); bool fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors); bool updateQmldir(QQmlQmldirData *data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors); @@ -250,7 +249,6 @@ public: virtual QString stringAt(int) const { return QString(); } QQmlImports m_importCache; - bool m_isSingleton; QHash<const QV4::CompiledData::Import*, int> m_unresolvedImports; QList<QQmlQmldirData *> m_qmldirs; }; @@ -320,20 +318,24 @@ public: private: friend class QQmlDataBlob; friend class QQmlTypeLoaderThread; +#ifndef QT_NO_NETWORK friend class QQmlTypeLoaderNetworkReplyProxy; +#endif // QT_NO_NETWORK void shutdownThread(); void loadThread(QQmlDataBlob *); void loadWithStaticDataThread(QQmlDataBlob *, const QByteArray &); void loadWithCachedUnitThread(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit); +#ifndef QT_NO_NETWORK void networkReplyFinished(QNetworkReply *); void networkReplyProgress(QNetworkReply *, qint64, qint64); typedef QHash<QNetworkReply *, QQmlDataBlob *> NetworkReplies; +#endif void setData(QQmlDataBlob *, const QByteArray &); - void setData(QQmlDataBlob *, QQmlFile *); + void setData(QQmlDataBlob *, const QString &fileName); void setData(QQmlDataBlob *, const QQmlDataBlob::Data &); void setCachedUnit(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit); @@ -362,7 +364,9 @@ private: QQmlEngine *m_engine; QQmlTypeLoaderThread *m_thread; +#ifndef QT_NO_NETWORK NetworkReplies m_networkReplies; +#endif TypeCache m_typeCache; int m_typeCacheTrimThreshold; ScriptCache m_scriptCache; @@ -381,6 +385,7 @@ private: class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob { + Q_DECLARE_TR_FUNCTIONS(QQmlTypeData) public: struct TypeReference { @@ -412,13 +417,9 @@ private: public: ~QQmlTypeData(); - const QHash<int, TypeReference> &resolvedTypeRefs() const { return m_resolvedTypes; } - const QList<ScriptReference> &resolvedScripts() const; - const QSet<QString> &namespaces() const; - const QList<TypeReference> &compositeSingletons() const; - QQmlCompiledData *compiledData() const; + QV4::CompiledData::CompilationUnit *compilationUnit() const; // Used by QQmlComponent to get notifications struct TypeDataCallback { @@ -440,14 +441,21 @@ protected: virtual QString stringAt(int index) const; private: + bool tryLoadFromDiskCache(); void continueLoadFromIR(); void resolveTypes(); + QQmlCompileError buildTypeResolutionCaches( + QQmlRefPointer<QQmlTypeNameCache> *importCache, + QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache + ) const; void compile(); + void rebuildTypeAndPropertyCaches(); bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref); virtual void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace); QScopedPointer<QmlIR::Document> m_document; + QV4::CompiledData::TypeReferenceMap m_typeReferences; QList<ScriptReference> m_scripts; @@ -458,11 +466,10 @@ private: QHash<int, TypeReference> m_resolvedTypes; bool m_typesResolved:1; - QQmlCompiledData *m_compiledData; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compiledData; QList<TypeDataCallback *> m_callbacks; - QV4::CompiledData::Import *m_implicitImport; bool m_implicitImportLoaded; bool loadImplicitImport(); }; @@ -572,40 +579,7 @@ QQmlDataBlob::Data::Data() { } -const char *QQmlDataBlob::Data::data() const -{ - Q_ASSERT(!d.isNull()); - - if (d.isT1()) return d.asT1()->constData(); - else return d.asT2()->data(); -} - -int QQmlDataBlob::Data::size() const -{ - Q_ASSERT(!d.isNull()); - - if (d.isT1()) return d.asT1()->size(); - else return d.asT2()->size(); -} - -bool QQmlDataBlob::Data::isFile() const -{ - return d.isT2(); -} - -QByteArray QQmlDataBlob::Data::asByteArray() const -{ - Q_ASSERT(!d.isNull()); - - if (d.isT1()) return *d.asT1(); - else return d.asT2()->dataByteArray(); -} -QQmlFile *QQmlDataBlob::Data::asFile() const -{ - if (d.isT2()) return d.asT2(); - else return 0; -} QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index 44fd47244d..8e87ec7f63 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -219,7 +219,7 @@ void QQmlValueType::read(QObject *obj, int idx) QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); } -void QQmlValueType::write(QObject *obj, int idx, QQmlPropertyPrivate::WriteFlags flags) +void QQmlValueType::write(QObject *obj, int idx, QQmlPropertyData::WriteFlags flags) { Q_ASSERT(gadgetPtr); int status = -1; diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h index 910d39cf0a..11e1dfdb00 100644 --- a/src/qml/qml/qqmlvaluetype_p.h +++ b/src/qml/qml/qqmlvaluetype_p.h @@ -69,7 +69,7 @@ public: QQmlValueType(int userType, const QMetaObject *metaObject); ~QQmlValueType(); void read(QObject *, int); - void write(QObject *, int, QQmlPropertyPrivate::WriteFlags flags); + void write(QObject *, int, QQmlPropertyData::WriteFlags flags); QVariant value(); void setValue(const QVariant &); diff --git a/src/qml/qml/qqmlvaluetypeproxybinding.cpp b/src/qml/qml/qqmlvaluetypeproxybinding.cpp index 6858215a79..595cd01d05 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding.cpp +++ b/src/qml/qml/qqmlvaluetypeproxybinding.cpp @@ -58,7 +58,7 @@ QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding() } } -void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) +void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) { QQmlAbstractBinding *b = m_bindings.data(); while (b) { diff --git a/src/qml/qml/qqmlvaluetypeproxybinding_p.h b/src/qml/qml/qqmlvaluetypeproxybinding_p.h index de5acc2984..6e297bb3ea 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding_p.h +++ b/src/qml/qml/qqmlvaluetypeproxybinding_p.h @@ -63,7 +63,7 @@ public: QQmlAbstractBinding *binding(int targetPropertyIndex); void removeBindings(quint32 mask); - virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags); + virtual void setEnabled(bool, QQmlPropertyData::WriteFlags); virtual bool isValueTypeProxy() const; protected: diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 04a556f46c..b69eea61c5 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -50,6 +50,7 @@ #include <private/qv4functionobject_p.h> #include <private/qv4variantobject_p.h> #include <private/qv4alloca_p.h> +#include <private/qv4objectiterator_p.h> #include <private/qv4qobjectwrapper_p.h> QT_BEGIN_NAMESPACE @@ -245,6 +246,34 @@ PropertyAttributes QQmlValueTypeWrapper::query(const Managed *m, String *name) return result ? Attr_Data : Attr_Invalid; } +void QQmlValueTypeWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) +{ + name->setM(0); + *index = UINT_MAX; + + QQmlValueTypeWrapper *that = static_cast<QQmlValueTypeWrapper*>(m); + + if (QQmlValueTypeReference *ref = that->as<QQmlValueTypeReference>()) { + if (!ref->readReferenceValue()) + return; + } + + if (that->d()->propertyCache) { + const QMetaObject *mo = that->d()->propertyCache->createMetaObject(); + const int propertyCount = mo->propertyCount(); + if (it->arrayIndex < static_cast<uint>(propertyCount)) { + Scope scope(that->engine()); + ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(mo->property(it->arrayIndex).name()))); + name->setM(propName->d()); + ++it->arrayIndex; + *attributes = QV4::Attr_Data; + p->value = that->QV4::Object::get(propName); + return; + } + } + QV4::Object::advanceIterator(m, it, name, index, p, attributes); +} + bool QQmlValueTypeWrapper::isEqual(const QVariant& value) { if (QQmlValueTypeReference *ref = as<QQmlValueTypeReference>()) @@ -303,8 +332,7 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(CallContext *ctx) if (QMetaType::convert(w->d()->gadgetPtr, w->d()->valueType->typeId, &convertResult, QMetaType::QString)) { result = convertResult; } else { - result = QString::fromUtf8(QMetaType::typeName(w->d()->valueType->typeId)); - result += QLatin1Char('('); + result = QString::fromUtf8(QMetaType::typeName(w->d()->valueType->typeId)) + QLatin1Char('('); const QMetaObject *mo = w->d()->propertyCache->metaObject(); const int propCount = mo->propertyCount(); for (int i = 0; i < propCount; ++i) { @@ -431,7 +459,7 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); bindingFunction->initBindingLocation(); - QQmlBinding *newBinding = new QQmlBinding(value, reference->d()->object, context); + QQmlBinding *newBinding = QQmlBinding::create(&cacheData, value, reference->d()->object, context); newBinding->setTarget(reference->d()->object, cacheData); QQmlPropertyPrivate::setBinding(newBinding); return; diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index c2861f5bfa..94eeba366a 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -99,6 +99,7 @@ public: static void put(Managed *m, String *name, const Value &value); static bool isEqualTo(Managed *m, Managed *other); static PropertyAttributes query(const Managed *, String *name); + static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static QV4::ReturnedValue method_toString(CallContext *ctx); diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp index 5f9fa69944..01c4f476d6 100644 --- a/src/qml/qml/qqmlvme.cpp +++ b/src/qml/qml/qqmlvme.cpp @@ -39,7 +39,6 @@ #include "qqmlvme_p.h" -#include "qqmlcompiler_p.h" #include "qqmlboundsignal_p.h" #include "qqmlstringconverters_p.h" #include <private/qmetaobjectbuilder_p.h> diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h index ac9db5c046..99d63380ad 100644 --- a/src/qml/qml/qqmlvme_p.h +++ b/src/qml/qml/qqmlvme_p.h @@ -69,7 +69,6 @@ QT_BEGIN_NAMESPACE class QObject; class QJSValue; class QQmlScriptData; -class QQmlCompiledData; class QQmlContextData; namespace QQmlVMETypes { @@ -84,10 +83,9 @@ namespace QQmlVMETypes { struct State { enum Flag { Deferred = 0x00000001 }; - State() : flags(0), context(0), compiledData(0), instructionStream(0) {} + State() : flags(0), context(0), instructionStream(0) {} quint32 flags; QQmlContextData *context; - QQmlCompiledData *compiledData; const char *instructionStream; QBitField bindingSkipList; }; diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 775309d04a..d5001674ad 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -59,6 +59,32 @@ QT_BEGIN_NAMESPACE +static void list_append(QQmlListProperty<QObject> *prop, QObject *o) +{ + QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); + list->append(o); + static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), 0); +} + +static int list_count(QQmlListProperty<QObject> *prop) +{ + QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); + return list->count(); +} + +static QObject *list_at(QQmlListProperty<QObject> *prop, int index) +{ + QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); + return list->at(index); +} + +static void list_clear(QQmlListProperty<QObject> *prop) +{ + QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); + list->clear(); + static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), 0); +} + QQmlVMEVariantQObjectPtr::QQmlVMEVariantQObjectPtr() : QQmlGuard<QObject>(0), m_target(0), m_index(-1) { @@ -74,10 +100,10 @@ void QQmlVMEVariantQObjectPtr::objectDestroyed(QObject *) return; if (m_index >= 0) { - QV4::ExecutionEngine *v4 = m_target->properties.engine(); + QV4::ExecutionEngine *v4 = m_target->propertyAndMethodStorage.engine(); if (v4) { QV4::Scope scope(v4); - QV4::Scoped<QV4::MemberData> sp(scope, m_target->properties.value()); + QV4::Scoped<QV4::MemberData> sp(scope, m_target->propertyAndMethodStorage.value()); if (sp) *(sp->data() + m_index) = QV4::Primitive::nullValue(); } @@ -115,22 +141,32 @@ void QQmlVMEMetaObjectEndpoint_callback(QQmlNotifierEndpoint *e, void **) void QQmlVMEMetaObjectEndpoint::tryConnect() { + Q_ASSERT(metaObject->compiledObject); int aliasId = this - metaObject->aliasEndpoints; if (metaObject.flag()) { // This is actually notify - int sigIdx = metaObject->methodOffset() + aliasId + metaObject->metaData->propertyCount; + int sigIdx = metaObject->methodOffset() + aliasId + metaObject->compiledObject->nProperties; metaObject->activate(metaObject->object, sigIdx, 0); } else { - QQmlVMEMetaData::AliasData *d = metaObject->metaData->aliasData() + aliasId; - if (!d->isObjectAlias()) { + const QV4::CompiledData::Alias *aliasData = &metaObject->compiledObject->aliasTable()[aliasId]; + if (!aliasData->isObjectAlias()) { QQmlContextData *ctxt = metaObject->ctxt; - QObject *target = ctxt->idValues[d->contextIdx].data(); + QObject *target = ctxt->idValues[aliasData->targetObjectId].data(); if (!target) return; - if (d->notifySignal != -1) - connect(target, d->notifySignal, ctxt->engine); + QQmlData *targetDData = QQmlData::get(target, /*create*/false); + if (!targetDData) + return; + int coreIndex; + QQmlPropertyData::decodeValueTypePropertyIndex(aliasData->encodedMetaPropertyIndex, &coreIndex); + const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); + if (!pd) + return; + + if (pd->notifyIndex != -1) + connect(target, pd->notifyIndex, ctxt->engine); } metaObject.setFlag(); @@ -184,7 +220,7 @@ int QQmlInterceptorMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a) { if (c == QMetaObject::WriteProperty && interceptors && - !(*reinterpret_cast<int*>(a[3]) & QQmlPropertyPrivate::BypassInterceptor)) { + !(*reinterpret_cast<int*>(a[3]) & QQmlPropertyData::BypassInterceptor)) { for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) { if (vi->m_coreIndex != id) @@ -242,7 +278,7 @@ bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a) bool updated = false; if (newComponentValue != prevComponentValue) { valueProp.write(valueType, prevComponentValue); - valueType->write(object, id, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); + valueType->write(object, id, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor); vi->write(newComponentValue); updated = true; @@ -278,136 +314,124 @@ QAbstractDynamicMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObje } QQmlVMEMetaObject::QQmlVMEMetaObject(QObject *obj, - QQmlPropertyCache *cache, - const QQmlVMEMetaData *meta) + QQmlPropertyCache *cache, QV4::CompiledData::CompilationUnit *qmlCompilationUnit, int qmlObjectId) : QQmlInterceptorMetaObject(obj, cache), - ctxt(QQmlData::get(obj, true)->outerContext), metaData(meta), - aliasEndpoints(0), - methods(0) + ctxt(QQmlData::get(obj, true)->outerContext), + aliasEndpoints(0), compilationUnit(qmlCompilationUnit), compiledObject(0) { - cache->addref(); - QQmlData::get(obj)->hasVMEMetaObject = true; - 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); - - // ### 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; + if (compilationUnit && qmlObjectId >= 0) { + compiledObject = compilationUnit->data->objectAt(qmlObjectId); + + if (compiledObject->nProperties || compiledObject->nFunctions) { + Q_ASSERT(cache && cache->engine); + QV4::ExecutionEngine *v4 = cache->engine; + QV4::Heap::MemberData *data = QV4::MemberData::allocate(v4, compiledObject->nProperties + compiledObject->nFunctions); + propertyAndMethodStorage.set(v4, data); + std::fill(data->data, data->data + data->size, QV4::Encode::undefined()); + + // Need JS wrapper to ensure properties/methods are marked. + ensureQObjectWrapper(); } } - - if (needsJSWrapper) - ensureQObjectWrapper(); } QQmlVMEMetaObject::~QQmlVMEMetaObject() { if (parent.isT1()) parent.asT1()->objectDestroyed(object); delete [] aliasEndpoints; - delete [] methods; qDeleteAll(varObjectGuards); - - cache->release(); } -QV4::MemberData *QQmlVMEMetaObject::propertiesAsMemberData() +QV4::MemberData *QQmlVMEMetaObject::propertyAndMethodStorageAsMemberData() { - if (properties.isUndefined()) { - if (properties.valueRef()) + if (propertyAndMethodStorage.isUndefined()) { + if (propertyAndMethodStorage.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(); } - return static_cast<QV4::MemberData*>(properties.asManaged()); + return static_cast<QV4::MemberData*>(propertyAndMethodStorage.asManaged()); } void QQmlVMEMetaObject::writeProperty(int id, int v) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) *(md->data() + id) = QV4::Primitive::fromInt32(v); } void QQmlVMEMetaObject::writeProperty(int id, bool v) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) *(md->data() + id) = QV4::Primitive::fromBoolean(v); } void QQmlVMEMetaObject::writeProperty(int id, double v) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) *(md->data() + id) = QV4::Primitive::fromDouble(v); } void QQmlVMEMetaObject::writeProperty(int id, const QString& v) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) *(md->data() + id) = cache->engine->newString(v); } void QQmlVMEMetaObject::writeProperty(int id, const QUrl& v) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); } void QQmlVMEMetaObject::writeProperty(int id, const QDate& v) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); } void QQmlVMEMetaObject::writeProperty(int id, const QDateTime& v) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); } void QQmlVMEMetaObject::writeProperty(int id, const QPointF& v) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); } void QQmlVMEMetaObject::writeProperty(int id, const QSizeF& v) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); } void QQmlVMEMetaObject::writeProperty(int id, const QRectF& v) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); } void QQmlVMEMetaObject::writeProperty(int id, QObject* v) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) *(md->data() + id) = QV4::QObjectWrapper::wrap(cache->engine, v); @@ -422,7 +446,7 @@ void QQmlVMEMetaObject::writeProperty(int id, QObject* v) int QQmlVMEMetaObject::readPropertyAsInt(int id) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return 0; @@ -435,7 +459,7 @@ int QQmlVMEMetaObject::readPropertyAsInt(int id) bool QQmlVMEMetaObject::readPropertyAsBool(int id) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return false; @@ -448,7 +472,7 @@ bool QQmlVMEMetaObject::readPropertyAsBool(int id) double QQmlVMEMetaObject::readPropertyAsDouble(int id) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return 0.0; @@ -461,7 +485,7 @@ double QQmlVMEMetaObject::readPropertyAsDouble(int id) QString QQmlVMEMetaObject::readPropertyAsString(int id) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return QString(); @@ -474,7 +498,7 @@ QString QQmlVMEMetaObject::readPropertyAsString(int id) QUrl QQmlVMEMetaObject::readPropertyAsUrl(int id) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return QUrl(); @@ -488,7 +512,7 @@ QUrl QQmlVMEMetaObject::readPropertyAsUrl(int id) QDate QQmlVMEMetaObject::readPropertyAsDate(int id) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return QDate(); @@ -502,7 +526,7 @@ QDate QQmlVMEMetaObject::readPropertyAsDate(int id) QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return QDateTime(); @@ -516,7 +540,7 @@ QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id) QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return QSizeF(); @@ -530,7 +554,7 @@ QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id) QPointF QQmlVMEMetaObject::readPropertyAsPointF(int id) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return QPointF(); @@ -544,7 +568,7 @@ QPointF QQmlVMEMetaObject::readPropertyAsPointF(int id) QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return 0; @@ -558,7 +582,7 @@ QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id) QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return 0; @@ -574,7 +598,7 @@ QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id) QRectF QQmlVMEMetaObject::readPropertyAsRectF(int id) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return QRectF(); @@ -596,15 +620,20 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * if (intercept(c, _id, a)) return -1; + const int propertyCount = compiledObject ? int(compiledObject->nProperties) : 0; + const int aliasCount = compiledObject ? int(compiledObject->nAliases) : 0; + const int signalCount = compiledObject ? int(compiledObject->nSignals) : 0; + const int methodCount = compiledObject ? int(compiledObject->nFunctions) : 0; + if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty) { if (id >= propOffset()) { id -= propOffset(); - if (id < metaData->propertyCount) { - int t = (metaData->propertyData() + id)->propertyType; + if (id < propertyCount) { + const QV4::CompiledData::Property::Type t = static_cast<QV4::CompiledData::Property::Type>(qint32(compiledObject->propertyTable()[id].type)); bool needActivate = false; - if (t == QQmlVMEMetaData::VarPropertyType) { + if (t == QV4::CompiledData::Property::Var) { // 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(); @@ -620,132 +649,180 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * } } else { + int fallbackMetaType = QMetaType::UnknownType; + switch (t) { + case QV4::CompiledData::Property::Font: + fallbackMetaType = QMetaType::QFont; + break; + case QV4::CompiledData::Property::Time: + fallbackMetaType = QMetaType::QTime; + break; + case QV4::CompiledData::Property::Color: + fallbackMetaType = QMetaType::QColor; + break; + case QV4::CompiledData::Property::Vector2D: + fallbackMetaType = QMetaType::QVector2D; + break; + case QV4::CompiledData::Property::Vector3D: + fallbackMetaType = QMetaType::QVector3D; + break; + case QV4::CompiledData::Property::Vector4D: + fallbackMetaType = QMetaType::QVector4D; + break; + case QV4::CompiledData::Property::Matrix4x4: + fallbackMetaType = QMetaType::QMatrix4x4; + break; + case QV4::CompiledData::Property::Quaternion: + fallbackMetaType = QMetaType::QQuaternion; + break; + default: break; + } + if (c == QMetaObject::ReadProperty) { - switch(t) { - case QVariant::Int: + switch (t) { + case QV4::CompiledData::Property::Int: *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id); break; - case QVariant::Bool: + case QV4::CompiledData::Property::Bool: *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id); break; - case QVariant::Double: + case QV4::CompiledData::Property::Real: *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id); break; - case QVariant::String: + case QV4::CompiledData::Property::String: *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id); break; - case QVariant::Url: + case QV4::CompiledData::Property::Url: *reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id); break; - case QVariant::Date: + case QV4::CompiledData::Property::Date: *reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id); break; - case QVariant::DateTime: + case QV4::CompiledData::Property::DateTime: *reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id); break; - case QVariant::RectF: + case QV4::CompiledData::Property::Rect: *reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id); break; - case QVariant::SizeF: + case QV4::CompiledData::Property::Size: *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id); break; - case QVariant::PointF: + case QV4::CompiledData::Property::Point: *reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id); break; - case QMetaType::QObjectStar: + case QV4::CompiledData::Property::Custom: *reinterpret_cast<QObject **>(a[0]) = readPropertyAsQObject(id); break; - case QMetaType::QVariant: + case QV4::CompiledData::Property::Variant: *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id); break; - default: - { - 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) { - QVariant propertyAsVariant; - if (QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>()) - propertyAsVariant = v->d()->data; - QQml_valueTypeProvider()->readValueType(propertyAsVariant, a[0], t); - } - } + case QV4::CompiledData::Property::CustomList: { + 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)); break; } + case QV4::CompiledData::Property::Font: + case QV4::CompiledData::Property::Time: + case QV4::CompiledData::Property::Color: + case QV4::CompiledData::Property::Vector2D: + case QV4::CompiledData::Property::Vector3D: + case QV4::CompiledData::Property::Vector4D: + case QV4::CompiledData::Property::Matrix4x4: + case QV4::CompiledData::Property::Quaternion: + Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); + if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { + QVariant propertyAsVariant; + if (QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>()) + propertyAsVariant = v->d()->data; + QQml_valueTypeProvider()->readValueType(propertyAsVariant, a[0], fallbackMetaType); + } + break; + case QV4::CompiledData::Property::Var: + Q_UNREACHABLE(); } } else if (c == QMetaObject::WriteProperty) { switch(t) { - case QVariant::Int: + case QV4::CompiledData::Property::Int: needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id); writeProperty(id, *reinterpret_cast<int *>(a[0])); break; - case QVariant::Bool: + case QV4::CompiledData::Property::Bool: needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id); writeProperty(id, *reinterpret_cast<bool *>(a[0])); break; - case QVariant::Double: + case QV4::CompiledData::Property::Real: needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id); writeProperty(id, *reinterpret_cast<double *>(a[0])); break; - case QVariant::String: + case QV4::CompiledData::Property::String: needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id); writeProperty(id, *reinterpret_cast<QString *>(a[0])); break; - case QVariant::Url: + case QV4::CompiledData::Property::Url: needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id); writeProperty(id, *reinterpret_cast<QUrl *>(a[0])); break; - case QVariant::Date: + case QV4::CompiledData::Property::Date: needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id); writeProperty(id, *reinterpret_cast<QDate *>(a[0])); break; - case QVariant::DateTime: + case QV4::CompiledData::Property::DateTime: needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id); writeProperty(id, *reinterpret_cast<QDateTime *>(a[0])); break; - case QVariant::RectF: + case QV4::CompiledData::Property::Rect: needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id); writeProperty(id, *reinterpret_cast<QRectF *>(a[0])); break; - case QVariant::SizeF: + case QV4::CompiledData::Property::Size: needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id); writeProperty(id, *reinterpret_cast<QSizeF *>(a[0])); break; - case QVariant::PointF: + case QV4::CompiledData::Property::Point: needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id); writeProperty(id, *reinterpret_cast<QPointF *>(a[0])); break; - case QMetaType::QObjectStar: + case QV4::CompiledData::Property::Custom: needActivate = *reinterpret_cast<QObject **>(a[0]) != readPropertyAsQObject(id); writeProperty(id, *reinterpret_cast<QObject **>(a[0])); break; - case QMetaType::QVariant: + case QV4::CompiledData::Property::Variant: writeProperty(id, *reinterpret_cast<QVariant *>(a[0])); break; - default: { - QV4::MemberData *md = propertiesAsMemberData(); - if (md) { + case QV4::CompiledData::Property::CustomList: + // Writing such a property is not supported. Content is added through the list property + // methods. + break; + case QV4::CompiledData::Property::Font: + case QV4::CompiledData::Property::Time: + case QV4::CompiledData::Property::Color: + case QV4::CompiledData::Property::Vector2D: + case QV4::CompiledData::Property::Vector3D: + case QV4::CompiledData::Property::Vector4D: + case QV4::CompiledData::Property::Matrix4x4: + case QV4::CompiledData::Property::Quaternion: + Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); + if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { 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); + QQml_valueTypeProvider()->initValueType(fallbackMetaType, v->d()->data); } - needActivate = !QQml_valueTypeProvider()->equalValueType(t, a[0], v->d()->data); - QQml_valueTypeProvider()->writeValueType(t, a[0], v->d()->data); + needActivate = !QQml_valueTypeProvider()->equalValueType(fallbackMetaType, a[0], v->d()->data); + QQml_valueTypeProvider()->writeValueType(fallbackMetaType, a[0], v->d()->data); } break; - } + case QV4::CompiledData::Property::Var: + Q_UNREACHABLE(); } } @@ -758,56 +835,68 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * return -1; } - id -= metaData->propertyCount; - - if (id < metaData->aliasCount) { + id -= propertyCount; - QQmlVMEMetaData::AliasData *d = metaData->aliasData() + id; + if (id < aliasCount) { + const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[id]; - if (d->flags & QML_ALIAS_FLAG_PTR && c == QMetaObject::ReadProperty) + if ((aliasData->flags & QV4::CompiledData::Alias::AliasPointsToPointerObject) && c == QMetaObject::ReadProperty) *reinterpret_cast<void **>(a[0]) = 0; if (!ctxt) return -1; + while (aliasData->aliasToLocalAlias) + aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex]; + QQmlContext *context = ctxt->asQQmlContext(); QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context); - QObject *target = ctxtPriv->data->idValues[d->contextIdx].data(); + QObject *target = ctxtPriv->data->idValues[aliasData->targetObjectId].data(); if (!target) return -1; connectAlias(id); - if (d->isObjectAlias()) { + if (aliasData->isObjectAlias()) { *reinterpret_cast<QObject **>(a[0]) = target; return -1; } + QQmlData *targetDData = QQmlData::get(target, /*create*/false); + if (!targetDData) + return -1; + + int coreIndex; + const int valueTypePropertyIndex = QQmlPropertyData::decodeValueTypePropertyIndex(aliasData->encodedMetaPropertyIndex, &coreIndex); + // Remove binding (if any) on write if(c == QMetaObject::WriteProperty) { int flags = *reinterpret_cast<int*>(a[3]); - if (flags & QQmlPropertyPrivate::RemoveBindingOnAliasWrite) { + if (flags & QQmlPropertyData::RemoveBindingOnAliasWrite) { QQmlData *targetData = QQmlData::get(target); - if (targetData && targetData->hasBindingBit(d->propertyIndex())) - QQmlPropertyPrivate::removeBinding(target, d->propertyIdx); + if (targetData && targetData->hasBindingBit(coreIndex)) + QQmlPropertyPrivate::removeBinding(target, aliasData->encodedMetaPropertyIndex); } } - if (d->isValueTypeAlias()) { + if (valueTypePropertyIndex != -1) { + if (!targetDData->propertyCache) + return -1; + const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); // Value type property - QQmlValueType *valueType = QQmlValueTypeFactory::valueType(d->valueType()); + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(pd->propType); Q_ASSERT(valueType); - valueType->read(target, d->propertyIndex()); - int rv = QMetaObject::metacall(valueType, c, d->valueTypeIndex(), a); + valueType->read(target, coreIndex); + int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a); if (c == QMetaObject::WriteProperty) - valueType->write(target, d->propertyIndex(), 0x00); + valueType->write(target, coreIndex, 0x00); return rv; } else { - return QMetaObject::metacall(target, c, d->propertyIndex(), a); + return QMetaObject::metacall(target, c, coreIndex, a); } } @@ -820,8 +909,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * if (id >= methodOffset()) { id -= methodOffset(); - int plainSignals = metaData->signalCount + metaData->propertyCount + - metaData->aliasCount; + int plainSignals = signalCount + propertyCount + aliasCount; if (id < plainSignals) { activate(object, _id, a); return -1; @@ -829,7 +917,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * id -= plainSignals; - if (id < metaData->methodCount) { + if (id < methodCount) { if (!ctxt->engine) return -1; // We can't run the method @@ -853,23 +941,21 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * return -1; // The dynamic method with that id is not available. } - QQmlVMEMetaData::MethodData *data = metaData->methodData() + id; - - QV4::ScopedCallData callData(scope, data->parameterCount); + const unsigned int parameterCount = function->formalParameterCount(); + QV4::ScopedCallData callData(scope, parameterCount); callData->thisObject = ep->v8engine()->global(); - for (int ii = 0; ii < data->parameterCount; ++ii) + for (uint ii = 0; ii < parameterCount; ++ii) callData->args[ii] = scope.engine->fromVariant(*(QVariant *)a[ii + 1]); - QV4::ScopedValue result(scope); - result = function->call(callData); + function->call(scope, callData); if (scope.hasException()) { QQmlError error = scope.engine->catchExceptionAsQmlError(); if (error.isValid()) ep->warning(error); if (a[0]) *(QVariant *)a[0] = QVariant(); } else { - if (a[0]) *(QVariant *)a[0] = scope.engine->toVariant(result, 0); + if (a[0]) *(QVariant *)a[0] = scope.engine->toVariant(scope.result, 0); } ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. @@ -887,22 +973,23 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * QV4::ReturnedValue QQmlVMEMetaObject::method(int index) { - if (!ctxt || !ctxt->isValid()) { + if (!ctxt || !ctxt->isValid() || !compiledObject) { qWarning("QQmlVMEMetaObject: Internal error - attempted to evaluate a function in an invalid context"); return QV4::Encode::undefined(); } - if (!methods) + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); + if (!md) return QV4::Encode::undefined(); - return methods[index].value(); + return (md->data() + index + compiledObject->nProperties)->asReturnedValue(); } QV4::ReturnedValue QQmlVMEMetaObject::readVarProperty(int id) { - Q_ASSERT((metaData->propertyData() + id)->propertyType == QQmlVMEMetaData::VarPropertyType); + Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var); - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) return (md->data() + id)->asReturnedValue(); return QV4::Primitive::undefinedValue().asReturnedValue(); @@ -910,7 +997,7 @@ QV4::ReturnedValue QQmlVMEMetaObject::readVarProperty(int id) QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id) { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) { const QV4::QObjectWrapper *wrapper = (md->data() + id)->as<QV4::QObjectWrapper>(); if (wrapper) @@ -925,9 +1012,9 @@ QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id) void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value) { - Q_ASSERT((metaData->propertyData() + id)->propertyType == QQmlVMEMetaData::VarPropertyType); + Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var); - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return; @@ -965,8 +1052,8 @@ void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value) void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) { - if ((metaData->propertyData() + id)->propertyType == QQmlVMEMetaData::VarPropertyType) { - QV4::MemberData *md = propertiesAsMemberData(); + if (compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var) { + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return; @@ -996,7 +1083,7 @@ void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) needActivate = readPropertyAsQObject(id) != o; // TODO: still correct? writeProperty(id, o); } else { - QV4::MemberData *md = propertiesAsMemberData(); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) { QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); needActivate = (!v || @@ -1015,61 +1102,16 @@ void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) } } -void QQmlVMEMetaObject::listChanged(int id) -{ - activate(object, methodOffset() + id, 0); -} - -void QQmlVMEMetaObject::list_append(QQmlListProperty<QObject> *prop, QObject *o) -{ - QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); - list->append(o); - static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), 0); -} - -int QQmlVMEMetaObject::list_count(QQmlListProperty<QObject> *prop) -{ - QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); - return list->count(); -} - -QObject *QQmlVMEMetaObject::list_at(QQmlListProperty<QObject> *prop, int index) -{ - QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); - return list->at(index); -} - -void QQmlVMEMetaObject::list_clear(QQmlListProperty<QObject> *prop) -{ - QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); - list->clear(); - static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), 0); -} - -quint16 QQmlVMEMetaObject::vmeMethodLineNumber(int index) -{ - if (index < methodOffset()) { - Q_ASSERT(parentVMEMetaObject()); - return parentVMEMetaObject()->vmeMethodLineNumber(index); - } - - int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; - Q_ASSERT(index >= (methodOffset() + plainSignals) && index < (methodOffset() + plainSignals + metaData->methodCount)); - - int rawIndex = index - methodOffset() - plainSignals; - - QQmlVMEMetaData::MethodData *data = metaData->methodData() + rawIndex; - return data->lineNumber; -} - QV4::ReturnedValue QQmlVMEMetaObject::vmeMethod(int index) { if (index < methodOffset()) { Q_ASSERT(parentVMEMetaObject()); return parentVMEMetaObject()->vmeMethod(index); } - int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; - Q_ASSERT(index >= (methodOffset() + plainSignals) && index < (methodOffset() + plainSignals + metaData->methodCount)); + if (!compiledObject) + return QV4::Primitive::undefinedValue().asReturnedValue(); + const int plainSignals = compiledObject->nSignals + compiledObject->nProperties + compiledObject->nAliases; + Q_ASSERT(index >= (methodOffset() + plainSignals) && index < (methodOffset() + plainSignals + int(compiledObject->nFunctions))); return method(index - methodOffset() - plainSignals); } @@ -1080,14 +1122,16 @@ void QQmlVMEMetaObject::setVmeMethod(int index, const QV4::Value &function) Q_ASSERT(parentVMEMetaObject()); return parentVMEMetaObject()->setVmeMethod(index, function); } - int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; - Q_ASSERT(index >= (methodOffset() + plainSignals) && index < (methodOffset() + plainSignals + metaData->methodCount)); - - if (!methods) - methods = new QV4::PersistentValue[metaData->methodCount]; + if (!compiledObject) + return; + const int plainSignals = compiledObject->nSignals + compiledObject->nProperties + compiledObject->nAliases; + Q_ASSERT(index >= (methodOffset() + plainSignals) && index < (methodOffset() + plainSignals + int(compiledObject->nFunctions))); int methodIndex = index - methodOffset() - plainSignals; - methods[methodIndex].set(function.as<QV4::Object>()->engine(), function); + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); + if (!md) + return; + *(md->data() + methodIndex + compiledObject->nProperties) = function; } QV4::ReturnedValue QQmlVMEMetaObject::vmeProperty(int index) @@ -1122,26 +1166,15 @@ void QQmlVMEMetaObject::mark(QV4::ExecutionEngine *e) if (v4 != e) return; - properties.markOnce(e); + propertyAndMethodStorage.markOnce(e); if (QQmlVMEMetaObject *parent = parentVMEMetaObject()) parent->mark(e); } -void QQmlVMEMetaObject::allocateProperties() -{ - 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 { - Q_ASSERT(index >= propOffset() + metaData->propertyCount); + Q_ASSERT(compiledObject && (index >= propOffset() + int(compiledObject->nProperties))); *target = 0; *coreIndex = -1; @@ -1150,31 +1183,24 @@ bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, if (!ctxt) return false; - QQmlVMEMetaData::AliasData *d = metaData->aliasData() + (index - propOffset() - metaData->propertyCount); - QQmlContext *context = ctxt->asQQmlContext(); - QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context); - - *target = ctxtPriv->data->idValues[d->contextIdx].data(); + const int aliasId = index - propOffset() - compiledObject->nProperties; + const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId]; + *target = ctxt->idValues[aliasData->targetObjectId].data(); if (!*target) return false; - if (d->isObjectAlias()) { - } else if (d->isValueTypeAlias()) { - *coreIndex = d->propertyIndex(); - *valueTypeIndex = d->valueTypeIndex(); - } else { - *coreIndex = d->propertyIndex(); - } - + if (!aliasData->isObjectAlias()) + *valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(aliasData->encodedMetaPropertyIndex, coreIndex); return true; } void QQmlVMEMetaObject::connectAlias(int aliasId) { + Q_ASSERT(compiledObject); if (!aliasEndpoints) - aliasEndpoints = new QQmlVMEMetaObjectEndpoint[metaData->aliasCount]; + aliasEndpoints = new QQmlVMEMetaObjectEndpoint[compiledObject->nAliases]; - QQmlVMEMetaData::AliasData *d = metaData->aliasData() + aliasId; + const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId]; QQmlVMEMetaObjectEndpoint *endpoint = aliasEndpoints + aliasId; if (endpoint->metaObject.data()) { @@ -1184,14 +1210,15 @@ void QQmlVMEMetaObject::connectAlias(int aliasId) } endpoint->metaObject = this; - endpoint->connect(&ctxt->idValues[d->contextIdx].bindings); + endpoint->connect(&ctxt->idValues[aliasData->targetObjectId].bindings); endpoint->tryConnect(); } void QQmlVMEMetaObject::connectAliasSignal(int index, bool indexInSignalRange) { - int aliasId = (index - (indexInSignalRange ? signalOffset() : methodOffset())) - metaData->propertyCount; - if (aliasId < 0 || aliasId >= metaData->aliasCount) + Q_ASSERT(compiledObject); + int aliasId = (index - (indexInSignalRange ? signalOffset() : methodOffset())) - compiledObject->nProperties; + if (aliasId < 0 || aliasId >= int(compiledObject->nAliases)) return; connectAlias(aliasId); diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index 98fdae60ee..4a81fc50d2 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -64,82 +64,18 @@ #include <private/qobject_p.h> #include "qqmlguard_p.h" -#include "qqmlcompiler_p.h" #include "qqmlcontext_p.h" +#include "qqmlpropertycache_p.h" #include <private/qv8engine_p.h> #include <private/qflagpointer_p.h> +#include <private/qv4object_p.h> #include <private/qv4value_p.h> +#include <private/qqmlpropertyvalueinterceptor_p.h> QT_BEGIN_NAMESPACE -#define QML_ALIAS_FLAG_PTR 0x00000001 - -struct QQmlVMEMetaData -{ - short propertyCount; - short aliasCount; - short signalCount; - short methodCount; - // Make sure this structure is always aligned to int - - struct AliasData { - int contextIdx; - int propertyIdx; - int propType; - int flags; - int notifySignal; - - bool isObjectAlias() const { - return propertyIdx == -1; - } - bool isPropertyAlias() const { - return !isObjectAlias() && valueTypeIndex() == -1; - } - bool isValueTypeAlias() const { - return !isObjectAlias() && valueTypeIndex() != -1; - } - int propertyIndex() const { - int index; - QQmlPropertyData::decodeValueTypePropertyIndex(propertyIdx, &index); - return index; - } - int valueTypeIndex() const { - return QQmlPropertyData::decodeValueTypePropertyIndex(propertyIdx); - } - int valueType() const { - return (valueTypeIndex() != -1) ? propType : 0; - } - }; - - enum { - VarPropertyType = -1 - }; - - struct PropertyData { - int propertyType; - }; - - struct MethodData { - int runtimeFunctionIndex; - int parameterCount; - quint16 lineNumber; - }; - - PropertyData *propertyData() const { - return (PropertyData *)(((char *)const_cast<QQmlVMEMetaData *>(this)) + sizeof(QQmlVMEMetaData)); - } - - AliasData *aliasData() const { - return (AliasData *)(propertyData() + propertyCount); - } - - MethodData *methodData() const { - return (MethodData *)(aliasData() + aliasCount); - } -}; - class QQmlVMEMetaObject; class QQmlVMEVariantQObjectPtr : public QQmlGuard<QObject> { @@ -165,18 +101,27 @@ public: static QQmlInterceptorMetaObject *get(QObject *obj); - virtual QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *o); + QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *o) Q_DECL_OVERRIDE; // Used by auto-tests for inspection QQmlPropertyCache *propertyCache() const { return cache; } + bool intercepts(int coreIndex) const + { + for (auto it = interceptors; it; it = it->m_next) { + if (it->m_coreIndex == coreIndex) + return true; + } + return false; + } + protected: - virtual int metaCall(QObject *o, QMetaObject::Call c, int id, void **a); + int metaCall(QObject *o, QMetaObject::Call c, int id, void **a) Q_DECL_OVERRIDE; bool intercept(QMetaObject::Call c, int id, void **a); public: QObject *object; - QQmlPropertyCache *cache; + QQmlRefPointer<QQmlPropertyCache> cache; QBiPointer<QDynamicMetaObjectData, const QMetaObject> parent; QQmlPropertyValueInterceptor *interceptors; @@ -195,18 +140,15 @@ inline QQmlInterceptorMetaObject *QQmlInterceptorMetaObject::get(QObject *obj) return 0; } -class QQmlVMEVariant; -class QQmlRefCount; class QQmlVMEMetaObjectEndpoint; class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject { public: - QQmlVMEMetaObject(QObject *obj, QQmlPropertyCache *cache, const QQmlVMEMetaData *data); + QQmlVMEMetaObject(QObject *obj, QQmlPropertyCache *cache, QV4::CompiledData::CompilationUnit *qmlCompilationUnit, int qmlObjectId); ~QQmlVMEMetaObject(); bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const; QV4::ReturnedValue vmeMethod(int index); - quint16 vmeMethodLineNumber(int index); void setVmeMethod(int index, const QV4::Value &function); QV4::ReturnedValue vmeProperty(int index); void setVMEProperty(int index, const QV4::Value &v); @@ -219,16 +161,11 @@ public: static QQmlVMEMetaObject *getForSignal(QObject *o, int coreIndex); protected: - virtual int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a); + int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a) Q_DECL_OVERRIDE; public: - friend class QQmlVMEMetaObjectEndpoint; - friend class QQmlVMEVariantQObjectPtr; - friend class QQmlPropertyCache; - QQmlGuardedContextData ctxt; - const QQmlVMEMetaData *metaData; inline int propOffset() const; inline int methodOffset() const; inline int signalOffset() const; @@ -236,9 +173,8 @@ public: QQmlVMEMetaObjectEndpoint *aliasEndpoints; - QV4::WeakValue properties; - inline void allocateProperties(); - QV4::MemberData *propertiesAsMemberData(); + QV4::WeakValue propertyAndMethodStorage; + QV4::MemberData *propertyAndMethodStorageAsMemberData(); int readPropertyAsInt(int id); bool readPropertyAsBool(int id); @@ -271,7 +207,6 @@ public: void connectAlias(int aliasId); - QV4::PersistentValue *methods; QV4::ReturnedValue method(int); QV4::ReturnedValue readVarProperty(int); @@ -281,18 +216,17 @@ public: inline QQmlVMEMetaObject *parentVMEMetaObject() const; - void listChanged(int); - - static void list_append(QQmlListProperty<QObject> *, QObject *); - static int list_count(QQmlListProperty<QObject> *); - static QObject *list_at(QQmlListProperty<QObject> *, int); - static void list_clear(QQmlListProperty<QObject> *); - void activate(QObject *, int, void **); QList<QQmlVMEVariantQObjectPtr *> varObjectGuards; QQmlVMEVariantQObjectPtr *getQObjectGuardForProperty(int) const; + + + // keep a reference to the compilation unit in order to still + // do property access when the context has been invalidated. + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + const QV4::CompiledData::Object *compiledObject; }; QQmlVMEMetaObject *QQmlVMEMetaObject::get(QObject *obj) diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 24f49eb6e7..201d634411 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -70,7 +70,7 @@ using namespace QV4; -#ifndef QT_NO_XMLSTREAMREADER +#if !defined(QT_NO_XMLSTREAMREADER) && !defined(QT_NO_NETWORK) #define V4THROW_REFERENCE(string) { \ ScopedObject error(scope, ctx->engine()->newReferenceErrorObject(QStringLiteral(string))); \ @@ -158,7 +158,7 @@ class DocumentImpl : public QQmlRefCount, public NodeImpl public: DocumentImpl() : root(0) { type = Document; } virtual ~DocumentImpl() { - if (root) delete root; + delete root; } QString version; @@ -715,7 +715,7 @@ ReturnedValue Text::method_isElementContentWhitespace(CallContext *ctx) Scoped<Node> r(scope, ctx->thisObject().as<Node>()); if (!r) return Encode::undefined(); - return Encode(r->d()->d->data.trimmed().isEmpty()); + return Encode(QStringRef(&r->d()->d->data).trimmed().isEmpty()); } ReturnedValue Text::method_wholeText(CallContext *ctx) @@ -1235,7 +1235,8 @@ void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url) } else if (m_method == QLatin1String("DELETE")) { m_network = networkAccessManager()->deleteResource(request); } else if ((m_method == QLatin1String("OPTIONS")) || - m_method == QLatin1String("PROPFIND")) { + m_method == QLatin1String("PROPFIND") || + m_method == QLatin1String("PATCH")) { QBuffer *buffer = new QBuffer; buffer->setData(m_data); buffer->open(QIODevice::ReadOnly); @@ -1559,7 +1560,7 @@ void QQmlXMLHttpRequest::dispatchCallback(Object *thisObj, QQmlContextData *cont QV4::ScopedCallData callData(scope); callData->thisObject = Encode::undefined(); - callback->call(callData); + callback->call(scope, callData); if (scope.engine->hasException) { QQmlError error = scope.engine->catchExceptionAsQmlError(); @@ -1620,22 +1621,23 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject c->proto->mark(e); FunctionObject::markObjects(that, e); } - static ReturnedValue construct(const Managed *that, QV4::CallData *) + static void construct(const Managed *that, Scope &scope, QV4::CallData *) { - Scope scope(static_cast<const QQmlXMLHttpRequestCtor *>(that)->engine()); Scoped<QQmlXMLHttpRequestCtor> ctor(scope, that->as<QQmlXMLHttpRequestCtor>()); - if (!ctor) - return scope.engine->throwTypeError(); + if (!ctor) { + scope.result = scope.engine->throwTypeError(); + return; + } 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(); + scope.result = w.asReturnedValue(); } - static ReturnedValue call(const Managed *, QV4::CallData *) { - return Primitive::undefinedValue().asReturnedValue(); + static void call(const Managed *, Scope &scope, QV4::CallData *) { + scope.result = Primitive::undefinedValue(); } void setupProto(); @@ -1735,7 +1737,8 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(CallContext *ctx) method != QLatin1String("POST") && method != QLatin1String("DELETE") && method != QLatin1String("OPTIONS") && - method != QLatin1String("PROPFIND")) + method != QLatin1String("PROPFIND") && + method != QLatin1String("PATCH")) V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Unsupported HTTP method type"); // Argument 1 - URL @@ -2038,6 +2041,6 @@ void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4) QT_END_NAMESPACE -#endif // QT_NO_XMLSTREAMREADER +#endif // QT_NO_XMLSTREAMREADER && QT_NO_NETWORK #include <qqmlxmlhttprequest.moc> diff --git a/src/qml/qml/qqmlxmlhttprequest_p.h b/src/qml/qml/qqmlxmlhttprequest_p.h index 7bbfb5243c..df30873915 100644 --- a/src/qml/qml/qqmlxmlhttprequest_p.h +++ b/src/qml/qml/qqmlxmlhttprequest_p.h @@ -55,7 +55,7 @@ #include <QtCore/qglobal.h> #include <private/qqmlglobal_p.h> -#ifndef QT_NO_XMLSTREAMREADER +#if !defined(QT_NO_XMLSTREAMREADER) && !defined(QT_NO_NETWORK) QT_BEGIN_NAMESPACE @@ -64,7 +64,7 @@ void qt_rem_qmlxmlhttprequest(QV4::ExecutionEngine *engine, void *); QT_END_NAMESPACE -#endif // QT_NO_XMLSTREAMREADER +#endif // QT_NO_XMLSTREAMREADER && QT_NO_NETWORK #endif // QQMLXMLHTTPREQUEST_P_H diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index b9fb1f4ffe..76a900c846 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -45,6 +45,7 @@ #include <private/qqmlstringconverters_p.h> #include <private/qqmllocale_p.h> #include <private/qv8engine_p.h> +#include <private/qqmldelayedcallqueue_p.h> #include <QFileInfo> #include <private/qqmldebugconnector_p.h> @@ -146,6 +147,8 @@ Heap::QtObject::QtObject(QQmlEngine *qmlEngine) o->defineAccessorProperty(QStringLiteral("inputMethod"), QV4::QtObject::method_get_inputMethod, 0); #endif o->defineAccessorProperty(QStringLiteral("styleHints"), QV4::QtObject::method_get_styleHints, 0); + + o->defineDefaultProperty(QStringLiteral("callLater"), QV4::QtObject::method_callLater); } void QtObject::addAll() @@ -1282,11 +1285,10 @@ void QQmlBindingFunction::initBindingLocation() d()->bindingLocation.line = frame.line; } -ReturnedValue QQmlBindingFunction::call(const Managed *that, CallData *callData) +void QQmlBindingFunction::call(const Managed *that, Scope &scope, CallData *callData) { - Scope scope(static_cast<const QQmlBindingFunction*>(that)->engine()); ScopedFunctionObject function(scope, static_cast<const QQmlBindingFunction*>(that)->d()->originalFunction); - return function->call(callData); + function->call(scope, callData); } void QQmlBindingFunction::markObjects(Heap::Base *that, ExecutionEngine *e) @@ -1995,6 +1997,31 @@ ReturnedValue GlobalExtensions::method_string_arg(CallContext *ctx) return ctx->d()->engine->newString(value.arg(arg->toQString()))->asReturnedValue(); } +/*! +\qmlmethod Qt::callLater(function) +\qmlmethod Qt::callLater(function, argument1, argument2, ...) +\since 5.8 +Use this function to eliminate redundant calls to a function or signal. + +The function passed as the first argument to Qt.callLater() +will be called later, once the QML engine returns to the event loop. + +When this function is called multiple times in quick succession with the +same function as its first argument, that function will be called only once. + +For example: +\snippet qml/qtLater.qml 0 + +Any additional arguments passed to Qt.callLater() will +be passed on to the function invoked. Note that if redundant calls +are eliminated, then only the last set of arguments will be passed to the +function. +*/ +ReturnedValue QtObject::method_callLater(CallContext *ctx) +{ + QV8Engine *v8engine = ctx->engine()->v8Engine; + return v8engine->delayedCallQueue()->addUniquelyAndExecuteLater(ctx); +} QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index dc806c6031..d29983c476 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -135,6 +135,8 @@ struct QtObject : Object #endif static ReturnedValue method_get_styleHints(CallContext *ctx); + static ReturnedValue method_callLater(CallContext *ctx); + private: void addAll(); ReturnedValue findAndAdd(const QString *name, bool &foundProperty) const; @@ -186,7 +188,7 @@ struct QQmlBindingFunction : public QV4::FunctionObject void initBindingLocation(); // from caller stack trace - static ReturnedValue call(const Managed *that, CallData *callData); + static void call(const Managed *that, Scope &scope, CallData *callData); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 46fd4fbbeb..e08ff3b979 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -150,6 +150,7 @@ QV8Engine::QV8Engine(QJSEngine* qq) m_v4Engine = new QV4::ExecutionEngine; m_v4Engine->v8Engine = this; + m_delayedCallQueue.init(m_v4Engine); QV4::QObjectWrapper::initializeBindings(m_v4Engine); } @@ -159,18 +160,22 @@ QV8Engine::~QV8Engine() qDeleteAll(m_extensionData); m_extensionData.clear(); +#if !defined(QT_NO_XMLSTREAMREADER) && defined(QT_NO_NETWORK) qt_rem_qmlxmlhttprequest(m_v4Engine, m_xmlHttpRequestData); m_xmlHttpRequestData = 0; +#endif delete m_listModelData; m_listModelData = 0; delete m_v4Engine; } +#ifndef QT_NO_NETWORK QNetworkAccessManager *QV8Engine::networkAccessManager() { return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager(); } +#endif const QSet<QString> &QV8Engine::illegalNames() const { @@ -189,8 +194,10 @@ void QV8Engine::initializeGlobal() QQmlDateExtension::registerExtension(m_v4Engine); QQmlNumberExtension::registerExtension(m_v4Engine); +#if !defined(QT_NO_XMLSTREAMREADER) && !defined(QT_NO_NETWORK) qt_add_domexceptions(m_v4Engine); m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(m_v4Engine); +#endif qt_add_sqlexceptions(m_v4Engine); diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index 2cfa996409..390831609b 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -67,6 +67,7 @@ #include <private/qv4value_p.h> #include <private/qv4identifier_p.h> #include <private/qv4context_p.h> +#include <private/qqmldelayedcallqueue_p.h> QT_BEGIN_NAMESPACE @@ -76,12 +77,6 @@ namespace QV4 { struct QObjectMethod; } -// Uncomment the following line to enable global handle debugging. When enabled, all the persistent -// handles allocated using qPersistentNew() (or registered with qPersistentRegsiter()) and disposed -// with qPersistentDispose() are tracked. If you try and do something illegal, like double disposing -// a handle, qFatal() is called. -// #define QML_GLOBAL_HANDLE_DEBUGGING - #define V4THROW_ERROR(string) \ return ctx->engine()->throwError(QString::fromUtf8(string)); @@ -184,6 +179,7 @@ public: QQmlEngine *engine() { return m_engine; } QJSEngine *publicEngine() { return q; } QV4::ReturnedValue global(); + QQmlDelayedCallQueue *delayedCallQueue() { return &m_delayedCallQueue; } void *xmlHttpRequestData() { return m_xmlHttpRequestData; } @@ -192,10 +188,12 @@ public: void freezeObject(const QV4::Value &value); +#ifndef QT_NO_NETWORK // Return the network access manager for this engine. By default this returns the network // access manager of the QQmlEngine. It is overridden in the case of a threaded v8 // instance (like in WorkerScript). virtual QNetworkAccessManager *networkAccessManager(); +#endif // Return the list of illegal id names (the names of the properties on the global object) const QSet<QString> &illegalNames() const; @@ -217,6 +215,7 @@ public: protected: QJSEngine* q; QQmlEngine *m_engine; + QQmlDelayedCallQueue m_delayedCallQueue; QV4::ExecutionEngine *m_v4Engine; diff --git a/src/qml/qml/v8/v8.pri b/src/qml/qml/v8/v8.pri index 3d6a012481..4592022939 100644 --- a/src/qml/qml/v8/v8.pri +++ b/src/qml/qml/v8/v8.pri @@ -9,4 +9,3 @@ SOURCES += \ $$PWD/qv4domerrors.cpp \ $$PWD/qv4sqlerrors.cpp \ $$PWD/qqmlbuiltinfunctions.cpp - diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp index ed9a8533c0..a545fe57ca 100644 --- a/src/qml/types/qqmlbind.cpp +++ b/src/qml/types/qqmlbind.cpp @@ -51,6 +51,7 @@ #include <QtCore/qfile.h> #include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> #include <private/qobject_p.h> @@ -59,16 +60,18 @@ QT_BEGIN_NAMESPACE class QQmlBindPrivate : public QObjectPrivate { public: - QQmlBindPrivate() : componentComplete(true), obj(0) {} + QQmlBindPrivate() : obj(0), componentComplete(true), delayed(false), pendingEval(false) {} ~QQmlBindPrivate() { } QQmlNullableValue<bool> when; - bool componentComplete; QPointer<QObject> obj; QString propName; QQmlNullableValue<QVariant> value; QQmlProperty prop; QQmlAbstractBinding::Ptr prevBind; + bool componentComplete:1; + bool delayed:1; + bool pendingEval:1; void validate(QObject *binding) const; }; @@ -281,7 +284,43 @@ void QQmlBind::setValue(const QVariant &v) { Q_D(QQmlBind); d->value = v; - eval(); + prepareEval(); +} + +/*! + \qmlproperty bool QtQml::Binding::delayed + \since 5.8 + + This property holds whether the binding should be delayed. + + A delayed binding will not immediately update the target, but rather wait + until the event queue has been cleared. This can be used as an optimization, + or to prevent intermediary values from being assigned. + + \code + Binding { + target: contactName; property: 'text' + value: givenName + " " + familyName; when: list.ListView.isCurrentItem + delayed: true + } + \endcode +*/ +bool QQmlBind::delayed() const +{ + Q_D(const QQmlBind); + return d->delayed; +} + +void QQmlBind::setDelayed(bool delayed) +{ + Q_D(QQmlBind); + if (d->delayed == delayed) + return; + + d->delayed = delayed; + + if (!d->delayed) + eval(); } void QQmlBind::setTarget(const QQmlProperty &p) @@ -307,9 +346,22 @@ void QQmlBind::componentComplete() eval(); } +void QQmlBind::prepareEval() +{ + Q_D(QQmlBind); + if (d->delayed) { + if (!d->pendingEval) + QTimer::singleShot(0, this, &QQmlBind::eval); + d->pendingEval = true; + } else { + eval(); + } +} + void QQmlBind::eval() { Q_D(QQmlBind); + d->pendingEval = false; if (!d->prop.isValid() || d->value.isNull || !d->componentComplete) return; diff --git a/src/qml/types/qqmlbind_p.h b/src/qml/types/qqmlbind_p.h index 581995af56..77939a40bc 100644 --- a/src/qml/types/qqmlbind_p.h +++ b/src/qml/types/qqmlbind_p.h @@ -68,6 +68,7 @@ class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSourc Q_PROPERTY(QString property READ property WRITE setProperty) Q_PROPERTY(QVariant value READ value WRITE setValue) Q_PROPERTY(bool when READ when WRITE setWhen) + Q_PROPERTY(bool delayed READ delayed WRITE setDelayed REVISION 8) public: QQmlBind(QObject *parent=0); @@ -85,12 +86,16 @@ public: QVariant value() const; void setValue(const QVariant &); + bool delayed() const; + void setDelayed(bool); + protected: virtual void setTarget(const QQmlProperty &); virtual void classBegin(); virtual void componentComplete(); private: + void prepareEval(); void eval(); }; diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index a16acac3ab..84782114ac 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -44,7 +44,6 @@ #include <private/qqmlboundsignal_p.h> #include <qqmlcontext.h> #include <private/qqmlcontext_p.h> -#include <private/qqmlcompiler_p.h> #include <qqmlinfo.h> #include <QtCore/qdebug.h> @@ -67,7 +66,7 @@ public: bool ignoreUnknownSignals; bool componentcomplete; - QQmlRefPointer<QQmlCompiledData> cdata; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; QList<const QV4::CompiledData::Binding *> bindings; }; @@ -258,11 +257,11 @@ void QQmlConnectionsParser::verifyBindings(const QV4::CompiledData::Unit *qmlUni } } -void QQmlConnectionsParser::applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings) +void QQmlConnectionsParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { QQmlConnectionsPrivate *p = static_cast<QQmlConnectionsPrivate *>(QObjectPrivate::get(object)); - p->cdata = cdata; + p->compilationUnit = compilationUnit; p->bindings = bindings; } @@ -278,7 +277,7 @@ void QQmlConnections::connectSignals() QQmlData *ddata = QQmlData::get(this); QQmlContextData *ctxtdata = ddata ? ddata->outerContext : 0; - const QV4::CompiledData::Unit *qmlUnit = d->cdata->compilationUnit->data; + const QV4::CompiledData::Unit *qmlUnit = d->compilationUnit->data; foreach (const QV4::CompiledData::Binding *binding, d->bindings) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); QString propName = qmlUnit->stringAt(binding->propertyNameIndex); @@ -291,7 +290,7 @@ void QQmlConnections::connectSignals() QQmlBoundSignalExpression *expression = ctxtdata ? new QQmlBoundSignalExpression(target, signalIndex, - ctxtdata, this, d->cdata->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : 0; + ctxtdata, this, d->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : 0; signal->takeExpression(expression); d->boundsignals += signal; } else { diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h index ff128a0ca6..d454affba8 100644 --- a/src/qml/types/qqmlconnections_p.h +++ b/src/qml/types/qqmlconnections_p.h @@ -99,7 +99,7 @@ class QQmlConnectionsParser : public QQmlCustomParser { public: virtual void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props); - virtual void applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings); + virtual void applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings); }; diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 0e7b0c1b14..d6d4553946 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -48,7 +48,6 @@ #include <private/qqmlengine_p.h> #include <private/qqmlcomponent_p.h> #include <private/qqmlincubator_p.h> -#include <private/qqmlcompiler_p.h> #include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> @@ -92,17 +91,17 @@ struct DelegateModelGroupFunction : QV4::FunctionObject return scope->engine()->memoryManager->allocObject<DelegateModelGroupFunction>(scope, flag, code); } - static QV4::ReturnedValue call(const QV4::Managed *that, QV4::CallData *callData) + static void call(const QV4::Managed *that, QV4::Scope &scope, QV4::CallData *callData) { - QV4::ExecutionEngine *v4 = static_cast<const DelegateModelGroupFunction *>(that)->engine(); - QV4::Scope scope(v4); 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")); + if (!o) { + scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")); + return; + } QV4::ScopedValue v(scope, callData->argument(0)); - return f->d()->code(o->d()->item, f->d()->flag, v); + scope.result = f->d()->code(o->d()->item, f->d()->flag, v); } }; @@ -235,10 +234,8 @@ void QQmlDelegateModelPrivate::init() } QQmlDelegateModel::QQmlDelegateModel() -: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(0))) + : QQmlDelegateModel(nullptr, nullptr) { - Q_D(QQmlDelegateModel); - d->init(); } QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent) @@ -1657,7 +1654,7 @@ void QQmlDelegateModelItemMetaType::initializeMetaObject() int notifierId = 0; for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - QString propertyName = QStringLiteral("in") + groupNames.at(i); + QString propertyName = QLatin1String("in") + groupNames.at(i); propertyName.replace(2, 1, propertyName.at(2).toUpper()); builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); QMetaPropertyBuilder propertyBuilder = builder.addProperty( @@ -1665,7 +1662,7 @@ void QQmlDelegateModelItemMetaType::initializeMetaObject() propertyBuilder.setWritable(true); } for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); + const QString propertyName = groupNames.at(i) + QLatin1String("Index"); builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); QMetaPropertyBuilder propertyBuilder = builder.addProperty( propertyName.toUtf8(), "int", notifierId); @@ -1713,7 +1710,7 @@ void QQmlDelegateModelItemMetaType::initializePrototype() proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); for (int i = 2; i < groupNames.count(); ++i) { - QString propertyName = QStringLiteral("in") + groupNames.at(i); + QString propertyName = QLatin1String("in") + groupNames.at(i); propertyName.replace(2, 1, propertyName.at(2).toUpper()); s = v4->newString(propertyName); p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_member))); @@ -1721,7 +1718,7 @@ void QQmlDelegateModelItemMetaType::initializePrototype() proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); } for (int i = 2; i < groupNames.count(); ++i) { - const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); + const QString propertyName = groupNames.at(i) + QLatin1String("Index"); s = v4->newString(propertyName); p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_index))); p->setSetter(0); @@ -1921,9 +1918,10 @@ void QQmlDelegateModelItem::incubateObject( QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component); - incubatorPriv->compiledData = componentPriv->cc; - incubatorPriv->compiledData->addref(); - incubatorPriv->creator.reset(new QQmlObjectCreator(context, componentPriv->cc, componentPriv->creationContext)); + incubatorPriv->compilationUnit = componentPriv->compilationUnit; + incubatorPriv->compilationUnit->addref(); + incubatorPriv->enginePriv = enginePriv; + incubatorPriv->creator.reset(new QQmlObjectCreator(context, componentPriv->compilationUnit, componentPriv->creationContext)); incubatorPriv->subComponentToCreate = componentPriv->start; enginePriv->incubate(*incubationTask, forContext); @@ -2322,7 +2320,7 @@ QQmlDelegateModelGroup::QQmlDelegateModelGroup(QObject *parent) QQmlDelegateModelGroup::QQmlDelegateModelGroup( const QString &name, QQmlDelegateModel *model, int index, QObject *parent) - : QObject(*new QQmlDelegateModelGroupPrivate, parent) + : QQmlDelegateModelGroup(parent) { Q_D(QQmlDelegateModelGroup); d->name = name; diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 1e1ebc4939..f7fdbf0d80 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -42,7 +42,6 @@ #include <private/qqmlopenmetaobject_p.h> #include <private/qqmljsast_p.h> #include <private/qqmljsengine_p.h> -#include <private/qqmlcompiler_p.h> #include <private/qqmlcustomparser_p.h> #include <private/qqmlengine_p.h> @@ -79,13 +78,16 @@ static bool isMemoryUsed(const char *mem) static QString roleTypeName(ListLayout::Role::DataType t) { - QString result; - const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject", "VariantMap", "DateTime" }; + static const QString roleTypeNames[] = { + QStringLiteral("String"), QStringLiteral("Number"), QStringLiteral("Bool"), + QStringLiteral("List"), QStringLiteral("QObject"), QStringLiteral("VariantMap"), + QStringLiteral("DateTime") + }; if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType) - result = QString::fromLatin1(roleTypeNames[t]); + return roleTypeNames[t]; - return result; + return QString(); } const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type) @@ -1440,8 +1442,7 @@ void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector<int> const QByteArray &keyUtf8 = key.toUtf8(); QQmlListModel *existingModel = qobject_cast<QQmlListModel *>(m_meta->value(keyUtf8).value<QObject *>()); - if (existingModel) - delete existingModel; + delete existingModel; if (m_meta->setValue(keyUtf8, value)) roles << roleIndex; @@ -1457,8 +1458,7 @@ DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject() { for (int i=0 ; i < count() ; ++i) { QQmlListModel *subModel = qobject_cast<QQmlListModel *>(value(i).value<QObject *>()); - if (subModel) - delete subModel; + delete subModel; } } @@ -1469,8 +1469,7 @@ void DynamicRoleModelNodeMetaObject::propertyWrite(int index) QVariant v = value(index); QQmlListModel *model = qobject_cast<QQmlListModel *>(v.value<QObject *>()); - if (model) - delete model; + delete model; } void DynamicRoleModelNodeMetaObject::propertyWritten(int index) @@ -1981,15 +1980,7 @@ void QQmlListModel::setDynamicRoles(bool enableDynamicRoles) */ int QQmlListModel::count() const { - int count; - - if (m_dynamicRoles) - count = m_modelObjects.count(); - else { - count = m_listModel->elementCount(); - } - - return count; + return m_dynamicRoles ? m_modelObjects.count() : m_listModel->elementCount(); } /*! @@ -2001,7 +1992,7 @@ int QQmlListModel::count() const */ void QQmlListModel::clear() { - int cleared = count(); + const int cleared = count(); emitItemsAboutToBeRemoved(0, cleared); @@ -2411,7 +2402,7 @@ bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit, listElementTypeName = objName; // cache right name for next time } - if (!qmlUnit->stringAt(target->idIndex).isEmpty()) { + if (!qmlUnit->stringAt(target->idNameIndex).isEmpty()) { error(target->locationOfIdProperty, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property")); return false; } @@ -2518,13 +2509,13 @@ void QQmlListModelParser::verifyBindings(const QV4::CompiledData::Unit *qmlUnit, } } -void QQmlListModelParser::applyBindings(QObject *obj, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings) +void QQmlListModelParser::applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { QQmlListModel *rv = static_cast<QQmlListModel *>(obj); rv->m_engine = QV8Engine::getV4(qmlEngine(rv)); - const QV4::CompiledData::Unit *qmlUnit = cdata->compilationUnit->data; + const QV4::CompiledData::Unit *qmlUnit = compilationUnit->data; bool setRoles = false; diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h index b35a5a1224..220b0e54b5 100644 --- a/src/qml/types/qqmllistmodel_p.h +++ b/src/qml/types/qqmllistmodel_p.h @@ -187,8 +187,8 @@ public: QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} - virtual void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings); - virtual void applyBindings(QObject *obj, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings); + void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE; + void applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE; private: bool verifyProperty(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding); diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h index f519b5ff61..d3ff032ae9 100644 --- a/src/qml/types/qqmllistmodel_p_p.h +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -199,7 +199,7 @@ public: explicit Role(const Role *other); ~Role(); - // This enum must be kept in sync with the roleTypeNames variable in qdeclarativelistmodel.cpp + // This enum must be kept in sync with the roleTypeNames variable in qqmllistmodel.cpp enum DataType { Invalid = -1, diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index e819e4ee7d..10666476fe 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -52,10 +52,12 @@ #include <QtCore/qwaitcondition.h> #include <QtCore/qfile.h> #include <QtCore/qdatetime.h> -#include <QtNetwork/qnetworkaccessmanager.h> #include <QtQml/qqmlinfo.h> #include <QtQml/qqmlfile.h> +#ifndef QT_NO_NETWORK +#include <QtNetwork/qnetworkaccessmanager.h> #include "qqmlnetworkaccessmanagerfactory.h" +#endif #include <private/qv8engine_p.h> #include <private/qv4serialize_p.h> @@ -141,7 +143,10 @@ public: ~WorkerEngine(); void init(); + +#ifndef QT_NO_NETWORK virtual QNetworkAccessManager *networkAccessManager(); +#endif QQuickWorkerScriptEnginePrivate *p; @@ -150,7 +155,9 @@ public: QV4::PersistentValue onmessage; private: QV4::PersistentValue createsend; +#ifndef QT_NO_NETWORK QNetworkAccessManager *accessManager; +#endif }; WorkerEngine *workerEngine; @@ -194,14 +201,19 @@ private: }; QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent) -: QV8Engine(0), p(parent), accessManager(0) +: QV8Engine(0), p(parent) +#ifndef QT_NO_NETWORK +, accessManager(0) +#endif { m_v4Engine->v8Engine = this; } QQuickWorkerScriptEnginePrivate::WorkerEngine::~WorkerEngine() { +#ifndef QT_NO_NETWORK delete accessManager; +#endif } void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() @@ -239,7 +251,8 @@ void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() QV4::ScopedCallData callData(scope, 1); callData->args[0] = function; callData->thisObject = global(); - createsend.set(scope.engine, createsendconstructor->call(callData)); + createsendconstructor->call(scope, callData); + createsend.set(scope.engine, scope.result.asReturnedValue()); } // Requires handle and context scope @@ -252,16 +265,16 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(i QV4::Scope scope(v4); QV4::ScopedFunctionObject f(scope, createsend.value()); - QV4::ScopedValue v(scope); QV4::ScopedCallData callData(scope, 1); callData->args[0] = QV4::Primitive::fromInt32(id); callData->thisObject = global(); - v = f->call(callData); + f->call(scope, callData); if (scope.hasException()) - v = scope.engine->catchException(); - return v->asReturnedValue(); + scope.result = scope.engine->catchException(); + return scope.result.asReturnedValue(); } +#ifndef QT_NO_NETWORK QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerEngine::networkAccessManager() { if (!accessManager) { @@ -273,6 +286,7 @@ QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerEngine::networkAcc } return accessManager; } +#endif QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine) : workerEngine(0), qmlengine(engine), m_nextId(0) @@ -366,7 +380,7 @@ void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &d callData->thisObject = workerEngine->global(); callData->args[0] = qmlContext->d()->qml; // ### callData->args[1] = value; - f->call(callData); + f->call(scope, callData); if (scope.hasException()) { QQmlError error = scope.engine->catchExceptionAsQmlError(); reportScriptException(script, error); |