diff options
Diffstat (limited to 'src')
155 files changed, 4813 insertions, 2834 deletions
diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp index f425eaa220..5ed38db3d7 100644 --- a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp +++ b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp @@ -96,10 +96,10 @@ void QQuickFolderListModelPrivate::init() { Q_Q(QQuickFolderListModel); qRegisterMetaType<QList<FileProperty> >("QList<FileProperty>"); - q->connect(&fileInfoThread, SIGNAL(directoryChanged(QString, QList<FileProperty>)), - q, SLOT(_q_directoryChanged(QString, QList<FileProperty>))); - q->connect(&fileInfoThread, SIGNAL(directoryUpdated(QString, QList<FileProperty>, int, int)), - q, SLOT(_q_directoryUpdated(QString, QList<FileProperty>, int, int))); + q->connect(&fileInfoThread, SIGNAL(directoryChanged(QString,QList<FileProperty>)), + q, SLOT(_q_directoryChanged(QString,QList<FileProperty>))); + q->connect(&fileInfoThread, SIGNAL(directoryUpdated(QString,QList<FileProperty>,int,int)), + q, SLOT(_q_directoryUpdated(QString,QList<FileProperty>,int,int))); q->connect(&fileInfoThread, SIGNAL(sortFinished(QList<FileProperty>)), q, SLOT(_q_sortFinished(QList<FileProperty>))); q->connect(q, SIGNAL(rowCountChanged()), q, SIGNAL(countChanged())); diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index 6b2242d00b..7a7649c6f1 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -131,6 +131,7 @@ public: bool forwardOnly; // type == Rows }; +DEFINE_REF(QQmlSqlDatabaseWrapper, Object); DEFINE_OBJECT_VTABLE(QQmlSqlDatabaseWrapper); static ReturnedValue qmlsqldatabase_version(CallContext *ctx) @@ -206,7 +207,7 @@ static QString qmlsqldatabase_databaseFile(const QString& connectionName, QV8Eng return qmlsqldatabase_databasesPath(engine) + QDir::separator() + connectionName; } -static ReturnedValue qmlsqldatabase_rows_index(QV4::Referenced<QQmlSqlDatabaseWrapper> r, ExecutionEngine *v4, quint32 index, bool *hasProperty = 0) +static ReturnedValue qmlsqldatabase_rows_index(QQmlSqlDatabaseWrapperRef r, ExecutionEngine *v4, quint32 index, bool *hasProperty = 0) { Scope scope(v4); QV8Engine *v8 = v4->v8Engine; diff --git a/src/imports/xmllistmodel/qqmlxmllistmodel.cpp b/src/imports/xmllistmodel/qqmlxmllistmodel.cpp index ebfdad5cbf..0b9be3105b 100644 --- a/src/imports/xmllistmodel/qqmlxmllistmodel.cpp +++ b/src/imports/xmllistmodel/qqmlxmllistmodel.cpp @@ -44,7 +44,7 @@ #include <qqmlcontext.h> #include <private/qqmlengine_p.h> #include <private/qv8engine_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4engine_p.h> #include <private/qv4object_p.h> diff --git a/src/particles/qquickv4particledata_p.h b/src/particles/qquickv4particledata_p.h index b3614f94ff..10e9ecacc5 100644 --- a/src/particles/qquickv4particledata_p.h +++ b/src/particles/qquickv4particledata_p.h @@ -44,7 +44,7 @@ #include <private/qv8engine_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/accessible/quick/qaccessiblequickitem.cpp b/src/plugins/accessible/quick/qaccessiblequickitem.cpp index 3521d4f98e..4b68574149 100644 --- a/src/plugins/accessible/quick/qaccessiblequickitem.cpp +++ b/src/plugins/accessible/quick/qaccessiblequickitem.cpp @@ -152,52 +152,24 @@ QList<QQuickItem *> QAccessibleQuickItem::childItems() const QAccessible::State QAccessibleQuickItem::state() const { - QAccessible::State state; + QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item()); + if (!attached) + return QAccessible::State(); - if (item()->hasActiveFocus()) { - state.focusable = true; - state.focused = true; - } - - if (item()->activeFocusOnTab()) - state.focusable = true; + QAccessible::State st = attached->state(); if (!item()->window() || !item()->window()->isVisible() ||!item()->isVisible() || qFuzzyIsNull(item()->opacity())) - state.invisible = true; + st.invisible = true; - QAccessible::Role r = role(); - switch (r) { - case QAccessible::Button: { - state.focusable = true; - QVariant checkable = item()->property("checkable"); - if (!checkable.toBool()) - break; - // fall through - } - case QAccessible::CheckBox: - case QAccessible::RadioButton: { - state.focusable = true; - state.checkable = true; - state.checked = item()->property("checked").toBool(); - break; - } - case QAccessible::MenuItem: - case QAccessible::PageTab: - case QAccessible::EditableText: - case QAccessible::SpinBox: - case QAccessible::Terminal: - case QAccessible::ScrollBar: - state.focusable = true; - break; - case QAccessible::ComboBox: - state.focusable = true; - state.editable = item()->property("editable").toBool(); - break; - default: - break; - } + if (item()->activeFocusOnTab()) + st.focusable = true; + if (item()->hasActiveFocus()) + st.focused = true; + + if (role() == QAccessible::ComboBox) + st.editable = item()->property("editable").toBool(); - return state; + return st; } QAccessible::Role QAccessibleQuickItem::role() const diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp index 2ba13be090..f933465100 100644 --- a/src/qml/compiler/qqmlcodegenerator.cpp +++ b/src/qml/compiler/qqmlcodegenerator.cpp @@ -54,6 +54,8 @@ QT_USE_NAMESPACE +static const quint32 emptyStringIndex = 0; + DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS); using namespace QtQml; @@ -77,6 +79,7 @@ void QmlObject::init(MemoryPool *pool, int typeNameIndex, int id, const AST::Sou qmlSignals = pool->New<PoolList<Signal> >(); bindings = pool->New<PoolList<Binding> >(); functions = pool->New<PoolList<Function> >(); + declarationsOverride = 0; } void QmlObject::dump(DebugStream &out) @@ -88,6 +91,95 @@ void QmlObject::dump(DebugStream &out) out << "}" << endl; } +QString QmlObject::sanityCheckFunctionNames(const QList<CompiledFunctionOrExpression> &allFunctions, const QSet<QString> &illegalNames, AST::SourceLocation *errorLocation) +{ + QSet<int> functionNames; + for (Function *f = functions->first; f; f = f->next) { + AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(allFunctions.at(f->index).node); + Q_ASSERT(function); + *errorLocation = function->identifierToken; + QString name = function->name.toString(); + if (functionNames.contains(f->nameIndex)) + return tr("Duplicate method name"); + functionNames.insert(f->nameIndex); + if (signalNames.contains(f->nameIndex)) + return tr("Duplicate method name"); + + if (name.at(0).isUpper()) + return tr("Method names cannot begin with an upper case letter"); + if (illegalNames.contains(name)) + return tr("Illegal method name"); + } + return QString(); // no error +} + +QString QmlObject::appendSignal(Signal *signal) +{ + QmlObject *target = declarationsOverride; + if (!target) + target = this; + if (target->signalNames.contains(signal->nameIndex)) + return tr("Duplicate signal name"); + target->signalNames.insert(signal->nameIndex); + target->qmlSignals->append(signal); + return QString(); // no error +} + +QString QmlObject::appendProperty(QmlProperty *prop, const QString &propertyName, bool isDefaultProperty, const AST::SourceLocation &defaultToken, AST::SourceLocation *errorLocation) +{ + QmlObject *target = declarationsOverride; + if (!target) + target = this; + + if (target->propertyNames.contains(prop->nameIndex)) + return tr("Duplicate property name"); + + if (propertyName.constData()->isUpper()) + return tr("Property names cannot begin with an upper case letter"); + + target->propertyNames.insert(prop->nameIndex); + + const int index = target->properties->append(prop); + if (isDefaultProperty) { + if (target->indexOfDefaultProperty != -1) { + *errorLocation = defaultToken; + return tr("Duplicate default property"); + } + target->indexOfDefaultProperty = index; + } + return QString(); // no error +} + +void QmlObject::appendFunction(Function *f) +{ + QmlObject *target = declarationsOverride; + if (!target) + target = this; + target->functions->append(f); +} + +QString QmlObject::appendBinding(Binding *b, bool isListBinding) +{ + const bool bindingToDefaultProperty = (b->propertyNameIndex == 0); + if (!isListBinding && !bindingToDefaultProperty + && b->type != QV4::CompiledData::Binding::Type_GroupProperty + && b->type != QV4::CompiledData::Binding::Type_AttachedProperty + && !(b->flags & QV4::CompiledData::Binding::IsOnAssignment)) { + if (bindingNames.contains(b->propertyNameIndex)) + return tr("Property value set multiple times"); + bindingNames.insert(b->propertyNameIndex); + } + if (isListBinding) { + bindings->append(b); + } else if (bindingToDefaultProperty) { + Binding *insertionPoint = bindings->findSortedInsertionPoint<QV4::CompiledData::Location, QV4::CompiledData::Binding, &QV4::CompiledData::Binding::location>(b); + bindings->insertAfter(insertionPoint, b); + } else { + bindings->prepend(b); + } + return QString(); // no error +} + QStringList Signal::parameterStringList(const QStringList &stringPool) const { QStringList result; @@ -100,6 +192,7 @@ QStringList Signal::parameterStringList(const QStringList &stringPool) const QQmlCodeGenerator::QQmlCodeGenerator(const QSet<QString> &illegalNames) : illegalNames(illegalNames) , _object(0) + , _propertyDeclaration(0) , jsGenerator(0) { } @@ -148,7 +241,7 @@ bool QQmlCodeGenerator::generateFromQml(const QString &code, const QUrl &url, co this->pool = output->jsParserEngine.pool(); this->jsGenerator = &output->jsGenerator; - emptyStringIndex = registerString(QString()); + Q_ASSERT(registerString(QString()) == emptyStringIndex); sourceCode = code; @@ -221,7 +314,7 @@ bool QQmlCodeGenerator::visit(AST::UiObjectDefinition *node) int idx = defineQMLObject(node); appendBinding(node->qualifiedTypeNameId->identifierToken, emptyStringIndex, idx); } else { - int idx = defineQMLObject(/*qualfied type name id*/0, node->initializer); + int idx = defineQMLObject(/*qualfied type name id*/0, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer, /*declarations should go here*/_object); appendBinding(node->qualifiedTypeNameId, idx); } return false; @@ -229,7 +322,7 @@ bool QQmlCodeGenerator::visit(AST::UiObjectDefinition *node) bool QQmlCodeGenerator::visit(AST::UiObjectBinding *node) { - int idx = defineQMLObject(node->qualifiedTypeNameId, node->initializer); + int idx = defineQMLObject(node->qualifiedTypeNameId, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer); appendBinding(node->qualifiedId, idx, node->hasOnToken); return false; } @@ -293,47 +386,31 @@ void QQmlCodeGenerator::accept(AST::Node *node) AST::Node::acceptChild(node, this); } -bool QQmlCodeGenerator::sanityCheckFunctionNames() -{ - QSet<QString> functionNames; - for (Function *f = _object->functions->first; f; f = f->next) { - AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(_functions.at(f->index).node); - Q_ASSERT(function); - QString name = function->name.toString(); - if (functionNames.contains(name)) - COMPILE_EXCEPTION(function->identifierToken, tr("Duplicate method name")); - functionNames.insert(name); - if (_object->signalNames.contains(name)) - COMPILE_EXCEPTION(function->identifierToken, tr("Duplicate method name")); - - if (name.at(0).isUpper()) - COMPILE_EXCEPTION(function->identifierToken, tr("Method names cannot begin with an upper case letter")); - if (illegalNames.contains(name)) - COMPILE_EXCEPTION(function->identifierToken, tr("Illegal method name")); - } - return true; -} - -int QQmlCodeGenerator::defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, AST::UiObjectInitializer *initializer) +int QQmlCodeGenerator::defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, const AST::SourceLocation &location, AST::UiObjectInitializer *initializer, QmlObject *declarationsOverride) { QmlObject *obj = New<QmlObject>(); _objects.append(obj); const int objectIndex = _objects.size() - 1; qSwap(_object, obj); - AST::SourceLocation loc; - if (qualifiedTypeNameId) - loc = qualifiedTypeNameId->firstSourceLocation(); - _object->init(pool, registerString(asString(qualifiedTypeNameId)), emptyStringIndex, loc); + _object->init(pool, registerString(asString(qualifiedTypeNameId)), emptyStringIndex, location); + _object->declarationsOverride = declarationsOverride; - QSet<QString> propertyNames; - QSet<QString> signalNames; + // A new object is also a boundary for property declarations. + QmlProperty *declaration = 0; + qSwap(_propertyDeclaration, declaration); accept(initializer); - sanityCheckFunctionNames(); + qSwap(_propertyDeclaration, declaration); qSwap(_object, obj); + + AST::SourceLocation loc; + QString error = obj->sanityCheckFunctionNames(_functions, illegalNames, &loc); + if (!error.isEmpty()) + recordError(loc, error); + return objectIndex; } @@ -522,7 +599,7 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) QString signalName = node->name.toString(); signal->nameIndex = registerString(signalName); - AST::SourceLocation loc = node->firstSourceLocation(); + AST::SourceLocation loc = node->typeToken; signal->location.line = loc.startLine; signal->location.column = loc.startColumn; @@ -582,17 +659,17 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) p = p->next; } - if (_object->signalNames.contains(signalName)) - COMPILE_EXCEPTION(node->identifierToken, tr("Duplicate signal name")); - _object->signalNames.insert(signalName); - if (signalName.at(0).isUpper()) COMPILE_EXCEPTION(node->identifierToken, tr("Signal names cannot begin with an upper case letter")); if (illegalNames.contains(signalName)) COMPILE_EXCEPTION(node->identifierToken, tr("Illegal signal name")); - _object->qmlSignals->append(signal); + QString error = _object->appendSignal(signal); + if (!error.isEmpty()) { + recordError(node->identifierToken, error); + return false; + } } else { const QStringRef &memberType = node->memberType; const QStringRef &name = node->name; @@ -660,7 +737,8 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) else property->customTypeNameIndex = emptyStringIndex; - property->nameIndex = registerString(name.toString()); + const QString propName = name.toString(); + property->nameIndex = registerString(propName); AST::SourceLocation loc = node->firstSourceLocation(); property->location.line = loc.startLine; @@ -708,25 +786,38 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) propertyValue += alias.at(2); } property->aliasPropertyValueIndex = registerString(propertyValue); - } else if (node->statement) - appendBinding(node->identifierToken, property->nameIndex, node->statement); + } else if (node->statement) { + qSwap(_propertyDeclaration, property); + appendBinding(node->identifierToken, _propertyDeclaration->nameIndex, node->statement); + qSwap(_propertyDeclaration, property); + } - _object->properties->append(property); + AST::SourceLocation errorLocation; + QString error; - if (node->isDefaultMember) { - if (_object->indexOfDefaultProperty != -1) { - QQmlError error; - error.setDescription(QCoreApplication::translate("QQmlParser","Duplicate default property")); - error.setLine(node->defaultToken.startLine); - error.setColumn(node->defaultToken.startColumn); - errors << error; - return false; - } - _object->indexOfDefaultProperty = _object->properties->count - 1; + 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; + + QQmlError qmlError; + qmlError.setDescription(error); + qmlError.setLine(errorLocation.startLine); + qmlError.setColumn(errorLocation.startColumn); + errors << qmlError; + return false; } - // process QML-like initializers (e.g. property Object o: Object {}) - AST::Node::accept(node->binding, this); + if (node->binding) { + qSwap(_propertyDeclaration, property); + // process QML-like initializers (e.g. property Object o: Object {}) + AST::Node::accept(node->binding, this); + qSwap(_propertyDeclaration, property); + } } return false; @@ -738,11 +829,12 @@ bool QQmlCodeGenerator::visit(AST::UiSourceElement *node) _functions << funDecl; Function *f = New<Function>(); f->functionDeclaration = funDecl; - AST::SourceLocation loc = funDecl->firstSourceLocation(); + AST::SourceLocation loc = funDecl->identifierToken; f->location.line = loc.startLine; f->location.column = loc.startColumn; f->index = _functions.size() - 1; - _object->functions->append(f); + f->nameIndex = registerString(funDecl->name.toString()); + _object->appendFunction(f); } else { QQmlError error; error.setDescription(QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element")); @@ -800,7 +892,12 @@ QStringRef QQmlCodeGenerator::textRefAt(const AST::SourceLocation &first, const void QQmlCodeGenerator::setBindingValue(QV4::CompiledData::Binding *binding, AST::Statement *statement) { + AST::SourceLocation loc = statement->firstSourceLocation(); + binding->valueLocation.line = loc.startLine; + binding->valueLocation.column = loc.startColumn; binding->type = QV4::CompiledData::Binding::Type_Invalid; + if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly)) + binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(statement)) { AST::ExpressionNode *expr = stmt->expression; @@ -856,13 +953,10 @@ void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, int objectIndex, qSwap(_object, object); } -void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, AST::Statement *value) +void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, quint32 propertyNameIndex, AST::Statement *value) { - if (!sanityCheckPropertyName(nameLocation, propertyNameIndex)) - return; - if (stringAt(propertyNameIndex) == QStringLiteral("id")) { - setId(value); + setId(nameLocation, value); return; } @@ -872,14 +966,14 @@ void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, i binding->location.column = nameLocation.startColumn; binding->flags = 0; setBindingValue(binding, value); - _object->bindings->append(binding); + QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false); + if (!error.isEmpty()) { + recordError(nameLocation, error); + } } -void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem, bool isOnAssignment) +void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem, bool isOnAssignment) { - if (!sanityCheckPropertyName(nameLocation, propertyNameIndex, isListItem | isOnAssignment)) - return; - if (stringAt(propertyNameIndex) == QStringLiteral("id")) { recordError(nameLocation, tr("Invalid component id specification")); return; @@ -889,35 +983,55 @@ void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, i binding->propertyNameIndex = propertyNameIndex; binding->location.line = nameLocation.startLine; binding->location.column = nameLocation.startColumn; + + const QmlObject *obj = _objects.at(objectIndex); + binding->valueLocation = obj->location; + binding->flags = 0; + if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly)) + binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; + // No type name on the initializer means it must be a group property - if (stringAt(_objects.at(objectIndex)->inheritedTypeNameIndex).isEmpty()) + if (_objects.at(objectIndex)->inheritedTypeNameIndex == emptyStringIndex) binding->type = QV4::CompiledData::Binding::Type_GroupProperty; else binding->type = QV4::CompiledData::Binding::Type_Object; if (isOnAssignment) binding->flags |= QV4::CompiledData::Binding::IsOnAssignment; + if (isListItem) + binding->flags |= QV4::CompiledData::Binding::IsListItem; binding->value.objectIndex = objectIndex; - _object->bindings->append(binding); + QString error = bindingsTarget()->appendBinding(binding, isListItem); + if (!error.isEmpty()) { + recordError(nameLocation, error); + } } -bool QQmlCodeGenerator::setId(AST::Statement *value) +QmlObject *QQmlCodeGenerator::bindingsTarget() const +{ + if (_propertyDeclaration && _object->declarationsOverride) + return _object->declarationsOverride; + return _object; +} + +bool QQmlCodeGenerator::setId(const AST::SourceLocation &idLocation, AST::Statement *value) { AST::SourceLocation loc = value->firstSourceLocation(); QStringRef str; AST::Node *node = value; if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node)) { - if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(stmt->expression)) + if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(stmt->expression)) { str = lit->value; - else + node = 0; + } else node = stmt->expression; } - if (str.isEmpty()) + if (node && str.isEmpty()) str = asStringRef(node); if (str.isEmpty()) @@ -941,6 +1055,9 @@ bool QQmlCodeGenerator::setId(AST::Statement *value) if (illegalNames.contains(idQString)) COMPILE_EXCEPTION(loc, tr( "ID illegally masks global JavaScript property")); + if (_object->idIndex != emptyStringIndex) + COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times")); + _object->idIndex = registerString(idQString); _object->locationOfIdProperty.line = loc.startLine; _object->locationOfIdProperty.column = loc.startColumn; @@ -950,56 +1067,57 @@ bool QQmlCodeGenerator::setId(AST::Statement *value) bool QQmlCodeGenerator::resolveQualifiedId(AST::UiQualifiedId **nameToResolve, QmlObject **object) { - AST::UiQualifiedId *name = *nameToResolve; + AST::UiQualifiedId *qualifiedIdElement = *nameToResolve; + + if (qualifiedIdElement->name == QStringLiteral("id") && qualifiedIdElement->next) + COMPILE_EXCEPTION(qualifiedIdElement->identifierToken, tr( "Invalid use of id property")); - if (name->name == QStringLiteral("id") && name->next) - COMPILE_EXCEPTION(name->identifierToken, tr( "Invalid use of id property")); + // If it's a namespace, prepend the qualifier and we'll resolve it later to the correct type. + QString currentName = qualifiedIdElement->name.toString(); + if (qualifiedIdElement->next) { + foreach (QV4::CompiledData::Import* import, _imports) + if (import->qualifierIndex != emptyStringIndex + && stringAt(import->qualifierIndex) == currentName) { + qualifiedIdElement = qualifiedIdElement->next; + currentName += QLatin1Char('.'); + currentName += qualifiedIdElement->name; + + if (!qualifiedIdElement->name.unicode()->isUpper()) + COMPILE_EXCEPTION(qualifiedIdElement->firstSourceLocation(), tr("Expected type name")); + + break; + } + } *object = _object; - while (name->next) { + while (qualifiedIdElement->next) { Binding *binding = New<Binding>(); - binding->propertyNameIndex = registerString(name->name.toString()); - binding->location.line = name->identifierToken.startLine; - binding->location.column = name->identifierToken.startColumn; + binding->propertyNameIndex = registerString(currentName); + binding->location.line = qualifiedIdElement->identifierToken.startLine; + binding->location.column = qualifiedIdElement->identifierToken.startColumn; + binding->valueLocation.line = binding->valueLocation.column = 0; binding->flags = 0; - if (name->name.unicode()->isUpper()) + if (qualifiedIdElement->name.unicode()->isUpper()) binding->type = QV4::CompiledData::Binding::Type_AttachedProperty; else binding->type = QV4::CompiledData::Binding::Type_GroupProperty; - int objIndex = defineQMLObject(0, 0); + int objIndex = defineQMLObject(0, AST::SourceLocation(), 0, 0); binding->value.objectIndex = objIndex; - (*object)->bindings->append(binding); + QString error = (*object)->appendBinding(binding, /*isListBinding*/false); + if (!error.isEmpty()) { + recordError(qualifiedIdElement->identifierToken, error); + return false; + } *object = _objects[objIndex]; - name = name->next; + qualifiedIdElement = qualifiedIdElement->next; + if (qualifiedIdElement) + currentName = qualifiedIdElement->name.toString(); } - *nameToResolve = name; - return true; -} - -bool QQmlCodeGenerator::sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex, bool isListItemOnOrAssignment) -{ - const QString &name = jsGenerator->strings.at(nameIndex); - if (name.isEmpty()) - return true; - - // List items are implement by multiple bindings to the same name, so allow duplicates. - if (!isListItemOnOrAssignment) { - if (_object->propertyNames.contains(name)) - COMPILE_EXCEPTION(nameLocation, tr("Duplicate property name")); - - _object->propertyNames.insert(name); - } - - if (name.at(0).isUpper()) - COMPILE_EXCEPTION(nameLocation, tr("Property names cannot begin with an upper case letter")); - - if (illegalNames.contains(name)) - COMPILE_EXCEPTION(nameLocation, tr("Illegal property name")); - + *nameToResolve = qualifiedIdElement; return true; } @@ -1016,18 +1134,21 @@ void QQmlCodeGenerator::recordError(const AST::SourceLocation &location, const Q void QQmlCodeGenerator::collectTypeReferences() { foreach (QmlObject *obj, _objects) { - if (!stringAt(obj->inheritedTypeNameIndex).isEmpty()) - _typeReferences.add(obj->inheritedTypeNameIndex, obj->location); + if (obj->inheritedTypeNameIndex != emptyStringIndex) { + QV4::CompiledData::TypeReference &r = _typeReferences.add(obj->inheritedTypeNameIndex, obj->location); + r.needsCreation = true; + } - for (QmlProperty *prop = obj->properties->first; prop; prop = prop->next) { + for (const QmlProperty *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. - _typeReferences.add(prop->customTypeNameIndex, obj->location); + QV4::CompiledData::TypeReference &r = _typeReferences.add(prop->customTypeNameIndex, obj->location); + r.needsCreation = true; } } - for (Binding *binding = obj->bindings->first; binding; binding = binding->next) { + for (const Binding *binding = obj->firstBinding(); binding; binding = binding->next) { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) _typeReferences.add(binding->propertyNameIndex, binding->location); } @@ -1085,10 +1206,10 @@ QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const int objectsSize = 0; foreach (QmlObject *o, output.objects) { objectOffsets.insert(o, unitSize + importSize + objectOffsetTableSize + objectsSize); - objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functions->count, o->properties->count, o->qmlSignals->count, o->bindings->count); + objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount()); int signalTableSize = 0; - for (Signal *s = o->qmlSignals->first; s; s = s->next) + for (const Signal *s = o->firstSignal(); s; s = s->next) signalTableSize += QV4::CompiledData::Signal::calculateSize(s->parameters->count); objectsSize += signalTableSize; @@ -1131,46 +1252,45 @@ QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const quint32 nextOffset = sizeof(QV4::CompiledData::Object); - objectToWrite->nFunctions = o->functions->count; + objectToWrite->nFunctions = o->functionCount(); objectToWrite->offsetToFunctions = nextOffset; nextOffset += objectToWrite->nFunctions * sizeof(quint32); - objectToWrite->nProperties = o->properties->count; + objectToWrite->nProperties = o->propertyCount(); objectToWrite->offsetToProperties = nextOffset; nextOffset += objectToWrite->nProperties * sizeof(QV4::CompiledData::Property); - objectToWrite->nSignals = o->qmlSignals->count; + objectToWrite->nSignals = o->signalCount(); objectToWrite->offsetToSignals = nextOffset; nextOffset += objectToWrite->nSignals * sizeof(quint32); - objectToWrite->nBindings = o->bindings->count; + objectToWrite->nBindings = o->bindingCount(); objectToWrite->offsetToBindings = nextOffset; nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding); quint32 *functionsTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToFunctions); - for (Function *f = o->functions->first; f; f = f->next) + for (const Function *f = o->firstFunction(); f; f = f->next) *functionsTable++ = runtimeFunctionIndices[f->index]; char *propertiesPtr = objectPtr + objectToWrite->offsetToProperties; - for (QmlProperty *p = o->properties->first; p; p = p->next) { + for (const QmlProperty *p = o->firstProperty(); p; p = p->next) { QV4::CompiledData::Property *propertyToWrite = reinterpret_cast<QV4::CompiledData::Property*>(propertiesPtr); *propertyToWrite = *p; propertiesPtr += sizeof(QV4::CompiledData::Property); } char *bindingPtr = objectPtr + objectToWrite->offsetToBindings; - for (Binding *b = o->bindings->first; b; b = b->next) { - QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr); - *bindingToWrite = *b; - if (b->type == QV4::CompiledData::Binding::Type_Script) - bindingToWrite->value.compiledScriptIndex = runtimeFunctionIndices[b->value.compiledScriptIndex]; - bindingPtr += sizeof(QV4::CompiledData::Binding); - } + bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isValueBindingNoAlias); + bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isSignalHandler); + bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isAttachedProperty); + bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isGroupProperty); + bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isValueBindingToAlias); + Q_ASSERT((bindingPtr - objectToWrite->offsetToBindings - objectPtr) / sizeof(QV4::CompiledData::Binding) == unsigned(o->bindingCount())); quint32 *signalOffsetTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToSignals); quint32 signalTableSize = 0; char *signalPtr = objectPtr + nextOffset; - for (Signal *s = o->qmlSignals->first; s; s = s->next) { + for (const Signal *s = o->firstSignal(); s; s = s->next) { *signalOffsetTable++ = signalPtr - objectPtr; QV4::CompiledData::Signal *signalToWrite = reinterpret_cast<QV4::CompiledData::Signal*>(signalPtr); @@ -1187,7 +1307,7 @@ QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const signalPtr += size; } - objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functions->count, o->properties->count, o->qmlSignals->count, o->bindings->count); + objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount()); objectPtr += signalTableSize; } @@ -1202,6 +1322,20 @@ QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const return qmlUnit; } +char *QmlUnitGenerator::writeBindings(char *bindingPtr, QmlObject *o, const QVector<int> &runtimeFunctionIndices, BindingFilter filter) const +{ + for (const Binding *b = o->firstBinding(); b; b = b->next) { + if (!(b->*(filter))()) + continue; + QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr); + *bindingToWrite = *b; + if (b->type == QV4::CompiledData::Binding::Type_Script) + bindingToWrite->value.compiledScriptIndex = runtimeFunctionIndices[b->value.compiledScriptIndex]; + bindingPtr += sizeof(QV4::CompiledData::Binding); + } + return bindingPtr; +} + int QmlUnitGenerator::getStringId(const QString &str) const { return jsUnitGenerator->getStringId(str); @@ -1623,6 +1757,7 @@ SignalHandlerConverter::SignalHandlerConverter(QQmlEnginePrivate *enginePrivate, : enginePrivate(enginePrivate) , parsedQML(parsedQML) , unit(unit) + , illegalNames(QV8Engine::get(QQmlEnginePrivate::get(enginePrivate))->illegalNames()) { } @@ -1651,7 +1786,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio // map from signal name defined in qml itself to list of parameters QHash<QString, QStringList> customSignals; - for (Binding *binding = obj->bindings->first; binding; binding = binding->next) { + for (Binding *binding = obj->firstBinding(); binding; binding = binding->next) { QString propertyName = stringAt(binding->propertyNameIndex); // Attached property? if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { @@ -1690,8 +1825,22 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio QQmlPropertyData *signal = resolver.signal(propertyName, ¬InRevision); if (signal) { int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex); - foreach (const QByteArray ¶m, propertyCache->signalParameterNames(sigIndex)) - parameters << QString::fromUtf8(param); + sigIndex = propertyCache->originalClone(sigIndex); + + bool unnamedParameter = false; + + QList<QByteArray> parameterNames = propertyCache->signalParameterNames(sigIndex); + for (int i = 0; i < parameterNames.count(); ++i) { + const QString param = QString::fromUtf8(parameterNames.at(i)); + if (param.isEmpty()) + unnamedParameter = true; + else if (unnamedParameter) { + COMPILE_EXCEPTION(binding->location, tr("Signal uses unnamed parameter followed by named parameter.")); + } else if (illegalNames.contains(param)) { + COMPILE_EXCEPTION(binding->location, tr("Signal parameter \"%1\" hides global variable.").arg(param)); + } + parameters += param; + } } else { if (notInRevision) { // Try assinging it as a property later @@ -1713,12 +1862,12 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio // build cache if necessary if (customSignals.isEmpty()) { - for (Signal *signal = obj->qmlSignals->first; signal; signal = signal->next) { + for (const Signal *signal = obj->firstSignal(); signal; signal = signal->next) { const QString &signalName = stringAt(signal->nameIndex); customSignals.insert(signalName, signal->parameterStringList(parsedQML->jsGenerator.strings)); } - for (QmlProperty *property = obj->properties->first; property; property = property->next) { + for (const QmlProperty *property = obj->firstProperty(); property; property = property->next) { const QString propName = stringAt(property->nameIndex); customSignals.insert(propName, QStringList()); } @@ -1739,6 +1888,14 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio parameters = entry.value(); } + binding->propertyNameIndex = parsedQML->jsGenerator.registerString(propertyName); + + // Binding object to signal means connect the signal to the object's default method. + if (binding->type == QV4::CompiledData::Binding::Type_Object) { + binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerObject; + continue; + } + if (binding->type != QV4::CompiledData::Binding::Type_Script) { COMPILE_EXCEPTION(binding->location, tr("Incorrectly specified signal assignment")); } @@ -1770,7 +1927,6 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio parsedQML->functions[binding->value.compiledScriptIndex] = functionDeclaration; binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression; - binding->propertyNameIndex = parsedQML->jsGenerator.registerString(propertyName); } return true; } @@ -1785,11 +1941,11 @@ void SignalHandlerConverter::recordError(const QV4::CompiledData::Location &loca errors << error; } -QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision) +QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, QObject *object, QQmlContextData *context) { if (notInRevision) *notInRevision = false; - QQmlPropertyData *d = cache->property(name, 0, 0); + QQmlPropertyData *d = cache->property(name, object, context); // Find the first property while (d && d->isFunction()) @@ -1804,11 +1960,11 @@ QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRev } -QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) +QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision, QObject *object, QQmlContextData *context) { if (notInRevision) *notInRevision = false; - QQmlPropertyData *d = cache->property(name, 0, 0); + QQmlPropertyData *d = cache->property(name, object, context); if (notInRevision) *notInRevision = false; while (d && !(d->isFunction())) @@ -1824,7 +1980,7 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis if (name.endsWith(QStringLiteral("Changed"))) { QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed"))); - d = property(propName, notInRevision); + d = property(propName, notInRevision, object, context); if (d) return cache->signal(d->notifyIndex); } diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h index c296a3e624..7c09b8bfa2 100644 --- a/src/qml/compiler/qqmlcodegenerator_p.h +++ b/src/qml/compiler/qqmlcodegenerator_p.h @@ -93,15 +93,49 @@ struct PoolList T *last; int count; - void append(T *item) { + int append(T *item) { item->next = 0; if (last) last->next = item; else first = item; last = item; + return count++; + } + + void prepend(T *item) { + item->next = first; + first = item; + if (!last) + last = first; ++count; } + + template <typename Sortable, typename Base, Sortable Base::*sortMember> + T *findSortedInsertionPoint(T *item) const + { + T *insertPos = 0; + + for (T *it = first; it; it = it->next) { + if (!(it->*sortMember < item->*sortMember)) + break; + insertPos = it; + } + + return insertPos; + } + + void insertAfter(T *insertionPoint, T *item) { + if (!insertionPoint) { + prepend(item); + } else if (insertionPoint == last) { + append(item); + } else { + item->next = insertionPoint->next; + insertionPoint->next = item; + ++count; + } + } }; struct QmlObject; @@ -137,31 +171,71 @@ struct Function { AST::FunctionDeclaration *functionDeclaration; QV4::CompiledData::Location location; + int nameIndex; int index; // index in parsedQML::functions Function *next; }; +struct CompiledFunctionOrExpression +{ + CompiledFunctionOrExpression() + : node(0) + , disableAcceleratedLookups(false) + {} + CompiledFunctionOrExpression(AST::Node *n) + : node(n) + , disableAcceleratedLookups(false) + {} + AST::Node *node; // FunctionDeclaration, Statement or Expression + QString name; + bool disableAcceleratedLookups; +}; + struct QmlObject { - int inheritedTypeNameIndex; - int idIndex; + Q_DECLARE_TR_FUNCTIONS(QmlObject) +public: + quint32 inheritedTypeNameIndex; + quint32 idIndex; int indexOfDefaultProperty; QV4::CompiledData::Location location; QV4::CompiledData::Location locationOfIdProperty; - PoolList<QmlProperty> *properties; - PoolList<Signal> *qmlSignals; - PoolList<Binding> *bindings; - PoolList<Function> *functions; + const QmlProperty *firstProperty() const { return properties->first; } + int propertyCount() const { return properties->count; } + const Signal *firstSignal() const { return qmlSignals->first; } + int signalCount() const { return qmlSignals->count; } + Binding *firstBinding() const { return bindings->first; } + int bindingCount() const { return bindings->count; } + const Function *firstFunction() const { return functions->first; } + int functionCount() const { return functions->count; } - // caches to quickly find duplicates - QSet<QString> propertyNames; - QSet<QString> signalNames; + // If set, then declarations for this object (and init bindings for these) should go into the + // specified object. Used for declarations inside group properties. + QmlObject *declarationsOverride; void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int id, const AST::SourceLocation &location = AST::SourceLocation()); void dump(DebugStream &out); + + QString sanityCheckFunctionNames(const QList<CompiledFunctionOrExpression> &allFunctions, const QSet<QString> &illegalNames, AST::SourceLocation *errorLocation); + + QString appendSignal(Signal *signal); + QString appendProperty(QmlProperty *prop, const QString &propertyName, bool isDefaultProperty, const AST::SourceLocation &defaultToken, AST::SourceLocation *errorLocation); + void appendFunction(Function *f); + + QString appendBinding(Binding *b, bool isListBinding); + +private: + PoolList<QmlProperty> *properties; + PoolList<Signal> *qmlSignals; + PoolList<Binding> *bindings; + PoolList<Function> *functions; + + QSet<int> propertyNames; + QSet<int> bindingNames; + QSet<int> signalNames; }; struct Pragma @@ -174,21 +248,6 @@ struct Pragma QV4::CompiledData::Location location; }; -struct CompiledFunctionOrExpression -{ - CompiledFunctionOrExpression() - : node(0) - , disableAcceleratedLookups(false) - {} - CompiledFunctionOrExpression(AST::Node *n) - : node(n) - , disableAcceleratedLookups(false) - {} - AST::Node *node; // FunctionDeclaration, Statement or Expression - QString name; - bool disableAcceleratedLookups; -}; - struct ParsedQML { ParsedQML(bool debugMode) @@ -243,9 +302,9 @@ public: void accept(AST::Node *node); // returns index in _objects - int defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, AST::UiObjectInitializer *initializer); - int defineQMLObject(AST::UiObjectDefinition *node) - { return defineQMLObject(node->qualifiedTypeNameId, node->initializer); } + int defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, const AST::SourceLocation &location, AST::UiObjectInitializer *initializer, QmlObject *declarationsOverride = 0); + int defineQMLObject(AST::UiObjectDefinition *node, QmlObject *declarationsOverride = 0) + { return defineQMLObject(node->qualifiedTypeNameId, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer, declarationsOverride); } static QString asString(AST::UiQualifiedId *node); QStringRef asStringRef(AST::Node *node); @@ -263,24 +322,24 @@ public: void appendBinding(AST::UiQualifiedId *name, AST::Statement *value); void appendBinding(AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false); - void appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, AST::Statement *value); - void appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false); + void appendBinding(const AST::SourceLocation &nameLocation, quint32 propertyNameIndex, AST::Statement *value); + void appendBinding(const AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false); - bool setId(AST::Statement *value); + QmlObject *bindingsTarget() const; + + bool setId(const AST::SourceLocation &idLocation, AST::Statement *value); // resolves qualified name (font.pixelSize for example) and returns the last name along // with the object any right-hand-side of a binding should apply to. bool resolveQualifiedId(AST::UiQualifiedId **nameToResolve, QmlObject **object); - bool sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex, bool isListItemOnOrAssignment = false); - void recordError(const AST::SourceLocation &location, const QString &description); void collectTypeReferences(); static QQmlScript::LocationSpan location(AST::SourceLocation start, AST::SourceLocation end); - int registerString(const QString &str) const { return jsGenerator->registerString(str); } + quint32 registerString(const QString &str) const { return jsGenerator->registerString(str); } template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); } QString stringAt(int index) const { return jsGenerator->strings.at(index); } @@ -299,13 +358,12 @@ public: QV4::CompiledData::TypeReferenceMap _typeReferences; QmlObject *_object; + QmlProperty *_propertyDeclaration; QQmlJS::MemoryPool *pool; QString sourceCode; QUrl url; QV4::Compiler::JSUnitGenerator *jsGenerator; - int emptyStringIndex; - bool sanityCheckFunctionNames(); }; struct Q_QML_EXPORT QmlUnitGenerator @@ -318,6 +376,9 @@ struct Q_QML_EXPORT QmlUnitGenerator QV4::CompiledData::QmlUnit *generate(ParsedQML &output, const QVector<int> &runtimeFunctionIndices); private: + typedef bool (Binding::*BindingFilter)() const; + char *writeBindings(char *bindingPtr, QmlObject *o, const QVector<int> &runtimeFunctionIndices, BindingFilter filter) const; + int getStringId(const QString &str) const; QV4::Compiler::JSUnitGenerator *jsUnitGenerator; @@ -334,10 +395,10 @@ struct PropertyResolver return cache->property(index); } - QQmlPropertyData *property(const QString &name, bool *notInRevision = 0); + QQmlPropertyData *property(const QString &name, bool *notInRevision = 0, QObject *object = 0, QQmlContextData *context = 0); // This code must match the semantics of QQmlPropertyPrivate::findSignalByName - QQmlPropertyData *signal(const QString &name, bool *notInRevision); + QQmlPropertyData *signal(const QString &name, bool *notInRevision, QObject *object = 0, QQmlContextData *context = 0); QQmlPropertyCache *cache; }; @@ -366,6 +427,7 @@ private: QQmlEnginePrivate *enginePrivate; ParsedQML *parsedQML; QQmlCompiledData *unit; + const QSet<QString> &illegalNames; }; struct Q_QML_EXPORT JSCodeGen : public QQmlJS::Codegen diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 06ee180119..dae57fe6f4 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -46,6 +46,7 @@ #include <private/qqmlcustomparser_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlcomponent_p.h> +#include <private/qqmlstringconverters_p.h> #define COMPILE_EXCEPTION(token, desc) \ { \ @@ -83,7 +84,7 @@ bool QQmlTypeCompiler::compile() QScopedPointer<QQmlCompiledData::TypeReference> ref(new QQmlCompiledData::TypeReference); QQmlType *qmlType = resolvedType->type; if (resolvedType->typeData) { - if (qmlType->isCompositeSingleton()) { + if (resolvedType->needsCreation && qmlType->isCompositeSingleton()) { QQmlError error; QString reason = tr("Composite Singleton Type %1 is not creatable.").arg(qmlType->qmlTypeName()); error.setDescription(reason); @@ -98,7 +99,7 @@ bool QQmlTypeCompiler::compile() ref->type = qmlType; Q_ASSERT(ref->type); - if (!ref->type->isCreatable()) { + if (resolvedType->needsCreation && !ref->type->isCreatable()) { QQmlError error; QString reason = ref->type->noCreationReason(); if (reason.isEmpty()) @@ -149,6 +150,17 @@ bool QQmlTypeCompiler::compile() } } + { + QQmlEnumTypeResolver enumResolver(this); + if (!enumResolver.resolveEnumBindings()) + return false; + } + + { + QQmlAliasAnnotator annotator(this); + annotator.annotateBindingsToAliases(); + } + // Collect imported scripts const QList<QQmlTypeData::ScriptReference> &scripts = typeData->resolvedScripts(); compiledData->scripts.reserve(scripts.count()); @@ -183,6 +195,11 @@ bool QQmlTypeCompiler::compile() JSCodeGen jsCodeGen(typeData->finalUrlString(), parsedQML->code, &parsedQML->jsModule, &parsedQML->jsParserEngine, parsedQML->program, compiledData->importCache); const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(parsedQML->functions); + QList<QQmlError> jsErrors = jsCodeGen.errors(); + if (!jsErrors.isEmpty()) { + errors << jsErrors; + return false; + } QV4::ExecutionEngine *v4 = engine->v4engine(); @@ -227,6 +244,27 @@ bool QQmlTypeCompiler::compile() if (!validator.validate()) return false; + // Collect some data for instantiation later. + int bindingCount = 0; + int parserStatusCount = 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; + } + if (typeRef->component) { + bindingCount += typeRef->component->totalBindingsCount; + parserStatusCount += typeRef->component->totalParserStatusCount; + } + } + } + + compiledData->totalBindingsCount = bindingCount; + compiledData->totalParserStatusCount = parserStatusCount; + return errors.isEmpty(); } @@ -380,10 +418,29 @@ bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int r const QmlObject *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 needVMEMetaObject = obj->properties->count != 0 || obj->qmlSignals->count != 0 || obj->functions->count != 0; + bool notInRevision = false; + instantiatingProperty = PropertyResolver(parentCache).property(stringAt(instantiatingBinding->propertyNameIndex), ¬InRevision); + if (instantiatingProperty) { + if (instantiatingProperty->isQObject()) { + baseTypeCache = enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType); + Q_ASSERT(baseTypeCache); + } else if (QQmlValueType *vt = QQmlValueTypeFactory::valueType(instantiatingProperty->propType)) { + baseTypeCache = enginePrivate->cache(vt->metaObject()); + Q_ASSERT(baseTypeCache); + } + } + } + + bool needVMEMetaObject = obj->propertyCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0; if (!needVMEMetaObject) { - for (const QtQml::Binding *binding = obj->bindings->first; binding; binding = binding->next) { + for (const QtQml::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. @@ -393,30 +450,17 @@ bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int r // 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 (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) { - QQmlPropertyCache *parentCache = propertyCaches.at(referencingObjectIndex); - Q_ASSERT(parentCache); - Q_ASSERT(!stringAt(instantiatingBinding->propertyNameIndex).isEmpty()); - - bool notInRevision = false; - QQmlPropertyData *pd = PropertyResolver(parentCache).property(stringAt(instantiatingBinding->propertyNameIndex), ¬InRevision); - Q_ASSERT(pd); - if (QQmlValueTypeFactory::isValueType(pd->propType)) { - needVMEMetaObject = false; - if (!ensureMetaObject(referencingObjectIndex)) - return false; - } else if (pd->isQObject()) { - baseTypeCache = enginePrivate->rawPropertyCacheForType(pd->propType); - Q_ASSERT(baseTypeCache); - } + if (instantiatingProperty && QQmlValueTypeFactory::isValueType(instantiatingProperty->propType)) { + needVMEMetaObject = false; + if (!ensureMetaObject(referencingObjectIndex)) + return false; } break; } } } - QString typeName = stringAt(obj->inheritedTypeNameIndex); - if (!typeName.isEmpty()) { + if (obj->inheritedTypeNameIndex != 0) { QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); @@ -431,10 +475,12 @@ bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int r baseTypeCache->addref(); } - for (const QtQml::Binding *binding = obj->bindings->first; binding; binding = binding->next) - if (binding->type == QV4::CompiledData::Binding::Type_Object) - if (!buildMetaObjectRecursively(binding->value.objectIndex, objectIndex, binding)) - return false; + if (propertyCaches.at(objectIndex)) { + for (const QtQml::Binding *binding = obj->firstBinding(); binding; binding = binding->next) + if (binding->type == QV4::CompiledData::Binding::Type_Object || binding->type == QV4::CompiledData::Binding::Type_GroupProperty) + if (!buildMetaObjectRecursively(binding->value.objectIndex, objectIndex, binding)) + return false; + } return true; } @@ -453,9 +499,9 @@ bool QQmlPropertyCacheCreator::ensureMetaObject(int objectIndex) bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QtQml::QmlObject *obj, QQmlPropertyCache *baseTypeCache) { QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(QQmlEnginePrivate::get(enginePrivate), - obj->properties->count, - obj->functions->count + obj->properties->count + obj->qmlSignals->count, - obj->qmlSignals->count + obj->properties->count); + obj->propertyCount(), + obj->functionCount() + obj->propertyCount() + obj->signalCount(), + obj->signalCount() + obj->propertyCount()); propertyCaches[objectIndex] = cache; struct TypeData { @@ -510,25 +556,26 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QtQml::Qm int aliasCount = 0; int varPropCount = 0; - for (QtQml::QmlProperty *p = obj->properties->first; p; p = p->next) { + PropertyResolver resolver(baseTypeCache); + + for (const QtQml::QmlProperty *p = obj->firstProperty(); p; p = p->next) { if (p->type == QV4::CompiledData::Property::Alias) aliasCount++; else if (p->type == QV4::CompiledData::Property::Var) varPropCount++; -#if 0 // ### Do this elsewhere // No point doing this for both the alias and non alias cases - QQmlPropertyData *d = property(obj, p->name); + bool notInRevision = false; + QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); if (d && d->isFinal()) COMPILE_EXCEPTION(p, tr("Cannot override FINAL property")); -#endif } typedef QQmlVMEMetaData VMD; QByteArray &dynamicData = vmeMetaObjects[objectIndex] = QByteArray(sizeof(QQmlVMEMetaData) - + obj->properties->count * sizeof(VMD::PropertyData) - + obj->functions->count * sizeof(VMD::MethodData) + + obj->propertyCount() * sizeof(VMD::PropertyData) + + obj->functionCount() * sizeof(VMD::MethodData) + aliasCount * sizeof(VMD::AliasData), 0); int effectivePropertyIndex = cache->propertyIndexCacheStart; @@ -564,7 +611,7 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QtQml::Qm if (ii == NSS_Var && varPropCount == 0) continue; else if (ii == NSS_Alias && aliasCount == 0) continue; - for (QtQml::QmlProperty *p = obj->properties->first; p; p = p->next) { + for (const QtQml::QmlProperty *p = obj->firstProperty(); p; p = p->next) { if ((ii == NSS_Normal && (p->type == QV4::CompiledData::Property::Alias || p->type == QV4::CompiledData::Property::Var)) || ((ii == NSS_Var) && (p->type != QV4::CompiledData::Property::Var)) || @@ -582,7 +629,7 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QtQml::Qm } // Dynamic signals - for (QtQml::Signal *s = obj->qmlSignals->first; s; s = s->next) { + for (const QtQml::Signal *s = obj->firstSignal(); s; s = s->next) { const int paramCount = s->parameters->count; QList<QByteArray> names; @@ -640,7 +687,7 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QtQml::Qm // Dynamic slots - for (QtQml::Function *s = obj->functions->first; s; s = s->next) { + for (const QtQml::Function *s = obj->firstFunction(); s; s = s->next) { AST::FunctionDeclaration *astFunction = s->functionDeclaration; quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; @@ -668,7 +715,7 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QtQml::Qm // Dynamic properties (except var and aliases) int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; int propertyIdx = 0; - for (QtQml::QmlProperty *p = obj->properties->first; p; p = p->next, ++propertyIdx) { + for (const QtQml::QmlProperty *p = obj->firstProperty(); p; p = p->next, ++propertyIdx) { if (p->type == QV4::CompiledData::Property::Alias || p->type == QV4::CompiledData::Property::Var) @@ -744,7 +791,7 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QtQml::Qm // Now do var properties propertyIdx = 0; - for (QtQml::QmlProperty *p = obj->properties->first; p; p = p->next, ++propertyIdx) { + for (const QtQml::QmlProperty *p = obj->firstProperty(); p; p = p->next, ++propertyIdx) { if (p->type != QV4::CompiledData::Property::Var) continue; @@ -770,7 +817,7 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QtQml::Qm ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount; // Dynamic slot data - comes after the property data - for (QtQml::Function *s = obj->functions->first; s; s = s->next) { + for (const QtQml::Function *s = obj->firstFunction(); s; s = s->next) { AST::FunctionDeclaration *astFunction = s->functionDeclaration; int formalsCount = 0; AST::FormalParameterList *param = astFunction->formals; @@ -792,6 +839,187 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QtQml::Qm return true; } +QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , qmlObjects(*typeCompiler->qmlObjects()) + , propertyCaches(typeCompiler->propertyCaches()) + , imports(typeCompiler->imports()) + , resolvedTypes(typeCompiler->resolvedTypes()) +{ +} + +bool QQmlEnumTypeResolver::resolveEnumBindings() +{ + for (int i = 0; i < qmlObjects.count(); ++i) { + QQmlPropertyCache *propertyCache = propertyCaches.at(i); + if (!propertyCache) + continue; + const QmlObject *obj = qmlObjects.at(i); + + PropertyResolver resolver(propertyCache); + + for (QtQml::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + continue; + + if (binding->type != QV4::CompiledData::Binding::Type_Script) + continue; + + const QString propertyName = stringAt(binding->propertyNameIndex); + bool notInRevision = false; + QQmlPropertyData *pd = resolver.property(propertyName, ¬InRevision); + if (!pd) + continue; + + if (!pd->isEnum() && pd->propType != QMetaType::Int) + continue; + + if (!tryQualifiedEnumAssignment(obj, propertyCache, pd, binding)) + return false; + } + } + + return true; +} + +struct StaticQtMetaObject : public QObject +{ + static const QMetaObject *get() + { return &staticQtMetaObject; } +}; + +bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlObject *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, Binding *binding) +{ + bool isIntProp = (prop->propType == QMetaType::Int) && !prop->isEnum(); + if (!prop->isEnum() && !isIntProp) + return true; + + if (!prop->isWritable() && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)) + COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property").arg(stringAt(binding->propertyNameIndex))); + + Q_ASSERT(binding->type = QV4::CompiledData::Binding::Type_Script); + QString string = stringAt(binding->stringIndex); + if (!string.at(0).isUpper()) + return true; + + int dot = string.indexOf(QLatin1Char('.')); + if (dot == -1 || dot == string.length()-1) + return true; + + if (string.indexOf(QLatin1Char('.'), dot+1) != -1) + return true; + + QHashedStringRef typeName(string.constData(), dot); + QString enumValue = string.mid(dot+1); + + if (isIntProp) { + // Allow enum assignment to ints. + bool ok; + int enumval = evaluateEnum(typeName.toString(), enumValue.toUtf8(), &ok); + if (ok) { + binding->type = QV4::CompiledData::Binding::Type_Number; + binding->value.d = (double)enumval; + binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum; + } + return true; + } + QQmlType *type = 0; + imports->resolveType(typeName, &type, 0, 0, 0); + + if (!type && typeName != QLatin1String("Qt")) + return true; + if (type && type->isComposite()) //No enums on composite (or composite singleton) types + return true; + + int value = 0; + bool ok = false; + + QQmlCompiledData::TypeReference *tr = resolvedTypes->value(obj->inheritedTypeNameIndex); + if (type && tr && tr->type == type) { + QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex); + + // When these two match, we can short cut the search + if (mprop.isFlagType()) { + value = mprop.enumerator().keysToValue(enumValue.toUtf8().constData(), &ok); + } else { + value = mprop.enumerator().keyToValue(enumValue.toUtf8().constData(), &ok); + } + } else { + // Otherwise we have to search the whole type + if (type) { + value = type->enumValue(QHashedStringRef(enumValue), &ok); + } else { + QByteArray enumName = enumValue.toUtf8(); + const QMetaObject *metaObject = StaticQtMetaObject::get(); + for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) { + QMetaEnum e = metaObject->enumerator(ii); + value = e.keyToValue(enumName.constData(), &ok); + } + } + } + + if (!ok) + return true; + + binding->type = QV4::CompiledData::Binding::Type_Number; + binding->value.d = (double)value; + binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum; + return true; +} + +int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const +{ + Q_ASSERT_X(ok, "QQmlEnumTypeResolver::evaluateEnum", "ok must not be a null pointer"); + *ok = false; + + if (scope != QLatin1String("Qt")) { + QQmlType *type = 0; + imports->resolveType(scope, &type, 0, 0, 0); + return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; + } + + const QMetaObject *mo = StaticQtMetaObject::get(); + int i = mo->enumeratorCount(); + while (i--) { + int v = mo->enumerator(i).keyToValue(enumValue.constData(), ok); + if (*ok) + return v; + } + return -1; +} + + +QQmlAliasAnnotator::QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , qmlObjects(*typeCompiler->qmlObjects()) + , propertyCaches(typeCompiler->propertyCaches()) +{ +} + +void QQmlAliasAnnotator::annotateBindingsToAliases() +{ + for (int i = 0; i < qmlObjects.count(); ++i) { + QQmlPropertyCache *propertyCache = propertyCaches.at(i); + if (!propertyCache) + continue; + + const QmlObject *obj = qmlObjects.at(i); + + PropertyResolver resolver(propertyCache); + QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); + + for (QtQml::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; + if (pd && pd->isAlias()) + binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias; + } + } +} + QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler) : QQmlCompilePass(typeCompiler) , enginePrivate(typeCompiler->enginePrivate()) @@ -818,10 +1046,10 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QtQm QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); - for (QtQml::Binding *binding = obj->bindings->first; binding; binding = binding->next) { + for (QtQml::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { if (binding->type != QV4::CompiledData::Binding::Type_Object) continue; - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) continue; const QtQml::QmlObject *targetObject = qmlObjects->at(binding->value.objectIndex); @@ -832,10 +1060,9 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QtQm continue; QQmlPropertyData *pd = 0; - QString propertyName = stringAt(binding->propertyNameIndex); - if (!propertyName.isEmpty()) { + if (binding->propertyNameIndex != 0) { bool notInRevision = false; - pd = propertyResolver.property(propertyName, ¬InRevision); + pd = propertyResolver.property(stringAt(binding->propertyNameIndex), ¬InRevision); } else { pd = defaultProperty; } @@ -873,7 +1100,9 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QtQm QtQml::Binding *syntheticBinding = pool->New<QtQml::Binding>(); *syntheticBinding = *binding; syntheticBinding->type = QV4::CompiledData::Binding::Type_Object; - syntheticComponent->bindings->append(syntheticBinding); + QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false); + Q_ASSERT(error.isEmpty()); + Q_UNUSED(error); binding->value.objectIndex = componentIndex; @@ -891,7 +1120,7 @@ bool QQmlComponentAndAliasResolver::resolve() const int objCountWithoutSynthesizedComponents = qmlObjects->count(); for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) { const QtQml::QmlObject *obj = qmlObjects->at(i); - if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) + if (obj->inheritedTypeNameIndex == 0) continue; QQmlCompiledData::TypeReference *tref = resolvedTypes->value(obj->inheritedTypeNameIndex); @@ -903,17 +1132,17 @@ bool QQmlComponentAndAliasResolver::resolve() componentRoots.append(i); - if (obj->functions->count > 0) + if (obj->functionCount() > 0) COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions.")); - if (obj->properties->count > 0) + if (obj->propertyCount() > 0) COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties.")); - if (obj->qmlSignals->count > 0) + if (obj->signalCount() > 0) COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals.")); - if (obj->bindings->count == 0) + if (obj->bindingCount() == 0) COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification")); - const QtQml::Binding *rootBinding = obj->bindings->first; + const QtQml::Binding *rootBinding = obj->firstBinding(); if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object) COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id")); @@ -924,7 +1153,7 @@ bool QQmlComponentAndAliasResolver::resolve() for (int i = 0; i < componentRoots.count(); ++i) { const QtQml::QmlObject *component = qmlObjects->at(componentRoots.at(i)); - const QtQml::Binding *rootBinding = component->bindings->first; + const QtQml::Binding *rootBinding = component->firstBinding(); _componentIndex = i; _idToObjectIndex.clear(); @@ -957,8 +1186,7 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) { const QtQml::QmlObject *obj = qmlObjects->at(objectIndex); - QString id = stringAt(obj->idIndex); - if (!id.isEmpty()) { + if (obj->idIndex != 0) { if (_idToObjectIndex.contains(obj->idIndex)) { recordError(obj->locationOfIdProperty, tr("id is not unique")); return false; @@ -967,14 +1195,14 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) _objectIndexToIdInScope->insert(objectIndex, _objectIndexToIdInScope->count()); } - for (QtQml::QmlProperty *property = obj->properties->first; property; property = property->next) { + for (const QtQml::QmlProperty *property = obj->firstProperty(); property; property = property->next) { if (property->type == QV4::CompiledData::Property::Alias) { _objectsWithAliases.append(objectIndex); break; } } - for (QtQml::Binding *binding = obj->bindings->first; binding; binding = binding->next) { + for (const QtQml::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { if (binding->type != QV4::CompiledData::Binding::Type_Object && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) @@ -1003,8 +1231,8 @@ bool QQmlComponentAndAliasResolver::resolveAliases() int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count(); int effectiveAliasIndex = 0; - const QtQml::QmlProperty *p = obj->properties->first; - for (int propertyIndex = 0; propertyIndex < obj->properties->count; ++propertyIndex, p = p->next) { + const QtQml::QmlProperty *p = obj->firstProperty(); + for (int propertyIndex = 0; propertyIndex < obj->propertyCount(); ++propertyIndex, p = p->next) { if (p->type != QV4::CompiledData::Property::Alias) continue; @@ -1141,6 +1369,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases() QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, const QVector<int> &runtimeFunctionIndices) : QQmlCompilePass(typeCompiler) + , enginePrivate(typeCompiler->enginePrivate()) , qmlUnit(typeCompiler->qmlUnit()) , resolvedTypes(*typeCompiler->resolvedTypes()) , propertyCaches(typeCompiler->propertyCaches()) @@ -1152,7 +1381,7 @@ QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, con bool QQmlPropertyValidator::validate() { - if (!validateObject(qmlUnit->indexOfRootObject)) + if (!validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0)) return false; compiler->setCustomParserBindings(customParserBindings); return true; @@ -1179,28 +1408,39 @@ QQmlBinding::Identifier QQmlPropertyValidator::bindingIdentifier(const QV4::Comp return id; } -bool QQmlPropertyValidator::validateObject(int objectIndex) +bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding) { const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex); - if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) - return true; - if (isComponent(objectIndex)) - return true; + if (isComponent(objectIndex)) { + 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); - Q_ASSERT(propertyCache); + if (!propertyCache) + return true; QQmlCustomParser *customParser = 0; QQmlCompiledData::TypeReference *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex); - Q_ASSERT(objectType); - if (objectType->type) + if (objectType && objectType->type) customParser = objectType->type->customParser(); QList<const QV4::CompiledData::Binding*> customBindings; PropertyResolver propertyResolver(propertyCache); - QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); + QString defaultPropertyName; + QQmlPropertyData *defaultProperty = 0; + if (obj->indexOfDefaultProperty != -1) { + QQmlPropertyCache *cache = propertyCache->parent(); + defaultPropertyName = cache->defaultPropertyName(); + defaultProperty = cache->defaultProperty(); + } else { + defaultPropertyName = propertyCache->defaultPropertyName(); + defaultProperty = propertyCache->defaultProperty(); + } const QV4::CompiledData::Binding *binding = obj->bindingTable(); for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { @@ -1223,39 +1463,76 @@ bool QQmlPropertyValidator::validateObject(int objectIndex) } if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - if (!validateObject(binding->value.objectIndex)) + if (!validateObject(binding->value.objectIndex, binding)) return false; // Nothing further to check for attached properties. if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) continue; } - const QString name = stringAt(binding->propertyNameIndex); + // 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; + + QString name = stringAt(binding->propertyNameIndex); bool bindingToDefaultProperty = false; bool notInRevision = false; QQmlPropertyData *pd = 0; if (!name.isEmpty()) { - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) pd = propertyResolver.signal(name, ¬InRevision); else pd = propertyResolver.property(name, ¬InRevision); if (notInRevision) { QString typeName = stringAt(obj->inheritedTypeNameIndex); - if (objectType->type) { + 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)); } } } else { + if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) + COMPILE_EXCEPTION(binding, tr("Cannot assign a value directly to a grouped property")); + pd = defaultProperty; + name = defaultPropertyName; bindingToDefaultProperty = true; } - if (!pd) { + if (pd) { + if (!pd->isWritable() + && !pd->isQList() + && binding->type != QV4::CompiledData::Binding::Type_GroupProperty + && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration) + ) { + 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 (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 (customParser) { customBindings << binding; continue; @@ -1285,4 +1562,347 @@ bool QQmlPropertyValidator::validateObject(int objectIndex) return true; } +bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) +{ + if (property->isEnum()) { + if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) + return true; + + QString value = binding->valueAsString(&qmlUnit->header); + 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->type != QV4::CompiledData::Binding::Type_String) { + recordError(binding->valueLocation, tr("Invalid property assignment: string expected")); + return false; + } + } + break; + case QVariant::StringList: { + if (binding->type != QV4::CompiledData::Binding::Type_String) { + 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->header), &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->header), &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->header), &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->header), &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->header), &ok).toPoint(); + 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->header), &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->header), &ok).toSize(); + 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->header), &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->header), &ok).toRect(); + 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->header), &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::Vector3D: { + struct { + float xp; + float yp; + float zy; + } vec; + if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(&qmlUnit->header), &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->header), &vec, sizeof(vec))) { + recordError(binding->valueLocation, tr("Invalid property assignment: 4D vector 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: real or array of reals 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; + } + break; + } else if (property->propType == qMetaTypeId<QList<QString> >()) { + if (binding->type != QV4::CompiledData::Binding::Type_String) { + 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->location, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType)))); + return false; + } + } + break; + } + return true; +} + +bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) +{ + 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 (isComponent(binding->value.objectIndex)) + 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()) { + // ### TODO: list error handling + 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 { + // 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.value(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; +} + QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 28229a13a9..2e97d13481 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -139,6 +139,42 @@ protected: QVector<QQmlPropertyCache*> propertyCaches; }; +// ### This will go away when the codegen resolves all enums to constant expressions +// and we replace the constant expression with a literal binding instead of using +// a script. +class QQmlEnumTypeResolver : public QQmlCompilePass +{ + Q_DECLARE_TR_FUNCTIONS(QQmlEnumTypeResolver) +public: + QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler); + + bool resolveEnumBindings(); + +private: + bool tryQualifiedEnumAssignment(const QmlObject *obj, const QQmlPropertyCache *propertyCache, + const QQmlPropertyData *prop, + QtQml::Binding *binding); + int evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const; + + + const QList<QtQml::QmlObject*> &qmlObjects; + const QVector<QQmlPropertyCache *> propertyCaches; + const QQmlImports *imports; + QHash<int, QQmlCompiledData::TypeReference *> *resolvedTypes; +}; + +// Annotate properties bound to aliases with a flag +class QQmlAliasAnnotator : public QQmlCompilePass +{ +public: + QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler); + + void annotateBindingsToAliases(); +private: + const QList<QtQml::QmlObject*> &qmlObjects; + const QVector<QQmlPropertyCache *> propertyCaches; +}; + class QQmlComponentAndAliasResolver : public QQmlCompilePass { Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver) @@ -190,10 +226,13 @@ public: virtual QQmlBinding::Identifier bindingIdentifier(const QV4::CompiledData::Binding *binding, QQmlCustomParser *parser); private: - bool validateObject(int objectIndex); + bool validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding); + bool validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding); + bool validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding); bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } + QQmlEnginePrivate *enginePrivate; const QV4::CompiledData::QmlUnit *qmlUnit; const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes; const QVector<QQmlPropertyCache *> &propertyCaches; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 717e7f4ba2..aa8db06fd5 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -67,15 +67,15 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) assert(!runtimeStrings); assert(data); - runtimeStrings = (QV4::SafeString *)malloc(data->stringTableSize * sizeof(QV4::SafeString)); + runtimeStrings = (QV4::StringValue *)malloc(data->stringTableSize * sizeof(QV4::StringValue)); // memset the strings to 0 in case a GC run happens while we're within the loop below - memset(runtimeStrings, 0, data->stringTableSize * sizeof(QV4::SafeString)); + memset(runtimeStrings, 0, data->stringTableSize * sizeof(QV4::StringValue)); for (uint i = 0; i < data->stringTableSize; ++i) runtimeStrings[i] = engine->newIdentifier(data->stringAt(i)); - runtimeRegularExpressions = new QV4::SafeValue[data->regexpTableSize]; + runtimeRegularExpressions = new QV4::Value[data->regexpTableSize]; // memset the regexps to 0 in case a GC run happens while we're within the loop below - memset(runtimeRegularExpressions, 0, data->regexpTableSize * sizeof(QV4::SafeValue)); + memset(runtimeRegularExpressions, 0, data->regexpTableSize * sizeof(QV4::Value)); for (uint i = 0; i < data->regexpTableSize; ++i) { const CompiledData::RegExp *re = data->regexpAt(i); int flags = 0; @@ -95,21 +95,24 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) for (uint i = 0; i < data->lookupTableSize; ++i) { QV4::Lookup *l = runtimeLookups + i; - if (compiledLookups[i].type_and_flags == CompiledData::Lookup::Type_Getter) + Lookup::Type type = Lookup::Type(compiledLookups[i].type_and_flags); + if (type == CompiledData::Lookup::Type_Getter) l->getter = QV4::Lookup::getterGeneric; - else if (compiledLookups[i].type_and_flags == CompiledData::Lookup::Type_Setter) + else if (type == CompiledData::Lookup::Type_Setter) l->setter = QV4::Lookup::setterGeneric; - else if (compiledLookups[i].type_and_flags == CompiledData::Lookup::Type_GlobalGetter) + else if (type == CompiledData::Lookup::Type_GlobalGetter) l->globalGetter = QV4::Lookup::globalGetterGeneric; - else if (compiledLookups[i].type_and_flags == CompiledData::Lookup::Type_IndexedGetter) + else if (type == CompiledData::Lookup::Type_IndexedGetter) l->indexedGetter = QV4::Lookup::indexedGetterGeneric; + else if (type == CompiledData::Lookup::Type_IndexedSetter) + l->indexedSetter = QV4::Lookup::indexedSetterGeneric; for (int j = 0; j < QV4::Lookup::Size; ++j) l->classList[j] = 0; l->level = -1; l->index = UINT_MAX; l->name = runtimeStrings[compiledLookups[i].nameIndex].asString(); - if (compiledLookups[i].type_and_flags == CompiledData::Lookup::Type_IndexedGetter) + if (type == CompiledData::Lookup::Type_IndexedGetter || type == CompiledData::Lookup::Type_IndexedSetter) l->engine = engine; } } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 4208ec9441..94ea4bdc90 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -45,7 +45,7 @@ #include <QVector> #include <QStringList> #include <QHash> -#include <private/qv4value_def_p.h> +#include <private/qv4value_p.h> #include <private/qv4executableallocator_p.h> QT_BEGIN_NAMESPACE @@ -70,17 +70,32 @@ struct RegExp; struct Location { - int line; - int column; + qint32 line; + qint32 column; + inline bool operator<(const Location &other) const { + return line < other.line || + (line == other.line && column < other.column); + } +}; + +struct TypeReference +{ + TypeReference(const Location &loc) + : location(loc) + , needsCreation(false) + {} + Location location; // first use + bool needsCreation; // whether the type needs to be creatable or not }; // map from name index to location of first use -struct TypeReferenceMap : QHash<int, Location> +struct TypeReferenceMap : QHash<int, TypeReference> { - void add(int nameIndex, const Location &loc) { - if (contains(nameIndex)) - return; - insert(nameIndex, loc); + TypeReference &add(int nameIndex, const Location &loc) { + Iterator it = find(nameIndex); + if (it != end()) + return *it; + return *insert(nameIndex, loc); } }; @@ -103,7 +118,8 @@ struct Lookup Type_Getter = 0x0, Type_Setter = 0x1, Type_GlobalGetter = 2, - Type_IndexedGetter = 3 + Type_IndexedGetter = 3, + Type_IndexedSetter = 4 }; quint32 type_and_flags; @@ -189,8 +205,8 @@ struct Unit const RegExp *regexpAt(int index) const { return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp)); } - const QV4::SafeValue *constants() const { - return reinterpret_cast<const QV4::SafeValue*>(reinterpret_cast<const char *>(this) + offsetToConstantTable); + const QV4::Value *constants() const { + return reinterpret_cast<const QV4::Value*>(reinterpret_cast<const char *>(this) + offsetToConstantTable); } const JSClassMember *jsClassAt(int idx, int *nMembers) const { @@ -282,7 +298,12 @@ struct Q_QML_EXPORT Binding enum Flags { IsSignalHandlerExpression = 0x1, - IsOnAssignment = 0x2 + IsSignalHandlerObject = 0x2, + IsOnAssignment = 0x4, + InitializerForReadOnlyDeclaration = 0x8, + IsResolvedEnum = 0x10, + IsListItem = 0x20, + IsBindingToAlias = 0x40 }; quint32 flags : 16; @@ -296,6 +317,54 @@ struct Q_QML_EXPORT Binding quint32 stringIndex; // Set for Type_String and Type_Script (the latter because of script strings) Location location; + Location valueLocation; + + bool isValueBinding() const + { + if (type == Type_AttachedProperty + || type == Type_GroupProperty) + return false; + if (flags & IsSignalHandlerExpression + || flags & IsSignalHandlerObject) + return false; + return true; + } + + bool isValueBindingNoAlias() const { return isValueBinding() && !(flags & IsBindingToAlias); } + bool isValueBindingToAlias() const { return isValueBinding() && (flags & IsBindingToAlias); } + + bool isSignalHandler() const + { + if (flags & IsSignalHandlerExpression || flags & IsSignalHandlerObject) { + Q_ASSERT(!isValueBinding()); + Q_ASSERT(!isAttachedProperty()); + Q_ASSERT(!isGroupProperty()); + return true; + } + return false; + } + + bool isAttachedProperty() const + { + if (type == Type_AttachedProperty) { + Q_ASSERT(!isValueBinding()); + Q_ASSERT(!isSignalHandler()); + Q_ASSERT(!isGroupProperty()); + return true; + } + return false; + } + + bool isGroupProperty() const + { + if (type == Type_GroupProperty) { + Q_ASSERT(!isValueBinding()); + Q_ASSERT(!isSignalHandler()); + Q_ASSERT(!isAttachedProperty()); + return true; + } + return false; + } QString valueAsString(const Unit *unit) const; QString valueAsScriptString(const Unit *unit) const; @@ -494,9 +563,9 @@ struct Q_QML_EXPORT CompilationUnit QString fileName() const { return data->stringAt(data->sourceFileIndex); } - QV4::SafeString *runtimeStrings; // Array + QV4::StringValue *runtimeStrings; // Array QV4::Lookup *runtimeLookups; - QV4::SafeValue *runtimeRegularExpressions; + QV4::Value *runtimeRegularExpressions; QV4::InternalClass **runtimeClasses; QVector<QV4::Function *> runtimeFunctions; // QVector<QV4::Function *> runtimeFunctionsSortedByAddress; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index d18c43a969..bc47b815f2 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -53,6 +53,8 @@ QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QQmlJS::V4IR::Module *module, in if (headerSize == -1) headerSize = sizeof(QV4::CompiledData::Unit); this->headerSize = headerSize; + // Make sure the empty string always gets index 0 + registerString(QString()); } int QV4::Compiler::JSUnitGenerator::registerString(const QString &str) @@ -81,6 +83,15 @@ uint QV4::Compiler::JSUnitGenerator::registerIndexedGetterLookup() return lookups.size() - 1; } +uint QV4::Compiler::JSUnitGenerator::registerIndexedSetterLookup() +{ + CompiledData::Lookup l; + l.type_and_flags = CompiledData::Lookup::Type_IndexedSetter; + l.nameIndex = 0; + lookups << l; + return lookups.size() - 1; +} + uint QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name) { CompiledData::Lookup l; diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 4779b5d3c4..6baefae7b7 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -71,6 +71,7 @@ struct Q_QML_EXPORT JSUnitGenerator { uint registerSetterLookup(const QString &name); uint registerGlobalGetterLookup(const QString &name); uint registerIndexedGetterLookup(); + uint registerIndexedSetterLookup(); int registerRegExp(QQmlJS::V4IR::RegExp *regexp); diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index a2cc051003..e3e227796e 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -43,7 +43,7 @@ #define QV4INSTR_MOTH_P_H #include <QtCore/qglobal.h> -#include <private/qv4value_def_p.h> +#include <private/qv4value_p.h> #include <private/qv4function_p.h> #include <private/qv4runtime_p.h> @@ -51,16 +51,20 @@ QT_BEGIN_NAMESPACE #define FOR_EACH_MOTH_INSTR(F) \ F(Ret, ret) \ + F(Debug, debug) \ F(LoadRuntimeString, loadRuntimeString) \ F(LoadRegExp, loadRegExp) \ F(LoadClosure, loadClosure) \ F(Move, move) \ + F(MoveConst, moveConst) \ F(SwapTemps, swapTemps) \ F(LoadName, loadName) \ F(GetGlobalLookup, getGlobalLookup) \ F(StoreName, storeName) \ F(LoadElement, loadElement) \ + F(LoadElementLookup, loadElementLookup) \ F(StoreElement, storeElement) \ + F(StoreElementLookup, storeElementLookup) \ F(LoadProperty, loadProperty) \ F(GetLookup, getLookup) \ F(StoreProperty, storeProperty) \ @@ -103,7 +107,8 @@ QT_BEGIN_NAMESPACE F(CreateActivationProperty, createActivationProperty) \ F(ConstructGlobalLookup, constructGlobalLookup) \ F(Jump, jump) \ - F(CJump, cjump) \ + F(JumpEq, jumpEq) \ + F(JumpNe, jumpNe) \ F(UNot, unot) \ F(UNotBool, unotBool) \ F(UPlus, uplus) \ @@ -117,15 +122,16 @@ QT_BEGIN_NAMESPACE F(BitAnd, bitAnd) \ F(BitOr, bitOr) \ F(BitXor, bitXor) \ + F(Shr, shr) \ + F(Shl, shl) \ F(BitAndConst, bitAndConst) \ F(BitOrConst, bitOrConst) \ F(BitXorConst, bitXorConst) \ + F(ShrConst, shrConst) \ + F(ShlConst, shlConst) \ F(Mul, mul) \ F(Sub, sub) \ F(BinopContext, binopContext) \ - F(AddNumberParams, addNumberParams) \ - F(MulNumberParams, mulNumberParams) \ - F(SubNumberParams, subNumberParams) \ F(LoadThis, loadThis) \ F(LoadQmlIdArray, loadQmlIdArray) \ F(LoadQmlImportedScripts, loadQmlImportedScripts) \ @@ -140,11 +146,9 @@ QT_BEGIN_NAMESPACE #define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlJS::Moth::Instr) - 1) #ifdef MOTH_THREADED_INTERPRETER -# define MOTH_INSTR_HEADER void *code; \ - unsigned int breakPoint : 1; +# define MOTH_INSTR_HEADER void *code; #else -# define MOTH_INSTR_HEADER quint8 instructionType; \ - unsigned int breakPoint : 1; +# define MOTH_INSTR_HEADER quint32 instructionType; #endif #define MOTH_INSTR_ENUM(I, FMT) I, @@ -232,6 +236,10 @@ union Instr MOTH_INSTR_HEADER Param result; }; + struct instr_debug { + MOTH_INSTR_HEADER + quint32 breakPoint; + }; struct instr_loadRuntimeString { MOTH_INSTR_HEADER int stringId; @@ -247,6 +255,11 @@ union Instr Param source; Param result; }; + struct instr_moveConst { + MOTH_INSTR_HEADER + QV4::ReturnedValue source; + Param result; + }; struct instr_swapTemps { MOTH_INSTR_HEADER Param left; @@ -322,12 +335,26 @@ union Instr Param index; Param result; }; + struct instr_loadElementLookup { + MOTH_INSTR_HEADER + uint lookup; + Param base; + Param index; + Param result; + }; struct instr_storeElement { MOTH_INSTR_HEADER Param base; Param index; Param source; }; + struct instr_storeElementLookup { + MOTH_INSTR_HEADER + uint lookup; + Param base; + Param index; + Param source; + }; struct instr_push { MOTH_INSTR_HEADER quint32 value; @@ -527,11 +554,15 @@ union Instr MOTH_INSTR_HEADER ptrdiff_t offset; }; - struct instr_cjump { + struct instr_jumpEq { + MOTH_INSTR_HEADER + ptrdiff_t offset; + Param condition; + }; + struct instr_jumpNe { MOTH_INSTR_HEADER ptrdiff_t offset; Param condition; - bool invert; }; struct instr_unot { MOTH_INSTR_HEADER @@ -604,57 +635,63 @@ union Instr Param rhs; Param result; }; - struct instr_bitAndConst { + struct instr_shr { MOTH_INSTR_HEADER Param lhs; - int rhs; + Param rhs; Param result; }; - struct instr_bitOrConst { + struct instr_shl { + MOTH_INSTR_HEADER + Param lhs; + Param rhs; + Param result; + }; + struct instr_bitAndConst { MOTH_INSTR_HEADER Param lhs; int rhs; Param result; }; - struct instr_bitXorConst { + struct instr_bitOrConst { MOTH_INSTR_HEADER Param lhs; int rhs; Param result; }; - struct instr_mul { + struct instr_bitXorConst { MOTH_INSTR_HEADER Param lhs; - Param rhs; + int rhs; Param result; }; - struct instr_sub { + struct instr_shrConst { MOTH_INSTR_HEADER Param lhs; - Param rhs; + int rhs; Param result; }; - struct instr_binopContext { + struct instr_shlConst { MOTH_INSTR_HEADER - QV4::BinOpContext alu; Param lhs; - Param rhs; + int rhs; Param result; }; - struct instr_addNumberParams { + struct instr_mul { MOTH_INSTR_HEADER Param lhs; Param rhs; Param result; }; - struct instr_mulNumberParams { + struct instr_sub { MOTH_INSTR_HEADER Param lhs; Param rhs; Param result; }; - struct instr_subNumberParams { + struct instr_binopContext { MOTH_INSTR_HEADER + QV4::BinOpContext alu; Param lhs; Param rhs; Param result; @@ -687,16 +724,20 @@ union Instr instr_common common; instr_ret ret; + instr_debug debug; instr_loadRuntimeString loadRuntimeString; instr_loadRegExp loadRegExp; instr_move move; + instr_moveConst moveConst; instr_swapTemps swapTemps; instr_loadClosure loadClosure; instr_loadName loadName; instr_getGlobalLookup getGlobalLookup; instr_storeName storeName; instr_loadElement loadElement; + instr_loadElementLookup loadElementLookup; instr_storeElement storeElement; + instr_storeElementLookup storeElementLookup; instr_loadProperty loadProperty; instr_getLookup getLookup; instr_loadQObjectProperty loadQObjectProperty; @@ -739,7 +780,8 @@ union Instr instr_createActivationProperty createActivationProperty; instr_constructGlobalLookup constructGlobalLookup; instr_jump jump; - instr_cjump cjump; + instr_jumpEq jumpEq; + instr_jumpNe jumpNe; instr_unot unot; instr_unotBool unotBool; instr_uplus uplus; @@ -753,15 +795,16 @@ union Instr instr_bitAnd bitAnd; instr_bitOr bitOr; instr_bitXor bitXor; + instr_shr shr; + instr_shl shl; instr_bitAndConst bitAndConst; instr_bitOrConst bitOrConst; instr_bitXorConst bitXorConst; + instr_shrConst shrConst; + instr_shlConst shlConst; instr_mul mul; instr_sub sub; instr_binopContext binopContext; - instr_addNumberParams addNumberParams; - instr_mulNumberParams mulNumberParams; - instr_subNumberParams subNumberParams; instr_loadThis loadThis; instr_loadQmlIdArray loadQmlIdArray; instr_loadQmlImportedScripts loadQmlImportedScripts; diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp index b8b16a4151..a1145c2f10 100644 --- a/src/qml/compiler/qv4isel_masm.cpp +++ b/src/qml/compiler/qv4isel_masm.cpp @@ -245,12 +245,12 @@ Assembler::Pointer Assembler::loadTempAddress(RegisterID baseReg, V4IR::Temp *t) case V4IR::Temp::Formal: case V4IR::Temp::ScopedFormal: { loadPtr(Address(context, qOffsetOf(ExecutionContext, callData)), baseReg); - offset = sizeof(CallData) + (t->index - 1) * sizeof(SafeValue); + offset = sizeof(CallData) + (t->index - 1) * sizeof(Value); } break; case V4IR::Temp::Local: case V4IR::Temp::ScopedLocal: { loadPtr(Address(context, qOffsetOf(CallContext, locals)), baseReg); - offset = t->index * sizeof(SafeValue); + offset = t->index * sizeof(Value); } break; case V4IR::Temp::StackSlot: { return stackSlotPointer(t); @@ -266,7 +266,7 @@ Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &s loadPtr(Address(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, compilationUnit)), Assembler::ScratchRegister); loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(QV4::CompiledData::CompilationUnit, runtimeStrings)), reg); const int id = _isel->registerString(string); - return Pointer(reg, id * sizeof(QV4::SafeString)); + return Pointer(reg, id * sizeof(QV4::StringValue)); } void Assembler::loadStringRef(RegisterID reg, const QString &string) @@ -274,7 +274,7 @@ void Assembler::loadStringRef(RegisterID reg, const QString &string) loadPtr(Address(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, compilationUnit)), reg); loadPtr(Address(reg, qOffsetOf(QV4::CompiledData::CompilationUnit, runtimeStrings)), reg); const int id = _isel->registerString(string); - addPtr(TrustedImmPtr(id * sizeof(QV4::SafeString)), reg); + addPtr(TrustedImmPtr(id * sizeof(QV4::StringValue)), reg); } template <typename Result, typename Source> @@ -615,7 +615,7 @@ void InstructionSelection::run(int functionIndex) const int locals = _as->stackLayout().calculateJSStackFrameSize(); _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext, engine)), Assembler::ScratchRegister); _as->loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop)), Assembler::LocalsRegister); - _as->addPtr(Assembler::TrustedImm32(sizeof(QV4::SafeValue)*locals), Assembler::LocalsRegister); + _as->addPtr(Assembler::TrustedImm32(sizeof(QV4::Value)*locals), Assembler::LocalsRegister); _as->storePtr(Assembler::LocalsRegister, Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop))); int lastLine = -1; @@ -1023,107 +1023,19 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR: return; } -#if 0 // QT_POINTER_SIZE == 8 - V4IR::Temp *tbase = base->asTemp(); - V4IR::Temp *tindex = index->asTemp(); - if (tbase && tindex && - tbase->kind != V4IR::Temp::PhysicalRegister) { - Assembler::Pointer addr = _as->loadTempAddress(Assembler::ReturnValueRegister, tbase); - _as->load64(addr, Assembler::ScratchRegister); - _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsManaged_Shift), Assembler::ReturnValueRegister); - Assembler::Jump notManaged = _as->branch64(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm64(0)); - // check whether we have an object with a simple array - // ### need to check we have an object first! - Assembler::Address managedType(Assembler::ScratchRegister, qOffsetOf(QV4::Object, arrayData) + qOffsetOf(QV4::ArrayData, flags)); - _as->load8(managedType, Assembler::ReturnValueRegister); - _as->and32(Assembler::TrustedImm32(QV4::ArrayData::SimpleArray), Assembler::ReturnValueRegister); - Assembler::Jump notSimple = _as->branch32(Assembler::Equal, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); - - bool needNegativeCheck = false; - Assembler::Jump fallback, fallback2; - if (tindex->kind == V4IR::Temp::PhysicalRegister) { - if (tindex->type == V4IR::SInt32Type) { - fallback = _as->branch32(Assembler::LessThan, (Assembler::RegisterID)tindex->index, Assembler::TrustedImm32(0)); - _as->move((Assembler::RegisterID) tindex->index, Assembler::ScratchRegister); - needNegativeCheck = true; - } else { - // double, convert and check if it's a int - fallback2 = _as->branchTruncateDoubleToUint32((Assembler::FPRegisterID) tindex->index, Assembler::ScratchRegister); - _as->convertInt32ToDouble(Assembler::ScratchRegister, Assembler::FPGpr0); - fallback = _as->branchDouble(Assembler::DoubleNotEqual, Assembler::FPGpr0, (Assembler::FPRegisterID) tindex->index); - } - } else { - Assembler::Pointer indexAddr = _as->loadTempAddress(Assembler::ReturnValueRegister, tindex); - _as->load64(indexAddr, Assembler::ScratchRegister); - _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsNumber_Shift), Assembler::ReturnValueRegister); - Assembler::Jump isInteger = _as->branch64(Assembler::Equal, Assembler::ReturnValueRegister, Assembler::TrustedImm64(1)); - - // other type, convert to double and check if it's a int - // this check is ok to do even if the type is something else than a double, as - // that would result in a NaN - _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ReturnValueRegister); - _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - _as->move64ToDouble(Assembler::ReturnValueRegister, Assembler::FPGpr0); - fallback2 = _as->branchTruncateDoubleToUint32(Assembler::FPGpr0, Assembler::ScratchRegister); - _as->convertInt32ToDouble(Assembler::ScratchRegister, Assembler::FPGpr1); - fallback = _as->branchDouble(Assembler::DoubleNotEqualOrUnordered, Assembler::FPGpr0, Assembler::FPGpr1); - - isInteger.link(_as); - _as->or32(Assembler::TrustedImm32(0), Assembler::ScratchRegister); - needNegativeCheck = true; - } - - // get data, ScratchRegister holds index - addr = _as->loadTempAddress(Assembler::ReturnValueRegister, tbase); - _as->load64(addr, Assembler::ReturnValueRegister); - Address dataLen(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData) + qOffsetOf(ArrayData, length)); - Assembler::Jump outOfRange; - if (needNegativeCheck) - outOfRange = _as->branch32(Assembler::LessThan, Assembler::ScratchRegister, Assembler::TrustedImm32(0)); - Assembler::Jump outOfRange2 = _as->branch32(Assembler::GreaterThanOrEqual, Assembler::ScratchRegister, dataLen); - Address arrayData(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData) + qOffsetOf(ArrayData, data)); - _as->load64(arrayData, Assembler::ReturnValueRegister); - Q_ASSERT(sizeof(Property) == (1<<4)); - _as->lshift64(Assembler::TrustedImm32(4), Assembler::ScratchRegister); - _as->add64(Assembler::ReturnValueRegister, Assembler::ScratchRegister); - Address value(Assembler::ScratchRegister, qOffsetOf(Property, value)); - _as->load64(value, Assembler::ReturnValueRegister); - - // check that the value is not empty - _as->move(Assembler::ReturnValueRegister, Assembler::ScratchRegister); - _as->urshift64(Assembler::TrustedImm32(32), Assembler::ScratchRegister); - Assembler::Jump emptyValue = _as->branch32(Assembler::Equal, Assembler::TrustedImm32(QV4::Value::Empty_Type), Assembler::ScratchRegister); - _as->storeReturnValue(target); - - Assembler::Jump done = _as->jump(); - - emptyValue.link(_as); - if (outOfRange.isSet()) - outOfRange.link(_as); - outOfRange2.link(_as); - if (fallback.isSet()) - fallback.link(_as); - if (fallback2.isSet()) - fallback2.link(_as); - notSimple.link(_as); - notManaged.link(_as); - - generateFunctionCall(target, __qmljs_get_element, Assembler::ContextRegister, - Assembler::PointerToValue(base), Assembler::PointerToValue(index)); - - done.link(_as); - return; - } -#endif - generateFunctionCall(target, __qmljs_get_element, Assembler::ContextRegister, Assembler::PointerToValue(base), Assembler::PointerToValue(index)); } void InstructionSelection::setElement(V4IR::Expr *source, V4IR::Expr *targetBase, V4IR::Expr *targetIndex) { + if (useFastLookups) { + uint lookup = registerIndexedSetterLookup(); + generateLookupCall(Assembler::Void, lookup, qOffsetOf(QV4::Lookup, indexedSetter), + Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex), + Assembler::PointerToValue(source)); + return; + } generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex), Assembler::PointerToValue(source)); @@ -2019,7 +1931,7 @@ void InstructionSelection::visitRet(V4IR::Ret *s) _as->exceptionReturnLabel = _as->label(); const int locals = _as->stackLayout().calculateJSStackFrameSize(); - _as->subPtr(Assembler::TrustedImm32(sizeof(QV4::SafeValue)*locals), Assembler::LocalsRegister); + _as->subPtr(Assembler::TrustedImm32(sizeof(QV4::Value)*locals), Assembler::LocalsRegister); _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext, engine)), Assembler::ScratchRegister); _as->storePtr(Assembler::LocalsRegister, Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop))); diff --git a/src/qml/compiler/qv4isel_masm_p.h b/src/qml/compiler/qv4isel_masm_p.h index a146220015..b1e981533b 100644 --- a/src/qml/compiler/qv4isel_masm_p.h +++ b/src/qml/compiler/qv4isel_masm_p.h @@ -45,7 +45,7 @@ #include "qv4jsir_p.h" #include "qv4isel_p.h" #include "qv4isel_util_p.h" -#include "private/qv4value_def_p.h" +#include "private/qv4value_p.h" #include "private/qv4lookup_p.h" #include <QtCore/QHash> @@ -351,7 +351,7 @@ public: // space for the callee saved registers int frameSize = RegisterSize * calleeSavedRegisterCount; - frameSize += savedRegCount * sizeof(QV4::SafeValue); // these get written out as Values, not as native registers + frameSize += savedRegCount * sizeof(QV4::Value); // these get written out as Values, not as native registers frameSize = WTF::roundUpToMultipleOf(StackAlignment, frameSize + stackSpaceAllocatedOtherwise); frameSize -= stackSpaceAllocatedOtherwise; @@ -361,8 +361,8 @@ public: int calculateJSStackFrameSize() const { - const int locals = (localCount + sizeof(QV4::CallData)/sizeof(QV4::SafeValue) - 1 + maxOutgoingArgumentCount) + 1; - int frameSize = locals * sizeof(QV4::SafeValue); + const int locals = (localCount + sizeof(QV4::CallData)/sizeof(QV4::Value) - 1 + maxOutgoingArgumentCount) + 1; + int frameSize = locals * sizeof(QV4::Value); return frameSize; } @@ -372,7 +372,7 @@ public: Q_ASSERT(idx < localCount); Pointer addr = callDataAddress(0); - addr.offset -= sizeof(QV4::SafeValue) * (idx + 1); + addr.offset -= sizeof(QV4::Value) * (idx + 1); return addr; } @@ -384,11 +384,11 @@ public: Q_ASSERT(argument < maxOutgoingArgumentCount); const int index = maxOutgoingArgumentCount - argument; - return Pointer(Assembler::LocalsRegister, sizeof(QV4::SafeValue) * (-index)); + return Pointer(Assembler::LocalsRegister, sizeof(QV4::Value) * (-index)); } Pointer callDataAddress(int offset = 0) const { - return Pointer(Assembler::LocalsRegister, -(sizeof(QV4::CallData) + sizeof(QV4::SafeValue) * (maxOutgoingArgumentCount - 1)) + offset); + return Pointer(Assembler::LocalsRegister, -(sizeof(QV4::CallData) + sizeof(QV4::Value) * (maxOutgoingArgumentCount - 1)) + offset); } Address savedRegPointer(int offset) const @@ -396,7 +396,7 @@ public: Q_ASSERT(offset >= 0); Q_ASSERT(offset < savedRegCount); - const int off = offset * sizeof(QV4::SafeValue); + const int off = offset * sizeof(QV4::Value); return Address(Assembler::StackFrameRegister, - calleeSavedRegisterSpace() - off); } @@ -657,7 +657,7 @@ public: void storeUInt32ReturnValue(RegisterID dest) { - Pointer tmp(StackPointerRegister, -int(sizeof(QV4::SafeValue))); + Pointer tmp(StackPointerRegister, -int(sizeof(QV4::Value))); storeReturnValue(tmp); toUInt32Register(tmp, dest); } @@ -669,7 +669,7 @@ public: xor64(ScratchRegister, ReturnValueRegister); move64ToDouble(ReturnValueRegister, dest); #else - Pointer tmp(StackPointerRegister, -int(sizeof(QV4::SafeValue))); + Pointer tmp(StackPointerRegister, -int(sizeof(QV4::Value))); storeReturnValue(tmp); loadDouble(tmp, dest); #endif @@ -1580,8 +1580,8 @@ private: int prepareVariableArguments(V4IR::ExprList* args); int prepareCallData(V4IR::ExprList* args, V4IR::Expr *thisObject); - template <typename Retval, typename Arg1, typename Arg2> - void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2) + template <typename Retval, typename Arg1, typename Arg2, typename Arg3> + void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2, Arg3 arg3) { Assembler::RegisterID lookupRegister; #if CPU(ARM) @@ -1595,13 +1595,13 @@ private: getterSetter.offset += getterSetterOffset; _as->generateFunctionCallImp(retval, "lookup getter/setter", - RelativeCall(getterSetter), lookupAddr, arg1, arg2); + RelativeCall(getterSetter), lookupAddr, arg1, arg2, arg3); } - template <typename Arg1> - void generateLookupCall(uint index, uint getterSetterOffset, Arg1 arg1) + template <typename Retval, typename Arg1, typename Arg2> + void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2) { - generateLookupCall(index, getterSetterOffset, arg1, Assembler::VoidType()); + generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, Assembler::VoidType()); } /// This is a temporary method, and will be removed when registers are fully supported. diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index bf9af178fe..130725f3ed 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -47,6 +47,7 @@ #include <private/qv4function_p.h> #include <private/qv4regexpobject_p.h> #include <private/qv4compileddata_p.h> +#include <private/qqmlengine_p.h> #undef USE_TYPE_INFO @@ -150,6 +151,167 @@ inline bool isBoolType(V4IR::Expr *e) return (e->type == V4IR::BoolType); } +/* + * stack slot allocation: + * + * foreach bb do + * foreach stmt do + * if the current statement is not a phi-node: + * purge ranges that end before the current statement + * check for life ranges to activate, and if they don't have a stackslot associated then allocate one + * renumber temps to stack + * for phi nodes: check if all temps (src+dst) are assigned stack slots and marked as allocated + * if it's a jump: + * foreach phi node in the successor: + * allocate slots for each temp (both sources and targets) if they don't have one allocated already + * insert moves before the jump + */ +class AllocateStackSlots: protected ConvertTemps +{ + const QVector<V4IR::LifeTimeInterval> _intervals; + QVector<const V4IR::LifeTimeInterval *> _unhandled; + QVector<const V4IR::LifeTimeInterval *> _live; + QBitArray _slotIsInUse; + V4IR::Function *_function; + +public: + AllocateStackSlots(const QVector<V4IR::LifeTimeInterval> &intervals) + : _intervals(intervals) + , _slotIsInUse(intervals.size(), false) + , _function(0) + { + _live.reserve(8); + _unhandled.reserve(_intervals.size()); + for (int i = _intervals.size() - 1; i >= 0; --i) + _unhandled.append(&_intervals.at(i)); + } + + void forFunction(V4IR::Function *function) + { + V4IR::Optimizer::showMeTheCode(function); + _function = function; + toStackSlots(function); + +// QTextStream os(stdout, QIODevice::WriteOnly); +// os << "Frame layout:" << endl; +// foreach (int t, _stackSlotForTemp.keys()) { +// os << "\t" << t << " -> " << _stackSlotForTemp[t] << endl; +// } + } + +protected: + virtual int allocateFreeSlot() + { + for (int i = 0, ei = _slotIsInUse.size(); i != ei; ++i) { + if (!_slotIsInUse[i]) { + if (_nextUnusedStackSlot <= i) { + Q_ASSERT(_nextUnusedStackSlot == i); + _nextUnusedStackSlot = i + 1; + } + _slotIsInUse[i] = true; + return i; + } + } + + Q_UNREACHABLE(); + return -1; + } + + virtual void process(V4IR::Stmt *s) + { + Q_ASSERT(s->id > 0); + +// qDebug("L%d statement %d:", _currentBasicBlock->index, s->id); + + if (V4IR::Phi *phi = s->asPhi()) { + visitPhi(phi); + } else { + // purge ranges no longer alive: + for (int i = 0; i < _live.size(); ) { + const V4IR::LifeTimeInterval *lti = _live.at(i); + if (lti->end() < s->id) { +// qDebug() << "\t - moving temp" << lti->temp().index << "to handled, freeing slot" << _stackSlotForTemp[lti->temp().index]; + _live.remove(i); + Q_ASSERT(_slotIsInUse[_stackSlotForTemp[lti->temp().index]]); + _slotIsInUse[_stackSlotForTemp[lti->temp().index]] = false; + continue; + } else { + ++i; + } + } + + // active new ranges: + while (!_unhandled.isEmpty()) { + const V4IR::LifeTimeInterval *lti = _unhandled.last(); + if (lti->start() > s->id) + break; // we're done + Q_ASSERT(!_stackSlotForTemp.contains(lti->temp().index)); + _stackSlotForTemp[lti->temp().index] = allocateFreeSlot(); +// qDebug() << "\t - activating temp" << lti->temp().index << "on slot" << _stackSlotForTemp[lti->temp().index]; + _live.append(lti); + _unhandled.removeLast(); + } + + s->accept(this); + } + + if (V4IR::Jump *jump = s->asJump()) { + V4IR::MoveMapping moves; + foreach (V4IR::Stmt *succStmt, jump->target->statements) { + if (V4IR::Phi *phi = succStmt->asPhi()) { + forceActivation(*phi->targetTemp); + for (int i = 0, ei = phi->d->incoming.size(); i != ei; ++i) { + V4IR::Expr *e = phi->d->incoming[i]; + if (V4IR::Temp *t = e->asTemp()) { + forceActivation(*t); + } + if (jump->target->in[i] == _currentBasicBlock) + moves.add(phi->d->incoming[i], phi->targetTemp); + } + } else { + break; + } + } + moves.order(); + QList<V4IR::Move *> newMoves = moves.insertMoves(_currentBasicBlock, _function, true); + foreach (V4IR::Move *move, newMoves) + move->accept(this); + } + } + + void forceActivation(const V4IR::Temp &t) + { + if (_stackSlotForTemp.contains(t.index)) + return; + + int i = _unhandled.size() - 1; + for (; i >= 0; --i) { + const V4IR::LifeTimeInterval *lti = _unhandled[i]; + if (lti->temp() == t) { + _live.append(lti); + _unhandled.remove(i); + break; + } + } + Q_ASSERT(i >= 0); // check that we always found the entry + + _stackSlotForTemp[t.index] = allocateFreeSlot(); +// qDebug() << "\t - force activating temp" << t.index << "on slot" << _stackSlotForTemp[t.index]; + } + + virtual void visitPhi(V4IR::Phi *phi) + { + Q_UNUSED(phi); +#if !defined(QT_NO_DEBUG) + Q_ASSERT(_stackSlotForTemp.contains(phi->targetTemp->index)); + Q_ASSERT(_slotIsInUse[_stackSlotForTemp[phi->targetTemp->index]]); + foreach (V4IR::Expr *e, phi->d->incoming) { + if (V4IR::Temp *t = e->asTemp()) + Q_ASSERT(_stackSlotForTemp.contains(t->index)); + } +#endif // defined(QT_NO_DEBUG) + } +}; } // anonymous namespace InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) @@ -194,10 +356,19 @@ void InstructionSelection::run(int functionIndex) V4IR::Optimizer opt(_function); opt.run(qmlEngine); if (opt.isInSSA()) { - opt.convertOutOfSSA(); + static const bool doStackSlotAllocation = + qgetenv("QV4_NO_INTERPRETER_STACK_SLOT_ALLOCATION").isEmpty(); + + if (doStackSlotAllocation) { + AllocateStackSlots(opt.lifeRanges()).forFunction(_function); + } else { + opt.convertOutOfSSA(); + ConvertTemps().toStackSlots(_function); + } opt.showMeTheCode(_function); + } else { + ConvertTemps().toStackSlots(_function); } - ConvertTemps().toStackSlots(_function); QSet<V4IR::Jump *> removableJumps = opt.calculateOptionalJumps(); qSwap(_removableJumps, removableJumps); @@ -217,7 +388,13 @@ void InstructionSelection::run(int functionIndex) QVector<uint> lineNumberMappings; lineNumberMappings.reserve(_function->basicBlocks.size() * 2); + uint currentLine = -1; for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) { + if (irModule->debugMode) { + Instruction::Debug debug; + debug.breakPoint = 0; + addInstruction(debug); + } _block = _function->basicBlocks[i]; _nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0; _addrs.insert(_block, _codeNext - _codeStart); @@ -237,8 +414,15 @@ void InstructionSelection::run(int functionIndex) foreach (V4IR::Stmt *s, _block->statements) { _currentStatement = s; - if (s->location.isValid()) + if (s->location.isValid()) { lineNumberMappings << _codeNext - _codeStart << s->location.startLine; + if (irModule->debugMode && s->location.startLine != currentLine) { + Instruction::Debug debug; + debug.breakPoint = 0; + addInstruction(debug); + currentLine = s->location.startLine; + } + } s->accept(this); } @@ -323,7 +507,7 @@ void InstructionSelection::callSubscript(V4IR::Expr *base, V4IR::Expr *index, V4 void InstructionSelection::convertType(V4IR::Temp *source, V4IR::Temp *target) { // FIXME: do something more useful with this info - if (target->type & V4IR::NumberType) + if (target->type & V4IR::NumberType && !(source->type & V4IR::NumberType)) unop(V4IR::OpUPlus, source, target); else copyValue(source, target); @@ -428,8 +612,8 @@ void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targe { assert(sourceConst); - Instruction::Move move; - move.source = getParam(sourceConst); + Instruction::MoveConst move; + move.source = convertToValue(sourceConst).asReturnedValue(); move.result = getResultParam(targetTemp); addInstruction(move); } @@ -546,6 +730,15 @@ void InstructionSelection::getQObjectProperty(V4IR::Expr *base, int propertyInde void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target) { + if (useFastLookups) { + Instruction::LoadElementLookup load; + load.lookup = registerIndexedGetterLookup(); + load.base = getParam(base); + load.index = getParam(index); + load.result = getResultParam(target); + addInstruction(load); + return; + } Instruction::LoadElement load; load.base = getParam(base); load.index = getParam(index); @@ -556,6 +749,15 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR: void InstructionSelection::setElement(V4IR::Expr *source, V4IR::Expr *targetBase, V4IR::Expr *targetIndex) { + if (useFastLookups) { + Instruction::StoreElementLookup store; + store.lookup = registerIndexedSetterLookup(); + store.base = getParam(targetBase); + store.index = getParam(targetIndex); + store.source = getParam(source); + addInstruction(store); + return; + } Instruction::StoreElement store; store.base = getParam(targetBase); store.index = getParam(targetIndex); @@ -613,7 +815,8 @@ void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR:: Instruction::Move move; move.source = getParam(sourceTemp); move.result = getResultParam(targetTemp); - addInstruction(move); + if (move.source != move.result) + addInstruction(move); return; } Instruction::UPlus uplus; @@ -664,37 +867,6 @@ void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR: Param InstructionSelection::binopHelper(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) { - if (isNumberType(leftSource) && isNumberType(rightSource)) { - switch (oper) { - case V4IR::OpAdd: { - Instruction::AddNumberParams instr; - instr.lhs = getParam(leftSource); - instr.rhs = getParam(rightSource); - instr.result = getResultParam(target); - addInstruction(instr); - return instr.result; - } - case V4IR::OpMul: { - Instruction::MulNumberParams instr; - instr.lhs = getParam(leftSource); - instr.rhs = getParam(rightSource); - instr.result = getResultParam(target); - addInstruction(instr); - return instr.result; - } - case V4IR::OpSub: { - Instruction::SubNumberParams instr; - instr.lhs = getParam(leftSource); - instr.rhs = getParam(rightSource); - instr.result = getResultParam(target); - addInstruction(instr); - return instr.result; - } - default: - break; - } - } - if (oper == V4IR::OpAdd) { Instruction::Add add; add.lhs = getParam(leftSource); @@ -773,6 +945,38 @@ Param InstructionSelection::binopHelper(V4IR::AluOp oper, V4IR::Expr *leftSource addInstruction(bitXor); return bitXor.result; } + if (oper == V4IR::OpRShift) { + if (V4IR::Const *c = rightSource->asConst()) { + Instruction::ShrConst shr; + shr.lhs = getParam(leftSource); + shr.rhs = convertToValue(c).Value::toInt32() & 0x1f; + shr.result = getResultParam(target); + addInstruction(shr); + return shr.result; + } + Instruction::Shr shr; + shr.lhs = getParam(leftSource); + shr.rhs = getParam(rightSource); + shr.result = getResultParam(target); + addInstruction(shr); + return shr.result; + } + if (oper == V4IR::OpLShift) { + if (V4IR::Const *c = rightSource->asConst()) { + Instruction::ShlConst shl; + shl.lhs = getParam(leftSource); + shl.rhs = convertToValue(c).Value::toInt32() & 0x1f; + shl.result = getResultParam(target); + addInstruction(shl); + return shl.result; + } + Instruction::Shl shl; + shl.lhs = getParam(leftSource); + shl.rhs = getParam(rightSource); + shl.result = getResultParam(target); + addInstruction(shl); + return shl.result; + } if (oper == V4IR::OpInstanceof || oper == V4IR::OpIn || oper == V4IR::OpAdd) { Instruction::BinopContext binop; @@ -810,10 +1014,17 @@ void InstructionSelection::prepareCallArgs(V4IR::ExprList *e, quint32 &argc, qui // We need to move all the temps into the function arg array assert(argLocation >= 0); while (e) { - Instruction::Move move; - move.source = getParam(e->expr); - move.result = Param::createTemp(argLocation); - addInstruction(move); + if (V4IR::Const *c = e->expr->asConst()) { + Instruction::MoveConst move; + move.source = convertToValue(c).asReturnedValue(); + move.result = Param::createTemp(argLocation); + addInstruction(move); + } else { + Instruction::Move move; + move.source = getParam(e->expr); + move.result = Param::createTemp(argLocation); + addInstruction(move); + } ++argLocation; ++argc; e = e->next; @@ -846,16 +1057,16 @@ void InstructionSelection::visitCJump(V4IR::CJump *s) Q_UNIMPLEMENTED(); } - Instruction::CJump jump; - jump.offset = 0; - jump.condition = condition; - if (s->iftrue == _nextBlock) { - jump.invert = true; + Instruction::JumpNe jump; + jump.offset = 0; + jump.condition = condition; ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); _patches[s->iffalse].append(falseLoc); } else { - jump.invert = false; + Instruction::JumpEq jump; + jump.offset = 0; + jump.condition = condition; ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); _patches[s->iftrue].append(trueLoc); @@ -959,9 +1170,8 @@ void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result) { - Instruction::Move move; - int idx = jsUnitGenerator()->registerConstant(QV4::Encode(false)); - move.source = Param::createConstant(idx); + Instruction::MoveConst move; + move.source = QV4::Encode(false); move.result = getResultParam(result); addInstruction(move); } @@ -1082,10 +1292,17 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4 bool isData = it->expr->asConst()->value; it = it->next; - Instruction::Move move; - move.source = getParam(it->expr); - move.result = Param::createTemp(argLocation); - addInstruction(move); + if (V4IR::Const *c = it->expr->asConst()) { + Instruction::MoveConst move; + move.source = convertToValue(c).asReturnedValue(); + move.result = Param::createTemp(argLocation); + addInstruction(move); + } else { + Instruction::Move move; + move.source = getParam(it->expr); + move.result = Param::createTemp(argLocation); + addInstruction(move); + } ++argLocation; if (!isData) { @@ -1124,12 +1341,12 @@ void QQmlJS::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 - instr.common.breakPoint = 0; int instructionSize = Instr::size(type); if (_codeEnd - _codeNext < instructionSize) { diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index d8a85ff249..b43868d186 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -46,7 +46,7 @@ #include <private/qv4isel_p.h> #include <private/qv4isel_util_p.h> #include <private/qv4jsir_p.h> -#include <private/qv4value_def_p.h> +#include <private/qv4value_p.h> #include "qv4instr_moth_p.h" QT_BEGIN_NAMESPACE @@ -160,7 +160,7 @@ private: int scratchTempIndex() const { return _function->tempCount; } int callDataStart() const { return scratchTempIndex() + 1; } - int outgoingArgumentTempStart() const { return callDataStart() + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue); } + int outgoingArgumentTempStart() const { return callDataStart() + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value); } int frameSize() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; } template <int Instr> diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index 6f33098172..57222672b9 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -73,6 +73,7 @@ public: int registerString(const QString &str) { return jsGenerator->registerString(str); } uint registerIndexedGetterLookup() { return jsGenerator->registerIndexedGetterLookup(); } + uint registerIndexedSetterLookup() { return jsGenerator->registerIndexedSetterLookup(); } uint registerGetterLookup(const QString &name) { return jsGenerator->registerGetterLookup(name); } uint registerSetterLookup(const QString &name) { return jsGenerator->registerSetterLookup(name); } uint registerGlobalGetterLookup(const QString &name) { return jsGenerator->registerGlobalGetterLookup(name); } diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h index 38ea682d3b..637746dce1 100644 --- a/src/qml/compiler/qv4isel_util_p.h +++ b/src/qml/compiler/qv4isel_util_p.h @@ -42,7 +42,7 @@ #ifndef QV4ISEL_UTIL_P_H #define QV4ISEL_UTIL_P_H -#include "private/qv4value_def_p.h" +#include "private/qv4value_p.h" #include "qv4jsir_p.h" QT_BEGIN_NAMESPACE @@ -97,38 +97,52 @@ inline QV4::Primitive convertToValue(V4IR::Const *c) class ConvertTemps: protected V4IR::StmtVisitor, protected V4IR::ExprVisitor { - int _nextFreeStackSlot; - QHash<V4IR::Temp, int> _stackSlotForTemp; - void renumber(V4IR::Temp *t) { if (t->kind != V4IR::Temp::VirtualRegister) return; - int stackSlot = _stackSlotForTemp.value(*t, -1); + int stackSlot = _stackSlotForTemp.value(t->index, -1); if (stackSlot == -1) { - stackSlot = _nextFreeStackSlot++; - _stackSlotForTemp[*t] = stackSlot; + stackSlot = allocateFreeSlot(); + _stackSlotForTemp[t->index] = stackSlot; } t->kind = V4IR::Temp::StackSlot; t->index = stackSlot; } +protected: + int _nextUnusedStackSlot; + QHash<int, int> _stackSlotForTemp; + V4IR::BasicBlock *_currentBasicBlock; + virtual int allocateFreeSlot() + { + return _nextUnusedStackSlot++; + } + + virtual void process(V4IR::Stmt *s) + { + s->accept(this); + } + public: ConvertTemps() - : _nextFreeStackSlot(0) + : _nextUnusedStackSlot(0) + , _currentBasicBlock(0) {} void toStackSlots(V4IR::Function *function) { _stackSlotForTemp.reserve(function->tempCount); - foreach (V4IR::BasicBlock *bb, function->basicBlocks) + foreach (V4IR::BasicBlock *bb, function->basicBlocks) { + _currentBasicBlock = bb; foreach (V4IR::Stmt *s, bb->statements) - s->accept(this); + process(s); + } - function->tempCount = _nextFreeStackSlot; + function->tempCount = _nextUnusedStackSlot; } protected: diff --git a/src/qml/compiler/qv4regalloc.cpp b/src/qml/compiler/qv4regalloc.cpp index 2f1f64e523..bc88d5849a 100644 --- a/src/qml/compiler/qv4regalloc.cpp +++ b/src/qml/compiler/qv4regalloc.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ #include "qv4regalloc_p.h" -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <algorithm> diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 6b4d7e7434..3ffaca5134 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -3552,7 +3552,13 @@ private: for (int i = bb->statements.size() - 1; i >= 0; --i) { Stmt *s = bb->statements[i]; if (Phi *phi = s->asPhi()) { - live.remove(*phi->targetTemp); + LiveRegs::iterator it = live.find(*phi->targetTemp); + if (it == live.end()) { + // a phi node target that is only defined, but never used + _intervals[*phi->targetTemp].setFrom(s); + } else { + live.erase(it); + } continue; } collector.collect(s); @@ -3811,7 +3817,6 @@ void Optimizer::convertOutOfSSA() { // There should be no critical edges at this point. foreach (BasicBlock *bb, function->basicBlocks) { - const int id = bb->statements.last()->id; MoveMapping moves; foreach (BasicBlock *successor, bb->out) { @@ -3820,7 +3825,7 @@ void Optimizer::convertOutOfSSA() { foreach (Stmt *s, successor->statements) { if (Phi *phi = s->asPhi()) { moves.add(clone(phi->d->incoming[inIdx], function), - clone(phi->targetTemp, function)->asTemp(), id); + clone(phi->targetTemp, function)->asTemp()); } else { break; } @@ -3946,7 +3951,7 @@ MoveMapping::Moves MoveMapping::sourceUsages(Expr *e, const Moves &moves) return usages; } -void MoveMapping::add(Expr *from, Temp *to, int id) { +void MoveMapping::add(Expr *from, Temp *to) { if (Temp *t = from->asTemp()) { if (overlappingStorage(*t, *to)) { // assignments like fp1 = fp1 or var{&1} = double{&1} can safely be skipped. @@ -3962,7 +3967,7 @@ void MoveMapping::add(Expr *from, Temp *to, int id) { } } - Move m(from, to, id); + Move m(from, to); if (_moves.contains(m)) return; _moves.append(m); @@ -3989,16 +3994,21 @@ void MoveMapping::order() qSwap(_moves, output); } -void MoveMapping::insertMoves(BasicBlock *bb, Function *function, bool atEnd) const +QList<V4IR::Move *> MoveMapping::insertMoves(BasicBlock *bb, Function *function, bool atEnd) const { + QList<V4IR::Move *> newMoves; + newMoves.reserve(_moves.size()); + int insertionPoint = atEnd ? bb->statements.size() - 1 : 0; foreach (const Move &m, _moves) { V4IR::Move *move = function->New<V4IR::Move>(); - move->init(m.to, m.from); - move->id = m.id; + move->init(clone(m.to, function), clone(m.from, function)); move->swap = m.needsSwap; bb->statements.insert(insertionPoint++, move); + newMoves.append(move); } + + return newMoves; } void MoveMapping::dump() const diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h index 65d4f0834a..95bed40a27 100644 --- a/src/qml/compiler/qv4ssa_p.h +++ b/src/qml/compiler/qv4ssa_p.h @@ -170,11 +170,10 @@ class MoveMapping struct Move { Expr *from; Temp *to; - int id; bool needsSwap; - Move(Expr *from, Temp *to, int id) - : from(from), to(to), id(id), needsSwap(false) + Move(Expr *from, Temp *to) + : from(from), to(to), needsSwap(false) {} bool operator==(const Move &other) const @@ -187,9 +186,9 @@ class MoveMapping static Moves sourceUsages(Expr *e, const Moves &moves); public: - void add(Expr *from, Temp *to, int id = 0); + void add(Expr *from, Temp *to); void order(); - void insertMoves(BasicBlock *bb, Function *function, bool atEnd) const; + QList<V4IR::Move *> insertMoves(BasicBlock *bb, Function *function, bool atEnd) const; void dump() const; diff --git a/src/qml/debugger/qqmldebugserver.cpp b/src/qml/debugger/qqmldebugserver.cpp index 0523762971..f1e17df12b 100644 --- a/src/qml/debugger/qqmldebugserver.cpp +++ b/src/qml/debugger/qqmldebugserver.cpp @@ -42,6 +42,12 @@ #include "qqmldebugserver_p.h" #include "qqmldebugservice_p.h" #include "qqmldebugservice_p_p.h" +#include "qqmlenginedebugservice_p.h" +#include "qv4debugservice_p.h" +#include "qv4profilerservice_p.h" +#include "qdebugmessageservice_p.h" +#include "qqmlprofilerservice_p.h" + #include <private/qqmlengine_p.h> #include <private/qqmlglobal_p.h> @@ -56,6 +62,17 @@ QT_BEGIN_NAMESPACE +// We can't friend the Q_GLOBAL_STATIC to have the constructor available so we need a little +// workaround here. Using this wrapper we can also make QQmlEnginePrivate's cleanup() available to +// qAddPostRoutine(). We can't do the cleanup in the destructor because we need a QApplication to +// be available when stopping the plugins. +struct QQmlDebugServerInstanceWrapper { + QQmlDebugServer m_instance; + void cleanup(); +}; + +Q_GLOBAL_STATIC(QQmlDebugServerInstanceWrapper, debugServerInstance) + /* QQmlDebug Protocol (Version 1): @@ -93,6 +110,7 @@ public: QQmlDebugServerPrivate(); void advertisePlugins(); + void cleanup(); QQmlDebugServerConnection *loadConnectionPlugin(const QString &pluginName); QQmlDebugServerConnection *connection; @@ -115,6 +133,9 @@ private: void _q_sendMessages(const QList<QByteArray> &messages); }; +void QQmlDebugServerInstanceWrapper::cleanup() +{ m_instance.d_func()->cleanup(); } + class QQmlDebugServerThread : public QThread { public: @@ -174,6 +195,35 @@ void QQmlDebugServerPrivate::advertisePlugins() QMetaObject::invokeMethod(q, "_q_sendMessages", Qt::QueuedConnection, Q_ARG(QList<QByteArray>, QList<QByteArray>() << message)); } +void QQmlDebugServerPrivate::cleanup() +{ + Q_Q(QQmlDebugServer); + { + QReadLocker lock(&pluginsLock); + foreach (QQmlDebugService *service, plugins.values()) { + changeServiceStateCalls.ref(); + QMetaObject::invokeMethod(q, "_q_changeServiceState", Qt::QueuedConnection, + Q_ARG(QString, service->name()), + Q_ARG(QQmlDebugService::State, QQmlDebugService::NotConnected)); + } + } + + // Wait for changeServiceState calls to finish + // (while running an event loop because some services + // might again use slots to execute stuff in the GUI thread) + QEventLoop loop; + while (!changeServiceStateCalls.testAndSetOrdered(0, 0)) + loop.processEvents(); + + // Stop the thread while the application is still there. + if (thread) { + thread->exit(); + thread->wait(); + delete thread; + thread = 0; + } +} + QQmlDebugServerConnection *QQmlDebugServerPrivate::loadConnectionPlugin( const QString &pluginName) { @@ -222,11 +272,13 @@ QQmlDebugServerConnection *QQmlDebugServerPrivate::loadConnectionPlugin( void QQmlDebugServerThread::run() { - QQmlDebugServer *server = QQmlDebugServer::instance(); + QQmlDebugServerInstanceWrapper *wrapper = debugServerInstance(); + Q_ASSERT_X(wrapper != 0, Q_FUNC_INFO, "There should always be a debug server available here."); + QQmlDebugServer *server = &wrapper->m_instance; QQmlDebugServerConnection *connection = server->d_func()->loadConnectionPlugin(m_pluginName); if (connection) { - connection->setServer(QQmlDebugServer::instance()); + connection->setServer(server); connection->setPortRange(m_portFrom, m_portTo, m_block, m_hostAddress); } else { QCoreApplicationPrivate *appD = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(qApp)); @@ -255,112 +307,107 @@ bool QQmlDebugServer::blockingMode() const return d->blockingMode; } -static QQmlDebugServer *qQmlDebugServer = 0; - - -static void cleanup() +QQmlDebugServer *QQmlDebugServer::instance() { - delete qQmlDebugServer; - qQmlDebugServer = 0; + QQmlDebugServerInstanceWrapper *wrapper = debugServerInstance(); + if (wrapper && wrapper->m_instance.d_func()->thread) { + QQmlDebugServer *ret = &(wrapper->m_instance); + QQmlDebugServerPrivate *d = ret->d_func(); + QMutexLocker locker(&d->helloMutex); + if (d->blockingMode && !d->gotHello) + d->helloCondition.wait(&d->helloMutex); + return ret; + } else { + return 0; + } } -QQmlDebugServer *QQmlDebugServer::instance() +static void cleanup() { - static bool commandLineTested = false; + QQmlDebugServerInstanceWrapper *wrapper = debugServerInstance(); + if (wrapper) + wrapper->cleanup(); +} - if (!commandLineTested) { - commandLineTested = true; - QCoreApplicationPrivate *appD = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(qApp)); +QQmlDebugServer::QQmlDebugServer() + : QObject(*(new QQmlDebugServerPrivate)) +{ + QCoreApplicationPrivate *appD = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(qApp)); #ifndef QT_QML_NO_DEBUGGER - // ### remove port definition when protocol is changed - int portFrom = 0; - int portTo = 0; - bool block = false; - bool ok = false; - QString hostAddress; - - // format: qmljsdebugger=port:<port_from>[,port_to],host:<ip address>][,block] - if (!appD->qmljsDebugArgumentsString().isEmpty()) { - if (!QQmlEnginePrivate::qml_debugging_enabled) { - qWarning() << QString(QLatin1String( - "QML Debugger: Ignoring \"-qmljsdebugger=%1\". " - "Debugging has not been enabled.")).arg( - appD->qmljsDebugArgumentsString()); - return 0; - } + // ### remove port definition when protocol is changed + int portFrom = 0; + int portTo = 0; + bool block = false; + bool ok = false; + QString hostAddress; + + // format: qmljsdebugger=port:<port_from>[,port_to],host:<ip address>][,block] + if (!appD->qmljsDebugArgumentsString().isEmpty()) { + if (!QQmlEnginePrivate::qml_debugging_enabled) { + qWarning() << QString(QLatin1String( + "QML Debugger: Ignoring \"-qmljsdebugger=%1\". " + "Debugging has not been enabled.")).arg( + appD->qmljsDebugArgumentsString()); + return; + } - QString pluginName; - QStringList lstjsDebugArguments = appD->qmljsDebugArgumentsString() - .split(QLatin1Char(',')); - QStringList::const_iterator argsItEnd = lstjsDebugArguments.end(); - QStringList::const_iterator argsIt = lstjsDebugArguments.begin(); - for (; argsIt != argsItEnd; ++argsIt) { - const QString strArgument = *argsIt; - if (strArgument.startsWith(QLatin1String("port:"))) { - pluginName = QLatin1String("qmldbg_tcp"); - portFrom = strArgument.mid(5).toInt(&ok); - portTo = portFrom; - QStringList::const_iterator argsNext = argsIt + 1; - if (argsNext == argsItEnd) - break; - const QString nextArgument = *argsNext; - if (ok && nextArgument.contains(QRegExp(QStringLiteral("^\\s*\\d+\\s*$")))) { - portTo = nextArgument.toInt(&ok); - ++argsIt; - } - } else if (strArgument.startsWith(QLatin1String("host:"))) { - hostAddress = strArgument.mid(5); - } else if (strArgument == QLatin1String("block")) { - block = true; - } else { - qWarning() << QString::fromLatin1("QML Debugger: Invalid argument '%1' " - "detected. Ignoring the same.") - .arg(strArgument); + QString pluginName; + QStringList lstjsDebugArguments = appD->qmljsDebugArgumentsString() + .split(QLatin1Char(',')); + QStringList::const_iterator argsItEnd = lstjsDebugArguments.end(); + QStringList::const_iterator argsIt = lstjsDebugArguments.begin(); + for (; argsIt != argsItEnd; ++argsIt) { + const QString strArgument = *argsIt; + if (strArgument.startsWith(QLatin1String("port:"))) { + pluginName = QLatin1String("qmldbg_tcp"); + portFrom = strArgument.mid(5).toInt(&ok); + portTo = portFrom; + QStringList::const_iterator argsNext = argsIt + 1; + if (argsNext == argsItEnd) + break; + const QString nextArgument = *argsNext; + if (ok && nextArgument.contains(QRegExp(QStringLiteral("^\\s*\\d+\\s*$")))) { + portTo = nextArgument.toInt(&ok); + ++argsIt; } + } else if (strArgument.startsWith(QLatin1String("host:"))) { + hostAddress = strArgument.mid(5); + } else if (strArgument == QLatin1String("block")) { + block = true; + } else { + qWarning() << QString::fromLatin1("QML Debugger: Invalid argument '%1' " + "detected. Ignoring the same.") + .arg(strArgument); } + } - if (ok) { - qQmlDebugServer = new QQmlDebugServer(); - QQmlDebugServerThread *thread = new QQmlDebugServerThread; - qQmlDebugServer->d_func()->thread = thread; - qQmlDebugServer->moveToThread(thread); - thread->setPluginName(pluginName); - thread->setPortRange(portFrom, portTo, block, hostAddress); - - QQmlDebugServerPrivate *d = qQmlDebugServer->d_func(); - d->blockingMode = block; - - QMutexLocker locker(&d->helloMutex); - thread->start(); + if (ok) { + qAddPostRoutine(cleanup); + Q_D(QQmlDebugServer); + d->thread = new QQmlDebugServerThread; + moveToThread(d->thread); + d->thread->setPluginName(pluginName); + d->thread->setPortRange(portFrom, portTo, block, hostAddress); - if (d->blockingMode) - d->helloCondition.wait(&d->helloMutex); + d->blockingMode = block; + d->thread->start(); - } else { - qWarning() << QString(QLatin1String( - "QML Debugger: Ignoring \"-qmljsdebugger=%1\". " - "Format is qmljsdebugger=port:<port_from>[,port_to],host:" - "<ip address>][,block]")).arg(appD->qmljsDebugArgumentsString()); - } - } -#else - if (!appD->qmljsDebugArgumentsString().isEmpty()) { + } else { qWarning() << QString(QLatin1String( - "QML Debugger: Ignoring \"-qmljsdebugger=%1\". " - "QtQml is not configured for debugging.")).arg( - appD->qmljsDebugArgumentsString()); + "QML Debugger: Ignoring \"-qmljsdebugger=%1\". " + "Format is qmljsdebugger=port:<port_from>[,port_to],host:" + "<ip address>][,block]")).arg(appD->qmljsDebugArgumentsString()); } -#endif } - - return qQmlDebugServer; -} - -QQmlDebugServer::QQmlDebugServer() - : QObject(*(new QQmlDebugServerPrivate)) -{ - qAddPostRoutine(cleanup); +#else + if (!appD->qmljsDebugArgumentsString().isEmpty()) { + qWarning() << QString(QLatin1String( + "QML Debugger: Ignoring \"-qmljsdebugger=%1\". " + "QtQml is not configured for debugging.")).arg( + appD->qmljsDebugArgumentsString()); + } +#endif } // called from GUI thread! @@ -368,28 +415,7 @@ QQmlDebugServer::~QQmlDebugServer() { Q_D(QQmlDebugServer); - { - QReadLocker lock(&d->pluginsLock); - foreach (QQmlDebugService *service, d->plugins.values()) { - d->changeServiceStateCalls.ref(); - QMetaObject::invokeMethod(this, "_q_changeServiceState", Qt::QueuedConnection, - Q_ARG(QString, service->name()), - Q_ARG(QQmlDebugService::State, QQmlDebugService::NotConnected)); - } - } - - // Wait for changeServiceState calls to finish - // (while running an event loop because some services - // might again use slots to execute stuff in the GUI thread) - QEventLoop loop; - while (!d->changeServiceStateCalls.testAndSetOrdered(0, 0)) - loop.processEvents(); - - if (d->thread) { - d->thread->exit(); - d->thread->wait(); - delete d->thread; - } + delete d->thread; delete d->connection; } @@ -409,6 +435,7 @@ void QQmlDebugServer::receiveMessage(const QByteArray &message) int op = -1; in >> op; if (op == 0) { + QWriteLocker lock(&d->pluginsLock); int version; in >> version >> d->clientPlugins; @@ -423,24 +450,22 @@ void QQmlDebugServer::receiveMessage(const QByteArray &message) // the plugins below start sending messages. QByteArray helloAnswer; - { - QReadLocker readPluginsLock(&d->pluginsLock); - QQmlDebugStream out(&helloAnswer, QIODevice::WriteOnly); - QStringList pluginNames; - QList<float> pluginVersions; - foreach (QQmlDebugService *service, d->plugins.values()) { - pluginNames << service->name(); - pluginVersions << service->version(); - } - - out << QString(QStringLiteral("QDeclarativeDebugClient")) << 0 << protocolVersion - << pluginNames << pluginVersions << s_dataStreamVersion; + QQmlDebugStream out(&helloAnswer, QIODevice::WriteOnly); + QStringList pluginNames; + QList<float> pluginVersions; + foreach (QQmlDebugService *service, d->plugins.values()) { + pluginNames << service->name(); + pluginVersions << service->version(); } + + out << QString(QStringLiteral("QDeclarativeDebugClient")) << 0 << protocolVersion + << pluginNames << pluginVersions << s_dataStreamVersion; + d->connection->send(QList<QByteArray>() << helloAnswer); + QMutexLocker helloLock(&d->helloMutex); d->gotHello = true; - QReadLocker lock(&d->pluginsLock); QHash<QString, QQmlDebugService*>::ConstIterator iter = d->plugins.constBegin(); for (; iter != d->plugins.constEnd(); ++iter) { QQmlDebugService::State newState = QQmlDebugService::Unavailable; @@ -450,16 +475,15 @@ void QQmlDebugServer::receiveMessage(const QByteArray &message) d->_q_changeServiceState(iter.value()->name(), newState); } - QMutexLocker helloLock(&d->helloMutex); d->helloCondition.wakeAll(); } else if (op == 1) { + QWriteLocker lock(&d->pluginsLock); // Service Discovery QStringList oldClientPlugins = d->clientPlugins; in >> d->clientPlugins; - QReadLocker lock(&d->pluginsLock); QHash<QString, QQmlDebugService*>::ConstIterator iter = d->plugins.constBegin(); for (; iter != d->plugins.constEnd(); ++iter) { const QString pluginName = iter.key(); @@ -507,7 +531,10 @@ void QQmlDebugServerPrivate::_q_changeServiceState(const QString &serviceName, QQmlDebugService *service = 0; { - QReadLocker lock(&pluginsLock); + // Write lock here, because this can be called from receiveMessage which already has a write + // lock. We cannot downgrade it. We also don't want to give up the write lock and later get + // a read lock as that technique has great potential for deadlocks. + QWriteLocker lock(&pluginsLock); service = plugins.value(serviceName); } @@ -543,27 +570,50 @@ QStringList QQmlDebugServer::serviceNames() const return d->plugins.keys(); } -bool QQmlDebugServer::addService(QQmlDebugService *service) +void QQmlDebugServer::addEngine(QQmlEngine *engine) { Q_D(QQmlDebugServer); + QReadLocker lock(&d->pluginsLock); - // to be executed in GUI thread - Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread()); + foreach (QQmlDebugService *service, d->plugins) + service->engineAboutToBeAdded(engine); - { - QWriteLocker lock(&d->pluginsLock); - if (!service || d->plugins.contains(service->name())) - return false; - d->plugins.insert(service->name(), service); - } - { - QReadLocker lock(&d->pluginsLock); - d->advertisePlugins(); - QQmlDebugService::State newState = QQmlDebugService::Unavailable; - if (d->clientPlugins.contains(service->name())) - newState = QQmlDebugService::Enabled; - service->d_func()->state = newState; - } + // TODO: Later wait here for initialization. + + foreach (QQmlDebugService *service, d->plugins) + service->engineAdded(engine); +} + +void QQmlDebugServer::removeEngine(QQmlEngine *engine) +{ + Q_D(QQmlDebugServer); + QReadLocker lock(&d->pluginsLock); + + foreach (QQmlDebugService *service, d->plugins) + service->engineAboutToBeRemoved(engine); + + // TODO: Later wait here for cleanup + + foreach (QQmlDebugService *service, d->plugins) + service->engineRemoved(engine); +} + +bool QQmlDebugServer::addService(QQmlDebugService *service) +{ + Q_D(QQmlDebugServer); + + // to be executed outside of debugger thread + Q_ASSERT(QThread::currentThread() != thread()); + + QWriteLocker lock(&d->pluginsLock); + if (!service || d->plugins.contains(service->name())) + return false; + d->plugins.insert(service->name(), service); + d->advertisePlugins(); + QQmlDebugService::State newState = QQmlDebugService::Unavailable; + if (d->clientPlugins.contains(service->name())) + newState = QQmlDebugService::Enabled; + service->d_func()->state = newState; return true; } @@ -571,24 +621,22 @@ bool QQmlDebugServer::removeService(QQmlDebugService *service) { Q_D(QQmlDebugServer); - // to be executed in GUI thread - Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread()); + // to be executed outside of debugger thread + Q_ASSERT(QThread::currentThread() != thread()); - { - QWriteLocker lock(&d->pluginsLock); - QQmlDebugService::State newState = QQmlDebugService::NotConnected; + QWriteLocker lock(&d->pluginsLock); + QQmlDebugService::State newState = QQmlDebugService::NotConnected; - d->changeServiceStateCalls.ref(); - QMetaObject::invokeMethod(this, "_q_changeServiceState", Qt::QueuedConnection, - Q_ARG(QString, service->name()), - Q_ARG(QQmlDebugService::State, newState)); + d->changeServiceStateCalls.ref(); + QMetaObject::invokeMethod(this, "_q_changeServiceState", Qt::QueuedConnection, + Q_ARG(QString, service->name()), + Q_ARG(QQmlDebugService::State, newState)); - if (!service || !d->plugins.contains(service->name())) - return false; - d->plugins.remove(service->name()); + if (!service || !d->plugins.contains(service->name())) + return false; + d->plugins.remove(service->name()); - d->advertisePlugins(); - } + d->advertisePlugins(); return true; } diff --git a/src/qml/debugger/qqmldebugserver_p.h b/src/qml/debugger/qqmldebugserver_p.h index e285d7cfe7..2d3ffb9351 100644 --- a/src/qml/debugger/qqmldebugserver_p.h +++ b/src/qml/debugger/qqmldebugserver_p.h @@ -77,6 +77,8 @@ public: QList<QQmlDebugService*> services() const; QStringList serviceNames() const; + void addEngine(QQmlEngine *engine); + void removeEngine(QQmlEngine *engine); bool addService(QQmlDebugService *service); bool removeService(QQmlDebugService *service); @@ -89,6 +91,7 @@ private: friend class QQmlDebugService; friend class QQmlDebugServicePrivate; friend class QQmlDebugServerThread; + friend struct QQmlDebugServerInstanceWrapper; QQmlDebugServer(); Q_PRIVATE_SLOT(d_func(), void _q_changeServiceState(const QString &serviceName, QQmlDebugService::State state)) diff --git a/src/qml/debugger/qqmldebugservice.cpp b/src/qml/debugger/qqmldebugservice.cpp index d8fc2f2bb2..f9e6643305 100644 --- a/src/qml/debugger/qqmldebugservice.cpp +++ b/src/qml/debugger/qqmldebugservice.cpp @@ -318,6 +318,22 @@ void QQmlDebugService::messageReceived(const QByteArray &) { } +void QQmlDebugService::engineAboutToBeAdded(QQmlEngine *) +{ +} + +void QQmlDebugService::engineAboutToBeRemoved(QQmlEngine *) +{ +} + +void QQmlDebugService::engineAdded(QQmlEngine *) +{ +} + +void QQmlDebugService::engineRemoved(QQmlEngine *) +{ +} + QQmlDebugStream::QQmlDebugStream() : QDataStream() { diff --git a/src/qml/debugger/qqmldebugservice_p.h b/src/qml/debugger/qqmldebugservice_p.h index 71a116f6a5..fd2a8c361a 100644 --- a/src/qml/debugger/qqmldebugservice_p.h +++ b/src/qml/debugger/qqmldebugservice_p.h @@ -60,6 +60,7 @@ QT_BEGIN_NAMESPACE +class QQmlEngine; class QQmlDebugServicePrivate; class Q_QML_PRIVATE_EXPORT QQmlDebugService : public QObject @@ -103,6 +104,11 @@ protected: virtual void stateChanged(State); virtual void messageReceived(const QByteArray &); + virtual void engineAboutToBeAdded(QQmlEngine *); + virtual void engineAboutToBeRemoved(QQmlEngine *); + virtual void engineAdded(QQmlEngine *); + virtual void engineRemoved(QQmlEngine *); + private: friend class QQmlDebugServer; friend class QQmlDebugServerPrivate; diff --git a/src/qml/debugger/qqmlenginedebugservice.cpp b/src/qml/debugger/qqmlenginedebugservice.cpp index 300561050f..0530b93a82 100644 --- a/src/qml/debugger/qqmlenginedebugservice.cpp +++ b/src/qml/debugger/qqmlenginedebugservice.cpp @@ -759,7 +759,7 @@ void QQmlEngineDebugService::propertyChanged(int id, int objectId, const QMetaPr sendMessage(reply); } -void QQmlEngineDebugService::addEngine(QQmlEngine *engine) +void QQmlEngineDebugService::engineAboutToBeAdded(QQmlEngine *engine) { Q_ASSERT(engine); Q_ASSERT(!m_engines.contains(engine)); @@ -767,7 +767,7 @@ void QQmlEngineDebugService::addEngine(QQmlEngine *engine) m_engines.append(engine); } -void QQmlEngineDebugService::remEngine(QQmlEngine *engine) +void QQmlEngineDebugService::engineAboutToBeRemoved(QQmlEngine *engine) { Q_ASSERT(engine); Q_ASSERT(m_engines.contains(engine)); diff --git a/src/qml/debugger/qqmlenginedebugservice_p.h b/src/qml/debugger/qqmlenginedebugservice_p.h index bbd6aac1b1..4809ee3ed5 100644 --- a/src/qml/debugger/qqmlenginedebugservice_p.h +++ b/src/qml/debugger/qqmlenginedebugservice_p.h @@ -96,8 +96,8 @@ public: bool hasNotifySignal; }; - void addEngine(QQmlEngine *); - void remEngine(QQmlEngine *); + void engineAboutToBeAdded(QQmlEngine *); + void engineAboutToBeRemoved(QQmlEngine *); void objectCreated(QQmlEngine *, QObject *); void setStatesDelegate(QQmlDebugStatesDelegate *); diff --git a/src/qml/debugger/qqmlprofilerservice.cpp b/src/qml/debugger/qqmlprofilerservice.cpp index 91d27f9b11..5d4574231e 100644 --- a/src/qml/debugger/qqmlprofilerservice.cpp +++ b/src/qml/debugger/qqmlprofilerservice.cpp @@ -50,7 +50,7 @@ QT_BEGIN_NAMESPACE // instance will be set, unset in constructor. Allows static methods to be inlined. -QQmlProfilerService *QQmlProfilerService::instance = 0; +QQmlProfilerService *QQmlProfilerService::m_instance = 0; Q_GLOBAL_STATIC(QQmlProfilerService, profilerInstance) bool QQmlProfilerService::enabled = false; @@ -154,13 +154,14 @@ QQmlProfilerService::QQmlProfilerService() QQmlProfilerService::~QQmlProfilerService() { enabled = false; - instance = 0; + m_instance = 0; } -void QQmlProfilerService::initialize() +QQmlProfilerService *QQmlProfilerService::instance() { // just make sure that the service is properly registered - instance = profilerInstance(); + m_instance = profilerInstance(); + return m_instance; } bool QQmlProfilerService::startProfiling() diff --git a/src/qml/debugger/qqmlprofilerservice_p.h b/src/qml/debugger/qqmlprofilerservice_p.h index c3329dbb89..cdc49f2ea7 100644 --- a/src/qml/debugger/qqmlprofilerservice_p.h +++ b/src/qml/debugger/qqmlprofilerservice_p.h @@ -234,7 +234,7 @@ public: MaximumSceneGraphFrameType }; - static void initialize(); + static QQmlProfilerService *instance(); static bool startProfiling(); static bool stopProfiling(); @@ -242,7 +242,7 @@ public: template<EventType DetailType> static void addEvent() { - instance->processMessage(QQmlProfilerData(instance->timestamp(), 1 << Event, + m_instance->processMessage(QQmlProfilerData(m_instance->timestamp(), 1 << Event, 1 << DetailType)); } @@ -251,7 +251,7 @@ public: int animCount = QUnifiedTimer::instance()->runningAnimationCount(); if (animCount > 0 && delta > 0) { - instance->processMessage(QQmlProfilerData(instance->timestamp(), 1 << Event, + m_instance->processMessage(QQmlProfilerData(m_instance->timestamp(), 1 << Event, 1 << AnimationFrame, QString(), 0, 0, 1000 / (int)delta /* trim fps to integer */, animCount)); @@ -262,7 +262,7 @@ public: static void sceneGraphFrame(qint64 value1, qint64 value2 = -1, qint64 value3 = -1, qint64 value4 = -1, qint64 value5 = -1) { - instance->processMessage(QQmlProfilerData(instance->timestamp(), 1 << SceneGraphFrame, + m_instance->processMessage(QQmlProfilerData(m_instance->timestamp(), 1 << SceneGraphFrame, 1 << FrameType1 | 1 << FrameType2, value1, value2, value3, value4, value5)); } @@ -270,13 +270,13 @@ public: template<PixmapEventType PixmapState> static void pixmapStateChanged(const QUrl &url) { - instance->processMessage(QQmlProfilerData(instance->timestamp(), 1 << PixmapCacheEvent, + m_instance->processMessage(QQmlProfilerData(m_instance->timestamp(), 1 << PixmapCacheEvent, 1 << PixmapState, url)); } static void pixmapLoadingFinished(const QUrl &url, const QSize &size) { - instance->processMessage(QQmlProfilerData(instance->timestamp(), 1 << PixmapCacheEvent, + m_instance->processMessage(QQmlProfilerData(m_instance->timestamp(), 1 << PixmapCacheEvent, (1 << PixmapLoadingFinished) | ((size.width() > 0 && size.height() > 0) ? (1 << PixmapSizeKnown) : 0), url, size.width(), size.height())); } @@ -284,7 +284,7 @@ public: template<PixmapEventType CountType> static void pixmapCountChanged(const QUrl &url, int count) { - instance->processMessage(QQmlProfilerData(instance->timestamp(), 1 << PixmapCacheEvent, + m_instance->processMessage(QQmlProfilerData(m_instance->timestamp(), 1 << PixmapCacheEvent, 1 << CountType, url, 0, 0, 0, count)); } @@ -305,7 +305,7 @@ private: static void startBinding(const QString &fileName, int line, int column, BindingType bindingType) { - instance->processMessage(QQmlProfilerData(instance->timestamp(), + m_instance->processMessage(QQmlProfilerData(m_instance->timestamp(), (1 << RangeStart | 1 << RangeLocation), 1 << Binding, fileName, line, column, 0, 0, bindingType)); @@ -315,14 +315,14 @@ private: // This is somewhat pointless but important for backwards compatibility. static void startCompiling(const QString &name) { - instance->processMessage(QQmlProfilerData(instance->timestamp(), + m_instance->processMessage(QQmlProfilerData(m_instance->timestamp(), (1 << RangeStart | 1 << RangeLocation | 1 << RangeData), 1 << Compiling, name, 1, 1, 0, 0, QmlBinding)); } static void startHandlingSignal(const QString &fileName, int line, int column) { - instance->processMessage(QQmlProfilerData(instance->timestamp(), + m_instance->processMessage(QQmlProfilerData(m_instance->timestamp(), (1 << RangeStart | 1 << RangeLocation), 1 << HandlingSignal, fileName, line, column, 0, 0, QmlBinding)); @@ -330,7 +330,7 @@ private: static void startCreating(const QString &typeName, const QUrl &fileName, int line, int column) { - instance->processMessage(QQmlProfilerData(instance->timestamp(), + m_instance->processMessage(QQmlProfilerData(m_instance->timestamp(), (1 << RangeStart | 1 << RangeLocation | 1 << RangeData), 1 << Creating, typeName, fileName, line, column, 0, 0, QmlBinding)); @@ -338,21 +338,21 @@ private: static void startCreating(const QString &typeName) { - instance->processMessage(QQmlProfilerData(instance->timestamp(), + m_instance->processMessage(QQmlProfilerData(m_instance->timestamp(), (1 << RangeStart | 1 << RangeData), 1 << Creating, typeName, 0, 0, 0, 0, QmlBinding)); } static void creatingLocation(const QUrl &fileName, int line, int column) { - instance->processMessage(QQmlProfilerData(instance->timestamp(), 1 << RangeLocation, + m_instance->processMessage(QQmlProfilerData(m_instance->timestamp(), 1 << RangeLocation, 1 << Creating, fileName, line, column)); } template<RangeType Range> static void endRange() { - instance->processMessage(QQmlProfilerData(instance->timestamp(), 1 << RangeEnd, + m_instance->processMessage(QQmlProfilerData(m_instance->timestamp(), 1 << RangeEnd, 1 << Range)); } @@ -375,7 +375,7 @@ private: QMutex m_initializeMutex; QWaitCondition m_initializeCondition; - static QQmlProfilerService *instance; + static QQmlProfilerService *m_instance; friend struct QQmlBindingProfiler; friend struct QQmlHandlingSignalProfiler; @@ -402,20 +402,19 @@ struct QQmlBindingProfiler { struct QQmlHandlingSignalProfiler { QQmlHandlingSignalProfiler(QQmlBoundSignalExpression *expression) { - if (QQmlProfilerService::enabled) { - if (expression->sourceFile().isEmpty()) { - QV4::Function *function = expression->function(); - if (function) { - Q_QML_PROFILE(startHandlingSignal( - function->sourceFile(), function->compiledFunction->location.line, - function->compiledFunction->location.column)); - } + Q_QML_PROFILE_IF_ENABLED({ + QV4::Function *function; + if (expression->sourceFile().isEmpty() && (function = expression->function())) { + QQmlProfilerService::startHandlingSignal( + function->sourceFile(), function->compiledFunction->location.line, + function->compiledFunction->location.column); + } else { - Q_QML_PROFILE(startHandlingSignal( + QQmlProfilerService::startHandlingSignal( expression->sourceFile(), expression->lineNumber(), - expression->columnNumber())); + expression->columnNumber()); } - } + }); } ~QQmlHandlingSignalProfiler() @@ -455,9 +454,9 @@ public: { ranges.clear(); if (running) - QQmlProfilerService::instance->endRange<QQmlProfilerService::Creating>(); + QQmlProfilerService::m_instance->endRange<QQmlProfilerService::Creating>(); for (int i = 0; i < backgroundRanges.count(); ++i) { - QQmlProfilerService::instance->endRange<QQmlProfilerService::Creating>(); + QQmlProfilerService::m_instance->endRange<QQmlProfilerService::Creating>(); } backgroundRanges.clear(); running = false; @@ -466,10 +465,10 @@ public: void startBackground(const QString &typeName) { if (running) { - QQmlProfilerService::instance->endRange<QQmlProfilerService::Creating>(); + QQmlProfilerService::m_instance->endRange<QQmlProfilerService::Creating>(); running = false; } - QQmlProfilerService::instance->startCreating(typeName); + QQmlProfilerService::m_instance->startCreating(typeName); backgroundRanges.push(typeName); } @@ -477,13 +476,13 @@ public: { switchRange(); setCurrentRange(typeName, url, line, column); - QQmlProfilerService::instance->startCreating(typeName, url, line, column); + QQmlProfilerService::m_instance->startCreating(typeName, url, line, column); } void stop() { if (running) { - QQmlProfilerService::instance->endRange<QQmlProfilerService::Creating>(); + QQmlProfilerService::m_instance->endRange<QQmlProfilerService::Creating>(); running = false; } } @@ -493,7 +492,7 @@ public: if (ranges.count() > 0) { switchRange(); currentRange = ranges.pop(); - QQmlProfilerService::instance->startCreating(currentRange.typeName, currentRange.url, + QQmlProfilerService::m_instance->startCreating(currentRange.typeName, currentRange.url, currentRange.line, currentRange.column); } } @@ -509,7 +508,7 @@ public: if (backgroundRanges.count() > 0) { switchRange(); setCurrentRange(backgroundRanges.pop(), url, line, column); - QQmlProfilerService::instance->creatingLocation(url, line, column); + QQmlProfilerService::m_instance->creatingLocation(url, line, column); } } @@ -518,7 +517,7 @@ private: void switchRange() { if (running) - QQmlProfilerService::instance->endRange<QQmlProfilerService::Creating>(); + QQmlProfilerService::m_instance->endRange<QQmlProfilerService::Creating>(); else running = true; } diff --git a/src/qml/debugger/qv4debugservice.cpp b/src/qml/debugger/qv4debugservice.cpp index 372a51e997..0058e78e7f 100644 --- a/src/qml/debugger/qv4debugservice.cpp +++ b/src/qml/debugger/qv4debugservice.cpp @@ -1022,10 +1022,10 @@ QV4DebugService *QV4DebugService::instance() return v4ServiceInstance(); } -void QV4DebugService::addEngine(const QQmlEngine *engine) +void QV4DebugService::engineAboutToBeAdded(QQmlEngine *engine) { Q_D(QV4DebugService); - + QMutexLocker lock(&d->initializeMutex); if (engine) { QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); if (QQmlDebugServer *server = QQmlDebugServer::instance()) { @@ -1041,9 +1041,10 @@ void QV4DebugService::addEngine(const QQmlEngine *engine) } } -void QV4DebugService::removeEngine(const QQmlEngine *engine) +void QV4DebugService::engineAboutToBeRemoved(QQmlEngine *engine) { Q_D(QV4DebugService); + QMutexLocker lock(&d->initializeMutex); if (engine){ const QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); if (ee) { diff --git a/src/qml/debugger/qv4debugservice_p.h b/src/qml/debugger/qv4debugservice_p.h index e35010bebf..0aa38150c6 100644 --- a/src/qml/debugger/qv4debugservice_p.h +++ b/src/qml/debugger/qv4debugservice_p.h @@ -69,8 +69,8 @@ public: ~QV4DebugService(); static QV4DebugService *instance(); - void addEngine(const QQmlEngine *engine); - void removeEngine(const QQmlEngine *engine); + void engineAboutToBeAdded(QQmlEngine *engine); + void engineAboutToBeRemoved(QQmlEngine *engine); void signalEmitted(const QString &signal); diff --git a/src/qml/debugger/qv4profilerservice.cpp b/src/qml/debugger/qv4profilerservice.cpp index 50bec8ec25..b064f57089 100644 --- a/src/qml/debugger/qv4profilerservice.cpp +++ b/src/qml/debugger/qv4profilerservice.cpp @@ -127,12 +127,6 @@ QV4ProfilerService *QV4ProfilerService::instance() return v4ProfilerInstance(); } -void QV4ProfilerService::initialize() -{ - // just make sure that the service is properly registered - v4ProfilerInstance(); -} - void QV4ProfilerService::stateAboutToBeChanged(QQmlDebugService::State newState) { Q_D(QV4ProfilerService); diff --git a/src/qml/debugger/qv4profilerservice_p.h b/src/qml/debugger/qv4profilerservice_p.h index 252c154a68..cf330e5a1b 100644 --- a/src/qml/debugger/qv4profilerservice_p.h +++ b/src/qml/debugger/qv4profilerservice_p.h @@ -92,7 +92,6 @@ public: ~QV4ProfilerService(); static QV4ProfilerService *instance(); - static void initialize(); public Q_SLOTS: void startProfiling(const QString &title); diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index c8de31ef78..38a8e40cb2 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -45,7 +45,7 @@ #include "qjsengine.h" #include "qjsvalue.h" #include "qjsvalue_p.h" -#include "qv4value_p.h" +#include "qv4value_inl_p.h" #include "qv4object_p.h" #include "qv4functionobject_p.h" #include "qv4dateobject_p.h" diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h index d8da664cc6..41856b1e98 100644 --- a/src/qml/jsapi/qjsvalue_p.h +++ b/src/qml/jsapi/qjsvalue_p.h @@ -55,7 +55,7 @@ #include <qjsvalue.h> #include <private/qtqmlglobal_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4string_p.h> #include <private/qv4engine_p.h> #include <private/qv4object_p.h> diff --git a/src/qml/jsapi/qjsvalueiterator_p.h b/src/qml/jsapi/qjsvalueiterator_p.h index 80d1885811..882c779c00 100644 --- a/src/qml/jsapi/qjsvalueiterator_p.h +++ b/src/qml/jsapi/qjsvalueiterator_p.h @@ -58,11 +58,11 @@ public: QV4::PersistentValue iterator; QV4::Property currentProperty; QV4::PropertyAttributes currentAttributes; - QV4::SafeString currentName; + QV4::StringValue currentName; uint currentIndex; QV4::Property nextProperty; QV4::PropertyAttributes nextAttributes; - QV4::SafeString nextName; + QV4::StringValue nextName; uint nextIndex; }; diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index a90cea96c2..43eb61ac9a 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -6,6 +6,7 @@ SOURCES += \ $$PWD/qv4context.cpp \ $$PWD/qv4runtime.cpp \ $$PWD/qv4value.cpp \ + $$PWD/qv4persistent.cpp \ $$PWD/qv4debugging.cpp \ $$PWD/qv4lookup.cpp \ $$PWD/qv4identifier.cpp \ @@ -49,8 +50,9 @@ HEADERS += \ $$PWD/qv4context_p.h \ $$PWD/qv4runtime_p.h \ $$PWD/qv4math_p.h \ + $$PWD/qv4value_inl_p.h \ $$PWD/qv4value_p.h \ - $$PWD/qv4value_def_p.h \ + $$PWD/qv4persistent_p.h \ $$PWD/qv4debugging_p.h \ $$PWD/qv4lookup_p.h \ $$PWD/qv4identifier_p.h \ diff --git a/src/qml/jsruntime/qv4alloca_p.h b/src/qml/jsruntime/qv4alloca_p.h index e51c6dff00..a76c327b04 100644 --- a/src/qml/jsruntime/qv4alloca_p.h +++ b/src/qml/jsruntime/qv4alloca_p.h @@ -49,10 +49,8 @@ # ifndef __GNUC__ # define alloca _alloca # endif -#else -#if !defined(__FreeBSD__) && !defined(__DragonFly__) +#elif !defined(Q_OS_BSD4) || defined(Q_OS_DARWIN) # include <alloca.h> #endif -#endif #endif diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index 20354272f2..b50c4f081d 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -80,7 +80,7 @@ struct ArgumentsObject: Object { Q_MANAGED_TYPE(ArgumentsObject) CallContext *context; bool fullyCreated; - QVector<SafeValue> mappedArguments; + QVector<Value> mappedArguments; ArgumentsObject(CallContext *context); ~ArgumentsObject() {} diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index 9c6ff94583..44727cba17 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -110,7 +110,7 @@ void ArrayData::realloc(Object *o, Type newType, uint offset, uint alloc, bool e newType = Complex; alloc = qMax(alloc, 2*oldAlloc) + offset; - size_t size = alloc*sizeof(SafeValue); + size_t size = alloc*sizeof(Value); if (enforceAttributes) size += alloc*sizeof(PropertyAttributes); @@ -120,7 +120,7 @@ void ArrayData::realloc(Object *o, Type newType, uint offset, uint alloc, bool e new (newData) SimpleArrayData(o->engine()); newData->alloc = alloc - offset; newData->type = newType; - newData->data = reinterpret_cast<SafeValue *>(newData + 1) + offset; + newData->data = reinterpret_cast<Value *>(newData + 1) + offset; newData->attrs = enforceAttributes ? reinterpret_cast<PropertyAttributes *>(newData->data + alloc) + offset : 0; newData->offset = offset; newData->len = d ? static_cast<SimpleArrayData *>(d)->len : 0; @@ -131,13 +131,13 @@ void ArrayData::realloc(Object *o, Type newType, uint offset, uint alloc, bool e new (newData) SparseArrayData(o->engine()); newData->alloc = alloc; newData->type = newType; - newData->data = reinterpret_cast<SafeValue *>(newData + 1); + newData->data = reinterpret_cast<Value *>(newData + 1); newData->attrs = enforceAttributes ? reinterpret_cast<PropertyAttributes *>(newData->data + alloc) : 0; o->arrayData = newData; } if (d) { - memcpy(o->arrayData->data, d->data, sizeof(SafeValue)*toCopy); + memcpy(o->arrayData->data, d->data, sizeof(Value)*toCopy); if (enforceAttributes) { if (d->attrs) memcpy(o->arrayData->attrs, d->attrs, sizeof(PropertyAttributes)*toCopy); @@ -267,7 +267,7 @@ PropertyAttributes SimpleArrayData::attribute(const ArrayData *d, uint index) return d->attrs[index]; } -void SimpleArrayData::push_front(Object *o, SafeValue *values, uint n) +void SimpleArrayData::push_front(Object *o, Value *values, uint n) { SimpleArrayData *dd = static_cast<SimpleArrayData *>(o->arrayData); Q_ASSERT(!dd->attrs); @@ -309,8 +309,8 @@ uint SimpleArrayData::truncate(Object *o, uint newLen) return newLen; if (dd->attrs) { - SafeValue *it = dd->data + dd->len; - const SafeValue *begin = dd->data + newLen; + Value *it = dd->data + dd->len; + const Value *begin = dd->data + newLen; while (--it >= begin) { if (!it->isEmpty() && !dd->attrs[it - dd->data].isConfigurable()) { newLen = it - dd->data + 1; @@ -328,7 +328,7 @@ uint SimpleArrayData::length(const ArrayData *d) return static_cast<const SimpleArrayData *>(d)->len; } -bool SimpleArrayData::putArray(Object *o, uint index, SafeValue *values, uint n) +bool SimpleArrayData::putArray(Object *o, uint index, Value *values, uint n) { SimpleArrayData *dd = static_cast<SimpleArrayData *>(o->arrayData); if (index + n > dd->alloc) { @@ -347,7 +347,7 @@ void SparseArrayData::free(ArrayData *d, uint idx) { Q_ASSERT(d && d->type == ArrayData::Sparse); SparseArrayData *dd = static_cast<SparseArrayData *>(d); - SafeValue *v = dd->data + idx; + Value *v = dd->data + idx; if (dd->attrs && dd->attrs[idx].isAccessor()) { // double slot, free both. Order is important, so we have a double slot for allocation again afterwards. v[1].tag = Value::Empty_Type; @@ -502,7 +502,7 @@ PropertyAttributes SparseArrayData::attribute(const ArrayData *d, uint index) return d->attrs[n->value]; } -void SparseArrayData::push_front(Object *o, SafeValue *values, uint n) +void SparseArrayData::push_front(Object *o, Value *values, uint n) { Q_ASSERT(!o->arrayData->attrs); for (int i = n - 1; i >= 0; --i) { @@ -561,7 +561,7 @@ uint SparseArrayData::length(const ArrayData *d) return n ? n->key() + 1 : 0; } -bool SparseArrayData::putArray(Object *o, uint index, SafeValue *values, uint n) +bool SparseArrayData::putArray(Object *o, uint index, Value *values, uint n) { for (uint i = 0; i < n; ++i) put(o, index + i, values[i]); @@ -630,6 +630,47 @@ Property *ArrayData::insert(Object *o, uint index, bool isAccessor) return reinterpret_cast<Property *>(o->arrayData->data + n->value); } + +class ArrayElementLessThan +{ +public: + inline ArrayElementLessThan(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn) + : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {} + + bool operator()(const Value &v1, const Value &v2) const; + +private: + ExecutionContext *m_context; + ObjectRef thisObject; + const ValueRef m_comparefn; +}; + + +bool ArrayElementLessThan::operator()(const Value &v1, const Value &v2) const +{ + Scope scope(m_context); + + if (v1.isUndefined() || v1.isEmpty()) + return false; + if (v2.isUndefined() || v2.isEmpty()) + return true; + ScopedObject o(scope, m_comparefn); + if (o) { + Scope scope(o->engine()); + ScopedValue result(scope); + ScopedCallData callData(scope, 2); + callData->thisObject = Primitive::undefinedValue(); + callData->args[0] = v1; + callData->args[1] = v2; + result = __qmljs_call_value(m_context, m_comparefn, callData); + + return result->toNumber() < 0; + } + ScopedString p1s(scope, v1.toString(m_context)); + ScopedString p2s(scope, v2.toString(m_context)); + return p1s->toQString() < p2s->toQString(); +} + void ArrayData::sort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint len) { if (!len) @@ -720,7 +761,7 @@ void ArrayData::sort(ExecutionContext *context, ObjectRef thisObject, const Valu ArrayElementLessThan lessThan(context, thisObject, comparefn); - SafeValue *begin = thisObject->arrayData->data; + Value *begin = thisObject->arrayData->data; std::sort(begin, begin + len, lessThan); #ifdef CHECK_SPARSE_ARRAYS diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index 48b17a239f..f127c75fb0 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -67,11 +67,11 @@ struct ArrayVTable ArrayData *(*reallocate)(Object *o, uint n, bool enforceAttributes); ReturnedValue (*get)(const ArrayData *d, uint index); bool (*put)(Object *o, uint index, ValueRef value); - bool (*putArray)(Object *o, uint index, SafeValue *values, uint n); + bool (*putArray)(Object *o, uint index, Value *values, uint n); bool (*del)(Object *o, uint index); void (*setAttribute)(Object *o, uint index, PropertyAttributes attrs); PropertyAttributes (*attribute)(const ArrayData *d, uint index); - void (*push_front)(Object *o, SafeValue *values, uint n); + void (*push_front)(Object *o, Value *values, uint n); ReturnedValue (*pop_front)(Object *o); uint (*truncate)(Object *o, uint newLen); uint (*length)(const ArrayData *d); @@ -95,7 +95,7 @@ struct Q_QML_EXPORT ArrayData : public Managed uint alloc; Type type; PropertyAttributes *attrs; - SafeValue *data; + Value *data; const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(internalClass->vtable); } bool isSparse() const { return this && type == Sparse; } @@ -154,11 +154,11 @@ struct Q_QML_EXPORT SimpleArrayData : public ArrayData static ReturnedValue get(const ArrayData *d, uint index); static bool put(Object *o, uint index, ValueRef value); - static bool putArray(Object *o, uint index, SafeValue *values, uint n); + static bool putArray(Object *o, uint index, Value *values, uint n); static bool del(Object *o, uint index); static void setAttribute(Object *o, uint index, PropertyAttributes attrs); static PropertyAttributes attribute(const ArrayData *d, uint index); - static void push_front(Object *o, SafeValue *values, uint n); + static void push_front(Object *o, Value *values, uint n); static ReturnedValue pop_front(Object *o); static uint truncate(Object *o, uint newLen); static uint length(const ArrayData *d); @@ -184,11 +184,11 @@ struct Q_QML_EXPORT SparseArrayData : public ArrayData static ArrayData *reallocate(Object *o, uint n, bool enforceAttributes); static ReturnedValue get(const ArrayData *d, uint index); static bool put(Object *o, uint index, ValueRef value); - static bool putArray(Object *o, uint index, SafeValue *values, uint n); + static bool putArray(Object *o, uint index, Value *values, uint n); static bool del(Object *o, uint index); static void setAttribute(Object *o, uint index, PropertyAttributes attrs); static PropertyAttributes attribute(const ArrayData *d, uint index); - static void push_front(Object *o, SafeValue *values, uint n); + static void push_front(Object *o, Value *values, uint n); static ReturnedValue pop_front(Object *o); static uint truncate(Object *o, uint newLen); static uint length(const ArrayData *d); diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 9a2407fe2c..652c0f00ac 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -629,8 +629,8 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx) Q_ASSERT(instance->arrayType() == ArrayData::Simple || instance->arrayType() == ArrayData::Complex); if (len > instance->arrayData->length()) len = instance->arrayData->length(); - SafeValue *val = instance->arrayData->data; - SafeValue *end = val + len; + Value *val = instance->arrayData->data; + Value *end = val + len; val += fromIndex; while (val < end) { if (!val->isEmpty()) { diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index cc712c759b..15fea72b25 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -71,13 +71,13 @@ CallContext *ExecutionContext::newCallContext(FunctionObject *function, CallData c->lookups = c->compilationUnit->runtimeLookups; } - c->locals = (SafeValue *)(c + 1); + c->locals = (Value *)(c + 1); if (function->varCount) std::fill(c->locals, c->locals + function->varCount, Primitive::undefinedValue()); c->callData = reinterpret_cast<CallData *>(c->locals + function->varCount); - ::memcpy(c->callData, callData, sizeof(CallData) + (callData->argc - 1) * sizeof(SafeValue)); + ::memcpy(c->callData, callData, sizeof(CallData) + (callData->argc - 1) * sizeof(Value)); if (callData->argc < static_cast<int>(function->formalParameterCount)) std::fill(c->callData->args + c->callData->argc, c->callData->args + function->formalParameterCount, Primitive::undefinedValue()); c->callData->argc = qMax((uint)callData->argc, function->formalParameterCount); @@ -214,7 +214,7 @@ CallContext::CallContext(ExecutionEngine *engine, ObjectRef qml, FunctionObject lookups = compilationUnit->runtimeLookups; } - locals = (SafeValue *)(this + 1); + locals = (Value *)(this + 1); if (function->varCount) std::fill(locals, locals + function->varCount, Primitive::undefinedValue()); } diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index b794c28582..5904d0b638 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -42,7 +42,7 @@ #define QMLJS_ENVIRONMENT_H #include "qv4global_p.h" -#include "qv4value_def_p.h" +#include "qv4scopedvalue_p.h" #include "qv4managed_p.h" #include "qv4engine_p.h" @@ -174,13 +174,17 @@ struct CallContext : public ExecutionContext FunctionObject *function; int realArgumentCount; - SafeValue *locals; + Value *locals; Object *activation; inline ReturnedValue argument(int i); bool needsOwnArguments() const; }; +inline ReturnedValue CallContext::argument(int i) { + return i < callData->argc ? callData->args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); +} + struct GlobalContext : public ExecutionContext { GlobalContext(ExecutionEngine *engine); @@ -192,8 +196,8 @@ struct CatchContext : public ExecutionContext { CatchContext(ExecutionEngine *engine, const StringRef exceptionVarName, const ValueRef exceptionValue); - SafeString exceptionVarName; - SafeValue exceptionValue; + StringValue exceptionVarName; + Value exceptionValue; }; struct WithContext : public ExecutionContext @@ -243,6 +247,15 @@ struct ExecutionContextSaver } }; +inline Scope::Scope(ExecutionContext *ctx) + : engine(ctx->engine) +#ifndef QT_NO_DEBUG + , size(0) +#endif +{ + mark = engine->jsStackTop; +} + /* Function *f, int argc */ #define requiredMemoryForExecutionContect(f, argc) \ sizeof(CallContext) + sizeof(Value) * (f->varCount + qMax((uint)argc, f->formalParameterCount)) + sizeof(CallData) diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index ad8dec87c9..c52e8c3ee1 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -54,7 +54,7 @@ namespace QV4 { struct DateObject: Object { V4_OBJECT Q_MANAGED_TYPE(DateObject) - SafeValue value; + Value value; DateObject(ExecutionEngine *engine, const ValueRef date): Object(engine->dateClass) { value = date; } diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp index 4170b6817f..9ab1622fee 100644 --- a/src/qml/jsruntime/qv4debugging.cpp +++ b/src/qml/jsruntime/qv4debugging.cpp @@ -179,7 +179,7 @@ void Debugger::resume(Speed speed) return; if (!m_returnedValue.isUndefined()) - m_returnedValue = Primitive::undefinedValue(); + m_returnedValue = Encode::undefined(); clearTemporaryBreakPoints(); if (speed == StepOver) @@ -648,15 +648,15 @@ void Debugger::applyPendingBreakPoints() void Debugger::setBreakOnInstruction(Function *function, qptrdiff codeOffset, bool onoff) { uchar *codePtr = const_cast<uchar *>(function->codeData) + codeOffset; - QQmlJS::Moth::Instr *instruction = reinterpret_cast<QQmlJS::Moth::Instr*>(codePtr); - instruction->common.breakPoint = onoff; + QQmlJS::Moth::Instr::instr_debug *debug = reinterpret_cast<QQmlJS::Moth::Instr::instr_debug *>(codePtr); + debug->breakPoint = onoff; } bool Debugger::hasBreakOnInstruction(Function *function, qptrdiff codeOffset) { uchar *codePtr = const_cast<uchar *>(function->codeData) + codeOffset; - QQmlJS::Moth::Instr *instruction = reinterpret_cast<QQmlJS::Moth::Instr*>(codePtr); - return instruction->common.breakPoint; + QQmlJS::Moth::Instr::instr_debug *debug = reinterpret_cast<QQmlJS::Moth::Instr::instr_debug *>(codePtr); + return debug->breakPoint; } bool Debugger::reallyHitTheBreakPoint(const QString &filename, int linenr) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 093fe96dd5..16bacbfafd 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ #include <qv4engine_p.h> #include <qv4context_p.h> -#include <qv4value_p.h> +#include <qv4value_inl_p.h> #include <qv4object_p.h> #include <qv4objectproto_p.h> #include <qv4objectiterator_p.h> @@ -157,10 +157,10 @@ quintptr getStackLimit() ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) - : memoryManager(new QV4::MemoryManager) + : current(0) + , memoryManager(new QV4::MemoryManager) , executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) - , current(0) , bumperPointerAllocator(new WTF::BumpPointerAllocator) , jsStack(new WTF::PageAllocation) , debugger(0) @@ -197,11 +197,11 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) // we allow it to grow to 2 times JSStackLimit, as we can overshoot due to garbage collection // and ScopedValues allocated outside of JIT'ed methods. *jsStack = WTF::PageAllocation::allocate(2*JSStackLimit, WTF::OSAllocator::JSVMStackPages, true); - jsStackBase = (SafeValue *)jsStack->base(); + jsStackBase = (Value *)jsStack->base(); jsStackTop = jsStackBase; // set up stack limits - jsStackLimit = jsStackBase + JSStackLimit/sizeof(SafeValue); + jsStackLimit = jsStackBase + JSStackLimit/sizeof(Value); cStackLimit = getStackLimit(); Scope scope(this); @@ -457,7 +457,7 @@ Returned<FunctionObject> *ExecutionEngine::newBuiltinFunction(ExecutionContext * return f->asReturned<FunctionObject>(); } -Returned<BoundFunction> *ExecutionEngine::newBoundFunction(ExecutionContext *scope, FunctionObjectRef target, const ValueRef boundThis, const QVector<SafeValue> &boundArgs) +Returned<BoundFunction> *ExecutionEngine::newBoundFunction(ExecutionContext *scope, FunctionObjectRef target, const ValueRef boundThis, const QVector<Value> &boundArgs) { Q_ASSERT(target); @@ -560,7 +560,7 @@ Returned<RegExpObject> *ExecutionEngine::newRegExpObject(const QString &pattern, return newRegExpObject(re, global); } -Returned<RegExpObject> *ExecutionEngine::newRegExpObject(Referenced<RegExp> re, bool global) +Returned<RegExpObject> *ExecutionEngine::newRegExpObject(RegExpRef re, bool global) { RegExpObject *object = new (memoryManager) RegExpObject(this, re, global); return object->asReturned<RegExpObject>(); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index ecb5f2b4d5..63c57b8478 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -121,11 +121,6 @@ struct ExecutionContextSaver; struct Q_QML_EXPORT ExecutionEngine { - MemoryManager *memoryManager; - ExecutableAllocator *executableAllocator; - ExecutableAllocator *regExpAllocator; - QScopedPointer<QQmlJS::EvalISelFactory> iselFactory; - private: friend struct ExecutionContextSaver; friend struct ExecutionContext; @@ -133,20 +128,27 @@ private: public: ExecutionContext *currentContext() const { return current; } + Value *jsStackTop; + quint32 hasException; GlobalContext *rootContext; - SafeValue *jsStackTop; - SafeValue *jsStackLimit; + MemoryManager *memoryManager; + ExecutableAllocator *executableAllocator; + ExecutableAllocator *regExpAllocator; + QScopedPointer<QQmlJS::EvalISelFactory> iselFactory; + + + Value *jsStackLimit; quintptr cStackLimit; WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. enum { JSStackLimit = 4*1024*1024 }; WTF::PageAllocation *jsStack; - SafeValue *jsStackBase; + Value *jsStackBase; - SafeValue *stackPush(uint nValues) { - SafeValue *ptr = jsStackTop; + Value *stackPush(uint nValues) { + Value *ptr = jsStackTop; jsStackTop = ptr + nValues; return ptr; } @@ -174,22 +176,22 @@ public: QV8Engine *v8Engine; - SafeValue objectCtor; - SafeValue stringCtor; - SafeValue numberCtor; - SafeValue booleanCtor; - SafeValue arrayCtor; - SafeValue functionCtor; - SafeValue dateCtor; - SafeValue regExpCtor; - SafeValue errorCtor; - SafeValue evalErrorCtor; - SafeValue rangeErrorCtor; - SafeValue referenceErrorCtor; - SafeValue syntaxErrorCtor; - SafeValue typeErrorCtor; - SafeValue uRIErrorCtor; - SafeValue sequencePrototype; + Value objectCtor; + Value stringCtor; + Value numberCtor; + Value booleanCtor; + Value arrayCtor; + Value functionCtor; + Value dateCtor; + Value regExpCtor; + Value errorCtor; + Value evalErrorCtor; + Value rangeErrorCtor; + Value referenceErrorCtor; + Value syntaxErrorCtor; + Value typeErrorCtor; + Value uRIErrorCtor; + Value sequencePrototype; QQmlJS::MemoryPool classPool; InternalClass *emptyClass; @@ -228,36 +230,36 @@ public: QVector<Property> argumentsAccessors; - SafeString id_undefined; - SafeString id_null; - SafeString id_true; - SafeString id_false; - SafeString id_boolean; - SafeString id_number; - SafeString id_string; - SafeString id_object; - SafeString id_function; - SafeString id_length; - SafeString id_prototype; - SafeString id_constructor; - SafeString id_arguments; - SafeString id_caller; - SafeString id_callee; - SafeString id_this; - SafeString id___proto__; - SafeString id_enumerable; - SafeString id_configurable; - SafeString id_writable; - SafeString id_value; - SafeString id_get; - SafeString id_set; - SafeString id_eval; - SafeString id_uintMax; - SafeString id_name; - SafeString id_index; - SafeString id_input; - SafeString id_toString; - SafeString id_valueOf; + StringValue id_undefined; + StringValue id_null; + StringValue id_true; + StringValue id_false; + StringValue id_boolean; + StringValue id_number; + StringValue id_string; + StringValue id_object; + StringValue id_function; + StringValue id_length; + StringValue id_prototype; + StringValue id_constructor; + StringValue id_arguments; + StringValue id_caller; + StringValue id_callee; + StringValue id_this; + StringValue id___proto__; + StringValue id_enumerable; + StringValue id_configurable; + StringValue id_writable; + StringValue id_value; + StringValue id_get; + StringValue id_set; + StringValue id_eval; + StringValue id_uintMax; + StringValue id_name; + StringValue id_index; + StringValue id_input; + StringValue id_toString; + StringValue id_valueOf; QSet<CompiledData::CompilationUnit*> compilationUnits; QMap<quintptr, QV4::Function*> allFunctions; @@ -295,7 +297,7 @@ public: ExecutionContext *popContext(); Returned<FunctionObject> *newBuiltinFunction(ExecutionContext *scope, const StringRef name, ReturnedValue (*code)(CallContext *)); - Returned<BoundFunction> *newBoundFunction(ExecutionContext *scope, FunctionObjectRef target, const ValueRef boundThis, const QVector<SafeValue> &boundArgs); + Returned<BoundFunction> *newBoundFunction(ExecutionContext *scope, FunctionObjectRef target, const ValueRef boundThis, const QVector<Value> &boundArgs); Returned<Object> *newObject(); Returned<Object> *newObject(InternalClass *internalClass); @@ -315,7 +317,7 @@ public: Returned<DateObject> *newDateObject(const QDateTime &dt); Returned<RegExpObject> *newRegExpObject(const QString &pattern, int flags); - Returned<RegExpObject> *newRegExpObject(Referenced<RegExp> re, bool global); + Returned<RegExpObject> *newRegExpObject(RegExpRef re, bool global); Returned<RegExpObject> *newRegExpObject(const QRegExp &re); Returned<Object> *newErrorObject(const ValueRef value); @@ -352,8 +354,7 @@ public: bool recheckCStackLimits(); // Exception handling - SafeValue exceptionValue; - quint32 hasException; + Value exceptionValue; StackTrace exceptionStackTrace; ReturnedValue throwException(const ValueRef value); diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 0e90e213c4..811b445f9e 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -42,7 +42,7 @@ #include "qv4function_p.h" #include "qv4managed_p.h" #include "qv4string_p.h" -#include "qv4value_p.h" +#include "qv4value_inl_p.h" #include "qv4engine_p.h" #include "qv4lookup_p.h" diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 8d07853b45..377b45bfa3 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -47,7 +47,7 @@ #include <QtCore/QByteArray> #include <QtCore/qurl.h> -#include "qv4value_def_p.h" +#include "qv4value_p.h" #include <private/qv4compileddata_p.h> #include <private/qv4engine_p.h> @@ -81,7 +81,7 @@ struct InternalClass; struct Lookup; struct Function { - SafeString name; + StringValue name; const CompiledData::Function *compiledFunction; CompiledData::CompilationUnit *compilationUnit; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index a6ed03cea8..a3e47c42ca 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -352,7 +352,7 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx) } else { int alen = qMin(len, arr->arrayData->length()); if (alen) - memcpy(callData->args, arr->arrayData->data, alen*sizeof(SafeValue)); + memcpy(callData->args, arr->arrayData->data, alen*sizeof(Value)); for (quint32 i = alen; i < len; ++i) callData->args[i] = Primitive::undefinedValue(); } @@ -387,7 +387,7 @@ ReturnedValue FunctionPrototype::method_bind(CallContext *ctx) return ctx->throwTypeError(); ScopedValue boundThis(scope, ctx->argument(0)); - QVector<SafeValue> boundArgs; + QVector<Value> boundArgs; for (int i = 1; i < ctx->callData->argc; ++i) boundArgs += ctx->callData->args[i]; @@ -643,7 +643,7 @@ DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); DEFINE_OBJECT_VTABLE(BoundFunction); -BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObjectRef target, const ValueRef boundThis, const QVector<SafeValue> &boundArgs) +BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObjectRef target, const ValueRef boundThis, const QVector<Value> &boundArgs) : FunctionObject(scope, QStringLiteral("__bound function__")) , target(target) , boundArgs(boundArgs) @@ -683,8 +683,8 @@ ReturnedValue BoundFunction::call(Managed *that, CallData *dd) ScopedCallData callData(scope, f->boundArgs.size() + dd->argc); callData->thisObject = f->boundThis; - memcpy(callData->args, f->boundArgs.constData(), f->boundArgs.size()*sizeof(SafeValue)); - memcpy(callData->args + f->boundArgs.size(), dd->args, dd->argc*sizeof(SafeValue)); + memcpy(callData->args, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value)); + memcpy(callData->args + f->boundArgs.size(), dd->args, dd->argc*sizeof(Value)); return f->target->call(callData); } @@ -696,8 +696,8 @@ ReturnedValue BoundFunction::construct(Managed *that, CallData *dd) return Encode::undefined(); ScopedCallData callData(scope, f->boundArgs.size() + dd->argc); - memcpy(callData->args, f->boundArgs.constData(), f->boundArgs.size()*sizeof(SafeValue)); - memcpy(callData->args + f->boundArgs.size(), dd->args, dd->argc*sizeof(SafeValue)); + memcpy(callData->args, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value)); + memcpy(callData->args + f->boundArgs.size(), dd->args, dd->argc*sizeof(Value)); return f->target->construct(callData); } diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 64a1dc92d2..af4ec024d5 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -111,7 +111,7 @@ struct Q_QML_EXPORT FunctionObject: Object { }; ExecutionContext *scope; - SafeString name; + StringValue name; unsigned int formalParameterCount; unsigned int varCount; Function *function; @@ -155,6 +155,8 @@ inline FunctionObject *value_cast(const Value &v) { return v.asFunctionObject(); } +DEFINE_REF(FunctionObject, Object); + struct FunctionCtor: FunctionObject { V4_OBJECT @@ -228,10 +230,10 @@ struct SimpleScriptFunction: FunctionObject { struct BoundFunction: FunctionObject { V4_OBJECT FunctionObject *target; - SafeValue boundThis; - QVector<SafeValue> boundArgs; + Value boundThis; + QVector<Value> boundArgs; - BoundFunction(ExecutionContext *scope, FunctionObjectRef target, const ValueRef boundThis, const QVector<SafeValue> &boundArgs); + BoundFunction(ExecutionContext *scope, FunctionObjectRef target, const ValueRef boundThis, const QVector<Value> &boundArgs); ~BoundFunction() {} diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 1d465df0c0..746513cc2f 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -156,12 +156,16 @@ template<typename T> struct Returned; typedef Returned<String> ReturnedString; typedef Returned<Object> ReturnedObject; typedef Returned<FunctionObject> ReturnedFunctionObject; -template<typename T> struct Referenced; -typedef Referenced<Managed> ManagedRef; -typedef Referenced<String> StringRef; -typedef Referenced<Object> ObjectRef; -typedef Referenced<ArrayObject> ArrayObjectRef; -typedef Referenced<FunctionObject> FunctionObjectRef; +struct ManagedRef; +struct StringRef; +struct ObjectRef; +struct ArrayObjectRef; +struct FunctionObjectRef; +struct RegExpRef; + +struct PersistentValuePrivate; +class PersistentValue; +class WeakValue; namespace Global { diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 224d383614..76a8b0c25c 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -41,7 +41,7 @@ #include "qv4globalobject_p.h" #include "qv4mm_p.h" -#include "qv4value_p.h" +#include "qv4value_inl_p.h" #include "qv4context_p.h" #include "qv4function_p.h" #include "qv4debugging_p.h" diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h index c4c72f3a83..d1cadb3aa5 100644 --- a/src/qml/jsruntime/qv4include_p.h +++ b/src/qml/jsruntime/qv4include_p.h @@ -58,7 +58,7 @@ #include <private/qqmlcontext_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4context_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index c9d22523a9..4a75272843 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -180,6 +180,63 @@ ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, const ValueRef object, c return indexedGetterFallback(l, object, index); } +void Lookup::indexedSetterGeneric(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef v) +{ + if (object->isObject()) { + Object *o = object->objectValue(); + if (o->arrayData && o->arrayData->type == ArrayData::Simple && index->asArrayIndex() < UINT_MAX) { + l->indexedSetter = indexedSetterObjectInt; + indexedSetterObjectInt(l, object, index, v); + return; + } + } + indexedSetterFallback(l, object, index, v); +} + +void Lookup::indexedSetterFallback(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef value) +{ + ExecutionContext *ctx = l->engine->currentContext(); + Scope scope(ctx); + ScopedObject o(scope, object->toObject(ctx)); + if (scope.engine->hasException) + return; + + uint idx = index->asArrayIndex(); + if (idx < UINT_MAX) { + if (o->arrayData && o->arrayData->type == ArrayData::Simple) { + SimpleArrayData *s = static_cast<SimpleArrayData *>(o->arrayData); + if (s && idx < s->len && !s->data[idx].isEmpty()) { + s->data[idx] = value; + return; + } + } + o->putIndexed(idx, value); + return; + } + + ScopedString name(scope, index->toString(ctx)); + o->put(name, value); +} + +void Lookup::indexedSetterObjectInt(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef v) +{ + uint idx = index->asArrayIndex(); + if (idx == UINT_MAX || !object->isObject()) { + indexedSetterGeneric(l, object, index, v); + return; + } + + Object *o = object->objectValue(); + if (o->arrayData && o->arrayData->type == ArrayData::Simple) { + SimpleArrayData *s = static_cast<SimpleArrayData *>(o->arrayData); + if (idx < s->len && !s->data[idx].isEmpty()) { + s->data[idx] = v; + return; + } + } + indexedSetterFallback(l, object, index, v); +} + ReturnedValue Lookup::getterGeneric(QV4::Lookup *l, const ValueRef object) { if (Object *o = object->asObject()) diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 50874411de..7f107bf8eb 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -56,6 +56,7 @@ struct Lookup { enum { Size = 4 }; union { ReturnedValue (*indexedGetter)(Lookup *l, const ValueRef object, const ValueRef index); + void (*indexedSetter)(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef v); ReturnedValue (*getter)(Lookup *l, const ValueRef object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionContext *ctx); void (*setter)(Lookup *l, const ValueRef object, const ValueRef v); @@ -78,6 +79,10 @@ struct Lookup { static ReturnedValue indexedGetterFallback(Lookup *l, const ValueRef object, const ValueRef index); static ReturnedValue indexedGetterObjectInt(Lookup *l, const ValueRef object, const ValueRef index); + static void indexedSetterGeneric(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef v); + static void indexedSetterFallback(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef value); + static void indexedSetterObjectInt(Lookup *l, const ValueRef object, const ValueRef index, const ValueRef v); + static ReturnedValue getterGeneric(Lookup *l, const ValueRef object); static ReturnedValue getter0(Lookup *l, const ValueRef object); static ReturnedValue getter1(Lookup *l, const ValueRef object); diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index da2aab627e..8c6f2daf9b 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -45,7 +45,7 @@ #include <QtCore/QVector> #include <QtCore/QDebug> #include "qv4global_p.h" -#include "qv4value_def_p.h" +#include "qv4value_p.h" #include "qv4internalclass_p.h" QT_BEGIN_NAMESPACE @@ -230,7 +230,7 @@ protected: Managed(InternalClass *internal) : internalClass(internal), _data(0) { - Q_ASSERT(!internalClass || internalClass->vtable); + Q_ASSERT(internalClass && internalClass->vtable); inUse = 1; extensible = 1; } @@ -353,6 +353,7 @@ private: friend struct ObjectIterator; }; + template<> inline Managed *value_cast(const Value &v) { return v.asManaged(); @@ -381,6 +382,68 @@ inline FunctionObject *managed_cast(Managed *m) } +Value *extractValuePointer(const ScopedValue &); +template<typename T> +Value *extractValuePointer(const Scoped<T> &); + +#define DEFINE_REF_METHODS(Class, Base) \ + Class##Ref(const QV4::ScopedValue &v) \ + { QV4::Value *val = extractValuePointer(v); ptr = QV4::value_cast<Class>(*val) ? val : 0; } \ + Class##Ref(const QV4::Scoped<Class> &v) { ptr = extractValuePointer(v); } \ + Class##Ref(QV4::TypedValue<Class> &v) { ptr = &v; } \ + Class##Ref(QV4::Value &v) { ptr = QV4::value_cast<Class>(v) ? &v : 0; } \ + Class##Ref &operator=(Class *t) { \ + if (sizeof(void *) == 4) \ + ptr->tag = QV4::Value::Managed_Type; \ + ptr->m = t; \ + return *this; \ + } \ + Class##Ref &operator=(QV4::Returned<Class> *t) { \ + if (sizeof(void *) == 4) \ + ptr->tag = QV4::Value::Managed_Type; \ + ptr->m = t->getPointer(); \ + return *this; \ + } \ + operator const Class *() const { return ptr ? static_cast<Class*>(ptr->managed()) : 0; } \ + const Class *operator->() const { return static_cast<Class*>(ptr->managed()); } \ + operator Class *() { return ptr ? static_cast<Class*>(ptr->managed()) : 0; } \ + Class *operator->() { return static_cast<Class*>(ptr->managed()); } \ + Class *getPointer() const { return static_cast<Class *>(ptr->managed()); } \ + operator QV4::Returned<Class> *() const { return ptr ? QV4::Returned<Class>::create(getPointer()) : 0; } \ + static Class##Ref null() { Class##Ref c; c.ptr = 0; return c; } \ +protected: \ + Class##Ref() {} \ +public: \ + +#define DEFINE_REF(Class, Base) \ +struct Class##Ref : public Base##Ref \ +{ DEFINE_REF_METHODS(Class, Base) } \ + + +struct ManagedRef { + // Important: Do NOT add a copy constructor to this class or any derived class + // adding a copy constructor actually changes the calling convention, ie. + // is not even binary compatible. Adding it would break assumptions made + // in the jit'ed code. + DEFINE_REF_METHODS(Managed, Managed); + + bool operator==(const ManagedRef &other) { + if (ptr == other.ptr) + return true; + return ptr && other.ptr && ptr->m == other.ptr->m; + } + bool operator!=(const ManagedRef &other) { + return !operator==(other); + } + bool operator!() const { return !ptr || !ptr->managed(); } + + bool isNull() const { return !ptr; } + ReturnedValue asReturnedValue() const { return ptr ? ptr->val : Primitive::undefinedValue().asReturnedValue(); } + +public: + Value *ptr; +}; + } diff --git a/src/qml/jsruntime/qv4mm.cpp b/src/qml/jsruntime/qv4mm.cpp index 0ba0b18cda..7bb41f1eec 100644 --- a/src/qml/jsruntime/qv4mm.cpp +++ b/src/qml/jsruntime/qv4mm.cpp @@ -355,7 +355,7 @@ Managed *MemoryManager::alloc(std::size_t size) void MemoryManager::mark() { - SafeValue *markBase = m_d->engine->jsStackTop; + Value *markBase = m_d->engine->jsStackTop; m_d->engine->markObjects(); @@ -698,8 +698,8 @@ void MemoryManager::collectFromStack() const void MemoryManager::collectFromJSStack() const { - SafeValue *v = engine()->jsStackBase; - SafeValue *top = engine()->jsStackTop; + Value *v = engine()->jsStackBase; + Value *top = engine()->jsStackTop; while (v < top) { Managed *m = v->asManaged(); if (m && m->inUse) diff --git a/src/qml/jsruntime/qv4mm_p.h b/src/qml/jsruntime/qv4mm_p.h index 7d28319536..a8fd585332 100644 --- a/src/qml/jsruntime/qv4mm_p.h +++ b/src/qml/jsruntime/qv4mm_p.h @@ -44,7 +44,7 @@ #include "qv4global_p.h" #include "qv4context_p.h" -#include "qv4value_p.h" +#include "qv4value_inl_p.h" #include <QScopedPointer> diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 93ad51d26f..de84747221 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -198,9 +198,10 @@ void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter) void Object::defineAccessorProperty(const StringRef name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)) { ExecutionEngine *v4 = engine(); - Property p; - p.setGetter(getter ? v4->newBuiltinFunction(v4->rootContext, name, getter)->getPointer() : 0); - p.setSetter(setter ? v4->newBuiltinFunction(v4->rootContext, name, setter)->getPointer() : 0); + QV4::Scope scope(v4); + ScopedProperty p(scope); + p->setGetter(getter ? v4->newBuiltinFunction(v4->rootContext, name, getter)->getPointer() : 0); + p->setSetter(setter ? v4->newBuiltinFunction(v4->rootContext, name, setter)->getPointer() : 0); insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); } @@ -581,7 +582,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, StringRef name, uin } // dense arrays while (it->arrayIndex < o->arrayData->length()) { - SafeValue *val = o->arrayData->data + it->arrayIndex; + Value *val = o->arrayData->data + it->arrayIndex; PropertyAttributes a = o->arrayData->attributes(it->arrayIndex); ++it->arrayIndex; if (!val->isEmpty() @@ -1118,7 +1119,7 @@ void Object::copyArrayData(Object *other) d->len = static_cast<SimpleArrayData *>(other->arrayData)->len; d->offset = 0; } - memcpy(arrayData->data, other->arrayData->data, arrayData->alloc*sizeof(SafeValue)); + memcpy(arrayData->data, other->arrayData->data, arrayData->alloc*sizeof(Value)); } setArrayLengthUnchecked(other->getLength()); } diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index ca54388359..e3361ae160 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -191,7 +191,7 @@ public: bool arrayPut(uint index, ValueRef value) { return arrayData->vtable()->put(this, index, value); } - bool arrayPut(uint index, SafeValue *values, uint n) { + bool arrayPut(uint index, Value *values, uint n) { return arrayData->vtable()->putArray(this, index, values, n); } void setArrayAttributes(uint i, PropertyAttributes a) { @@ -206,7 +206,7 @@ public: void push_back(const ValueRef v); ArrayData::Type arrayType() const { - return arrayData ? (ArrayData::Type)arrayData->type : ArrayData::Simple; + return arrayData ? arrayData->type : ArrayData::Simple; } // ### remove me void setArrayType(ArrayData::Type t) { @@ -304,7 +304,7 @@ private: struct BooleanObject: Object { V4_OBJECT Q_MANAGED_TYPE(BooleanObject) - SafeValue value; + Value value; BooleanObject(ExecutionEngine *engine, const ValueRef val) : Object(engine->booleanClass) { value = val; @@ -320,7 +320,7 @@ protected: struct NumberObject: Object { V4_OBJECT Q_MANAGED_TYPE(NumberObject) - SafeValue value; + Value value; NumberObject(ExecutionEngine *engine, const ValueRef val) : Object(engine->numberClass) { value = val; @@ -417,11 +417,28 @@ inline ArrayObject *value_cast(const Value &v) { } template<> -inline ReturnedValue value_convert<Object>(ExecutionContext *ctx, const Value &v) +inline ReturnedValue value_convert<Object>(ExecutionEngine *e, const Value &v) { - return v.toObject(ctx)->asReturnedValue(); + return v.toObject(e->currentContext())->asReturnedValue(); } +struct ObjectRef : public ManagedRef +{ + DEFINE_REF_METHODS(Object, Managed) + + static ObjectRef fromValuePointer(Value *s) { + ObjectRef r; + r.ptr = s; + if (sizeof(void *) == 8) + r.ptr->val = 0; + else + *r.ptr = Value::fromManaged(0); + return r; + } +}; + +DEFINE_REF(ArrayObject, Object); + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 0e35996710..e5f693c323 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -46,26 +46,26 @@ using namespace QV4; -ObjectIterator::ObjectIterator(SafeObject *scratch1, SafeObject *scratch2, const ObjectRef o, uint flags) - : object(*scratch1) - , current(*scratch2) +ObjectIterator::ObjectIterator(Value *scratch1, Value *scratch2, const ObjectRef o, uint flags) + : object(ObjectRef::fromValuePointer(scratch1)) + , current(ObjectRef::fromValuePointer(scratch2)) , arrayNode(0) , arrayIndex(0) , memberIndex(0) , flags(flags) { - object = o; - current = o; + object = o.getPointer(); + current = o.getPointer(); - if (object && object->asArgumentsObject()) { + if (!!object && object->asArgumentsObject()) { Scope scope(object->engine()); Scoped<ArgumentsObject> (scope, object->asReturnedValue())->fullyCreate(); } } ObjectIterator::ObjectIterator(Scope &scope, const ObjectRef o, uint flags) - : object(*static_cast<SafeObject *>(scope.alloc(1))) - , current(*static_cast<SafeObject *>(scope.alloc(1))) + : object(ObjectRef::fromValuePointer(scope.alloc(1))) + , current(ObjectRef::fromValuePointer(scope.alloc(1))) , arrayNode(0) , arrayIndex(0) , memberIndex(0) @@ -74,7 +74,7 @@ ObjectIterator::ObjectIterator(Scope &scope, const ObjectRef o, uint flags) object = o; current = o; - if (object && object->asArgumentsObject()) { + if (!!object && object->asArgumentsObject()) { Scope scope(object->engine()); Scoped<ArgumentsObject> (scope, object->asReturnedValue())->fullyCreate(); } @@ -103,7 +103,7 @@ void ObjectIterator::next(StringRef name, uint *index, Property *pd, PropertyAtt Object *o = object; bool shadowed = false; while (o != current) { - if ((name && o->hasOwnProperty(name)) || + if ((!!name && o->hasOwnProperty(name)) || (*index != UINT_MAX && o->hasOwnProperty(*index))) { shadowed = true; break; diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h index 08a740fc37..c87f284288 100644 --- a/src/qml/jsruntime/qv4objectiterator_p.h +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -74,7 +74,7 @@ struct Q_QML_EXPORT ObjectIterator uint memberIndex; uint flags; - ObjectIterator(SafeObject *scratch1, SafeObject *scratch2, const ObjectRef o, uint flags); + ObjectIterator(Value *scratch1, Value *scratch2, const ObjectRef o, uint flags); ObjectIterator(Scope &scope, const ObjectRef o, uint flags); void next(StringRef name, uint *index, Property *pd, PropertyAttributes *attributes = 0); ReturnedValue nextPropertyName(ValueRef value); @@ -87,7 +87,8 @@ struct ForEachIteratorObject: Object { Q_MANAGED_TYPE(ForeachIteratorObject) ObjectIterator it; ForEachIteratorObject(ExecutionContext *ctx, const ObjectRef o) - : Object(ctx->engine), it(workArea, workArea + 1, o, ObjectIterator::EnumerableOnly|ObjectIterator::WithProtoChain) { + : Object(ctx->engine), it(workArea, workArea + 1, + o, ObjectIterator::EnumerableOnly|ObjectIterator::WithProtoChain) { setVTable(staticVTable()); } @@ -96,7 +97,7 @@ struct ForEachIteratorObject: Object { protected: static void markObjects(Managed *that, ExecutionEngine *e); - SafeObject workArea[2]; + Value workArea[2]; }; diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp new file mode 100644 index 0000000000..72e3651757 --- /dev/null +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4persistent_p.h" +#include "qv4mm_p.h" + +using namespace QV4; + +PersistentValue::PersistentValue(const ValueRef val) + : d(new PersistentValuePrivate(val.asReturnedValue())) +{ +} + +PersistentValue::PersistentValue(ReturnedValue val) + : d(new PersistentValuePrivate(val)) +{ +} + +PersistentValue::PersistentValue(const PersistentValue &other) + : d(other.d) +{ + if (d) + d->ref(); +} + +PersistentValue &PersistentValue::operator=(const PersistentValue &other) +{ + if (d == other.d) + return *this; + + // the memory manager cleans up those with a refcount of 0 + + if (d) + d->deref(); + d = other.d; + if (d) + d->ref(); + + return *this; +} + +PersistentValue &PersistentValue::operator =(const ValueRef other) +{ + if (!d) { + d = new PersistentValuePrivate(other.asReturnedValue()); + return *this; + } + d = d->detach(other.asReturnedValue()); + return *this; +} + +PersistentValue &PersistentValue::operator =(ReturnedValue other) +{ + if (!d) { + d = new PersistentValuePrivate(other); + return *this; + } + d = d->detach(other); + return *this; +} + +PersistentValue::~PersistentValue() +{ + if (d) + d->deref(); +} + +WeakValue::WeakValue(const ValueRef val) + : d(new PersistentValuePrivate(val.asReturnedValue(), /*engine*/0, /*weak*/true)) +{ +} + +WeakValue::WeakValue(const WeakValue &other) + : d(other.d) +{ + if (d) + d->ref(); +} + +WeakValue::WeakValue(ReturnedValue val) + : d(new PersistentValuePrivate(val, /*engine*/0, /*weak*/true)) +{ +} + +WeakValue &WeakValue::operator=(const WeakValue &other) +{ + if (d == other.d) + return *this; + + // the memory manager cleans up those with a refcount of 0 + + if (d) + d->deref(); + d = other.d; + if (d) + d->ref(); + + return *this; +} + +WeakValue &WeakValue::operator =(const ValueRef other) +{ + if (!d) { + d = new PersistentValuePrivate(other.asReturnedValue(), /*engine*/0, /*weak*/true); + return *this; + } + d = d->detach(other.asReturnedValue(), /*weak*/true); + return *this; +} + +WeakValue &WeakValue::operator =(const ReturnedValue &other) +{ + if (!d) { + d = new PersistentValuePrivate(other, /*engine*/0, /*weak*/true); + return *this; + } + d = d->detach(other, /*weak*/true); + return *this; +} + + +WeakValue::~WeakValue() +{ + if (d) + d->deref(); +} + +void WeakValue::markOnce(ExecutionEngine *e) +{ + if (!d) + return; + d->value.mark(e); +} + +PersistentValuePrivate::PersistentValuePrivate(ReturnedValue v, ExecutionEngine *e, bool weak) + : refcount(1) + , weak(weak) + , engine(e) + , prev(0) + , next(0) +{ + value.val = v; + init(); +} + +void PersistentValuePrivate::init() +{ + if (!engine) { + Managed *m = value.asManaged(); + if (!m) + return; + + engine = m->engine(); + } + if (engine && !prev) { + PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues; + + prev = listRoot; + next = *listRoot; + *prev = this; + if (next) + next->prev = &this->next; + } +} + +PersistentValuePrivate::~PersistentValuePrivate() +{ +} + +void PersistentValuePrivate::removeFromList() +{ + if (prev) { + if (next) + next->prev = prev; + *prev = next; + next = 0; + prev = 0; + } +} + +void PersistentValuePrivate::deref() +{ + // if engine is not 0, they are registered with the memory manager + // and will get cleaned up in the next gc run + if (!--refcount) { + removeFromList(); + delete this; + } +} + +PersistentValuePrivate *PersistentValuePrivate::detach(const QV4::ReturnedValue val, bool weak) +{ + if (refcount == 1) { + value.val = val; + + Managed *m = value.asManaged(); + if (!prev) { + if (m) { + ExecutionEngine *engine = m->engine(); + if (engine) { + PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues; + prev = listRoot; + next = *listRoot; + *prev = this; + if (next) + next->prev = &this->next; + } + } + } else if (!m) + removeFromList(); + + return this; + } + --refcount; + return new PersistentValuePrivate(val, engine, weak); +} + diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h new file mode 100644 index 0000000000..21f37f3d96 --- /dev/null +++ b/src/qml/jsruntime/qv4persistent_p.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4PERSISTENT_H +#define QV4PERSISTENT_H + +#include "qv4value_inl_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Q_QML_PRIVATE_EXPORT PersistentValuePrivate +{ + PersistentValuePrivate(ReturnedValue v, ExecutionEngine *engine = 0, bool weak = false); + virtual ~PersistentValuePrivate(); + Value value; + uint refcount; + bool weak; + QV4::ExecutionEngine *engine; + PersistentValuePrivate **prev; + PersistentValuePrivate *next; + + void init(); + void removeFromList(); + void ref() { ++refcount; } + void deref(); + PersistentValuePrivate *detach(const ReturnedValue value, bool weak = false); + + bool checkEngine(QV4::ExecutionEngine *otherEngine) { + if (!engine) { + Q_ASSERT(!value.isObject()); + engine = otherEngine; + } + return (engine == otherEngine); + } +}; + +class Q_QML_EXPORT PersistentValue +{ +public: + PersistentValue() : d(0) {} + PersistentValue(const PersistentValue &other); + PersistentValue &operator=(const PersistentValue &other); + + PersistentValue(const ValueRef val); + PersistentValue(ReturnedValue val); + template<typename T> + PersistentValue(Returned<T> *obj); + PersistentValue(const ManagedRef obj); + PersistentValue &operator=(const ValueRef other); + PersistentValue &operator=(const ScopedValue &other); + PersistentValue &operator =(ReturnedValue other); + template<typename T> + PersistentValue &operator=(Returned<T> *obj); + PersistentValue &operator=(const ManagedRef obj); + ~PersistentValue(); + + ReturnedValue value() const { + return (d ? d->value.asReturnedValue() : Primitive::undefinedValue().asReturnedValue()); + } + + ExecutionEngine *engine() { + if (!d) + return 0; + if (d->engine) + return d->engine; + Managed *m = d->value.asManaged(); + return m ? m->engine() : 0; + } + + bool isUndefined() const { return !d || d->value.isUndefined(); } + bool isNullOrUndefined() const { return !d || d->value.isNullOrUndefined(); } + void clear() { + *this = PersistentValue(); + } + +private: + friend struct ValueRef; + PersistentValuePrivate *d; +}; + +class Q_QML_EXPORT WeakValue +{ +public: + WeakValue() : d(0) {} + WeakValue(const ValueRef val); + WeakValue(const WeakValue &other); + WeakValue(ReturnedValue val); + template<typename T> + WeakValue(Returned<T> *obj); + WeakValue &operator=(const WeakValue &other); + WeakValue &operator=(const ValueRef other); + WeakValue &operator =(const ReturnedValue &other); + template<typename T> + WeakValue &operator=(Returned<T> *obj); + + ~WeakValue(); + + ReturnedValue value() const { + return (d ? d->value.asReturnedValue() : Primitive::undefinedValue().asReturnedValue()); + } + + ExecutionEngine *engine() { + if (!d) + return 0; + if (d->engine) + return d->engine; + Managed *m = d->value.asManaged(); + return m ? m->engine() : 0; + } + + bool isUndefined() const { return !d || d->value.isUndefined(); } + bool isNullOrUndefined() const { return !d || d->value.isNullOrUndefined(); } + void clear() { + *this = WeakValue(); + } + + void markOnce(ExecutionEngine *e); + +private: + friend struct ValueRef; + PersistentValuePrivate *d; +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h index 6381fe7687..aceb6022f8 100644 --- a/src/qml/jsruntime/qv4property_p.h +++ b/src/qml/jsruntime/qv4property_p.h @@ -42,7 +42,7 @@ #define QV4PROPERTYDESCRIPTOR_H #include "qv4global_p.h" -#include "qv4value_p.h" +#include "qv4value_inl_p.h" #include "qv4internalclass_p.h" QT_BEGIN_NAMESPACE @@ -52,8 +52,8 @@ namespace QV4 { struct FunctionObject; struct Property { - SafeValue value; - SafeValue set; + Value value; + Value set; // Section 8.10 inline void fullyPopulated(PropertyAttributes *attrs) { diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 235aaf94db..9bc01d12cc 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -694,7 +694,7 @@ PropertyAttributes QObjectWrapper::query(const Managed *m, StringRef name) QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(engine); QQmlPropertyData local; if (that->findProperty(engine, qmlContext, name, IgnoreRevision, &local) - || name->equals(const_cast<SafeString &>(that->m_destroy)) || name->equals(engine->id_toString)) + || name->equals(const_cast<StringValue &>(that->m_destroy)) || name->equals(engine->id_toString)) return QV4::Attr_Data; else return QV4::Object::query(m, name); @@ -1702,7 +1702,8 @@ QV4::ReturnedValue CallArgument::toValue(QV8Engine *engine) } else if (type == -1 || type == qMetaTypeId<QVariant>()) { QVariant value = *qvariantPtr; QV4::ScopedValue rv(scope, engine->fromVariant(value)); - if (QV4::Referenced<QObjectWrapper> qobjectWrapper = rv->asRef<QV4::QObjectWrapper>()) { + QV4::QObjectWrapperRef qobjectWrapper = rv; + if (!!qobjectWrapper) { if (QObject *object = qobjectWrapper->object()) QQmlData::get(object, true)->setImplicitDestructible(); } diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index f6d1ac0790..ca38c5b0dc 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -62,7 +62,7 @@ #include <private/qqmlpropertycache_p.h> #include <private/qintrusivelist_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4functionobject_p.h> QT_BEGIN_NAMESPACE @@ -111,7 +111,7 @@ private: QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; QPointer<QObject> m_object; - SafeString m_destroy; + StringValue m_destroy; static ReturnedValue get(Managed *m, const StringRef name, bool *hasProperty); static void put(Managed *m, const StringRef name, const ValueRef value); @@ -200,6 +200,8 @@ private Q_SLOTS: void removeDestroyedObject(QObject*); }; +DEFINE_REF(QObjectWrapper, Object); + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 1b93542f8a..74c8a2d35e 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -80,7 +80,7 @@ RegExpObject::RegExpObject(InternalClass *ic) init(ic->engine); } -RegExpObject::RegExpObject(ExecutionEngine *engine, Referenced<RegExp> value, bool global) +RegExpObject::RegExpObject(ExecutionEngine *engine, RegExpRef value, bool global) : Object(engine->regExpClass) , value(value) , global(global) @@ -433,7 +433,7 @@ ReturnedValue RegExpPrototype::method_compile(CallContext *ctx) return ctx->throwTypeError(); ScopedCallData callData(scope, ctx->callData->argc); - memcpy(callData->args, ctx->callData->args, ctx->callData->argc*sizeof(SafeValue)); + memcpy(callData->args, ctx->callData->args, ctx->callData->argc*sizeof(Value)); Scoped<RegExpObject> re(scope, ctx->engine->regExpCtor.asFunctionObject()->construct(callData)); diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index a4cb4b9fb5..1b408749d3 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -84,7 +84,7 @@ struct RegExpObject: Object { Property *lastIndexProperty(ExecutionContext *ctx); bool global; - RegExpObject(ExecutionEngine *engine, Referenced<RegExp> value, bool global); + RegExpObject(ExecutionEngine *engine, RegExpRef value, bool global); RegExpObject(ExecutionEngine *engine, const QRegExp &re); ~RegExpObject() {} @@ -101,14 +101,15 @@ protected: static void markObjects(Managed *that, ExecutionEngine *e); }; +DEFINE_REF(RegExp, Object); struct RegExpCtor: FunctionObject { V4_OBJECT RegExpCtor(ExecutionContext *scope); - SafeValue lastMatch; - SafeString lastInput; + Value lastMatch; + StringValue lastInput; int lastMatchStart; int lastMatchEnd; void clearLastMatch(); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index c929b39e6a..a3cae3382d 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -383,8 +383,8 @@ ReturnedValue __qmljs_object_default_value(Object *object, int typeHint) if (engine->hasException) return Encode::undefined(); - SafeString *meth1 = &engine->id_toString; - SafeString *meth2 = &engine->id_valueOf; + StringValue *meth1 = &engine->id_toString; + StringValue *meth2 = &engine->id_valueOf; if (typeHint == NUMBER_HINT) qSwap(meth1, meth2); @@ -613,10 +613,14 @@ void __qmljs_set_element(ExecutionContext *ctx, const ValueRef object, const Val uint idx = index->asArrayIndex(); if (idx < UINT_MAX) { - if (o->arrayType() == ArrayData::Simple && idx < o->arrayData->length()) - o->arrayPut(idx, value); - else - o->putIndexed(idx, value); + if (o->arrayType() == ArrayData::Simple) { + SimpleArrayData *s = static_cast<SimpleArrayData *>(o->arrayData); + if (s && idx < s->len && !s->data[idx].isEmpty()) { + s->data[idx] = value; + return; + } + } + o->putIndexed(idx, value); return; } @@ -890,7 +894,7 @@ ReturnedValue __qmljs_call_property(ExecutionContext *context, const StringRef n ReturnedValue __qmljs_call_property_lookup(ExecutionContext *context, uint index, CallDataRef callData) { Lookup *l = context->lookups + index; - SafeValue v; + Value v; v = l->getter(l, callData->thisObject); if (!v.isObject()) return context->throwTypeError(); @@ -978,7 +982,7 @@ ReturnedValue __qmljs_construct_property(ExecutionContext *context, const String ReturnedValue __qmljs_construct_property_lookup(ExecutionContext *context, uint index, CallDataRef callData) { Lookup *l = context->lookups + index; - SafeValue v; + Value v; v = l->getter(l, callData->thisObject); if (!v.isObject()) return context->throwTypeError(); @@ -1098,7 +1102,7 @@ void __qmljs_builtin_define_property(ExecutionContext *ctx, const ValueRef objec } } -ReturnedValue __qmljs_builtin_define_array(ExecutionContext *ctx, SafeValue *values, uint length) +ReturnedValue __qmljs_builtin_define_array(ExecutionContext *ctx, Value *values, uint length) { Scope scope(ctx); Scoped<ArrayObject> a(scope, ctx->engine->newArrayObject()); @@ -1304,7 +1308,7 @@ QV4::ReturnedValue __qmljs_get_qml_singleton(QV4::NoThrowContext *ctx, const QV4 void __qmljs_builtin_convert_this_to_object(ExecutionContext *ctx) { - SafeValue *t = &ctx->callData->thisObject; + Value *t = &ctx->callData->thisObject; if (t->isObject()) return; if (t->isNullOrUndefined()) { diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 2e252b1067..3c2824ee23 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -42,9 +42,10 @@ #define QMLJS_RUNTIME_H #include "qv4global_p.h" -#include "qv4value_p.h" +#include "qv4value_inl_p.h" #include "qv4math_p.h" #include "qv4scopedvalue_p.h" +#include "qv4context_p.h" #include <QtCore/QString> #include <QtCore/qnumeric.h> @@ -141,7 +142,7 @@ QV4::ExecutionContext *__qmljs_builtin_pop_scope(QV4::ExecutionContext *ctx); ReturnedValue __qmljs_builtin_unwind_exception(ExecutionContext *ctx); void __qmljs_builtin_declare_var(QV4::ExecutionContext *ctx, bool deletable, const QV4::StringRef name); void __qmljs_builtin_define_property(QV4::ExecutionContext *ctx, const QV4::ValueRef object, const QV4::StringRef name, QV4::ValueRef val); -QV4::ReturnedValue __qmljs_builtin_define_array(QV4::ExecutionContext *ctx, SafeValue *values, uint length); +QV4::ReturnedValue __qmljs_builtin_define_array(QV4::ExecutionContext *ctx, Value *values, uint length); void __qmljs_builtin_define_getter_setter(QV4::ExecutionContext *ctx, const QV4::ValueRef object, const QV4::StringRef name, const QV4::ValueRef getter, const QV4::ValueRef setter); QV4::ReturnedValue __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, const QV4::Value *args, int classId); QV4::ReturnedValue __qmljs_builtin_setup_arguments_object(ExecutionContext *ctx); @@ -305,12 +306,7 @@ inline QV4::ReturnedValue __qmljs_compl(const QV4::ValueRef value) { TRACE1(value); - int n; - if (value->integerCompatible()) - n = value->int_32; - else - n = value->toInt32(); - + int n = value->toInt32(); return Encode((int)~n); } @@ -327,9 +323,6 @@ inline ReturnedValue __qmljs_bit_or(const QV4::ValueRef left, const QV4::ValueRe { TRACE2(left, right); - if (QV4::Value::integerCompatible(*left, *right)) - return Encode(left->integerValue() | right->integerValue()); - int lval = left->toInt32(); int rval = right->toInt32(); return Encode(lval | rval); @@ -339,9 +332,6 @@ inline ReturnedValue __qmljs_bit_xor(const QV4::ValueRef left, const QV4::ValueR { TRACE2(left, right); - if (QV4::Value::integerCompatible(*left, *right)) - return Encode(left->integerValue() ^ right->integerValue()); - int lval = left->toInt32(); int rval = right->toInt32(); return Encode(lval ^ rval); @@ -351,9 +341,6 @@ inline ReturnedValue __qmljs_bit_and(const QV4::ValueRef left, const QV4::ValueR { TRACE2(left, right); - if (QV4::Value::integerCompatible(*left, *right)) - return Encode(left->integerValue() & right->integerValue()); - int lval = left->toInt32(); int rval = right->toInt32(); return Encode(lval & rval); diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index 2c71a36a3d..5d471ab4fb 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -41,8 +41,9 @@ #ifndef QV4SCOPEDVALUE_P_H #define QV4SCOPEDVALUE_P_H -#include "qv4context_p.h" -#include "qv4value_def_p.h" +#include "qv4engine_p.h" +#include "qv4value_p.h" +#include "qv4persistent_p.h" QT_BEGIN_NAMESPACE @@ -54,15 +55,7 @@ namespace QV4 { struct ScopedValue; struct Scope { - explicit Scope(ExecutionContext *ctx) - : engine(ctx->engine) -#ifndef QT_NO_DEBUG - , size(0) -#endif - { - mark = engine->jsStackTop; - } - + inline explicit Scope(ExecutionContext *ctx); explicit Scope(ExecutionEngine *e) : engine(e) #ifndef QT_NO_DEBUG @@ -75,13 +68,13 @@ struct Scope { ~Scope() { #ifndef QT_NO_DEBUG Q_ASSERT(engine->jsStackTop >= mark); - memset(mark, 0, (engine->jsStackTop - mark)*sizeof(SafeValue)); + memset(mark, 0, (engine->jsStackTop - mark)*sizeof(Value)); #endif engine->jsStackTop = mark; } - SafeValue *alloc(int nValues) { - SafeValue *ptr = engine->jsStackTop; + Value *alloc(int nValues) { + Value *ptr = engine->jsStackTop; engine->jsStackTop += nValues; #ifndef QT_NO_DEBUG size += nValues; @@ -94,7 +87,7 @@ struct Scope { } ExecutionEngine *engine; - SafeValue *mark; + Value *mark; #ifndef QT_NO_DEBUG mutable int size; #endif @@ -178,17 +171,17 @@ struct ScopedValue return *this; } - SafeValue *operator->() { + Value *operator->() { return ptr; } - const SafeValue *operator->() const { + const Value *operator->() const { return ptr; } ReturnedValue asReturnedValue() const { return ptr->val; } - SafeValue *ptr; + Value *ptr; }; template<typename T> @@ -234,7 +227,7 @@ struct Scoped Scoped(const Scope &scope, const Value &v, _Convert) { ptr = scope.engine->jsStackTop++; - ptr->val = value_convert<T>(scope.engine->currentContext(), v); + ptr->val = value_convert<T>(scope.engine, v); #ifndef QT_NO_DEBUG ++scope.size; #endif @@ -281,7 +274,7 @@ struct Scoped Scoped(const Scope &scope, const ReturnedValue &v, _Convert) { ptr = scope.engine->jsStackTop++; - ptr->val = value_convert<T>(scope.engine->currentContext(), QV4::Value::fromReturnedValue(v)); + ptr->val = value_convert<T>(scope.engine, QV4::Value::fromReturnedValue(v)); #ifndef QT_NO_DEBUG ++scope.size; #endif @@ -339,7 +332,7 @@ struct Scoped #endif } - SafeValue *ptr; + Value *ptr; }; struct CallData @@ -356,14 +349,14 @@ struct CallData return i < argc ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); } - SafeValue thisObject; - SafeValue args[1]; + Value thisObject; + Value args[1]; }; struct ScopedCallData { ScopedCallData(Scope &scope, int argc) { - int size = qMax(argc, (int)QV4::Global::ReservedArgumentCount) + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue); + int size = qMax(argc, (int)QV4::Global::ReservedArgumentCount) + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value); ptr = reinterpret_cast<CallData *>(scope.engine->stackPush(size)); ptr->tag = QV4::Value::Integer_Type; ptr->argc = argc; @@ -385,141 +378,10 @@ struct ScopedCallData { CallData *ptr; }; -struct ValueRef { - ValueRef(const ScopedValue &v) - : ptr(v.ptr) {} - template <typename T> - ValueRef(const Scoped<T> &v) - : ptr(v.ptr) {} - ValueRef(const PersistentValue &v) - : ptr(&v.d->value) {} - ValueRef(PersistentValuePrivate *p) - : ptr(&p->value) {} - ValueRef(SafeValue &v) { ptr = &v; } - // Important: Do NOT add a copy constructor to this class - // adding a copy constructor actually changes the calling convention, ie. - // is not even binary compatible. Adding it would break assumptions made - // in the jit'ed code. - ValueRef &operator=(const ScopedValue &o) - { *ptr = *o.ptr; return *this; } - ValueRef &operator=(const ValueRef &o) - { *ptr = *o.ptr; return *this; } - ValueRef &operator=(const Value &v) - { *ptr = v; return *this; } - ValueRef &operator=(const ReturnedValue &v) { - ptr->val = v; - return *this; - } - template <typename T> - ValueRef &operator=(Returned<T> *v) { - ptr->val = v->asReturnedValue(); - return *this; - } - - operator const Value *() const { - return ptr; - } - const Value *operator->() const { - return ptr; - } - - operator Value *() { - return ptr; - } - SafeValue *operator->() { - return ptr; - } - - static ValueRef fromRawValue(Value *v) { - return ValueRef(v); - } - static const ValueRef fromRawValue(const Value *v) { - return ValueRef(const_cast<Value *>(v)); - } - - ReturnedValue asReturnedValue() const { return ptr->val; } - - // ### get rid of this one! - ValueRef(Value *v) { ptr = reinterpret_cast<SafeValue *>(v); } -private: - SafeValue *ptr; -}; - - -template<typename T> -struct Referenced { - // Important: Do NOT add a copy constructor to this class - // adding a copy constructor actually changes the calling convention, ie. - // is not even binary compatible. Adding it would break assumptions made - // in the jit'ed code. - Referenced(const Scoped<T> &v) - : ptr(v.ptr) {} - Referenced(Safe<T> &v) { ptr = &v; } - Referenced(SafeValue &v) { - ptr = value_cast<T>(v) ? &v : 0; - } - - Referenced &operator=(const Referenced &o) - { *ptr = *o.ptr; return *this; } - Referenced &operator=(T *t) - { -#if QT_POINTER_SIZE == 4 - ptr->tag = Value::Managed_Type; -#endif - ptr->m = t; - return *this; - } - Referenced &operator=(Returned<T> *t) { -#if QT_POINTER_SIZE == 4 - ptr->tag = Value::Managed_Type; -#endif - ptr->m = t->getPointer(); - return *this; - } - - operator const T *() const { - return ptr ? static_cast<T*>(ptr->managed()) : 0; - } - const T *operator->() const { - return static_cast<T*>(ptr->managed()); - } - - operator T *() { - return ptr ? static_cast<T*>(ptr->managed()) : 0; - } - T *operator->() { - return static_cast<T*>(ptr->managed()); - } - - T *getPointer() const { - return static_cast<T *>(ptr->managed()); - } - ReturnedValue asReturnedValue() const { return ptr ? ptr->val : Primitive::undefinedValue().asReturnedValue(); } - operator Returned<T> *() const { return ptr ? Returned<T>::create(getPointer()) : 0; } - - bool operator==(const Referenced<T> &other) { - if (ptr == other.ptr) - return true; - return ptr && other.ptr && ptr->m == other.ptr->m; - } - bool operator!=(const Referenced<T> &other) { - if (ptr == other.ptr) - return false; - return !ptr || ptr->m != other.ptr->m; - } - bool operator!() const { return !ptr || !ptr->managed(); } - - static Referenced null() { return Referenced(Null); } - bool isNull() const { return !ptr; } -private: - enum _Null { Null }; - Referenced(_Null) { ptr = 0; } - SafeValue *ptr; -}; -typedef Referenced<String> StringRef; -typedef Referenced<Object> ObjectRef; -typedef Referenced<FunctionObject> FunctionObjectRef; +struct StringRef; +struct ObjectRef; +struct FunctionObjectRef; template<typename T> inline Scoped<T>::Scoped(const Scope &scope, const ValueRef &v) @@ -569,51 +431,6 @@ private: CallData *ptr; }; -struct Encode { - static ReturnedValue undefined() { - return quint64(Value::Undefined_Type) << Value::Tag_Shift; - } - static ReturnedValue null() { - return quint64(Value::_Null_Type) << Value::Tag_Shift; - } - - Encode(bool b) { - val = (quint64(Value::_Boolean_Type) << Value::Tag_Shift) | (uint)b; - } - Encode(double d) { - Value v; - v.setDouble(d); - val = v.val; - } - Encode(int i) { - val = (quint64(Value::_Integer_Type) << Value::Tag_Shift) | (uint)i; - } - Encode(uint i) { - if (i <= INT_MAX) { - val = (quint64(Value::_Integer_Type) << Value::Tag_Shift) | i; - } else { - Value v; - v.setDouble(i); - val = v.val; - } - } - Encode(ReturnedValue v) { - val = v; - } - - template<typename T> - Encode(Returned<T> *t) { - val = t->getPointer()->asReturnedValue(); - } - - operator ReturnedValue() const { - return val; - } - quint64 val; -private: - Encode(void *); -}; - template <typename T> inline Value &Value::operator=(Returned<T> *t) @@ -622,81 +439,68 @@ inline Value &Value::operator=(Returned<T> *t) return *this; } -inline SafeValue &SafeValue::operator =(const ScopedValue &v) +inline Value &Value::operator =(const ScopedValue &v) { val = v.ptr->val; return *this; } template<typename T> -inline SafeValue &SafeValue::operator=(Returned<T> *t) -{ - val = t->getPointer()->asReturnedValue(); - return *this; -} - -template<typename T> -inline SafeValue &SafeValue::operator=(const Scoped<T> &t) +inline Value &Value::operator=(const Scoped<T> &t) { val = t.ptr->val; return *this; } -inline SafeValue &SafeValue::operator=(const ValueRef v) +inline Value &Value::operator=(const ValueRef v) { val = v.asReturnedValue(); return *this; } template<typename T> -inline Returned<T> *SafeValue::as() +inline Returned<T> *Value::as() { return Returned<T>::create(value_cast<T>(*this)); } -template<typename T> inline -Referenced<T> SafeValue::asRef() -{ - return Referenced<T>(*this); -} - template<typename T> -inline Safe<T> &Safe<T>::operator =(T *t) +inline TypedValue<T> &TypedValue<T>::operator =(T *t) { val = t->asReturnedValue(); return *this; } template<typename T> -inline Safe<T> &Safe<T>::operator =(const Scoped<T> &v) +inline TypedValue<T> &TypedValue<T>::operator =(const Scoped<T> &v) { val = v.ptr->val; return *this; } template<typename T> -inline Safe<T> &Safe<T>::operator=(Returned<T> *t) +inline TypedValue<T> &TypedValue<T>::operator=(Returned<T> *t) { val = t->getPointer()->asReturnedValue(); return *this; } -template<typename T> -inline Safe<T> &Safe<T>::operator =(const Referenced<T> &v) -{ - val = v.asReturnedValue(); - return *this; -} +//template<typename T> +//inline TypedValue<T> &TypedValue<T>::operator =(const ManagedRef<T> &v) +//{ +// val = v.asReturnedValue(); +// return *this; +//} template<typename T> -inline Safe<T> &Safe<T>::operator=(const Safe<T> &t) +inline TypedValue<T> &TypedValue<T>::operator=(const TypedValue<T> &t) { val = t.val; return *this; } template<typename T> -inline Returned<T> * Safe<T>::ret() const +inline Returned<T> * TypedValue<T>::ret() const { return Returned<T>::create(static_cast<T *>(managed())); } @@ -713,9 +517,8 @@ PersistentValue::PersistentValue(Returned<T> *obj) { } -template<typename T> -inline PersistentValue::PersistentValue(const Referenced<T> obj) - : d(new PersistentValuePrivate(*obj.ptr)) +inline PersistentValue::PersistentValue(const ManagedRef obj) + : d(new PersistentValuePrivate(obj.asReturnedValue())) { } @@ -725,12 +528,15 @@ inline PersistentValue &PersistentValue::operator=(Returned<T> *obj) return operator=(QV4::Value::fromManaged(obj->getPointer()).asReturnedValue()); } -template<typename T> -inline PersistentValue &PersistentValue::operator=(const Referenced<T> obj) +inline PersistentValue &PersistentValue::operator=(const ManagedRef obj) { - return operator=(*obj.ptr); + return operator=(obj.asReturnedValue()); } +inline PersistentValue &PersistentValue::operator=(const ScopedValue &other) +{ + return operator=(other.asReturnedValue()); +} template<typename T> inline WeakValue::WeakValue(Returned<T> *obj) @@ -744,10 +550,53 @@ inline WeakValue &WeakValue::operator=(Returned<T> *obj) return operator=(QV4::Value::fromManaged(obj->getPointer()).asReturnedValue()); } -inline ReturnedValue CallContext::argument(int i) { - return i < callData->argc ? callData->args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); +inline ValueRef::ValueRef(const ScopedValue &v) + : ptr(v.ptr) +{} + +template <typename T> +inline ValueRef::ValueRef(const Scoped<T> &v) + : ptr(v.ptr) +{} + +inline ValueRef::ValueRef(const PersistentValue &v) + : ptr(&v.d->value) +{} + +inline ValueRef::ValueRef(PersistentValuePrivate *p) + : ptr(&p->value) +{} + +inline ValueRef &ValueRef::operator=(const ScopedValue &o) +{ + *ptr = *o.ptr; + return *this; +} + + +inline Value *extractValuePointer(const ScopedValue &v) +{ + return v.ptr; } +template<typename T> +Value *extractValuePointer(const Scoped<T> &v) +{ + return v.ptr; +} + +struct ScopedProperty +{ + ScopedProperty(Scope &scope) + { + property = reinterpret_cast<Property*>(scope.alloc(sizeof(Property) / sizeof(Value))); + } + + Property *operator->() { return property; } + operator const Property &() { return *property; } + + Property *property; +}; } diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index c50b025bc9..7d94195304 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -168,7 +168,7 @@ Script::Script(ExecutionEngine *v4, ObjectRef qml, CompiledData::CompilationUnit Q_ASSERT(vmFunction); Scope valueScope(v4); ScopedValue holder(valueScope, new (v4->memoryManager) CompilationUnitHolder(v4, compilationUnit)); - compilationUnitHolder = holder; + compilationUnitHolder = holder.asReturnedValue(); } else vmFunction = 0; } @@ -236,7 +236,7 @@ void Script::parse() QV4::CompiledData::CompilationUnit *compilationUnit = isel->compile(); vmFunction = compilationUnit->linkToEngine(v4); ScopedValue holder(valueScope, new (v4->memoryManager) CompilationUnitHolder(v4, compilationUnit)); - compilationUnitHolder = holder; + compilationUnitHolder = holder.asReturnedValue(); } if (!vmFunction) { diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 4039f81896..a82dcffbca 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -175,10 +175,10 @@ public: , m_propertyIndex(-1) , m_isReference(false) { - setArrayType(ArrayData::Custom); QV4::Scope scope(engine); QV4::ScopedObject protectThis(scope, this); Q_UNUSED(protectThis); + setArrayType(ArrayData::Custom); init(); } @@ -188,10 +188,10 @@ public: , m_propertyIndex(propertyIndex) , m_isReference(true) { - setArrayType(ArrayData::Custom); QV4::Scope scope(engine); QV4::ScopedObject protectThis(scope, this); Q_UNUSED(protectThis); + setArrayType(ArrayData::Custom); loadReference(); init(); } diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index 54a96863df..4a5e82b688 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -56,7 +56,7 @@ #include <QtCore/qglobal.h> #include <QtCore/qvariant.h> -#include "qv4value_p.h" +#include "qv4value_inl_p.h" #include "qv4object_p.h" QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp index 50899c3893..3d754389a2 100644 --- a/src/qml/jsruntime/qv4serialize.cpp +++ b/src/qml/jsruntime/qv4serialize.cpp @@ -45,7 +45,7 @@ #include <private/qqmllistmodel_p.h> #include <private/qqmllistmodelworkeragent_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4dateobject_p.h> #include <private/qv4regexpobject_p.h> #include <private/qv4sequenceobject_p.h> diff --git a/src/qml/jsruntime/qv4serialize_p.h b/src/qml/jsruntime/qv4serialize_p.h index a9f40e43cd..5ab8ae9258 100644 --- a/src/qml/jsruntime/qv4serialize_p.h +++ b/src/qml/jsruntime/qv4serialize_p.h @@ -54,7 +54,7 @@ // #include <QtCore/qbytearray.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4sparsearray.cpp b/src/qml/jsruntime/qv4sparsearray.cpp index 7169f5c20e..ffd1adf98b 100644 --- a/src/qml/jsruntime/qv4sparsearray.cpp +++ b/src/qml/jsruntime/qv4sparsearray.cpp @@ -53,32 +53,6 @@ using namespace QV4; -bool ArrayElementLessThan::operator()(const SafeValue &v1, const SafeValue &v2) const -{ - Scope scope(m_context); - - if (v1.isUndefined() || v1.isEmpty()) - return false; - if (v2.isUndefined() || v2.isEmpty()) - return true; - ScopedObject o(scope, m_comparefn); - if (o) { - Scope scope(o->engine()); - ScopedValue result(scope); - ScopedCallData callData(scope, 2); - callData->thisObject = Primitive::undefinedValue(); - callData->args[0] = v1; - callData->args[1] = v2; - result = __qmljs_call_value(m_context, m_comparefn, callData); - - return result->toNumber() < 0; - } - ScopedString p1s(scope, v1.toString(m_context)); - ScopedString p2s(scope, v2.toString(m_context)); - return p1s->toQString() < p2s->toQString(); -} - - const SparseArrayNode *SparseArrayNode::nextNode() const { const SparseArrayNode *n = this; diff --git a/src/qml/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h index 6c2808b6b1..0bcbc805f3 100644 --- a/src/qml/jsruntime/qv4sparsearray_p.h +++ b/src/qml/jsruntime/qv4sparsearray_p.h @@ -39,12 +39,12 @@ ** ****************************************************************************/ -#ifndef QV4ARRAY_H -#define QV4ARRAY_H +#ifndef QV4SPARSEARRAY_H +#define QV4SPARSEARRAY_H #include "qv4global_p.h" #include <QtCore/qmap.h> -#include "qv4value_p.h" +#include "qv4value_inl_p.h" #include "qv4scopedvalue_p.h" #include "qv4property_p.h" #include <assert.h> @@ -62,21 +62,6 @@ namespace QV4 { struct SparseArray; -class ArrayElementLessThan -{ -public: - inline ArrayElementLessThan(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn) - : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {} - - bool operator()(const SafeValue &v1, const SafeValue &v2) const; - -private: - ExecutionContext *m_context; - ObjectRef thisObject; - const ValueRef m_comparefn; -}; - - struct SparseArrayNode { quintptr p; diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index e3d8326d84..ade64d1352 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -66,10 +66,6 @@ struct Q_QML_EXPORT String : public Managed { StringType_ArrayIndex }; - String() - : Managed(0), _text(QStringData::sharedNull()), identifier(0) - , stringHash(UINT_MAX), largestSubLength(0), len(0) - { subtype = StringType_Unknown; } String(ExecutionEngine *engine, const QString &text); String(ExecutionEngine *engine, String *l, String *n); ~String() { @@ -186,11 +182,13 @@ inline String *value_cast(const Value &v) { } template<> -inline ReturnedValue value_convert<String>(ExecutionContext *ctx, const Value &v) +inline ReturnedValue value_convert<String>(ExecutionEngine *e, const Value &v) { - return v.toString(ctx)->asReturnedValue(); + return v.toString(e)->asReturnedValue(); } +DEFINE_REF(String, Managed); + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index 9b26343c63..c38fd5b75f 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -53,7 +53,7 @@ struct StringObject: Object { V4_OBJECT Q_MANAGED_TYPE(StringObject) - SafeValue value; + Value value; mutable Property tmpProperty; StringObject(ExecutionEngine *engine, const ValueRef value); diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index 30f7e8cdb0..c91084caee 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -262,6 +262,11 @@ double Primitive::toInteger(double number) return std::signbit(number) ? -v : v; } +String *Value::toString(ExecutionEngine *e) const +{ + return toString(e->currentContext()); +} + String *Value::toString(ExecutionContext *ctx) const { if (isString()) @@ -276,213 +281,3 @@ Object *Value::toObject(ExecutionContext *ctx) const return __qmljs_convert_to_object(ctx, ValueRef::fromRawValue(this))->getPointer(); } - -PersistentValue::PersistentValue(const ValueRef val) - : d(new PersistentValuePrivate(val.asReturnedValue())) -{ -} - -PersistentValue::PersistentValue(ReturnedValue val) - : d(new PersistentValuePrivate(val)) -{ -} - -PersistentValue::PersistentValue(const PersistentValue &other) - : d(other.d) -{ - if (d) - d->ref(); -} - -PersistentValue &PersistentValue::operator=(const PersistentValue &other) -{ - if (d == other.d) - return *this; - - // the memory manager cleans up those with a refcount of 0 - - if (d) - d->deref(); - d = other.d; - if (d) - d->ref(); - - return *this; -} - -PersistentValue &PersistentValue::operator =(const ValueRef other) -{ - if (!d) { - d = new PersistentValuePrivate(other.asReturnedValue()); - return *this; - } - d = d->detach(other.asReturnedValue()); - return *this; -} - -PersistentValue &PersistentValue::operator =(ReturnedValue other) -{ - if (!d) { - d = new PersistentValuePrivate(other); - return *this; - } - d = d->detach(other); - return *this; -} - -PersistentValue::~PersistentValue() -{ - if (d) - d->deref(); -} - -WeakValue::WeakValue(const ValueRef val) - : d(new PersistentValuePrivate(val.asReturnedValue(), /*engine*/0, /*weak*/true)) -{ -} - -WeakValue::WeakValue(const WeakValue &other) - : d(other.d) -{ - if (d) - d->ref(); -} - -WeakValue::WeakValue(ReturnedValue val) - : d(new PersistentValuePrivate(val, /*engine*/0, /*weak*/true)) -{ -} - -WeakValue &WeakValue::operator=(const WeakValue &other) -{ - if (d == other.d) - return *this; - - // the memory manager cleans up those with a refcount of 0 - - if (d) - d->deref(); - d = other.d; - if (d) - d->ref(); - - return *this; -} - -WeakValue &WeakValue::operator =(const ValueRef other) -{ - if (!d) { - d = new PersistentValuePrivate(other.asReturnedValue(), /*engine*/0, /*weak*/true); - return *this; - } - d = d->detach(other.asReturnedValue(), /*weak*/true); - return *this; -} - -WeakValue &WeakValue::operator =(const ReturnedValue &other) -{ - if (!d) { - d = new PersistentValuePrivate(other, /*engine*/0, /*weak*/true); - return *this; - } - d = d->detach(other, /*weak*/true); - return *this; -} - - -WeakValue::~WeakValue() -{ - if (d) - d->deref(); -} - -void WeakValue::markOnce(ExecutionEngine *e) -{ - if (!d) - return; - d->value.mark(e); -} - -PersistentValuePrivate::PersistentValuePrivate(ReturnedValue v, ExecutionEngine *e, bool weak) - : refcount(1) - , weak(weak) - , engine(e) - , prev(0) - , next(0) -{ - value.val = v; - init(); -} - -void PersistentValuePrivate::init() -{ - if (!engine) { - Managed *m = value.asManaged(); - if (!m) - return; - - engine = m->engine(); - } - if (engine && !prev) { - PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues; - - prev = listRoot; - next = *listRoot; - *prev = this; - if (next) - next->prev = &this->next; - } -} - -PersistentValuePrivate::~PersistentValuePrivate() -{ -} - -void PersistentValuePrivate::removeFromList() -{ - if (prev) { - if (next) - next->prev = prev; - *prev = next; - next = 0; - prev = 0; - } -} - -void PersistentValuePrivate::deref() -{ - // if engine is not 0, they are registered with the memory manager - // and will get cleaned up in the next gc run - if (!--refcount) { - removeFromList(); - delete this; - } -} - -PersistentValuePrivate *PersistentValuePrivate::detach(const QV4::ReturnedValue val, bool weak) -{ - if (refcount == 1) { - value.val = val; - - Managed *m = value.asManaged(); - if (!prev) { - if (m) { - ExecutionEngine *engine = m->engine(); - if (engine) { - PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues; - prev = listRoot; - next = *listRoot; - *prev = this; - if (next) - next->prev = &this->next; - } - } - } else if (!m) - removeFromList(); - - return this; - } - --refcount; - return new PersistentValuePrivate(val, engine, weak); -} - diff --git a/src/qml/jsruntime/qv4value_def_p.h b/src/qml/jsruntime/qv4value_def_p.h deleted file mode 100644 index cf351c125a..0000000000 --- a/src/qml/jsruntime/qv4value_def_p.h +++ /dev/null @@ -1,476 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4VALUE_DEF_P_H -#define QV4VALUE_DEF_P_H - -#include <QtCore/QString> -#include "qv4global_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -typedef uint Bool; - -template <typename T> -struct Returned : private T -{ - static Returned<T> *create(T *t) { return static_cast<Returned<T> *>(t); } - T *getPointer() { return this; } - template<typename X> - static T *getPointer(Returned<X> *x) { return x->getPointer(); } - template<typename X> - Returned<X> *as() { return Returned<X>::create(Returned<X>::getPointer(this)); } - using T::asReturnedValue; -}; - -struct Q_QML_EXPORT Value -{ - /* - We use two different ways of encoding JS values. One for 32bit and one for 64bit systems. - - In both cases, we 8 bytes for a value and different variant of NaN boxing. A Double NaN (actually -qNaN) - is indicated by a number that has the top 13 bits set. THe other values are usually set to 0 by the - processor, and are thus free for us to store other data. We keep pointers in there for managed objects, - and encode the other types using the free space given to use by the unused bits for NaN values. This also - works for pointers on 64 bit systems, as they all currently only have 48 bits of addressable memory. - - On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value that - will make the number a NaN. The Masks below are used for encoding the other types. - - On 64 bit, we xor Doubles with (0xffff8000 << 32). Thas has the effect that no doubles will get encoded - with the 13 highest bits all 0. We are now using special values for bits 14-17 to encode our values. These - can be used, as the highest valid pointer on a 64 bit system is 2^48-1. - - If they are all 0, we have a pointer to a Managed object. If bit 14 is set we have an integer. - This makes testing for pointers and numbers very fast (we have a number if any of the highest 14 bits is set). - - Bit 15-17 is then used to encode other immediates. - */ - - - union { - quint64 val; -#if QT_POINTER_SIZE == 8 - Managed *m; - Object *o; - String *s; -#else - double dbl; -#endif - struct { -#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN - uint tag; -#endif - union { - uint uint_32; - int int_32; -#if QT_POINTER_SIZE == 4 - Managed *m; - Object *o; - String *s; -#endif - }; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - uint tag; -#endif - }; - }; - -#if QT_POINTER_SIZE == 4 - enum Masks { - NaN_Mask = 0x7ff80000, - NotDouble_Mask = 0x7ffc0000, - Type_Mask = 0xffff8000, - Immediate_Mask = NotDouble_Mask | 0x00008000, - IsNullOrUndefined_Mask = Immediate_Mask | 0x20000, - Tag_Shift = 32 - }; - enum ValueType { - Undefined_Type = Immediate_Mask | 0x00000, - Null_Type = Immediate_Mask | 0x10000, - Boolean_Type = Immediate_Mask | 0x20000, - Integer_Type = Immediate_Mask | 0x30000, - Managed_Type = NotDouble_Mask | 0x00000, - Empty_Type = NotDouble_Mask | 0x30000 - }; - - enum ImmediateFlags { - ConvertibleToInt = Immediate_Mask | 0x1 - }; - - enum ValueTypeInternal { - _Null_Type = Null_Type | ConvertibleToInt, - _Boolean_Type = Boolean_Type | ConvertibleToInt, - _Integer_Type = Integer_Type | ConvertibleToInt, - - }; -#else - static const quint64 NaNEncodeMask = 0xffff800000000000ll; - static const quint64 IsInt32Mask = 0x0002000000000000ll; - static const quint64 IsDoubleMask = 0xfffc000000000000ll; - static const quint64 IsNumberMask = IsInt32Mask|IsDoubleMask; - static const quint64 IsNullOrUndefinedMask = 0x0000800000000000ll; - static const quint64 IsNullOrBooleanMask = 0x0001000000000000ll; - static const quint64 IsConvertibleToIntMask = IsInt32Mask|IsNullOrBooleanMask; - - enum Masks { - NaN_Mask = 0x7ff80000, - Type_Mask = 0xffff8000, - IsDouble_Mask = 0xfffc0000, - Immediate_Mask = 0x00018000, - IsNullOrUndefined_Mask = 0x00008000, - IsNullOrBoolean_Mask = 0x00010000, - Tag_Shift = 32 - }; - enum ValueType { - Undefined_Type = IsNullOrUndefined_Mask, - Null_Type = IsNullOrUndefined_Mask|IsNullOrBoolean_Mask, - Boolean_Type = IsNullOrBoolean_Mask, - Integer_Type = 0x20000|IsNullOrBoolean_Mask, - Managed_Type = 0, - Empty_Type = Undefined_Type | 0x4000 - }; - enum { - IsDouble_Shift = 64-14, - IsNumber_Shift = 64-15, - IsConvertibleToInt_Shift = 64-16, - IsManaged_Shift = 64-17 - }; - - - enum ValueTypeInternal { - _Null_Type = Null_Type, - _Boolean_Type = Boolean_Type, - _Integer_Type = Integer_Type - }; -#endif - - inline unsigned type() const { - return tag & Type_Mask; - } - - // used internally in property - inline bool isEmpty() const { return tag == Empty_Type; } - - inline bool isUndefined() const { return tag == Undefined_Type; } - inline bool isNull() const { return tag == _Null_Type; } - inline bool isBoolean() const { return tag == _Boolean_Type; } -#if QT_POINTER_SIZE == 8 - inline bool isInteger() const { return (val >> IsNumber_Shift) == 1; } - inline bool isDouble() const { return (val >> IsDouble_Shift); } - inline bool isNumber() const { return (val >> IsNumber_Shift); } - inline bool isManaged() const { return !(val >> IsManaged_Shift); } - inline bool isNullOrUndefined() const { return ((val >> IsManaged_Shift) & ~2) == 1; } - inline bool integerCompatible() const { return ((val >> IsConvertibleToInt_Shift) & ~2) == 1; } - static inline bool integerCompatible(Value a, Value b) { - return a.integerCompatible() && b.integerCompatible(); - } - static inline bool bothDouble(Value a, Value b) { - return a.isDouble() && b.isDouble(); - } - double doubleValue() const { - Q_ASSERT(isDouble()); - union { - quint64 i; - double d; - } v; - v.i = val ^ NaNEncodeMask; - return v.d; - } - void setDouble(double d) { - union { - quint64 i; - double d; - } v; - v.d = d; - val = v.i ^ NaNEncodeMask; - Q_ASSERT(isDouble()); - } - bool isNaN() const { return (tag & 0x7fff8000) == 0x00078000; } -#else - inline bool isInteger() const { return tag == _Integer_Type; } - inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } - inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } - inline bool isManaged() const { return tag == Managed_Type; } - inline bool isNullOrUndefined() const { return (tag & IsNullOrUndefined_Mask) == Undefined_Type; } - inline bool integerCompatible() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } - static inline bool integerCompatible(Value a, Value b) { - return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; - } - static inline bool bothDouble(Value a, Value b) { - return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; - } - double doubleValue() const { return dbl; } - void setDouble(double d) { dbl = d; } - bool isNaN() const { return (tag & QV4::Value::NotDouble_Mask) == QV4::Value::NaN_Mask; } -#endif - inline bool isString() const; - inline bool isObject() const; - inline bool isInt32() { - if (tag == _Integer_Type) - return true; - if (isDouble()) { - double d = doubleValue(); - int i = (int)d; - if (i == d) { - int_32 = i; - tag = _Integer_Type; - return true; - } - } - return false; - } - double asDouble() const { - if (tag == _Integer_Type) - return int_32; - return doubleValue(); - } - - bool booleanValue() const { - return int_32; - } - int integerValue() const { - return int_32; - } - - String *stringValue() const { - return s; - } - Object *objectValue() const { - return o; - } - Managed *managed() const { - return m; - } - - quint64 rawValue() const { - return val; - } - - static inline Value fromManaged(Managed *o); - - int toUInt16() const; - inline int toInt32() const; - inline unsigned int toUInt32() const; - - inline bool toBoolean() const; - double toInteger() const; - inline double toNumber() const; - double toNumberImpl() const; - QString toQStringNoThrow() const; - QString toQString() const; - String *toString(ExecutionContext *ctx) const; - Object *toObject(ExecutionContext *ctx) const; - - inline bool isPrimitive() const; - inline bool tryIntegerConversion() { - bool b = integerCompatible(); - if (b) - tag = _Integer_Type; - return b; - } - - inline String *asString() const; - inline Managed *asManaged() const; - inline Object *asObject() const; - inline FunctionObject *asFunctionObject() const; - inline NumberObject *asNumberObject() const; - inline StringObject *asStringObject() const; - inline DateObject *asDateObject() const; - inline ArrayObject *asArrayObject() const; - inline ErrorObject *asErrorObject() const; - - template<typename T> inline T *as() const; - - inline uint asArrayIndex() const; - inline uint asArrayLength(bool *ok) const; - - inline ExecutionEngine *engine() const; - - ReturnedValue asReturnedValue() const { return val; } - static Value fromReturnedValue(ReturnedValue val) { Value v; v.val = val; return v; } - Value &operator=(ReturnedValue v) { val = v; return *this; } - template <typename T> - inline Value &operator=(Returned<T> *t); - - // Section 9.12 - bool sameValue(Value other) const; - - inline void mark(ExecutionEngine *e) const; -}; - -inline Managed *Value::asManaged() const -{ - if (isManaged()) - return managed(); - return 0; -} - -inline String *Value::asString() const -{ - if (isString()) - return stringValue(); - return 0; -} - -struct Q_QML_EXPORT Primitive : public Value -{ - inline static Primitive emptyValue(); - static inline Primitive fromBoolean(bool b); - static inline Primitive fromInt32(int i); - inline static Primitive undefinedValue(); - static inline Primitive nullValue(); - static inline Primitive fromDouble(double d); - static inline Primitive fromUInt32(uint i); - - static double toInteger(double fromNumber); - static int toInt32(double value); - static unsigned int toUInt32(double value); - - inline operator ValueRef(); - Value asValue() const { return *this; } -}; - -inline Primitive Primitive::undefinedValue() -{ - Primitive v; -#if QT_POINTER_SIZE == 8 - v.val = quint64(Undefined_Type) << Tag_Shift; -#else - v.tag = Undefined_Type; - v.int_32 = 0; -#endif - return v; -} - -inline Primitive Primitive::emptyValue() -{ - Primitive v; - v.tag = Value::Empty_Type; - v.uint_32 = 0; - return v; -} - -inline Value Value::fromManaged(Managed *m) -{ - if (!m) - return QV4::Primitive::undefinedValue(); - Value v; -#if QT_POINTER_SIZE == 8 - v.m = m; -#else - v.tag = Managed_Type; - v.m = m; -#endif - return v; -} - -struct SafeValue : public Value -{ - SafeValue &operator =(const ScopedValue &v); - template<typename T> - SafeValue &operator=(Returned<T> *t); - SafeValue &operator=(ReturnedValue v) { - val = v; - return *this; - } - template<typename T> - SafeValue &operator=(T *t) { - val = Value::fromManaged(t).val; - return *this; - } - - template<typename T> - SafeValue &operator=(const Scoped<T> &t); - SafeValue &operator=(const ValueRef v); - SafeValue &operator=(const Value &v) { - val = v.val; - return *this; - } - template<typename T> - inline Returned<T> *as(); - template<typename T> - inline Referenced<T> asRef(); -}; - -template <typename T> -struct Safe : public SafeValue -{ - template<typename X> - Safe &operator =(X *x) { - val = Value::fromManaged(x).val; - } - Safe &operator =(T *t); - Safe &operator =(const Scoped<T> &v); - Safe &operator =(const Referenced<T> &v); - Safe &operator =(Returned<T> *t); - - Safe &operator =(const Safe<T> &t); - - bool operator!() const { return !managed(); } - - T *operator->() { return static_cast<T *>(managed()); } - const T *operator->() const { return static_cast<T *>(managed()); } - T *getPointer() const { return static_cast<T *>(managed()); } - Returned<T> *ret() const; - - void mark(ExecutionEngine *e) { if (managed()) managed()->mark(e); } -}; -typedef Safe<String> SafeString; -typedef Safe<Object> SafeObject; - -template<typename T> -T *value_cast(const Value &v) -{ - return v.as<T>(); -} - -template<typename T> -ReturnedValue value_convert(ExecutionContext *ctx, const Value &v); - - - -} - -QT_END_NAMESPACE - -#endif // QV4VALUE_DEF_P_H diff --git a/src/qml/jsruntime/qv4value_inl_p.h b/src/qml/jsruntime/qv4value_inl_p.h new file mode 100644 index 0000000000..d82af0643e --- /dev/null +++ b/src/qml/jsruntime/qv4value_inl_p.h @@ -0,0 +1,283 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4VALUE_INL_H +#define QV4VALUE_INL_H + +#include <cmath> // this HAS to come + +#include <QtCore/QString> +#include <QtCore/qnumeric.h> +#include "qv4global_p.h" +#include "qv4string_p.h" +#include <QtCore/QDebug> +#include "qv4managed_p.h" +#include "qv4engine_p.h" +#include <private/qtqmlglobal_p.h> + +//#include <wtf/MathExtras.h> + +#include "qv4value_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +inline bool Value::isString() const +{ + if (!isManaged()) + return false; + return managed() && managed()->internalClass->vtable->isString; +} +inline bool Value::isObject() const +{ + if (!isManaged()) + return false; + return managed() && managed()->internalClass->vtable->isObject; +} + +inline bool Value::isPrimitive() const +{ + return !isObject(); +} + +inline ExecutionEngine *Value::engine() const +{ + Managed *m = asManaged(); + return m ? m->engine() : 0; +} + +inline void Value::mark(ExecutionEngine *e) const +{ + if (!val) + return; + Managed *m = asManaged(); + if (m) + m->mark(e); +} + +inline Primitive Primitive::nullValue() +{ + Primitive v; +#if QT_POINTER_SIZE == 8 + v.val = quint64(_Null_Type) << Tag_Shift; +#else + v.tag = _Null_Type; + v.int_32 = 0; +#endif + return v; +} + +inline Primitive Primitive::fromBoolean(bool b) +{ + Primitive v; + v.tag = _Boolean_Type; + v.int_32 = (bool)b; + return v; +} + +inline Primitive Primitive::fromDouble(double d) +{ + Primitive v; + v.setDouble(d); + return v; +} + +inline Primitive Primitive::fromInt32(int i) +{ + Primitive v; + v.tag = _Integer_Type; + v.int_32 = i; + return v; +} + +inline Primitive Primitive::fromUInt32(uint i) +{ + Primitive v; + if (i < INT_MAX) { + v.tag = _Integer_Type; + v.int_32 = (int)i; + } else { + v.setDouble(i); + } + return v; +} + +inline double Value::toNumber() const +{ + if (integerCompatible()) + return int_32; + if (isDouble()) + return doubleValue(); + return toNumberImpl(); +} + +inline int Value::toInt32() const +{ + if (integerCompatible()) + return int_32; + double d; + if (isDouble()) + d = doubleValue(); + else + d = toNumberImpl(); + + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; + + if ((d >= -D31 && d < D31)) + return static_cast<int>(d); + + return Primitive::toInt32(d); +} + +inline unsigned int Value::toUInt32() const +{ + return (unsigned int)toInt32(); +} + + +inline bool Value::toBoolean() const +{ + switch (type()) { + case Value::Undefined_Type: + case Value::Null_Type: + return false; + case Value::Boolean_Type: + case Value::Integer_Type: + return (bool)int_32; + case Value::Managed_Type: + if (isString()) + return stringValue()->toQString().length() > 0; + return true; + default: // double + return doubleValue() && !std::isnan(doubleValue()); + } +} + +inline uint Value::asArrayIndex() const +{ +#if QT_POINTER_SIZE == 8 + if (!isNumber()) + return UINT_MAX; + if (isInteger()) + return int_32 >= 0 ? (uint)int_32 : UINT_MAX; +#else + if (isInteger() && int_32 >= 0) + return (uint)int_32; + if (!isDouble()) + return UINT_MAX; +#endif + double d = doubleValue(); + uint idx = (uint)d; + if (idx != d) + return UINT_MAX; + return idx; +} + +inline uint Value::asArrayLength(bool *ok) const +{ + *ok = true; + if (integerCompatible() && int_32 >= 0) + return (uint)int_32; + if (isDouble()) { + double d = doubleValue(); + uint idx = (uint)d; + if (idx != d) { + *ok = false; + return UINT_MAX; + } + return idx; + } + if (isString()) + return stringValue()->toUInt(ok); + + uint idx = toUInt32(); + double d = toNumber(); + if (d != idx) { + *ok = false; + return UINT_MAX; + } + return idx; +} + +inline Object *Value::asObject() const +{ + return isObject() ? objectValue() : 0; +} + +inline FunctionObject *Value::asFunctionObject() const +{ + return isObject() ? managed()->asFunctionObject() : 0; +} + +inline NumberObject *Value::asNumberObject() const +{ + return isObject() ? managed()->asNumberObject() : 0; +} + +inline StringObject *Value::asStringObject() const +{ + return isObject() ? managed()->asStringObject() : 0; +} + +inline DateObject *Value::asDateObject() const +{ + return isObject() ? managed()->asDateObject() : 0; +} + +inline ArrayObject *Value::asArrayObject() const +{ + return isObject() ? managed()->asArrayObject() : 0; +} + +inline ErrorObject *Value::asErrorObject() const +{ + return isObject() ? managed()->asErrorObject() : 0; +} + +template<typename T> +inline T *Value::as() const { Managed *m = isObject() ? managed() : 0; return m ? m->as<T>() : 0; } + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index b93fcbe4bd..27c81d59a5 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -38,360 +38,530 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#ifndef QMLJS_VALUE_H -#define QMLJS_VALUE_H +#ifndef QV4VALUE_P_H +#define QV4VALUE_P_H -#include <cmath> // this HAS to come +#include <limits.h> #include <QtCore/QString> -#include <QtCore/qnumeric.h> #include "qv4global_p.h" -#include "qv4string_p.h" -#include <QtCore/QDebug> -#include "qv4managed_p.h" -#include "qv4engine_p.h" -#include <private/qtqmlglobal_p.h> - -//#include <wtf/MathExtras.h> - -#include "qv4value_def_p.h" QT_BEGIN_NAMESPACE namespace QV4 { -inline bool Value::isString() const -{ - if (!isManaged()) - return false; - return managed() && managed()->internalClass->vtable->isString; -} -inline bool Value::isObject() const -{ - if (!isManaged()) - return false; - return managed() && managed()->internalClass->vtable->isObject; -} +typedef uint Bool; -inline bool Value::isPrimitive() const +template <typename T> +struct Returned : private T { - return !isObject(); -} + static Returned<T> *create(T *t) { return static_cast<Returned<T> *>(t); } + T *getPointer() { return this; } + template<typename X> + static T *getPointer(Returned<X> *x) { return x->getPointer(); } + template<typename X> + Returned<X> *as() { return Returned<X>::create(Returned<X>::getPointer(this)); } + using T::asReturnedValue; +}; -inline ExecutionEngine *Value::engine() const +struct Q_QML_EXPORT Value { - Managed *m = asManaged(); - return m ? m->engine() : 0; -} + /* + We use two different ways of encoding JS values. One for 32bit and one for 64bit systems. -inline void Value::mark(ExecutionEngine *e) const -{ - if (!val) - return; - Managed *m = asManaged(); - if (m) - m->mark(e); -} + In both cases, we 8 bytes for a value and different variant of NaN boxing. A Double NaN (actually -qNaN) + is indicated by a number that has the top 13 bits set. THe other values are usually set to 0 by the + processor, and are thus free for us to store other data. We keep pointers in there for managed objects, + and encode the other types using the free space given to use by the unused bits for NaN values. This also + works for pointers on 64 bit systems, as they all currently only have 48 bits of addressable memory. -inline Primitive Primitive::nullValue() -{ - Primitive v; + On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value that + will make the number a NaN. The Masks below are used for encoding the other types. + + On 64 bit, we xor Doubles with (0xffff8000 << 32). Thas has the effect that no doubles will get encoded + with the 13 highest bits all 0. We are now using special values for bits 14-17 to encode our values. These + can be used, as the highest valid pointer on a 64 bit system is 2^48-1. + + If they are all 0, we have a pointer to a Managed object. If bit 14 is set we have an integer. + This makes testing for pointers and numbers very fast (we have a number if any of the highest 14 bits is set). + + Bit 15-17 is then used to encode other immediates. + */ + + + union { + quint64 val; #if QT_POINTER_SIZE == 8 - v.val = quint64(_Null_Type) << Tag_Shift; + Managed *m; + Object *o; + String *s; #else - v.tag = _Null_Type; - v.int_32 = 0; + double dbl; +#endif + struct { +#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN + uint tag; +#endif + union { + uint uint_32; + int int_32; +#if QT_POINTER_SIZE == 4 + Managed *m; + Object *o; + String *s; +#endif + }; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + uint tag; +#endif + }; + }; + +#if QT_POINTER_SIZE == 4 + enum Masks { + NaN_Mask = 0x7ff80000, + NotDouble_Mask = 0x7ffc0000, + Type_Mask = 0xffff8000, + Immediate_Mask = NotDouble_Mask | 0x00008000, + IsNullOrUndefined_Mask = Immediate_Mask | 0x20000, + Tag_Shift = 32 + }; + enum ValueType { + Undefined_Type = Immediate_Mask | 0x00000, + Null_Type = Immediate_Mask | 0x10000, + Boolean_Type = Immediate_Mask | 0x20000, + Integer_Type = Immediate_Mask | 0x30000, + Managed_Type = NotDouble_Mask | 0x00000, + Empty_Type = NotDouble_Mask | 0x30000 + }; + + enum ImmediateFlags { + ConvertibleToInt = Immediate_Mask | 0x1 + }; + + enum ValueTypeInternal { + _Null_Type = Null_Type | ConvertibleToInt, + _Boolean_Type = Boolean_Type | ConvertibleToInt, + _Integer_Type = Integer_Type | ConvertibleToInt, + + }; +#else + static const quint64 NaNEncodeMask = 0xffff800000000000ll; + static const quint64 IsInt32Mask = 0x0002000000000000ll; + static const quint64 IsDoubleMask = 0xfffc000000000000ll; + static const quint64 IsNumberMask = IsInt32Mask|IsDoubleMask; + static const quint64 IsNullOrUndefinedMask = 0x0000800000000000ll; + static const quint64 IsNullOrBooleanMask = 0x0001000000000000ll; + static const quint64 IsConvertibleToIntMask = IsInt32Mask|IsNullOrBooleanMask; + + enum Masks { + NaN_Mask = 0x7ff80000, + Type_Mask = 0xffff8000, + IsDouble_Mask = 0xfffc0000, + Immediate_Mask = 0x00018000, + IsNullOrUndefined_Mask = 0x00008000, + IsNullOrBoolean_Mask = 0x00010000, + Tag_Shift = 32 + }; + enum ValueType { + Undefined_Type = IsNullOrUndefined_Mask, + Null_Type = IsNullOrUndefined_Mask|IsNullOrBoolean_Mask, + Boolean_Type = IsNullOrBoolean_Mask, + Integer_Type = 0x20000|IsNullOrBoolean_Mask, + Managed_Type = 0, + Empty_Type = Undefined_Type | 0x4000 + }; + enum { + IsDouble_Shift = 64-14, + IsNumber_Shift = 64-15, + IsConvertibleToInt_Shift = 64-16, + IsManaged_Shift = 64-17 + }; + + + enum ValueTypeInternal { + _Null_Type = Null_Type, + _Boolean_Type = Boolean_Type, + _Integer_Type = Integer_Type + }; #endif - return v; -} - -inline Primitive Primitive::fromBoolean(bool b) -{ - Primitive v; - v.tag = _Boolean_Type; - v.int_32 = (bool)b; - return v; -} -inline Primitive Primitive::fromDouble(double d) -{ - Primitive v; - v.setDouble(d); - return v; -} + inline unsigned type() const { + return tag & Type_Mask; + } -inline Primitive Primitive::fromInt32(int i) -{ - Primitive v; - v.tag = _Integer_Type; - v.int_32 = i; - return v; -} + // used internally in property + inline bool isEmpty() const { return tag == Empty_Type; } -inline Primitive Primitive::fromUInt32(uint i) -{ - Primitive v; - if (i < INT_MAX) { - v.tag = _Integer_Type; - v.int_32 = (int)i; - } else { - v.setDouble(i); + inline bool isUndefined() const { return tag == Undefined_Type; } + inline bool isNull() const { return tag == _Null_Type; } + inline bool isBoolean() const { return tag == _Boolean_Type; } +#if QT_POINTER_SIZE == 8 + inline bool isInteger() const { return (val >> IsNumber_Shift) == 1; } + inline bool isDouble() const { return (val >> IsDouble_Shift); } + inline bool isNumber() const { return (val >> IsNumber_Shift); } + inline bool isManaged() const { return !(val >> IsManaged_Shift); } + inline bool isNullOrUndefined() const { return ((val >> IsManaged_Shift) & ~2) == 1; } + inline bool integerCompatible() const { return ((val >> IsConvertibleToInt_Shift) & ~2) == 1; } + static inline bool integerCompatible(Value a, Value b) { + return a.integerCompatible() && b.integerCompatible(); } - return v; -} - -inline double Value::toNumber() const -{ - if (integerCompatible()) - return int_32; - if (isDouble()) + static inline bool bothDouble(Value a, Value b) { + return a.isDouble() && b.isDouble(); + } + double doubleValue() const { + Q_ASSERT(isDouble()); + union { + quint64 i; + double d; + } v; + v.i = val ^ NaNEncodeMask; + return v.d; + } + void setDouble(double d) { + union { + quint64 i; + double d; + } v; + v.d = d; + val = v.i ^ NaNEncodeMask; + Q_ASSERT(isDouble()); + } + bool isNaN() const { return (tag & 0x7fff8000) == 0x00078000; } +#else + inline bool isInteger() const { return tag == _Integer_Type; } + inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } + inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } + inline bool isManaged() const { return tag == Managed_Type; } + inline bool isNullOrUndefined() const { return (tag & IsNullOrUndefined_Mask) == Undefined_Type; } + inline bool integerCompatible() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } + static inline bool integerCompatible(Value a, Value b) { + return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; + } + static inline bool bothDouble(Value a, Value b) { + return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; + } + double doubleValue() const { return dbl; } + void setDouble(double d) { dbl = d; } + bool isNaN() const { return (tag & QV4::Value::NotDouble_Mask) == QV4::Value::NaN_Mask; } +#endif + inline bool isString() const; + inline bool isObject() const; + inline bool isInt32() { + if (tag == _Integer_Type) + return true; + if (isDouble()) { + double d = doubleValue(); + int i = (int)d; + if (i == d) { + int_32 = i; + tag = _Integer_Type; + return true; + } + } + return false; + } + double asDouble() const { + if (tag == _Integer_Type) + return int_32; return doubleValue(); - return toNumberImpl(); -} + } -inline int Value::toInt32() const -{ - if (integerCompatible()) + bool booleanValue() const { + return int_32; + } + int integerValue() const { return int_32; - double d; - if (isDouble()) - d = doubleValue(); - else - d = toNumberImpl(); + } - const double D32 = 4294967296.0; - const double D31 = D32 / 2.0; + String *stringValue() const { + return s; + } + Object *objectValue() const { + return o; + } + Managed *managed() const { + return m; + } - if ((d >= -D31 && d < D31)) - return static_cast<int>(d); + quint64 rawValue() const { + return val; + } - return Primitive::toInt32(d); -} + static inline Value fromManaged(Managed *o); + + int toUInt16() const; + inline int toInt32() const; + inline unsigned int toUInt32() const; + + inline bool toBoolean() const; + double toInteger() const; + inline double toNumber() const; + double toNumberImpl() const; + QString toQStringNoThrow() const; + QString toQString() const; + String *toString(ExecutionEngine *e) const; + String *toString(ExecutionContext *ctx) const; + Object *toObject(ExecutionContext *ctx) const; + + inline bool isPrimitive() const; + inline bool tryIntegerConversion() { + bool b = integerCompatible(); + if (b) + tag = _Integer_Type; + return b; + } -inline unsigned int Value::toUInt32() const -{ - return (unsigned int)toInt32(); -} + inline String *asString() const; + inline Managed *asManaged() const; + inline Object *asObject() const; + inline FunctionObject *asFunctionObject() const; + inline NumberObject *asNumberObject() const; + inline StringObject *asStringObject() const; + inline DateObject *asDateObject() const; + inline ArrayObject *asArrayObject() const; + inline ErrorObject *asErrorObject() const; + template<typename T> inline T *as() const; -inline bool Value::toBoolean() const -{ - switch (type()) { - case Value::Undefined_Type: - case Value::Null_Type: - return false; - case Value::Boolean_Type: - case Value::Integer_Type: - return (bool)int_32; - case Value::Managed_Type: - if (isString()) - return stringValue()->toQString().length() > 0; - return true; - default: // double - return doubleValue() && !std::isnan(doubleValue()); - } -} + inline uint asArrayIndex() const; + inline uint asArrayLength(bool *ok) const; -inline uint Value::asArrayIndex() const -{ -#if QT_POINTER_SIZE == 8 - if (!isNumber()) - return UINT_MAX; - if (isInteger()) - return int_32 >= 0 ? (uint)int_32 : UINT_MAX; -#else - if (isInteger() && int_32 >= 0) - return (uint)int_32; - if (!isDouble()) - return UINT_MAX; -#endif - double d = doubleValue(); - uint idx = (uint)d; - if (idx != d) - return UINT_MAX; - return idx; -} + inline ExecutionEngine *engine() const; -inline uint Value::asArrayLength(bool *ok) const -{ - *ok = true; - if (integerCompatible() && int_32 >= 0) - return (uint)int_32; - if (isDouble()) { - double d = doubleValue(); - uint idx = (uint)d; - if (idx != d) { - *ok = false; - return UINT_MAX; - } - return idx; + ReturnedValue asReturnedValue() const { return val; } + static Value fromReturnedValue(ReturnedValue val) { Value v; v.val = val; return v; } + + // Section 9.12 + bool sameValue(Value other) const; + + inline void mark(ExecutionEngine *e) const; + + Value &operator =(const ScopedValue &v); + Value &operator=(ReturnedValue v) { val = v; return *this; } + template<typename T> + Value &operator=(Returned<T> *t); + template<typename T> + Value &operator=(T *t) { + val = Value::fromManaged(t).val; + return *this; } - if (isString()) - return stringValue()->toUInt(ok); - uint idx = toUInt32(); - double d = toNumber(); - if (d != idx) { - *ok = false; - return UINT_MAX; + template<typename T> + Value &operator=(const Scoped<T> &t); + Value &operator=(const ValueRef v); + Value &operator=(const Value &v) { + val = v.val; + return *this; } - return idx; -} + template<typename T> + inline Returned<T> *as(); +}; -inline Object *Value::asObject() const +inline Managed *Value::asManaged() const { - return isObject() ? objectValue() : 0; + if (isManaged()) + return managed(); + return 0; } -inline FunctionObject *Value::asFunctionObject() const +inline String *Value::asString() const { - return isObject() ? managed()->asFunctionObject() : 0; + if (isString()) + return stringValue(); + return 0; } -inline NumberObject *Value::asNumberObject() const +struct Q_QML_EXPORT Primitive : public Value { - return isObject() ? managed()->asNumberObject() : 0; -} + inline static Primitive emptyValue(); + static inline Primitive fromBoolean(bool b); + static inline Primitive fromInt32(int i); + inline static Primitive undefinedValue(); + static inline Primitive nullValue(); + static inline Primitive fromDouble(double d); + static inline Primitive fromUInt32(uint i); + + static double toInteger(double fromNumber); + static int toInt32(double value); + static unsigned int toUInt32(double value); + + inline operator ValueRef(); + Value asValue() const { return *this; } +}; -inline StringObject *Value::asStringObject() const +inline Primitive Primitive::undefinedValue() { - return isObject() ? managed()->asStringObject() : 0; + Primitive v; +#if QT_POINTER_SIZE == 8 + v.val = quint64(Undefined_Type) << Tag_Shift; +#else + v.tag = Undefined_Type; + v.int_32 = 0; +#endif + return v; } -inline DateObject *Value::asDateObject() const +inline Primitive Primitive::emptyValue() { - return isObject() ? managed()->asDateObject() : 0; + Primitive v; + v.tag = Value::Empty_Type; + v.uint_32 = 0; + return v; } -inline ArrayObject *Value::asArrayObject() const +inline Value Value::fromManaged(Managed *m) { - return isObject() ? managed()->asArrayObject() : 0; + if (!m) + return QV4::Primitive::undefinedValue(); + Value v; +#if QT_POINTER_SIZE == 8 + v.m = m; +#else + v.tag = Managed_Type; + v.m = m; +#endif + return v; } -inline ErrorObject *Value::asErrorObject() const +template <typename T> +struct TypedValue : public Value { - return isObject() ? managed()->asErrorObject() : 0; -} + template<typename X> + TypedValue &operator =(X *x) { + val = Value::fromManaged(x).val; + } + TypedValue &operator =(T *t); + TypedValue &operator =(const Scoped<T> &v); +// TypedValue &operator =(const ManagedRef<T> &v); + TypedValue &operator =(Returned<T> *t); -template<typename T> -inline T *Value::as() const { Managed *m = isObject() ? managed() : 0; return m ? m->as<T>() : 0; } + TypedValue &operator =(const TypedValue<T> &t); -struct Q_QML_PRIVATE_EXPORT PersistentValuePrivate -{ - PersistentValuePrivate(ReturnedValue v, ExecutionEngine *engine = 0, bool weak = false); - virtual ~PersistentValuePrivate(); - SafeValue value; - uint refcount; - bool weak; - QV4::ExecutionEngine *engine; - PersistentValuePrivate **prev; - PersistentValuePrivate *next; - - void init(); - void removeFromList(); - void ref() { ++refcount; } - void deref(); - PersistentValuePrivate *detach(const ReturnedValue value, bool weak = false); - - bool checkEngine(QV4::ExecutionEngine *otherEngine) { - if (!engine) { - Q_ASSERT(!value.isObject()); - engine = otherEngine; - } - return (engine == otherEngine); - } -}; + bool operator!() const { return !managed(); } -class Q_QML_EXPORT PersistentValue -{ -public: - PersistentValue() : d(0) {} - PersistentValue(const PersistentValue &other); - PersistentValue &operator=(const PersistentValue &other); + T *operator->() { return static_cast<T *>(managed()); } + const T *operator->() const { return static_cast<T *>(managed()); } + T *getPointer() const { return static_cast<T *>(managed()); } + Returned<T> *ret() const; + + void mark(ExecutionEngine *e) { if (managed()) managed()->mark(e); } +}; +typedef TypedValue<String> StringValue; - PersistentValue(const ValueRef val); - PersistentValue(ReturnedValue val); - template<typename T> - PersistentValue(Returned<T> *obj); - template<typename T> - PersistentValue(const Referenced<T> obj); - PersistentValue &operator=(const ValueRef other); - PersistentValue &operator =(ReturnedValue other); - template<typename T> - PersistentValue &operator=(Returned<T> *obj); - template<typename T> - PersistentValue &operator=(const Referenced<T> obj); - ~PersistentValue(); - ReturnedValue value() const { - return (d ? d->value.asReturnedValue() : Primitive::undefinedValue().asReturnedValue()); +struct Encode { + static ReturnedValue undefined() { + return quint64(Value::Undefined_Type) << Value::Tag_Shift; + } + static ReturnedValue null() { + return quint64(Value::_Null_Type) << Value::Tag_Shift; } - ExecutionEngine *engine() { - if (!d) - return 0; - if (d->engine) - return d->engine; - Managed *m = d->value.asManaged(); - return m ? m->engine() : 0; + Encode(bool b) { + val = (quint64(Value::_Boolean_Type) << Value::Tag_Shift) | (uint)b; + } + Encode(double d) { + Value v; + v.setDouble(d); + val = v.val; + } + Encode(int i) { + val = (quint64(Value::_Integer_Type) << Value::Tag_Shift) | (uint)i; + } + Encode(uint i) { + if (i <= INT_MAX) { + val = (quint64(Value::_Integer_Type) << Value::Tag_Shift) | i; + } else { + Value v; + v.setDouble(i); + val = v.val; + } + } + Encode(ReturnedValue v) { + val = v; } - bool isUndefined() const { return !d || d->value.isUndefined(); } - bool isNullOrUndefined() const { return !d || d->value.isNullOrUndefined(); } - void clear() { - *this = PersistentValue(); + template<typename T> + Encode(Returned<T> *t) { + val = t->getPointer()->asReturnedValue(); } + operator ReturnedValue() const { + return val; + } + quint64 val; private: - friend struct ValueRef; - PersistentValuePrivate *d; + Encode(void *); }; -class Q_QML_EXPORT WeakValue -{ -public: - WeakValue() : d(0) {} - WeakValue(const ValueRef val); - WeakValue(const WeakValue &other); - WeakValue(ReturnedValue val); - template<typename T> - WeakValue(Returned<T> *obj); - WeakValue &operator=(const WeakValue &other); - WeakValue &operator=(const ValueRef other); - WeakValue &operator =(const ReturnedValue &other); - template<typename T> - WeakValue &operator=(Returned<T> *obj); - - ~WeakValue(); +struct ValueRef { + ValueRef(const ScopedValue &v); + template <typename T> + ValueRef(const Scoped<T> &v); + ValueRef(const PersistentValue &v); + ValueRef(PersistentValuePrivate *p); + ValueRef(Value &v) { ptr = &v; } + // Important: Do NOT add a copy constructor to this class + // adding a copy constructor actually changes the calling convention, ie. + // is not even binary compatible. Adding it would break assumptions made + // in the jit'ed code. + ValueRef &operator=(const ScopedValue &o); + ValueRef &operator=(const Value &v) + { *ptr = v; return *this; } + ValueRef &operator=(const ReturnedValue &v) { + ptr->val = v; + return *this; + } + template <typename T> + ValueRef &operator=(Returned<T> *v) { + ptr->val = v->asReturnedValue(); + return *this; + } - ReturnedValue value() const { - return (d ? d->value.asReturnedValue() : Primitive::undefinedValue().asReturnedValue()); + operator const Value *() const { + return ptr; + } + const Value *operator->() const { + return ptr; } - ExecutionEngine *engine() { - if (!d) - return 0; - if (d->engine) - return d->engine; - Managed *m = d->value.asManaged(); - return m ? m->engine() : 0; + operator Value *() { + return ptr; + } + Value *operator->() { + return ptr; } - bool isUndefined() const { return !d || d->value.isUndefined(); } - bool isNullOrUndefined() const { return !d || d->value.isNullOrUndefined(); } - void clear() { - *this = WeakValue(); + static ValueRef fromRawValue(Value *v) { + return ValueRef(v); + } + static const ValueRef fromRawValue(const Value *v) { + return ValueRef(const_cast<Value *>(v)); } - void markOnce(ExecutionEngine *e); + ReturnedValue asReturnedValue() const { return ptr->val; } + // ### get rid of this one! + ValueRef(Value *v) { ptr = reinterpret_cast<Value *>(v); } private: - friend struct ValueRef; - PersistentValuePrivate *d; + Value *ptr; }; -} // namespace QV4 + +template<typename T> +T *value_cast(const Value &v) +{ + return v.as<T>(); +} + +template<typename T> +ReturnedValue value_convert(ExecutionEngine *e, const Value &v); + + + +} QT_END_NAMESPACE -#endif +#endif // QV4VALUE_DEF_P_H diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index 5bfd7786e9..92cc19d8b9 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -77,7 +77,7 @@ QVariant VariantObject::toVariant(const QV4::ValueRef v) if (v->isBoolean()) return QVariant(v->booleanValue()); if (v->isNumber()) { - QV4::SafeValue val; + QV4::Value val; val = v; if (val.isInt32()) return QVariant(val.integerValue()); diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h index 3715dbc8c4..656608d49b 100644 --- a/src/qml/jsruntime/qv4variantobject_p.h +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -57,7 +57,7 @@ #include <QtQml/qqmllist.h> #include <QtCore/qvariant.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4object_p.h> QT_BEGIN_NAMESPACE @@ -82,6 +82,8 @@ public: static bool isEqualTo(Managed *m, Managed *other); }; +DEFINE_REF(VariantObject, Object); + struct VariantPrototype : VariantObject { public: diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 9d6540ebe9..df8e3632fb 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -41,7 +41,7 @@ #include "qv4vme_moth_p.h" #include "qv4instr_moth_p.h" -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4debugging_p.h> #include <private/qv4math_p.h> #include <private/qv4scopedvalue_p.h> @@ -64,8 +64,6 @@ using namespace QQmlJS::Moth; #define MOTH_BEGIN_INSTR_COMMON(I) { \ const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ code += InstrMeta<(int)Instr::I>::Size; \ - if (debugger && (instr.breakPoint || debugger->pauseAtNextOpportunity())) \ - debugger->maybeBreakAtInstruction(code, instr.breakPoint); \ Q_UNUSED(instr); \ TRACE_INSTR(I) @@ -74,11 +72,6 @@ using namespace QQmlJS::Moth; # define MOTH_BEGIN_INSTR(I) op_##I: \ MOTH_BEGIN_INSTR_COMMON(I) -# define MOTH_NEXT_INSTR(I) { \ - genericInstr = reinterpret_cast<const Instr *>(code); \ - goto *genericInstr->common.code; \ - } - # define MOTH_END_INSTR(I) } \ genericInstr = reinterpret_cast<const Instr *>(code); \ goto *genericInstr->common.code; \ @@ -89,10 +82,6 @@ using namespace QQmlJS::Moth; case Instr::I: \ MOTH_BEGIN_INSTR_COMMON(I) -# define MOTH_NEXT_INSTR(I) { \ - continue; \ - } - # define MOTH_END_INSTR(I) } \ continue; @@ -161,16 +150,15 @@ Param traceParam(const Param ¶m) #define STOREVALUE(param, value) { \ QV4::ReturnedValue tmp = (value); \ - if (context->engine->hasException) \ + if (engine->hasException) \ goto catchException; \ VALUE(param) = tmp; \ } #define CHECK_EXCEPTION \ - if (context->engine->hasException) \ + if (engine->hasException) \ goto catchException -QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, - QV4::SafeValue *stack, unsigned stackSize +QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code #ifdef MOTH_THREADED_INTERPRETER , void ***storeJumpTable #endif @@ -192,15 +180,18 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, } #endif + QV4::Value *stack = 0; + unsigned stackSize = 0; + const uchar *exceptionHandler = 0; - QV4::Debugging::Debugger *debugger = context->engine->debugger; + QV4::ExecutionEngine *engine = context->engine; #ifdef DO_TRACE_INSTR qDebug("Starting VME with context=%p and code=%p", context, code); #endif // DO_TRACE_INSTR - QV4::SafeString * const runtimeStrings = context->compilationUnit->runtimeStrings; + QV4::StringValue * const runtimeStrings = context->compilationUnit->runtimeStrings; context->interpreterInstructionPointer = &code; // setup lookup scopes @@ -213,9 +204,9 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, } } - QV4::SafeValue **scopes = static_cast<QV4::SafeValue **>(alloca(sizeof(QV4::SafeValue *)*(2 + 2*scopeDepth))); + QV4::Value **scopes = static_cast<QV4::Value **>(alloca(sizeof(QV4::Value *)*(2 + 2*scopeDepth))); { - scopes[0] = const_cast<QV4::SafeValue *>(context->compilationUnit->data->constants()); + scopes[0] = const_cast<QV4::Value *>(context->compilationUnit->data->constants()); // stack gets setup in push instruction scopes[1] = 0; QV4::ExecutionContext *scope = context; @@ -247,6 +238,10 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, VALUE(instr.result) = VALUE(instr.source); MOTH_END_INSTR(Move) + MOTH_BEGIN_INSTR(MoveConst) + VALUE(instr.result) = instr.source; + MOTH_END_INSTR(MoveConst) + MOTH_BEGIN_INSTR(SwapTemps) qSwap(VALUE(instr.left), VALUE(instr.right)); MOTH_END_INSTR(MoveTemp) @@ -286,11 +281,22 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, STOREVALUE(instr.result, __qmljs_get_element(context, VALUEPTR(instr.base), VALUEPTR(instr.index))); MOTH_END_INSTR(LoadElement) + MOTH_BEGIN_INSTR(LoadElementLookup) + QV4::Lookup *l = context->lookups + instr.lookup; + STOREVALUE(instr.result, l->indexedGetter(l, VALUEPTR(instr.base), VALUEPTR(instr.index))); + MOTH_END_INSTR(LoadElementLookup) + MOTH_BEGIN_INSTR(StoreElement) __qmljs_set_element(context, VALUEPTR(instr.base), VALUEPTR(instr.index), VALUEPTR(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElement) + MOTH_BEGIN_INSTR(StoreElementLookup) + QV4::Lookup *l = context->lookups + instr.lookup; + l->indexedSetter(l, VALUEPTR(instr.base), VALUEPTR(instr.index), VALUEPTR(instr.source)); + CHECK_EXCEPTION; + MOTH_END_INSTR(StoreElementLookup) + MOTH_BEGIN_INSTR(LoadProperty) STOREVALUE(instr.result, __qmljs_get_property(context, VALUEPTR(instr.base), runtimeStrings[instr.name])); MOTH_END_INSTR(LoadProperty) @@ -328,7 +334,9 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, TRACE(inline, "stack size: %u", instr.value); stackSize = instr.value; stack = context->engine->stackPush(stackSize); - memset(stack, 0, stackSize * sizeof(QV4::SafeValue)); +#ifndef QT_NO_DEBUG + memset(stack, 0, stackSize * sizeof(QV4::Value)); +#endif scopes[1] = stack; MOTH_END_INSTR(Push) @@ -343,7 +351,7 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, } } #endif // DO_TRACE_INSTR - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; @@ -353,7 +361,7 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, MOTH_BEGIN_INSTR(CallProperty) TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; @@ -363,7 +371,7 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, MOTH_BEGIN_INSTR(CallPropertyLookup) TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; @@ -372,7 +380,7 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallElement) - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; @@ -382,7 +390,7 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, MOTH_BEGIN_INSTR(CallActivationProperty) TRACE(args, "starting at %d, length %d", instr.args, instr.argc); - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; @@ -392,7 +400,7 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, MOTH_BEGIN_INSTR(CallGlobalLookup) TRACE(args, "starting at %d, length %d", instr.args, instr.argc); - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; @@ -475,12 +483,12 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, MOTH_BEGIN_INSTR(CallBuiltinDefineArray) Q_ASSERT(instr.args + instr.argc <= stackSize); - QV4::SafeValue *args = stack + instr.args; + QV4::Value *args = stack + instr.args; STOREVALUE(instr.result, __qmljs_builtin_define_array(context, args, instr.argc)); MOTH_END_INSTR(CallBuiltinDefineArray) MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral) - QV4::SafeValue *args = stack + instr.args; + QV4::Value *args = stack + instr.args; STOREVALUE(instr.result, __qmljs_builtin_define_object_literal(context, args, instr.internalClassId)); MOTH_END_INSTR(CallBuiltinDefineObjectLiteral) @@ -494,7 +502,7 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, MOTH_END_INSTR(CallBuiltinConvertThisToObject) MOTH_BEGIN_INSTR(CreateValue) - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; @@ -503,7 +511,7 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; @@ -512,7 +520,7 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(ConstructPropertyLookup) - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; @@ -522,7 +530,7 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, MOTH_BEGIN_INSTR(CreateActivationProperty) TRACE(inline, "property name = %s, args = %d, argc = %d", runtimeStrings[instr.name]->toQString().toUtf8().constData(), instr.args, instr.argc); - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; @@ -532,7 +540,7 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, MOTH_BEGIN_INSTR(ConstructGlobalLookup) TRACE(inline, "property name = %s, args = %d, argc = %d", runtimeStrings[instr.name]->toQString().toUtf8().constData(), instr.args, instr.argc); - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; @@ -544,14 +552,19 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, code = ((uchar *)&instr.offset) + instr.offset; MOTH_END_INSTR(Jump) - MOTH_BEGIN_INSTR(CJump) - uint cond = __qmljs_to_boolean(VALUEPTR(instr.condition)); + MOTH_BEGIN_INSTR(JumpEq) + bool cond = VALUEPTR(instr.condition)->toBoolean(); TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); - if (instr.invert) - cond = !cond; if (cond) code = ((uchar *)&instr.offset) + instr.offset; - MOTH_END_INSTR(CJump) + MOTH_END_INSTR(JumpEq) + + MOTH_BEGIN_INSTR(JumpNe) + bool cond = VALUEPTR(instr.condition)->toBoolean(); + TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); + if (!cond) + code = ((uchar *)&instr.offset) + instr.offset; + MOTH_END_INSTR(JumpNe) MOTH_BEGIN_INSTR(UNot) STOREVALUE(instr.result, __qmljs_not(VALUEPTR(instr.source))); @@ -606,6 +619,14 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, STOREVALUE(instr.result, __qmljs_bit_xor(VALUEPTR(instr.lhs), VALUEPTR(instr.rhs))); MOTH_END_INSTR(BitXor) + MOTH_BEGIN_INSTR(Shr) + STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() >> (VALUEPTR(instr.rhs)->toInt32() & 0x1f)))); + MOTH_END_INSTR(Shr) + + MOTH_BEGIN_INSTR(Shl) + STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() << (VALUEPTR(instr.rhs)->toInt32() & 0x1f)))); + MOTH_END_INSTR(Shl) + MOTH_BEGIN_INSTR(BitAndConst) int lhs = VALUEPTR(instr.lhs)->toInt32(); STOREVALUE(instr.result, QV4::Encode((int)(lhs & instr.rhs))); @@ -621,6 +642,14 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, STOREVALUE(instr.result, QV4::Encode((int)(lhs ^ instr.rhs))); MOTH_END_INSTR(BitXor) + MOTH_BEGIN_INSTR(ShrConst) + STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() >> instr.rhs))); + MOTH_END_INSTR(ShrConst) + + MOTH_BEGIN_INSTR(ShlConst) + STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() << instr.rhs))); + MOTH_END_INSTR(ShlConst) + MOTH_BEGIN_INSTR(Mul) STOREVALUE(instr.result, __qmljs_mul(VALUEPTR(instr.lhs), VALUEPTR(instr.rhs))); MOTH_END_INSTR(Mul) @@ -633,30 +662,18 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, STOREVALUE(instr.result, instr.alu(context, VALUEPTR(instr.lhs), VALUEPTR(instr.rhs))); MOTH_END_INSTR(BinopContext) - MOTH_BEGIN_INSTR(AddNumberParams) - double lhs = VALUE(instr.lhs).asDouble(); - double rhs = VALUE(instr.rhs).asDouble(); - VALUEPTR(instr.result)->setDouble(lhs + rhs); - MOTH_END_INSTR(AddNumberParams) - - MOTH_BEGIN_INSTR(MulNumberParams) - double lhs = VALUE(instr.lhs).asDouble(); - double rhs = VALUE(instr.rhs).asDouble(); - VALUEPTR(instr.result)->setDouble(lhs * rhs); - MOTH_END_INSTR(MulNumberParams) - - MOTH_BEGIN_INSTR(SubNumberParams) - double lhs = VALUE(instr.lhs).asDouble(); - double rhs = VALUE(instr.rhs).asDouble(); - VALUEPTR(instr.result)->setDouble(lhs - rhs); - MOTH_END_INSTR(SubNumberParams) - MOTH_BEGIN_INSTR(Ret) context->engine->stackPop(stackSize); // TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); return VALUE(instr.result).asReturnedValue(); MOTH_END_INSTR(Ret) + MOTH_BEGIN_INSTR(Debug) + QV4::Debugging::Debugger *debugger = context->engine->debugger; + if (debugger && (instr.breakPoint || debugger->pauseAtNextOpportunity())) + debugger->maybeBreakAtInstruction(code, instr.breakPoint); + MOTH_END_INSTR(Debug) + MOTH_BEGIN_INSTR(LoadThis) VALUE(instr.result) = context->callData->thisObject; MOTH_END_INSTR(LoadThis) @@ -709,7 +726,7 @@ void **VME::instructionJumpTable() static void **jumpTable = 0; if (!jumpTable) { const uchar *code = 0; - VME().run(0, code, 0, 0, &jumpTable); + VME().run(0, code, &jumpTable); } return jumpTable; } diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index 974dfdd615..8d0822f16f 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -60,8 +60,7 @@ public: #endif private: - QV4::ReturnedValue run(QV4::ExecutionContext *, const uchar *code, - QV4::SafeValue *stack = 0, unsigned stackSize = 0 + QV4::ReturnedValue run(QV4::ExecutionContext *, const uchar *code #ifdef MOTH_THREADED_INTERPRETER , void ***storeJumpTable = 0 #endif diff --git a/src/qml/qml/ftw/qfinitestack_p.h b/src/qml/qml/ftw/qfinitestack_p.h index 31a530fa64..6bfd353771 100644 --- a/src/qml/qml/ftw/qfinitestack_p.h +++ b/src/qml/qml/ftw/qfinitestack_p.h @@ -114,6 +114,7 @@ T &QFiniteStack<T>::top() template<typename T> void QFiniteStack<T>::push(const T &o) { + Q_ASSERT(_size < _alloc); if (QTypeInfo<T>::isComplex) { new (_array + _size++) T(o); } else { @@ -124,6 +125,7 @@ void QFiniteStack<T>::push(const T &o) template<typename T> T QFiniteStack<T>::pop() { + Q_ASSERT(_size > 0); --_size; if (QTypeInfo<T>::isComplex) { diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index bc56fe1f2d..d0476a92d4 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -54,7 +54,7 @@ #include <private/qv4debugservice_p.h> #include "qqmlinfo.h" -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <QtCore/qstringbuilder.h> #include <QtCore/qdebug.h> diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp index 05ec0ab9c7..75740e17e6 100644 --- a/src/qml/qml/qqmlcompileddata.cpp +++ b/src/qml/qml/qqmlcompileddata.cpp @@ -88,7 +88,7 @@ int QQmlCompiledData::indexForUrl(const QUrl &data) QQmlCompiledData::QQmlCompiledData(QQmlEngine *engine) : engine(engine), importCache(0), metaTypeId(-1), listMetaTypeId(-1), isRegisteredWithEngine(false), - rootPropertyCache(0), compilationUnit(0), qmlUnit(0) + rootPropertyCache(0), compilationUnit(0), qmlUnit(0), totalBindingsCount(0), totalParserStatusCount(0) { Q_ASSERT(engine); diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp index e36f3fd967..9262e2a119 100644 --- a/src/qml/qml/qqmlcompiler.cpp +++ b/src/qml/qml/qqmlcompiler.cpp @@ -1925,7 +1925,7 @@ bool QQmlCompiler::buildPropertyInNamespace(QQmlImportNamespace *ns, for (Property *prop = nsProp->value->properties.first(); prop; prop = nsProp->value->properties.next(prop)) { if (!isAttachedPropertyName(prop->name())) - COMPILE_EXCEPTION(prop, tr("Not an attached property name")); + COMPILE_EXCEPTION(prop, tr("Expected type name")); // Setup attached property data @@ -2377,7 +2377,7 @@ bool QQmlCompiler::buildScriptStringProperty(QQmlScript::Property *prop, const BindingContext &ctxt) { if (prop->values.isMany()) - COMPILE_EXCEPTION(prop->values.first()->nextValue, tr( "Cannot assign multiple values to a script property")); + COMPILE_EXCEPTION(prop->values.first(), tr( "Cannot assign multiple values to a script property")); if (prop->values.first()->object) COMPILE_EXCEPTION(prop->values.first(), tr( "Invalid property assignment: script expected")); @@ -3456,7 +3456,7 @@ bool QQmlCompiler::buildBinding(QQmlScript::Value *value, Q_ASSERT(prop->parent->metatype); if (!prop->core.isWritable() && !prop->core.isQList() && !prop->isReadOnlyDeclaration) - COMPILE_EXCEPTION(prop, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); + COMPILE_EXCEPTION(value, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); JSBindingReference *reference = pool->New<JSBindingReference>(); reference->expression = value->value; diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h index c63f430b0a..2ce850da5d 100644 --- a/src/qml/qml/qqmlcompiler_p.h +++ b/src/qml/qml/qqmlcompiler_p.h @@ -85,6 +85,7 @@ class QQmlComponent; class QQmlContext; class QQmlContextData; +// ### Merge with QV4::CompiledData::CompilationUnit class Q_AUTOTEST_EXPORT QQmlCompiledData : public QQmlRefCount, public QQmlCleanup { public: @@ -161,6 +162,8 @@ public: // hash key is object index QHash<int, QByteArray> customParserData; QVector<int> customParserBindings; // index is binding identifier, value is compiled function index. + int totalBindingsCount; // Number of bindings used in this type + int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } bool isCompositeType() const { return !datas.at(qmlUnit->indexOfRootObject).isEmpty(); } diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index a36150ee42..6abf805a4a 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -862,7 +862,7 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) enginePriv->referenceScarceResources(); QObject *rv = 0; if (enginePriv->useNewCompiler) { - state.creator = new QmlObjectCreator(context, cc); + state.creator = new QmlObjectCreator(context, cc, creationContext); rv = state.creator->create(start); if (!rv) state.errors = state.creator->errors; @@ -914,7 +914,8 @@ void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionS { if (state->completePending) { if (enginePriv->useNewCompiler) { - state->creator->finalize(); + QQmlInstantiationInterrupt interrupt; + state->creator->finalize(interrupt); } else { state->vme.complete(); } @@ -991,7 +992,7 @@ QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj) if (p->activeVME) { // XXX should only be allowed during begin a->add(&p->activeVME->componentAttached); } else if (p->activeObjectCreator) { - a->add(&p->activeObjectCreator->componentAttached); + a->add(p->activeObjectCreator->componentAttached); } else { QQmlData *d = QQmlData::get(obj); Q_ASSERT(d); @@ -1057,7 +1058,7 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, p->compiledData = d->cc; p->compiledData->addref(); if (enginePriv->useNewCompiler) { - p->creator.reset(new QmlObjectCreator(contextData, d->cc)); + p->creator.reset(new QmlObjectCreator(contextData, d->cc, d->creationContext)); p->subComponentToCreate = d->start; } else p->vme.init(contextData, d->cc, d->start, d->creationContext); @@ -1085,9 +1086,9 @@ public: QScopedPointer<QQmlComponentIncubator> incubator; QV8Engine *v8; QPointer<QObject> parent; - QV4::SafeValue valuemap; - QV4::SafeValue qmlGlobal; - QV4::SafeValue m_statusChanged; + QV4::Value valuemap; + QV4::Value qmlGlobal; + QV4::Value m_statusChanged; void statusChanged(QQmlIncubator::Status); void setInitialState(QObject *); diff --git a/src/qml/qml/qqmlcontextwrapper.cpp b/src/qml/qml/qqmlcontextwrapper.cpp index d3ef02a9ad..1ca9c6ac12 100644 --- a/src/qml/qml/qqmlcontextwrapper.cpp +++ b/src/qml/qml/qqmlcontextwrapper.cpp @@ -46,7 +46,7 @@ #include <private/qqmlcontext_p.h> #include <private/qv4engine_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4objectproto_p.h> #include <private/qv4mm_p.h> #include <private/qv4function_p.h> diff --git a/src/qml/qml/qqmlcontextwrapper_p.h b/src/qml/qml/qqmlcontextwrapper_p.h index bd56d4bcec..0ecec93652 100644 --- a/src/qml/qml/qqmlcontextwrapper_p.h +++ b/src/qml/qml/qqmlcontextwrapper_p.h @@ -56,7 +56,7 @@ #include <QtCore/qglobal.h> #include <private/qtqmlglobal_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4object_p.h> #include <private/qqmlcontext_p.h> #include <private/qv4functionobject_p.h> diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index c4b19394f0..93060e97fb 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -56,7 +56,8 @@ #include <private/qtqmlglobal_p.h> #include <private/qobject_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> +#include <private/qv4persistent_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 965741241a..0529e41007 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -62,6 +62,7 @@ #include "qqmllist_p.h" #include "qqmltypenamecache_p.h" #include "qqmlnotifier_p.h" +#include <private/qqmldebugserver_p.h> #include <private/qqmlprofilerservice_p.h> #include <private/qv4debugservice_p.h> #include <private/qdebugmessageservice_p.h> @@ -813,11 +814,12 @@ void QQmlEnginePrivate::init() if (QCoreApplication::instance()->thread() == q->thread() && QQmlEngineDebugService::isDebuggingEnabled()) { isDebugging = true; - QQmlEngineDebugService::instance()->addEngine(q); - QV4DebugService::instance()->addEngine(q); - QV4ProfilerService::initialize(); - QQmlProfilerService::initialize(); + QQmlEngineDebugService::instance(); + QV4DebugService::instance(); + QV4ProfilerService::instance(); + QQmlProfilerService::instance(); QDebugMessageService::instance(); + QQmlDebugServer::instance()->addEngine(q); } } @@ -895,10 +897,8 @@ QQmlEngine::QQmlEngine(QQmlEnginePrivate &dd, QObject *parent) QQmlEngine::~QQmlEngine() { Q_D(QQmlEngine); - if (d->isDebugging) { - QQmlEngineDebugService::instance()->remEngine(this); - QV4DebugService::instance()->removeEngine(this); - } + if (d->isDebugging) + QQmlDebugServer::instance()->removeEngine(this); // Emit onDestruction signals for the root context before // we destroy the contexts, engine, Singleton Types etc. that diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 0848af07e3..97f82a2e8d 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -180,17 +180,22 @@ struct RegisteredPlugin { QPluginLoader* loader; }; -typedef QMap<QString, RegisteredPlugin> StringRegisteredPluginMap; +struct StringRegisteredPluginMap : public QMap<QString, RegisteredPlugin> { + QMutex mutex; +}; + Q_GLOBAL_STATIC(StringRegisteredPluginMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri and the PluginLoaders void qmlClearEnginePlugins() { - foreach (RegisteredPlugin plugin, qmlEnginePluginsWithRegisteredTypes()->values()) { + StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes(); + QMutexLocker lock(&plugins->mutex); + foreach (RegisteredPlugin plugin, plugins->values()) { QPluginLoader* loader = plugin.loader; if (!loader->unload()) qWarning("Unloading %s failed: %s", qPrintable(plugin.uri), qPrintable(loader->errorString())); delete loader; } - qmlEnginePluginsWithRegisteredTypes()->clear(); + plugins->clear(); } typedef QPair<QStaticPlugin, QJsonArray> StaticPluginPair; @@ -1909,10 +1914,12 @@ bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &ur const QString absoluteFilePath = fileInfo.absoluteFilePath(); bool engineInitialized = initializedPlugins.contains(absoluteFilePath); - bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath); + StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes(); + QMutexLocker lock(&plugins->mutex); + bool typesRegistered = plugins->contains(absoluteFilePath); if (typesRegistered) { - Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath).uri == uri, + Q_ASSERT_X(plugins->value(absoluteFilePath).uri == uri, "QQmlImportDatabase::importPlugin", "Internal error: Plugin imported previously with different uri"); } @@ -1942,7 +1949,7 @@ bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &ur return false; } } else { - loader = qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath).loader; + loader = plugins->value(absoluteFilePath).loader; } QObject *instance = loader->instance(); @@ -1951,7 +1958,7 @@ bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &ur RegisteredPlugin plugin; plugin.uri = uri; plugin.loader = loader; - qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, plugin); + plugins->insert(absoluteFilePath, plugin); // Continue with shared code path for dynamic and static plugins: if (!importPlugin(instance, fileInfo.absolutePath(), uri, typeNamespace, false, errors)) diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index 10fd05ab58..1cc75387a3 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -91,14 +91,17 @@ void QQmlEnginePrivate::incubate(QQmlIncubator &i, QQmlContextData *forContext) p->changeStatus(QQmlIncubator::Loading); if (!watcher.hasRecursed()) { - QQmlVME::Interrupt i; + QQmlInstantiationInterrupt i; p->incubate(i); } } else { incubatorList.insert(p.data()); incubatorCount++; - p->vmeGuard.guard(&p->vme); + if (useNewCompiler) + p->vmeGuard.guard(p->creator.data()); + else + p->vmeGuard.guard(&p->vme); p->changeStatus(QQmlIncubator::Loading); if (incubationController) @@ -254,7 +257,7 @@ void QQmlIncubationController::incubatingObjectCountChanged(int incubatingObject Q_UNUSED(incubatingObjectCount); } -void QQmlIncubatorPrivate::forceCompletion(QQmlVME::Interrupt &i) +void QQmlIncubatorPrivate::forceCompletion(QQmlInstantiationInterrupt &i) { while (QQmlIncubator::Loading == status) { while (QQmlIncubator::Loading == status && !waitingFor.isEmpty()) @@ -264,7 +267,7 @@ void QQmlIncubatorPrivate::forceCompletion(QQmlVME::Interrupt &i) } } -void QQmlIncubatorPrivate::incubate(QQmlVME::Interrupt &i) +void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) { if (!compiledData) return; @@ -345,7 +348,7 @@ void QQmlIncubatorPrivate::incubate(QQmlVME::Interrupt &i) QQmlContextData *ctxt = 0; if (enginePriv->useNewCompiler) - ctxt = creator->finalize(); + ctxt = creator->finalize(i); else ctxt = vme.complete(i); if (ctxt) { @@ -379,7 +382,10 @@ finishIncubate: } } } else { - vmeGuard.guard(&vme); + if (enginePriv->useNewCompiler) + vmeGuard.guard(creator.data()); + else + vmeGuard.guard(&vme); } } @@ -391,7 +397,7 @@ void QQmlIncubationController::incubateFor(int msecs) if (!d || !d->incubatorCount) return; - QQmlVME::Interrupt i(msecs * 1000000); + QQmlInstantiationInterrupt i(msecs * 1000000); i.reset(); do { static_cast<QQmlIncubatorPrivate*>(d->incubatorList.first())->incubate(i); @@ -410,7 +416,7 @@ void QQmlIncubationController::incubateWhile(volatile bool *flag, int msecs) if (!d || !d->incubatorCount) return; - QQmlVME::Interrupt i(flag, msecs * 1000000); + QQmlInstantiationInterrupt i(flag, msecs * 1000000); i.reset(); do { static_cast<QQmlIncubatorPrivate*>(d->incubatorList.first())->incubate(i); @@ -609,7 +615,7 @@ returns, the incubator will not be in the Loading state. */ void QQmlIncubator::forceCompletion() { - QQmlVME::Interrupt i; + QQmlInstantiationInterrupt i; d->forceCompletion(i); } diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h index a8b549bd28..b7009bfe43 100644 --- a/src/qml/qml/qqmlincubator_p.h +++ b/src/qml/qml/qqmlincubator_p.h @@ -104,8 +104,8 @@ public: void clear(); - void forceCompletion(QQmlVME::Interrupt &i); - void incubate(QQmlVME::Interrupt &i); + void forceCompletion(QQmlInstantiationInterrupt &i); + void incubate(QQmlInstantiationInterrupt &i); }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 499ade1ca5..b7673bdb37 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -43,7 +43,7 @@ #include <private/qqmlexpression_p.h> #include <private/qqmlcontextwrapper_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4script_p.h> #include <private/qv4errorobject_p.h> diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index 2c35e12227..0d60fee5d0 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -57,6 +57,9 @@ QmlListWrapper::QmlListWrapper(QV8Engine *engine) v8(engine) { setVTable(staticVTable()); + QV4::Scope scope(QV8Engine::getV4(engine)); + QV4::ScopedObject protectThis(scope, this); + Q_UNUSED(protectThis); setArrayType(ArrayData::Custom); } diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h index a8105d256e..a7ce8b30bf 100644 --- a/src/qml/qml/qqmllistwrapper_p.h +++ b/src/qml/qml/qqmllistwrapper_p.h @@ -58,7 +58,7 @@ #include <QtQml/qqmllist.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4object_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 24a8327722..e2063a979f 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -55,6 +55,7 @@ #include <private/qqmlcustomparser_p.h> #include <private/qqmlscriptstring_p.h> #include <private/qqmlpropertyvalueinterceptor_p.h> +#include <private/qqmlvaluetypeproxybinding_p.h> QT_USE_NAMESPACE @@ -79,19 +80,24 @@ static void removeBindingOnProperty(QObject *o, int index) if (binding) binding->destroy(); } -QmlObjectCreator::QmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData) +QmlObjectCreator::QmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlContextData *creationContext, QQmlContextData *rootContext, + QFiniteStack<QQmlAbstractBinding *> *inheritedBindingStack, QFiniteStack<QQmlParserStatus *> *inheritedParserStatusStack) : componentAttached(0) , url(compiledData->url) , engine(parentContext->engine) , qmlUnit(compiledData->qmlUnit) , jsUnit(compiledData->compilationUnit) , parentContext(parentContext) + , creationContext(creationContext) , context(0) , resolvedTypes(compiledData->resolvedTypes) , propertyCaches(compiledData->propertyCaches) , vmeMetaObjectData(compiledData->datas) + , allCreatedBindings(0) + , allParserStatusCallbacks(0) + , ownBindingAndParserStatusStacks(false) , compiledData(compiledData) - , rootContext(0) + , rootContext(rootContext) , _qobject(0) , _scopeObject(0) , _valueTypeProperty(0) @@ -103,12 +109,51 @@ QmlObjectCreator::QmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledD { if (!compiledData->isInitialized()) compiledData->initialize(engine); + + componentAttachedImpl = 0; + componentAttached = &componentAttachedImpl; + + if (inheritedBindingStack) { + Q_ASSERT(rootContext); + Q_ASSERT(inheritedParserStatusStack); + allCreatedBindings = inheritedBindingStack; + allParserStatusCallbacks = inheritedParserStatusStack; + ownBindingAndParserStatusStacks = false; + } else { + ownBindingAndParserStatusStacks = true; + allCreatedBindings = new QFiniteStack<QQmlAbstractBinding*>; + allCreatedBindings->allocate(compiledData->totalBindingsCount); + allParserStatusCallbacks = new QFiniteStack<QQmlParserStatus*>; + allParserStatusCallbacks->allocate(compiledData->totalParserStatusCount); + } +} + +QmlObjectCreator::~QmlObjectCreator() +{ + if (ownBindingAndParserStatusStacks) { + for (int i = 0; i < allCreatedBindings->count(); ++i) { + QQmlAbstractBinding *b = allCreatedBindings->at(i); + if (b) + b->m_mePtr = 0; + } + for (int i = 0; i < allParserStatusCallbacks->count(); ++i) { + QQmlParserStatus *ps = allParserStatusCallbacks->at(i); + if (ps) + ps->d = 0; + } + + delete allCreatedBindings; + delete allParserStatusCallbacks; + } } QObject *QmlObjectCreator::create(int subComponentIndex, QObject *parent) { int objectToCreate; + Q_ASSERT(!ownBindingAndParserStatusStacks || allCreatedBindings->isEmpty()); + Q_ASSERT(!ownBindingAndParserStatusStacks || allParserStatusCallbacks->isEmpty()); + if (subComponentIndex == -1) { objectIndexToId = compiledData->objectIndexToIdForRoot; objectToCreate = qmlUnit->indexOfRootObject; @@ -126,8 +171,10 @@ QObject *QmlObjectCreator::create(int subComponentIndex, QObject *parent) context->imports->addref(); context->setParent(parentContext); - if (!rootContext) + if (!rootContext) { rootContext = context; + rootContext->isRootObjectInCreation = true; + } QVector<QQmlContextData::ObjectIdMapping> mapping(objectIndexToId.count()); for (QHash<int, int>::ConstIterator it = objectIndexToId.constBegin(), end = objectIndexToId.constEnd(); @@ -145,19 +192,15 @@ QObject *QmlObjectCreator::create(int subComponentIndex, QObject *parent) QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); QV4::Scope scope(v4); QV4::ScopedObject scripts(scope, v4->newArrayObject(compiledData->scripts.count())); + context->importedScripts = scripts; for (int i = 0; i < compiledData->scripts.count(); ++i) { QQmlScriptData *s = compiledData->scripts.at(i); scripts->putIndexed(i, s->scriptValueForContext(context)); } - context->importedScripts = scripts; - } else if (parentContext) { - context->importedScripts = parentContext->importedScripts; + } else if (creationContext) { + context->importedScripts = creationContext->importedScripts; } - QVector<QQmlParserStatus*> parserStatusCallbacks; - parserStatusCallbacks.resize(qmlUnit->nObjects); - qSwap(_parserStatusCallbacks, parserStatusCallbacks); - QObject *instance = createInstance(objectToCreate, parent); if (instance) { QQmlData *ddata = QQmlData::get(instance); @@ -168,9 +211,6 @@ QObject *QmlObjectCreator::create(int subComponentIndex, QObject *parent) context->contextObject = instance; } - qSwap(_parserStatusCallbacks, parserStatusCallbacks); - allParserStatusCallbacks.prepend(parserStatusCallbacks); - return instance; } @@ -185,9 +225,11 @@ void QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, const QV4::C QV4::Scope scope(v4); // ### This should be resolved earlier at compile time and the binding value should be changed accordingly. - if (property->isEnum()) { + if (property->isEnum() && !(binding->flags & QV4::CompiledData::Binding::IsResolvedEnum)) { QVariant value = binding->valueAsString(&qmlUnit->header); - QQmlPropertyPrivate::write(_qobject, *property, value, context); + bool ok = QQmlPropertyPrivate::write(_qobject, *property, value, context); + Q_ASSERT(ok); + Q_UNUSED(ok); return; } @@ -235,109 +277,79 @@ void QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, const QV4::C } break; case QVariant::String: { - if (binding->type == QV4::CompiledData::Binding::Type_String) { - QString value = binding->valueAsString(&qmlUnit->header); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: string expected")); - } + Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); + QString value = binding->valueAsString(&qmlUnit->header); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::StringList: { - if (binding->type == QV4::CompiledData::Binding::Type_String) { - QStringList value(binding->valueAsString(&qmlUnit->header)); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: string or string list expected")); - } + Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); + QStringList value(binding->valueAsString(&qmlUnit->header)); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::ByteArray: { - if (binding->type == QV4::CompiledData::Binding::Type_String) { - QByteArray value(binding->valueAsString(&qmlUnit->header).toUtf8()); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: byte array expected")); - } + Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); + QByteArray value(binding->valueAsString(&qmlUnit->header).toUtf8()); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::Url: { - if (binding->type == QV4::CompiledData::Binding::Type_String) { - QString string = binding->valueAsString(&qmlUnit->header); - // Encoded dir-separators defeat QUrl processing - decode them first - string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); - QUrl value = string.isEmpty() ? QUrl() : this->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); - } else { - recordError(binding->location, tr("Invalid property assignment: url expected")); - } + Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); + QString string = binding->valueAsString(&qmlUnit->header); + // Encoded dir-separators defeat QUrl processing - decode them first + string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); + QUrl value = string.isEmpty() ? QUrl() : this->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); } break; case QVariant::UInt: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double d = binding->valueAsNumber(); - if (double(uint(d)) == d) { - uint value = uint(d); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - break; - } - } - recordError(binding->location, tr("Invalid property assignment: unsigned int expected")); + 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); + break; } break; case QVariant::Int: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double d = binding->valueAsNumber(); - if (double(int(d)) == d) { - int value = int(d); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - break; - } - } - recordError(binding->location, tr("Invalid property assignment: int expected")); + 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); + break; } break; case QMetaType::Float: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { - float value = float(binding->valueAsNumber()); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: number expected")); - } + 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); } break; case QVariant::Double: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double value = binding->valueAsNumber(); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: number expected")); - } + Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); + double value = binding->valueAsNumber(); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::Color: { bool ok = false; uint colorValue = QQmlStringConverters::rgbaFromString(binding->valueAsString(&qmlUnit->header), &ok); - - if (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); - } - } else { - recordError(binding->location, tr("Invalid property assignment: color expected")); + 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); } } break; @@ -345,111 +357,87 @@ void QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, const QV4::C case QVariant::Date: { bool ok = false; QDate value = QQmlStringConverters::dateFromString(binding->valueAsString(&qmlUnit->header), &ok); - if (ok) { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: date expected")); - } + Q_ASSERT(ok); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::Time: { bool ok = false; QTime value = QQmlStringConverters::timeFromString(binding->valueAsString(&qmlUnit->header), &ok); - if (ok) { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: time expected")); - } + Q_ASSERT(ok); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::DateTime: { bool ok = false; QDateTime value = QQmlStringConverters::dateTimeFromString(binding->valueAsString(&qmlUnit->header), &ok); - if (ok) { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: datetime expected")); + // ### VME compatibility :( + { + const qint64 date = value.date().toJulianDay(); + const int msecsSinceStartOfDay = value.time().msecsSinceStartOfDay(); + value = QDateTime(QDate::fromJulianDay(date), QTime::fromMSecsSinceStartOfDay(msecsSinceStartOfDay)); } + Q_ASSERT(ok); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; #endif // QT_NO_DATESTRING case QVariant::Point: { bool ok = false; QPoint value = QQmlStringConverters::pointFFromString(binding->valueAsString(&qmlUnit->header), &ok).toPoint(); - if (ok) { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: point expected")); - } + Q_ASSERT(ok); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::PointF: { bool ok = false; QPointF value = QQmlStringConverters::pointFFromString(binding->valueAsString(&qmlUnit->header), &ok); - if (ok) { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: point expected")); - } + Q_ASSERT(ok); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::Size: { bool ok = false; QSize value = QQmlStringConverters::sizeFFromString(binding->valueAsString(&qmlUnit->header), &ok).toSize(); - if (ok) { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: size expected")); - } + Q_ASSERT(ok); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::SizeF: { bool ok = false; QSizeF value = QQmlStringConverters::sizeFFromString(binding->valueAsString(&qmlUnit->header), &ok); - if (ok) { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: size expected")); - } + Q_ASSERT(ok); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::Rect: { bool ok = false; QRect value = QQmlStringConverters::rectFFromString(binding->valueAsString(&qmlUnit->header), &ok).toRect(); - if (ok) { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: point expected")); - } + Q_ASSERT(ok); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::RectF: { bool ok = false; QRectF value = QQmlStringConverters::rectFFromString(binding->valueAsString(&qmlUnit->header), &ok); - if (ok) { - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: point expected")); - } + Q_ASSERT(ok); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::Bool: { - if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { - bool value = binding->valueAsBoolean(); - argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: boolean expected")); - } + Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Boolean); + bool value = binding->valueAsBoolean(); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::Vector3D: { @@ -458,12 +446,11 @@ void QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, const QV4::C float yp; float zy; } vec; - if (QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(&qmlUnit->header), &vec, sizeof(vec))) { - argv[0] = reinterpret_cast<void *>(&vec); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: 3D vector expected")); - } + bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(&qmlUnit->header), &vec, sizeof(vec)); + Q_ASSERT(ok); + Q_UNUSED(ok); + argv[0] = reinterpret_cast<void *>(&vec); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::Vector4D: { @@ -473,74 +460,55 @@ void QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, const QV4::C float zy; float wp; } vec; - if (QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(&qmlUnit->header), &vec, sizeof(vec))) { - argv[0] = reinterpret_cast<void *>(&vec); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: 4D vector expected")); - } + bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(&qmlUnit->header), &vec, sizeof(vec)); + Q_ASSERT(ok); + Q_UNUSED(ok); + argv[0] = reinterpret_cast<void *>(&vec); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; case QVariant::RegExp: - recordError(binding->location, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); + Q_ASSERT(!"not possible"); break; 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) { - QList<qreal> value; - value.append(binding->valueAsNumber()); - argv[0] = reinterpret_cast<void *>(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: real or array of reals expected")); - } + 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); break; } else if (property->propType == qMetaTypeId<QList<int> >()) { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double n = binding->valueAsNumber(); - if (double(int(n)) == n) { - QList<int> value; - value.append(int(n)); - argv[0] = reinterpret_cast<void *>(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - break; - } else { - recordError(binding->location, tr("Invalid property assignment: int or array of ints expected")); - } - } + 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); break; } else if (property->propType == qMetaTypeId<QList<bool> >()) { - if (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); - } else { - recordError(binding->location, tr("Invalid property assignment: bool or array of bools expected")); - } + 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); break; } else if (property->propType == qMetaTypeId<QList<QUrl> >()) { - if (binding->type == QV4::CompiledData::Binding::Type_String) { - QString urlString = binding->valueAsString(&qmlUnit->header); - QUrl u = urlString.isEmpty() ? QUrl() : this->url.resolved(QUrl(urlString)); - QList<QUrl> value; - value.append(u); - argv[0] = reinterpret_cast<void *>(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: url or array of urls expected")); - } + Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); + QString urlString = binding->valueAsString(&qmlUnit->header); + QUrl u = urlString.isEmpty() ? QUrl() : this->url.resolved(QUrl(urlString)); + QList<QUrl> value; + value.append(u); + argv[0] = reinterpret_cast<void *>(&value); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); break; } else if (property->propType == qMetaTypeId<QList<QString> >()) { - if (binding->type == QV4::CompiledData::Binding::Type_String) { - QList<QString> value; - value.append(binding->valueAsString(&qmlUnit->header)); - argv[0] = reinterpret_cast<void *>(&value); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: string or array of strings expected")); - } + Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); + QList<QString> value; + value.append(binding->valueAsString(&qmlUnit->header)); + argv[0] = reinterpret_cast<void *>(&value); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); break; } else if (property->propType == qMetaTypeId<QJSValue>()) { QJSValue value; @@ -563,25 +531,33 @@ void QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, const QV4::C // otherwise, try a custom type assignment QString stringValue = binding->valueAsString(&qmlUnit->header); QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType); - if (converter) { - QVariant value = (*converter)(stringValue); + Q_ASSERT(converter); + QVariant value = (*converter)(stringValue); - QMetaProperty metaProperty = _qobject->metaObject()->property(property->coreIndex); - if (value.isNull() || ((int)metaProperty.type() != property->propType && metaProperty.userType() != property->propType)) { - recordError(binding->location, tr("Cannot assign value %1 to property %2").arg(stringValue).arg(QString::fromUtf8(metaProperty.name()))); - break; - } - - argv[0] = value.data(); - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType)))); + QMetaProperty metaProperty = _qobject->metaObject()->property(property->coreIndex); + if (value.isNull() || ((int)metaProperty.type() != property->propType && metaProperty.userType() != property->propType)) { + recordError(binding->location, tr("Cannot assign value %1 to property %2").arg(stringValue).arg(QString::fromUtf8(metaProperty.name()))); + break; } + + argv[0] = value.data(); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } break; } } +static QQmlType *qmlTypeForObject(QObject *object) +{ + QQmlType *type = 0; + const QMetaObject *mo = object->metaObject(); + while (mo && !type) { + type = QQmlMetaType::qmlType(mo); + mo = mo->superClass(); + } + return type; +} + void QmlObjectCreator::setupBindings() { QQmlListProperty<void> savedList; @@ -593,7 +569,7 @@ void QmlObjectCreator::setupBindings() QString id = stringAt(_compiledObject->idIndex); if (!id.isEmpty()) { QQmlPropertyData *idProperty = _propertyCache->property(QStringLiteral("id"), _qobject, context); - if (idProperty) { + if (idProperty && idProperty->isWritable()) { QV4::CompiledData::Binding idBinding; idBinding.propertyNameIndex = 0; // Not used idBinding.flags = 0; @@ -604,6 +580,36 @@ void QmlObjectCreator::setupBindings() } } + // ### this is best done through type-compile-time binding skip lists. + if (_valueTypeProperty) { + QQmlAbstractBinding *binding = + QQmlPropertyPrivate::binding(_bindingTarget, _valueTypeProperty->coreIndex, -1); + + if (binding && binding->bindingType() != QQmlAbstractBinding::ValueTypeProxy) { + QQmlPropertyPrivate::setBinding(_bindingTarget, _valueTypeProperty->coreIndex, -1, 0); + binding->destroy(); + } else if (binding) { + QQmlValueTypeProxyBinding *proxy = + static_cast<QQmlValueTypeProxyBinding *>(binding); + + if (qmlTypeForObject(_bindingTarget)) { + quint32 bindingSkipList = 0; + + const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable(); + for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) { + if (binding->type != QV4::CompiledData::Binding::Type_Script) + continue; + property = binding->propertyNameIndex != 0 ? _propertyCache->property(stringAt(binding->propertyNameIndex), _qobject, context) : defaultProperty; + if (property) + bindingSkipList |= (1 << property->coreIndex); + } + property = 0; + + proxy->removeBindings(bindingSkipList); + } + } + } + const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable(); for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) { @@ -611,10 +617,17 @@ void QmlObjectCreator::setupBindings() if (name.isEmpty()) property = 0; - if (!property || (i > 0 && (binding - 1)->propertyNameIndex != binding->propertyNameIndex)) { - if (!name.isEmpty()) - property = _propertyCache->property(name, _qobject, context); - else + if (!property + || (i > 0 && ((binding - 1)->propertyNameIndex != binding->propertyNameIndex + || (binding - 1)->flags != binding->flags)) + ) { + if (!name.isEmpty()) { + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + property = PropertyResolver(_propertyCache).signal(name, /*notInRevision*/0, _qobject, context); + else + property = _propertyCache->property(name, _qobject, context); + } else property = defaultProperty; if (property && property->isQList()) { @@ -625,14 +638,14 @@ void QmlObjectCreator::setupBindings() } - if (!setPropertyValue(property, i, binding)) + if (!setPropertyBinding(property, binding)) return; } qSwap(_currentList, savedList); } -bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingIndex, const QV4::CompiledData::Binding *binding) +bool QmlObjectCreator::setPropertyBinding(QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { Q_ASSERT(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty()); @@ -667,6 +680,7 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI QObject *createdSubObject = 0; if (binding->type == QV4::CompiledData::Binding::Type_Object) { + Q_ASSERT(!_valueTypeProperty); createdSubObject = createInstance(binding->value.objectIndex, _bindingTarget); if (!createdSubObject) return false; @@ -726,7 +740,8 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI } if (_ddata->hasBindingBit(property->coreIndex) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) - && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) + && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) + && !_valueTypeProperty) removeBindingOnProperty(_bindingTarget, property->coreIndex); if (binding->type == QV4::CompiledData::Binding::Type_Script) { @@ -756,10 +771,20 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine); qmlBinding->setTarget(_bindingTarget, targetCorePropertyData, context); - qmlBinding->addToObject(); - _createdBindings[bindingIndex] = qmlBinding; - qmlBinding->m_mePtr = &_createdBindings[bindingIndex]; + if (targetCorePropertyData.isAlias()) { + QQmlAbstractBinding *old = + QQmlPropertyPrivate::setBindingNoEnable(_bindingTarget, + targetCorePropertyData.coreIndex, + targetCorePropertyData.getValueTypeCoreIndex(), + qmlBinding); + if (old) { old->destroy(); } + } else { + qmlBinding->addToObject(); + } + + allCreatedBindings->push(qmlBinding); + qmlBinding->m_mePtr = &allCreatedBindings->top(); } return true; } @@ -767,12 +792,7 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI if (binding->type == QV4::CompiledData::Binding::Type_Object) { if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { // ### determine value source and interceptor casts ahead of time. - QQmlType *type = 0; - const QMetaObject *mo = createdSubObject->metaObject(); - while (mo && !type) { - type = QQmlMetaType::qmlType(mo); - mo = mo->superClass(); - } + QQmlType *type = qmlTypeForObject(createdSubObject); Q_ASSERT(type); QQmlPropertyData targetCorePropertyData = *property; @@ -802,6 +822,30 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI return false; } + // Assigning object to signal property? + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) { + if (!property->isFunction()) { + recordError(binding->valueLocation, tr("Cannot assign an object to signal property %1").arg(property->name(_qobject))); + return false; + } + QMetaMethod method = QQmlMetaType::defaultMethod(createdSubObject); + if (!method.isValid()) { + recordError(binding->valueLocation, tr("Cannot assign object type %1 with no default method").arg(QString::fromLatin1(createdSubObject->metaObject()->className()))); + return false; + } + + QMetaMethod signalMethod = _qobject->metaObject()->method(property->coreIndex); + if (!QMetaObject::checkConnectArgs(signalMethod, method)) { + recordError(binding->valueLocation, tr("Cannot connect mismatched signal/slot %1 %vs. %2") + .arg(QString::fromLatin1(method.methodSignature().constData())) + .arg(QString::fromLatin1(signalMethod.methodSignature().constData()))); + return false; + } + + QQmlPropertyPrivate::connect(_qobject, property->coreIndex, createdSubObject, method.methodIndex()); + return true; + } + QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::RemoveBindingOnAliasWrite; int propertyWriteStatus = -1; @@ -842,33 +886,9 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI if (_currentList.append) _currentList.append(&_currentList, itemToAdd); } else { - QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); - - // 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.value(binding->value.objectIndex); - if (!c) - c = enginePrivate->cache(createdSubObject); - while (c && !isAssignable) { - isAssignable |= c == propertyMetaObject; - c = c->parent(); - } - } - - if (isAssignable) { - argv[0] = &createdSubObject; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); - } else { - recordError(binding->location, tr("Cannot assign object to property")); - return false; - } + // pointer compatibility was tested in QQmlPropertyValidator at type compile time + argv[0] = &createdSubObject; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); } return true; } @@ -918,6 +938,7 @@ QObject *QmlObjectCreator::createInstance(int index, QObject *parent) bool isComponent = false; QObject *instance = 0; QQmlCustomParser *customParser = 0; + QQmlParserStatus *parserStatus = 0; if (compiledData->isComponent(index)) { isComponent = true; @@ -938,14 +959,16 @@ QObject *QmlObjectCreator::createInstance(int index, QObject *parent) } const int parserStatusCast = type->parserStatusCast(); - if (parserStatusCast != -1) { - QQmlParserStatus *parserStatus = reinterpret_cast<QQmlParserStatus*>(reinterpret_cast<char *>(instance) + parserStatusCast); - parserStatus->classBegin(); - _parserStatusCallbacks[index] = parserStatus; - parserStatus->d = &_parserStatusCallbacks[index]; - } + if (parserStatusCast != -1) + parserStatus = reinterpret_cast<QQmlParserStatus*>(reinterpret_cast<char *>(instance) + parserStatusCast); customParser = type->customParser(); + + if (rootContext->isRootObjectInCreation) { + QQmlData *ddata = QQmlData::get(instance, /*create*/true); + ddata->rootObjectInCreation = true; + rootContext->isRootObjectInCreation = false; + } } else { Q_ASSERT(typeRef->component); if (typeRef->component->qmlUnit->isSingleton()) @@ -953,16 +976,13 @@ QObject *QmlObjectCreator::createInstance(int index, QObject *parent) recordError(obj->location, tr("Composite Singleton Type %1 is not creatable").arg(stringAt(obj->inheritedTypeNameIndex))); return 0; } - QmlObjectCreator subCreator(context, typeRef->component); + QmlObjectCreator subCreator(context, typeRef->component, creationContext, rootContext, allCreatedBindings, allParserStatusCallbacks); + subCreator.componentAttached = componentAttached; instance = subCreator.create(); if (!instance) { errors += subCreator.errors; return 0; } - if (subCreator.componentAttached) - subCreator.componentAttached->add(&componentAttached); - allCreatedBindings << subCreator.allCreatedBindings; - allParserStatusCallbacks << subCreator.allParserStatusCallbacks; } // ### use no-event variant if (parent) @@ -970,6 +990,7 @@ QObject *QmlObjectCreator::createInstance(int index, QObject *parent) } QQmlData *ddata = QQmlData::get(instance, /*create*/true); + ddata->setImplicitDestructible(); if (static_cast<quint32>(index) == qmlUnit->indexOfRootObject) { if (ddata->context) { Q_ASSERT(ddata->context != context); @@ -986,6 +1007,12 @@ QObject *QmlObjectCreator::createInstance(int index, QObject *parent) ddata->outerContext = context; + if (parserStatus) { + parserStatus->classBegin(); + allParserStatusCallbacks->push(parserStatus); + parserStatus->d = &allParserStatusCallbacks->top(); + } + QHash<int, int>::ConstIterator idEntry = objectIndexToId.find(index); if (idEntry != objectIndexToId.constEnd()) context->setIdProperty(idEntry.value(), instance); @@ -1007,8 +1034,10 @@ QObject *QmlObjectCreator::createInstance(int index, QObject *parent) QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); QV4::Scope valueScope(v4); - QV4::ScopedObject jsScopeObject(valueScope, QV4::QmlContextWrapper::qmlScope(QV8Engine::get(engine), context, _scopeObject)); - QV4::Scoped<QV4::QmlBindingWrapper> qmlBindingWrapper(valueScope, new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, jsScopeObject)); + QV4::ScopedValue scopeObjectProtector(valueScope, ddata ? ddata->jsWrapper.value() : 0); + Q_UNUSED(scopeObjectProtector); + QV4::ScopedObject qmlScope(valueScope, QV4::QmlContextWrapper::qmlScope(QV8Engine::get(engine), context, _scopeObject)); + QV4::Scoped<QV4::QmlBindingWrapper> qmlBindingWrapper(valueScope, new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, qmlScope)); QV4::ExecutionContext *qmlContext = qmlBindingWrapper->context(); qSwap(_qmlContext, qmlContext); @@ -1021,51 +1050,41 @@ QObject *QmlObjectCreator::createInstance(int index, QObject *parent) return result ? instance : 0; } -QQmlContextData *QmlObjectCreator::finalize() +QQmlContextData *QmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt) { { QQmlTrace trace("VME Binding Enable"); trace.event("begin binding eval"); - Q_ASSERT(allCreatedBindings.isEmpty() || allCreatedBindings.isDetached()); + while (!allCreatedBindings->isEmpty()) { + if (interrupt.shouldInterrupt()) + return 0; - for (QLinkedList<QVector<QQmlAbstractBinding*> >::Iterator it = allCreatedBindings.begin(), end = allCreatedBindings.end(); - it != end; ++it) { - const QVector<QQmlAbstractBinding *> &bindings = *it; - for (int i = 0; i < bindings.count(); ++i) { - QQmlAbstractBinding *b = bindings.at(i); - if (!b) - continue; - b->m_mePtr = 0; - QQmlData *data = QQmlData::get(b->object()); - Q_ASSERT(data); - data->clearPendingBindingBit(b->propertyIndex()); - b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | - QQmlPropertyPrivate::DontRemoveBinding); - } + QQmlAbstractBinding *b = allCreatedBindings->pop(); + if (!b) + continue; + b->m_mePtr = 0; + QQmlData *data = QQmlData::get(b->object()); + Q_ASSERT(data); + data->clearPendingBindingBit(b->propertyIndex()); + b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | + QQmlPropertyPrivate::DontRemoveBinding); } } if (true /* ### componentCompleteEnabled()*/) { // the qml designer does the component complete later QQmlTrace trace("VME Component Complete"); - for (QLinkedList<QVector<QQmlParserStatus*> >::ConstIterator it = allParserStatusCallbacks.constBegin(), end = allParserStatusCallbacks.constEnd(); - it != end; ++it) { - const QVector<QQmlParserStatus *> &parserStatusCallbacks = *it; - for (int i = parserStatusCallbacks.count() - 1; i >= 0; --i) { - QQmlParserStatus *status = parserStatusCallbacks.at(i); - - if (status && status->d) { - status->d = 0; - status->componentComplete(); - } + while (!allParserStatusCallbacks->isEmpty()) { + QQmlParserStatus *status = allParserStatusCallbacks->pop(); - #if 0 // ### - if (watcher.hasRecursed() || interrupt.shouldInterrupt()) - return 0; - #endif + if (status && status->d) { + status->d = 0; + status->componentComplete(); } + + if (interrupt.shouldInterrupt()) + return 0; } - allParserStatusCallbacks.clear(); } { @@ -1077,18 +1096,14 @@ QQmlContextData *QmlObjectCreator::finalize() void *args[] = { 0 }; QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, callback.second, args); } -#if 0 // ### - if (watcher.hasRecursed()) - return 0; -#endif } finalizeCallbacks.clear(); } { QQmlTrace trace("VME Component.onCompleted Callbacks"); - while (componentAttached) { - QQmlComponentAttached *a = componentAttached; + while (componentAttachedImpl) { + QQmlComponentAttached *a = componentAttachedImpl; a->rem(); QQmlData *d = QQmlData::get(a->parent()); Q_ASSERT(d); @@ -1097,10 +1112,8 @@ QQmlContextData *QmlObjectCreator::finalize() // ### designer if (componentCompleteEnabled()) emit a->completed(); -#if 0 // ### - if (watcher.hasRecursed() || interrupt.shouldInterrupt()) + if (interrupt.shouldInterrupt()) return 0; -#endif } } @@ -1127,11 +1140,11 @@ bool QmlObjectCreator::populateInstance(int index, QObject *instance, QQmlRefPoi vmeMetaObject = new QQmlVMEMetaObject(_qobject, _propertyCache, reinterpret_cast<const QQmlVMEMetaData*>(data.constData())); if (_ddata->propertyCache) _ddata->propertyCache->release(); - _ddata->propertyCache = _propertyCache; - _ddata->propertyCache->addref(); } else { vmeMetaObject = QQmlVMEMetaObject::get(_qobject); } + _ddata->propertyCache = _propertyCache; + _ddata->propertyCache->addref(); _ddata->lineNumber = _compiledObject->location.line; _ddata->columnNumber = _compiledObject->location.column; @@ -1139,14 +1152,10 @@ bool QmlObjectCreator::populateInstance(int index, QObject *instance, QQmlRefPoi qSwap(_vmeMetaObject, vmeMetaObject); QVector<QQmlAbstractBinding*> createdBindings(_compiledObject->nBindings, 0); - qSwap(_createdBindings, createdBindings); - setupBindings(); setupFunctions(); + setupBindings(); - allCreatedBindings.append(_createdBindings); - - qSwap(_createdBindings, createdBindings); qSwap(_vmeMetaObject, vmeMetaObject); qSwap(_bindingTarget, bindingTarget); qSwap(_ddata, declarativeData); diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 951c3a2eed..68f2eaeab3 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -46,34 +46,39 @@ #include <private/qv4compileddata_p.h> #include <private/qqmlcompiler_p.h> #include <private/qqmltypecompiler_p.h> -#include <QLinkedList> +#include <private/qfinitestack_p.h> QT_BEGIN_NAMESPACE class QQmlAbstractBinding; struct QQmlTypeCompiler; +class QQmlInstantiationInterrupt; class QmlObjectCreator { Q_DECLARE_TR_FUNCTIONS(QmlObjectCreator) public: - QmlObjectCreator(QQmlContextData *contextData, QQmlCompiledData *compiledData); + QmlObjectCreator(QQmlContextData *contextData, QQmlCompiledData *compiledData, QQmlContextData *creationContext, QQmlContextData *rootContext = 0, + QFiniteStack<QQmlAbstractBinding*> *inheritedBindingStack = 0, QFiniteStack<QQmlParserStatus*> *inheritedParserStatusStack = 0); + ~QmlObjectCreator(); QObject *create(int subComponentIndex = -1, QObject *parent = 0); - QQmlContextData *finalize(); + QQmlContextData *finalize(QQmlInstantiationInterrupt &interrupt); - QQmlComponentAttached *componentAttached; + QQmlComponentAttached **componentAttached; QList<QQmlEnginePrivate::FinalizeCallback> finalizeCallbacks; QList<QQmlError> errors; + QQmlContextData *parentContextData() const { return parentContext; } + private: QObject *createInstance(int index, QObject *parent = 0); bool populateInstance(int index, QObject *instance, QQmlRefPointer<QQmlPropertyCache> cache, QObject *bindingTarget, QQmlPropertyData *valueTypeProperty); void setupBindings(); - bool setPropertyValue(QQmlPropertyData *property, int index, const QV4::CompiledData::Binding *binding); + bool setPropertyBinding(QQmlPropertyData *property, const QV4::CompiledData::Binding *binding); void setPropertyValue(QQmlPropertyData *property, const QV4::CompiledData::Binding *binding); void setupFunctions(); @@ -85,15 +90,18 @@ private: const QV4::CompiledData::QmlUnit *qmlUnit; const QV4::CompiledData::CompilationUnit *jsUnit; QQmlContextData *parentContext; + QQmlContextData *creationContext; QQmlContextData *context; const QHash<int, QQmlCompiledData::TypeReference*> resolvedTypes; const QVector<QQmlPropertyCache *> propertyCaches; const QVector<QByteArray> vmeMetaObjectData; QHash<int, int> objectIndexToId; - QLinkedList<QVector<QQmlAbstractBinding*> > allCreatedBindings; - QLinkedList<QVector<QQmlParserStatus*> > allParserStatusCallbacks; + QFiniteStack<QQmlAbstractBinding*> *allCreatedBindings; + QFiniteStack<QQmlParserStatus*> *allParserStatusCallbacks; + bool ownBindingAndParserStatusStacks; QQmlCompiledData *compiledData; QQmlContextData *rootContext; + QQmlComponentAttached *componentAttachedImpl; QObject *_qobject; QObject *_scopeObject; @@ -104,10 +112,8 @@ private: QQmlData *_ddata; QQmlRefPointer<QQmlPropertyCache> _propertyCache; QQmlVMEMetaObject *_vmeMetaObject; - QVector<QQmlAbstractBinding*> _createdBindings; QQmlListProperty<void> _currentList; QV4::ExecutionContext *_qmlContext; - QVector<QQmlParserStatus*> _parserStatusCallbacks; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index d026b95201..a0af0e94ef 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -50,7 +50,7 @@ #include <private/qqmlaccessors_p.h> #include <private/qmetaobjectbuilder_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <QtCore/qdebug.h> @@ -1195,7 +1195,7 @@ int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index, int argc = m.parameterCount(); if (!rv->arguments) { - A *args = c->createArgumentsObject(argc); + A *args = c->createArgumentsObject(argc, m.parameterNames()); rv->arguments = args; } A *args = static_cast<A *>(rv->arguments); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index bc0cef9f4c..fe7f204e87 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -62,7 +62,7 @@ #include <QtCore/qvarlengtharray.h> #include <QtCore/qvector.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> QT_BEGIN_NAMESPACE @@ -351,7 +351,7 @@ private: QQmlPropertyData::Flag signalFlags = QQmlPropertyData::NoFlags); QQmlPropertyCacheMethodArguments *createArgumentsObject(int count, - const QList<QByteArray> &names = QList<QByteArray>()); + const QList<QByteArray> &names); QQmlPropertyData *signal(int, QQmlPropertyCache **) const; typedef QVector<QQmlPropertyData> IndexCache; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index f734b77eec..7c32d4f19c 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2453,8 +2453,8 @@ void QQmlTypeData::resolveTypes() error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(name).arg(error.description())); } - error.setLine(unresolvedRef->line); - error.setColumn(unresolvedRef->column); + error.setLine(unresolvedRef->location.line); + error.setColumn(unresolvedRef->location.column); errors.prepend(error); setError(errors); @@ -2468,8 +2468,10 @@ void QQmlTypeData::resolveTypes() ref.majorVersion = majorVersion; ref.minorVersion = minorVersion; - ref.location.line = unresolvedRef->line; - ref.location.column = unresolvedRef->column; + ref.location.line = unresolvedRef->location.line; + ref.location.column = unresolvedRef->location.column; + + ref.needsCreation = unresolvedRef->needsCreation; m_resolvedTypes.insert(unresolvedRef.key(), ref); } diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 976f5a5ffb..ab6f47e8de 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -69,7 +69,7 @@ #include <private/qqmlbundle_p.h> #include <private/qflagpointer_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4script_p.h> QT_BEGIN_NAMESPACE @@ -396,7 +396,7 @@ class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob public: struct TypeReference { - TypeReference() : type(0), majorVersion(0), minorVersion(0), typeData(0) {} + TypeReference() : type(0), majorVersion(0), minorVersion(0), typeData(0), needsCreation(true) {} QQmlScript::Location location; QQmlType *type; @@ -404,6 +404,7 @@ public: int minorVersion; QQmlTypeData *typeData; QString prefix; // used by CompositeSingleton types + bool needsCreation; }; struct ScriptReference diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index eb1eee5096..355a6751a9 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -56,7 +56,7 @@ #include <QtCore/qglobal.h> #include <QtCore/qpointer.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4object_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index 256573966a..d66dbbba0c 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -56,7 +56,7 @@ #include <QtCore/qglobal.h> #include <private/qtqmlglobal_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4object_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp index e60413327a..7d1e8cc8f0 100644 --- a/src/qml/qml/qqmlvme.cpp +++ b/src/qml/qml/qqmlvme.cpp @@ -184,7 +184,7 @@ struct ActiveVMERestorer }; } -QObject *QQmlVME::execute(QList<QQmlError> *errors, const Interrupt &interrupt) +QObject *QQmlVME::execute(QList<QQmlError> *errors, const QQmlInstantiationInterrupt &interrupt) { Q_ASSERT(states.count() >= 1); @@ -226,7 +226,7 @@ static QVariant variantFromString(const QString &string) return QQmlStringConverters::variantFromString(string); } -static QV4::ExecutionContext *qmlBindingContext(QQmlEngine *engine, QV4::ExecutionEngine *v4, QV4::SafeValue *qmlBindingWrappers, QQmlContextData *context, QObject *scope, int objIdx) +static QV4::ExecutionContext *qmlBindingContext(QQmlEngine *engine, QV4::ExecutionEngine *v4, QV4::Value *qmlBindingWrappers, QQmlContextData *context, QObject *scope, int objIdx) { QV4::Scope valueScope(v4); QV4::Scoped<QV4::QmlBindingWrapper> wrapper(valueScope, qmlBindingWrappers[objIdx]); @@ -330,7 +330,7 @@ static QV4::ExecutionContext *qmlBindingContext(QQmlEngine *engine, QV4::Executi removeBindingOnProperty(o, index) QObject *QQmlVME::run(QList<QQmlError> *errors, - const Interrupt &interrupt + const QQmlInstantiationInterrupt &interrupt #ifdef QML_THREADED_VME_INTERPRETER , void * const **storeJumpTable #endif @@ -355,7 +355,7 @@ QObject *QQmlVME::run(QList<QQmlError> *errors, QV4::ExecutionEngine *v4 = ep->v4engine(); QV4::Scope valueScope(v4); QV4::ScopedValue tmpValue(valueScope); - QV4::SafeValue *qmlBindingWrappers = valueScope.alloc(objects.capacity()); + QV4::Value *qmlBindingWrappers = valueScope.alloc(objects.capacity()); std::fill(qmlBindingWrappers, qmlBindingWrappers + objects.capacity(), QV4::Primitive::undefinedValue()); int status = -1; // needed for dbus @@ -1134,14 +1134,14 @@ void *const *QQmlVME::instructionJumpTable() static void * const *jumpTable = 0; if (!jumpTable) { QQmlVME dummy; - QQmlVME::Interrupt i; + QQmlInstantiationInterrupt i; dummy.run(0, i, &jumpTable); } return jumpTable; } #endif -QQmlContextData *QQmlVME::complete(const Interrupt &interrupt) +QQmlContextData *QQmlVME::complete(const QQmlInstantiationInterrupt &interrupt) { Q_ASSERT(engine || (bindValues.isEmpty() && @@ -1307,6 +1307,14 @@ void QQmlVMEGuard::guard(QQmlVME *vme) m_contexts[m_contextCount - 1] = vme->rootContext.contextData(); } +void QQmlVMEGuard::guard(QmlObjectCreator *creator) +{ + clear(); + m_contextCount = 1; + m_contexts = new QQmlGuardedContextData[m_contextCount]; + m_contexts[0] = creator->parentContextData(); +} + void QQmlVMEGuard::clear() { delete [] m_objects; diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h index d26812a68f..4da1aec74b 100644 --- a/src/qml/qml/qqmlvme_p.h +++ b/src/qml/qml/qqmlvme_p.h @@ -102,28 +102,28 @@ Q_DECLARE_TYPEINFO(QQmlVMETypes::List, Q_PRIMITIVE_TYPE | Q_MOVABLE_TYPE); template<> class QTypeInfo<QQmlVMETypes::State> : public QTypeInfoMerger<QQmlVMETypes::State, QBitField> {}; //Q_DECLARE_TYPEINFO +class QQmlInstantiationInterrupt { +public: + inline QQmlInstantiationInterrupt(); + inline QQmlInstantiationInterrupt(volatile bool *runWhile, int nsecs=0); + inline QQmlInstantiationInterrupt(int nsecs); + + inline void reset(); + inline bool shouldInterrupt() const; +private: + enum Mode { None, Time, Flag }; + Mode mode; + struct { + QElapsedTimer timer; + int nsecs; + }; + volatile bool *runWhile; +}; + class Q_QML_PRIVATE_EXPORT QQmlVME { Q_DECLARE_TR_FUNCTIONS(QQmlVME) public: - class Interrupt { - public: - inline Interrupt(); - inline Interrupt(volatile bool *runWhile, int nsecs=0); - inline Interrupt(int nsecs); - - inline void reset(); - inline bool shouldInterrupt() const; - private: - enum Mode { None, Time, Flag }; - Mode mode; - struct { - QElapsedTimer timer; - int nsecs; - }; - volatile bool *runWhile; - }; - QQmlVME() : data(0), componentAttached(0) {} QQmlVME(void *data) : data(data), componentAttached(0) {} @@ -136,8 +136,8 @@ public: bool initDeferred(QObject *); void reset(); - QObject *execute(QList<QQmlError> *errors, const Interrupt & = Interrupt()); - QQmlContextData *complete(const Interrupt & = Interrupt()); + QObject *execute(QList<QQmlError> *errors, const QQmlInstantiationInterrupt & = QQmlInstantiationInterrupt()); + QQmlContextData *complete(const QQmlInstantiationInterrupt & = QQmlInstantiationInterrupt()); static void enableComponentComplete(); static void disableComponentComplete(); @@ -146,7 +146,7 @@ public: private: friend class QQmlVMEGuard; - QObject *run(QList<QQmlError> *errors, const Interrupt & + QObject *run(QList<QQmlError> *errors, const QQmlInstantiationInterrupt & #ifdef QML_THREADED_VME_INTERPRETER , void *const**storeJumpTable = 0 #endif @@ -196,6 +196,7 @@ public: ~QQmlVMEGuard(); void guard(QQmlVME *); + void guard(QmlObjectCreator *); void clear(); bool isOK() const; @@ -207,28 +208,28 @@ private: QQmlGuardedContextData *m_contexts; }; -QQmlVME::Interrupt::Interrupt() +QQmlInstantiationInterrupt::QQmlInstantiationInterrupt() : mode(None), nsecs(0), runWhile(0) { } -QQmlVME::Interrupt::Interrupt(volatile bool *runWhile, int nsecs) +QQmlInstantiationInterrupt::QQmlInstantiationInterrupt(volatile bool *runWhile, int nsecs) : mode(Flag), nsecs(nsecs), runWhile(runWhile) { } -QQmlVME::Interrupt::Interrupt(int nsecs) +QQmlInstantiationInterrupt::QQmlInstantiationInterrupt(int nsecs) : mode(Time), nsecs(nsecs), runWhile(0) { } -void QQmlVME::Interrupt::reset() +void QQmlInstantiationInterrupt::reset() { if (mode == Time || nsecs) timer.start(); } -bool QQmlVME::Interrupt::shouldInterrupt() const +bool QQmlInstantiationInterrupt::shouldInterrupt() const { if (mode == None) { return false; diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 0b966484ca..f9cb9565b3 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -1086,7 +1086,8 @@ void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) // And, if the new value is a scarce resource, we need to ensure that it does not get // automatically released by the engine until no other references to it exist. QV4::ScopedValue newv(scope, QQmlEnginePrivate::get(ctxt->engine)->v8engine()->fromVariant(value)); - if (QV4::Referenced<QV4::VariantObject> v = newv->asRef<QV4::VariantObject>()) + QV4::VariantObjectRef v = newv; + if (!!v) v->addVmePropertyReference(); // Write the value and emit change signal as appropriate. diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index 77db107668..778559d0c4 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -71,7 +71,7 @@ #include <private/qv8engine_p.h> #include <private/qflagpointer_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 316d066097..76327e3a69 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -68,7 +68,7 @@ #include <QtCore/qdatetime.h> #include <private/qsimd_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4dateobject_p.h> #include <private/qv4objectiterator_p.h> #include <private/qv4mm_p.h> diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index e390ef56dd..1295e671f0 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -68,7 +68,7 @@ #include <private/qqmlpropertycache_p.h> #include <private/qv4qobjectwrapper_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4object_p.h> #include <private/qv4identifier_p.h> diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index f10f54038d..3b02e1850b 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -52,7 +52,7 @@ #include <private/qqmlincubator_p.h> #include <private/qqmlcompiler_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4functionobject_p.h> QT_BEGIN_NAMESPACE @@ -1629,45 +1629,45 @@ void QQmlDelegateModelItemMetaType::initializePrototype() proto->defineAccessorProperty(QStringLiteral("model"), QQmlDelegateModelItem::get_model, 0); proto->defineAccessorProperty(QStringLiteral("groups"), QQmlDelegateModelItem::get_groups, QQmlDelegateModelItem::set_groups); QV4::ScopedString s(scope); - QV4::Property p; + QV4::ScopedProperty p(scope); s = v4->newString(QStringLiteral("isUnresolved")); - p.setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, 30, QQmlDelegateModelItem::get_member)); - p.setSetter(0); + p->setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, 30, QQmlDelegateModelItem::get_member)); + p->setSetter(0); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); s = v4->newString(QStringLiteral("inItems")); - p.setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, QQmlListCompositor::Default, QQmlDelegateModelItem::get_member)); - p.setSetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, QQmlListCompositor::Default, QQmlDelegateModelItem::set_member)); + p->setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, QQmlListCompositor::Default, QQmlDelegateModelItem::get_member)); + p->setSetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, QQmlListCompositor::Default, QQmlDelegateModelItem::set_member)); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); s = v4->newString(QStringLiteral("inPersistedItems")); - p.setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_member)); - p.setSetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, QQmlListCompositor::Persisted, QQmlDelegateModelItem::set_member)); + p->setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_member)); + p->setSetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, QQmlListCompositor::Persisted, QQmlDelegateModelItem::set_member)); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); s = v4->newString(QStringLiteral("itemsIndex")); - p.setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, QQmlListCompositor::Default, QQmlDelegateModelItem::get_index)); + p->setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, QQmlListCompositor::Default, QQmlDelegateModelItem::get_index)); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); s = v4->newString(QStringLiteral("persistedItemsIndex")); - p.setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_index)); - p.setSetter(0); + p->setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_index)); + p->setSetter(0); 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); propertyName.replace(2, 1, propertyName.at(2).toUpper()); s = v4->newString(propertyName); - p.setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, i + 1, QQmlDelegateModelItem::get_member)); - p.setSetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, i + 1, QQmlDelegateModelItem::set_member)); + p->setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, i + 1, QQmlDelegateModelItem::get_member)); + p->setSetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, i + 1, QQmlDelegateModelItem::set_member)); 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"); s = v4->newString(propertyName); - p.setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, i + 1, QQmlDelegateModelItem::get_index)); - p.setSetter(0); + p->setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, i + 1, QQmlDelegateModelItem::get_index)); + p->setSetter(0); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); } modelItemProto = proto; @@ -1873,7 +1873,7 @@ void QQmlDelegateModelItem::incubateObject( incubatorPriv->compiledData = componentPriv->cc; incubatorPriv->compiledData->addref(); if (enginePriv->useNewCompiler) { - incubatorPriv->creator.reset(new QmlObjectCreator(context, componentPriv->cc)); + incubatorPriv->creator.reset(new QmlObjectCreator(context, componentPriv->cc, componentPriv->creationContext)); incubatorPriv->subComponentToCreate = componentPriv->start; } else { incubatorPriv->vme.init( @@ -3198,6 +3198,9 @@ public: : Object(engine) { setVTable(staticVTable()); + QV4::Scope scope(engine); + QV4::ScopedObject protectThis(scope, this); + Q_UNUSED(protectThis); setArrayType(QV4::ArrayData::Custom); } virtual ~QQmlDelegateModelGroupChangeArray() {} diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index 5af33a34f8..da34101c36 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -62,7 +62,7 @@ #include <private/qv8engine_p.h> #include <private/qv4serialize_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4script_p.h> #include <private/qv4scopedvalue_p.h> diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index 36c3623d8b..d38e5ac3df 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -46,7 +46,7 @@ #include <private/qqmlproperty_p.h> #include <private/qv8engine_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4functionobject_p.h> QT_BEGIN_NAMESPACE @@ -219,6 +219,7 @@ public: QV4::Scoped<QV4::Object> proto(scope, v4->newObject()); proto->defineAccessorProperty(QStringLiteral("index"), get_index, 0); proto->defineAccessorProperty(QStringLiteral("hasModelChildren"), get_hasModelChildren, 0); + QV4::ScopedProperty p(scope); typedef QHash<QByteArray, int>::const_iterator iterator; for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) { @@ -226,9 +227,8 @@ public: const QByteArray &propertyName = it.key(); QV4::ScopedString name(scope, v4->newString(QString::fromUtf8(propertyName))); - QV4::Property p; - p.setGetter(new (v4->memoryManager) QV4::IndexedBuiltinFunction(v4->rootContext, propertyId, QQmlDMCachedModelData::get_property)); - p.setSetter(new (v4->memoryManager) QV4::IndexedBuiltinFunction(v4->rootContext, propertyId, QQmlDMCachedModelData::set_property)); + p->setGetter(new (v4->memoryManager) QV4::IndexedBuiltinFunction(v4->rootContext, propertyId, QQmlDMCachedModelData::get_property)); + p->setSetter(new (v4->memoryManager) QV4::IndexedBuiltinFunction(v4->rootContext, propertyId, QQmlDMCachedModelData::set_property)); proto->insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable); } prototype = proto; diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index 763dd5251b..ed5c8d7bcb 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -54,7 +54,7 @@ #include <QtCore/QBuffer> #include <QtCore/qdatetime.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4scopedvalue_p.h> diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index f636d5a619..9851983201 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -65,7 +65,7 @@ #include <QtCore/qnumeric.h> #include <private/qquickwindow_p.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4objectproto_p.h> #include <private/qv4scopedvalue_p.h> @@ -871,6 +871,9 @@ struct QQuickJSContext2DPixelData : public QV4::Object : QV4::Object(engine) { setVTable(staticVTable()); + QV4::Scope scope(engine); + QV4::ScopedObject protectThis(scope, this); + Q_UNUSED(protectThis); setArrayType(QV4::ArrayData::Custom); } @@ -915,9 +918,11 @@ struct QQuickJSContext2DImageData : public QV4::Object - QV4::SafeValue pixelData; + QV4::Value pixelData; }; +DEFINE_REF(QQuickJSContext2DImageData, QV4::Object); + DEFINE_OBJECT_VTABLE(QQuickJSContext2DImageData); static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV8Engine* engine, const QImage& image) @@ -2109,7 +2114,8 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_path(QV4::CallContext *ctx) QV4::ScopedValue value(scope, ctx->argument(0)); r->context->beginPath(); - if (QV4::Referenced<QV4::QObjectWrapper> qobjectWrapper = value->asRef<QV4::QObjectWrapper>()) { + QV4::QObjectWrapperRef qobjectWrapper = value; + if (!!qobjectWrapper) { if (QQuickPath *path = qobject_cast<QQuickPath*>(qobjectWrapper->object())) r->context->m_path = path->path(); } else { @@ -2980,7 +2986,8 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawImage(QV4::CallContext pixmap = r->context->createPixmap(url); } else if (arg->isObject()) { - if (QV4::Referenced<QV4::QObjectWrapper> qobjectWrapper = arg->asRef<QV4::QObjectWrapper>()) { + QV4::QObjectWrapperRef qobjectWrapper = arg; + if (!!qobjectWrapper) { if (QQuickImage *imageItem = qobject_cast<QQuickImage*>(qobjectWrapper->object())) { pixmap = r->context->createPixmap(imageItem->source()); } else if (QQuickCanvasItem *canvas = qobject_cast<QQuickCanvasItem*>(qobjectWrapper->object())) { @@ -2990,19 +2997,22 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawImage(QV4::CallContext } else { V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); } - } else if (QV4::Referenced<QQuickJSContext2DImageData> imageData = arg->asRef<QQuickJSContext2DImageData>()) { - QV4::Scoped<QQuickJSContext2DPixelData> pix(scope, imageData->pixelData.as<QQuickJSContext2DPixelData>()); - if (pix && !pix->image.isNull()) { - pixmap.take(new QQuickCanvasPixmap(pix->image)); + } else { + QQuickJSContext2DImageDataRef imageData = arg; + if (!!imageData) { + QV4::Scoped<QQuickJSContext2DPixelData> pix(scope, imageData->pixelData.as<QQuickJSContext2DPixelData>()); + if (pix && !pix->image.isNull()) { + pixmap.take(new QQuickCanvasPixmap(pix->image)); + } else { + V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); + } } else { - V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); + QUrl url(arg->toQStringNoThrow()); + if (url.isValid()) + pixmap = r->context->createPixmap(url); + else + V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); } - } else { - QUrl url(arg->toQStringNoThrow()); - if (url.isValid()) - pixmap = r->context->createPixmap(url); - else - V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); } } else { V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); @@ -3260,7 +3270,8 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createImageData(QV4::CallC if (ctx->callData->argc == 1) { QV4::ScopedValue arg0(scope, ctx->callData->args[0]); - if (QV4::Referenced<QQuickJSContext2DImageData> imgData = arg0->asRef<QQuickJSContext2DImageData>()) { + QQuickJSContext2DImageDataRef imgData = arg0; + if (!!imgData) { QV4::Scoped<QQuickJSContext2DPixelData> pa(scope, imgData->pixelData.as<QQuickJSContext2DPixelData>()); if (pa) { qreal w = pa->image.width(); @@ -3337,7 +3348,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_putImageData(QV4::CallCont if (!qIsFinite(dx) || !qIsFinite(dy)) V4THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments"); - QV4::Referenced<QQuickJSContext2DImageData> imageData = arg0->asRef<QQuickJSContext2DImageData>(); + QQuickJSContext2DImageDataRef imageData = arg0; if (!imageData) return ctx->callData->thisObject.asReturnedValue(); diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h index 6399da3dee..4390ae62cc 100644 --- a/src/quick/items/context2d/qquickcontext2d_p.h +++ b/src/quick/items/context2d/qquickcontext2d_p.h @@ -55,7 +55,7 @@ #include <private/qv8engine_p.h> #include <QtCore/QWaitCondition> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> //#define QQUICKCONTEXT2D_DEBUG //enable this for just DEBUG purpose! diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp index 3c00a7d62d..b5cc6ea3ef 100644 --- a/src/quick/items/qquickaccessibleattached.cpp +++ b/src/quick/items/qquickaccessibleattached.cpp @@ -139,6 +139,57 @@ QT_BEGIN_NAMESPACE \endtable */ +/*! \qmlproperty bool focusable + \brief This property holds whether this item is focusable. + + By default, this property is false except for items where the role is one of + CheckBox, RadioButton, Button, MenuItem, PageTab, EditableText, SpinBox, ComboBox, + Terminal or ScrollBar. +*/ +/*! \qmlproperty bool focused + \brief This property holds whether this item currently has the active focus. + + By default, this property is false, but it will return true for items that + have \l QQuickItem::hasActiveFocus() returning true. +*/ +/*! \qmlproperty bool checkable + \brief This property holds whether this item is checkable (like a check box or some buttons). +*/ +/*! \qmlproperty bool checked + \brief This property holds whether this item is currently checked. +*/ +/*! \qmlproperty bool editable + \brief This property holds whether this item has editable text. +*/ +/*! \qmlproperty bool multiLine + \brief This property holds whether this item has multiple text lines. +*/ +/*! \qmlproperty bool readOnly + \brief This property holds whether this item while being of type \l QAccessible::EditableText + is set to read-only. +*/ +/*! \qmlproperty bool selected + \brief This property holds whether this item is selected. +*/ +/*! \qmlproperty bool selectable + \brief This property holds whether this item can be selected. +*/ +/*! \qmlproperty bool pressed + \brief This property holds whether this item is pressed (for example a button during a mouse click). +*/ +/*! \qmlproperty bool checkStateMixed + \brief This property holds whether this item is in the partially checked state. +*/ +/*! \qmlproperty bool defaultButton + \brief This property holds whether this item is the default button of a dialog. +*/ +/*! \qmlproperty bool passwordEdit + \brief This property holds whether this item is a password text edit. +*/ +/*! \qmlproperty bool selectableText + \brief This property holds whether this item contains selectable text. +*/ + QQuickAccessibleAttached::QQuickAccessibleAttached(QObject *parent) : QObject(parent), m_role(QAccessible::NoRole) { diff --git a/src/quick/items/qquickaccessibleattached_p.h b/src/quick/items/qquickaccessibleattached_p.h index 298c473cf8..1f2c6bf532 100644 --- a/src/quick/items/qquickaccessibleattached_p.h +++ b/src/quick/items/qquickaccessibleattached_p.h @@ -55,15 +55,46 @@ QT_BEGIN_NAMESPACE +#define STATE_PROPERTY(P) \ + Q_PROPERTY(bool P READ P WRITE set_ ## P NOTIFY P ## Changed FINAL) \ + bool P() const { return m_state.P ; } \ + void set_ ## P(bool arg) \ + { \ + if (m_state.P == arg) \ + return; \ + m_state.P = arg; \ + emit P ## Changed(arg); \ + QAccessible::State changedState; \ + changedState.P = true; \ + QAccessibleStateChangeEvent ev(parent(), changedState); \ + QAccessible::updateAccessibility(&ev); \ + } \ + Q_SIGNAL void P ## Changed(bool arg); + + class Q_QUICK_PRIVATE_EXPORT QQuickAccessibleAttached : public QObject { Q_OBJECT - Q_PROPERTY(QAccessible::Role role READ role WRITE setRole NOTIFY roleChanged) - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged) + Q_PROPERTY(QAccessible::Role role READ role WRITE setRole NOTIFY roleChanged FINAL) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL) + Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged FINAL) public: - Q_ENUMS(QAccessible::Role QAccessible::Event QAccessible::State) + Q_ENUMS(QAccessible::Role QAccessible::Event) + STATE_PROPERTY(checkable) + STATE_PROPERTY(checked) + STATE_PROPERTY(editable) + STATE_PROPERTY(focusable) + STATE_PROPERTY(focused) + STATE_PROPERTY(multiLine) + STATE_PROPERTY(readOnly) + STATE_PROPERTY(selected) + STATE_PROPERTY(selectable) + STATE_PROPERTY(pressed) + STATE_PROPERTY(checkStateMixed) + STATE_PROPERTY(defaultButton) + STATE_PROPERTY(passwordEdit) + STATE_PROPERTY(selectableText) QQuickAccessibleAttached(QObject *parent); ~QQuickAccessibleAttached(); @@ -76,9 +107,33 @@ public: Q_EMIT roleChanged(); // There is no way to signify role changes at the moment. // QAccessible::updateAccessibility(parent(), 0, QAccessible::); + + switch (role) { + case QAccessible::CheckBox: + case QAccessible::RadioButton: + m_state.focusable = true; + m_state.checkable = true; + break; + case QAccessible::Button: + case QAccessible::MenuItem: + case QAccessible::PageTab: + case QAccessible::EditableText: + case QAccessible::SpinBox: + case QAccessible::ComboBox: + case QAccessible::Terminal: + case QAccessible::ScrollBar: + m_state.focusable = true; + break; + default: + break; + } } } - QString name() const { return m_name; } + QString name() const { + if (m_state.passwordEdit) + return QString(); + return m_name; + } void setName(const QString &name) { if (name != m_name) { m_name = name; @@ -102,12 +157,12 @@ public: // Factory function static QQuickAccessibleAttached *qmlAttachedProperties(QObject *obj); - // Property getter - static QObject *attachedProperties(const QObject *obj) + static QQuickAccessibleAttached *attachedProperties(const QObject *obj) { - return qmlAttachedPropertiesObject<QQuickAccessibleAttached>(obj, false); + return qobject_cast<QQuickAccessibleAttached*>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(obj, false)); } + // Property getter static QVariant property(const QObject *object, const char *propertyName) { if (QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object)) @@ -128,8 +183,8 @@ public: static QObject *findAccessible(QObject *object, QAccessible::Role role = QAccessible::NoRole) { while (object) { - QObject *att = QQuickAccessibleAttached::attachedProperties(object); - if (att && (role == QAccessible::NoRole || att->property("role").toInt() == role)) { + QQuickAccessibleAttached *att = QQuickAccessibleAttached::attachedProperties(object); + if (att && (role == QAccessible::NoRole || att->role() == role)) { break; } object = object->parent(); @@ -137,6 +192,8 @@ public: return object; } + QAccessible::State state() { return m_state; } + public Q_SLOTS: void valueChanged() { QAccessibleValueChangeEvent ev(parent(), parent()->property("value")); @@ -153,7 +210,10 @@ Q_SIGNALS: void descriptionChanged(); private: + QQuickItem *item() const { return static_cast<QQuickItem*>(parent()); } + QAccessible::Role m_role; + QAccessible::State m_state; QString m_name; QString m_description; diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index c666af2729..2b08cc2598 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE class QQuickItem; class QQuickTransformPrivate; -class QQuickTransform : public QObject +class Q_QUICK_EXPORT QQuickTransform : public QObject { Q_OBJECT public: diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 96746223ce..a52ccf4832 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -269,6 +269,9 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickText, 2>(uri, 2, 2, "Text"); qmlRegisterType<QQuickTextEdit, 2>(uri, 2, 2, "TextEdit"); + + qmlRegisterType<QQuickText, 3>(uri, 2, 3, "Text"); + qmlRegisterType<QQuickTextEdit, 3>(uri, 2, 3, "TextEdit"); } static void initResources() diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h index 32c271222d..5783418619 100644 --- a/src/quick/items/qquickloader_p_p.h +++ b/src/quick/items/qquickloader_p_p.h @@ -58,7 +58,7 @@ #include "qquickitemchangelistener_p.h" #include <qqmlincubator.h> -#include <private/qv4value_p.h> +#include <private/qv4value_inl_p.h> QT_BEGIN_NAMESPACE diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp index 410f18e767..54c7527eec 100644 --- a/src/quick/items/qquickscreen.cpp +++ b/src/quick/items/qquickscreen.cpp @@ -323,7 +323,7 @@ void QQuickScreenAttached::screenChanged(QScreen *screen) this, SIGNAL(orientationChanged())); connect(screen, SIGNAL(primaryOrientationChanged(Qt::ScreenOrientation)), this, SIGNAL(primaryOrientationChanged())); - connect(screen, SIGNAL(virtualGeometryChanged(const QRect &)), + connect(screen, SIGNAL(virtualGeometryChanged(QRect)), this, SIGNAL(desktopGeometryChanged())); connect(screen, SIGNAL(logicalDotsPerInchChanged(qreal)), this, SIGNAL(logicalPixelDensityChanged())); diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 5b5dd0ef03..ae14a43de0 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -2216,7 +2216,7 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data else node = static_cast<QQuickTextNode *>(oldNode); - node->setUseNativeRenderer(d->renderType == NativeRendering && d->window->devicePixelRatio() <= 1); + node->setUseNativeRenderer(d->renderType == NativeRendering); node->deleteContent(); node->setMatrix(QMatrix4x4()); @@ -2569,7 +2569,7 @@ bool QQuickTextPrivate::isLinkHoveredConnected() text. The link must be in rich text or HTML format and the \a link string provides access to the particular link. - \sa hoveredLink + \sa hoveredLink, linkAt() */ /*! @@ -2580,7 +2580,7 @@ bool QQuickTextPrivate::isLinkHoveredConnected() embedded in the text. The link must be in rich text or HTML format and the \a hoveredLink string provides access to the particular link. - \sa onLinkHovered + \sa onLinkHovered, linkAt() */ QString QQuickText::hoveredLink() const @@ -2649,9 +2649,6 @@ void QQuickText::hoverLeaveEvent(QHoverEvent *event) not require advanced features such as transformation of the text. Using such features in combination with the NativeRendering render type will lend poor and sometimes pixelated results. - - On HighDpi "retina" displays and mobile and embedded platforms, this property is ignored - and QtRendering is always used. */ QQuickText::RenderType QQuickText::renderType() const { @@ -2683,4 +2680,19 @@ void QQuickText::doLayout() d->updateSize(); } +/*! + \qmlmethod QtQuick::Text::linkAt(real x, real y) + \since 5.3 + + Returns the link string at point \a x, \a y in content coordinates, + or an empty string if no link exists at that point. + + \sa hoveredLink +*/ +QString QQuickText::linkAt(qreal x, qreal y) const +{ + Q_D(const QQuickText); + return d->anchorAt(QPointF(x, y)); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h index d55dc7e287..44bd5aa9b0 100644 --- a/src/quick/items/qquicktext_p.h +++ b/src/quick/items/qquicktext_p.h @@ -210,6 +210,8 @@ public: QString hoveredLink() const; + Q_REVISION(3) Q_INVOKABLE QString linkAt(qreal x, qreal y) const; + Q_SIGNALS: void textChanged(const QString &text); void linkActivated(const QString &link); diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index c342c79ce5..810a47cfa8 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -418,9 +418,6 @@ void QQuickTextEdit::setTextFormat(TextFormat format) not require advanced features such as transformation of the text. Using such features in combination with the NativeRendering render type will lend poor and sometimes pixelated results. - - On HighDpi "retina" displays and mobile and embedded platforms, this property is ignored - and QtRendering is always used. */ QQuickTextEdit::RenderType QQuickTextEdit::renderType() const { @@ -2371,7 +2368,7 @@ QQuickTextNode *QQuickTextEditPrivate::createTextNode() { Q_Q(QQuickTextEdit); QQuickTextNode* node = new QQuickTextNode(q); - node->setUseNativeRenderer(renderType == QQuickTextEdit::NativeRendering && window->devicePixelRatio() <= 1); + node->setUseNativeRenderer(renderType == QQuickTextEdit::NativeRendering); node->initEngine(color, selectedTextColor, selectionColor); return node; } @@ -2518,7 +2515,7 @@ bool QQuickTextEditPrivate::isLinkHoveredConnected() The link must be in rich text or HTML format and the \a link string provides access to the particular link. - \sa hoveredLink + \sa hoveredLink, linkAt() */ /*! @@ -2529,7 +2526,7 @@ bool QQuickTextEditPrivate::isLinkHoveredConnected() embedded in the text. The link must be in rich text or HTML format and the link string provides access to the particular link. - \sa onLinkHovered + \sa onLinkHovered, linkAt() */ QString QQuickTextEdit::hoveredLink() const @@ -2602,4 +2599,19 @@ void QQuickTextEdit::append(const QString &text) d->control->updateCursorRectangle(false); } +/*! + \qmlmethod QtQuick::TextEdit::linkAt(real x, real y) + \since 5.3 + + Returns the link string at point \a x, \a y in content coordinates, + or an empty string if no link exists at that point. + + \sa hoveredLink +*/ +QString QQuickTextEdit::linkAt(qreal x, qreal y) const +{ + Q_D(const QQuickTextEdit); + return d->control->anchorAt(QPointF(x, y)); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index 8057f76be5..b84552d255 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -258,6 +258,8 @@ public: QString hoveredLink() const; + Q_REVISION(3) Q_INVOKABLE QString linkAt(qreal x, qreal y) const; + Q_SIGNALS: void textChanged(); void contentSizeChanged(); diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 1dd1dfa57e..9f155f9cf7 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -155,9 +155,6 @@ void QQuickTextInput::setText(const QString &s) not require advanced features such as transformation of the text. Using such features in combination with the NativeRendering render type will lend poor and sometimes pixelated results. - - On HighDpi "retina" displays and mobile and embedded platforms, this property is ignored - and QtRendering is always used. */ QQuickTextInput::RenderType QQuickTextInput::renderType() const { @@ -1862,7 +1859,7 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData } } } else { - node->setUseNativeRenderer(d->renderType == NativeRendering && d->window->devicePixelRatio() <= 1); + node->setUseNativeRenderer(d->renderType == NativeRendering); node->deleteContent(); node->setMatrix(QMatrix4x4()); diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp index 18ee1a479d..1133636a74 100644 --- a/src/quick/items/qquicktextnode.cpp +++ b/src/quick/items/qquicktextnode.cpp @@ -144,11 +144,9 @@ QSGGlyphNode *QQuickTextNode::addGlyphs(const QPointF &position, const QGlyphRun { QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext(); QRawFont font = glyphs.rawFont(); - bool smoothScalable = QFontDatabase().isSmoothlyScalable(font.familyName(), - font.styleName()); - QSGGlyphNode *node = m_useNativeRenderer || !smoothScalable - ? sg->sceneGraphContext()->createNativeGlyphNode(sg) - : sg->sceneGraphContext()->createGlyphNode(sg); + bool smoothScalable = QFontDatabase().isSmoothlyScalable(font.familyName(), font.styleName()); + bool preferNativeGlyphNode = m_useNativeRenderer || !smoothScalable; + QSGGlyphNode *node = sg->sceneGraphContext()->createGlyphNode(sg, preferNativeGlyphNode); node->setOwnerElement(m_ownerElement); node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index a311971266..6778cf13e9 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -344,6 +344,7 @@ void QQuickWindowPrivate::syncSceneGraph() mode |= QSGRenderer::ClearColorBuffer; renderer->setClearMode(mode); + renderer->setCustomRenderMode(customRenderMode); context->endSync(); } @@ -418,6 +419,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c) contentItemPrivate->windowRefCount = 1; contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope; + customRenderMode = qgetenv("QSG_VISUALIZE"); windowManager = QSGRenderLoop::instance(); QSGContext *sg = windowManager->sceneGraphContext(); context = windowManager->createRenderContext(sg); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index d5c7b5d64c..944a320dce 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -57,6 +57,7 @@ #include "qquickwindow.h" #include <QtQuick/private/qsgcontext_p.h> +#include <private/qsgbatchrenderer_p.h> #include <QtCore/qthread.h> #include <QtCore/qmutex.h> @@ -200,6 +201,7 @@ public: QSGRenderContext *context; QSGRenderer *renderer; + QByteArray customRenderMode; // Default renderer supports "clip", "overdraw", "changes", "batches" and blank. QSGRenderLoop *windowManager; QQuickAnimatorController *animationController; diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 1a9669f9ab..9de7bb8667 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -42,6 +42,10 @@ #include "qsgbatchrenderer_p.h" #include <private/qsgshadersourcebuilder_p.h> +#include <QQuickWindow> + +#include <qmath.h> + #include <QtCore/QElapsedTimer> #include <QtGui/QGuiApplication> @@ -106,7 +110,7 @@ bool qsg_sort_batch_is_valid(Batch *a, Batch *b) { return a->first && !b->first; bool qsg_sort_batch_increasing_order(Batch *a, Batch *b) { return a->first->order < b->first->order; } bool qsg_sort_batch_decreasing_order(Batch *a, Batch *b) { return a->first->order > b->first->order; } -QSGMaterial::Flag QSGMaterial_RequiresFullMatrixBit = (QSGMaterial::Flag) (QSGMaterial::RequiresFullMatrix & ~QSGMaterial::RequiresFullMatrixExceptTranslate); +QSGMaterial::Flag QSGMaterial_FullMatrix = (QSGMaterial::Flag) (QSGMaterial::RequiresFullMatrix & ~QSGMaterial::RequiresFullMatrixExceptTranslate); struct QMatrix4x4_Accessor { @@ -305,6 +309,9 @@ void Updater::updateStates(QSGNode *n) qDebug() << " - forceupdate"; } + if (Q_UNLIKELY(renderer->m_visualizeMode == Renderer::VisualizeChanges)) + renderer->visualizeChangesPrepare(sn); + visitNode(sn); } @@ -760,6 +767,7 @@ Renderer::Renderer(QSGRenderContext *ctx) , m_currentClip(0) , m_currentClipType(NoClip) , m_vao(0) + , m_visualizeMode(VisualizeNothing) { setNodeUpdater(new Updater(this)); @@ -1668,7 +1676,7 @@ void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData, *indexCount += iCount; } -const QMatrix4x4 &Renderer::matrixForRoot(Node *node) +static QMatrix4x4 qsg_matrixForRoot(Node *node) { if (node->type() == QSGNode::TransformNodeType) return static_cast<QSGTransformNode *>(node->sgNode)->combinedMatrix(); @@ -1701,14 +1709,12 @@ void Renderer::uploadBatch(Batch *b) QSGGeometryNode *gn = b->first->node; QSGGeometry *g = gn->geometry(); - + QSGMaterial::Flags flags = gn->activeMaterial()->flags(); bool canMerge = (g->drawingMode() == GL_TRIANGLES || g->drawingMode() == GL_TRIANGLE_STRIP) && b->positionAttribute >= 0 && g->indexType() == GL_UNSIGNED_SHORT - && (gn->activeMaterial()->flags() & QSGMaterial::CustomCompileStep) == 0 - && (((gn->activeMaterial()->flags() & QSGMaterial::RequiresDeterminant) == 0) - || (((gn->activeMaterial()->flags() & QSGMaterial_RequiresFullMatrixBit) == 0) && b->isTranslateOnlyToRoot()) - ) + && (flags & (QSGMaterial::CustomCompileStep | QSGMaterial_FullMatrix)) == 0 + && ((flags & QSGMaterial::RequiresFullMatrixExceptTranslate) == 0 || b->isTranslateOnlyToRoot()) && b->isSafeToBatch(); b->merged = canMerge; @@ -2004,7 +2010,7 @@ void Renderer::renderMergedBatch(const Batch *batch) // We always have dirty matrix as all batches are at a unique z range. QSGMaterialShader::RenderState::DirtyStates dirty = QSGMaterialShader::RenderState::DirtyMatrix; if (batch->root) - m_current_model_view_matrix = matrixForRoot(batch->root); + m_current_model_view_matrix = qsg_matrixForRoot(batch->root); else m_current_model_view_matrix.setToIdentity(); m_current_determinant = m_current_model_view_matrix.determinant(); @@ -2133,7 +2139,7 @@ void Renderer::renderUnmergedBatch(const Batch *batch) char *iOffset = indexBase + batch->vertexCount * gn->geometry()->sizeOfVertex(); #endif - QMatrix4x4 rootMatrix = batch->root ? matrixForRoot(batch->root) : QMatrix4x4(); + QMatrix4x4 rootMatrix = batch->root ? qsg_matrixForRoot(batch->root) : QMatrix4x4(); while (e) { gn = e->node; @@ -2358,7 +2364,6 @@ void Renderer::render() cleanupBatches(&m_opaqueBatches); cleanupBatches(&m_alphaBatches); - if (m_rebuild & BuildBatches) { prepareOpaqueBatches(); prepareAlphaBatches(); @@ -2408,6 +2413,9 @@ void Renderer::render() m_rebuild = 0; + if (m_visualizeMode != VisualizeNothing) + visualize(); + if (m_vao) m_vao->release(); } @@ -2446,7 +2454,7 @@ void Renderer::renderRenderNode(Batch *batch) QMatrix4x4 matrix; while (xform != rootNode()) { if (xform->type() == QSGNode::TransformNodeType) { - matrix = matrixForRoot(e->root) * static_cast<QSGTransformNode *>(xform)->combinedMatrix(); + matrix = qsg_matrixForRoot(e->root) * static_cast<QSGTransformNode *>(xform)->combinedMatrix(); break; } xform = xform->parent(); @@ -2510,6 +2518,312 @@ void Renderer::renderRenderNode(Batch *batch) } +class VisualizeShader : public QOpenGLShaderProgram +{ +public: + int color; + int matrix; + int rotation; + int tweak; +}; + +void Renderer::visualizeDrawGeometry(const QSGGeometry *g) +{ + if (g->attributeCount() < 1) + return; + const QSGGeometry::Attribute *a = g->attributes(); + glVertexAttribPointer(0, a->tupleSize, a->type, false, g->sizeOfVertex(), g->vertexData()); + if (g->indexCount()) + glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData()); + else + glDrawArrays(g->drawingMode(), 0, g->vertexCount()); + +} + +void Renderer::visualizeBatch(Batch *b) +{ + VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram); + + if (b->positionAttribute != 0) + return; + + QSGGeometryNode *gn = b->first->node; + QSGGeometry *g = gn->geometry(); + const QSGGeometry::Attribute &a = g->attributes()[b->positionAttribute]; + + glBindBuffer(GL_ARRAY_BUFFER, b->vbo.id); + + QMatrix4x4 matrix(m_current_projection_matrix); + if (b->root) + matrix = matrix * qsg_matrixForRoot(b->root); + + QRect viewport = viewportRect(); + shader->setUniformValue(shader->tweak, viewport.width(), viewport.height(), b->merged ? 0 : 1, 0); + + QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0); + float cr = color.redF(); + float cg = color.greenF(); + float cb = color.blueF(); + shader->setUniformValue(shader->color, cr, cg, cb, 1.0); + + if (b->merged) { + shader->setUniformValue(shader->matrix, matrix); + for (int ds=0; ds<b->drawSets.size(); ++ds) { + const DrawSet &set = b->drawSets.at(ds); + glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(), (void *) (qintptr) (set.vertices)); + glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (b->vbo.data + set.indices)); + } + } else { + Element *e = b->first; + int offset = 0; + while (e) { + gn = e->node; + g = gn->geometry(); + shader->setUniformValue(shader->matrix, matrix * *gn->matrix()); + glVertexAttribPointer(a.position, a.tupleSize, a.type, false, g->sizeOfVertex(), (void *) (qintptr) offset); + glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData()); + offset += g->sizeOfVertex() * g->vertexCount(); + e = e->nextInBatch; + } + } +} + + + + +void Renderer::visualizeClipping(QSGNode *node) +{ + if (node->type() == QSGNode::ClipNodeType) { + VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram); + QSGClipNode *clipNode = static_cast<QSGClipNode *>(node); + QMatrix4x4 matrix = m_current_projection_matrix; + if (clipNode->matrix()) + matrix = matrix * *clipNode->matrix(); + shader->setUniformValue(shader->matrix, matrix); + visualizeDrawGeometry(clipNode->geometry()); + } + + QSGNODE_TRAVERSE(node) { + visualizeClipping(child); + } +} + +#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \ + | QSGNode::DirtyOpacity \ + | QSGNode::DirtyMatrix \ + | QSGNode::DirtyNodeRemoved) + +void Renderer::visualizeChangesPrepare(Node *n, uint parentChanges) +{ + uint childDirty = (parentChanges | n->dirtyState) & QSGNODE_DIRTY_PARENT; + uint selfDirty = n->dirtyState | parentChanges; + if (n->type() == QSGNode::GeometryNodeType && selfDirty != 0) + m_visualizeChanceSet.insert(n, selfDirty); + SHADOWNODE_TRAVERSE(n) { + visualizeChangesPrepare(*child, childDirty); + } +} + +void Renderer::visualizeChanges(Node *n) +{ + + if (n->type() == QSGNode::GeometryNodeType && m_visualizeChanceSet.contains(n)) { + uint dirty = m_visualizeChanceSet.value(n); + bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0; + + VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram); + QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 0.3, 1.0); + float ca = 0.5; + float cr = color.redF() * ca; + float cg = color.greenF() * ca; + float cb = color.blueF() * ca; + shader->setUniformValue(shader->color, cr, cg, cb, ca); + + QRect viewport = viewportRect(); + shader->setUniformValue(shader->tweak, viewport.width(), viewport.height(), tinted ? 0.5 : 0, 0); + + QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode); + + QMatrix4x4 matrix = m_current_projection_matrix; + if (n->element()->batch->root) + matrix = matrix * qsg_matrixForRoot(n->element()->batch->root); + matrix = matrix * *gn->matrix(); + shader->setUniformValue(shader->matrix, matrix); + visualizeDrawGeometry(gn->geometry()); + + // This is because many changes don't propegate their dirty state to the + // parent so the node updater will not unset these states. They are + // not used for anything so, unsetting it should have no side effects. + n->dirtyState = 0; + } + + SHADOWNODE_TRAVERSE(n) { + visualizeChanges(*child); + } +} + +void Renderer::visualizeOverdraw_helper(Node *node) +{ + if (node->type() == QSGNode::GeometryNodeType) { + VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram); + QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode); + + QMatrix4x4 matrix = m_current_projection_matrix; + matrix(2, 2) = m_zRange; + matrix(2, 3) = 1.0f - node->element()->order * m_zRange; + + if (node->element()->batch->root) + matrix = matrix * qsg_matrixForRoot(node->element()->batch->root); + matrix = matrix * *gn->matrix(); + shader->setUniformValue(shader->matrix, matrix); + + QColor color = node->element()->batch->isOpaque ? QColor::fromRgbF(0.3, 1.0, 0.3) : QColor::fromRgbF(1.0, 0.3, 0.3); + float ca = 0.33; + shader->setUniformValue(shader->color, color.redF() * ca, color.greenF() * ca, color.blueF() * ca, ca); + + visualizeDrawGeometry(gn->geometry()); + } + + SHADOWNODE_TRAVERSE(node) { + visualizeOverdraw_helper(*child); + } +} + +void Renderer::visualizeOverdraw() +{ + VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram); + shader->setUniformValue(shader->color, 0.5, 0.5, 1, 1); + + QRect viewport = viewportRect(); + shader->setUniformValue(shader->tweak, viewport.width(), viewport.height(), 0, 1); + + glBlendFunc(GL_ONE, GL_ONE); + + static float step = 0; + step += M_PI * 2 / 1000.; + if (step > M_PI * 2) + step = 0; + float angle = 80.0 * sin(step); + + QMatrix4x4 xrot; xrot.rotate(20, 1, 0, 0); + QMatrix4x4 zrot; zrot.rotate(angle, 0, 0, 1); + QMatrix4x4 tx; tx.translate(0, 0, 1); + + QMatrix4x4 m; + +// m.rotate(180, 0, 1, 0); + + m.translate(0, 0.5, 4); + m.scale(2, 2, 1); + + m.rotate(-30, 1, 0, 0); + m.rotate(angle, 0, 1, 0); + m.translate(0, 0, -1); + + shader->setUniformValue(shader->rotation, m); + + float box[] = { + // lower + -1, 1, 0, 1, 1, 0, + -1, 1, 0, -1, -1, 0, + 1, 1, 0, 1, -1, 0, + -1, -1, 0, 1, -1, 0, + + // upper + -1, 1, 1, 1, 1, 1, + -1, 1, 1, -1, -1, 1, + 1, 1, 1, 1, -1, 1, + -1, -1, 1, 1, -1, 1, + + // sides + -1, -1, 0, -1, -1, 1, + 1, -1, 0, 1, -1, 1, + -1, 1, 0, -1, 1, 1, + 1, 1, 0, 1, 1, 1 + }; + glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, box); + glLineWidth(2); + glDrawArrays(GL_LINES, 0, 24); + + visualizeOverdraw_helper(m_nodes.value(rootNode())); + + // Animate the view... + QSurface *surface = QOpenGLContext::currentContext()->surface(); + if (surface->surfaceClass() == QSurface::Window) + if (QQuickWindow *window = qobject_cast<QQuickWindow *>(static_cast<QWindow *>(surface))) + window->update(); +} + +void Renderer::setCustomRenderMode(const QByteArray &mode) +{ + if (mode.isEmpty()) m_visualizeMode = VisualizeNothing; + else if (mode == "clip") m_visualizeMode = VisualizeClipping; + else if (mode == "overdraw") m_visualizeMode = VisualizeOverdraw; + else if (mode == "batches") m_visualizeMode = VisualizeBatches; + else if (mode == "changes") m_visualizeMode = VisualizeChanges; +} + +void Renderer::visualize() +{ + if (!m_shaderManager->visualizeProgram) { + VisualizeShader *prog = new VisualizeShader(); + QSGShaderSourceBuilder::initializeProgramFromFiles( + prog, + QStringLiteral(":/scenegraph/shaders/visualization.vert"), + QStringLiteral(":/scenegraph/shaders/visualization.frag")); + prog->bindAttributeLocation("v", 0); + prog->link(); + prog->bind(); + prog->color = prog->uniformLocation("color"); + prog->tweak = prog->uniformLocation("tweak"); + prog->matrix = prog->uniformLocation("matrix"); + prog->rotation = prog->uniformLocation("rotation"); + m_shaderManager->visualizeProgram = prog; + } else { + m_shaderManager->visualizeProgram->bind(); + } + VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram); + + glDisable(GL_DEPTH_TEST); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glEnableVertexAttribArray(0); + + // Blacken out the actual rendered content... + float bgOpacity = 0.8; + if (m_visualizeMode == VisualizeBatches) + bgOpacity = 1.0; + float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 }; + shader->setUniformValue(shader->color, 0, 0, 0, bgOpacity); + shader->setUniformValue(shader->matrix, QMatrix4x4()); + shader->setUniformValue(shader->rotation, QMatrix4x4()); + shader->setUniformValue(shader->tweak, 0, 0, 0, 0); + glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, v); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + if (m_visualizeMode == VisualizeBatches) { + srand(0); // To force random colors to be roughly the same every time.. + for (int i=0; i<m_opaqueBatches.size(); ++i) visualizeBatch(m_opaqueBatches.at(i)); + for (int i=0; i<m_alphaBatches.size(); ++i) visualizeBatch(m_alphaBatches.at(i)); + } else if (m_visualizeMode == VisualizeClipping) { + QRect viewport = viewportRect(); + shader->setUniformValue(shader->tweak, viewport.width(), viewport.height(), 0.5, 0); + shader->setUniformValue(shader->color, 0.2, 0, 0, 0.2); + visualizeClipping(rootNode()); + } else if (m_visualizeMode == VisualizeChanges) { + visualizeChanges(m_nodes.value(rootNode())); + m_visualizeChanceSet.clear(); + } else if (m_visualizeMode == VisualizeOverdraw) { + visualizeOverdraw(); + } + + // Reset state back to defaults.. + glDisable(GL_BLEND); + glDisableVertexAttribArray(0); + shader->release(); +} + QT_END_NAMESPACE } diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h index 0aa84da185..d22ab4069e 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h @@ -368,7 +368,7 @@ public: float lastOpacity; }; - ShaderManager() : blitProgram(0) { } + ShaderManager() : blitProgram(0), visualizeProgram(0) { } ~ShaderManager() { qDeleteAll(rewrittenShaders.values()); qDeleteAll(stockShaders.values()); @@ -385,6 +385,7 @@ public: QHash<QSGMaterialType *, Shader *> stockShaders; QOpenGLShaderProgram *blitProgram; + QOpenGLShaderProgram *visualizeProgram; }; class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer @@ -393,6 +394,14 @@ public: Renderer(QSGRenderContext *); ~Renderer(); + enum VisualizeMode { + VisualizeNothing, + VisualizeBatches, + VisualizeClipping, + VisualizeChanges, + VisualizeOverdraw + }; + protected: void nodeChanged(QSGNode *node, QSGNode::DirtyState state); void preprocess() Q_DECL_OVERRIDE; @@ -448,6 +457,16 @@ private: inline Batch *newBatch(); void invalidateAndRecycleBatch(Batch *b); + void visualize(); + void visualizeBatch(Batch *b); + void visualizeClipping(QSGNode *node); + void visualizeChangesPrepare(Node *n, uint parentChanges = 0); + void visualizeChanges(Node *n); + void visualizeOverdraw(); + void visualizeOverdraw_helper(Node *node); + void visualizeDrawGeometry(const QSGGeometry *g); + void setCustomRenderMode(const QByteArray &mode); + QSet<Node *> m_taggedRoots; QDataBuffer<Element *> m_opaqueRenderList; QDataBuffer<Element *> m_alphaRenderList; @@ -484,6 +503,9 @@ private: // For minimal OpenGL core profile support QOpenGLVertexArrayObject *m_vao; + + QHash<Node *, uint> m_visualizeChanceSet; + VisualizeMode m_visualizeMode; }; Batch *Renderer::newBatch() diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h index 43811e6d5e..296d6e2cfd 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h @@ -132,6 +132,8 @@ public: void setClearMode(ClearMode mode) { m_clear_mode = mode; } ClearMode clearMode() const { return m_clear_mode; } + virtual void setCustomRenderMode(const QByteArray &) { }; + Q_SIGNALS: void sceneGraphChanged(); // Add, remove, ChangeFlags changes... diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index 829d33a0d7..202ae91ac3 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -253,32 +253,14 @@ QSGImageNode *QSGContext::createImageNode() } /*! - Factory function for scene graph backends of the Text elements which supports native - text rendering. Used in special cases where native look and feel is a main objective. -*/ -QSGGlyphNode *QSGContext::createNativeGlyphNode(QSGRenderContext *rc) -{ -#if defined(QT_OPENGL_ES) && !defined(QT_OPENGL_ES_2_ANGLE) - Q_D(QSGContext); - if (d->distanceFieldDisabled) - return new QSGDefaultGlyphNode; - else - return createGlyphNode(rc); -#else - Q_UNUSED(rc); - return new QSGDefaultGlyphNode; -#endif -} - -/*! Factory function for scene graph backends of the Text elements; */ -QSGGlyphNode *QSGContext::createGlyphNode(QSGRenderContext *rc) +QSGGlyphNode *QSGContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) { Q_D(QSGContext); - if (d->distanceFieldDisabled) { - return createNativeGlyphNode(rc); + if (d->distanceFieldDisabled || preferNativeGlyphNode) { + return new QSGDefaultGlyphNode; } else { QSGDistanceFieldGlyphNode *node = new QSGDistanceFieldGlyphNode(rc); node->setPreferredAntialiasingMode(d->distanceFieldAntialiasing); diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index c1bf78a018..883287e35d 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -158,8 +158,7 @@ public: virtual QSGRectangleNode *createRectangleNode(); virtual QSGImageNode *createImageNode(); - virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc); - virtual QSGGlyphNode *createNativeGlyphNode(QSGRenderContext *rc); + virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode); virtual QAnimationDriver *createAnimationDriver(QObject *parent); virtual QSize minimumFBOSize() const; diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index 6868e10b90..570d6b92b5 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -160,5 +160,7 @@ OTHER_FILES += \ $$PWD/shaders/textmask_core.vert \ $$PWD/shaders/texture_core.frag \ $$PWD/shaders/vertexcolor_core.frag \ - $$PWD/shaders/vertexcolor_core.vert + $$PWD/shaders/vertexcolor_core.vert \ + scenegraph/shaders/visualization.frag \ + scenegraph/shaders/visualization.vert diff --git a/src/quick/scenegraph/scenegraph.qrc b/src/quick/scenegraph/scenegraph.qrc index 2be8b246d3..e6a90c9120 100644 --- a/src/quick/scenegraph/scenegraph.qrc +++ b/src/quick/scenegraph/scenegraph.qrc @@ -64,5 +64,7 @@ <file>shaders/texture_core.frag</file> <file>shaders/vertexcolor_core.frag</file> <file>shaders/vertexcolor_core.vert</file> + <file>shaders/visualization.vert</file> + <file>shaders/visualization.frag</file> </qresource> </RCC> diff --git a/src/quick/scenegraph/shaders/visualization.frag b/src/quick/scenegraph/shaders/visualization.frag new file mode 100644 index 0000000000..205b726c03 --- /dev/null +++ b/src/quick/scenegraph/shaders/visualization.frag @@ -0,0 +1,11 @@ +uniform lowp vec4 color; +uniform mediump vec4 tweak; // x,y -> width, height; z -> intensity of ; + +varying mediump vec2 pos; + +void main(void) +{ + lowp vec4 c = color; + c.xyz += pow(max(sin(pos.x + pos.y), 0.0), 2.0) * tweak.z * 0.1; + gl_FragColor = c; +} diff --git a/src/quick/scenegraph/shaders/visualization.vert b/src/quick/scenegraph/shaders/visualization.vert new file mode 100644 index 0000000000..f1892b71da --- /dev/null +++ b/src/quick/scenegraph/shaders/visualization.vert @@ -0,0 +1,22 @@ +attribute highp vec4 v; +uniform highp mat4 matrix; +uniform highp mat4 rotation; + +// w -> apply 3d rotation and projection +uniform lowp vec4 tweak; + +varying mediump vec2 pos; + +void main() +{ + vec4 p = matrix * v; + + if (tweak.w > 0.0) { + vec4 proj = rotation * p; + gl_Position = vec4(proj.x, proj.y, 0, proj.z); + } else { + gl_Position = p; + } + + pos = v.xy * 1.37; +} diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp index d080c59198..e6a2096c80 100644 --- a/src/quick/scenegraph/util/qsgatlastexture.cpp +++ b/src/quick/scenegraph/util/qsgatlastexture.cpp @@ -148,9 +148,10 @@ Atlas::Atlas(const QSize &size) #if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_NO_SDK) QString *deviceName = static_cast<QString *>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("AndroidDeviceName")); - static bool wrongfullyReportsBgra8888Support = deviceName->compare(QStringLiteral("samsung SM-T211"), Qt::CaseInsensitive) == 0 - || deviceName->compare(QStringLiteral("samsung SM-T210"), Qt::CaseInsensitive) == 0 - || deviceName->compare(QStringLiteral("samsung SM-T215"), Qt::CaseInsensitive) == 0; + static bool wrongfullyReportsBgra8888Support = deviceName != 0 + && (deviceName->compare(QStringLiteral("samsung SM-T211"), Qt::CaseInsensitive) == 0 + || deviceName->compare(QStringLiteral("samsung SM-T210"), Qt::CaseInsensitive) == 0 + || deviceName->compare(QStringLiteral("samsung SM-T215"), Qt::CaseInsensitive) == 0); #else static bool wrongfullyReportsBgra8888Support = false; #endif diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index ae1bec3f42..b738896a6c 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -684,9 +684,10 @@ void QSGPlainTexture::bind() #if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_NO_SDK) QString *deviceName = static_cast<QString *>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("AndroidDeviceName")); - static bool wrongfullyReportsBgra8888Support = deviceName->compare(QStringLiteral("samsung SM-T211"), Qt::CaseInsensitive) == 0 - || deviceName->compare(QStringLiteral("samsung SM-T210"), Qt::CaseInsensitive) == 0 - || deviceName->compare(QStringLiteral("samsung SM-T215"), Qt::CaseInsensitive) == 0; + static bool wrongfullyReportsBgra8888Support = deviceName != 0 + && (deviceName->compare(QStringLiteral("samsung SM-T211"), Qt::CaseInsensitive) == 0 + || deviceName->compare(QStringLiteral("samsung SM-T210"), Qt::CaseInsensitive) == 0 + || deviceName->compare(QStringLiteral("samsung SM-T215"), Qt::CaseInsensitive) == 0); #else static bool wrongfullyReportsBgra8888Support = false; #endif diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp index fa6f615649..3bc4cef5b9 100644 --- a/src/quick/util/qquickanimatorjob.cpp +++ b/src/quick/util/qquickanimatorjob.cpp @@ -271,7 +271,7 @@ void QQuickTransformAnimatorJob::initialize(QQuickAnimatorController *controller m_helper = new Helper(); m_helper->item = m_target; m_controller->m_transforms.insert(m_target, m_helper); - QObject::connect(m_target, SIGNAL(destroyed(QObject *)), m_controller, SLOT(itemDestroyed(QObject*)), Qt::DirectConnection); + QObject::connect(m_target, SIGNAL(destroyed(QObject*)), m_controller, SLOT(itemDestroyed(QObject*)), Qt::DirectConnection); } else { ++m_helper->ref; // Make sure leftovers from previous runs are being used... |