diff options
Diffstat (limited to 'src/qml/compiler')
-rw-r--r-- | src/qml/compiler/compiler.pri | 6 | ||||
-rw-r--r-- | src/qml/compiler/qqmlcodegenerator.cpp | 108 | ||||
-rw-r--r-- | src/qml/compiler/qqmlcodegenerator_p.h | 16 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler.cpp | 1204 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler_p.h | 201 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata.cpp | 51 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 8 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_masm.cpp | 13 | ||||
-rw-r--r-- | src/qml/compiler/qv4regalloc.cpp | 3 |
9 files changed, 1548 insertions, 62 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index df4f5e8dc3..b2569f9111 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -15,7 +15,8 @@ HEADERS += \ $$PWD/qv4ssa_p.h \ $$PWD/qv4regalloc_p.h \ $$PWD/qqmlcodegenerator_p.h \ - $$PWD/qv4isel_masm_p.h + $$PWD/qv4isel_masm_p.h \ + $$PWD/qqmltypecompiler_p.h SOURCES += \ $$PWD/qv4compileddata.cpp \ @@ -28,6 +29,7 @@ SOURCES += \ $$PWD/qv4ssa.cpp \ $$PWD/qv4regalloc.cpp \ $$PWD/qqmlcodegenerator.cpp \ - $$PWD/qv4isel_masm.cpp + $$PWD/qv4isel_masm.cpp \ + $$PWD/qqmltypecompiler.cpp include(../../3rdparty/masm/masm.pri) diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp index 13b23fde68..22ae1228c7 100644 --- a/src/qml/compiler/qqmlcodegenerator.cpp +++ b/src/qml/compiler/qqmlcodegenerator.cpp @@ -64,6 +64,21 @@ using namespace QtQml; return false; \ } +void QmlObject::init(MemoryPool *pool, int typeNameIndex, int id, const AST::SourceLocation &loc) +{ + inheritedTypeNameIndex = typeNameIndex; + + location.line = loc.startLine; + location.column = loc.startColumn; + + idIndex = id; + indexOfDefaultProperty = -1; + properties = pool->New<PoolList<QmlProperty> >(); + qmlSignals = pool->New<PoolList<Signal> >(); + bindings = pool->New<PoolList<Binding> >(); + functions = pool->New<PoolList<Function> >(); +} + void QmlObject::dump(DebugStream &out) { out << inheritedTypeNameIndex << " {" << endl; @@ -82,8 +97,9 @@ QStringList Signal::parameterStringList(const QStringList &stringPool) const return result; } -QQmlCodeGenerator::QQmlCodeGenerator() - : _object(0) +QQmlCodeGenerator::QQmlCodeGenerator(const QSet<QString> &illegalNames) + : illegalNames(illegalNames) + , _object(0) , jsGenerator(0) { } @@ -214,7 +230,7 @@ bool QQmlCodeGenerator::visit(AST::UiObjectDefinition *node) bool QQmlCodeGenerator::visit(AST::UiObjectBinding *node) { int idx = defineQMLObject(node->qualifiedTypeNameId, node->initializer); - appendBinding(node->qualifiedId, idx); + appendBinding(node->qualifiedId, idx, node->hasOnToken); return false; } @@ -292,10 +308,8 @@ bool QQmlCodeGenerator::sanityCheckFunctionNames() if (name.at(0).isUpper()) COMPILE_EXCEPTION(function->identifierToken, tr("Method names cannot begin with an upper case letter")); -#if 0 // ### - if (enginePrivate->v8engine()->illegalNames().contains(currSlot.name.toString())) - COMPILE_EXCEPTION(&currSlot, tr("Illegal method name")); -#endif + if (illegalNames.contains(name)) + COMPILE_EXCEPTION(function->identifierToken, tr("Illegal method name")); } return true; } @@ -307,20 +321,10 @@ int QQmlCodeGenerator::defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, const int objectIndex = _objects.size() - 1; qSwap(_object, obj); - _object->inheritedTypeNameIndex = registerString(asString(qualifiedTypeNameId)); - AST::SourceLocation loc; if (qualifiedTypeNameId) loc = qualifiedTypeNameId->firstSourceLocation(); - _object->location.line = loc.startLine; - _object->location.column = loc.startColumn; - - _object->idIndex = emptyStringIndex; - _object->indexOfDefaultProperty = -1; - _object->properties = New<PoolList<QmlProperty> >(); - _object->qmlSignals = New<PoolList<Signal> >(); - _object->bindings = New<PoolList<Binding> >(); - _object->functions = New<PoolList<Function> >(); + _object->init(pool, registerString(asString(qualifiedTypeNameId)), emptyStringIndex, loc); QSet<QString> propertyNames; qSwap(_propertyNames, propertyNames); @@ -589,10 +593,8 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) if (signalName.at(0).isUpper()) COMPILE_EXCEPTION(node->identifierToken, tr("Signal names cannot begin with an upper case letter")); -#if 0 // ### cannot access identifier table from separate thread - if (enginePrivate->v8engine()->illegalNames().contains(currSig.name.toString())) - COMPILE_EXCEPTION(&currSig, tr("Illegal signal name")); -#endif + if (illegalNames.contains(signalName)) + COMPILE_EXCEPTION(node->identifierToken, tr("Illegal signal name")); _object->qmlSignals->append(signal); } else { @@ -739,6 +741,10 @@ bool QQmlCodeGenerator::visit(AST::UiSourceElement *node) if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) { _functions << funDecl; Function *f = New<Function>(); + f->functionDeclaration = funDecl; + AST::SourceLocation loc = funDecl->firstSourceLocation(); + f->location.line = loc.startLine; + f->location.column = loc.startColumn; f->index = _functions.size() - 1; _object->functions->append(f); } else { @@ -844,13 +850,13 @@ void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, AST::Statement * qSwap(_object, object); } -void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, int objectIndex) +void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment) { QmlObject *object = 0; if (!resolveQualifiedId(&name, &object)) return; qSwap(_object, object); - appendBinding(name->identifierToken, registerString(name->name.toString()), objectIndex); + appendBinding(name->identifierToken, registerString(name->name.toString()), objectIndex, /*isListItem*/false, isOnAssignment); qSwap(_object, object); } @@ -873,9 +879,9 @@ void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, i _object->bindings->append(binding); } -void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem) +void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem, bool isOnAssignment) { - if (!sanityCheckPropertyName(nameLocation, propertyNameIndex, isListItem)) + if (!sanityCheckPropertyName(nameLocation, propertyNameIndex, isListItem | isOnAssignment)) return; if (stringAt(propertyNameIndex) == QStringLiteral("id")) { @@ -888,7 +894,16 @@ void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, i binding->location.line = nameLocation.startLine; binding->location.column = nameLocation.startColumn; binding->flags = 0; - binding->type = QV4::CompiledData::Binding::Type_Object; + + // No type name on the initializer means it must be a group property + if (stringAt(_objects.at(objectIndex)->inheritedTypeNameIndex).isEmpty()) + binding->type = QV4::CompiledData::Binding::Type_GroupProperty; + else + binding->type = QV4::CompiledData::Binding::Type_Object; + + if (isOnAssignment) + binding->flags |= QV4::CompiledData::Binding::IsOnAssignment; + binding->value.objectIndex = objectIndex; _object->bindings->append(binding); } @@ -926,12 +941,11 @@ bool QQmlCodeGenerator::setId(AST::Statement *value) COMPILE_EXCEPTION(loc, tr( "IDs must contain only letters, numbers, and underscores")); } -#if 0 // ### - if (enginePrivate->v8engine()->illegalNames().contains(str)) - COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property")); -#endif + QString idQString(str.toString()); + if (illegalNames.contains(idQString)) + COMPILE_EXCEPTION(loc, tr( "ID illegally masks global JavaScript property")); - _object->idIndex = registerString(str.toString()); + _object->idIndex = registerString(idQString); _object->locationOfIdProperty.line = loc.startLine; _object->locationOfIdProperty.column = loc.startColumn; @@ -970,14 +984,14 @@ bool QQmlCodeGenerator::resolveQualifiedId(AST::UiQualifiedId **nameToResolve, Q return true; } -bool QQmlCodeGenerator::sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex, bool isListItem) +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 (!isListItem) { + if (!isListItemOnOrAssignment) { if (_propertyNames.contains(name)) COMPILE_EXCEPTION(nameLocation, tr("Duplicate property name")); @@ -987,13 +1001,8 @@ bool QQmlCodeGenerator::sanityCheckPropertyName(const AST::SourceLocation &nameL if (name.at(0).isUpper()) COMPILE_EXCEPTION(nameLocation, tr("Property names cannot begin with an upper case letter")); -#if 0 // ### how to check against illegalNames when in separate thread? - if (enginePrivate->v8engine()->illegalNames().contains(prop.name.toString())) { - COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line, - prop.nameLocation.column, - tr("Illegal property name")); - } -#endif + if (illegalNames.contains(name)) + COMPILE_EXCEPTION(nameLocation, tr("Illegal property name")); return true; } @@ -1625,11 +1634,16 @@ SignalHandlerConverter::SignalHandlerConverter(QQmlEnginePrivate *enginePrivate, bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations() { - foreach (QmlObject *obj, parsedQML->objects) { + for (int objectIndex = 0; objectIndex < parsedQML->objects.count(); ++objectIndex) { + QmlObject * const obj = parsedQML->objects.at(objectIndex); QString elementName = stringAt(obj->inheritedTypeNameIndex); if (elementName.isEmpty()) continue; - QQmlPropertyCache *cache = unit->resolvedTypes[obj->inheritedTypeNameIndex].createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + QQmlCompiledData::TypeReference &tr = unit->resolvedTypes[obj->inheritedTypeNameIndex]; + if (tr.type && tr.type->customParser()) + continue; + QQmlPropertyCache *cache = unit->propertyCaches.value(objectIndex); + Q_ASSERT(cache); if (!convertSignalHandlerExpressionsToFunctionDeclarations(obj, elementName, cache)) return false; } @@ -1659,10 +1673,6 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio if (!QQmlCodeGenerator::isSignalPropertyName(propertyName)) continue; - if (binding->type != QV4::CompiledData::Binding::Type_Script) { - COMPILE_EXCEPTION(binding->location, tr("Incorrectly specified signal assignment")); - } - PropertyResolver resolver(propertyCache); Q_ASSERT(propertyName.startsWith(QStringLiteral("on"))); @@ -1731,6 +1741,10 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio parameters = entry.value(); } + if (binding->type != QV4::CompiledData::Binding::Type_Script) { + COMPILE_EXCEPTION(binding->location, tr("Incorrectly specified signal assignment")); + } + QQmlJS::Engine &jsEngine = parsedQML->jsParserEngine; QQmlJS::MemoryPool *pool = jsEngine.pool(); diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h index 0a0e4f2d5b..f1050f4476 100644 --- a/src/qml/compiler/qqmlcodegenerator_p.h +++ b/src/qml/compiler/qqmlcodegenerator_p.h @@ -135,6 +135,8 @@ struct Binding : public QV4::CompiledData::Binding struct Function { + AST::FunctionDeclaration *functionDeclaration; + QV4::CompiledData::Location location; int index; // index in parsedQML::functions Function *next; }; @@ -153,6 +155,8 @@ struct QmlObject PoolList<Binding> *bindings; PoolList<Function> *functions; + void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int id, const AST::SourceLocation &location = AST::SourceLocation()); + void dump(DebugStream &out); }; @@ -207,7 +211,7 @@ struct Q_QML_EXPORT QQmlCodeGenerator : public AST::Visitor { Q_DECLARE_TR_FUNCTIONS(QQmlCodeGenerator) public: - QQmlCodeGenerator(); + QQmlCodeGenerator(const QSet<QString> &illegalNames); bool generateFromQml(const QString &code, const QUrl &url, const QString &urlString, ParsedQML *output); static bool isSignalPropertyName(const QString &name); @@ -253,9 +257,9 @@ public: void setBindingValue(QV4::CompiledData::Binding *binding, AST::Statement *statement); void appendBinding(AST::UiQualifiedId *name, AST::Statement *value); - void appendBinding(AST::UiQualifiedId *name, int objectIndex); + 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); + void appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false); bool setId(AST::Statement *value); @@ -263,7 +267,7 @@ public: // 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 isListItem = false); + bool sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex, bool isListItemOnOrAssignment = false); void recordError(const AST::SourceLocation &location, const QString &description); @@ -272,7 +276,7 @@ public: static QQmlScript::LocationSpan location(AST::SourceLocation start, AST::SourceLocation end); int registerString(const QString &str) const { return jsGenerator->registerString(str); } - template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } + template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); } QString stringAt(int index) const { return jsGenerator->strings.at(index); } @@ -280,6 +284,8 @@ public: QList<QQmlError> errors; + QSet<QString> illegalNames; + QList<QV4::CompiledData::Import*> _imports; QList<Pragma*> _pragmas; QList<QmlObject*> _objects; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp new file mode 100644 index 0000000000..d6dde63d4b --- /dev/null +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -0,0 +1,1204 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 "qqmltypecompiler_p.h" + +#include <private/qqmlcompiler_p.h> +#include <private/qqmlobjectcreator_p.h> +#include <private/qqmlcustomparser_p.h> +#include <private/qqmlvmemetaobject_p.h> +#include <private/qqmlcomponent_p.h> + +#define COMPILE_EXCEPTION(token, desc) \ + { \ + recordError((token)->location, desc); \ + return false; \ + } + +QT_BEGIN_NAMESPACE + +QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlCompiledData *compiledData, QQmlTypeData *typeData, QtQml::ParsedQML *parsedQML) + : engine(engine) + , compiledData(compiledData) + , typeData(typeData) + , parsedQML(parsedQML) +{ +} + +bool QQmlTypeCompiler::compile() +{ + compiledData->importCache = new QQmlTypeNameCache; + + foreach (const QString &ns, typeData->namespaces()) + compiledData->importCache->add(ns); + + // Add any Composite Singletons that were used to the import cache + foreach (const QQmlTypeData::TypeReference &singleton, typeData->compositeSingletons()) + compiledData->importCache->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); + + typeData->imports().populateCache(compiledData->importCache); + compiledData->importCache->addref(); + + const QHash<int, QQmlTypeData::TypeReference> &resolvedTypes = typeData->resolvedTypeRefs(); + for (QHash<int, QQmlTypeData::TypeReference>::ConstIterator resolvedType = resolvedTypes.constBegin(), end = resolvedTypes.constEnd(); + resolvedType != end; ++resolvedType) { + QQmlCompiledData::TypeReference ref; + if (resolvedType->typeData) { + ref.component = resolvedType->typeData->compiledData(); + ref.component->addref(); + } else { + ref.type = resolvedType->type; + Q_ASSERT(ref.type); + } + ref.majorVersion = resolvedType->majorVersion; + ref.minorVersion = resolvedType->minorVersion; + compiledData->resolvedTypes.insert(resolvedType.key(), ref); + } + + // Build property caches and VME meta object data + + const int objectCount = parsedQML->objects.count(); + compiledData->datas.reserve(objectCount); + compiledData->propertyCaches.reserve(objectCount); + + { + QQmlPropertyCacheCreator propertyCacheBuilder(this); + if (!propertyCacheBuilder.buildMetaObjects()) + return false; + } + + { + SignalHandlerConverter converter(engine, parsedQML, compiledData); + if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations()) { + errors << converter.errors; + return false; + } + } + + // Collect imported scripts + const QList<QQmlTypeData::ScriptReference> &scripts = typeData->resolvedScripts(); + compiledData->scripts.reserve(scripts.count()); + for (int scriptIndex = 0; scriptIndex < scripts.count(); ++scriptIndex) { + const QQmlTypeData::ScriptReference &script = scripts.at(scriptIndex); + + QString qualifier = script.qualifier; + QString enclosingNamespace; + + const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.')); + if (lastDotIndex != -1) { + enclosingNamespace = qualifier.left(lastDotIndex); + qualifier = qualifier.mid(lastDotIndex+1); + } + + compiledData->importCache->add(qualifier, scriptIndex, enclosingNamespace); + QQmlScriptData *scriptData = script.script->scriptData(); + scriptData->addref(); + compiledData->scripts << scriptData; + } + + // Resolve component boundaries and aliases + + { + // Scan for components, determine their scopes and resolve aliases within the scope. + QQmlComponentAndAliasResolver resolver(this); + if (!resolver.resolve()) + return false; + } + + // Compile JS binding expressions and signal handlers + + JSCodeGen jsCodeGen(typeData->finalUrlString(), parsedQML->code, &parsedQML->jsModule, &parsedQML->jsParserEngine, parsedQML->program, compiledData->importCache); + const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(parsedQML->functions); + + QV4::ExecutionEngine *v4 = engine->v4engine(); + + QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(engine, v4->executableAllocator, &parsedQML->jsModule, &parsedQML->jsGenerator)); + isel->setUseFastLookups(false); + QV4::CompiledData::CompilationUnit *jsUnit = isel->compile(/*generated unit data*/false); + + // Generate QML compiled type data structures + + QmlUnitGenerator qmlGenerator; + QV4::CompiledData::QmlUnit *qmlUnit = qmlGenerator.generate(*parsedQML, runtimeFunctionIndices); + + if (jsUnit) { + Q_ASSERT(!jsUnit->data); + jsUnit->ownsData = false; + jsUnit->data = &qmlUnit->header; + } + + compiledData->compilationUnit = jsUnit; + if (compiledData->compilationUnit) + compiledData->compilationUnit->ref(); + compiledData->qmlUnit = qmlUnit; // ownership transferred to m_compiledData + + // Add to type registry of composites + if (compiledData->isCompositeType()) + engine->registerInternalCompositeType(compiledData); + else { + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(qmlUnit->indexOfRootObject); + QQmlCompiledData::TypeReference typeRef = compiledData->resolvedTypes.value(obj->inheritedTypeNameIndex); + if (typeRef.component) { + compiledData->metaTypeId = typeRef.component->metaTypeId; + compiledData->listMetaTypeId = typeRef.component->listMetaTypeId; + } else { + compiledData->metaTypeId = typeRef.type->typeId(); + compiledData->listMetaTypeId = typeRef.type->qListTypeId(); + } + } + + // Sanity check property bindings + QQmlPropertyValidator validator(this); + if (!validator.validate()) + return false; + + return errors.isEmpty(); +} + +void QQmlTypeCompiler::recordError(const QQmlError &error) +{ + QQmlError e = error; + e.setUrl(compiledData->url); + errors << e; +} + +QString QQmlTypeCompiler::stringAt(int idx) const +{ + return parsedQML->stringAt(idx); +} + +int QQmlTypeCompiler::registerString(const QString &str) +{ + return parsedQML->jsGenerator.registerString(str); +} + +const QV4::CompiledData::QmlUnit *QQmlTypeCompiler::qmlUnit() const +{ + return compiledData->qmlUnit; +} + +const QQmlImports *QQmlTypeCompiler::imports() const +{ + return &typeData->imports(); +} + +QHash<int, QQmlCompiledData::TypeReference> *QQmlTypeCompiler::resolvedTypes() +{ + return &compiledData->resolvedTypes; +} + +QList<QmlObject *> *QQmlTypeCompiler::qmlObjects() +{ + return &parsedQML->objects; +} + +int QQmlTypeCompiler::rootObjectIndex() const +{ + return parsedQML->indexOfRootObject; +} + +void QQmlTypeCompiler::setPropertyCaches(const QVector<QQmlPropertyCache *> &caches) +{ + Q_ASSERT(compiledData->propertyCaches.isEmpty()); + compiledData->propertyCaches = caches; + Q_ASSERT(caches.count() >= parsedQML->indexOfRootObject); + compiledData->rootPropertyCache = caches.at(parsedQML->indexOfRootObject); +} + +const QVector<QQmlPropertyCache *> &QQmlTypeCompiler::propertyCaches() const +{ + return compiledData->propertyCaches; +} + +void QQmlTypeCompiler::setVMEMetaObjects(const QVector<QByteArray> &metaObjects) +{ + Q_ASSERT(compiledData->datas.isEmpty()); + compiledData->datas = metaObjects; +} + +QVector<QByteArray> *QQmlTypeCompiler::vmeMetaObjects() const +{ + return &compiledData->datas; +} + +QHash<int, int> *QQmlTypeCompiler::objectIndexToIdForRoot() +{ + return &compiledData->objectIndexToIdForRoot; +} + +QHash<int, QHash<int, int> > *QQmlTypeCompiler::objectIndexToIdPerComponent() +{ + return &compiledData->objectIndexToIdPerComponent; +} + +QHash<int, QByteArray> *QQmlTypeCompiler::customParserData() +{ + return &compiledData->customParserData; +} + +MemoryPool *QQmlTypeCompiler::memoryPool() +{ + return parsedQML->jsParserEngine.pool(); +} + +QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler) + : compiler(typeCompiler) +{ +} + +void QQmlCompilePass::recordError(const QV4::CompiledData::Location &location, const QString &description) +{ + QQmlError error; + error.setLine(location.line); + error.setColumn(location.column); + error.setDescription(description); + compiler->recordError(error); +} + +static QAtomicInt classIndexCounter(0); + +QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , enginePrivate(typeCompiler->enginePrivate()) + , qmlObjects(*typeCompiler->qmlObjects()) + , imports(typeCompiler->imports()) + , resolvedTypes(typeCompiler->resolvedTypes()) +{ +} + +QQmlPropertyCacheCreator::~QQmlPropertyCacheCreator() +{ + for (int i = 0; i < propertyCaches.count(); ++i) + if (QQmlPropertyCache *cache = propertyCaches.at(i)) + cache->release(); + propertyCaches.clear(); +} + +bool QQmlPropertyCacheCreator::buildMetaObjects() +{ + propertyCaches.resize(qmlObjects.count()); + vmeMetaObjects.resize(qmlObjects.count()); + + if (!buildMetaObjectRecursively(compiler->rootObjectIndex(), /*referencing object*/-1, /*instantiating binding*/0)) + return false; + + compiler->setVMEMetaObjects(vmeMetaObjects); + compiler->setPropertyCaches(propertyCaches); + propertyCaches.clear(); + + return true; +} + +bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding) +{ + const QmlObject *obj = qmlObjects.at(objectIndex); + + QQmlPropertyCache *baseTypeCache = 0; + + bool needVMEMetaObject = obj->properties->count != 0 || obj->qmlSignals->count != 0 || obj->functions->count != 0; + if (!needVMEMetaObject) { + for (const QtQml::Binding *binding = obj->bindings->first; binding; binding = binding->next) { + if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { + + // On assignments are implemented using value interceptors, which require a VME meta object. + needVMEMetaObject = true; + + // If the on assignment is inside a group property, we need to distinguish between QObject based + // group properties and value type group properties. For the former the base type is derived from + // the property that references us, for the latter we only need a meta-object on the referencing object + // because interceptors can't go to the shared value type instances. + if (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); + } + } + break; + } + } + } + + QString typeName = stringAt(obj->inheritedTypeNameIndex); + if (!typeName.isEmpty()) { + QQmlCompiledData::TypeReference typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + baseTypeCache = typeRef.createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + Q_ASSERT(baseTypeCache); + } + + if (needVMEMetaObject) { + if (!createMetaObject(objectIndex, obj, baseTypeCache)) + return false; + } else if (baseTypeCache) { + propertyCaches[objectIndex] = baseTypeCache; + 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; + + return true; +} + +bool QQmlPropertyCacheCreator::ensureMetaObject(int objectIndex) +{ + if (!vmeMetaObjects.at(objectIndex).isEmpty()) + return true; + const QtQml::QmlObject *obj = qmlObjects.at(objectIndex); + QQmlCompiledData::TypeReference typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + QQmlPropertyCache *baseTypeCache = typeRef.createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + return createMetaObject(objectIndex, obj, baseTypeCache); +} + +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); + propertyCaches[objectIndex] = cache; + cache->addref(); + + struct TypeData { + QV4::CompiledData::Property::Type dtype; + int metaType; + } builtinTypes[] = { + { QV4::CompiledData::Property::Var, qMetaTypeId<QJSValue>() }, + { QV4::CompiledData::Property::Variant, QMetaType::QVariant }, + { QV4::CompiledData::Property::Int, QMetaType::Int }, + { QV4::CompiledData::Property::Bool, QMetaType::Bool }, + { QV4::CompiledData::Property::Real, QMetaType::Double }, + { QV4::CompiledData::Property::String, QMetaType::QString }, + { QV4::CompiledData::Property::Url, QMetaType::QUrl }, + { QV4::CompiledData::Property::Color, QMetaType::QColor }, + { QV4::CompiledData::Property::Font, QMetaType::QFont }, + { QV4::CompiledData::Property::Time, QMetaType::QTime }, + { QV4::CompiledData::Property::Date, QMetaType::QDate }, + { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime }, + { QV4::CompiledData::Property::Rect, QMetaType::QRectF }, + { QV4::CompiledData::Property::Point, QMetaType::QPointF }, + { QV4::CompiledData::Property::Size, QMetaType::QSizeF }, + { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D }, + { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D }, + { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D }, + { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 }, + { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion } + }; + static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); + + QByteArray newClassName; + + if (false /* ### compileState->root == obj && !compileState->nested*/) { +#if 0 // ### + QString path = output->url.path(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + if (lastSlash > -1) { + QString nameBase = path.mid(lastSlash + 1, path.length()-lastSlash-5); + if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) + newClassName = nameBase.toUtf8() + "_QMLTYPE_" + + QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)); + } +#endif + } + if (newClassName.isEmpty()) { + newClassName = QQmlMetaObject(baseTypeCache).className(); + newClassName.append("_QML_"); + newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); + } + + cache->_dynamicClassName = newClassName; + + int aliasCount = 0; + int varPropCount = 0; + + for (QtQml::QmlProperty *p = obj->properties->first; 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); + 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) + + aliasCount * sizeof(VMD::AliasData), 0); + + int effectivePropertyIndex = cache->propertyIndexCacheStart; + int effectiveMethodIndex = cache->methodIndexCacheStart; + + // For property change signal override detection. + // We prepopulate a set of signal names which already exist in the object, + // and throw an error if there is a signal/method defined as an override. + QSet<QString> seenSignals; + seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged"); + QQmlPropertyCache *parentCache = cache; + while ((parentCache = parentCache->parent())) { + if (int pSigCount = parentCache->signalCount()) { + int pSigOffset = parentCache->signalOffset(); + for (int i = pSigOffset; i < pSigCount; ++i) { + QQmlPropertyData *currPSig = parentCache->signal(i); + // XXX TODO: find a better way to get signal name from the property data :-/ + for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin(); + iter != parentCache->stringCache.end(); ++iter) { + if (currPSig == (*iter).second) { + seenSignals.insert(iter.key()); + break; + } + } + } + } + } + + // First set up notify signals for properties - first normal, then var, then alias + enum { NSS_Normal = 0, NSS_Var = 1, NSS_Alias = 2 }; + for (int ii = NSS_Normal; ii <= NSS_Alias; ++ii) { // 0 == normal, 1 == var, 2 == alias + + if (ii == NSS_Var && varPropCount == 0) continue; + else if (ii == NSS_Alias && aliasCount == 0) continue; + + for (QtQml::QmlProperty *p = obj->properties->first; p; p = p->next) { + if ((ii == NSS_Normal && (p->type == QV4::CompiledData::Property::Alias || + p->type == QV4::CompiledData::Property::Var)) || + ((ii == NSS_Var) && (p->type != QV4::CompiledData::Property::Var)) || + ((ii == NSS_Alias) && (p->type != QV4::CompiledData::Property::Alias))) + continue; + + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + + QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed"); + seenSignals.insert(changedSigName); + + cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); + } + } + + // Dynamic signals + for (QtQml::Signal *s = obj->qmlSignals->first; s; s = s->next) { + const int paramCount = s->parameters->count; + + QList<QByteArray> names; + QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0); + + if (paramCount) { + paramTypes[0] = paramCount; + + QtQml::SignalParameter *param = s->parameters->first; + for (int i = 0; i < paramCount; ++i, param = param->next) { + names.append(stringAt(param->nameIndex).toUtf8()); + if (param->type < builtinTypeCount) { + // built-in type + paramTypes[i + 1] = builtinTypes[param->type].metaType; + } else { + // lazily resolved type + Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); + const QString customTypeName = stringAt(param->customTypeNameIndex); + QQmlType *qmltype = 0; + if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0)) + COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(customTypeName)); + + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + QQmlCompiledData *data = tdata->compiledData(); + + paramTypes[i + 1] = data->metaTypeId; + + tdata->release(); + } else { + paramTypes[i + 1] = qmltype->typeId(); + } + } + } + } + + ((QQmlVMEMetaData *)dynamicData.data())->signalCount++; + + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + if (paramCount) + flags |= QQmlPropertyData::HasArguments; + + QString signalName = stringAt(s->nameIndex); + if (seenSignals.contains(signalName)) + COMPILE_EXCEPTION(s, tr("Duplicate signal name: invalid override of property change signal or superclass signal")); + seenSignals.insert(signalName); + + cache->appendSignal(signalName, flags, effectiveMethodIndex++, + paramCount?paramTypes.constData():0, names); + } + + + // Dynamic slots + for (QtQml::Function *s = obj->functions->first; s; s = s->next) { + AST::FunctionDeclaration *astFunction = s->functionDeclaration; + + quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; + + if (astFunction->formals) + flags |= QQmlPropertyData::HasArguments; + + QString slotName = astFunction->name.toString(); + if (seenSignals.contains(slotName)) + COMPILE_EXCEPTION(s, tr("Duplicate method name: invalid override of property change signal or superclass signal")); + // Note: we don't append slotName to the seenSignals list, since we don't + // protect against overriding change signals or methods with properties. + + QList<QByteArray> parameterNames; + AST::FormalParameterList *param = astFunction->formals; + while (param) { + parameterNames << param->name.toUtf8(); + param = param->next; + } + + cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); + } + + + // Dynamic properties (except var and aliases) + int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; + int propertyIdx = 0; + for (QtQml::QmlProperty *p = obj->properties->first; p; p = p->next, ++propertyIdx) { + + if (p->type == QV4::CompiledData::Property::Alias || + p->type == QV4::CompiledData::Property::Var) + continue; + + int propertyType = 0; + int vmePropertyType = 0; + quint32 propertyFlags = 0; + + if (p->type < builtinTypeCount) { + propertyType = builtinTypes[p->type].metaType; + vmePropertyType = propertyType; + + if (p->type == QV4::CompiledData::Property::Variant) + propertyFlags |= QQmlPropertyData::IsQVariant; + } else { + Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || + p->type == QV4::CompiledData::Property::Custom); + + QQmlType *qmltype = 0; + if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) { + COMPILE_EXCEPTION(p, tr("Invalid property type")); + } + + Q_ASSERT(qmltype); + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + QQmlCompiledData *data = tdata->compiledData(); + + if (p->type == QV4::CompiledData::Property::Custom) { + propertyType = data->metaTypeId; + vmePropertyType = QMetaType::QObjectStar; + } else { + propertyType = data->listMetaTypeId; + vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >(); + } + + tdata->release(); + } else { + if (p->type == QV4::CompiledData::Property::Custom) { + propertyType = qmltype->typeId(); + vmePropertyType = QMetaType::QObjectStar; + } else { + propertyType = qmltype->qListTypeId(); + vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >(); + } + } + + if (p->type == QV4::CompiledData::Property::Custom) + propertyFlags |= QQmlPropertyData::IsQObjectDerived; + else + propertyFlags |= QQmlPropertyData::IsQList; + } + + if ((!p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList) + propertyFlags |= QQmlPropertyData::IsWritable; + + + QString propertyName = stringAt(p->nameIndex); + if (propertyIdx == obj->indexOfDefaultProperty) cache->_defaultPropertyName = propertyName; + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + propertyType, effectiveSignalIndex); + + effectiveSignalIndex++; + + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + (vmd->propertyData() + vmd->propertyCount)->propertyType = vmePropertyType; + vmd->propertyCount++; + } + + // Now do var properties + propertyIdx = 0; + for (QtQml::QmlProperty *p = obj->properties->first; p; p = p->next, ++propertyIdx) { + + if (p->type != QV4::CompiledData::Property::Var) + continue; + + quint32 propertyFlags = QQmlPropertyData::IsVarProperty; + if (!p->flags & QV4::CompiledData::Property::IsReadOnly) + propertyFlags |= QQmlPropertyData::IsWritable; + + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + (vmd->propertyData() + vmd->propertyCount)->propertyType = QMetaType::QVariant; + vmd->propertyCount++; + ((QQmlVMEMetaData *)dynamicData.data())->varPropertyCount++; + + QString propertyName = stringAt(p->nameIndex); + if (propertyIdx == obj->indexOfDefaultProperty) cache->_defaultPropertyName = propertyName; + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + QMetaType::QVariant, effectiveSignalIndex); + + effectiveSignalIndex++; + } + + // Alias property count. Actual data is setup in buildDynamicMetaAliases + ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount; + + // Dynamic slot data - comes after the property data + for (QtQml::Function *s = obj->functions->first; s; s = s->next) { + AST::FunctionDeclaration *astFunction = s->functionDeclaration; + int formalsCount = 0; + AST::FormalParameterList *param = astFunction->formals; + while (param) { + formalsCount++; + param = param->next; + } + + VMD::MethodData methodData = { /* runtimeFunctionIndex*/ 0, // ### + formalsCount, + /* s->location.start.line */0 }; // ### + + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount); + vmd->methodCount++; + md = methodData; + } + + return true; +} + +QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , enginePrivate(typeCompiler->enginePrivate()) + , pool(typeCompiler->memoryPool()) + , qmlObjects(typeCompiler->qmlObjects()) + , indexOfRootObject(typeCompiler->rootObjectIndex()) + , _componentIndex(-1) + , _objectIndexToIdInScope(0) + , resolvedTypes(typeCompiler->resolvedTypes()) + , propertyCaches(typeCompiler->propertyCaches()) + , vmeMetaObjectData(typeCompiler->vmeMetaObjects()) + , objectIndexToIdForRoot(typeCompiler->objectIndexToIdForRoot()) + , objectIndexToIdPerComponent(typeCompiler->objectIndexToIdPerComponent()) +{ +} + +void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QtQml::QmlObject *obj, int objectIndex) +{ + QQmlPropertyCache *propertyCache = propertyCaches.value(objectIndex); + Q_ASSERT(propertyCache); + + PropertyResolver propertyResolver(propertyCache); + + bool defaultPropertyQueried = false; + QQmlPropertyData *defaultProperty = 0; + + for (QtQml::Binding *binding = obj->bindings->first; binding; binding = binding->next) { + if (binding->type != QV4::CompiledData::Binding::Type_Object) + continue; + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) + continue; + + const QtQml::QmlObject *targetObject = qmlObjects->at(binding->value.objectIndex); + QQmlType *targetType = resolvedTypes->value(targetObject->inheritedTypeNameIndex).type; + if (targetType && targetType->metaObject() == &QQmlComponent::staticMetaObject) + continue; + + QQmlPropertyData *pd = 0; + QString propertyName = stringAt(binding->propertyNameIndex); + if (!propertyName.isEmpty()) { + bool notInRevision = false; + pd = propertyResolver.property(propertyName, ¬InRevision); + } else { + if (!defaultPropertyQueried) { + defaultProperty = propertyCache->defaultProperty(); + defaultPropertyQueried = true; + } + pd = defaultProperty; + } + if (!pd || !pd->isQObject()) + continue; + + QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType); + const QMetaObject *mo = pc->firstCppMetaObject(); + while (mo) { + if (mo == &QQmlComponent::staticMetaObject) + break; + mo = mo->superClass(); + } + + if (!mo) + continue; + + static QQmlType *componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject); + Q_ASSERT(componentType); + + QtQml::QmlObject *syntheticComponent = pool->New<QtQml::QmlObject>(); + syntheticComponent->init(pool, compiler->registerString(QString::fromUtf8(componentType->typeName())), compiler->registerString(QString())); + + if (!resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) { + QQmlCompiledData::TypeReference typeRef; + typeRef.type = componentType; + typeRef.majorVersion = componentType->majorVersion(); + typeRef.minorVersion = componentType->minorVersion(); + resolvedTypes->insert(syntheticComponent->inheritedTypeNameIndex, typeRef); + } + + qmlObjects->append(syntheticComponent); + const int componentIndex = qmlObjects->count() - 1; + + QtQml::Binding *syntheticBinding = pool->New<QtQml::Binding>(); + *syntheticBinding = *binding; + syntheticBinding->type = QV4::CompiledData::Binding::Type_Object; + syntheticComponent->bindings->append(syntheticBinding); + + binding->value.objectIndex = componentIndex; + + componentRoots.append(componentIndex); + componentBoundaries.append(syntheticBinding->value.objectIndex); + } +} + +bool QQmlComponentAndAliasResolver::resolve() +{ + // Detect real Component {} objects as well as implicitly defined components, such as + // someItemDelegate: Item {} + // In the implicit case Item is surrounded by a synthetic Component {} because the property + // on the left hand side is of QQmlComponent type. + for (int i = 0; i < qmlObjects->count(); ++i) { + const QtQml::QmlObject *obj = qmlObjects->at(i); + if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) + continue; + + QQmlCompiledData::TypeReference tref = resolvedTypes->value(obj->inheritedTypeNameIndex); + if (!tref.type) + continue; + if (tref.type->metaObject() != &QQmlComponent::staticMetaObject) { + findAndRegisterImplicitComponents(obj, i); + continue; + } + + componentRoots.append(i); + + if (obj->functions->count > 0) + COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions.")); + if (obj->properties->count > 0) + COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties.")); + if (obj->qmlSignals->count > 0) + COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals.")); + + if (obj->bindings->count == 0) + COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification")); + + const QtQml::Binding *rootBinding = obj->bindings->first; + if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object) + COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id")); + + componentBoundaries.append(rootBinding->value.objectIndex); + } + + std::sort(componentBoundaries.begin(), componentBoundaries.end()); + + for (int i = 0; i < componentRoots.count(); ++i) { + const QtQml::QmlObject *component = qmlObjects->at(componentRoots.at(i)); + const QtQml::Binding *rootBinding = component->bindings->first; + + _componentIndex = i; + _idToObjectIndex.clear(); + + _objectIndexToIdInScope = &(*objectIndexToIdPerComponent)[componentRoots.at(i)]; + + _objectsWithAliases.clear(); + + if (!collectIdsAndAliases(rootBinding->value.objectIndex)) + return false; + + if (!resolveAliases()) + return false; + } + + // Collect ids and aliases for root + _componentIndex = -1; + _idToObjectIndex.clear(); + _objectIndexToIdInScope = objectIndexToIdForRoot; + _objectsWithAliases.clear(); + + collectIdsAndAliases(indexOfRootObject); + + resolveAliases(); + + return true; +} + +bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) +{ + const QtQml::QmlObject *obj = qmlObjects->at(objectIndex); + + QString id = stringAt(obj->idIndex); + if (!id.isEmpty()) { + if (_idToObjectIndex.contains(obj->idIndex)) { + recordError(obj->locationOfIdProperty, tr("id is not unique")); + return false; + } + _idToObjectIndex.insert(obj->idIndex, objectIndex); + _objectIndexToIdInScope->insert(objectIndex, _objectIndexToIdInScope->count()); + } + + for (QtQml::QmlProperty *property = obj->properties->first; 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) { + if (binding->type != QV4::CompiledData::Binding::Type_Object + && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty + && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) + continue; + + // Stop at Component boundary + if (std::binary_search(componentBoundaries.constBegin(), componentBoundaries.constEnd(), binding->value.objectIndex)) + continue; + + if (!collectIdsAndAliases(binding->value.objectIndex)) + return false; + } + + return true; +} + +bool QQmlComponentAndAliasResolver::resolveAliases() +{ + foreach (int objectIndex, _objectsWithAliases) { + const QtQml::QmlObject *obj = qmlObjects->at(objectIndex); + + QQmlPropertyCache *propertyCache = propertyCaches.value(objectIndex); + Q_ASSERT(propertyCache); + + int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); + 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) { + if (p->type != QV4::CompiledData::Property::Alias) + continue; + + const int idIndex = p->aliasIdValueIndex; + const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1); + if (targetObjectIndex == -1) { + recordError(p->aliasLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex))); + return false; + } + const int targetId = _objectIndexToIdInScope->value(targetObjectIndex, -1); + Q_ASSERT(targetId != -1); + + const QString aliasPropertyValue = stringAt(p->aliasPropertyValueIndex); + + QStringRef property; + QStringRef subProperty; + + const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.')); + if (propertySeparator != -1) { + property = aliasPropertyValue.leftRef(propertySeparator); + subProperty = aliasPropertyValue.midRef(propertySeparator + 1); + } else + property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length()); + + int propIdx = -1; + int propType = 0; + int notifySignal = -1; + int flags = 0; + int type = 0; + bool writable = false; + bool resettable = false; + + quint32 propertyFlags = QQmlPropertyData::IsAlias; + + if (property.isEmpty()) { + const QtQml::QmlObject *targetObject = qmlObjects->at(targetObjectIndex); + QQmlCompiledData::TypeReference typeRef = resolvedTypes->value(targetObject->inheritedTypeNameIndex); + + if (typeRef.type) + type = typeRef.type->typeId(); + else + type = typeRef.component->metaTypeId; + + flags |= QML_ALIAS_FLAG_PTR; + propertyFlags |= QQmlPropertyData::IsQObjectDerived; + } else { + QQmlPropertyCache *targetCache = propertyCaches.value(targetObjectIndex); + Q_ASSERT(targetCache); + QtQml::PropertyResolver resolver(targetCache); + + QQmlPropertyData *targetProperty = resolver.property(property.toString()); + if (!targetProperty || targetProperty->coreIndex > 0x0000FFFF) { + recordError(p->aliasLocation, tr("Invalid alias location")); + return false; + } + + propIdx = targetProperty->coreIndex; + type = targetProperty->propType; + + writable = targetProperty->isWritable(); + resettable = targetProperty->isResettable(); + notifySignal = targetProperty->notifyIndex; + + if (!subProperty.isEmpty()) { + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type); + if (!valueType) { + recordError(p->aliasLocation, tr("Invalid alias location")); + return false; + } + + propType = type; + + int valueTypeIndex = + valueType->metaObject()->indexOfProperty(subProperty.toString().toUtf8().constData()); + if (valueTypeIndex == -1) { + recordError(p->aliasLocation, tr("Invalid alias location")); + return false; + } + Q_ASSERT(valueTypeIndex <= 0x0000FFFF); + + propIdx |= (valueTypeIndex << 16); + if (valueType->metaObject()->property(valueTypeIndex).isEnumType()) + type = QVariant::Int; + else + type = valueType->metaObject()->property(valueTypeIndex).userType(); + + } else { + if (targetProperty->isEnum()) { + type = QVariant::Int; + } else { + // Copy type flags + propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask; + + if (targetProperty->isVarProperty()) + propertyFlags |= QQmlPropertyData::IsQVariant; + + if (targetProperty->isQObject()) + flags |= QML_ALIAS_FLAG_PTR; + } + } + } + + QQmlVMEMetaData::AliasData aliasData = { targetId, propIdx, propType, flags, notifySignal }; + + typedef QQmlVMEMetaData VMD; + QByteArray &dynamicData = (*vmeMetaObjectData)[objectIndex]; + Q_ASSERT(!dynamicData.isEmpty()); + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + *(vmd->aliasData() + effectiveAliasIndex++) = aliasData; + + Q_ASSERT(dynamicData.isDetached()); + + if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && writable) + propertyFlags |= QQmlPropertyData::IsWritable; + else + propertyFlags &= ~QQmlPropertyData::IsWritable; + + if (resettable) + propertyFlags |= QQmlPropertyData::IsResettable; + else + propertyFlags &= ~QQmlPropertyData::IsResettable; + + QString propertyName = stringAt(p->nameIndex); + if (propertyIndex == obj->indexOfDefaultProperty) propertyCache->_defaultPropertyName = propertyName; + propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + type, effectiveSignalIndex++); + + } + } + return true; +} + + +QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , qmlUnit(typeCompiler->qmlUnit()) + , resolvedTypes(*typeCompiler->resolvedTypes()) + , propertyCaches(typeCompiler->propertyCaches()) + , objectIndexToIdPerComponent(*typeCompiler->objectIndexToIdPerComponent()) + , customParserData(typeCompiler->customParserData()) +{ +} + +bool QQmlPropertyValidator::validate() +{ + for (quint32 i = 0; i < qmlUnit->nObjects; ++i) { + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i); + if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) + continue; + + if (isComponent(i)) + continue; + + QQmlPropertyCache *propertyCache = propertyCaches.value(i); + Q_ASSERT(propertyCache); + + if (!validateObject(obj, i, propertyCache)) + return false; + } + return true; +} + +const QQmlImports &QQmlPropertyValidator::imports() const +{ + return *compiler->imports(); +} + +bool QQmlPropertyValidator::validateObject(const QV4::CompiledData::Object *obj, int objectIndex, QQmlPropertyCache *propertyCache) +{ + QQmlCustomParser *customParser = 0; + QQmlCompiledData::TypeReference objectType = resolvedTypes.value(obj->inheritedTypeNameIndex); + if (objectType.type) + customParser = objectType.type->customParser(); + QList<const QV4::CompiledData::Binding*> customBindings; + + PropertyResolver propertyResolver(propertyCache); + + QQmlPropertyData *defaultProperty = propertyCache->defaultProperty(); + + const QV4::CompiledData::Binding *binding = obj->bindingTable(); + for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty + || binding->type == QV4::CompiledData::Binding::Type_GroupProperty) { + if (customParser) + customBindings << binding; + continue; + } + + const QString name = stringAt(binding->propertyNameIndex); + + bool bindingToDefaultProperty = false; + + bool notInRevision = false; + QQmlPropertyData *pd = 0; + if (!name.isEmpty()) { + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) + pd = propertyResolver.signal(name, ¬InRevision); + else + pd = propertyResolver.property(name, ¬InRevision); + + if (notInRevision) { + QString typeName = stringAt(obj->inheritedTypeNameIndex); + if (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 { + pd = defaultProperty; + bindingToDefaultProperty = true; + } + + if (!pd) { + if (customParser) { + customBindings << binding; + continue; + } + if (bindingToDefaultProperty) { + COMPILE_EXCEPTION(binding, tr("Cannot assign to non-existent default property")); + } else { + COMPILE_EXCEPTION(binding, tr("Cannot assign to non-existent property \"%1\"").arg(name)); + } + } + } + + if (customParser && !customBindings.isEmpty()) { + customParser->clearErrors(); + customParser->compiler = this; + QByteArray data = customParser->compile(qmlUnit, customBindings); + customParser->compiler = 0; + customParserData->insert(objectIndex, data); + const QList<QQmlError> parserErrors = customParser->errors(); + if (!parserErrors.isEmpty()) { + foreach (QQmlError error, parserErrors) + compiler->recordError(error); + return false; + } + } + + return true; +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h new file mode 100644 index 0000000000..248cd12796 --- /dev/null +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 QQMLTYPECOMPILER_P_H +#define QQMLTYPECOMPILER_P_H + +#include <qglobal.h> +#include <qqmlerror.h> +#include <qhash.h> +#include <private/qqmlcompiler_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlEnginePrivate; +class QQmlCompiledData; +class QQmlError; +class QQmlTypeData; +class QQmlImports; + +namespace QtQml { +struct ParsedQML; +} + +namespace QV4 { +namespace CompiledData { +struct QmlUnit; +struct Location; +} +} + +struct QQmlTypeCompiler +{ + QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlCompiledData *compiledData, QQmlTypeData *typeData, QtQml::ParsedQML *parsedQML); + + bool compile(); + + QList<QQmlError> compilationErrors() const { return errors; } + void recordError(const QQmlError &error); + + QString stringAt(int idx) const; + int registerString(const QString &str); + + const QV4::CompiledData::QmlUnit *qmlUnit() const; + + QQmlEnginePrivate *enginePrivate() const { return engine; } + const QQmlImports *imports() const; + QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes(); + QList<QtQml::QmlObject*> *qmlObjects(); + int rootObjectIndex() const; + void setPropertyCaches(const QVector<QQmlPropertyCache *> &caches); + const QVector<QQmlPropertyCache *> &propertyCaches() const; + void setVMEMetaObjects(const QVector<QByteArray> &metaObjects); + QVector<QByteArray> *vmeMetaObjects() const; + QHash<int, int> *objectIndexToIdForRoot(); + QHash<int, QHash<int, int> > *objectIndexToIdPerComponent(); + QHash<int, QByteArray> *customParserData(); + QQmlJS::MemoryPool *memoryPool(); + +private: + QList<QQmlError> errors; + QQmlEnginePrivate *engine; + QQmlCompiledData *compiledData; + QQmlTypeData *typeData; + QtQml::ParsedQML *parsedQML; +}; + +struct QQmlCompilePass +{ + virtual ~QQmlCompilePass() {} + + QQmlCompilePass(QQmlTypeCompiler *typeCompiler); + + QString stringAt(int idx) const { return compiler->stringAt(idx); } +protected: + void recordError(const QV4::CompiledData::Location &location, const QString &description); + + QQmlTypeCompiler *compiler; +}; + +class QQmlPropertyCacheCreator : public QQmlCompilePass +{ + Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreator) +public: + QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler); + ~QQmlPropertyCacheCreator(); + + bool buildMetaObjects(); +protected: + bool buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding); + bool ensureMetaObject(int objectIndex); + bool createMetaObject(int objectIndex, const QtQml::QmlObject *obj, QQmlPropertyCache *baseTypeCache); + + QQmlEnginePrivate *enginePrivate; + const QList<QtQml::QmlObject*> &qmlObjects; + const QQmlImports *imports; + QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes; + QVector<QByteArray> vmeMetaObjects; + QVector<QQmlPropertyCache*> propertyCaches; +}; + +class QQmlComponentAndAliasResolver : public QQmlCompilePass +{ + Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver) +public: + QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler); + + bool resolve(); + +protected: + void findAndRegisterImplicitComponents(const QtQml::QmlObject *obj, int objectIndex); + bool collectIdsAndAliases(int objectIndex); + bool resolveAliases(); + + QQmlEnginePrivate *enginePrivate; + QQmlJS::MemoryPool *pool; + + QList<QtQml::QmlObject*> *qmlObjects; + const int indexOfRootObject; + + // indices of the objects that are actually Component {} + QVector<int> componentRoots; + // indices of objects that are the beginning of a new component + // scope. This is sorted and used for binary search. + QVector<int> componentBoundaries; + + int _componentIndex; + QHash<int, int> _idToObjectIndex; + QHash<int, int> *_objectIndexToIdInScope; + QList<int> _objectsWithAliases; + + QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes; + const QVector<QQmlPropertyCache *> propertyCaches; + QVector<QByteArray> *vmeMetaObjectData; + QHash<int, int> *objectIndexToIdForRoot; + QHash<int, QHash<int, int> > *objectIndexToIdPerComponent; +}; + +class QQmlPropertyValidator : public QQmlCompilePass, public QQmlCustomParserCompilerBackend +{ + Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) +public: + QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler); + + bool validate(); + + // Re-implemented for QQmlCustomParser + virtual const QQmlImports &imports() const; + + +private: + bool validateObject(const QV4::CompiledData::Object *obj, int objectIndex, QQmlPropertyCache *propertyCache); + + bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } + + const QV4::CompiledData::QmlUnit *qmlUnit; + const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes; + const QVector<QQmlPropertyCache *> &propertyCaches; + const QHash<int, QHash<int, int> > objectIndexToIdPerComponent; + QHash<int, QByteArray> *customParserData; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPECOMPILER_P_H diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index ce0c7abf9e..9eac0e678d 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -193,6 +193,57 @@ QString Binding::valueAsString(const Unit *unit) const return QString(); } +//reverse of Lexer::singleEscape() +static QString escapedString(const QString &string) +{ + QString tmp = QLatin1String("\""); + for (int i = 0; i < string.length(); ++i) { + const QChar &c = string.at(i); + switch (c.unicode()) { + case 0x08: + tmp += QLatin1String("\\b"); + break; + case 0x09: + tmp += QLatin1String("\\t"); + break; + case 0x0A: + tmp += QLatin1String("\\n"); + break; + case 0x0B: + tmp += QLatin1String("\\v"); + break; + case 0x0C: + tmp += QLatin1String("\\f"); + break; + case 0x0D: + tmp += QLatin1String("\\r"); + break; + case 0x22: + tmp += QLatin1String("\\\""); + break; + case 0x27: + tmp += QLatin1String("\\\'"); + break; + case 0x5C: + tmp += QLatin1String("\\\\"); + break; + default: + tmp += c; + break; + } + } + tmp += QLatin1Char('\"'); + return tmp; +} + +QString Binding::valueAsScriptString(const Unit *unit) const +{ + if (type == Type_String) + return escapedString(unit->stringAt(stringIndex)); + else + return valueAsString(unit); +} + } } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 90f27d5f57..f9e85ef080 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -264,7 +264,7 @@ struct Function // Qml data structures -struct Binding +struct Q_QML_EXPORT Binding { quint32 propertyNameIndex; @@ -280,7 +280,8 @@ struct Binding }; enum Flags { - IsSignalHandlerExpression = 0x1 + IsSignalHandlerExpression = 0x1, + IsOnAssignment = 0x2 }; quint32 flags : 16; @@ -296,6 +297,7 @@ struct Binding Location location; QString valueAsString(const Unit *unit) const; + QString valueAsScriptString(const Unit *unit) const; double valueAsNumber() const { if (type == Type_Number) @@ -516,6 +518,8 @@ protected: } +Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE); + QT_END_NAMESPACE #endif diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp index 35097bae49..415fdad8e9 100644 --- a/src/qml/compiler/qv4isel_masm.cpp +++ b/src/qml/compiler/qv4isel_masm.cpp @@ -1015,7 +1015,7 @@ void InstructionSelection::setQObjectProperty(V4IR::Expr *source, V4IR::Expr *ta void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target) { -#if QT_POINTER_SIZE == 8 +#if 0 // QT_POINTER_SIZE == 8 V4IR::Temp *tbase = base->asTemp(); V4IR::Temp *tindex = index->asTemp(); if (tbase && tindex && @@ -1026,9 +1026,10 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR: _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 - Assembler::Address managedType(Assembler::ScratchRegister, qOffsetOf(QV4::Managed, flags)); + // ### 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::Managed::SimpleArray), 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; @@ -1069,12 +1070,12 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR: // get data, ScratchRegister holds index addr = _as->loadTempAddress(Assembler::ReturnValueRegister, tbase); _as->load64(addr, Assembler::ReturnValueRegister); - Address arrayDataLen(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayDataLen)); + 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, arrayDataLen); - Address arrayData(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData)); + 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); diff --git a/src/qml/compiler/qv4regalloc.cpp b/src/qml/compiler/qv4regalloc.cpp index 048aad8497..f95cd55b04 100644 --- a/src/qml/compiler/qv4regalloc.cpp +++ b/src/qml/compiler/qv4regalloc.cpp @@ -60,6 +60,9 @@ struct Use { } QT_BEGIN_NAMESPACE + +Q_DECLARE_TYPEINFO(Use, Q_MOVABLE_TYPE); + using namespace QQmlJS::V4IR; namespace QQmlJS { |