diff options
Diffstat (limited to 'src/qml')
86 files changed, 3764 insertions, 2345 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 { diff --git a/src/qml/debugger/qqmlprofilerservice.cpp b/src/qml/debugger/qqmlprofilerservice.cpp index af2aea21ae..03bf464542 100644 --- a/src/qml/debugger/qqmlprofilerservice.cpp +++ b/src/qml/debugger/qqmlprofilerservice.cpp @@ -131,6 +131,7 @@ QQmlProfilerService::QQmlProfilerService() QQmlProfilerService::~QQmlProfilerService() { + enabled = false; instance = 0; } @@ -426,153 +427,68 @@ void QQmlProfilerService::messageReceived(const QByteArray &message) } /*! - * \brief QQmlVmeProfiler::Data::clear Reset to defaults - * Reset the profiling data to defaults. + * \fn void QQmlVmeProfiler::Data::clear() + * Resets the profiling data to defaults. */ -void QQmlVmeProfiler::Data::clear() -{ - url.clear(); - line = 0; - column = 0; - typeName.clear(); -} /*! - * \brief QQmlVmeProfiler::start Start profiler without data + * \fn bool QQmlVmeProfiler::start() * Clears the current range data, then stops the profiler previously running in the - * foreground if any, then starts a new one. + * foreground if any, then starts a new one if profiling is enabled. + * Returns whether the new profiler was actually started. */ -bool QQmlVmeProfiler::start() -{ - if (QQmlProfilerService::enabled) { - currentRange.clear(); - if (running) - QQmlProfilerService::instance->endRange(QQmlProfilerService::Creating); - else - running = true; - QQmlProfilerService::instance->startRange(QQmlProfilerService::Creating); - return true; - } - return false; -} /*! - * \brief QQmlVmeProfiler::updateLocation Update current location information - * \param url URL of file being executed - * \param line line Curent line in file - * \param column column Current column in file - * Updates the current profiler's location information. + * \fn void QQmlVmeProfiler::updateLocation(const QUrl &url, int line, int column) + * Updates the current profiler's location information. \a url is the URL of + * file being executed, \line line is the current line in in that file, and + * \a column is the current column in that file. */ -void QQmlVmeProfiler::updateLocation(const QUrl &url, int line, int column) -{ - if (QQmlProfilerService::enabled && running) { - currentRange.url = url; - currentRange.line = line; - currentRange.column = column; - QQmlProfilerService::instance->rangeLocation( - QQmlProfilerService::Creating, url, line, column); - } -} /*! - * \brief QQmlVmeProfiler::updateTypeName Update current type information - * \param typeName Type of object being created - * Updates the current profiler's type information. + * \fn void QQmlVmeProfiler::updateTypeName(const QString &typename) + * Updates the current profiler's type information. \a typeName is the type of + * object being created. */ -void QQmlVmeProfiler::updateTypeName(const QString &typeName) -{ - if (QQmlProfilerService::enabled && running) { - currentRange.typeName = typeName; - QQmlProfilerService::instance->rangeData(QQmlProfilerService::Creating, typeName); - } -} /*! - * \brief QQmlVmeProfiler::pop Pops a paused profiler from the stack and restarts it + * \fn bool QQmlVmeProfiler::pop() * Stops the currently running profiler, if any, then retrieves an old one from the stack - * of paused profilers and starts that. + * of paused profilers and starts that if possible. + * Returns whether there actually is a running profiler after that. */ -void QQmlVmeProfiler::pop() -{ - if (QQmlProfilerService::enabled && ranges.count() > 0) { - start(); - currentRange = ranges.pop(); - QQmlProfilerService::instance->rangeLocation( - QQmlProfilerService::Creating, currentRange.url, currentRange.line, currentRange.column); - QQmlProfilerService::instance->rangeData(QQmlProfilerService::Creating, currentRange.typeName); - } -} /*! - * \brief QQmlVmeProfiler::push Pushes the currently running profiler on the stack. + * \fn void QQmlVmeProfiler::push() * Pushes the currently running profiler on the stack of paused profilers. Note: The profiler * isn't paused here. That's a separate step. If it's never paused, but pop()'ed later that * won't do any harm, though. */ -void QQmlVmeProfiler::push() -{ - if (QQmlProfilerService::enabled && running) - ranges.push(currentRange); -} /*! - * \brief QQmlVmeProfiler::clear Stop all running profilers and clear all data. + * \fn void QQmlVmeProfiler::clear() * Stops the currently running (foreground and background) profilers and removes all saved * data about paused profilers. */ -void QQmlVmeProfiler::clear() -{ - stop(); - ranges.clear(); - if (QQmlProfilerService::enabled) { - for (int i = 0; i < backgroundRanges.count(); ++i) { - QQmlProfilerService::instance->endRange(QQmlProfilerService::Creating); - } - } - backgroundRanges.clear(); - running = false; -} /*! - * \brief QQmlVmeProfiler::stop Stop profiler running in the foreground, if any. + * \fn void QQmlVmeProfiler::stop() + * Stop profiler running in the foreground, if any. */ -void QQmlVmeProfiler::stop() -{ - if (QQmlProfilerService::enabled && running) { - QQmlProfilerService::instance->endRange(QQmlProfilerService::Creating); - currentRange.clear(); - running = false; - } -} + /*! - * \brief QQmlVmeProfiler::background Push the current profiler to the background. - * Push the profiler currently running in the foreground to the background so that it + * \fn void QQmlVmeProfiler::background() + * Pushes the profiler currently running in the foreground to the background so that it * won't be stopped by stop() or start(). There can be multiple profilers in the background. * You can retrieve them in reverse order by calling foreground(). */ -void QQmlVmeProfiler::background() -{ - if (QQmlProfilerService::enabled && running) { - backgroundRanges.push(currentRange); - running = false; - } -} /*! - * \brief QQmlVmeProfiler::foreground Retrieve a profiler from the background - * Stop the profiler currently running in the foreground, if any and put the next profiler - * from the background in its place. + * \fn bool QQmlVmeProfiler::foreground() + * Stops the profiler currently running in the foreground, if any and puts the next profiler + * from the background in its place if there are any profiles in the background. + * Returns Whethere there actually is a valid running profiler afterwards. */ -bool QQmlVmeProfiler::foreground() -{ - if (QQmlProfilerService::enabled && backgroundRanges.count() > 0) { - stop(); - currentRange = backgroundRanges.pop(); - running = true; - return true; - } - return false; -} QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlprofilerservice_p.h b/src/qml/debugger/qqmlprofilerservice_p.h index 86807eacec..1261597f1e 100644 --- a/src/qml/debugger/qqmlprofilerservice_p.h +++ b/src/qml/debugger/qqmlprofilerservice_p.h @@ -294,7 +294,13 @@ public: int line; int column; QString typeName; - void clear(); + void clear() + { + url.clear(); + line = 0; + column = 0; + typeName.clear(); + } }; QQmlVmeProfiler() : @@ -307,19 +313,98 @@ public: clear(); } - void clear(); + void clear() + { + stop(); + ranges.clear(); + if (QQmlProfilerService::enabled) { + for (int i = 0; i < backgroundRanges.count(); ++i) { + QQmlProfilerService::instance->endRange(QQmlProfilerService::Creating); + } + } + backgroundRanges.clear(); + running = false; + } - bool start(); - void stop(); + bool start() + { + if (QQmlProfilerService::enabled) { + currentRange.clear(); + if (running) + QQmlProfilerService::instance->endRange(QQmlProfilerService::Creating); + else + running = true; + QQmlProfilerService::instance->startRange(QQmlProfilerService::Creating); + return true; + } + return false; + } - void updateLocation(const QUrl &url, int line, int column); - void updateTypeName(const QString &typeName); + void stop() + { + if (QQmlProfilerService::enabled && running) { + QQmlProfilerService::instance->endRange(QQmlProfilerService::Creating); + currentRange.clear(); + running = false; + } + } - void pop(); - void push(); + void updateLocation(const QUrl &url, int line, int column) + { + if (QQmlProfilerService::enabled && running) { + currentRange.url = url; + currentRange.line = line; + currentRange.column = column; + QQmlProfilerService::instance->rangeLocation( + QQmlProfilerService::Creating, url, line, column); + } + } - void background(); - bool foreground(); + void updateTypeName(const QString &typeName) + { + if (QQmlProfilerService::enabled && running) { + currentRange.typeName = typeName; + QQmlProfilerService::instance->rangeData(QQmlProfilerService::Creating, typeName); + } + } + + bool pop() + { + if (QQmlProfilerService::enabled && ranges.count() > 0) { + start(); + currentRange = ranges.pop(); + QQmlProfilerService::instance->rangeLocation( + QQmlProfilerService::Creating, currentRange.url, currentRange.line, currentRange.column); + QQmlProfilerService::instance->rangeData(QQmlProfilerService::Creating, currentRange.typeName); + return true; + } + return false; + } + + void push() + { + if (QQmlProfilerService::enabled && running) + ranges.push(currentRange); + } + + void background() + { + if (QQmlProfilerService::enabled && running) { + backgroundRanges.push(currentRange); + running = false; + } + } + + bool foreground() + { + if (QQmlProfilerService::enabled && backgroundRanges.count() > 0) { + stop(); + currentRange = backgroundRanges.pop(); + running = true; + return true; + } + return false; + } private: diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 6a0cf0cf6d..8e1ac38f4f 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -1049,7 +1049,7 @@ bool QJSValue::hasOwnProperty(const QString &name) const return false; ScopedString s(scope, engine->newIdentifier(name)); - return o->__getOwnProperty__(s); + return o->hasOwnProperty(s); } /*! diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 94983f51e5..a90cea96c2 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -14,6 +14,7 @@ SOURCES += \ $$PWD/qv4managed.cpp \ $$PWD/qv4internalclass.cpp \ $$PWD/qv4sparsearray.cpp \ + $$PWD/qv4arraydata.cpp \ $$PWD/qv4arrayobject.cpp \ $$PWD/qv4argumentsobject.cpp \ $$PWD/qv4booleanobject.cpp \ @@ -58,6 +59,7 @@ HEADERS += \ $$PWD/qv4managed_p.h \ $$PWD/qv4internalclass_p.h \ $$PWD/qv4sparsearray_p.h \ + $$PWD/qv4arraydata_p.h \ $$PWD/qv4arrayobject_p.h \ $$PWD/qv4argumentsobject_p.h \ $$PWD/qv4booleanobject_p.h \ diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 629c255b48..5094bafa66 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -51,8 +51,7 @@ ArgumentsObject::ArgumentsObject(CallContext *context) , context(context) , fullyCreated(false) { - type = Type_ArgumentsObject; - flags &= ~SimpleArray; + setArrayType(ArrayData::Complex); ExecutionEngine *v4 = context->engine; Scope scope(v4); @@ -66,14 +65,12 @@ ArgumentsObject::ArgumentsObject(CallContext *context) memberData[CallerPropertyIndex] = pd; arrayReserve(context->callData->argc); - for (int i = 0; i < context->callData->argc; ++i) - arrayData[i].value = context->callData->args[i]; - arrayDataLen = context->callData->argc; + arrayData->put(0, context->callData->args, context->callData->argc); + arrayData->setLength(context->callData->argc); fullyCreated = true; } else { Q_ASSERT(CalleePropertyIndex == internalClass->find(context->engine->id_callee)); memberData[CalleePropertyIndex].value = context->function->asReturnedValue(); - isNonStrictArgumentsObject = true; } Q_ASSERT(LengthPropertyIndex == internalClass->find(context->engine->id_length)); Property *lp = memberData + ArrayObject::LengthPropertyIndex; @@ -95,18 +92,18 @@ void ArgumentsObject::fullyCreate() uint numAccessors = qMin((int)context->function->formalParameterCount, context->realArgumentCount); uint argCount = qMin(context->realArgumentCount, context->callData->argc); arrayReserve(argCount); - ensureArrayAttributes(); + arrayData->ensureAttributes(); context->engine->requireArgumentsAccessors(numAccessors); for (uint i = 0; i < (uint)numAccessors; ++i) { mappedArguments.append(context->callData->args[i]); - arrayData[i] = context->engine->argumentsAccessors.at(i); - arrayAttributes[i] = Attr_Accessor; + arrayData->data[i] = context->engine->argumentsAccessors.at(i); + arrayData->setAttributes(i, Attr_Accessor); } - for (uint i = numAccessors; i < argCount; ++i) { - arrayData[i] = Property::fromValue(context->callData->args[i]); - arrayAttributes[i] = Attr_Data; - } - arrayDataLen = argCount; + arrayData->setLength(numAccessors); + arrayData->put(numAccessors, context->callData->args + numAccessors, argCount - numAccessors); + for (uint i = numAccessors; i < argCount; ++i) + arrayData->setAttributes(i, Attr_Data); + arrayData->setLength(argCount); fullyCreated = true; } @@ -116,27 +113,24 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const fullyCreate(); Scope scope(ctx); - uint pidx = propertyIndexFromArrayIndex(index); - Property *pd = arrayData + pidx; + Property *pd = arrayData->getProperty(index); Property map; PropertyAttributes mapAttrs; bool isMapped = false; if (pd && index < (uint)mappedArguments.size()) - isMapped = arrayAttributes && arrayAttributes[pidx].isAccessor() && pd->getter() == context->engine->argumentsAccessors.at(index).getter(); + isMapped = arrayData->attributes(index).isAccessor() && pd->getter() == context->engine->argumentsAccessors.at(index).getter(); if (isMapped) { map = *pd; - mapAttrs = arrayAttributes[pidx]; - arrayAttributes[pidx] = Attr_Data; + mapAttrs = arrayData->attributes(index); + arrayData->setAttributes(index, Attr_Data); pd->value = mappedArguments.at(index); } - isNonStrictArgumentsObject = false; bool strict = ctx->strictMode; ctx->strictMode = false; - bool result = Object::__defineOwnProperty__(ctx, index, desc, attrs); + bool result = Object::defineOwnProperty2(ctx, index, desc, attrs); ctx->strictMode = strict; - isNonStrictArgumentsObject = true; if (isMapped && attrs.isData()) { ScopedCallData callData(scope, 1); @@ -146,7 +140,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const if (attrs.isWritable()) { *pd = map; - arrayAttributes[pidx] = mapAttrs; + arrayData->setAttributes(index, mapAttrs); } } diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index d306fae92b..42b749cbb5 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -77,12 +77,17 @@ struct ArgumentsSetterFunction: FunctionObject struct ArgumentsObject: Object { Q_MANAGED + Q_MANAGED_TYPE(ArgumentsObject) CallContext *context; bool fullyCreated; QVector<SafeValue> mappedArguments; ArgumentsObject(CallContext *context); ~ArgumentsObject() {} + static bool isNonStrictArgumentsObject(Managed *m) { + return m->internalClass->vtable->type == Type_ArgumentsObject && + !static_cast<ArgumentsObject *>(m)->context->strictMode; + } enum { LengthPropertyIndex = 0, diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp new file mode 100644 index 0000000000..0ec36bd9db --- /dev/null +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -0,0 +1,563 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#include "qv4arraydata_p.h" +#include "qv4object_p.h" +#include "qv4functionobject_p.h" + +using namespace QV4; + +const ArrayVTable ArrayData::static_vtbl = +{ + ArrayData::Simple, + ArrayData::freeData, + ArrayData::reserve, + ArrayData::get, + ArrayData::put, + ArrayData::putArray, + ArrayData::del, + ArrayData::setAttribute, + ArrayData::attribute, + ArrayData::push_front, + ArrayData::pop_front, + ArrayData::truncate +}; + +const ArrayVTable SparseArrayData::static_vtbl = +{ + ArrayData::Sparse, + SparseArrayData::freeData, + SparseArrayData::reserve, + SparseArrayData::get, + SparseArrayData::put, + SparseArrayData::putArray, + SparseArrayData::del, + SparseArrayData::setAttribute, + SparseArrayData::attribute, + SparseArrayData::push_front, + SparseArrayData::pop_front, + SparseArrayData::truncate +}; + + +void ArrayData::getHeadRoom(ArrayData *d) +{ + Q_ASSERT(d); + Q_ASSERT(!d->offset); + d->offset = qMax(d->len >> 2, (uint)16); + Property *newArray = new Property[d->offset + d->alloc]; + memcpy(newArray + d->offset, d->data, d->len*sizeof(Property)); + delete [] d->data; + d->data = newArray + d->offset; + if (d->attrs) { + PropertyAttributes *newAttrs = new PropertyAttributes[d->offset + d->alloc]; + memcpy(newAttrs + d->offset, d->attrs, d->len*sizeof(PropertyAttributes)); + delete [] d->attrs; + d->attrs = newAttrs + d->offset; + } +} + +void ArrayData::reserve(ArrayData *d, uint n) +{ + if (n < 8) + n = 8; + if (n <= d->alloc) + return; + + d->alloc = qMax(n, 2*d->alloc); + Property *newArrayData = new Property[d->alloc + d->offset]; + if (d->data) { + memcpy(newArrayData + d->offset, d->data, sizeof(Property)*d->len); + delete [] (d->data - d->offset); + } + d->data = newArrayData + d->offset; + + if (d->attrs) { + PropertyAttributes *newAttrs = new PropertyAttributes[d->alloc]; + memcpy(newAttrs, d->attrs, sizeof(PropertyAttributes)*d->len); + delete [] (d->attrs - d->offset); + + d->attrs = newAttrs; + } +} + +void ArrayData::ensureAttributes() +{ + if (attrs) + return; + + if (type == Simple) + type = Complex; + attrs = new PropertyAttributes[alloc + offset]; + attrs += offset; + for (uint i = 0; i < len; ++i) + attrs[i] = Attr_Data; +} + + +void ArrayData::freeData(ArrayData *d) +{ + delete [] (d->data - d->offset); + if (d->attrs) + delete [] (d->attrs - d->offset); + delete d; +} + +ReturnedValue ArrayData::get(const ArrayData *d, uint index) +{ + if (index >= d->len) + return Primitive::emptyValue().asReturnedValue(); + return d->data[index].value.asReturnedValue(); +} + +bool ArrayData::put(ArrayData *d, uint index, ValueRef value) +{ + Q_ASSERT(!d->attrs || !d->attrs->isAccessor()); + // ### honour attributes + d->data[index].value = value; + return true; +} + +bool ArrayData::del(ArrayData *d, uint index) +{ + if (index >= d->len) + return true; + + if (!d->attrs || d->attrs[index].isConfigurable()) { + d->data[index].value = Primitive::emptyValue(); + if (d->attrs) + d->attrs[index] = Attr_Data; + return true; + } + if (d->data[index].value.isEmpty()) + return true; + return false; +} + +void ArrayData::setAttribute(ArrayData *d, uint index, PropertyAttributes attrs) +{ + d->attrs[index] = attrs; +} + +PropertyAttributes ArrayData::attribute(const ArrayData *d, uint index) +{ + return d->attrs[index]; +} + +void ArrayData::push_front(ArrayData *d, SafeValue *values, uint n) +{ + Q_ASSERT(!d->attrs); + for (int i = n - 1; i >= 0; --i) { + if (!d->offset) + ArrayData::getHeadRoom(d); + + --d->offset; + --d->data; + ++d->len; + ++d->alloc; + d->data->value = values[i].asReturnedValue(); + } + +} + +ReturnedValue ArrayData::pop_front(ArrayData *d) +{ + Q_ASSERT(!d->attrs); + if (!d->len) + return Encode::undefined(); + + ReturnedValue v = d->data[0].value.isEmpty() ? Encode::undefined() : d->data[0].value.asReturnedValue(); + ++d->offset; + ++d->data; + --d->len; + --d->alloc; + return v; +} + +uint ArrayData::truncate(ArrayData *d, uint newLen) +{ + if (d->attrs) { + Property *it = d->data + d->len; + const Property *begin = d->data + newLen; + while (--it >= begin) { + if (!it->value.isEmpty() && !d->attrs[it - d->data].isConfigurable()) { + newLen = it - d->data + 1; + break; + } + it->value = Primitive::emptyValue(); + } + } + d->len = newLen; + return newLen; +} + +bool ArrayData::putArray(ArrayData *d, uint index, SafeValue *values, uint n) +{ + if (index + n > d->alloc) + reserve(d, index + n + 1); + for (uint i = d->len; i < index; ++i) + d->data[i].value = Primitive::emptyValue(); + for (uint i = 0; i < n; ++i) + d->data[index + i].value = values[i]; + d->len = qMax(d->len, index + n); + return true; +} + +void SparseArrayData::free(ArrayData *d, uint idx) +{ + Q_ASSERT(d && d->type == ArrayData::Sparse); + SparseArrayData *dd = static_cast<SparseArrayData *>(d); + Property &pd = dd->data[idx]; + pd.value.uint_32 = dd->freeList; + dd->freeList = idx; + if (dd->attrs) + dd->attrs[idx].clear(); +} + + +void SparseArrayData::freeData(ArrayData *d) +{ + delete static_cast<SparseArrayData *>(d)->sparse; + ArrayData::freeData(d); +} + +void SparseArrayData::reserve(ArrayData *d, uint n) +{ + if (n < 8) + n = 8; + if (n <= d->alloc) + return; + + SparseArrayData *dd = static_cast<SparseArrayData *>(d); + // ### FIXME + dd->len = dd->alloc; + dd->alloc = qMax(n, 2*dd->alloc); + Property *newArrayData = new Property[dd->alloc]; + if (dd->data) { + memcpy(newArrayData, dd->data, sizeof(Property)*dd->len); + delete [] dd->data; + } + dd->data = newArrayData; + if (dd->attrs) { + PropertyAttributes *newAttrs = new PropertyAttributes[dd->alloc]; + memcpy(newAttrs, dd->attrs, sizeof(PropertyAttributes)*dd->len); + delete [] dd->attrs; + dd->attrs = newAttrs; + } + for (uint i = dd->freeList; i < dd->alloc; ++i) + dd->data[i].value = Primitive::fromInt32(i + 1); +} + +uint SparseArrayData::allocate(ArrayData *d) +{ + Q_ASSERT(d->type == ArrayData::Sparse); + SparseArrayData *dd = static_cast<SparseArrayData *>(d); + uint idx = dd->freeList; + if (dd->alloc == dd->freeList) + reserve(d, d->alloc + 1); + dd->freeList = dd->data[dd->freeList].value.uint_32; + if (dd->attrs) + dd->attrs[idx].setType(PropertyAttributes::Data); + return idx; +} + +ReturnedValue SparseArrayData::get(const ArrayData *d, uint index) +{ + SparseArrayNode *n = static_cast<const SparseArrayData *>(d)->sparse->findNode(index); + if (!n) + return Primitive::emptyValue().asReturnedValue(); + return d->data[n->value].value.asReturnedValue(); +} + +bool SparseArrayData::put(ArrayData *d, uint index, ValueRef value) +{ + // ### honour attributes + SparseArrayNode *n = static_cast<SparseArrayData *>(d)->sparse->insert(index); + if (n->value == UINT_MAX) + n->value = allocate(d); + d->data[n->value].value = value; + return true; +} + +bool SparseArrayData::del(ArrayData *d, uint index) +{ + SparseArrayData *dd = static_cast<SparseArrayData *>(d); + SparseArrayNode *n = dd->sparse->findNode(index); + if (!n) + return true; + + uint pidx = n->value; + Q_ASSERT(!dd->data[pidx].value.isEmpty()); + + if (!dd->attrs || dd->attrs[pidx].isConfigurable()) { + d->data[pidx].value.int_32 = static_cast<SparseArrayData *>(d)->freeList; + static_cast<SparseArrayData *>(d)->freeList = pidx; + static_cast<SparseArrayData *>(d)->sparse->erase(n); + return true; + } + return false; +} + +void SparseArrayData::setAttribute(ArrayData *d, uint index, PropertyAttributes attrs) +{ + SparseArrayNode *n = static_cast<SparseArrayData *>(d)->sparse->insert(index); + if (n->value == UINT_MAX) + n->value = allocate(d); + d->attrs[n->value] = attrs; +} + +PropertyAttributes SparseArrayData::attribute(const ArrayData *d, uint index) +{ + SparseArrayNode *n = static_cast<const SparseArrayData *>(d)->sparse->insert(index); + if (!n) + return PropertyAttributes(); + return d->attrs[n->value]; +} + +void SparseArrayData::push_front(ArrayData *d, SafeValue *values, uint n) +{ + Q_ASSERT(!d->attrs); + for (int i = n - 1; i >= 0; --i) { + uint idx = allocate(d); + d->data[idx].value = values[i]; + static_cast<SparseArrayData *>(d)->sparse->push_front(idx); + } +} + +ReturnedValue SparseArrayData::pop_front(ArrayData *d) +{ + Q_ASSERT(!d->attrs); + uint idx = static_cast<SparseArrayData *>(d)->sparse->pop_front(); + ReturnedValue v; + if (idx != UINT_MAX) { + v = d->data[idx].value.asReturnedValue(); + SparseArrayData::free(d, idx); + } else { + v = Encode::undefined(); + } + return v; +} + +uint SparseArrayData::truncate(ArrayData *d, uint newLen) +{ + SparseArrayNode *begin = static_cast<SparseArrayData *>(d)->sparse->lowerBound(newLen); + if (begin != static_cast<SparseArrayData *>(d)->sparse->end()) { + SparseArrayNode *it = static_cast<SparseArrayData *>(d)->sparse->end()->previousNode(); + while (1) { + Property &pd = d->data[it->value]; + if (d->attrs) { + if (!d->attrs[it->value].isConfigurable()) { + newLen = it->key() + 1; + break; + } + } + pd.value.tag = Value::Empty_Type; + pd.value.int_32 = static_cast<SparseArrayData *>(d)->freeList; + static_cast<SparseArrayData *>(d)->freeList = it->value; + bool brk = (it == begin); + SparseArrayNode *prev = it->previousNode(); + static_cast<SparseArrayData *>(d)->sparse->erase(it); + if (brk) + break; + it = prev; + } + } + return newLen; +} + +bool SparseArrayData::putArray(ArrayData *d, uint index, SafeValue *values, uint n) +{ + for (uint i = 0; i < n; ++i) + put(d, index + i, values[i]); + d->len = qMax(d->len, index + n); + return true; +} + + +uint ArrayData::append(Object *o, const ArrayObject *otherObj, uint n) +{ + ArrayData *d = o->arrayData; + if (!n) + return d->len; + + const ArrayData *other = otherObj->arrayData; + + if (other->isSparse()) { + o->initSparseArray(); + d = o->arrayData; + } + + uint oldSize = d->len; + + // ### copy attributes as well! + if (d->type == ArrayData::Sparse) { + if (other->isSparse()) { + for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other)->sparse->begin(); + it != static_cast<const SparseArrayData *>(other)->sparse->end(); it = it->nextNode()) + // ### accessor properties + o->arraySet(d->len + it->key(), other->data[it->value].value); + } else { + d->vtable->reserve(d, oldSize + n); + memcpy(d->data + oldSize, other->data, n*sizeof(Property)); + if (d->attrs) + std::fill(d->attrs + oldSize, d->attrs + oldSize + n, PropertyAttributes(Attr_Data)); + for (uint i = 0; i < n; ++i) { + SparseArrayNode *n = static_cast<SparseArrayData *>(d)->sparse->insert(d->len + i); + n->value = oldSize + i; + } + } + } else if (other->length()) { + d->vtable->reserve(d, oldSize + other->length()); + if (oldSize > d->len) { + for (uint i = d->len; i < oldSize; ++i) + d->data[i].value = Primitive::emptyValue(); + } + if (other->attrs) { + for (uint i = 0; i < other->len; ++i) { + bool exists; + d->data[oldSize + i].value = const_cast<ArrayObject *>(otherObj)->getIndexed(i, &exists); + d->len = oldSize + i + 1; + o->arrayData->setAttributes(oldSize + i, Attr_Data); + if (!exists) + d->data[oldSize + i].value = Primitive::emptyValue(); + } + } else { + d->len = oldSize + other->len; + memcpy(d->data + oldSize, other->data, other->len*sizeof(Property)); + if (d->attrs) + std::fill(d->attrs + oldSize, d->attrs + oldSize + other->len, PropertyAttributes(Attr_Data)); + } + } + + return oldSize + n; +} + +Property *ArrayData::insert(Object *o, uint index) +{ + Property *pd; + if (o->arrayData->type != ArrayData::Sparse && (index < 0x1000 || index < o->arrayData->len + (o->arrayData->len >> 2))) { + if (index >= o->arrayData->alloc) + o->arrayReserve(index + 1); + if (index >= o->arrayData->len) { + // mark possible hole in the array + for (uint i = o->arrayData->len; i < index; ++i) + o->arrayData->data[i].value = Primitive::emptyValue(); + o->arrayData->len = index + 1; + } + pd = o->arrayData->data + index; + } else { + o->initSparseArray(); + SparseArrayNode *n = static_cast<SparseArrayData *>(o->arrayData)->sparse->insert(index); + if (n->value == UINT_MAX) + n->value = SparseArrayData::allocate(o->arrayData); + pd = o->arrayData->data + n->value; + } + return pd; +} + +void ArrayData::markObjects(ExecutionEngine *e) +{ + if (type == ArrayData::Simple) { + for (uint i = 0; i < len; ++i) + data[i].value.mark(e); + return; + } else { + for (uint i = 0; i < len; ++i) { + const Property &pd = data[i]; + if (attrs && attrs[i].isAccessor()) { + if (pd.getter()) + pd.getter()->mark(e); + if (pd.setter()) + pd.setter()->mark(e); + } else { + pd.value.mark(e); + } + } + } + +} + +void ArrayData::sort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint len) +{ + ArrayData *d = thisObject->arrayData; + if (!d || !d->len) + return; + + if (d->type == ArrayData::Sparse) { + context->throwUnimplemented(QStringLiteral("Object::sort unimplemented for sparse arrays")); + return; + } + + if (len > d->len) + len = d->len; + + // The spec says the sorting goes through a series of get,put and delete operations. + // this implies that the attributes don't get sorted around. + // behavior of accessor properties is implementation defined. We simply turn them all + // into data properties and then sort. This is in line with the sentence above. + if (d->attrs) { + for (uint i = 0; i < len; i++) { + if (d->data[i].value.isEmpty()) { + while (--len > i) + if (!d->data[len].value.isEmpty()) + break; + d->data[i].value = thisObject->getValue(d->data + len, d->attrs ? d->attrs[len] : Attr_Data); + if (d->attrs) + d->attrs[i] = Attr_Data; + d->data[len].value = Primitive::emptyValue(); + } else if (d->attrs && d->attrs[i].isAccessor()) { + d->data[i].value = thisObject->getValue(d->data + i, d->attrs[i]); + d->attrs[i] = Attr_Data; + } + } + } + + if (!(comparefn->isUndefined() || comparefn->asObject())) { + context->throwTypeError(); + return; + } + + ArrayElementLessThan lessThan(context, thisObject, comparefn); + + if (!len) + return; + Property *begin = d->data; + std::sort(begin, begin + len, lessThan); +} diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h new file mode 100644 index 0000000000..98968fada2 --- /dev/null +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** 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 QV4ARRAYDATA_H +#define QV4ARRAYDATA_H + +#include "qv4global_p.h" +#include "qv4managed_p.h" +#include "qv4property_p.h" +#include "qv4sparsearray_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ArrayData; + +struct ArrayVTable +{ + uint type; + void (*freeData)(ArrayData *d); + void (*reserve)(ArrayData *d, uint n); + ReturnedValue (*get)(const ArrayData *d, uint index); + bool (*put)(ArrayData *d, uint index, ValueRef value); + bool (*putArray)(ArrayData *d, uint index, SafeValue *values, uint n); + bool (*del)(ArrayData *d, uint index); + void (*setAttribute)(ArrayData *d, uint index, PropertyAttributes attrs); + PropertyAttributes (*attribute)(const ArrayData *d, uint index); + void (*push_front)(ArrayData *d, SafeValue *values, uint n); + ReturnedValue (*pop_front)(ArrayData *d); + uint (*truncate)(ArrayData *d, uint newLen); +}; + + +struct Q_QML_EXPORT ArrayData +{ + ArrayData() + : vtable(&static_vtbl) + , offset(0) + , len(0) + , alloc(0) + , type(0) + , attrs(0) + , data(0) + { + } + + enum Type { + Simple = 0, + Complex = 1, + Sparse = 2, + Custom = 3 + }; + + const ArrayVTable *vtable; + uint offset; + uint len; + uint alloc; + uint type; + PropertyAttributes *attrs; + Property *data; + + bool isSparse() const { return this && type == Sparse; } + + uint length() const { + return this ? len : 0; + } + void setLength(uint l) { + Q_ASSERT(this); + len = l; + } + + bool hasAttributes() const { + return this && attrs; + } + void ensureAttributes(); + PropertyAttributes attributes(int i) const { + Q_ASSERT(this); + return attrs ? vtable->attribute(this, i) : Attr_Data; + } + void setAttributes(uint i, PropertyAttributes a) { + Q_ASSERT(this); + if (attrs || a != Attr_Data) { + ensureAttributes(); + a.resolve(); + vtable->setAttribute(this, i, a); + } + } + + bool isEmpty(uint i) const { + if (!this) + return true; + return (vtable->get(this, i) == Primitive::emptyValue().asReturnedValue()); + } + + + inline void free() { + vtable->freeData(this); + } + + inline void push_front(SafeValue *values, uint nValues) { + vtable->push_front(this, values, nValues); + } + inline ReturnedValue pop_front() { + return vtable->pop_front(this); + } + inline uint push_back(uint l, uint n, SafeValue *values) { + vtable->putArray(this, l, values, n); + return len; + } + inline bool deleteIndex(uint index) { + return vtable->del(this, index); + } + inline uint truncate(uint newLen) { + if (!this || len < newLen) + return newLen; + return vtable->truncate(this, newLen); + } + bool put(uint index, ValueRef value) { + return vtable->put(this, index, value); + } + bool put(uint index, SafeValue *values, uint n) { + return vtable->putArray(this, index, values, n); + } + ReturnedValue get(uint i) const { + if (!this) + return Primitive::emptyValue().asReturnedValue(); + return vtable->get(this, i); + } + inline Property *getProperty(uint index) const; + + + + static void sort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint dataLen); + static uint append(Object *o, const ArrayObject *otherObj, uint n); + static Property *insert(Object *o, uint index); + void markObjects(ExecutionEngine *e); + + static void getHeadRoom(ArrayData *d); + static void reserve(ArrayData *d, uint n); + + static void freeData(ArrayData *d); + static ReturnedValue get(const ArrayData *d, uint index); + static bool put(ArrayData *d, uint index, ValueRef value); + static bool putArray(ArrayData *d, uint index, SafeValue *values, uint n); + static bool del(ArrayData *d, uint index); + static void setAttribute(ArrayData *d, uint index, PropertyAttributes attrs); + static PropertyAttributes attribute(const ArrayData *d, uint index); + static void push_front(ArrayData *d, SafeValue *values, uint n); + static ReturnedValue pop_front(ArrayData *d); + static uint truncate(ArrayData *d, uint newLen); + + static const ArrayVTable static_vtbl; +}; + +struct Q_QML_EXPORT SparseArrayData : public ArrayData +{ + SparseArrayData() + : freeList(0) + , sparse(0) + { vtable = &static_vtbl; } + + uint freeList; + SparseArray *sparse; + + static uint allocate(ArrayData *d); + static void free(ArrayData *d, uint idx); + + static void freeData(ArrayData *d); + static void reserve(ArrayData *d, uint n); + static ReturnedValue get(const ArrayData *d, uint index); + static bool put(ArrayData *d, uint index, ValueRef value); + static bool putArray(ArrayData *d, uint index, SafeValue *values, uint n); + static bool del(ArrayData *d, uint index); + static void setAttribute(ArrayData *d, uint index, PropertyAttributes attrs); + static PropertyAttributes attribute(const ArrayData *d, uint index); + static void push_front(ArrayData *d, SafeValue *values, uint n); + static ReturnedValue pop_front(ArrayData *d); + static uint truncate(ArrayData *d, uint newLen); + + static const ArrayVTable static_vtbl; +}; + + +inline Property *ArrayData::getProperty(uint index) const +{ + if (!this) + return 0; + if (type != Sparse) { + if (index >= len || data[index].value.isEmpty()) + return 0; + return data + index; + } else { + SparseArrayNode *n = static_cast<const SparseArrayData *>(this)->sparse->findNode(index); + if (!n) + return 0; + return data + n->value; + } +} + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 296471692c..eb8a5301de 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -72,9 +72,8 @@ ReturnedValue ArrayCtor::construct(Managed *m, CallData *callData) } else { len = callData->argc; a->arrayReserve(len); - for (unsigned int i = 0; i < len; ++i) - a->arrayData[i].value = callData->args[i]; - a->arrayDataLen = len; + a->arrayData->put(0, callData->args, len); + a->arrayData->setLength(len); } a->setArrayLengthUnchecked(len); @@ -122,15 +121,6 @@ void ArrayPrototype::init(ExecutionEngine *engine, ObjectRef ctor) defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1); } -uint ArrayPrototype::getLength(ExecutionContext *ctx, ObjectRef o) -{ - if (o->isArrayObject()) - return o->arrayLength(); - Scope scope(ctx); - ScopedValue v(scope, o->get(ctx->engine->id_length)); - return v->toUInt32(); -} - ReturnedValue ArrayPrototype::method_isArray(CallContext *ctx) { bool isArray = ctx->callData->argc && ctx->callData->args[0].asArrayObject(); @@ -180,15 +170,17 @@ ReturnedValue ArrayPrototype::method_concat(CallContext *ctx) eltAsObj = ctx->callData->args[i]; elt = ctx->callData->args[i]; if (elt) { - result->arrayConcat(elt.getPointer()); + uint n = elt->getLength(); + uint newLen = ArrayData::append(result.getPointer(), elt.getPointer(), n); + result->setArrayLengthUnchecked(newLen); } else if (eltAsObj && eltAsObj->isListType()) { - const uint startIndex = getLength(ctx, result); - for (int i = 0, len = getLength(ctx, eltAsObj); i < len; ++i) { + const uint startIndex = result->getLength(); + for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) { entry = eltAsObj->getIndexed(i); result->putIndexed(startIndex + i, entry); } } else { - result->arraySet(getLength(ctx, result), ctx->callData->args[i]); + result->arraySet(result->getLength(), ctx->callData->args[i]); } } @@ -218,7 +210,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) // ### FIXME if (ArrayObject *a = self->asArrayObject()) { ScopedValue e(scope); - for (uint i = 0; i < a->arrayLength(); ++i) { + for (uint i = 0; i < a->getLength(); ++i) { if (i) R += r4; @@ -260,7 +252,7 @@ ReturnedValue ArrayPrototype::method_pop(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); if (!len) { if (!instance->isArrayObject()) @@ -288,7 +280,10 @@ ReturnedValue ArrayPrototype::method_push(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + + instance->arrayCreate(); + + uint len = instance->getLength(); if (len + ctx->callData->argc < len) { // ughh... @@ -308,21 +303,8 @@ ReturnedValue ArrayPrototype::method_push(CallContext *ctx) return Encode(newLen); } - if (!instance->protoHasArray() && instance->arrayDataLen <= len) { - for (int i = 0; i < ctx->callData->argc; ++i) { - if (!instance->sparseArray) { - if (len >= instance->arrayAlloc) - instance->arrayReserve(len + 1); - instance->arrayData[len].value = ctx->callData->args[i]; - if (instance->arrayAttributes) - instance->arrayAttributes[len] = Attr_Data; - instance->arrayDataLen = len + 1; - } else { - uint j = instance->allocArrayValue(ctx->callData->args[i]); - instance->sparseArray->push_back(j, len); - } - ++len; - } + if (!instance->protoHasArray() && instance->arrayData->length() <= len) { + len = instance->arrayData->push_back(len, ctx->callData->argc, ctx->callData->args); } else { for (int i = 0; i < ctx->callData->argc; ++i) instance->putIndexed(len + i, ctx->callData->args[i]); @@ -342,7 +324,7 @@ ReturnedValue ArrayPrototype::method_reverse(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint length = getLength(ctx, instance); + uint length = instance->getLength(); int lo = 0, hi = length - 1; @@ -374,7 +356,10 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + + instance->arrayCreate(); + + uint len = instance->getLength(); if (!len) { if (!instance->isArrayObject()) @@ -382,28 +367,14 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx) return Encode::undefined(); } - Property *front = 0; - uint pidx = instance->propertyIndexFromArrayIndex(0); - if (pidx < UINT_MAX && !instance->arrayData[pidx].value.isEmpty()) - front = instance->arrayData + pidx; + ScopedValue result(scope); - ScopedValue result(scope, front ? instance->getValue(front, instance->arrayAttributes ? instance->arrayAttributes[pidx] : Attr_Data) : Encode::undefined()); - - if (!instance->protoHasArray() && instance->arrayDataLen <= len) { - if (!instance->sparseArray) { - if (instance->arrayDataLen) { - ++instance->arrayOffset; - ++instance->arrayData; - --instance->arrayDataLen; - --instance->arrayAlloc; - if (instance->arrayAttributes) - ++instance->arrayAttributes; - } - } else { - uint idx = instance->sparseArray->pop_front(); - instance->freeArrayValue(idx); - } + if (!instance->protoHasArray() && !instance->arrayData->hasAttributes() && instance->arrayData->length() <= len) { + result = instance->arrayData->pop_front(); } else { + result = instance->getIndexed(0); + if (scope.hasException()) + return Encode::undefined(); ScopedValue v(scope); // do it the slow way for (uint k = 1; k < len; ++k) { @@ -415,6 +386,8 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx) instance->putIndexed(k - 1, v); else instance->deleteIndexedProperty(k - 1); + if (scope.hasException()) + return Encode::undefined(); } instance->deleteIndexedProperty(len - 1); if (scope.hasException()) @@ -436,7 +409,7 @@ ReturnedValue ArrayPrototype::method_slice(CallContext *ctx) return Encode::undefined(); Scoped<ArrayObject> result(scope, ctx->engine->newArrayObject()); - uint len = getLength(ctx, o); + uint len = o->getLength(); double s = ScopedValue(scope, ctx->argument(0))->toInteger(); uint start; if (s < 0) @@ -477,10 +450,10 @@ ReturnedValue ArrayPrototype::method_sort(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); ScopedValue comparefn(scope, ctx->argument(0)); - instance->arraySort(ctx, instance, comparefn, len); + ArrayData::sort(ctx, instance, comparefn, len); return ctx->callData->thisObject.asReturnedValue(); } @@ -490,7 +463,7 @@ ReturnedValue ArrayPrototype::method_splice(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<ArrayObject> newArray(scope, ctx->engine->newArrayObject()); @@ -504,17 +477,21 @@ ReturnedValue ArrayPrototype::method_splice(CallContext *ctx) uint deleteCount = (uint)qMin(qMax(ScopedValue(scope, ctx->argument(1))->toInteger(), 0.), (double)(len - start)); newArray->arrayReserve(deleteCount); + ScopedValue v(scope); for (uint i = 0; i < deleteCount; ++i) { - newArray->arrayData[i].value = instance->getIndexed(start + i); + bool exists; + v = instance->getIndexed(start + i, &exists); if (scope.hasException()) return Encode::undefined(); - newArray->arrayDataLen = i + 1; + if (exists) { + newArray->arrayData->put(i, v); + newArray->arrayData->setLength(i + 1); + } } newArray->setArrayLengthUnchecked(deleteCount); uint itemCount = ctx->callData->argc < 2 ? 0 : ctx->callData->argc - 2; - ScopedValue v(scope); if (itemCount < deleteCount) { for (uint k = start; k < len - deleteCount; ++k) { bool exists; @@ -568,32 +545,15 @@ ReturnedValue ArrayPrototype::method_unshift(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); - ScopedValue v(scope); - if (!instance->protoHasArray() && instance->arrayDataLen <= len) { - for (int i = ctx->callData->argc - 1; i >= 0; --i) { - v = ctx->argument(i); - - if (!instance->sparseArray) { - if (!instance->arrayOffset) - instance->getArrayHeadRoom(); - - --instance->arrayOffset; - --instance->arrayData; - ++instance->arrayDataLen; - ++instance->arrayAlloc; - if (instance->arrayAttributes) { - --instance->arrayAttributes; - *instance->arrayAttributes = Attr_Data; - } - instance->arrayData->value = v.asReturnedValue(); - } else { - uint idx = instance->allocArrayValue(v); - instance->sparseArray->push_front(idx); - } - } + instance->arrayCreate(); + + uint len = instance->getLength(); + + if (!instance->protoHasArray() && !instance->arrayData->hasAttributes() && instance->arrayData->length() <= len) { + instance->arrayData->push_front(ctx->callData->args, ctx->callData->argc); } else { + ScopedValue v(scope); for (uint k = len; k > 0; --k) { bool exists; v = instance->getIndexed(k - 1, &exists); @@ -622,7 +582,7 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); if (!len) return Encode(-1); @@ -651,7 +611,48 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx) return Encode(-1); } - return instance->arrayIndexOf(searchValue, fromIndex, len, ctx, instance.getPointer()); + ScopedValue value(scope); + + if ((instance->arrayData && instance->arrayType() != ArrayData::Simple) || instance->protoHasArray()) { + // lets be safe and slow + for (uint i = fromIndex; i < len; ++i) { + bool exists; + value = instance->getIndexed(i, &exists); + if (scope.hasException()) + return Encode::undefined(); + if (exists && __qmljs_strict_equal(value, searchValue)) + return Encode(i); + } + } else if (!instance->arrayData) { + return Encode(-1); + } else if (instance->arrayType() == ArrayData::Sparse) { + for (SparseArrayNode *n = static_cast<SparseArrayData *>(instance->arrayData)->sparse->lowerBound(fromIndex); + n != static_cast<SparseArrayData *>(instance->arrayData)->sparse->end() && n->key() < len; n = n->nextNode()) { + value = instance->getValue(instance->arrayData->data + n->value, + instance->arrayData->attrs ? instance->arrayData->attrs[n->value] : Attr_Data); + if (scope.hasException()) + return Encode::undefined(); + if (__qmljs_strict_equal(value, searchValue)) + return Encode(n->key()); + } + } else { + if (len > instance->arrayData->length()) + len = instance->arrayData->length(); + Property *pd = instance->arrayData->data; + Property *end = pd + len; + pd += fromIndex; + while (pd < end) { + if (!pd->value.isEmpty()) { + value = instance->getValue(pd, instance->arrayData->attributes(pd - instance->arrayData->data)); + if (scope.hasException()) + return Encode::undefined(); + if (__qmljs_strict_equal(value, searchValue)) + return Encode((uint)(pd - instance->arrayData->data)); + } + ++pd; + } + } + return Encode(-1); } ReturnedValue ArrayPrototype::method_lastIndexOf(CallContext *ctx) @@ -661,7 +662,7 @@ ReturnedValue ArrayPrototype::method_lastIndexOf(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); if (!len) return Encode(-1); @@ -707,7 +708,7 @@ ReturnedValue ArrayPrototype::method_every(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) @@ -741,7 +742,7 @@ ReturnedValue ArrayPrototype::method_some(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) @@ -775,7 +776,7 @@ ReturnedValue ArrayPrototype::method_forEach(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) @@ -806,7 +807,7 @@ ReturnedValue ArrayPrototype::method_map(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) @@ -843,7 +844,7 @@ ReturnedValue ArrayPrototype::method_filter(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) @@ -884,7 +885,7 @@ ReturnedValue ArrayPrototype::method_reduce(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) @@ -934,7 +935,7 @@ ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index 7e809f9064..641be86e2c 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -64,8 +64,6 @@ struct ArrayPrototype: ArrayObject void init(ExecutionEngine *engine, ObjectRef ctor); - static uint getLength(ExecutionContext *ctx, ObjectRef o); - static ReturnedValue method_isArray(CallContext *ctx); static ReturnedValue method_toString(CallContext *ctx); static ReturnedValue method_toLocaleString(CallContext *ctx); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index c2c9aefd5f..36b1512b63 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -53,6 +53,13 @@ using namespace QV4; const ManagedVTable ExecutionContext::static_vtbl = { + ExecutionContext::IsExecutionContext, + ExecutionContext::IsString, + ExecutionContext::IsObject, + ExecutionContext::IsFunctionObject, + ExecutionContext::IsErrorObject, + 0, + ExecutionContext::MyType, call, construct, markObjects, @@ -70,6 +77,7 @@ const ManagedVTable ExecutionContext::static_vtbl = 0, isEqualTo, 0, + 0, "ExecutionContext", }; @@ -349,16 +357,9 @@ void ExecutionContext::setProperty(const StringRef name, const ValueRef value) } if (activation) { - if (ctx->type == Type_QmlContext) { + if (ctx->type == Type_QmlContext || activation->hasOwnProperty(name)) { activation->put(name, value); return; - } else { - PropertyAttributes attrs; - Property *p = activation->__getOwnProperty__(name, &attrs); - if (p) { - activation->putValue(p, attrs, value); - return; - } } } } diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 334d033193..a0930a577e 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -70,6 +70,10 @@ struct WithContext; struct Q_QML_EXPORT ExecutionContext : public Managed { Q_MANAGED + Q_MANAGED_TYPE(ExecutionContext) + enum { + IsExecutionContext = true + }; enum ContextType { Type_GlobalContext = 0x1, diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 5d0c8ccf8e..2b6aa21ac2 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -647,7 +647,6 @@ DateObject::DateObject(ExecutionEngine *engine, const QDateTime &date) : Object(engine->dateClass) { setVTable(&static_vtbl); - type = Type_DateObject; value.setDouble(date.isValid() ? date.toMSecsSinceEpoch() : qSNaN()); } diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index 9c451dd251..244553b1d4 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -53,9 +53,9 @@ namespace QV4 { struct DateObject: Object { Q_MANAGED + Q_MANAGED_TYPE(DateObject) SafeValue value; DateObject(ExecutionEngine *engine, const ValueRef date): Object(engine->dateClass) { - type = Type_DateObject; value = date; } DateObject(ExecutionEngine *engine, const QDateTime &value); @@ -64,8 +64,7 @@ struct DateObject: Object { protected: DateObject(InternalClass *ic): Object(ic) { - setVTable(&static_vtbl); - type = Type_DateObject; + Q_ASSERT(internalClass->vtable == &static_vtbl); value = Primitive::fromDouble(qSNaN()); } }; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index ac18e56868..5de8e0de44 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -202,9 +202,9 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) identifierTable = new IdentifierTable(this); emptyClass = new (classPool.allocate(sizeof(InternalClass))) InternalClass(this); - executionContextClass = emptyClass->changeVTable(&ExecutionContext::static_vtbl); - stringClass = emptyClass->changeVTable(&String::static_vtbl); - regExpValueClass = emptyClass->changeVTable(&RegExp::static_vtbl); + executionContextClass = InternalClass::create(this, &ExecutionContext::static_vtbl, 0); + stringClass = InternalClass::create(this, &String::static_vtbl, 0); + regExpValueClass = InternalClass::create(this, &RegExp::static_vtbl, 0); id_undefined = newIdentifier(QStringLiteral("undefined")); id_null = newIdentifier(QStringLiteral("null")); @@ -237,7 +237,7 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) id_toString = newIdentifier(QStringLiteral("toString")); id_valueOf = newIdentifier(QStringLiteral("valueOf")); - ObjectPrototype *objectPrototype = new (memoryManager) ObjectPrototype(emptyClass->changeVTable(&ObjectPrototype::static_vtbl)); + ObjectPrototype *objectPrototype = new (memoryManager) ObjectPrototype(InternalClass::create(this, &ObjectPrototype::static_vtbl, 0)); objectClass = InternalClass::create(this, &Object::static_vtbl, objectPrototype); Q_ASSERT(objectClass->vtable == &Object::static_vtbl); @@ -256,16 +256,16 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) initRootContext(); - StringPrototype *stringPrototype = new (memoryManager) StringPrototype(objectClass); + StringPrototype *stringPrototype = new (memoryManager) StringPrototype(InternalClass::create(this, &StringPrototype::static_vtbl, objectPrototype)); stringObjectClass = InternalClass::create(this, &String::static_vtbl, stringPrototype); - NumberPrototype *numberPrototype = new (memoryManager) NumberPrototype(objectClass); + NumberPrototype *numberPrototype = new (memoryManager) NumberPrototype(InternalClass::create(this, &NumberPrototype::static_vtbl, objectPrototype)); numberClass = InternalClass::create(this, &NumberObject::static_vtbl, numberPrototype); - BooleanPrototype *booleanPrototype = new (memoryManager) BooleanPrototype(objectClass); + BooleanPrototype *booleanPrototype = new (memoryManager) BooleanPrototype(InternalClass::create(this, &BooleanPrototype::static_vtbl, objectPrototype)); booleanClass = InternalClass::create(this, &BooleanObject::static_vtbl, booleanPrototype); - DatePrototype *datePrototype = new (memoryManager) DatePrototype(objectClass); + DatePrototype *datePrototype = new (memoryManager) DatePrototype(InternalClass::create(this, &DatePrototype::static_vtbl, objectPrototype)); dateClass = InternalClass::create(this, &DateObject::static_vtbl, datePrototype); FunctionPrototype *functionPrototype = new (memoryManager) FunctionPrototype(InternalClass::create(this, &FunctionPrototype::static_vtbl, objectPrototype)); @@ -276,14 +276,14 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) protoClass = objectClass->addMember(id_constructor, Attr_NotEnumerable, &index); Q_ASSERT(index == FunctionObject::Index_ProtoConstructor); - RegExpPrototype *regExpPrototype = new (memoryManager) RegExpPrototype(objectClass); + RegExpPrototype *regExpPrototype = new (memoryManager) RegExpPrototype(InternalClass::create(this, &RegExpPrototype::static_vtbl, objectPrototype)); regExpClass = InternalClass::create(this, &RegExpObject::static_vtbl, regExpPrototype); regExpExecArrayClass = arrayClass->addMember(id_index, Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayIndex); regExpExecArrayClass = regExpExecArrayClass->addMember(id_input, Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayInput); - ErrorPrototype *errorPrototype = new (memoryManager) ErrorPrototype(objectClass); + ErrorPrototype *errorPrototype = new (memoryManager) ErrorPrototype(InternalClass::create(this, &ErrorObject::static_vtbl, objectPrototype)); errorClass = InternalClass::create(this, &ErrorObject::static_vtbl, errorPrototype); EvalErrorPrototype *evalErrorPrototype = new (memoryManager) EvalErrorPrototype(errorClass); evalErrorClass = InternalClass::create(this, &EvalErrorObject::static_vtbl, evalErrorPrototype); @@ -364,8 +364,8 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) globalObject->defineDefaultProperty(QStringLiteral("TypeError"), typeErrorCtor); globalObject->defineDefaultProperty(QStringLiteral("URIError"), uRIErrorCtor); ScopedObject o(scope); - globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = new (memoryManager) MathObject(this))); - globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = new (memoryManager) JsonObject(this))); + globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = new (memoryManager) MathObject(QV4::InternalClass::create(this, &MathObject::static_vtbl, objectPrototype)))); + globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = new (memoryManager) JsonObject(QV4::InternalClass::create(this, &JsonObject::static_vtbl, objectPrototype)))); globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Primitive::undefinedValue()); globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(std::numeric_limits<double>::quiet_NaN())); diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index cf5c06dd41..f5d21b2f30 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -76,8 +76,6 @@ ErrorObject::ErrorObject(InternalClass *ic) : Object(ic) , stack(0) { - type = Type_ErrorObject; - Scope scope(engine()); ScopedValue protectThis(scope, this); @@ -89,7 +87,6 @@ ErrorObject::ErrorObject(InternalClass *ic, const ValueRef message, ErrorType t) : Object(ic) , stack(0) { - type = Type_ErrorObject; subtype = t; Scope scope(engine()); @@ -113,7 +110,6 @@ ErrorObject::ErrorObject(InternalClass *ic, const QString &message, ErrorObject: : Object(ic) , stack(0) { - type = Type_ErrorObject; subtype = t; Scope scope(engine()); @@ -137,7 +133,6 @@ ErrorObject::ErrorObject(InternalClass *ic, const QString &message, const QStrin : Object(ic) , stack(0) { - type = Type_ErrorObject; subtype = t; Scope scope(engine()); diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index def776d3b6..560b003062 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -52,6 +52,10 @@ struct SyntaxErrorObject; struct ErrorObject: Object { Q_MANAGED + Q_MANAGED_TYPE(ErrorObject) + enum { + IsErrorObject = true + }; enum ErrorType { Error, diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index ce81282aa3..daa3d5b0de 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -112,7 +112,6 @@ FunctionObject::FunctionObject(InternalClass *ic) { name = ic->engine->id_undefined; - type = Type_FunctionObject; needsActivation = false; strictMode = false; } @@ -130,7 +129,6 @@ void FunctionObject::init(const StringRef n, bool createProto) Scope s(internalClass->engine); ScopedValue protectThis(s, this); - type = Type_FunctionObject; needsActivation = true; strictMode = false; @@ -342,19 +340,19 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx) if (!arg->isNullOrUndefined()) return ctx->throwTypeError(); } else { - len = ArrayPrototype::getLength(ctx, arr); + len = arr->getLength(); } ScopedCallData callData(scope, len); if (len) { - if (!(arr->flags & SimpleArray) || arr->protoHasArray() || arr->hasAccessorProperty) { + if (arr->arrayType() != ArrayData::Simple || arr->protoHasArray() || arr->hasAccessorProperty) { for (quint32 i = 0; i < len; ++i) callData->args[i] = arr->getIndexed(i); } else { - int alen = qMin(len, arr->arrayDataLen); + int alen = qMin(len, arr->arrayData->length()); for (int i = 0; i < alen; ++i) - callData->args[i] = arr->arrayData[i].value; + callData->args[i] = arr->arrayData->data[i].value; for (quint32 i = alen; i < len; ++i) callData->args[i] = Primitive::undefinedValue(); } @@ -426,8 +424,8 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) if (scope->strictMode) { Property pd = Property::fromAccessor(v4->thrower, v4->thrower); - *insertMember(scope->engine->id_caller, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; - *insertMember(scope->engine->id_arguments, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; + insertMember(scope->engine->id_caller, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + insertMember(scope->engine->id_arguments, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } } @@ -508,8 +506,8 @@ SimpleScriptFunction::SimpleScriptFunction(ExecutionContext *scope, Function *fu if (scope->strictMode) { Property pd = Property::fromAccessor(v4->thrower, v4->thrower); - *insertMember(scope->engine->id_caller, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; - *insertMember(scope->engine->id_arguments, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; + insertMember(scope->engine->id_caller, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + insertMember(scope->engine->id_arguments, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } } @@ -667,8 +665,8 @@ BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObjectRef target, ExecutionEngine *v4 = scope->engine; Property pd = Property::fromAccessor(v4->thrower, v4->thrower); - *insertMember(scope->engine->id_arguments, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; - *insertMember(scope->engine->id_caller, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; + insertMember(scope->engine->id_arguments, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + insertMember(scope->engine->id_caller, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } void BoundFunction::destroy(Managed *that) diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 96534cb68c..0168e13a6f 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -94,6 +94,10 @@ struct Lookup; struct Q_QML_EXPORT FunctionObject: Object { Q_MANAGED + Q_MANAGED_TYPE(FunctionObject) + enum { + IsFunctionObject = true + }; // Used with Managed::subType enum FunctionType { RegularFunction = 0, diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 29ede3d104..761a180722 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -183,6 +183,8 @@ InternalClass *InternalClass::changeMember(String *string, PropertyAttributes da InternalClass *InternalClass::create(ExecutionEngine *engine, const ManagedVTable *vtable, Object *proto) { InternalClass *c = engine->emptyClass->changeVTable(vtable); + if (!proto) + return c; return c->changePrototype(proto); } diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 6633435668..f40c765327 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -66,6 +66,8 @@ static int indent = 0; #endif +DEFINE_MANAGED_VTABLE(JsonObject); + class JsonParser { public: @@ -288,8 +290,7 @@ bool JsonParser::parseMember(ObjectRef o) if (idx < UINT_MAX) { o->putIndexed(idx, val); } else { - Property *p = o->insertMember(s, Attr_Data); - p->value = val.asReturnedValue(); + o->insertMember(s, val); } END; @@ -337,7 +338,7 @@ ReturnedValue JsonParser::parseArray() } } - DEBUG << "size =" << array->arrayLength(); + DEBUG << "size =" << array->getLength(); END; --nestingLevel; @@ -853,7 +854,7 @@ QString Stringify::JA(ArrayObjectRef a) indent += gap; QStringList partial; - uint len = a->arrayLength(); + uint len = a->getLength(); ScopedValue v(scope); for (uint i = 0; i < len; ++i) { bool exists; @@ -884,12 +885,10 @@ QString Stringify::JA(ArrayObjectRef a) } -JsonObject::JsonObject(ExecutionEngine *engine) - : Object(engine) +JsonObject::JsonObject(InternalClass *ic) + : Object(ic) { - type = Type_JSONObject; - - Scope scope(engine); + Scope scope(ic->engine); ScopedObject protectThis(scope, this); defineDefaultProperty(QStringLiteral("parse"), method_parse, 2); @@ -925,7 +924,7 @@ ReturnedValue JsonObject::method_stringify(CallContext *ctx) if (o) { stringify.replacerFunction = o->asFunctionObject(); if (o->isArrayObject()) { - uint arrayLen = o->arrayLength(); + uint arrayLen = o->getLength(); ScopedValue v(scope); for (uint i = 0; i < arrayLen; ++i) { v = o->getIndexed(i); @@ -1057,9 +1056,10 @@ QV4::ReturnedValue JsonObject::fromJsonArray(ExecutionEngine *engine, const QJso int size = array.size(); Scoped<ArrayObject> a(scope, engine->newArrayObject()); a->arrayReserve(size); + ScopedValue v(scope); for (int i = 0; i < size; i++) { - a->arrayData[i].value = fromJsonValue(engine, array.at(i)); - a->arrayDataLen = i + 1; + a->arrayData->put(i, (v = fromJsonValue(engine, array.at(i)))); + a->arrayData->setLength(i + 1); } a->setArrayLengthUnchecked(size); return a.asReturnedValue(); @@ -1083,7 +1083,7 @@ QJsonArray JsonObject::toJsonArray(ArrayObjectRef a, V4ObjectSet &visitedObjects visitedObjects.insert(a); ScopedValue v(scope); - quint32 length = a->arrayLength(); + quint32 length = a->getLength(); for (quint32 i = 0; i < length; ++i) { v = a->getIndexed(i); if (v->asFunctionObject()) diff --git a/src/qml/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h index 3bcbdeadbf..b37b28b078 100644 --- a/src/qml/jsruntime/qv4jsonobject_p.h +++ b/src/qml/jsruntime/qv4jsonobject_p.h @@ -51,10 +51,12 @@ QT_BEGIN_NAMESPACE namespace QV4 { struct JsonObject : Object { + Q_MANAGED_TYPE(JsonObject) + Q_MANAGED private: typedef QSet<QV4::Object *> V4ObjectSet; public: - JsonObject(ExecutionEngine *engine); + JsonObject(InternalClass *ic); static ReturnedValue method_parse(CallContext *ctx); static ReturnedValue method_stringify(CallContext *ctx); diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index a870cdac61..66dc7b7e4a 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -316,6 +316,15 @@ ReturnedValue Lookup::stringLengthGetter(Lookup *l, const ValueRef object) return getterGeneric(l, object); } +ReturnedValue Lookup::arrayLengthGetter(Lookup *l, const ValueRef object) +{ + if (ArrayObject *a = object->asArrayObject()) + return a->memberData[ArrayObject::LengthPropertyIndex].value.asReturnedValue(); + + l->getter = getterGeneric; + return getterGeneric(l, object); +} + ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionContext *ctx) { diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 9966d36604..93c2e2156e 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -85,6 +85,7 @@ struct Lookup { static ReturnedValue primitiveGetterAccessor0(Lookup *l, const ValueRef object); static ReturnedValue primitiveGetterAccessor1(Lookup *l, const ValueRef object); static ReturnedValue stringLengthGetter(Lookup *l, const ValueRef object); + static ReturnedValue arrayLengthGetter(Lookup *l, const ValueRef object); static ReturnedValue globalGetterGeneric(Lookup *l, ExecutionContext *ctx); static ReturnedValue globalGetter0(Lookup *l, ExecutionContext *ctx); diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index fef7489110..c3dec2535b 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -47,6 +47,13 @@ using namespace QV4; const ManagedVTable Managed::static_vtbl = { + Managed::IsExecutionContext, + Managed::IsString, + Managed::IsObject, + Managed::IsFunctionObject, + Managed::IsErrorObject, + 0, + Managed::MyType, call, construct, 0 /*markObjects*/, @@ -64,6 +71,7 @@ const ManagedVTable Managed::static_vtbl = 0, isEqualTo, 0, + 0, "Managed", }; @@ -101,7 +109,7 @@ ExecutionEngine *Managed::engine() const QString Managed::className() const { const char *s = 0; - switch (Type(type)) { + switch (Type(internalClass->vtable->type)) { case Type_Invalid: case Type_String: return QString(); @@ -157,18 +165,23 @@ QString Managed::className() const case Type_ArgumentsObject: s = "Arguments"; break; - case Type_JSONObject: + case Type_JsonObject: s = "JSON"; break; case Type_MathObject: s = "Math"; break; + + case Type_ExecutionContext: + s = "__ExecutionContext"; + break; case Type_ForeachIteratorObject: s = "__ForeachIterator"; break; case Type_RegExp: - s = "RegExp"; + s = "__RegExp"; break; + case Type_QmlSequence: s = "QmlSequence"; break; diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index e10409f397..932dbe818c 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -53,8 +53,8 @@ QT_BEGIN_NAMESPACE namespace QV4 { #define Q_MANAGED_CHECK \ - template <typename T> inline void qt_check_for_QMANAGED_macro(const T &_q_argument) const \ - { int i = qYouForgotTheQ_MANAGED_Macro(this, &_q_argument); i = i + 1; } + template <typename T> inline void qt_check_for_QMANAGED_macro(const T *_q_argument) const \ + { int i = qYouForgotTheQ_MANAGED_Macro(this, _q_argument); i = i + 1; } template <typename T> inline int qYouForgotTheQ_MANAGED_Macro(T, T) { return 0; } @@ -69,6 +69,9 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} template <typename T> \ QV4::Returned<T> *asReturned() { return QV4::Returned<T>::create(this); } \ +#define Q_MANAGED_TYPE(type) \ + public: \ + enum { MyType = Type_##type }; struct GCDeletable { @@ -80,6 +83,13 @@ struct GCDeletable struct ManagedVTable { + uint isExecutionContext : 1; + uint isString : 1; + uint isObject : 1; + uint isFunctionObject : 1; + uint isErrorObject : 1; + uint unused : 19; + uint type : 8; ReturnedValue (*call)(Managed *, CallData *data); ReturnedValue (*construct)(Managed *, CallData *data); void (*markObjects)(Managed *, ExecutionEngine *e); @@ -96,6 +106,7 @@ struct ManagedVTable ReturnedValue (*getLookup)(Managed *m, Lookup *l); void (*setLookup)(Managed *m, Lookup *l, const ValueRef v); bool (*isEqualTo)(Managed *m, Managed *other); + uint (*getLength)(const Managed *m); Property *(*advanceIterator)(Managed *m, ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attributes); const char *className; }; @@ -103,6 +114,13 @@ struct ManagedVTable #define DEFINE_MANAGED_VTABLE(classname) \ const QV4::ManagedVTable classname::static_vtbl = \ { \ + classname::IsExecutionContext, \ + classname::IsString, \ + classname::IsObject, \ + classname::IsFunctionObject, \ + classname::IsErrorObject, \ + 0, \ + classname::MyType, \ call, \ construct, \ markObjects, \ @@ -119,13 +137,52 @@ const QV4::ManagedVTable classname::static_vtbl = \ getLookup, \ setLookup, \ isEqualTo, \ + getLength, \ advanceIterator, \ #classname \ } +#define DEFINE_MANAGED_VTABLE_WITH_NAME(classname, name) \ +const QV4::ManagedVTable classname::static_vtbl = \ +{ \ + classname::IsExecutionContext, \ + classname::IsString, \ + classname::IsObject, \ + classname::IsFunctionObject, \ + classname::IsErrorObject, \ + 0, \ + classname::MyType, \ + call, \ + construct, \ + markObjects, \ + destroy, \ + 0, \ + get, \ + getIndexed, \ + put, \ + putIndexed, \ + query, \ + queryIndexed, \ + deleteProperty, \ + deleteIndexedProperty, \ + getLookup, \ + setLookup, \ + isEqualTo, \ + getLength, \ + advanceIterator, \ + #name \ +} + #define DEFINE_MANAGED_VTABLE_WITH_DELETABLES(classname) \ const QV4::ManagedVTable classname::static_vtbl = \ { \ + classname::IsExecutionContext, \ + classname::IsString, \ + classname::IsObject, \ + classname::IsFunctionObject, \ + classname::IsErrorObject, \ + 0, \ + classname::MyType, \ call, \ construct, \ markObjects, \ @@ -142,6 +199,7 @@ const QV4::ManagedVTable classname::static_vtbl = \ getLookup, \ setLookup, \ isEqualTo, \ + getLength, \ advanceIterator, \ #classname \ } @@ -149,6 +207,13 @@ const QV4::ManagedVTable classname::static_vtbl = \ struct Q_QML_EXPORT Managed { Q_MANAGED + enum { + IsExecutionContext = false, + IsString = false, + IsObject = false, + IsFunctionObject = false, + IsErrorObject = false + }; private: void *operator new(size_t); Managed(const Managed &other); @@ -183,13 +248,16 @@ public: Type_RegExpObject, Type_ErrorObject, Type_ArgumentsObject, - Type_JSONObject, + Type_JsonObject, Type_MathObject, + + Type_ExecutionContext, Type_ForeachIteratorObject, Type_RegExp, Type_QmlSequence }; + Q_MANAGED_TYPE(Invalid) ExecutionEngine *engine() const; @@ -199,7 +267,7 @@ public: if (!this || !internalClass) return 0; #if !defined(QT_NO_QOBJECT_CHECK) - static_cast<T *>(this)->qt_check_for_QMANAGED_macro(*static_cast<T *>(this)); + static_cast<T *>(this)->qt_check_for_QMANAGED_macro(static_cast<T *>(this)); #endif return internalClass->vtable == &T::static_vtbl ? static_cast<T *>(this) : 0; } @@ -209,26 +277,26 @@ public: if (!this) return 0; #if !defined(QT_NO_QOBJECT_CHECK) - reinterpret_cast<T *>(this)->qt_check_for_QMANAGED_macro(*reinterpret_cast<T *>(const_cast<Managed *>(this))); + static_cast<T *>(this)->qt_check_for_QMANAGED_macro(static_cast<T *>(const_cast<Managed *>(this))); #endif return internalClass->vtable == &T::static_vtbl ? static_cast<const T *>(this) : 0; } - String *asString() { return type == Type_String ? reinterpret_cast<String *>(this) : 0; } - Object *asObject() { return type != Type_String ? reinterpret_cast<Object *>(this) : 0; } - ArrayObject *asArrayObject() { return type == Type_ArrayObject ? reinterpret_cast<ArrayObject *>(this) : 0; } - FunctionObject *asFunctionObject() { return type == Type_FunctionObject ? reinterpret_cast<FunctionObject *>(this) : 0; } - BooleanObject *asBooleanObject() { return type == Type_BooleanObject ? reinterpret_cast<BooleanObject *>(this) : 0; } - NumberObject *asNumberObject() { return type == Type_NumberObject ? reinterpret_cast<NumberObject *>(this) : 0; } - StringObject *asStringObject() { return type == Type_StringObject ? reinterpret_cast<StringObject *>(this) : 0; } - DateObject *asDateObject() { return type == Type_DateObject ? reinterpret_cast<DateObject *>(this) : 0; } - ErrorObject *asErrorObject() { return type == Type_ErrorObject ? reinterpret_cast<ErrorObject *>(this) : 0; } - ArgumentsObject *asArgumentsObject() { return type == Type_ArgumentsObject ? reinterpret_cast<ArgumentsObject *>(this) : 0; } + String *asString() { return internalClass->vtable->isString ? reinterpret_cast<String *>(this) : 0; } + Object *asObject() { return internalClass->vtable->isObject ? reinterpret_cast<Object *>(this) : 0; } + ArrayObject *asArrayObject() { return internalClass->vtable->type == Type_ArrayObject ? reinterpret_cast<ArrayObject *>(this) : 0; } + FunctionObject *asFunctionObject() { return internalClass->vtable->isFunctionObject ? reinterpret_cast<FunctionObject *>(this) : 0; } + BooleanObject *asBooleanObject() { return internalClass->vtable->type == Type_BooleanObject ? reinterpret_cast<BooleanObject *>(this) : 0; } + NumberObject *asNumberObject() { return internalClass->vtable->type == Type_NumberObject ? reinterpret_cast<NumberObject *>(this) : 0; } + StringObject *asStringObject() { return internalClass->vtable->type == Type_StringObject ? reinterpret_cast<StringObject *>(this) : 0; } + DateObject *asDateObject() { return internalClass->vtable->type == Type_DateObject ? reinterpret_cast<DateObject *>(this) : 0; } + ErrorObject *asErrorObject() { return internalClass->vtable->isErrorObject ? reinterpret_cast<ErrorObject *>(this) : 0; } + ArgumentsObject *asArgumentsObject() { return internalClass->vtable->type == Type_ArgumentsObject ? reinterpret_cast<ArgumentsObject *>(this) : 0; } - bool isListType() const { return type == Type_QmlSequence; } + bool isListType() const { return internalClass->vtable->type == Type_QmlSequence; } - bool isArrayObject() const { return type == Type_ArrayObject; } - bool isStringObject() const { return type == Type_StringObject; } + bool isArrayObject() const { return internalClass->vtable->type == Type_ArrayObject; } + bool isStringObject() const { return internalClass->vtable->type == Type_StringObject; } QString className() const; @@ -263,6 +331,7 @@ public: bool isEqualTo(Managed *other) { return internalClass->vtable->isEqualTo(this, other); } + uint getLength() const { return internalClass->vtable->getLength(this); } Property *advanceIterator(ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attributes); static void destroy(Managed *that) { that->_data = 0; } @@ -271,34 +340,27 @@ public: static ReturnedValue getLookup(Managed *m, Lookup *); static void setLookup(Managed *m, Lookup *l, const ValueRef v); static bool isEqualTo(Managed *m, Managed *other); - - uint internalType() const { - return type; - } + static uint getLength(const Managed *) { return 0; } ReturnedValue asReturnedValue() { return Value::fromManaged(this).asReturnedValue(); } InternalClass *internalClass; - enum { - SimpleArray = 1 - }; - union { uint _data; struct { uchar markBit : 1; uchar inUse : 1; uchar extensible : 1; // used by Object - uchar isNonStrictArgumentsObject : 1; + uchar _unused : 1; uchar needsActivation : 1; // used by FunctionObject uchar strictMode : 1; // used by FunctionObject uchar bindingKeyFlag : 1; uchar hasAccessorProperty : 1; - uchar type; + uchar _type; mutable uchar subtype; - uchar flags; + uchar _flags; }; }; diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index 5a7af1f041..6225d6b4ae 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -51,14 +51,14 @@ using namespace QV4; +DEFINE_MANAGED_VTABLE(MathObject); + static const double qt_PI = 2.0 * ::asin(1.0); -MathObject::MathObject(ExecutionEngine *engine) - : Object(engine) +MathObject::MathObject(InternalClass *ic) + : Object(ic) { - type = Type_MathObject; - - Scope scope(engine); + Scope scope(ic->engine); ScopedObject protectThis(scope, this); defineReadonlyProperty(QStringLiteral("E"), Primitive::fromDouble(::exp(1.0))); diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h index 6fe3db3950..45f8e7cd2e 100644 --- a/src/qml/jsruntime/qv4mathobject_p.h +++ b/src/qml/jsruntime/qv4mathobject_p.h @@ -49,7 +49,9 @@ namespace QV4 { struct MathObject: Object { - MathObject(ExecutionEngine *engine); + Q_MANAGED + Q_MANAGED_TYPE(MathObject) + MathObject(InternalClass *ic); static ReturnedValue method_abs(CallContext *context); static ReturnedValue method_acos(CallContext *context); diff --git a/src/qml/jsruntime/qv4mm.cpp b/src/qml/jsruntime/qv4mm.cpp index 9923c8834c..d55ddead89 100644 --- a/src/qml/jsruntime/qv4mm.cpp +++ b/src/qml/jsruntime/qv4mm.cpp @@ -280,6 +280,7 @@ Managed *MemoryManager::alloc(std::size_t size) if (size >= MemoryManager::Data::MaxItemSize) { // we use malloc for this MemoryManager::Data::LargeItem *item = static_cast<MemoryManager::Data::LargeItem *>(malloc(size + sizeof(MemoryManager::Data::LargeItem))); + memset(item, 0, size + sizeof(MemoryManager::Data::LargeItem)); item->next = m_d->largeItems; m_d->largeItems = item; return item->managed(); @@ -525,6 +526,7 @@ void MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t size, m->internalClass->vtable->collectDeletables(m, deletable); m->internalClass->vtable->destroy(m); + memset(m, 0, size); m->setNextFree(*f); #ifdef V4_USE_VALGRIND VALGRIND_DISABLE_ERROR_REPORTING; diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 106525d412..00d11b4cd6 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -72,37 +72,27 @@ DEFINE_MANAGED_VTABLE(Object); Object::Object(ExecutionEngine *engine) : Managed(engine->objectClass) , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) - , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) { - type = Type_Object; - flags = SimpleArray; - memset(memberData, 0, sizeof(Property)*memberDataAlloc); } Object::Object(InternalClass *ic) : Managed(ic) , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) - , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) { Q_ASSERT(internalClass->vtable && internalClass->vtable != &Managed::static_vtbl); - type = Type_Object; - flags = SimpleArray; if (internalClass->size >= memberDataAlloc) { memberDataAlloc = internalClass->size; memberData = new Property[memberDataAlloc]; } - memset(memberData, 0, sizeof(Property)*memberDataAlloc); } Object::~Object() { if (memberData != inlineProperties) delete [] memberData; - delete [] (arrayData - (sparseArray ? 0 : arrayOffset)); - if (arrayAttributes) - delete [] (arrayAttributes - (sparseArray ? 0 : arrayOffset)); - delete sparseArray; + if (arrayData) + arrayData->free(); _data = 0; } @@ -172,12 +162,6 @@ void Object::putValue(Property *pd, PropertyAttributes attrs, const ValueRef val engine()->currentContext()->throwTypeError(); } -void Object::defineDefaultProperty(const StringRef name, ValueRef value) -{ - Property *pd = insertMember(name, Attr_Data|Attr_NotEnumerable); - pd->value = *value; -} - void Object::defineDefaultProperty(const QString &name, ValueRef value) { ExecutionEngine *e = engine(); @@ -216,12 +200,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 = insertMember(name, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - if (getter) - p->setGetter(v4->newBuiltinFunction(v4->rootContext, name, getter)->getPointer()); - if (setter) - p->setSetter(v4->newBuiltinFunction(v4->rootContext, name, setter)->getPointer()); + Property p; + 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); } void Object::defineReadonlyProperty(const QString &name, ValueRef value) @@ -234,8 +216,7 @@ void Object::defineReadonlyProperty(const QString &name, ValueRef value) void Object::defineReadonlyProperty(const StringRef name, ValueRef value) { - Property *pd = insertMember(name, Attr_ReadOnly); - pd->value = *value; + insertMember(name, value, Attr_ReadOnly); } void Object::markObjects(Managed *that, ExecutionEngine *e) @@ -258,23 +239,8 @@ void Object::markObjects(Managed *that, ExecutionEngine *e) } } } - if (o->flags & SimpleArray) { - for (uint i = 0; i < o->arrayDataLen; ++i) - o->arrayData[i].value.mark(e); - return; - } else { - for (uint i = 0; i < o->arrayDataLen; ++i) { - const Property &pd = o->arrayData[i]; - if (o->arrayAttributes && o->arrayAttributes[i].isAccessor()) { - if (pd.getter()) - pd.getter()->mark(e); - if (pd.setter()) - pd.setter()->mark(e); - } else { - pd.value.mark(e); - } - } - } + if (o->arrayData) + o->arrayData->markObjects(e); } void Object::ensureMemberIndex(uint idx) @@ -290,7 +256,7 @@ void Object::ensureMemberIndex(uint idx) } } -Property *Object::insertMember(const StringRef s, PropertyAttributes attributes) +void Object::insertMember(const StringRef s, const Property &p, PropertyAttributes attributes) { uint idx; internalClass = internalClass->addMember(s.getPointer(), attributes, &idx); @@ -300,7 +266,7 @@ Property *Object::insertMember(const StringRef s, PropertyAttributes attributes) ensureMemberIndex(idx); - return memberData + idx; + memberData[idx] = p; } // Section 8.12.1 @@ -324,14 +290,11 @@ Property *Object::__getOwnProperty__(const StringRef name, PropertyAttributes *a Property *Object::__getOwnProperty__(uint index, PropertyAttributes *attrs) { - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX) { - Property *p = arrayData + pidx; - if (!p->value.isEmpty() && !(arrayAttributes && arrayAttributes[pidx].isGeneric())) { - if (attrs) - *attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data); - return p; - } + Property *p = arrayData->getProperty(index); + if (p) { + if (attrs) + *attrs = arrayData->attributes(index); + return p; } if (isStringObject()) { if (attrs) @@ -372,14 +335,11 @@ Property *Object::__getPropertyDescriptor__(uint index, PropertyAttributes *attr { const Object *o = this; while (o) { - uint pidx = o->propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX) { - Property *p = o->arrayData + pidx; - if (!p->value.isEmpty()) { - if (attrs) - *attrs = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); - return p; - } + Property *p = o->arrayData->getProperty(index); + if (p) { + if (attrs) + *attrs = o->arrayData->attributes(index); + return p; } if (o->isStringObject()) { Property *p = static_cast<const StringObject *>(o)->getIndex(index); @@ -426,6 +386,16 @@ bool Object::__hasProperty__(uint index) const return false; } +bool Object::hasOwnProperty(const StringRef name) const +{ + return const_cast<Object *>(this)->__getOwnProperty__(name) != 0; +} + +bool Object::hasOwnProperty(uint index) const +{ + return const_cast<Object *>(this)->__getOwnProperty__(index) != 0; +} + ReturnedValue Object::get(Managed *m, const StringRef name, bool *hasProperty) { return static_cast<Object *>(m)->internalGet(name, hasProperty); @@ -463,13 +433,9 @@ PropertyAttributes Object::query(const Managed *m, StringRef name) PropertyAttributes Object::queryIndexed(const Managed *m, uint index) { const Object *o = static_cast<const Object *>(m); - uint pidx = o->propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX) { - if (o->arrayAttributes) - return o->arrayAttributes[pidx]; - if (!o->arrayData[pidx].value.isEmpty()) - return Attr_Data; - } + if (o->arrayData->get(index) != Primitive::emptyValue().asReturnedValue()) + return o->arrayData->attributes(index); + if (o->isStringObject()) { Property *p = static_cast<const StringObject *>(o)->getIndex(index); if (p) @@ -570,41 +536,42 @@ Property *Object::advanceIterator(Managed *m, ObjectIterator *it, StringRef name name = (String *)0; *index = UINT_MAX; - if (!it->arrayIndex) - it->arrayNode = o->sparseArrayBegin(); - - // sparse arrays - if (it->arrayNode) { - while (it->arrayNode != o->sparseArrayEnd()) { - int k = it->arrayNode->key(); - uint pidx = it->arrayNode->value; - Property *p = o->arrayData + pidx; - it->arrayNode = it->arrayNode->nextNode(); - PropertyAttributes a = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); - if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { - it->arrayIndex = k + 1; - *index = k; + if (o->arrayData) { + if (!it->arrayIndex) + it->arrayNode = o->sparseBegin(); + + // sparse arrays + if (it->arrayNode) { + while (it->arrayNode != o->sparseEnd()) { + int k = it->arrayNode->key(); + uint pidx = it->arrayNode->value; + Property *p = o->arrayData->data + pidx; + it->arrayNode = it->arrayNode->nextNode(); + PropertyAttributes a = o->arrayData->attributes(k); + if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { + it->arrayIndex = k + 1; + *index = k; + if (attrs) + *attrs = a; + return p; + } + } + it->arrayNode = 0; + it->arrayIndex = UINT_MAX; + } + // dense arrays + while (it->arrayIndex < o->arrayData->length()) { + Property *p = o->arrayData->data + it->arrayIndex; + PropertyAttributes a = o->arrayData->attributes(it->arrayIndex); + ++it->arrayIndex; + if (!p->value.isEmpty() + && (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) { + *index = it->arrayIndex - 1; if (attrs) *attrs = a; return p; } } - it->arrayNode = 0; - it->arrayIndex = UINT_MAX; - } - // dense arrays - while (it->arrayIndex < o->arrayDataLen) { - uint pidx = o->propertyIndexFromArrayIndex(it->arrayIndex); - Property *p = o->arrayData + pidx; - PropertyAttributes a = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); - ++it->arrayIndex; - if (!p->value.isEmpty() - && (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) { - *index = it->arrayIndex - 1; - if (attrs) - *attrs = a; - return p; - } } while (it->memberIndex < o->internalClass->size) { @@ -654,17 +621,14 @@ ReturnedValue Object::internalGet(const StringRef name, bool *hasProperty) ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) { Property *pd = 0; - PropertyAttributes attrs = Attr_Data; + PropertyAttributes attrs; Object *o = this; while (o) { - uint pidx = o->propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX) { - if (!o->arrayData[pidx].value.isEmpty()) { - pd = o->arrayData + pidx; - if (o->arrayAttributes) - attrs = o->arrayAttributes[pidx]; - break; - } + Property *p = o->arrayData->getProperty(index); + if (p) { + pd = p; + attrs = o->arrayData->attributes(index); + break; } if (o->isStringObject()) { pd = static_cast<StringObject *>(o)->getIndex(index); @@ -761,11 +725,8 @@ void Object::internalPut(const StringRef name, const ValueRef value) return; } - { - Property *p = insertMember(name, Attr_Data); - p->value = *value; - return; - } + insertMember(name, value); + return; reject: if (engine()->currentContext()->strictMode) { @@ -781,14 +742,11 @@ void Object::internalPutIndexed(uint index, const ValueRef value) if (internalClass->engine->hasException) return; - Property *pd = 0; PropertyAttributes attrs; - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX && !arrayData[pidx].value.isEmpty()) { - pd = arrayData + pidx; - attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data); - } + Property *pd = arrayData->getProperty(index); + if (pd) + attrs = arrayData->attributes(index); if (!pd && isStringObject()) { pd = static_cast<StringObject *>(this)->getIndex(index); @@ -879,22 +837,8 @@ bool Object::internalDeleteIndexedProperty(uint index) if (internalClass->engine->hasException) return false; - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx == UINT_MAX) - return true; - if (arrayData[pidx].value.isEmpty()) - return true; - - if (!arrayAttributes || arrayAttributes[pidx].isConfigurable()) { - arrayData[pidx].value = Primitive::emptyValue(); - if (arrayAttributes) - arrayAttributes[pidx].clear(); - if (sparseArray) { - arrayData[pidx].value.int_32 = arrayFreeList; - arrayFreeList = pidx; - } + if (!arrayData || arrayData->deleteIndex(index)) return true; - } if (engine()->currentContext()->strictMode) engine()->currentContext()->throwTypeError(); @@ -954,9 +898,9 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, const StringRef name, if (!extensible) goto reject; // clause 4 - Property *pd = insertMember(name, attrs); - *pd = p; - pd->fullyPopulated(&attrs); + Property pd = p; + pd.fullyPopulated(&attrs); + insertMember(name, pd, attrs); return true; } @@ -969,20 +913,27 @@ reject: bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs) { - Property *current = 0; - // 15.4.5.1, 4b - if (isArrayObject() && index >= arrayLength() && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) + if (isArrayObject() && index >= getLength() && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) goto reject; - if (isNonStrictArgumentsObject) + if (ArgumentsObject::isNonStrictArgumentsObject(this)) return static_cast<ArgumentsObject *>(this)->defineOwnProperty(ctx, index, p, attrs); + return defineOwnProperty2(ctx, index, p, attrs); +reject: + if (ctx->strictMode) + ctx->throwTypeError(); + return false; +} + +bool Object::defineOwnProperty2(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs) +{ + Property *current = 0; + // Clause 1 { - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX && !arrayData[pidx].value.isEmpty()) - current = arrayData + pidx; + current = arrayData->getProperty(index); if (!current && isStringObject()) current = static_cast<StringObject *>(this)->getIndex(index); } @@ -992,9 +943,15 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Prop if (!extensible) goto reject; // clause 4 - Property *pd = arrayInsert(index, attrs); - *pd = p; - pd->fullyPopulated(&attrs); + Property pp(p); + pp.fullyPopulated(&attrs); + if (attrs == Attr_Data) { + Scope scope(ctx); + ScopedValue v(scope, pp.value); + arraySet(index, v); + } else { + arraySet(index, pp, attrs); + } return true; } @@ -1011,11 +968,11 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, con if (attrs.isEmpty()) return true; - PropertyAttributes cattrs = Attr_Data; + PropertyAttributes cattrs; if (!member.isNull()) cattrs = internalClass->propertyData[current - memberData]; - else if (arrayAttributes) - cattrs = arrayAttributes[current - arrayData]; + else + cattrs = arrayData->attrs ? arrayData->attrs[current - arrayData->data] : Attr_Data; // clause 6 if (p.isSubset(attrs, *current, cattrs)) @@ -1071,10 +1028,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, con if (!member.isNull()) { internalClass = internalClass->changeMember(member.getPointer(), cattrs); } else { - if (cattrs != Attr_Data) - ensureArrayAttributes(); - if (arrayAttributes) - arrayAttributes[current - arrayData] = cattrs; + arrayData->setAttributes(current - arrayData->data, cattrs); } if (attrs.isAccessor()) hasAccessorProperty = 1; @@ -1100,7 +1054,7 @@ void Object::copyArrayData(Object *other) Scope scope(engine()); if (other->protoHasArray() || other->hasAccessorProperty) { - uint len = other->arrayLength(); + uint len = other->getLength(); Q_ASSERT(len); ScopedValue v(scope); @@ -1108,313 +1062,97 @@ void Object::copyArrayData(Object *other) arraySet(i, (v = other->getIndexed(i))); } } else { - arrayReserve(other->arrayDataLen); - arrayDataLen = other->arrayDataLen; - memcpy(arrayData, other->arrayData, arrayDataLen*sizeof(Property)); - } - - arrayOffset = 0; + Q_ASSERT(!arrayData && other->arrayData); + if (other->arrayType() == ArrayData::Sparse) { + SparseArrayData *od = static_cast<SparseArrayData *>(other->arrayData); + SparseArrayData *dd = new SparseArrayData; + dd->type = ArrayData::Sparse; + dd->sparse = new SparseArray(*od->sparse); + dd->freeList = od->freeList; + arrayData = dd; + } + arrayReserve(other->arrayData->len); + arrayData->len = other->arrayData->len; + // ### correctly deal with accessor properties + memcpy(arrayData->data, other->arrayData->data, arrayData->len*sizeof(Property)); - if (other->sparseArray) { - flags &= ~SimpleArray; - sparseArray = new SparseArray(*other->sparseArray); - arrayFreeList = other->arrayFreeList; + arrayData->offset = 0; } - - setArrayLengthUnchecked(other->arrayLength()); + setArrayLengthUnchecked(other->getLength()); } - -ReturnedValue Object::arrayIndexOf(const ValueRef v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) +uint Object::getLength(const Managed *m) { - Q_UNUSED(ctx); - - Scope scope(engine()); - ScopedValue value(scope); - - if (!(o->flags & SimpleArray) || o->protoHasArray()) { - // lets be safe and slow - for (uint i = fromIndex; i < endIndex; ++i) { - bool exists; - value = o->getIndexed(i, &exists); - if (scope.hasException()) - return Encode::undefined(); - if (exists && __qmljs_strict_equal(value, v)) - return Encode(i); - } - } else if (sparseArray) { - for (SparseArrayNode *n = sparseArray->lowerBound(fromIndex); n != sparseArray->end() && n->key() < endIndex; n = n->nextNode()) { - value = o->getValue(arrayData + n->value, arrayAttributes ? arrayAttributes[n->value] : Attr_Data); - if (scope.hasException()) - return Encode::undefined(); - if (__qmljs_strict_equal(value, v)) - return Encode(n->key()); - } - } else { - if (endIndex > arrayDataLen) - endIndex = arrayDataLen; - Property *pd = arrayData; - Property *end = pd + endIndex; - pd += fromIndex; - while (pd < end) { - if (!pd->value.isEmpty()) { - value = o->getValue(pd, arrayAttributes ? arrayAttributes[pd - arrayData] : Attr_Data); - if (scope.hasException()) - return Encode::undefined(); - if (__qmljs_strict_equal(value, v)) - return Encode((uint)(pd - arrayData)); - } - ++pd; - } - } - return Encode(-1); + Scope scope(m->engine()); + ScopedValue v(scope, const_cast<Managed *>(m)->get(scope.engine->id_length)); + return v->toUInt32(); } -void Object::arrayConcat(const ArrayObject *other) +bool Object::setArrayLength(uint newLen) { - int newLen = arrayDataLen + other->arrayLength(); - if (other->sparseArray) - initSparse(); - // ### copy attributes as well! - if (sparseArray) { - if (other->sparseArray) { - for (const SparseArrayNode *it = other->sparseArray->begin(); it != other->sparseArray->end(); it = it->nextNode()) - arraySet(arrayDataLen + it->key(), other->arrayData + it->value); - } else { - int oldSize = arrayDataLen; - arrayReserve(oldSize + other->arrayLength()); - memcpy(arrayData + oldSize, other->arrayData, other->arrayLength()*sizeof(Property)); - if (arrayAttributes) - std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayLength(), PropertyAttributes(Attr_Data)); - for (uint i = 0; i < other->arrayLength(); ++i) { - SparseArrayNode *n = sparseArray->insert(arrayDataLen + i); - n->value = oldSize + i; - } - } + Q_ASSERT(isArrayObject()); + const Property *lengthProperty = memberData + ArrayObject::LengthPropertyIndex; + if (lengthProperty && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) + return false; + uint oldLen = getLength(); + bool ok = true; + if (newLen < oldLen) { + uint l = arrayData->truncate(newLen); + if (l != newLen) + ok = false; + newLen = l; } else { - uint oldSize = arrayLength(); - arrayReserve(oldSize + other->arrayDataLen); - if (oldSize > arrayDataLen) { - for (uint i = arrayDataLen; i < oldSize; ++i) - arrayData[i].value = Primitive::emptyValue(); - } - if (other->arrayAttributes) { - for (uint i = 0; i < other->arrayDataLen; ++i) { - bool exists; - arrayData[oldSize + i].value = const_cast<ArrayObject *>(other)->getIndexed(i, &exists); - arrayDataLen = oldSize + i + 1; - if (arrayAttributes) - arrayAttributes[oldSize + i] = Attr_Data; - if (!exists) - arrayData[oldSize + i].value = Primitive::emptyValue(); - } - } else { - arrayDataLen = oldSize + other->arrayDataLen; - memcpy(arrayData + oldSize, other->arrayData, other->arrayDataLen*sizeof(Property)); - if (arrayAttributes) - std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayDataLen, PropertyAttributes(Attr_Data)); - } + if (newLen >= 0x100000) + initSparseArray(); } setArrayLengthUnchecked(newLen); + return ok; } -void Object::arraySort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint len) +void Object::initSparseArray() { - if (!arrayDataLen) + if (arrayType() == ArrayData::Sparse) return; - if (sparseArray) { - context->throwUnimplemented(QStringLiteral("Object::sort unimplemented for sparse arrays")); + SparseArrayData *data = new SparseArrayData; + data->type = ArrayData::Sparse; + data->sparse = new SparseArray; + if (!arrayData) { + arrayData = data; return; } - if (len > arrayDataLen) - len = arrayDataLen; - - // The spec says the sorting goes through a series of get,put and delete operations. - // this implies that the attributes don't get sorted around. - // behavior of accessor properties is implementation defined. We simply turn them all - // into data properties and then sort. This is in line with the sentence above. - if (arrayAttributes) { - for (uint i = 0; i < len; i++) { - if ((arrayAttributes && arrayAttributes[i].isGeneric()) || arrayData[i].value.isEmpty()) { - while (--len > i) - if (!((arrayAttributes && arrayAttributes[len].isGeneric())|| arrayData[len].value.isEmpty())) - break; - arrayData[i].value = getValue(arrayData + len, arrayAttributes[len]); - arrayData[len].value = Primitive::emptyValue(); - if (arrayAttributes) { - arrayAttributes[i] = Attr_Data; - arrayAttributes[len].clear(); - } - } else if (arrayAttributes[i].isAccessor()) { - arrayData[i].value = getValue(arrayData + i, arrayAttributes[i]); - arrayAttributes[i] = Attr_Data; - } - } - } - - if (!(comparefn->isUndefined() || comparefn->asObject())) { - context->throwTypeError(); - return; + uint oldOffset = arrayData->offset; + data->data = arrayData->data - arrayData->offset; + data->attrs = arrayData->attrs; + data->len = arrayData->len; + data->alloc = arrayData->alloc; + data->offset = 0; + arrayData->data = 0; + arrayData->attrs = 0; + delete arrayData; + + uint *lastFree = &data->freeList; + for (uint i = 0; i < oldOffset; ++i) { + *lastFree = i; + lastFree = &data->data[i].value.uint_32; } - - ArrayElementLessThan lessThan(context, thisObject, comparefn); - - if (!len) - return; - Property *begin = arrayData; - std::sort(begin, begin + len, lessThan); -} - - -void Object::initSparse() -{ - if (!sparseArray) { - flags &= ~SimpleArray; - sparseArray = new SparseArray; - for (uint i = 0; i < arrayDataLen; ++i) { - if (!((arrayAttributes && arrayAttributes[i].isGeneric()) || arrayData[i].value.isEmpty())) { - SparseArrayNode *n = sparseArray->insert(i); - n->value = i + arrayOffset; - } - } - - uint off = arrayOffset; - if (!arrayOffset) { - arrayFreeList = arrayDataLen; + for (uint i = 0; i < data->len; ++i) { + if (!data->data[i + oldOffset].value.isEmpty()) { + SparseArrayNode *n = data->sparse->insert(i); + n->value = i + oldOffset; } else { - arrayFreeList = 0; - arrayData -= off; - arrayAlloc += off; - int o = off; - for (int i = 0; i < o - 1; ++i) { - arrayData[i].value = Primitive::fromInt32(i + 1); - } - arrayData[o - 1].value = Primitive::fromInt32(arrayDataLen + off); - } - for (uint i = arrayDataLen + off; i < arrayAlloc; ++i) { - arrayData[i].value = Primitive::fromInt32(i + 1); + *lastFree = i + oldOffset; + lastFree = &data->data[i + oldOffset].value.uint_32; } } -} - -void Object::arrayReserve(uint n) -{ - if (n < 8) - n = 8; - if (n >= arrayAlloc) { - uint off; - if (sparseArray) { - assert(arrayFreeList == arrayAlloc); - // ### FIXME - arrayDataLen = arrayAlloc; - off = 0; - } else { - off = arrayOffset; - } - arrayAlloc = qMax(n, 2*arrayAlloc); - Property *newArrayData = new Property[arrayAlloc + off]; - if (arrayData) { - memcpy(newArrayData + off, arrayData, sizeof(Property)*arrayDataLen); - delete [] (arrayData - off); - } - arrayData = newArrayData + off; - if (sparseArray) { - for (uint i = arrayFreeList; i < arrayAlloc; ++i) { - arrayData[i].value = Primitive::emptyValue(); - arrayData[i].value = Primitive::fromInt32(i + 1); - } - } - - if (arrayAttributes) { - PropertyAttributes *newAttrs = new PropertyAttributes[arrayAlloc]; - memcpy(newAttrs, arrayAttributes, sizeof(PropertyAttributes)*arrayDataLen); - delete [] (arrayAttributes - off); - - arrayAttributes = newAttrs; - if (sparseArray) { - for (uint i = arrayFreeList; i < arrayAlloc; ++i) - arrayAttributes[i] = Attr_Invalid; - } - } + for (uint i = data->len + oldOffset; i < data->alloc; ++i) { + *lastFree = i; + lastFree = &data->data[i].value.uint_32; } + arrayData = data; } -void Object::ensureArrayAttributes() -{ - if (arrayAttributes) - return; - - flags &= ~SimpleArray; - uint off = sparseArray ? 0 : arrayOffset; - arrayAttributes = new PropertyAttributes[arrayAlloc + off]; - arrayAttributes += off; - for (uint i = 0; i < arrayDataLen; ++i) - arrayAttributes[i] = Attr_Data; - for (uint i = arrayDataLen; i < arrayAlloc; ++i) - arrayAttributes[i] = Attr_Invalid; -} - - -bool Object::setArrayLength(uint newLen) { - assert(isArrayObject()); - const Property *lengthProperty = memberData + ArrayObject::LengthPropertyIndex; - if (lengthProperty && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) - return false; - uint oldLen = arrayLength(); - bool ok = true; - if (newLen < oldLen) { - if (sparseArray) { - SparseArrayNode *begin = sparseArray->lowerBound(newLen); - if (begin != sparseArray->end()) { - SparseArrayNode *it = sparseArray->end()->previousNode(); - while (1) { - Property &pd = arrayData[it->value]; - if (arrayAttributes) { - if (!arrayAttributes[it->value].isConfigurable()) { - ok = false; - newLen = it->key() + 1; - break; - } else { - arrayAttributes[it->value].clear(); - } - } - pd.value.tag = Value::Empty_Type; - pd.value.int_32 = arrayFreeList; - arrayFreeList = it->value; - bool brk = (it == begin); - SparseArrayNode *prev = it->previousNode(); - sparseArray->erase(it); - if (brk) - break; - it = prev; - } - } - } else { - Property *it = arrayData + arrayDataLen; - const Property *begin = arrayData + newLen; - while (--it >= begin) { - if (arrayAttributes) { - if (!arrayAttributes[it - arrayData].isEmpty() && !arrayAttributes[it - arrayData].isConfigurable()) { - ok = false; - newLen = it - arrayData + 1; - break; - } else { - arrayAttributes[it - arrayData].clear(); - } - it->value = Primitive::emptyValue(); - } - } - arrayDataLen = newLen; - } - } else { - if (newLen >= 0x100000) - initSparse(); - } - setArrayLengthUnchecked(newLen); - return ok; -} DEFINE_MANAGED_VTABLE(ArrayObject); @@ -1431,9 +1169,10 @@ ArrayObject::ArrayObject(ExecutionEngine *engine, const QStringList &list) // elements converted to JS Strings. int len = list.count(); arrayReserve(len); + ScopedValue v(scope); for (int ii = 0; ii < len; ++ii) { - arrayData[ii].value = Encode(engine->newString(list.at(ii))); - arrayDataLen = ii + 1; + arrayData->put(ii, (v = engine->newString(list.at(ii)))); + arrayData->setLength(ii + 1); } setArrayLengthUnchecked(len); } @@ -1442,10 +1181,28 @@ void ArrayObject::init(ExecutionEngine *engine) { Q_UNUSED(engine); - type = Type_ArrayObject; memberData[LengthPropertyIndex].value = Primitive::fromInt32(0); } +ReturnedValue ArrayObject::getLookup(Managed *m, Lookup *l) +{ + if (l->name->equals(m->engine()->id_length)) { + // special case, as the property is on the object itself + l->getter = Lookup::arrayLengthGetter; + ArrayObject *a = static_cast<ArrayObject *>(m); + return a->memberData[ArrayObject::LengthPropertyIndex].value.asReturnedValue(); + } + return Object::getLookup(m, l); +} + +uint ArrayObject::getLength(const Managed *m) +{ + const ArrayObject *a = static_cast<const ArrayObject *>(m); + if (a->memberData[ArrayObject::LengthPropertyIndex].value.isInteger()) + return a->memberData[ArrayObject::LengthPropertyIndex].value.integerValue(); + return Primitive::toUInt32(a->memberData[ArrayObject::LengthPropertyIndex].value.doubleValue()); +} + QStringList ArrayObject::toQStringList() const { QStringList result; @@ -1454,7 +1211,7 @@ QStringList ArrayObject::toQStringList() const Scope scope(engine); ScopedValue v(scope); - uint32_t length = arrayLength(); + uint32_t length = getLength(); for (uint32_t i = 0; i < length; ++i) { v = const_cast<ArrayObject *>(this)->getIndexed(i); result.append(v->toQStringNoThrow()); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 23f2f682fd..9500c2a4d0 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -49,7 +49,7 @@ #include "qv4managed_p.h" #include "qv4property_p.h" #include "qv4internalclass_p.h" -#include "qv4sparsearray_p.h" +#include "qv4arraydata_p.h" #include <QtCore/QString> #include <QtCore/QHash> @@ -100,20 +100,17 @@ struct SyntaxErrorPrototype; struct TypeErrorPrototype; struct URIErrorPrototype; + struct Q_QML_EXPORT Object: Managed { Q_MANAGED + Q_MANAGED_TYPE(Object) + enum { + IsObject = true + }; uint memberDataAlloc; Property *memberData; - union { - uint arrayFreeList; - uint arrayOffset; - }; - uint arrayDataLen; - uint arrayAlloc; - PropertyAttributes *arrayAttributes; - Property *arrayData; - SparseArray *sparseArray; + ArrayData *arrayData; enum { InlinePropertySize = 4 @@ -136,10 +133,14 @@ struct Q_QML_EXPORT Object: Managed { bool __hasProperty__(const StringRef name) const; bool __hasProperty__(uint index) const; + bool hasOwnProperty(const StringRef name) const; + bool hasOwnProperty(uint index) const; + bool __defineOwnProperty__(ExecutionContext *ctx, Property *current, const StringRef member, const Property &p, PropertyAttributes attrs); bool __defineOwnProperty__(ExecutionContext *ctx, const StringRef name, const Property &p, PropertyAttributes attrs); bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs); bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs); + bool defineOwnProperty2(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs); // // helpers @@ -156,7 +157,9 @@ struct Q_QML_EXPORT Object: Managed { void putValue(Property *pd, PropertyAttributes attrs, const ValueRef value); /* The spec default: Writable: true, Enumerable: false, Configurable: true */ - void defineDefaultProperty(const StringRef name, ValueRef value); + void defineDefaultProperty(const StringRef name, ValueRef value) { + insertMember(name, value, Attr_Data|Attr_NotEnumerable); + } void defineDefaultProperty(const QString &name, ValueRef value); void defineDefaultProperty(const QString &name, ReturnedValue (*code)(CallContext *), int argumentCount = 0); void defineDefaultProperty(const StringRef name, ReturnedValue (*code)(CallContext *), int argumentCount = 0); @@ -166,113 +169,63 @@ struct Q_QML_EXPORT Object: Managed { void defineReadonlyProperty(const QString &name, ValueRef value); void defineReadonlyProperty(const StringRef name, ValueRef value); - Property *insertMember(const StringRef s, PropertyAttributes attributes); + void insertMember(const StringRef s, const ValueRef v, PropertyAttributes attributes = Attr_Data) { + insertMember(s, Property::fromValue(*v), attributes); + } + void insertMember(const StringRef s, const Property &p, PropertyAttributes attributes); inline ExecutionEngine *engine() const { return internalClass->engine; } // Array handling - uint allocArrayValue() { - uint idx = arrayFreeList; - if (arrayAlloc <= arrayFreeList) - arrayReserve(arrayAlloc + 1); - arrayFreeList = arrayData[arrayFreeList].value.uint_32; - if (arrayAttributes) - arrayAttributes[idx].setType(PropertyAttributes::Data); - return idx; - } - - uint allocArrayValue(const ValueRef v) { - uint idx = allocArrayValue(); - Property *pd = &arrayData[idx]; - pd->value = *v; - return idx; - } - void freeArrayValue(int idx) { - Property &pd = arrayData[idx]; - pd.value.tag = Value::Empty_Type; - pd.value.int_32 = arrayFreeList; - arrayFreeList = idx; - if (arrayAttributes) - arrayAttributes[idx].clear(); - } - - void getArrayHeadRoom() { - assert(!sparseArray && !arrayOffset); - arrayOffset = qMax(arrayDataLen >> 2, (uint)16); - Property *newArray = new Property[arrayOffset + arrayAlloc]; - memcpy(newArray + arrayOffset, arrayData, arrayDataLen*sizeof(Property)); - delete [] arrayData; - arrayData = newArray + arrayOffset; - if (arrayAttributes) { - PropertyAttributes *newAttrs = new PropertyAttributes[arrayOffset + arrayAlloc]; - memcpy(newAttrs + arrayOffset, arrayAttributes, arrayDataLen*sizeof(PropertyAttributes)); - delete [] arrayAttributes; - arrayAttributes = newAttrs + arrayOffset; - } - } - public: void copyArrayData(Object *other); - void initSparse(); - uint arrayLength() const; bool setArrayLength(uint newLen); void setArrayLengthUnchecked(uint l); - Property *arrayInsert(uint index, PropertyAttributes attributes = Attr_Data); + void arraySet(uint index, const Property &p, PropertyAttributes attributes = Attr_Data); - void arraySet(uint index, const Property *pd); void arraySet(uint index, ValueRef value); - uint propertyIndexFromArrayIndex(uint index) const - { - if (!sparseArray) { - if (index >= arrayDataLen) - return UINT_MAX; - return index; - } else { - SparseArrayNode *n = sparseArray->findNode(index); - if (!n) - return UINT_MAX; - return n->value; - } - } + void push_back(const ValueRef v); - Property *arrayAt(uint index) const { - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx == UINT_MAX) - return 0; - return arrayData + pidx; + ArrayData::Type arrayType() const { + return arrayData ? (ArrayData::Type)arrayData->type : ArrayData::Simple; } - - Property *nonSparseArrayAt(uint index) const { - if (sparseArray) - return 0; - if (index >= arrayDataLen) - return 0; - return arrayData + index; + // ### remove me + void setArrayType(ArrayData::Type t) { + Q_ASSERT(t != ArrayData::Simple && t != ArrayData::Sparse); + arrayCreate(); + arrayData->type = t; } - void push_back(const ValueRef v); + inline void arrayReserve(uint n) { + arrayCreate(); + arrayData->vtable->reserve(arrayData, n); + } - SparseArrayNode *sparseArrayBegin() { return sparseArray ? sparseArray->begin() : 0; } - SparseArrayNode *sparseArrayEnd() { return sparseArray ? sparseArray->end() : 0; } + void arrayCreate() { + if (!arrayData) + arrayData = new ArrayData; + } - void arrayConcat(const ArrayObject *other); - void arraySort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint arrayDataLen); - ReturnedValue arrayIndexOf(const ValueRef v, uint fromIndex, uint arrayDataLen, ExecutionContext *ctx, Object *o); + void initSparseArray(); + SparseArrayNode *sparseBegin() { return arrayType() == ArrayData::Sparse ? static_cast<SparseArrayData *>(arrayData)->sparse->begin() : 0; } + SparseArrayNode *sparseEnd() { return arrayType() == ArrayData::Sparse ? static_cast<SparseArrayData *>(arrayData)->sparse->end() : 0; } - void arrayReserve(uint n); - void ensureArrayAttributes(); + inline Property *arrayInsert(uint index) { + arrayCreate(); + return ArrayData::insert(this, index); + } inline bool protoHasArray() { Scope scope(engine()); Scoped<Object> p(scope, this); while ((p = p->prototype())) - if (p->arrayDataLen) + if (p->arrayData) return true; return false; @@ -298,6 +251,7 @@ public: using Managed::getLookup; using Managed::setLookup; using Managed::advanceIterator; + using Managed::getLength; protected: static void destroy(Managed *that); static void markObjects(Managed *that, ExecutionEngine *e); @@ -312,7 +266,7 @@ protected: static ReturnedValue getLookup(Managed *m, Lookup *l); static void setLookup(Managed *m, Lookup *l, const ValueRef v); static Property *advanceIterator(Managed *m, ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attributes); - + static uint getLength(const Managed *m); private: ReturnedValue internalGet(const StringRef name, bool *hasProperty); @@ -328,40 +282,39 @@ private: struct BooleanObject: Object { Q_MANAGED + Q_MANAGED_TYPE(BooleanObject) SafeValue value; BooleanObject(ExecutionEngine *engine, const ValueRef val) : Object(engine->booleanClass) { - type = Type_BooleanObject; value = val; } protected: BooleanObject(InternalClass *ic) : Object(ic) { - setVTable(&static_vtbl); - type = Type_BooleanObject; + Q_ASSERT(internalClass->vtable == &static_vtbl); value = Encode(false); } }; struct NumberObject: Object { Q_MANAGED + Q_MANAGED_TYPE(NumberObject) SafeValue value; NumberObject(ExecutionEngine *engine, const ValueRef val) : Object(engine->numberClass) { - type = Type_NumberObject; value = val; } protected: NumberObject(InternalClass *ic) : Object(ic) { - setVTable(&static_vtbl); - type = Type_NumberObject; + Q_ASSERT(internalClass->vtable == &static_vtbl); value = Encode((int)0); } }; struct ArrayObject: Object { Q_MANAGED + Q_MANAGED_TYPE(ArrayObject) enum { LengthPropertyIndex = 0 }; @@ -372,19 +325,13 @@ struct ArrayObject: Object { void init(ExecutionEngine *engine); + static ReturnedValue getLookup(Managed *m, Lookup *l); + using Managed::getLength; + static uint getLength(const Managed *m); + QStringList toQStringList() const; }; -inline uint Object::arrayLength() const -{ - if (isArrayObject()) { - if (memberData[ArrayObject::LengthPropertyIndex].value.isInteger()) - return memberData[ArrayObject::LengthPropertyIndex].value.integerValue(); - return Primitive::toUInt32(memberData[ArrayObject::LengthPropertyIndex].value.doubleValue()); - } - return 0; -} - inline void Object::setArrayLengthUnchecked(uint l) { if (isArrayObject()) { @@ -396,64 +343,35 @@ inline void Object::setArrayLengthUnchecked(uint l) inline void Object::push_back(const ValueRef v) { - uint idx = arrayLength(); - if (!sparseArray) { - if (idx >= arrayAlloc) - arrayReserve(idx + 1); - arrayData[idx].value = *v; - arrayDataLen = idx + 1; - } else { - uint idx = allocArrayValue(v); - sparseArray->push_back(idx, arrayLength()); - } + arrayCreate(); + Q_ASSERT(!arrayData->isSparse()); + + uint idx = getLength(); + arrayReserve(idx + 1); + arrayData->put(idx, v); + arrayData->setLength(idx + 1); setArrayLengthUnchecked(idx + 1); } -inline Property *Object::arrayInsert(uint index, PropertyAttributes attributes) { +inline void Object::arraySet(uint index, const Property &p, PropertyAttributes attributes) +{ if (attributes.isAccessor()) hasAccessorProperty = 1; - Property *pd; - if (!sparseArray && (index < 0x1000 || index < arrayDataLen + (arrayDataLen >> 2))) { - if (index >= arrayAlloc) - arrayReserve(index + 1); - if (index >= arrayDataLen) { - // mark possible hole in the array - for (uint i = arrayDataLen; i < index; ++i) { - arrayData[i].value = Primitive::emptyValue(); - if (arrayAttributes) - arrayAttributes[i].clear(); - } - arrayDataLen = index + 1; - } - pd = arrayData + index; - } else { - initSparse(); - SparseArrayNode *n = sparseArray->insert(index); - if (n->value == UINT_MAX) - n->value = allocArrayValue(); - pd = arrayData + n->value; - } - if (index >= arrayLength()) + Property *pd = arrayInsert(index); + *pd = p; + arrayData->setAttributes(index, attributes); + if (isArrayObject() && index >= getLength()) setArrayLengthUnchecked(index + 1); - if (arrayAttributes || attributes != Attr_Data) { - if (!arrayAttributes) - ensureArrayAttributes(); - attributes.resolve(); - arrayAttributes[pd - arrayData] = attributes; - } - return pd; } + inline void Object::arraySet(uint index, ValueRef value) { Property *pd = arrayInsert(index); - pd->value = *value; -} - -inline void Object::arraySet(uint index, const Property *pd) -{ - *arrayInsert(index) = *pd; + pd->value = value ? *value : Primitive::undefinedValue(); + if (isArrayObject() && index >= getLength()) + setArrayLengthUnchecked(index + 1); } template<> diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 04fa504991..f7a5cd7531 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -58,7 +58,7 @@ ObjectIterator::ObjectIterator(SafeObject *scratch1, SafeObject *scratch2, const current = o; tmpDynamicProperty.value = Primitive::undefinedValue(); - if (object && object->isNonStrictArgumentsObject) { + if (object && object->asArgumentsObject()) { Scope scope(object->engine()); Scoped<ArgumentsObject> (scope, object->asReturnedValue())->fullyCreate(); } @@ -76,7 +76,7 @@ ObjectIterator::ObjectIterator(Scope &scope, const ObjectRef o, uint flags) current = o; tmpDynamicProperty.value = Primitive::undefinedValue(); - if (object && object->isNonStrictArgumentsObject) { + if (object && object->asArgumentsObject()) { Scope scope(object->engine()); Scoped<ArgumentsObject> (scope, object->asReturnedValue())->fullyCreate(); } diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h index 6c333b328c..33228cefaa 100644 --- a/src/qml/jsruntime/qv4objectiterator_p.h +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -86,11 +86,11 @@ struct Q_QML_EXPORT ObjectIterator struct ForEachIteratorObject: Object { Q_MANAGED + Q_MANAGED_TYPE(ForeachIteratorObject) ObjectIterator it; ForEachIteratorObject(ExecutionContext *ctx, const ObjectRef o) : Object(ctx->engine), it(workArea, workArea + 1, o, ObjectIterator::EnumerableOnly|ObjectIterator::WithProtoChain) { setVTable(&static_vtbl); - type = Type_ForeachIteratorObject; } ReturnedValue nextPropertyName() { return it.nextPropertyNameAsString(); } diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 7ca790b970..ac19101a29 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -105,9 +105,9 @@ ReturnedValue ObjectCtor::call(Managed *m, CallData *callData) void ObjectPrototype::init(ExecutionEngine *v4, ObjectRef ctor) { Scope scope(v4); - ScopedObject o(scope); + ScopedObject o(scope, this); - ctor->defineReadonlyProperty(v4->id_prototype, (o = this)); + ctor->defineReadonlyProperty(v4->id_prototype, o); ctor->defineReadonlyProperty(v4->id_length, Primitive::fromInt32(1)); ctor->defineDefaultProperty(QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); @@ -134,9 +134,9 @@ void ObjectPrototype::init(ExecutionEngine *v4, ObjectRef ctor) defineDefaultProperty(QStringLiteral("__defineSetter__"), method_defineSetter, 2); Scoped<String> id_proto(scope, v4->id___proto__); - Property *p = insertMember(StringRef(v4->id___proto__), Attr_Accessor|Attr_NotEnumerable); - p->setGetter(v4->newBuiltinFunction(v4->rootContext, id_proto, method_get_proto)->getPointer()); - p->setSetter(v4->newBuiltinFunction(v4->rootContext, id_proto, method_set_proto)->getPointer()); + Property p = Property::fromAccessor(v4->newBuiltinFunction(v4->rootContext, id_proto, method_get_proto)->getPointer(), + v4->newBuiltinFunction(v4->rootContext, id_proto, method_set_proto)->getPointer()); + insertMember(StringRef(v4->id___proto__), p, Attr_Accessor|Attr_NotEnumerable); } ReturnedValue ObjectPrototype::method_getPrototypeOf(CallContext *ctx) @@ -157,7 +157,7 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(CallContext *ctx) if (!O) return ctx->throwTypeError(); - if (O->isNonStrictArgumentsObject) + if (ArgumentsObject::isNonStrictArgumentsObject(O.getPointer())) Scoped<ArgumentsObject>(scope, O)->fullyCreate(); ScopedValue v(scope, ctx->argument(1)); @@ -271,10 +271,12 @@ ReturnedValue ObjectPrototype::method_seal(CallContext *ctx) o->internalClass = o->internalClass->sealed(); - o->ensureArrayAttributes(); - for (uint i = 0; i < o->arrayDataLen; ++i) { - if (!(o->arrayAttributes[i].isGeneric() || o->arrayData[i].value.isEmpty())) - o->arrayAttributes[i].setConfigurable(false); + if (o->arrayData) { + o->arrayData->ensureAttributes(); + for (uint i = 0; i < o->arrayData->length(); ++i) { + if (!o->arrayData->isEmpty(i)) + o->arrayData->attrs[i].setConfigurable(false); + } } return o.asReturnedValue(); @@ -287,19 +289,21 @@ ReturnedValue ObjectPrototype::method_freeze(CallContext *ctx) if (!o) return ctx->throwTypeError(); - if (o->isNonStrictArgumentsObject) + if (ArgumentsObject::isNonStrictArgumentsObject(o.getPointer())) Scoped<ArgumentsObject>(scope, o)->fullyCreate(); o->extensible = false; o->internalClass = o->internalClass->frozen(); - o->ensureArrayAttributes(); - for (uint i = 0; i < o->arrayDataLen; ++i) { - if (!(o->arrayAttributes[i].isGeneric() || o->arrayData[i].value.isEmpty())) - o->arrayAttributes[i].setConfigurable(false); - if (o->arrayAttributes[i].isData()) - o->arrayAttributes[i].setWritable(false); + if (o->arrayData) { + o->arrayData->ensureAttributes(); + for (uint i = 0; i < o->arrayData->length(); ++i) { + if (!o->arrayData->isEmpty(i)) + o->arrayData->attrs[i].setConfigurable(false); + if (o->arrayData->attrs[i].isData()) + o->arrayData->attrs[i].setWritable(false); + } } return o.asReturnedValue(); } @@ -328,15 +332,16 @@ ReturnedValue ObjectPrototype::method_isSealed(CallContext *ctx) if (o->internalClass != o->internalClass->sealed()) return Encode(false); - if (!o->arrayDataLen) + if (!o->arrayData || !o->arrayData->length()) return Encode(true); - if (!o->arrayAttributes) + if (o->arrayData->length() && !o->arrayData->attrs) return Encode(false); - for (uint i = 0; i < o->arrayDataLen; ++i) { - if (!(o->arrayAttributes[i].isGeneric() || o->arrayData[i].value.isEmpty())) - if (o->arrayAttributes[i].isConfigurable()) + for (uint i = 0; i < o->arrayData->length(); ++i) { + // ### Fix for sparse arrays + if (!o->arrayData->isEmpty(i)) + if (o->arrayData->attributes(i).isConfigurable()) return Encode(false); } @@ -356,15 +361,16 @@ ReturnedValue ObjectPrototype::method_isFrozen(CallContext *ctx) if (o->internalClass != o->internalClass->frozen()) return Encode(false); - if (!o->arrayDataLen) + if (!o->arrayData->length()) return Encode(true); - if (!o->arrayAttributes) + if (o->arrayData->length() && !o->arrayData->attrs) return Encode(false); - for (uint i = 0; i < o->arrayDataLen; ++i) { - if (!(o->arrayAttributes[i].isGeneric() || o->arrayData[i].value.isEmpty())) - if (o->arrayAttributes[i].isConfigurable() || o->arrayAttributes[i].isWritable()) + for (uint i = 0; i < o->arrayData->length(); ++i) { + // ### Fix for sparse arrays + if (!o->arrayData->isEmpty(i)) + if (o->arrayData->attributes(i).isConfigurable() || o->arrayData->attributes(i).isWritable()) return Encode(false); } @@ -448,7 +454,7 @@ ReturnedValue ObjectPrototype::method_hasOwnProperty(CallContext *ctx) Scoped<Object> O(scope, ctx->callData->thisObject, Scoped<Object>::Convert); if (scope.engine->hasException) return Encode::undefined(); - bool r = O->__getOwnProperty__(P) != 0; + bool r = O->hasOwnProperty(P); if (!r) r = !O->query(P).isEmpty(); return Encode(r); diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h index 9763de5adf..ff9ea59514 100644 --- a/src/qml/jsruntime/qv4property_p.h +++ b/src/qml/jsruntime/qv4property_p.h @@ -53,7 +53,7 @@ struct FunctionObject; struct Property { union { - Value value; + SafeValue value; struct { FunctionObject *get; FunctionObject *set; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 61f92a0f5c..bad2cf3cd4 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1592,7 +1592,7 @@ void CallArgument::fromValue(int callType, QV8Engine *engine, const QV4::ValueRe if (array) { Scoped<QV4::QObjectWrapper> qobjectWrapper(scope); - uint32_t length = array->arrayLength(); + uint32_t length = array->getLength(); for (uint32_t ii = 0; ii < length; ++ii) { QObject *o = 0; qobjectWrapper = array->getIndexed(ii); @@ -1689,9 +1689,10 @@ QV4::ReturnedValue CallArgument::toValue(QV8Engine *engine) QList<QObject *> &list = *qlistPtr; QV4::Scoped<ArrayObject> array(scope, v4->newArrayObject()); array->arrayReserve(list.count()); + QV4::ScopedValue v(scope); for (int ii = 0; ii < list.count(); ++ii) { - array->arrayData[ii].value = QV4::QObjectWrapper::wrap(v4, list.at(ii)); - array->arrayDataLen = ii + 1; + array->arrayData->put(ii, (v = QV4::QObjectWrapper::wrap(v4, list.at(ii)))); + array->arrayData->setLength(ii + 1); } array->setArrayLengthUnchecked(list.count()); return array.asReturnedValue(); diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index 41ff9f9741..8544970347 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -99,8 +99,6 @@ RegExp::RegExp(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, , m_ignoreCase(ignoreCase) , m_multiLine(multiline) { - type = Type_RegExpObject; - if (!engine) return; const char* error = 0; diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h index 9041ff2ef4..d8e9930876 100644 --- a/src/qml/jsruntime/qv4regexp_p.h +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -94,6 +94,7 @@ public: class RegExp : public Managed { Q_MANAGED + Q_MANAGED_TYPE(RegExp) public: static RegExp* create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false); ~RegExp(); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 468fb34d76..1b55c5a2f1 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -76,6 +76,7 @@ RegExpObject::RegExpObject(InternalClass *ic) , value(RegExp::create(ic->engine, QString(), false, false)) , global(false) { + Q_ASSERT(internalClass->vtable == &static_vtbl); init(ic->engine); } @@ -143,14 +144,13 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, const QRegExp &re) void RegExpObject::init(ExecutionEngine *engine) { setVTable(&static_vtbl); - type = Type_RegExpObject; Scope scope(engine); ScopedObject protectThis(scope, this); ScopedString lastIndex(scope, engine->newIdentifier(QStringLiteral("lastIndex"))); - Property *lastIndexProperty = insertMember(lastIndex, Attr_NotEnumerable|Attr_NotConfigurable); - lastIndexProperty->value = Primitive::fromInt32(0); + ScopedValue v(scope, Primitive::fromInt32(0)); + insertMember(lastIndex, v, Attr_NotEnumerable|Attr_NotConfigurable); if (!this->value) return; @@ -162,7 +162,6 @@ void RegExpObject::init(ExecutionEngine *engine) p.replace('/', QLatin1String("\\/")); } - ScopedValue v(scope); defineReadonlyProperty(QStringLiteral("source"), (v = engine->newString(p))); defineReadonlyProperty(QStringLiteral("global"), Primitive::fromBoolean(global)); defineReadonlyProperty(QStringLiteral("ignoreCase"), Primitive::fromBoolean(this->value->ignoreCase())); @@ -344,11 +343,13 @@ ReturnedValue RegExpPrototype::method_exec(CallContext *ctx) Scoped<ArrayObject> array(scope, ctx->engine->newArrayObject(ctx->engine->regExpExecArrayClass)); int len = r->value->captureCount(); array->arrayReserve(len); + ScopedValue v(scope); for (int i = 0; i < len; ++i) { int start = matchOffsets[i * 2]; int end = matchOffsets[i * 2 + 1]; - array->arrayData[i].value = (start != -1 && end != -1) ? ctx->engine->newString(s.mid(start, end - start))->asReturnedValue() : Encode::undefined(); - array->arrayDataLen = i + 1; + v = (start != -1 && end != -1) ? ctx->engine->newString(s.mid(start, end - start))->asReturnedValue() : Encode::undefined(); + array->arrayData->put(i, v); + array->arrayData->setLength(i + 1); } array->setArrayLengthUnchecked(len); diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 0129f8d396..bd91e8f693 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -67,6 +67,7 @@ class RegExp; struct RegExpObject: Object { Q_MANAGED + Q_MANAGED_TYPE(RegExpObject) // needs to be compatible with the flags in qv4jsir_p.h enum Flags { RegExp_Global = 0x01, diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 011607f0ba..9275704984 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -589,12 +589,10 @@ ReturnedValue __qmljs_get_element(ExecutionContext *ctx, const ValueRef object, } if (idx < UINT_MAX) { - uint pidx = o->propertyIndexFromArrayIndex(idx); - if (pidx < UINT_MAX) { - if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) { - if (!o->arrayData[pidx].value.isEmpty()) - return o->arrayData[pidx].value.asReturnedValue(); - } + if (!o->arrayData->hasAttributes()) { + ScopedValue v(scope, o->arrayData->get(idx)); + if (!v->isEmpty()) + return v->asReturnedValue(); } return o->getIndexed(idx); @@ -615,36 +613,10 @@ void __qmljs_set_element(ExecutionContext *ctx, const ValueRef object, const Val uint idx = index->asArrayIndex(); if (idx < UINT_MAX) { - uint pidx = o->propertyIndexFromArrayIndex(idx); - if (pidx < UINT_MAX) { - if (o->arrayAttributes && !o->arrayAttributes[pidx].isEmpty() && !o->arrayAttributes[pidx].isWritable()) { - if (ctx->strictMode) - ctx->throwTypeError(); - return; - } - - Property *p = o->arrayData + pidx; - if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) { - p->value = *value; - return; - } - - if (o->arrayAttributes[pidx].isAccessor()) { - FunctionObject *setter = p->setter(); - if (!setter) { - if (ctx->strictMode) - ctx->throwTypeError(); - return; - } - - ScopedCallData callData(scope, 1); - callData->thisObject = o; - callData->args[0] = *value; - setter->call(callData); - return; - } - } - o->putIndexed(idx, value); + if (idx < o->arrayData->length() && o->arrayType() == ArrayData::Simple) + o->arrayData->put(idx, value); + else + o->putIndexed(idx, value); return; } @@ -1118,25 +1090,23 @@ void __qmljs_builtin_define_property(ExecutionContext *ctx, const ValueRef objec assert(o); uint idx = name->asArrayIndex(); - Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx) : o->insertMember(name, Attr_Data); - pd->value = val ? *val : Primitive::undefinedValue(); + if (idx != UINT_MAX) { + o->arraySet(idx, val); + } else { + ScopedValue v(scope, val ? *val : Primitive::undefinedValue()); + o->insertMember(name, v); + } } -ReturnedValue __qmljs_builtin_define_array(ExecutionContext *ctx, Value *values, uint length) +ReturnedValue __qmljs_builtin_define_array(ExecutionContext *ctx, SafeValue *values, uint length) { Scope scope(ctx); Scoped<ArrayObject> a(scope, ctx->engine->newArrayObject()); - // ### FIXME: We need to allocate the array data to avoid crashes other places - // This should rather be done when required - a->arrayReserve(length); if (length) { - a->arrayDataLen = length; - Property *pd = a->arrayData; - for (uint i = 0; i < length; ++i) { - pd->value = values[i]; - ++pd; - } + a->arrayReserve(length); + a->arrayData->setLength(length); + a->arrayData->put(0, values, length); a->setArrayLengthUnchecked(length); } return a.asReturnedValue(); @@ -1149,9 +1119,14 @@ void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const ValueRef Q_ASSERT(!!o); uint idx = name->asArrayIndex(); - Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx, Attr_Accessor) : o->insertMember(name, Attr_Accessor); - pd->setGetter(getter ? getter->asFunctionObject() : 0); - pd->setSetter(setter ? setter->asFunctionObject() : 0); + Property pd; + pd.setGetter(getter ? getter->asFunctionObject() : 0); + pd.setSetter(setter ? setter->asFunctionObject() : 0); + if (idx != UINT_MAX) { + o->arraySet(idx, pd, Attr_Accessor); + } else { + o->insertMember(name, pd, Attr_Accessor); + } } ReturnedValue __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, const QV4::Value *args, int classId) diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 9524b2459c..2e252b1067 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -141,7 +141,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, QV4::Value *values, uint length); +QV4::ReturnedValue __qmljs_builtin_define_array(QV4::ExecutionContext *ctx, SafeValue *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); diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 8b0e31cb71..feb3806ba5 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -166,6 +166,7 @@ template <typename Container> class QQmlSequence : public QV4::Object { Q_MANAGED + Q_MANAGED_TYPE(QmlSequence) public: QQmlSequence(QV4::ExecutionEngine *engine, const Container &container) : QV4::Object(InternalClass::create(engine, &static_vtbl, engine->sequencePrototype.asObject())) @@ -174,8 +175,7 @@ public: , m_propertyIndex(-1) , m_isReference(false) { - type = Type_QmlSequence; - flags &= ~SimpleArray; + setArrayType(ArrayData::Custom); QV4::Scope scope(engine); QV4::ScopedObject protectThis(scope, this); Q_UNUSED(protectThis); @@ -188,8 +188,7 @@ public: , m_propertyIndex(propertyIndex) , m_isReference(true) { - type = Type_QmlSequence; - flags &= ~SimpleArray; + setArrayType(ArrayData::Custom); QV4::Scope scope(engine); QV4::ScopedObject protectThis(scope, this); Q_UNUSED(protectThis); @@ -469,7 +468,7 @@ public: { QV4::Scope scope(array->engine()); Container result; - quint32 length = array->arrayLength(); + quint32 length = array->getLength(); QV4::ScopedValue v(scope); for (quint32 i = 0; i < length; ++i) result << convertValueToElement<typename Container::value_type>((v = array->getIndexed(i))); diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp index ee325db4c2..5f9e24d89c 100644 --- a/src/qml/jsruntime/qv4serialize.cpp +++ b/src/qml/jsruntime/qv4serialize.cpp @@ -183,7 +183,7 @@ void Serialize::serialize(QByteArray &data, const QV4::ValueRef v, QV8Engine *en push(data, valueheader(WorkerUndefined)); } else if (v->asArrayObject()) { QV4::ScopedArrayObject array(scope, v); - uint32_t length = array->arrayLength(); + uint32_t length = array->getLength(); if (length > 0xFFFFFF) { push(data, valueheader(WorkerUndefined)); return; @@ -266,7 +266,7 @@ void Serialize::serialize(QByteArray &data, const QV4::ValueRef v, QV8Engine *en // regular object QV4::ScopedValue val(scope, *v); QV4::ScopedArrayObject properties(scope, QV4::ObjectPrototype::getOwnPropertyNames(v4, val)); - quint32 length = properties->arrayLength(); + quint32 length = properties->getLength(); if (length > 0xFFFFFF) { push(data, valueheader(WorkerUndefined)); return; @@ -390,8 +390,8 @@ ReturnedValue Serialize::deserialize(const char *&data, QV8Engine *engine) array->arrayReserve(seqLength); for (quint32 ii = 0; ii < seqLength; ++ii) { value = deserialize(data, engine); - array->arrayData[ii].value = value.asReturnedValue(); - array->arrayDataLen = ii + 1; + array->arrayData->put(ii, value); + array->arrayData->setLength(ii + 1); } array->setArrayLengthUnchecked(seqLength); QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded); diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index e5633eb06f..df48faf601 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -103,6 +103,13 @@ static uint toArrayIndex(const char *ch, const char *end, bool *ok) const ManagedVTable String::static_vtbl = { + String::IsExecutionContext, + String::IsString, + String::IsObject, + String::IsFunctionObject, + String::IsErrorObject, + 0, + String::MyType, call, construct, markObjects, @@ -119,6 +126,7 @@ const ManagedVTable String::static_vtbl = 0 /*getLookup*/, 0 /*setLookup*/, isEqualTo, + 0, 0 /*advanceIterator*/, "String", }; @@ -233,10 +241,9 @@ bool String::isEqualTo(Managed *t, Managed *o) if (t == o) return true; - if (o->type != Type_String) + if (!o->internalClass->vtable->isString) return false; - Q_ASSERT(t->type == Type_String); String *that = static_cast<String *>(t); String *other = static_cast<String *>(o); if (that->hashValue() != other->hashValue()) @@ -257,7 +264,6 @@ String::String(ExecutionEngine *engine, const QString &text) { _text->ref.ref(); len = _text->size; - type = Type_String; subtype = StringType_Unknown; } @@ -267,7 +273,6 @@ String::String(ExecutionEngine *engine, String *l, String *r) , stringHash(UINT_MAX), largestSubLength(qMax(l->largestSubLength, r->largestSubLength)) , len(l->len + r->len) { - type = Type_String; subtype = StringType_Unknown; if (!l->largestSubLength && l->len > largestSubLength) @@ -418,3 +423,8 @@ uint String::toArrayIndex(const QString &str) bool ok; return ::toArrayIndex(str.constData(), str.constData() + str.length(), &ok); } + +uint String::getLength(const Managed *m) +{ + return static_cast<const String *>(m)->length(); +} diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 64e15b04c2..bc5b30518e 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -53,6 +53,11 @@ struct Identifier; struct Q_QML_EXPORT String : public Managed { Q_MANAGED + Q_MANAGED_TYPE(String) + enum { + IsString = true + }; + enum StringType { StringType_Unknown, StringType_Regular, @@ -63,7 +68,7 @@ struct Q_QML_EXPORT String : public Managed { String() : Managed(0), _text(QStringData::sharedNull()), identifier(0) , stringHash(UINT_MAX), largestSubLength(0), len(0) - { type = Type_String; subtype = StringType_Unknown; } + { subtype = StringType_Unknown; } String(ExecutionEngine *engine, const QString &text); String(ExecutionEngine *engine, String *l, String *n); ~String() { @@ -86,6 +91,7 @@ struct Q_QML_EXPORT String : public Managed { return toQString() == other->toQString(); } + inline bool compare(const String *other) { return toQString() < other->toQString(); } @@ -167,6 +173,7 @@ protected: static bool deleteProperty(Managed *, const StringRef); static bool deleteIndexedProperty(Managed *m, uint index); static bool isEqualTo(Managed *that, Managed *o); + static uint getLength(const Managed *m); private: QChar *recursiveAppend(QChar *ch) const; diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index d468fb6b83..1781ce2581 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -80,8 +80,7 @@ DEFINE_MANAGED_VTABLE(StringObject); StringObject::StringObject(InternalClass *ic) : Object(ic) { - setVTable(&static_vtbl); - type = Type_StringObject; + Q_ASSERT(internalClass->vtable == &static_vtbl); Scope scope(engine()); ScopedObject protectThis(scope, this); @@ -97,7 +96,6 @@ StringObject::StringObject(ExecutionEngine *engine, const ValueRef val) : Object(engine->stringObjectClass) { setVTable(&static_vtbl); - type = Type_StringObject; Scope scope(engine); ScopedObject protectThis(scope, this); @@ -146,14 +144,20 @@ Property *StringObject::advanceIterator(Managed *m, ObjectIterator *it, StringRe while (it->arrayIndex < slen) { *index = it->arrayIndex; ++it->arrayIndex; - if (attrs) - *attrs = s->arrayAttributes ? s->arrayAttributes[it->arrayIndex] : PropertyAttributes(Attr_NotWritable|Attr_NotConfigurable); - return s->__getOwnProperty__(*index); + PropertyAttributes a; + Property *p = s->__getOwnProperty__(*index, &a); + if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { + if (attrs) + *attrs = a; + return p; + } + } + if (s->arrayData) { + it->arrayNode = s->sparseBegin(); + // iterate until we're past the end of the string + while (it->arrayNode && it->arrayNode->key() < slen) + it->arrayNode = it->arrayNode->nextNode(); } - it->arrayNode = s->sparseArrayBegin(); - // iterate until we're past the end of the string - while (it->arrayNode && it->arrayNode->key() < slen) - it->arrayNode = it->arrayNode->nextNode(); } return Object::advanceIterator(m, it, name, index, attrs); @@ -694,18 +698,18 @@ ReturnedValue StringPrototype::method_split(CallContext *ctx) array->push_back((s = ctx->engine->newString(text.mid(offset, matchOffsets[0] - offset)))); offset = qMax(offset + 1, matchOffsets[1]); - if (array->arrayLength() >= limit) + if (array->getLength() >= limit) break; for (int i = 1; i < re->value->captureCount(); ++i) { uint start = matchOffsets[i * 2]; uint end = matchOffsets[i * 2 + 1]; array->push_back((s = ctx->engine->newString(text.mid(start, end - start)))); - if (array->arrayLength() >= limit) + if (array->getLength() >= limit) break; } } - if (array->arrayLength() < limit) + if (array->getLength() < limit) array->push_back((s = ctx->engine->newString(text.mid(offset)))); } else { QString separator = separatorValue->toString(ctx)->toQString(); @@ -720,10 +724,10 @@ ReturnedValue StringPrototype::method_split(CallContext *ctx) while ((end = text.indexOf(separator, start)) != -1) { array->push_back((s = ctx->engine->newString(text.mid(start, end - start)))); start = end + separator.size(); - if (array->arrayLength() >= limit) + if (array->getLength() >= limit) break; } - if (array->arrayLength() < limit && start != -1) + if (array->getLength() < limit && start != -1) array->push_back((s = ctx->engine->newString(text.mid(start)))); } return array.asReturnedValue(); diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index e8e46b85e7..b91cc48e36 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -51,6 +51,7 @@ namespace QV4 { struct StringObject: Object { Q_MANAGED + Q_MANAGED_TYPE(StringObject) SafeValue value; mutable Property tmpProperty; diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 680c7465ca..b93fcbe4bd 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -64,13 +64,13 @@ inline bool Value::isString() const { if (!isManaged()) return false; - return managed() && managed()->type == Managed::Type_String; + return managed() && managed()->internalClass->vtable->isString; } inline bool Value::isObject() const { if (!isManaged()) return false; - return managed() && managed()->type != Managed::Type_String; + return managed() && managed()->internalClass->vtable->isObject; } inline bool Value::isPrimitive() const diff --git a/src/qml/parser/qqmljsmemorypool_p.h b/src/qml/parser/qqmljsmemorypool_p.h index 820ae8ed71..29103930ad 100644 --- a/src/qml/parser/qqmljsmemorypool_p.h +++ b/src/qml/parser/qqmljsmemorypool_p.h @@ -108,6 +108,8 @@ public: _ptr = _end = 0; } + template <typename _Tp> _Tp *New() { return new (this->allocate(sizeof(_Tp))) _Tp(); } + private: void *allocate_helper(size_t size) { diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 9e2fb07066..9b83feebb4 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -146,7 +146,8 @@ QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlConte typeData->release(); } - } + } else + code = scriptPrivate->script; setNotifyOnValueChanged(true); QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context)); diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp index 3dd9d6a14d..069345ecd3 100644 --- a/src/qml/qml/qqmlcompiler.cpp +++ b/src/qml/qml/qqmlcompiler.cpp @@ -2614,7 +2614,7 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop, if (isIntProp) { // Allow enum assignment to ints. bool ok; - int enumval = evaluateEnum(typeName, enumValue.toUtf8(), &ok); + int enumval = evaluateEnum(typeName.toString(), enumValue.toUtf8(), &ok); if (ok) { v->type = Value::Literal; v->value = QQmlScript::Variant((double)enumval); @@ -2665,46 +2665,38 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop, return true; } -// Similar logic to above, but not knowing target property. -int QQmlCompiler::evaluateEnum(const QHashedStringRef &scope, const QByteArray& enumValue, bool *ok) const +int QQmlCompiler::bindingIdentifier(const QString &name, const Variant &value, const BindingContext &ctxt) { - Q_ASSERT_X(ok, "QQmlCompiler::evaluateEnum", "ok must not be a null pointer"); - *ok = false; + JSBindingReference *reference = pool->New<JSBindingReference>(); + reference->expression = value; + reference->property = pool->New<Property>(); + reference->property->setName(name); + reference->value = 0; + reference->bindingContext = ctxt; + reference->bindingContext.owner++; + // Unfortunately this is required for example for PropertyChanges where the bindings + // will be executed in the dynamic scope of the target, so we can't resolve any lookups + // at run-time. + reference->disableLookupAcceleration = true; - if (scope != QLatin1String("Qt")) { - QQmlType *type = 0; - unit->imports().resolveType(scope, &type, 0, 0, 0); - return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; - } + const int id = output->customParserBindings.count(); + output->customParserBindings.append(0); // Filled in later. + reference->customParserBindingsIndex = id; - 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; -} + compileState->totalBindingsCount++; + compileState->bindings.prepend(reference); -const QMetaObject *QQmlCompiler::resolveType(const QString& name) const -{ - QQmlType *qmltype = 0; - if (!unit->imports().resolveType(name, &qmltype, 0, 0, 0)) - return 0; - if (!qmltype) - return 0; - return qmltype->metaObject(); + return id; } -int QQmlCompiler::bindingIdentifier(const QString &name, const Variant &value, const BindingContext &ctxt) +QQmlBinding::Identifier QQmlCompiler::bindingIdentifier(const Variant &value, const QString &name, QQmlCustomParser *customParser) { JSBindingReference *reference = pool->New<JSBindingReference>(); reference->expression = value; reference->property = pool->New<Property>(); reference->property->setName(name); reference->value = 0; - reference->bindingContext = ctxt; + reference->bindingContext = QQmlCompilerTypes::BindingContext(customParser->object); reference->bindingContext.owner++; // Unfortunately this is required for example for PropertyChanges where the bindings // will be executed in the dynamic scope of the target, so we can't resolve any lookups @@ -2882,7 +2874,7 @@ bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mod Object::DynamicProperty::Type dtype; int metaType; } builtinTypes[] = { - { Object::DynamicProperty::Var, QMetaType::QVariant }, + { Object::DynamicProperty::Var, qMetaTypeId<QJSValue>() }, { Object::DynamicProperty::Variant, QMetaType::QVariant }, { Object::DynamicProperty::Int, QMetaType::Int }, { Object::DynamicProperty::Bool, QMetaType::Bool }, diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h index 516f6653ca..788def0288 100644 --- a/src/qml/qml/qqmlcompiler_p.h +++ b/src/qml/qml/qqmlcompiler_p.h @@ -65,6 +65,7 @@ #include <private/qqmlcodegenerator_p.h> #include "private/qv4identifier_p.h" #include <private/qqmljsastfwd_p.h> +#include "qqmlcustomparser_p.h" #include <QtCore/qbytearray.h> #include <QtCore/qset.h> @@ -144,9 +145,9 @@ public: QQmlPropertyCache *rootPropertyCache; QList<QString> primitives; - QList<QByteArray> datas; + QVector<QByteArray> datas; QByteArray bytecode; - QList<QQmlPropertyCache *> propertyCaches; + QVector<QQmlPropertyCache *> propertyCaches; QList<QVector<QQmlContextData::ObjectIdMapping> > contextCaches; QList<QQmlScriptData *> scripts; QList<QUrl> urls; @@ -157,6 +158,8 @@ public: // index in first hash is component index, hash inside maps from object index in that scope to integer id QHash<int, QHash<int, int> > objectIndexToIdPerComponent; QHash<int, int> objectIndexToIdForRoot; + // hash key is object index + QHash<int, QByteArray> customParserData; QVector<int> customParserBindings; // index is binding identifier, value is compiled function index. bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } @@ -330,7 +333,7 @@ namespace QQmlCompilerTypes { }; class QMetaObjectBuilder; -class Q_AUTOTEST_EXPORT QQmlCompiler +class Q_AUTOTEST_EXPORT QQmlCompiler : public QQmlCustomParserCompilerBackend { Q_DECLARE_TR_FUNCTIONS(QQmlCompiler) public: @@ -346,9 +349,10 @@ public: static bool isAttachedPropertyName(const QHashedStringRef &); static bool isSignalPropertyName(const QHashedStringRef &); - int evaluateEnum(const QHashedStringRef &scope, const QByteArray& enumValue, bool *ok) const; // for QQmlCustomParser::evaluateEnum - const QMetaObject *resolveType(const QString& name) const; // for QQmlCustomParser::resolveType int bindingIdentifier(const QString &name, const QQmlScript::Variant& value, const QQmlCompilerTypes::BindingContext &ctxt); // for QQmlCustomParser::bindingIndex + virtual QQmlBinding::Identifier bindingIdentifier(const QQmlScript::Variant&value, const QString&name, QQmlCustomParser *customParser); + + virtual const QQmlImports &imports() const { return unit->imports(); } private: typedef QQmlCompiledData::Instruction Instruction; diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 21bcd3569c..5813415347 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1056,7 +1056,11 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, p->compiledData = d->cc; p->compiledData->addref(); - p->vme.init(contextData, d->cc, d->start, d->creationContext); + if (enginePriv->useNewCompiler) { + p->creator.reset(new QmlObjectCreator(contextData, d->cc)); + p->subComponentToCreate = d->start; + } else + p->vme.init(contextData, d->cc, d->start, d->creationContext); enginePriv->incubate(incubator, forContextData); } diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index 19e49009ce..ca23451774 100644 --- a/src/qml/qml/qqmlcustomparser.cpp +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -230,6 +230,13 @@ void QQmlCustomParser::clearErrors() exceptions.clear(); } +QByteArray QQmlCustomParser::compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +{ + Q_UNUSED(qmlUnit) + Q_UNUSED(bindings) + return QByteArray(); +} + /*! Reports an error with the given \a description. @@ -256,7 +263,6 @@ void QQmlCustomParser::error(const QString& description) void QQmlCustomParser::error(const QQmlCustomParserProperty& prop, const QString& description) { QQmlError error; - QString exceptionDescription; error.setLine(prop.location().line); error.setColumn(prop.location().column); error.setDescription(description); @@ -271,7 +277,6 @@ void QQmlCustomParser::error(const QQmlCustomParserProperty& prop, const QString void QQmlCustomParser::error(const QQmlCustomParserNode& node, const QString& description) { QQmlError error; - QString exceptionDescription; error.setLine(node.location().line); error.setColumn(node.location().column); error.setDescription(description); @@ -279,6 +284,34 @@ void QQmlCustomParser::error(const QQmlCustomParserNode& node, const QString& de } /*! + Reports an error in parsing \a binding, with the given \a description. + + An error is generated referring to the position of \a node in the source file. +*/ +void QQmlCustomParser::error(const QV4::CompiledData::Binding *binding, const QString &description) +{ + QQmlError error; + error.setLine(binding->location.line); + error.setColumn(binding->location.column); + error.setDescription(description); + exceptions << error; +} + +/*! + Reports an error in parsing \a object, with the given \a description. + + An error is generated referring to the position of \a object in the source file. +*/ +void QQmlCustomParser::error(const QV4::CompiledData::Object *object, const QString &description) +{ + QQmlError error; + error.setLine(object->location.line); + error.setColumn(object->location.column); + error.setDescription(description); + exceptions << error; +} + +/*! If \a script is a simple enumeration expression (eg. Text.AlignLeft), returns the integer equivalent (eg. 1), and sets \a ok to true. @@ -313,7 +346,44 @@ const QMetaObject *QQmlCustomParser::resolveType(const QString& name) const */ QQmlBinding::Identifier QQmlCustomParser::bindingIdentifier(const QQmlScript::Variant &value, const QString& name) { - return compiler->bindingIdentifier(name, value, QQmlCompilerTypes::BindingContext(object)); + return compiler->bindingIdentifier(value, name, this); +} + +struct StaticQtMetaObject : public QObject +{ + static const QMetaObject *get() + { return &staticQtMetaObject; } +}; + +int QQmlCustomParserCompilerBackend::evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const +{ + Q_ASSERT_X(ok, "QQmlCompiler::evaluateEnum", "ok must not be a null pointer"); + *ok = false; + + if (scope != QLatin1String("Qt")) { + QQmlType *type = 0; + imports().resolveType(scope, &type, 0, 0, 0); + return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; + } + + const QMetaObject *mo = StaticQtMetaObject::get(); + int i = mo->enumeratorCount(); + while (i--) { + int v = mo->enumerator(i).keyToValue(enumValue.constData(), ok); + if (*ok) + return v; + } + return -1; +} + +const QMetaObject *QQmlCustomParserCompilerBackend::resolveType(const QString &name) const +{ + QQmlType *qmltype = 0; + if (!imports().resolveType(name, &qmltype, 0, 0, 0)) + return 0; + if (!qmltype) + return 0; + return qmltype->metaObject(); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h index 27f7e00b31..2f1a9ddec8 100644 --- a/src/qml/qml/qqmlcustomparser_p.h +++ b/src/qml/qml/qqmlcustomparser_p.h @@ -108,6 +108,17 @@ private: QQmlCustomParserNodePrivate *d; }; +struct QQmlCustomParserCompilerBackend +{ + virtual ~QQmlCustomParserCompilerBackend() {} + virtual const QQmlImports &imports() const = 0; + + int evaluateEnum(const QString &scope, const QByteArray& enumValue, bool *ok) const; + const QMetaObject *resolveType(const QString& name) const; + + virtual QQmlBinding::Identifier bindingIdentifier(const QQmlScript::Variant&, const QString&, QQmlCustomParser *) { return QQmlBinding::Invalid; } +}; + class Q_QML_PRIVATE_EXPORT QQmlCustomParser { public: @@ -126,6 +137,7 @@ public: Flags flags() const { return m_flags; } virtual QByteArray compile(const QList<QQmlCustomParserProperty> &)=0; + virtual QByteArray compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings); // ### make pure virtual virtual void setCustomData(QObject *, const QByteArray &)=0; QList<QQmlError> errors() const { return exceptions; } @@ -134,6 +146,8 @@ protected: void error(const QString& description); void error(const QQmlCustomParserProperty&, const QString& description); void error(const QQmlCustomParserNode&, const QString& description); + void error(const QV4::CompiledData::Binding *binding, const QString& description); + void error(const QV4::CompiledData::Object *object, const QString& description); int evaluateEnum(const QByteArray&, bool *ok) const; @@ -143,10 +157,11 @@ protected: private: QList<QQmlError> exceptions; - QQmlCompiler *compiler; + QQmlCustomParserCompilerBackend *compiler; QQmlScript::Object *object; Flags m_flags; friend class QQmlCompiler; + friend class QQmlPropertyValidator; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlCustomParser::Flags) diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index ade4634c2d..52bf1d8b3e 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -46,6 +46,7 @@ #include "qqmlcompiler_p.h" #include "qqmlexpression_p.h" #include "qqmlmemoryprofiler_p.h" +#include "qqmlobjectcreator_p.h" // XXX TODO // - check that the Component.onCompleted behavior is the same as 4.8 in the synchronous and @@ -292,7 +293,14 @@ void QQmlIncubatorPrivate::incubate(QQmlVME::Interrupt &i) if (progress == QQmlIncubatorPrivate::Execute) { enginePriv->referenceScarceResources(); - QObject *tresult = vme.execute(&errors, i); + QObject *tresult = 0; + if (enginePriv->useNewCompiler) { + tresult = creator->create(subComponentToCreate); + if (!tresult) + errors = creator->errors; + } else { + tresult = vme.execute(&errors, i); + } enginePriv->dereferenceScarceResources(); if (watcher.hasRecursed()) @@ -335,7 +343,11 @@ void QQmlIncubatorPrivate::incubate(QQmlVME::Interrupt &i) if (watcher.hasRecursed()) return; - QQmlContextData *ctxt = vme.complete(i); + QQmlContextData *ctxt = 0; + if (enginePriv->useNewCompiler) + ctxt = creator->finalize(); + else + ctxt = vme.complete(i); if (ctxt) { rootContext = ctxt; progress = QQmlIncubatorPrivate::Completed; @@ -566,6 +578,7 @@ void QQmlIncubator::clear() d->vme.reset(); d->vmeGuard.clear(); + d->creator.reset(0); Q_ASSERT(d->compiledData == 0); Q_ASSERT(d->waitingOnMe.data() == 0); diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h index e7246ce3b2..a8b549bd28 100644 --- a/src/qml/qml/qqmlincubator_p.h +++ b/src/qml/qml/qqmlincubator_p.h @@ -88,7 +88,12 @@ public: QPointer<QObject> result; QQmlGuardedContextData rootContext; QQmlCompiledData *compiledData; + // --- old compiler QQmlVME vme; + // --- new compiler + QScopedPointer<QmlObjectCreator> creator; + int subComponentToCreate; + // --- QQmlVMEGuard vmeGuard; QExplicitlySharedDataPointer<QQmlIncubatorPrivate> waitingOnMe; diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index 7b975c2cc8..76c11cb748 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -57,7 +57,7 @@ QmlListWrapper::QmlListWrapper(QV8Engine *engine) v8(engine) { setVTable(&static_vtbl); - flags &= ~SimpleArray; + setArrayType(ArrayData::Custom); } QmlListWrapper::~QmlListWrapper() diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index 36e0da5b60..625b3a3175 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -54,63 +54,6 @@ QT_BEGIN_NAMESPACE -class QQmlLocaleData : public QV4::Object -{ - Q_MANAGED -public: - QQmlLocaleData(QV4::ExecutionEngine *engine) - : QV4::Object(engine) - { - setVTable(&static_vtbl); - type = Type_Object; - } - - QLocale locale; - - static QLocale *getThisLocale(QV4::CallContext *ctx) { - QQmlLocaleData *thisObject = ctx->callData->thisObject.asObject()->as<QQmlLocaleData>(); - if (!thisObject) { - ctx->throwTypeError(); - return 0; - } - return &thisObject->locale; - } - - static QV4::ReturnedValue method_currencySymbol(QV4::CallContext *ctx); - static QV4::ReturnedValue method_dateTimeFormat(QV4::CallContext *ctx); - static QV4::ReturnedValue method_timeFormat(QV4::CallContext *ctx); - static QV4::ReturnedValue method_dateFormat(QV4::CallContext *ctx); - static QV4::ReturnedValue method_monthName(QV4::CallContext *ctx); - static QV4::ReturnedValue method_standaloneMonthName(QV4::CallContext *ctx); - static QV4::ReturnedValue method_dayName(QV4::CallContext *ctx); - static QV4::ReturnedValue method_standaloneDayName(QV4::CallContext *ctx); - - static QV4::ReturnedValue method_get_firstDayOfWeek(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_measurementSystem(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_textDirection(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_weekDays(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_uiLanguages(QV4::CallContext *ctx); - - static QV4::ReturnedValue method_get_name(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_nativeLanguageName(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_nativeCountryName(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_decimalPoint(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_groupSeparator(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_percent(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_zeroDigit(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_negativeSign(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_positiveSign(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_exponential(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_amText(QV4::CallContext *ctx); - static QV4::ReturnedValue method_get_pmText(QV4::CallContext *ctx); - -private: - static void destroy(Managed *that) - { - static_cast<QQmlLocaleData *>(that)->~QQmlLocaleData(); - } -}; - DEFINE_MANAGED_VTABLE(QQmlLocaleData); #define GET_LOCALE_DATA_RESOURCE(OBJECT) \ @@ -555,12 +498,12 @@ QV4::ReturnedValue QQmlLocaleData::method_get_weekDays(QV4::CallContext *ctx) QV4::Scoped<QV4::ArrayObject> result(scope, ctx->engine->newArrayObject()); result->arrayReserve(days.size()); - result->arrayDataLen = days.size(); + result->arrayData->setLength(days.size()); for (int i = 0; i < days.size(); ++i) { int day = days.at(i); if (day == 7) // JS Date days in range 0(Sunday) to 6(Saturday) day = 0; - result->arrayData[i].value = QV4::Primitive::fromInt32(day); + result->arrayData->put(i, QV4::Primitive::fromInt32(day)); } result->setArrayLengthUnchecked(days.size()); @@ -577,9 +520,10 @@ QV4::ReturnedValue QQmlLocaleData::method_get_uiLanguages(QV4::CallContext *ctx) QStringList langs = locale->uiLanguages(); QV4::Scoped<QV4::ArrayObject> result(scope, ctx->engine->newArrayObject()); result->arrayReserve(langs.size()); + QV4::ScopedValue v(scope); for (int i = 0; i < langs.size(); ++i) { - result->arrayData[i].value = ctx->engine->newString(langs.at(i)); - result->arrayDataLen = i + 1; + result->arrayData->put(i, (v = ctx->engine->newString(langs.at(i)))); + result->arrayData->setLength(i + 1); } result->setArrayLengthUnchecked(langs.size()); @@ -857,14 +801,21 @@ QQmlLocale::~QQmlLocale() { } -QV4::ReturnedValue QQmlLocale::locale(QV8Engine *v8engine, const QString &locale) +QV4::ReturnedValue QQmlLocale::locale(QV8Engine *v8engine, const QString &localeName) +{ + QLocale qlocale; + if (!localeName.isEmpty()) + qlocale = localeName; + return wrap(v8engine, qlocale); +} + +QV4::ReturnedValue QQmlLocale::wrap(QV8Engine *engine, const QLocale &locale) { - QV8LocaleDataDeletable *d = localeV8Data(v8engine); - QV4::ExecutionEngine *engine = QV8Engine::getV4(v8engine); - QV4::Scope scope(engine); - QV4::Scoped<QQmlLocaleData> wrapper(scope, new (engine->memoryManager) QQmlLocaleData(engine)); - if (!locale.isEmpty()) - wrapper->locale = QLocale(locale); + QV8LocaleDataDeletable *d = localeV8Data(engine); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + QV4::Scope scope(v4); + QV4::Scoped<QQmlLocaleData> wrapper(scope, new (v4->memoryManager) QQmlLocaleData(v4)); + wrapper->locale = locale; QV4::ScopedObject p(scope, d->prototype.value()); wrapper->setPrototype(p.getPointer()); return wrapper.asReturnedValue(); diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h index 8ca67a8c83..1f26f1834f 100644 --- a/src/qml/qml/qqmllocale_p.h +++ b/src/qml/qml/qqmllocale_p.h @@ -118,7 +118,8 @@ public: Saturday = Qt::Saturday }; - static QV4::ReturnedValue locale(QV8Engine *v8engine, const QString &lang); + static QV4::ReturnedValue locale(QV8Engine *v8engine, const QString &localeName); + static QV4::ReturnedValue wrap(QV8Engine *engine, const QLocale &locale); static void registerStringLocaleCompare(QV4::ExecutionEngine *engine); @@ -128,6 +129,61 @@ private: static QV4::ReturnedValue method_localeCompare(QV4::CallContext *ctx); }; +class QQmlLocaleData : public QV4::Object +{ + Q_MANAGED +public: + QQmlLocaleData(QV4::ExecutionEngine *engine) + : QV4::Object(engine) + { + setVTable(&static_vtbl); + } + + QLocale locale; + + static QLocale *getThisLocale(QV4::CallContext *ctx) { + QQmlLocaleData *thisObject = ctx->callData->thisObject.asObject()->as<QQmlLocaleData>(); + if (!thisObject) { + ctx->throwTypeError(); + return 0; + } + return &thisObject->locale; + } + + static QV4::ReturnedValue method_currencySymbol(QV4::CallContext *ctx); + static QV4::ReturnedValue method_dateTimeFormat(QV4::CallContext *ctx); + static QV4::ReturnedValue method_timeFormat(QV4::CallContext *ctx); + static QV4::ReturnedValue method_dateFormat(QV4::CallContext *ctx); + static QV4::ReturnedValue method_monthName(QV4::CallContext *ctx); + static QV4::ReturnedValue method_standaloneMonthName(QV4::CallContext *ctx); + static QV4::ReturnedValue method_dayName(QV4::CallContext *ctx); + static QV4::ReturnedValue method_standaloneDayName(QV4::CallContext *ctx); + + static QV4::ReturnedValue method_get_firstDayOfWeek(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_measurementSystem(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_textDirection(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_weekDays(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_uiLanguages(QV4::CallContext *ctx); + + static QV4::ReturnedValue method_get_name(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_nativeLanguageName(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_nativeCountryName(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_decimalPoint(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_groupSeparator(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_percent(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_zeroDigit(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_negativeSign(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_positiveSign(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_exponential(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_amText(QV4::CallContext *ctx); + static QV4::ReturnedValue method_get_pmText(QV4::CallContext *ctx); + +private: + static void destroy(Managed *that) + { + static_cast<QQmlLocaleData *>(that)->~QQmlLocaleData(); + } +}; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index ed0c0afd6f..437e920d1a 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -664,7 +664,7 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const { // Add any enum values defined by 'related' classes if (metaObject->d.relatedMetaObjects) { - const QMetaObject **related = metaObject->d.relatedMetaObjects; + const QMetaObject * const *related = metaObject->d.relatedMetaObjects; if (related) { while (*related) insertEnums(*related++); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 3798129e8b..963b8272f4 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -42,7 +42,6 @@ #include "qqmlobjectcreator_p.h" #include <private/qqmlengine_p.h> -#include <private/qqmlabstractbinding_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qv4function_p.h> #include <private/qv4functionobject_p.h> @@ -52,9 +51,10 @@ #include <private/qqmlboundsignal_p.h> #include <private/qqmltrace_p.h> #include <private/qqmlcomponentattached_p.h> -#include <QQmlComponent> #include <private/qqmlcomponent_p.h> -#include <private/qqmlcodegenerator_p.h> +#include <private/qqmlcustomparser_p.h> +#include <private/qqmlscriptstring_p.h> +#include <private/qqmlpropertyvalueinterceptor_p.h> QT_USE_NAMESPACE @@ -70,394 +70,6 @@ struct ActiveOCRestorer }; } -QQmlCompilePass::QQmlCompilePass(const QUrl &url, const QV4::CompiledData::QmlUnit *unit) - : url(url) - , qmlUnit(unit) -{ -} - -void QQmlCompilePass::recordError(const QV4::CompiledData::Location &location, const QString &description) -{ - QQmlError error; - error.setUrl(url); - error.setLine(location.line); - error.setColumn(location.column); - error.setDescription(description); - errors << error; -} - -#define COMPILE_EXCEPTION(token, desc) \ - { \ - recordError((token)->location, desc); \ - return false; \ - } - -static QAtomicInt classIndexCounter(0); - -QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlEnginePrivate *enginePrivate, const QV4::CompiledData::QmlUnit *unit, const QUrl &url, const QQmlImports *imports, - QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes) - : QQmlCompilePass(url, unit) - , enginePrivate(enginePrivate) - , imports(imports) - , resolvedTypes(resolvedTypes) -{ -} - -bool QQmlPropertyCacheCreator::create(const QV4::CompiledData::Object *obj, QQmlPropertyCache **resultCache, QByteArray *vmeMetaObjectData) -{ - Q_ASSERT(!stringAt(obj->inheritedTypeNameIndex).isEmpty()); - - QQmlCompiledData::TypeReference typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); - QQmlPropertyCache *baseTypeCache = typeRef.createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - Q_ASSERT(baseTypeCache); - if (obj->nProperties == 0 && obj->nSignals == 0 && obj->nFunctions == 0) { - *resultCache = baseTypeCache; - vmeMetaObjectData->clear(); - return true; - } - - QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(QQmlEnginePrivate::get(enginePrivate), - obj->nProperties, - obj->nFunctions + obj->nProperties + obj->nSignals, - obj->nSignals + obj->nProperties); - *resultCache = cache; - - vmeMetaObjectData->clear(); - - struct TypeData { - QV4::CompiledData::Property::Type dtype; - int metaType; - } builtinTypes[] = { - { QV4::CompiledData::Property::Var, QMetaType::QVariant }, - { QV4::CompiledData::Property::Variant, QMetaType::QVariant }, - { QV4::CompiledData::Property::Int, QMetaType::Int }, - { QV4::CompiledData::Property::Bool, QMetaType::Bool }, - { QV4::CompiledData::Property::Real, QMetaType::Double }, - { QV4::CompiledData::Property::String, QMetaType::QString }, - { QV4::CompiledData::Property::Url, QMetaType::QUrl }, - { QV4::CompiledData::Property::Color, QMetaType::QColor }, - { QV4::CompiledData::Property::Font, QMetaType::QFont }, - { QV4::CompiledData::Property::Time, QMetaType::QTime }, - { QV4::CompiledData::Property::Date, QMetaType::QDate }, - { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime }, - { QV4::CompiledData::Property::Rect, QMetaType::QRectF }, - { QV4::CompiledData::Property::Point, QMetaType::QPointF }, - { QV4::CompiledData::Property::Size, QMetaType::QSizeF }, - { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D }, - { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D }, - { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D }, - { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 }, - { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion } - }; - static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); - - QByteArray newClassName; - - if (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; - - const QV4::CompiledData::Property *p = obj->propertyTable(); - for (quint32 i = 0; i < obj->nProperties; ++i, ++p) { - - 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 = *vmeMetaObjectData = QByteArray(sizeof(QQmlVMEMetaData) - + obj->nProperties * sizeof(VMD::PropertyData) - + obj->nFunctions * 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; - - const QV4::CompiledData::Property *p = obj->propertyTable(); - for (quint32 i = 0; i < obj->nProperties; ++i, ++p) { - 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 (uint i = 0; i < obj->nSignals; ++i) { - const QV4::CompiledData::Signal *s = obj->signalAt(i); - const int paramCount = s->nParameters; - - QList<QByteArray> names; - QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0); - - if (paramCount) { - paramTypes[0] = paramCount; - - for (int i = 0; i < paramCount; ++i) { - const QV4::CompiledData::Parameter *param = s->parameterAt(i); - names.append(stringAt(param->nameIndex).toUtf8()); - if (param->type < builtinTypeCount) { - // built-in type - paramTypes[i + 1] = builtinTypes[param->type].metaType; - } else { - // lazily resolved type - Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); - const QString customTypeName = stringAt(param->customTypeNameIndex); - QQmlType *qmltype = 0; - if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0)) - 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 - const quint32 *functionIndex = obj->functionOffsetTable(); - for (quint32 i = 0; i < obj->nFunctions; ++i, ++functionIndex) { - const QV4::CompiledData::Function *s = qmlUnit->header.functionAt(*functionIndex); - int paramCount = s->nFormals; - - quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; - - if (paramCount) - flags |= QQmlPropertyData::HasArguments; - - QString slotName = stringAt(s->nameIndex); - 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. - - const quint32 *formalsIndices = s->formalsTable(); - QList<QByteArray> parameterNames; - parameterNames.reserve(paramCount); - for (int i = 0; i < paramCount; ++i) - parameterNames << stringAt(formalsIndices[i]).toUtf8(); - - cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); - } - - - // Dynamic properties (except var and aliases) - int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; - /* const QV4::CompiledData::Property* */ p = obj->propertyTable(); - for (quint32 i = 0; i < obj->nProperties; ++i, ++p) { - - 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 (i == 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 - /* const QV4::CompiledData::Property* */ p = obj->propertyTable(); - for (quint32 i = 0; i < obj->nProperties; ++i, ++p) { - - 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 (i == 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 - /*const quint32* */functionIndex = obj->functionOffsetTable(); - for (quint32 i = 0; i < obj->nFunctions; ++i, ++functionIndex) { - const QV4::CompiledData::Function *s = qmlUnit->header.functionAt(*functionIndex); - - VMD::MethodData methodData = { /* runtimeFunctionIndex*/ 0, // ### - int(s->nFormals), - /* s->location.start.line */0 }; // ### - - VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); - VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount); - vmd->methodCount++; - md = methodData; - } - - return true; -} - static void removeBindingOnProperty(QObject *o, int index) { int coreIndex = index & 0x0000FFFF; @@ -468,9 +80,10 @@ static void removeBindingOnProperty(QObject *o, int index) } QmlObjectCreator::QmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData) - : QQmlCompilePass(compiledData->url, compiledData->qmlUnit) - , componentAttached(0) + : componentAttached(0) + , url(compiledData->url) , engine(parentContext->engine) + , qmlUnit(compiledData->qmlUnit) , jsUnit(compiledData->compilationUnit) , parentContext(parentContext) , context(0) @@ -478,8 +91,9 @@ QmlObjectCreator::QmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledD , propertyCaches(compiledData->propertyCaches) , vmeMetaObjectData(compiledData->datas) , compiledData(compiledData) + , rootContext(0) , _qobject(0) - , _qobjectForBindings(0) + , _scopeObject(0) , _valueTypeProperty(0) , _compiledObject(0) , _ddata(0) @@ -512,6 +126,9 @@ QObject *QmlObjectCreator::create(int subComponentIndex, QObject *parent) context->imports->addref(); context->setParent(parentContext); + if (!rootContext) + rootContext = context; + QVector<QQmlContextData::ObjectIdMapping> mapping(objectIndexToId.count()); for (QHash<int, int>::ConstIterator it = objectIndexToId.constBegin(), end = objectIndexToId.constEnd(); it != end; ++it) { @@ -537,6 +154,10 @@ QObject *QmlObjectCreator::create(int subComponentIndex, QObject *parent) context->importedScripts = parentContext->importedScripts; } + QVector<QQmlParserStatus*> parserStatusCallbacks; + parserStatusCallbacks.resize(qmlUnit->nObjects); + qSwap(_parserStatusCallbacks, parserStatusCallbacks); + QObject *instance = createInstance(objectToCreate, parent); if (instance) { QQmlData *ddata = QQmlData::get(instance); @@ -546,6 +167,10 @@ QObject *QmlObjectCreator::create(int subComponentIndex, QObject *parent) context->contextObject = instance; } + + qSwap(_parserStatusCallbacks, parserStatusCallbacks); + allParserStatusCallbacks.prepend(parserStatusCallbacks); + return instance; } @@ -558,7 +183,13 @@ void QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, const QV4::C QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); QV4::Scope scope(v4); - // ### enums + + // ### This should be resolved earlier at compile time and the binding value should be changed accordingly. + if (property->isEnum()) { + QVariant value = binding->valueAsString(&qmlUnit->header); + QQmlPropertyPrivate::write(_qobject, *property, value, context); + return; + } switch (property->propType) { case QMetaType::QVariant: { @@ -1015,14 +646,32 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI const int id = attachedType->attachedPropertiesId(); QObject *qmlObject = qmlAttachedPropertiesObjectById(id, _qobject); QQmlRefPointer<QQmlPropertyCache> cache = QQmlEnginePrivate::get(engine)->cache(qmlObject); - if (!populateInstance(binding->value.objectIndex, qmlObject, cache, _qobject, /*value type property*/0)) + if (!populateInstance(binding->value.objectIndex, qmlObject, cache, qmlObject, /*value type property*/0)) return false; return true; } + // ### resolve this at compile time + if (property && property->propType == qMetaTypeId<QQmlScriptString>()) { + QQmlScriptString ss(binding->valueAsScriptString(&qmlUnit->header), context->asQQmlContext(), _scopeObject); + ss.d.data()->bindingId = QQmlBinding::Invalid; + ss.d.data()->lineNumber = binding->location.line; + ss.d.data()->columnNumber = binding->location.column; + ss.d.data()->isStringLiteral = binding->type == QV4::CompiledData::Binding::Type_String; + ss.d.data()->isNumberLiteral = binding->type == QV4::CompiledData::Binding::Type_Number; + ss.d.data()->numberValue = binding->valueAsNumber(); + + QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | + QQmlPropertyPrivate::RemoveBindingOnAliasWrite; + int propertyWriteStatus = -1; + void *argv[] = { &ss, 0, &propertyWriteStatus, &propertyWriteFlags }; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + return true; + } + QObject *createdSubObject = 0; if (binding->type == QV4::CompiledData::Binding::Type_Object) { - createdSubObject = createInstance(binding->value.objectIndex, _qobject); + createdSubObject = createInstance(binding->value.objectIndex, _bindingTarget); if (!createdSubObject) return false; } @@ -1035,11 +684,11 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) { QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); - QQmlRefPointer<QQmlPropertyCache> groupedObjCache; - QObject *groupedObjInstance = 0; - QObject *objForBindings = _qobjectForBindings; + QQmlRefPointer<QQmlPropertyCache> groupObjectPropertyCache; + QObject *groupObject = 0; QQmlValueType *valueType = 0; QQmlPropertyData *valueTypeProperty = 0; + QObject *bindingTarget = _bindingTarget; if (QQmlValueTypeFactory::isValueType(property->propType)) { valueType = QQmlValueTypeFactory::valueType(property->propType); @@ -1050,27 +699,27 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI valueType->read(_qobject, property->coreIndex); - groupedObjCache = enginePrivate->cache(valueType); - groupedObjInstance = valueType; + groupObjectPropertyCache = enginePrivate->cache(valueType); + groupObject = valueType; valueTypeProperty = property; } else { - groupedObjCache = enginePrivate->propertyCacheForType(property->propType); - if (!groupedObjCache) { + groupObjectPropertyCache = enginePrivate->propertyCacheForType(property->propType); + if (!groupObjectPropertyCache) { recordError(binding->location, tr("Invalid grouped property access")); return false; } - void *argv[1] = { &groupedObjInstance }; + void *argv[1] = { &groupObject }; QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex, argv); - if (!groupedObjInstance) { + if (!groupObject) { recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); return false; } - objForBindings = groupedObjInstance; + bindingTarget = groupObject; } - if (!populateInstance(binding->value.objectIndex, groupedObjInstance, groupedObjCache, objForBindings, valueTypeProperty)) + if (!populateInstance(binding->value.objectIndex, groupObject, groupObjectPropertyCache, bindingTarget, valueTypeProperty)) return false; if (valueType) @@ -1080,8 +729,9 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI } } - if (_ddata->hasBindingBit(property->coreIndex)) - removeBindingOnProperty(_qobject, property->coreIndex); + if (_ddata->hasBindingBit(property->coreIndex) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) + && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) + removeBindingOnProperty(_bindingTarget, property->coreIndex); if (binding->type == QV4::CompiledData::Binding::Type_Script) { QV4::Function *runtimeFunction = jsUnit->runtimeFunctions[binding->value.compiledScriptIndex]; @@ -1091,14 +741,14 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { int signalIndex = _propertyCache->methodIndexToSignalIndex(property->coreIndex); - QQmlBoundSignal *bs = new QQmlBoundSignal(_qobject, signalIndex, _qobject, engine); - QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_qobject, signalIndex, - context, _qobject, function); + QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); + QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, + context, _scopeObject, function); bs->takeExpression(expr); } else { - QQmlBinding *qmlBinding = new QQmlBinding(function, _qobject, context, - QString(), 0, 0); // ### + QQmlBinding *qmlBinding = new QQmlBinding(function, _scopeObject, context, + context->urlString, binding->location.line, binding->location.column); // When writing bindings to grouped properties implemented as value types, // such as point.x: { someExpression; }, then the binding is installed on @@ -1109,7 +759,7 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI if (_valueTypeProperty) targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine); - qmlBinding->setTarget(_qobjectForBindings, targetCorePropertyData, context); + qmlBinding->setTarget(_bindingTarget, targetCorePropertyData, context); qmlBinding->addToObject(); _createdBindings[bindingIndex] = qmlBinding; @@ -1119,6 +769,43 @@ 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(); + } + Q_ASSERT(type); + + QQmlPropertyData targetCorePropertyData = *property; + if (_valueTypeProperty) + targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine); + + int valueSourceCast = type->propertyValueSourceCast(); + if (valueSourceCast != -1) { + QQmlPropertyValueSource *vs = reinterpret_cast<QQmlPropertyValueSource *>(reinterpret_cast<char *>(createdSubObject) + valueSourceCast); + QObject *target = createdSubObject->parent(); + vs->setTarget(QQmlPropertyPrivate::restore(target, targetCorePropertyData, context)); + return true; + } + int valueInterceptorCast = type->propertyValueInterceptorCast(); + if (valueInterceptorCast != -1) { + QQmlPropertyValueInterceptor *vi = reinterpret_cast<QQmlPropertyValueInterceptor *>(reinterpret_cast<char *>(createdSubObject) + valueInterceptorCast); + QObject *target = createdSubObject->parent(); + + QQmlProperty prop = + QQmlPropertyPrivate::restore(target, targetCorePropertyData, context); + vi->setTarget(prop); + QQmlVMEMetaObject *mo = QQmlVMEMetaObject::get(target); + Q_ASSERT(mo); + mo->registerInterceptor(prop.index(), QQmlPropertyPrivate::valueTypeCoreIndex(prop), vi); + return true; + } + return false; + } + QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::RemoveBindingOnAliasWrite; int propertyWriteStatus = -1; @@ -1203,7 +890,6 @@ void QmlObjectCreator::setupFunctions() { QV4::Scope scope(_qmlContext); QV4::ScopedValue function(scope); - QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(_qobject); const quint32 *functionIdx = _compiledObject->functionOffsetTable(); for (quint32 i = 0; i < _compiledObject->nFunctions; ++i, ++functionIdx) { @@ -1215,16 +901,27 @@ void QmlObjectCreator::setupFunctions() continue; function = QV4::FunctionObject::creatScriptFunction(_qmlContext, runtimeFunction); - vme->setVmeMethod(property->coreIndex, function); + _vmeMetaObject->setVmeMethod(property->coreIndex, function); } } +void QmlObjectCreator::recordError(const QV4::CompiledData::Location &location, const QString &description) +{ + QQmlError error; + error.setUrl(url); + error.setLine(location.line); + error.setColumn(location.column); + error.setDescription(description); + errors << error; +} + QObject *QmlObjectCreator::createInstance(int index, QObject *parent) { ActiveOCRestorer ocRestorer(this, QQmlEnginePrivate::get(engine)); bool isComponent = false; QObject *instance = 0; + QQmlCustomParser *customParser = 0; if (compiledData->isComponent(index)) { isComponent = true; @@ -1242,6 +939,16 @@ QObject *QmlObjectCreator::createInstance(int index, QObject *parent) recordError(obj->location, tr("Unable to create object of type %1").arg(stringAt(obj->inheritedTypeNameIndex))); return 0; } + + 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]; + } + + customParser = type->customParser(); } else { Q_ASSERT(typeRef.component); if (typeRef.component->qmlUnit->isSingleton()) @@ -1258,6 +965,7 @@ QObject *QmlObjectCreator::createInstance(int index, QObject *parent) if (subCreator.componentAttached) subCreator.componentAttached->add(&componentAttached); allCreatedBindings << subCreator.allCreatedBindings; + allParserStatusCallbacks << subCreator.allParserStatusCallbacks; } // ### use no-event variant if (parent) @@ -1285,18 +993,37 @@ QObject *QmlObjectCreator::createInstance(int index, QObject *parent) if (idEntry != objectIndexToId.constEnd()) context->setIdProperty(idEntry.value(), instance); - if (!isComponent) { - QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.value(index); - Q_ASSERT(!cache.isNull()); - - if (!populateInstance(index, instance, cache, instance, /*value type property*/0)) - return 0; + if (customParser) { + QByteArray data = compiledData->customParserData.value(index); + customParser->setCustomData(instance, data); } - return instance; + if (isComponent) + return instance; + + QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.value(index); + Q_ASSERT(!cache.isNull()); + + QObject *scopeObject = instance; + qSwap(_scopeObject, scopeObject); + + 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::ExecutionContext *qmlContext = qmlBindingWrapper->context(); + + qSwap(_qmlContext, qmlContext); + + bool result = populateInstance(index, instance, cache, /*binding target*/instance, /*value type property*/0); + + qSwap(_qmlContext, qmlContext); + qSwap(_scopeObject, scopeObject); + + return result ? instance : 0; } -void QmlObjectCreator::finalize() +QQmlContextData *QmlObjectCreator::finalize() { { QQmlTrace trace("VME Binding Enable"); @@ -1321,6 +1048,45 @@ void QmlObjectCreator::finalize() } } + 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(); + } + + #if 0 // ### + if (watcher.hasRecursed() || interrupt.shouldInterrupt()) + return 0; + #endif + } + } + allParserStatusCallbacks.clear(); + } + + { + QQmlTrace trace("VME Finalize Callbacks"); + for (int ii = 0; ii < finalizeCallbacks.count(); ++ii) { + QQmlEnginePrivate::FinalizeCallback callback = finalizeCallbacks.at(ii); + QObject *obj = callback.first; + if (obj) { + 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) { @@ -1339,35 +1105,34 @@ void QmlObjectCreator::finalize() #endif } } + + return rootContext; } -bool QmlObjectCreator::populateInstance(int index, QObject *instance, QQmlRefPointer<QQmlPropertyCache> cache, - QObject *scopeObjectForBindings, QQmlPropertyData *valueTypeProperty) +bool QmlObjectCreator::populateInstance(int index, QObject *instance, QQmlRefPointer<QQmlPropertyCache> cache, QObject *bindingTarget, QQmlPropertyData *valueTypeProperty) { const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); - Q_ASSERT(scopeObjectForBindings); - QQmlData *declarativeData = QQmlData::get(instance, /*create*/true); qSwap(_propertyCache, cache); qSwap(_qobject, instance); - qSwap(_qobjectForBindings, scopeObjectForBindings); qSwap(_valueTypeProperty, valueTypeProperty); qSwap(_compiledObject, obj); qSwap(_ddata, declarativeData); + qSwap(_bindingTarget, bindingTarget); QQmlVMEMetaObject *vmeMetaObject = 0; const QByteArray data = vmeMetaObjectData.value(index); if (!data.isEmpty()) { // install on _object - vmeMetaObject = new QQmlVMEMetaObject(_qobjectForBindings, _propertyCache, reinterpret_cast<const QQmlVMEMetaData*>(data.constData())); + 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(_qobjectForBindings); + vmeMetaObject = QQmlVMEMetaObject::get(_qobject); } _ddata->lineNumber = _compiledObject->location.line; @@ -1378,387 +1143,21 @@ bool QmlObjectCreator::populateInstance(int index, QObject *instance, QQmlRefPoi QVector<QQmlAbstractBinding*> createdBindings(_compiledObject->nBindings, 0); qSwap(_createdBindings, createdBindings); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - QV4::Scope valueScope(v4); - QV4::ScopedObject scopeObject(valueScope, QV4::QmlContextWrapper::qmlScope(QV8Engine::get(engine), context, _qobjectForBindings)); - QV4::Scoped<QV4::QmlBindingWrapper> qmlBindingWrapper(valueScope, new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, scopeObject)); - QV4::ExecutionContext *qmlContext = qmlBindingWrapper->context(); - - qSwap(_qmlContext, qmlContext); - setupBindings(); setupFunctions(); allCreatedBindings.append(_createdBindings); - qSwap(_qmlContext, qmlContext); - qSwap(_createdBindings, createdBindings); qSwap(_vmeMetaObject, vmeMetaObject); - qSwap(_propertyCache, cache); + qSwap(_bindingTarget, bindingTarget); qSwap(_ddata, declarativeData); qSwap(_compiledObject, obj); qSwap(_valueTypeProperty, valueTypeProperty); - qSwap(_qobjectForBindings, scopeObjectForBindings); qSwap(_qobject, instance); + qSwap(_propertyCache, cache); return errors.isEmpty(); } -QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit, - const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes, - const QList<QQmlPropertyCache *> &propertyCaches, QList<QByteArray> *vmeMetaObjectData, - QHash<int, int> *objectIndexToIdForRoot, - QHash<int, QHash<int, int> > *objectIndexToIdPerComponent) - : QQmlCompilePass(url, qmlUnit) - , _componentIndex(-1) - , _objectIndexToIdInScope(0) - , resolvedTypes(resolvedTypes) - , propertyCaches(propertyCaches) - , vmeMetaObjectData(vmeMetaObjectData) - , objectIndexToIdForRoot(objectIndexToIdForRoot) - , objectIndexToIdPerComponent(objectIndexToIdPerComponent) -{ -} - -bool QQmlComponentAndAliasResolver::resolve() -{ - Q_ASSERT(componentRoots.isEmpty()); - - // Find objects that are Components. This is missing an extra pass - // that finds implicitly defined components, i.e. - // someProperty: Item { ... } - // when someProperty _is_ a QQmlComponent. In that case the Item {} - // should be implicitly surrounded by Component {} - - for (quint32 i = 0; i < qmlUnit->nObjects; ++i) { - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i); - if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) - continue; - - QQmlCompiledData::TypeReference tref = resolvedTypes.value(obj->inheritedTypeNameIndex); - if (!tref.type) - continue; - if (tref.type->metaObject() != &QQmlComponent::staticMetaObject) - continue; - - componentRoots.append(i); - // Sanity checks: There can be only an (optional) id property and - // a default property, that defines the component tree. - } - - std::sort(componentRoots.begin(), componentRoots.end()); - - // For each component's tree, remember to which component the children - // belong to - for (int i = 0; i < componentRoots.count(); ++i) { - const QV4::CompiledData::Object *component = qmlUnit->objectAt(componentRoots.at(i)); - - if (component->nFunctions > 0) - COMPILE_EXCEPTION(component, tr("Component objects cannot declare new functions.")); - if (component->nProperties > 0) - COMPILE_EXCEPTION(component, tr("Component objects cannot declare new properties.")); - if (component->nSignals > 0) - COMPILE_EXCEPTION(component, tr("Component objects cannot declare new signals.")); - - if (component->nBindings == 0) - COMPILE_EXCEPTION(component, tr("Cannot create empty component specification")); - - const QV4::CompiledData::Binding *rootBinding = component->bindingTable(); - if (component->nBindings > 1 || rootBinding->type != QV4::CompiledData::Binding::Type_Object) - COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id")); - - _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(qmlUnit->indexOfRootObject); - - resolveAliases(); - - return errors.isEmpty(); -} - -bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) -{ - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex); - - // Only include creatable types. Everything else is synthetic, such as group property - // objects. - if (_componentIndex != -1 && !stringAt(obj->inheritedTypeNameIndex).isEmpty()) - objectIndexToComponentIndex.insert(objectIndex, _componentIndex); - - 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()); - } - - const QV4::CompiledData::Property *property = obj->propertyTable(); - for (quint32 i = 0; i < obj->nProperties; ++i, ++property) - if (property->type == QV4::CompiledData::Property::Alias) { - _objectsWithAliases.append(objectIndex); - break; - } - - const QV4::CompiledData::Binding *binding = obj->bindingTable(); - for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { - 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(componentRoots.constBegin(), componentRoots.constEnd(), binding->value.objectIndex)) - continue; - - if (!collectIdsAndAliases(binding->value.objectIndex)) - return false; - } - - return true; -} - -bool QQmlComponentAndAliasResolver::resolveAliases() -{ - foreach (int objectIndex, _objectsWithAliases) { - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(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 QV4::CompiledData::Property *p = obj->propertyTable(); - for (quint32 propertyIndex = 0; propertyIndex < obj->nProperties; ++propertyIndex, ++p) { - 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 QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(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(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit, - const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes, - const QList<QQmlPropertyCache *> &propertyCaches, const QHash<int, QHash<int, int> > &objectIndexToIdPerComponent) - : QQmlCompilePass(url, qmlUnit) - , resolvedTypes(resolvedTypes) - , propertyCaches(propertyCaches) - , objectIndexToIdPerComponent(objectIndexToIdPerComponent) -{ -} - -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; -} - -bool QQmlPropertyValidator::validateObject(const QV4::CompiledData::Object *obj, int objectIndex, QQmlPropertyCache *propertyCache) -{ - 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) - continue; - - const QString name = stringAt(binding->propertyNameIndex); - - bool bindingToDefaultProperty = false; - - bool notInRevision = false; - QQmlPropertyData *pd = 0; - if (!name.isEmpty()) { - pd = propertyResolver.property(name, ¬InRevision); - - if (notInRevision) { - QString typeName = stringAt(obj->inheritedTypeNameIndex); - QQmlCompiledData::TypeReference type = resolvedTypes.value(objectIndex); - if (type.type) { - COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(type.type->module()).arg(type.majorVersion).arg(type.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 (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)); - } - } - } - - return true; -} diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index ec4b362491..a198396467 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -45,133 +45,60 @@ #include <private/qqmltypenamecache_p.h> #include <private/qv4compileddata_p.h> #include <private/qqmlcompiler_p.h> +#include <private/qqmltypecompiler_p.h> #include <QLinkedList> QT_BEGIN_NAMESPACE class QQmlAbstractBinding; +struct QQmlTypeCompiler; -struct QQmlCompilePass -{ - QQmlCompilePass(const QUrl &url, const QV4::CompiledData::QmlUnit *unit); - QList<QQmlError> errors; - -protected: - QString stringAt(int idx) const { return qmlUnit->header.stringAt(idx); } - void recordError(const QV4::CompiledData::Location &location, const QString &description); - - const QUrl url; - const QV4::CompiledData::QmlUnit *qmlUnit; -}; - -class QQmlPropertyCacheCreator : public QQmlCompilePass -{ - Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreator) -public: - QQmlPropertyCacheCreator(QQmlEnginePrivate *enginePrivate, const QV4::CompiledData::QmlUnit *qmlUnit, - const QUrl &url, const QQmlImports *imports, - QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes); - - bool create(const QV4::CompiledData::Object *obj, QQmlPropertyCache **cache, QByteArray *vmeMetaObjectData); - -protected: - QQmlEnginePrivate *enginePrivate; - const QQmlImports *imports; - QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes; -}; - -class QQmlComponentAndAliasResolver : public QQmlCompilePass -{ - Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver) -public: - QQmlComponentAndAliasResolver(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit, - const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes, - const QList<QQmlPropertyCache *> &propertyCaches, - QList<QByteArray> *vmeMetaObjectData, - QHash<int, int> *objectIndexToIdForRoot, - QHash<int, QHash<int, int> > *objectIndexToIdPerComponent); - - bool resolve(); - - QVector<int> componentRoots; - QHash<int, int> objectIndexToComponentIndex; - -protected: - bool collectIdsAndAliases(int objectIndex); - bool resolveAliases(); - - bool isComponentType(int typeNameIndex) const - { return resolvedTypes.value(typeNameIndex).type == 0; } - - int _componentIndex; - QHash<int, int> _idToObjectIndex; - QHash<int, int> *_objectIndexToIdInScope; - QList<int> _objectsWithAliases; - - const QHash<int, QQmlCompiledData::TypeReference> resolvedTypes; - const QList<QQmlPropertyCache *> propertyCaches; - QList<QByteArray> *vmeMetaObjectData; - QHash<int, int> *objectIndexToIdForRoot; - QHash<int, QHash<int, int> > *objectIndexToIdPerComponent; -}; - -class QQmlPropertyValidator : public QQmlCompilePass -{ - Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) -public: - QQmlPropertyValidator(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit, - const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes, - const QList<QQmlPropertyCache *> &propertyCaches, - const QHash<int, QHash<int, int> > &objectIndexToIdPerComponent); - - bool validate(); - -private: - bool validateObject(const QV4::CompiledData::Object *obj, int objectIndex, QQmlPropertyCache *propertyCache); - - bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } - - const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes; - const QList<QQmlPropertyCache *> &propertyCaches; - const QHash<int, QHash<int, int> > objectIndexToIdPerComponent; -}; - -class QmlObjectCreator : public QQmlCompilePass +class QmlObjectCreator { Q_DECLARE_TR_FUNCTIONS(QmlObjectCreator) public: QmlObjectCreator(QQmlContextData *contextData, QQmlCompiledData *compiledData); QObject *create(int subComponentIndex = -1, QObject *parent = 0); - void finalize(); + QQmlContextData *finalize(); QQmlComponentAttached *componentAttached; QList<QQmlEnginePrivate::FinalizeCallback> finalizeCallbacks; + QList<QQmlError> errors; + private: QObject *createInstance(int index, QObject *parent = 0); - bool populateInstance(int index, QObject *instance, QQmlRefPointer<QQmlPropertyCache> cache, - QObject *scopeObjectForJavaScript, QQmlPropertyData *valueTypeProperty); + 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); void setPropertyValue(QQmlPropertyData *property, const QV4::CompiledData::Binding *binding); void setupFunctions(); + QString stringAt(int idx) const { return qmlUnit->header.stringAt(idx); } + void recordError(const QV4::CompiledData::Location &location, const QString &description); + + QUrl url; QQmlEngine *engine; + const QV4::CompiledData::QmlUnit *qmlUnit; const QV4::CompiledData::CompilationUnit *jsUnit; QQmlContextData *parentContext; QQmlContextData *context; const QHash<int, QQmlCompiledData::TypeReference> resolvedTypes; - const QList<QQmlPropertyCache *> propertyCaches; - const QList<QByteArray> vmeMetaObjectData; + const QVector<QQmlPropertyCache *> propertyCaches; + const QVector<QByteArray> vmeMetaObjectData; QHash<int, int> objectIndexToId; QLinkedList<QVector<QQmlAbstractBinding*> > allCreatedBindings; + QLinkedList<QVector<QQmlParserStatus*> > allParserStatusCallbacks; QQmlCompiledData *compiledData; + QQmlContextData *rootContext; QObject *_qobject; - QObject *_qobjectForBindings; + QObject *_scopeObject; + QObject *_bindingTarget; + QQmlPropertyData *_valueTypeProperty; // belongs to _qobjectForBindings's property cache const QV4::CompiledData::Object *_compiledObject; QQmlData *_ddata; @@ -180,6 +107,7 @@ private: QVector<QQmlAbstractBinding*> _createdBindings; QQmlListProperty<void> _currentList; QV4::ExecutionContext *_qmlContext; + QVector<QQmlParserStatus*> _parserStatusCallbacks; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlparserstatus.h b/src/qml/qml/qqmlparserstatus.h index d3447e7752..1d63afd978 100644 --- a/src/qml/qml/qqmlparserstatus.h +++ b/src/qml/qml/qqmlparserstatus.h @@ -62,6 +62,7 @@ private: friend class QQmlComponent; friend class QQmlComponentPrivate; friend class QQmlEnginePrivate; + friend class QmlObjectCreator; QQmlParserStatus **d; }; diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 6c40557886..d026b95201 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -1140,7 +1140,7 @@ QString QQmlPropertyCache::signalParameterStringForJS(QQmlEngine *engine, const { QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); bool unnamedParameter = false; - const QV4::IdentifierHash<bool> &illegalNames = ep->v8engine()->illegalNames(); + const QSet<QString> &illegalNames = ep->v8engine()->illegalNames(); QString error; QString parameters; diff --git a/src/qml/qml/qqmlscriptstring.h b/src/qml/qml/qqmlscriptstring.h index 5421ef95fc..b192be8b77 100644 --- a/src/qml/qml/qqmlscriptstring.h +++ b/src/qml/qml/qqmlscriptstring.h @@ -53,6 +53,7 @@ QT_BEGIN_NAMESPACE class QObject; class QQmlContext; class QQmlScriptStringPrivate; +class QmlObjectCreator; class Q_QML_EXPORT QQmlScriptString { public: @@ -74,6 +75,7 @@ private: QQmlScriptString(const QString &script, QQmlContext *context, QObject *scope); QSharedDataPointer<QQmlScriptStringPrivate> d; + friend class QmlObjectCreator; friend class QQmlScriptStringPrivate; friend class QQmlVME; friend class QQmlExpression; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 6eda55e35b..278bb017d0 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -52,6 +52,7 @@ #include <private/qqmlprofilerservice_p.h> #include <private/qqmlmemoryprofiler_p.h> #include <private/qqmlcodegenerator_p.h> +#include <private/qqmltypecompiler_p.h> #include <QtCore/qdir.h> #include <QtCore/qfile.h> @@ -2154,8 +2155,9 @@ void QQmlTypeData::dataReceived(const Data &data) if (data.isFile()) preparseData = data.asFile()->metaData(QLatin1String("qml:preparse")); if (m_useNewCompiler) { - parsedQML.reset(new QtQml::ParsedQML(QV8Engine::getV4(typeLoader()->engine())->debugger != 0)); - QQmlCodeGenerator compiler; + QQmlEngine *qmlEngine = typeLoader()->engine(); + parsedQML.reset(new QtQml::ParsedQML(QV8Engine::getV4(qmlEngine)->debugger != 0)); + QQmlCodeGenerator compiler(QV8Engine::get(qmlEngine)->illegalNames()); if (!compiler.generateFromQml(code, finalUrl(), finalUrlString(), parsedQML.data())) { setError(compiler.errors); return; @@ -2305,173 +2307,9 @@ void QQmlTypeData::compile() QQmlCompilingProfiler prof(m_compiledData->name); if (m_useNewCompiler) { - m_compiledData->importCache = new QQmlTypeNameCache; - - foreach (const QString &ns, m_namespaces) - m_compiledData->importCache->add(ns); - - // Add any Composite Singletons that were used to the import cache - for (int i = 0; i < compositeSingletons().count(); ++i) { - m_compiledData->importCache->add(compositeSingletons().at(i).type->qmlTypeName(), - compositeSingletons().at(i).type->sourceUrl(), compositeSingletons().at(i).prefix); - } - - m_imports.populateCache(m_compiledData->importCache); - m_compiledData->importCache->addref(); - - QQmlEngine *engine = typeLoader()->engine(); - QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); - - for (QHash<int, TypeReference>::ConstIterator resolvedType = m_resolvedTypes.constBegin(), end = m_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; - m_compiledData->resolvedTypes.insert(resolvedType.key(), ref); - } - - { - SignalHandlerConverter converter(QQmlEnginePrivate::get(engine), - parsedQML.data(), - m_compiledData); - if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations()) { - setError(converter.errors); - m_compiledData->release(); - m_compiledData = 0; - return; - } - } - - // Collect imported scripts - m_compiledData->scripts.reserve(m_scripts.count()); - for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { - const ScriptReference &script = m_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); - } - - m_compiledData->importCache->add(qualifier, scriptIndex, enclosingNamespace); - QQmlScriptData *scriptData = script.script->scriptData(); - scriptData->addref(); - m_compiledData->scripts << scriptData; - } - - // Compile JS binding expressions and signal handlers - - JSCodeGen jsCodeGen(finalUrlString(), parsedQML->code, &parsedQML->jsModule, &parsedQML->jsParserEngine, parsedQML->program, m_compiledData->importCache); - const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(parsedQML->functions); - - QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); - - QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(enginePrivate, 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.data(), runtimeFunctionIndices); - - if (jsUnit) { - Q_ASSERT(!jsUnit->data); - jsUnit->ownsData = false; - jsUnit->data = &qmlUnit->header; - } - - m_compiledData->compilationUnit = jsUnit; - if (m_compiledData->compilationUnit) - m_compiledData->compilationUnit->ref(); - m_compiledData->qmlUnit = qmlUnit; // ownership transferred to m_compiledData - - QList<QQmlError> errors; - - // Build property caches and VME meta object data - - m_compiledData->datas.reserve(qmlUnit->nObjects); - m_compiledData->propertyCaches.reserve(qmlUnit->nObjects); - - QQmlPropertyCacheCreator propertyCacheBuilder(enginePrivate, - qmlUnit, m_compiledData->url, - &m_imports, &m_compiledData->resolvedTypes); - - for (quint32 i = 0; i < qmlUnit->nObjects; ++i) { - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i); - - QByteArray vmeMetaObjectData; - QQmlPropertyCache *propertyCache = 0; - - // If the object has no type, then it's probably a nested object definition as part - // of a group property. - const bool objectHasType = !parsedQML->jsGenerator.strings.at(obj->inheritedTypeNameIndex).isEmpty(); - if (objectHasType) { - if (!propertyCacheBuilder.create(obj, &propertyCache, &vmeMetaObjectData)) { - errors << propertyCacheBuilder.errors; - break; - } - } - - m_compiledData->datas << vmeMetaObjectData; - if (propertyCache) - propertyCache->addref(); - m_compiledData->propertyCaches << propertyCache; - - if (i == qmlUnit->indexOfRootObject) { - Q_ASSERT(propertyCache); - m_compiledData->rootPropertyCache = propertyCache; - propertyCache->addref(); - } - } - - // Resolve component boundaries and aliases - - if (errors.isEmpty()) { - // Scan for components, determine their scopes and resolve aliases within the scope. - QQmlComponentAndAliasResolver resolver(m_compiledData->url, m_compiledData->qmlUnit, m_compiledData->resolvedTypes, m_compiledData->propertyCaches, - &m_compiledData->datas, &m_compiledData->objectIndexToIdForRoot, &m_compiledData->objectIndexToIdPerComponent); - if (!resolver.resolve()) - errors << resolver.errors; - } - - if (errors.isEmpty()) { - // Add to type registry of composites - if (m_compiledData->isCompositeType()) - QQmlEnginePrivate::get(engine)->registerInternalCompositeType(m_compiledData); - else { - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(qmlUnit->indexOfRootObject); - QQmlCompiledData::TypeReference typeRef = m_compiledData->resolvedTypes.value(obj->inheritedTypeNameIndex); - if (typeRef.component) { - m_compiledData->metaTypeId = typeRef.component->metaTypeId; - m_compiledData->listMetaTypeId = typeRef.component->listMetaTypeId; - } else { - m_compiledData->metaTypeId = typeRef.type->typeId(); - m_compiledData->listMetaTypeId = typeRef.type->qListTypeId(); - } - } - } - - // Sanity check property bindings - if (errors.isEmpty()) { - QQmlPropertyValidator validator(m_compiledData->url, m_compiledData->qmlUnit, m_compiledData->resolvedTypes, - m_compiledData->propertyCaches, m_compiledData->objectIndexToIdPerComponent); - if (!validator.validate()) - errors << validator.errors; - } - - if (!errors.isEmpty()) { - setError(errors); + QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), m_compiledData, this, parsedQML.data()); + if (!compiler.compile()) { + setError(compiler.compilationErrors()); m_compiledData->release(); m_compiledData = 0; } diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index b93cf2942d..a022162a7c 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -425,7 +425,12 @@ public: const QQmlScript::Parser &parser() const; + // old compiler: const QList<TypeReference> &resolvedTypes() const; + // new compiler: + const QHash<int, TypeReference> &resolvedTypeRefs() const { return m_resolvedTypes; } + // --- + const QList<ScriptReference> &resolvedScripts() const; const QSet<QString> &namespaces() const; const QList<TypeReference> &compositeSingletons() const; diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 33f5a00a6c..aa3b7eeb34 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -164,7 +164,7 @@ QVariant QV8Engine::toVariant(const QV4::ValueRef value, int typeHint) QV4::ScopedArrayObject a(scope, value); if (typeHint == qMetaTypeId<QList<QObject *> >()) { QList<QObject *> list; - uint32_t length = a->arrayLength(); + uint32_t length = a->getLength(); QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope); for (uint32_t ii = 0; ii < length; ++ii) { qobjectWrapper = a->getIndexed(ii); @@ -196,9 +196,10 @@ static QV4::ReturnedValue arrayFromStringList(QV8Engine *engine, const QStringLi QV4::Scoped<QV4::ArrayObject> a(scope, e->newArrayObject()); int len = list.count(); a->arrayReserve(len); + QV4::ScopedValue v(scope); for (int ii = 0; ii < len; ++ii) { - a->arrayData[ii].value = QV4::Encode(e->newString(list.at(ii))); - a->arrayDataLen = ii + 1; + a->arrayData->put(ii, (v = QV4::Encode(e->newString(list.at(ii))))); + a->arrayData->setLength(ii + 1); } a->setArrayLengthUnchecked(len); return a.asReturnedValue(); @@ -211,9 +212,10 @@ static QV4::ReturnedValue arrayFromVariantList(QV8Engine *engine, const QVariant QV4::Scoped<QV4::ArrayObject> a(scope, e->newArrayObject()); int len = list.count(); a->arrayReserve(len); + QV4::ScopedValue v(scope); for (int ii = 0; ii < len; ++ii) { - a->arrayData[ii].value = engine->fromVariant(list.at(ii)); - a->arrayDataLen = ii + 1; + a->arrayData->put(ii, (v = engine->fromVariant(list.at(ii)))); + a->arrayData->setLength(ii + 1); } a->setArrayLengthUnchecked(len); return a.asReturnedValue(); @@ -298,7 +300,8 @@ QV4::ReturnedValue QV8Engine::fromVariant(const QVariant &variant) return QV4::JsonObject::fromJsonObject(m_v4Engine, *reinterpret_cast<const QJsonObject *>(ptr)); case QMetaType::QJsonArray: return QV4::JsonObject::fromJsonArray(m_v4Engine, *reinterpret_cast<const QJsonArray *>(ptr)); - + case QMetaType::QLocale: + return QQmlLocale::wrap(this, *reinterpret_cast<const QLocale*>(ptr)); default: break; } @@ -325,9 +328,10 @@ QV4::ReturnedValue QV8Engine::fromVariant(const QVariant &variant) const QList<QObject *> &list = *(QList<QObject *>*)ptr; QV4::Scoped<QV4::ArrayObject> a(scope, m_v4Engine->newArrayObject()); a->arrayReserve(list.count()); + QV4::ScopedValue v(scope); for (int ii = 0; ii < list.count(); ++ii) { - a->arrayData[ii].value = QV4::QObjectWrapper::wrap(m_v4Engine, list.at(ii)); - a->arrayDataLen = ii + 1; + a->arrayData->put(ii, (v = QV4::QObjectWrapper::wrap(m_v4Engine, list.at(ii)))); + a->arrayData->setLength(ii + 1); } a->setArrayLengthUnchecked(list.count()); return a.asReturnedValue(); @@ -361,7 +365,7 @@ QNetworkAccessManager *QV8Engine::networkAccessManager() return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager(); } -const QV4::IdentifierHash<bool> &QV8Engine::illegalNames() const +const QSet<QString> &QV8Engine::illegalNames() const { return m_illegalNames; } @@ -392,6 +396,8 @@ QVariant QV8Engine::toBasicVariant(const QV4::ValueRef value) return value->asDouble(); if (value->isString()) return value->stringValue()->toQString(); + if (QQmlLocaleData *ld = value->as<QQmlLocaleData>()) + return ld->locale; if (QV4::DateObject *d = value->asDateObject()) return d->toQDateTime(); // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)! @@ -407,7 +413,7 @@ QVariant QV8Engine::toBasicVariant(const QV4::ValueRef value) QV4::ScopedValue v(scope); QVariantList rv; - int length = a->arrayLength(); + int length = a->getLength(); for (int ii = 0; ii < length; ++ii) { v = a->getIndexed(ii); rv << toVariant(v, -1); @@ -437,9 +443,8 @@ void QV8Engine::initializeGlobal() qt_add_sqlexceptions(m_v4Engine); { - m_illegalNames = QV4::IdentifierHash<bool>(m_v4Engine); for (uint i = 0; i < m_v4Engine->globalObject->internalClass->size; ++i) - m_illegalNames.add(m_v4Engine->globalObject->internalClass->nameMap.at(i)->toQString(), true); + m_illegalNames.insert(m_v4Engine->globalObject->internalClass->nameMap.at(i)->toQString()); } { @@ -541,9 +546,10 @@ QV4::ReturnedValue QV8Engine::variantListToJS(const QVariantList &lst) QV4::Scope scope(m_v4Engine); QV4::Scoped<QV4::ArrayObject> a(scope, m_v4Engine->newArrayObject()); a->arrayReserve(lst.size()); + QV4::ScopedValue v(scope); for (int i = 0; i < lst.size(); i++) { - a->arrayData[i].value = variantToJS(lst.at(i)); - a->arrayDataLen = i + 1; + a->arrayData->put(i, (v = variantToJS(lst.at(i)))); + a->arrayData->setLength(i + 1); } a->setArrayLengthUnchecked(lst.size()); return a.asReturnedValue(); @@ -569,7 +575,7 @@ QVariantList QV8Engine::variantListFromJS(QV4::ArrayObjectRef a, QV4::Scope scope(a->engine()); QV4::ScopedValue v(scope); - quint32 length = a->arrayLength(); + quint32 length = a->getLength(); for (quint32 i = 0; i < length; ++i) { v = a->getIndexed(i); result.append(variantFromJS(v, visitedObjects)); @@ -590,10 +596,11 @@ QV4::ReturnedValue QV8Engine::variantMapToJS(const QVariantMap &vmap) QV4::Scoped<QV4::Object> o(scope, m_v4Engine->newObject()); QVariantMap::const_iterator it; QV4::ScopedString s(scope); + QV4::ScopedValue v(scope); for (it = vmap.constBegin(); it != vmap.constEnd(); ++it) { s = m_v4Engine->newIdentifier(it.key()); - QV4::Property *p = o->insertMember(s, QV4::Attr_Data); - p->value = variantToJS(it.value()); + v = variantToJS(it.value()); + o->insertMember(s, v); } return o.asReturnedValue(); } diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index e37b0d4920..5d1005b479 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -217,7 +217,7 @@ public: virtual QNetworkAccessManager *networkAccessManager(); // Return the list of illegal id names (the names of the properties on the global object) - const QV4::IdentifierHash<bool> &illegalNames() const; + const QSet<QString> &illegalNames() const; void gc(); @@ -268,7 +268,7 @@ protected: QVector<Deletable *> m_extensionData; Deletable *m_listModelData; - QV4::IdentifierHash<bool> m_illegalNames; + QSet<QString> m_illegalNames; QElapsedTimer m_time; QHash<QString, qint64> m_startedTimers; diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index 5e6a1a084a..b8920deb21 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -251,6 +251,44 @@ QQmlConnectionsParser::compile(const QList<QQmlCustomParserProperty> &props) return rv; } +QByteArray QQmlConnectionsParser::compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props) +{ + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + for (int ii = 0; ii < props.count(); ++ii) { + const QV4::CompiledData::Binding *binding = props.at(ii); + QString propName = qmlUnit->header.stringAt(binding->propertyNameIndex); + int propLine = binding->location.line; + int propColumn = binding->location.column; + + if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) { + error(props.at(ii), QQmlConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); + return QByteArray(); + } + + + if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + const QV4::CompiledData::Object *target = qmlUnit->objectAt(binding->value.objectIndex); + if (!qmlUnit->header.stringAt(target->inheritedTypeNameIndex).isEmpty()) + error(binding, QQmlConnections::tr("Connections: nested objects not allowed")); + else + error(binding, QQmlConnections::tr("Connections: syntax error")); + return QByteArray(); + } if (binding->type != QV4::CompiledData::Binding::Type_Script) { + error(binding, QQmlConnections::tr("Connections: script expected")); + return QByteArray(); + } else { + ds << propName; + ds << binding->valueAsString(&qmlUnit->header); + ds << propLine; + ds << propColumn; + } + } + + return rv; +} + void QQmlConnectionsParser::setCustomData(QObject *object, const QByteArray &data) { diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h index 9bc668e5f4..2579e4c239 100644 --- a/src/qml/types/qqmlconnections_p.h +++ b/src/qml/types/qqmlconnections_p.h @@ -85,6 +85,7 @@ class QQmlConnectionsParser : public QQmlCustomParser { public: virtual QByteArray compile(const QList<QQmlCustomParserProperty> &); + virtual QByteArray compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props); virtual void setCustomData(QObject *, const QByteArray &); }; diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 7276c0e5c6..4c8635b29c 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -1629,37 +1629,46 @@ 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; + s = v4->newString(QStringLiteral("isUnresolved")); - QV4::Property *p = proto->insertMember(s, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - p->setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, 30, QQmlDelegateModelItem::get_member)); + 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 = proto->insertMember(s, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - 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 = proto->insertMember(s, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - 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 = proto->insertMember(s, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - 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 = proto->insertMember(s, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - p->setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_index)); + 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 = proto->insertMember(s, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - 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 = proto->insertMember(s, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - p->setGetter(new (v4->memoryManager) DelegateModelGroupFunction(v4->rootContext, i + 1, QQmlDelegateModelItem::get_index)); + 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; } @@ -1692,7 +1701,7 @@ int QQmlDelegateModelItemMetaType::parseGroups(const QV4::ValueRef groups) const QV4::ScopedArrayObject array(scope, groups); if (array) { QV4::ScopedValue v(scope); - uint arrayLength = array->arrayLength(); + uint arrayLength = array->getLength(); for (uint i = 0; i < arrayLength; ++i) { v = array->getIndexed(i); const QString groupName = v->toQString(); @@ -1863,11 +1872,16 @@ void QQmlDelegateModelItem::incubateObject( incubatorPriv->compiledData = componentPriv->cc; incubatorPriv->compiledData->addref(); - incubatorPriv->vme.init( - context, - componentPriv->cc, - componentPriv->start, - componentPriv->creationContext); + if (enginePriv->useNewCompiler) { + incubatorPriv->creator.reset(new QmlObjectCreator(context, componentPriv->cc)); + incubatorPriv->subComponentToCreate = componentPriv->start; + } else { + incubatorPriv->vme.init( + context, + componentPriv->cc, + componentPriv->start, + componentPriv->creationContext); + } enginePriv->incubate(*incubationTask, forContext); } @@ -3184,7 +3198,7 @@ public: : Object(engine) { setVTable(&static_vtbl); - flags &= ~SimpleArray; + setArrayType(QV4::ArrayData::Custom); } virtual ~QQmlDelegateModelGroupChangeArray() {} diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 7e441023c9..eeb4aa0861 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -441,7 +441,7 @@ void ListModel::set(int elementIndex, QV4::ObjectRef object, QVector<int> *roles const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); ListModel *subModel = new ListModel(r.subLayout, 0, -1); - int arrayLength = a->arrayLength(); + int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { o = a->getIndexed(j); subModel->append(o, eng); @@ -517,7 +517,7 @@ void ListModel::set(int elementIndex, QV4::ObjectRef object, QV8Engine *eng) if (r.type == ListLayout::Role::List) { ListModel *subModel = new ListModel(r.subLayout, 0, -1); - int arrayLength = a->arrayLength(); + int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { o = a->getIndexed(j); subModel->append(o, eng); @@ -1188,7 +1188,7 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::ValueRef QV4::Scoped<QV4::Object> o(scope); ListModel *subModel = new ListModel(role.subLayout, 0, -1); - int arrayLength = a->arrayLength(); + int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { o = a->getIndexed(j); subModel->append(o, eng); @@ -1953,7 +1953,7 @@ void QQmlListModel::insert(QQmlV4Function *args) if (objectArray) { QV4::ScopedObject argObject(scope); - int objectArrayLength = objectArray->arrayLength(); + int objectArrayLength = objectArray->getLength(); for (int i=0 ; i < objectArrayLength ; ++i) { argObject = objectArray->getIndexed(i); @@ -2055,7 +2055,7 @@ void QQmlListModel::append(QQmlV4Function *args) if (objectArray) { QV4::Scoped<QV4::Object> argObject(scope); - int objectArrayLength = objectArray->arrayLength(); + int objectArrayLength = objectArray->getLength(); int index = count(); for (int i=0 ; i < objectArrayLength ; ++i) { diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h index 722cb2b44d..a7487537c5 100644 --- a/src/qml/types/qqmllistmodel_p.h +++ b/src/qml/types/qqmllistmodel_p.h @@ -188,8 +188,12 @@ private: ListModel *model; int elementIndex; }; + + friend class QTypeInfo<QQmlListModelParser::ListInstruction>; }; +Q_DECLARE_TYPEINFO(QQmlListModelParser::ListInstruction, Q_PRIMITIVE_TYPE); + QT_END_NAMESPACE QML_DECLARE_TYPE(QQmlListModel) diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index 38fdffdde6..36c3623d8b 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -226,9 +226,10 @@ public: const QByteArray &propertyName = it.key(); QV4::ScopedString name(scope, v4->newString(QString::fromUtf8(propertyName))); - QV4::Property *p = proto->insertMember(name, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable); - 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)); + 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)); + proto->insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable); } prototype = proto; } |