diff options
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r-- | src/qml/jsruntime/qv4arrayobject.cpp | 72 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4dataview.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4dateobject.cpp | 6 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 50 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4executablecompilationunit.cpp | 149 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4executablecompilationunit_p.h | 104 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4persistent.cpp | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 11 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 127 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtimeapi_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtimecodegen.cpp | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtimecodegen_p.h | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4script.cpp | 9 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4typedarray.cpp | 209 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4typedarray_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4value.cpp | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4value_p.h | 25 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4variantobject.cpp | 20 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 2 |
19 files changed, 616 insertions, 186 deletions
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index e6f6fd1b7a..df9f117d04 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -597,65 +597,63 @@ ReturnedValue ArrayPrototype::method_findIndex(const FunctionObject *b, const Va return Encode(-1); } -ReturnedValue ArrayPrototype::method_join(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +ReturnedValue ArrayPrototype::method_join(const FunctionObject *functionObject, + const Value *thisObject, const Value *argv, int argc) { - Scope scope(b); + Scope scope(functionObject); ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) return Encode(scope.engine->newString()); - ScopedValue arg(scope, argc ? argv[0] : Value::undefinedValue()); - - QString r4; - if (arg->isUndefined()) - r4 = QStringLiteral(","); - else - r4 = arg->toQString(); + // We cannot optimize the resolution of the argument away in case of length == 0 + // It may have side effects. + ScopedValue argument(scope, argc ? argv[0] : Value::undefinedValue()); + const QString separator = argument->isUndefined() + ? QStringLiteral(",") + : argument->toQString(); - ScopedValue length(scope, instance->get(scope.engine->id_length())); - const quint32 r2 = length->isUndefined() ? 0 : length->toUInt32(); - - if (!r2) + ScopedValue scopedLength(scope, instance->get(scope.engine->id_length())); + const quint32 genericLength = scopedLength->isUndefined() ? 0 : scopedLength->toUInt32(); + if (!genericLength) return Encode(scope.engine->newString()); - QString R; - - // ### FIXME - if (ArrayObject *a = instance->as<ArrayObject>()) { - ScopedValue e(scope); - for (uint i = 0; i < a->getLength(); ++i) { + QString result; + if (auto *arrayObject = instance->as<ArrayObject>()) { + ScopedValue entry(scope); + const qint64 arrayLength = arrayObject->getLength(); + Q_ASSERT(arrayLength >= 0); + Q_ASSERT(arrayLength <= std::numeric_limits<quint32>::max()); + for (quint32 i = 0; i < quint32(arrayLength); ++i) { if (i) - R += r4; + result += separator; - e = a->get(i); + entry = arrayObject->get(i); CHECK_EXCEPTION(); - if (!e->isNullOrUndefined()) - R += e->toQString(); + if (!entry->isNullOrUndefined()) + result += entry->toQString(); } } else { - // - // crazy! - // ScopedString name(scope, scope.engine->newString(QStringLiteral("0"))); - ScopedValue r6(scope, instance->get(name)); - if (!r6->isNullOrUndefined()) - R = r6->toQString(); + ScopedValue value(scope, instance->get(name)); + CHECK_EXCEPTION(); + + if (!value->isNullOrUndefined()) + result = value->toQString(); - ScopedValue r12(scope); - for (quint32 k = 1; k < r2; ++k) { - R += r4; + for (quint32 i = 1; i < genericLength; ++i) { + result += separator; - name = Value::fromDouble(k).toString(scope.engine); - r12 = instance->get(name); + name = Value::fromDouble(i).toString(scope.engine); + value = instance->get(name); CHECK_EXCEPTION(); - if (!r12->isNullOrUndefined()) - R += r12->toQString(); + if (!value->isNullOrUndefined()) + result += value->toQString(); } } - return Encode(scope.engine->newString(R)); + return Encode(scope.engine->newString(result)); } ReturnedValue ArrayPrototype::method_pop(const FunctionObject *b, const Value *thisObject, const Value *, int) diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index 5ab8cf2dcb..da1b91e69a 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -92,7 +92,7 @@ ReturnedValue DataViewCtor::virtualCallAsConstructor(const FunctionObject *f, co uint byteLength = (argc < 3 || argv[2].isUndefined()) ? (bufferLength - offset) : ::toIndex(scope.engine, argv[2]); if (scope.hasException()) return Encode::undefined(); - if (offset + byteLength > bufferLength) + if (offset > bufferLength || byteLength > bufferLength - offset) return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); Scoped<DataView> a(scope, scope.engine->memoryManager->allocate<DataView>()); diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index c2f48ffeac..bebcbd7e44 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -682,17 +682,17 @@ static inline QString ToTimeString(double t) static inline QString ToLocaleString(double t) { - return ToDateTime(t, Qt::LocalTime).toString(Qt::DefaultLocaleShortDate); + return QLocale().toString(ToDateTime(t, Qt::LocalTime), QLocale::ShortFormat); } static inline QString ToLocaleDateString(double t) { - return ToDateTime(t, Qt::LocalTime).date().toString(Qt::DefaultLocaleShortDate); + return QLocale().toString(ToDateTime(t, Qt::LocalTime).date(), QLocale::ShortFormat); } static inline QString ToLocaleTimeString(double t) { - return ToDateTime(t, Qt::LocalTime).time().toString(Qt::DefaultLocaleShortDate); + return QLocale().toString(ToDateTime(t, Qt::LocalTime).time(), QLocale::ShortFormat); } static double getLocalTZA() diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index df2c46b64a..680ccc02a8 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -303,15 +303,19 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) memoryManager = new QV4::MemoryManager(this); if (maxCallDepth == -1) { - ok = false; - maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok); - if (!ok || maxCallDepth <= 0) { + if (qEnvironmentVariableIsSet("QV4_CRASH_ON_STACKOVERFLOW")) { + maxCallDepth = std::numeric_limits<qint32>::max(); + } else { + ok = false; + maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok); + if (!ok || maxCallDepth <= 0) { #if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !__has_feature(address_sanitizer) - maxCallDepth = 1234; + maxCallDepth = 1234; #else - // no (tail call) optimization is done, so there'll be a lot mare stack frames active - maxCallDepth = 200; + // no (tail call) optimization is done, so there'll be a lot mare stack frames active + maxCallDepth = 200; #endif + } } } Q_ASSERT(maxCallDepth > 0); @@ -1262,20 +1266,15 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file) void ExecutionEngine::markObjects(MarkStack *markStack) { - for (int i = 0; i < NClasses; ++i) - if (classes[i]) { - classes[i]->mark(markStack); - if (markStack->top >= markStack->limit) - markStack->drain(); - } - markStack->drain(); + for (int i = 0; i < NClasses; ++i) { + if (Heap::InternalClass *c = classes[i]) + c->mark(markStack); + } identifierTable->markObjects(markStack); - for (auto compilationUnit: compilationUnits) { + for (auto compilationUnit: compilationUnits) compilationUnit->markObjects(markStack); - markStack->drain(); - } } ReturnedValue ExecutionEngine::throwError(const Value &value) @@ -1457,7 +1456,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int if (const QV4::VariantObject *v = value.as<QV4::VariantObject>()) return v->d()->data(); - if (typeHint == QVariant::Bool) + if (typeHint == QMetaType::Bool) return QVariant(value.toBoolean()); if (typeHint == QMetaType::QJsonValue) @@ -1522,7 +1521,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int retn = QVariant(typeHint, temp); QMetaType::destroy(typeHint, temp); auto retnAsIterable = retn.value<QtMetaTypePrivate::QSequentialIterableImpl>(); - if (retnAsIterable._iteratorCapabilities & QtMetaTypePrivate::ContainerIsAppendable) { + if (retnAsIterable.containerCapabilities() & QtMetaTypePrivate::ContainerIsAppendable) { auto const length = a->getLength(); QV4::ScopedValue arrayValue(scope); for (qint64 i = 0; i < length; ++i) { @@ -1533,8 +1532,8 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int if (!couldConvert) { qWarning() << QLatin1String("Could not convert array value at position %1 from %2 to %3") .arg(QString::number(i), - QMetaType::typeName(originalType), - QMetaType::typeName(retnAsIterable._metaType_id)); + QString::fromUtf8(QMetaType::typeName(originalType)), + QString::fromUtf8(QMetaType::typeName(retnAsIterable._metaType_id))); // create default constructed value asVariant = QVariant(retnAsIterable._metaType_id, nullptr); } @@ -1558,7 +1557,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int if (String *s = value.stringValue()) { const QString &str = s->toQString(); // QChars are stored as a strings - if (typeHint == QVariant::Char && str.size() == 1) + if (typeHint == QMetaType::QChar && str.size() == 1) return str.at(0); return str; } @@ -1857,7 +1856,7 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) Q_ASSERT(data != nullptr); QVariant variant(type, data); - if (QMetaType::Type(variant.type()) == QMetaType::QVariant) { + if (QMetaType::Type(variant.userType()) == QMetaType::QVariant) { // unwrap it: this is tested in QJSEngine, and makes the most sense for // end-user code too. return variantToJS(this, *reinterpret_cast<const QVariant*>(data)); @@ -1911,10 +1910,10 @@ QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule( sourceCode, sourceTimeStamp, &diagnostics); for (const QQmlJS::DiagnosticMessage &m : diagnostics) { if (m.isError()) { - throwSyntaxError(m.message, url.toString(), m.line, m.column); + throwSyntaxError(m.message, url.toString(), m.loc.startLine, m.loc.startColumn); return nullptr; } else { - qWarning() << url << ':' << m.line << ':' << m.column + qWarning() << url << ':' << m.loc.startLine << ':' << m.loc.startColumn << ": warning: " << m.message; } } @@ -1974,11 +1973,12 @@ void ExecutionEngine::initQmlGlobalObject() void ExecutionEngine::initializeGlobal() { QV4::Scope scope(this); - QV4::GlobalExtensions::init(globalObject, QJSEngine::AllExtensions); QV4::ScopedObject qt(scope, memoryManager->allocate<QV4::QtObject>(qmlEngine())); globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt); + QV4::GlobalExtensions::init(globalObject, QJSEngine::AllExtensions); + #if QT_CONFIG(qml_locale) QQmlLocale::registerStringLocaleCompare(this); QQmlDateExtension::registerExtension(this); diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index 79e2ec2a5d..29e1c6dbf7 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -53,6 +53,7 @@ #include <private/qv4compilationunitmapper_p.h> #include <private/qml_compile_hash_p.h> #include <private/qqmltypewrapper_p.h> +#include <private/inlinecomponentutils_p.h> #include <QtQml/qqmlfile.h> #include <QtQml/qqmlpropertymap.h> @@ -62,6 +63,7 @@ #include <QtCore/qfileinfo.h> #include <QtCore/qscopeguard.h> #include <QtCore/qcryptographichash.h> +#include <QtCore/QScopedValueRollback> #if defined(QML_COMPILE_HASH) # ifdef Q_OS_LINUX @@ -94,11 +96,15 @@ ExecutableCompilationUnit::~ExecutableCompilationUnit() QString ExecutableCompilationUnit::localCacheFilePath(const QUrl &url) { + static const QByteArray envCachePath = qgetenv("QML_DISK_CACHE_PATH"); + const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix(); QCryptographicHash fileNameHash(QCryptographicHash::Sha1); fileNameHash.addData(localSourcePath.toUtf8()); - QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); + QString directory = envCachePath.isEmpty() + ? QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/") + : QString::fromLocal8Bit(envCachePath) + QLatin1String("/"); QDir::root().mkpath(directory); return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix; } @@ -121,7 +127,7 @@ static QString toString(QV4::ReturnedValue v) static void dumpConstantTable(const StaticValue *constants, uint count) { QDebug d = qDebug(); - d.nospace() << right; + d.nospace() << Qt::right; for (uint i = 0; i < count; ++i) { d << qSetFieldWidth(8) << i << qSetFieldWidth(0) << ": " << toString(constants[i].asReturnedValue()).toUtf8().constData() << "\n"; @@ -283,7 +289,7 @@ void ExecutableCompilationUnit::unlink() Q_ASSERT(data && propertyCaches.count() > 0 && propertyCaches.at(/*root object*/0)); if (qmlEngine) qmlEngine->unregisterInternalCompositeType(this); - QQmlMetaType::unregisterInternalCompositeType(this); + QQmlMetaType::unregisterInternalCompositeType({metaTypeId, listMetaTypeId}); isRegisteredWithEngine = false; } @@ -383,21 +389,26 @@ IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int com return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache); } -void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine) +void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine, CompositeMetaTypeIds typeIds) { this->qmlEngine = qmlEngine; // Add to type registry of composites if (propertyCaches.needsVMEMetaObject(/*root object*/0)) { - QQmlMetaType::registerInternalCompositeType(this); + // typeIds is only valid for types that have references to themselves. + if (!typeIds.isValid()) + typeIds = QQmlMetaType::registerInternalCompositeType(rootPropertyCache()->className()); + metaTypeId = typeIds.id; + listMetaTypeId = typeIds.listId; qmlEngine->registerInternalCompositeType(this); + } else { const QV4::CompiledData::Object *obj = objectAt(/*root object*/0); auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); - if (typeRef->compilationUnit) { - metaTypeId = typeRef->compilationUnit->metaTypeId; - listMetaTypeId = typeRef->compilationUnit->listMetaTypeId; + if (const auto compilationUnit = typeRef->compilationUnit()) { + metaTypeId = compilationUnit->metaTypeId; + listMetaTypeId = compilationUnit->listMetaTypeId; } else { metaTypeId = typeRef->type.typeId(); listMetaTypeId = typeRef->type.qListTypeId(); @@ -405,29 +416,105 @@ void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngi } // Collect some data for instantiation later. + using namespace icutils; + std::vector<QV4::CompiledData::InlineComponent> allICs {}; + for (int i=0; i != objectCount(); ++i) { + const CompiledObject *obj = objectAt(i); + for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) { + allICs.push_back(*it); + } + } + std::vector<Node> nodes; + nodes.resize(allICs.size()); + std::iota(nodes.begin(), nodes.end(), 0); + AdjacencyList adjacencyList; + adjacencyList.resize(nodes.size()); + fillAdjacencyListForInlineComponents(this, adjacencyList, nodes, allICs); + bool hasCycle = false; + auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle); + Q_ASSERT(!hasCycle); // would have already been discovered by qqmlpropertycachcecreator + + // We need to first iterate over all inline components, as the containing component might create instances of them + // and in that case we need to add its object count + for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) { + const auto &ic = allICs.at(nodeIt->index); + int lastICRoot = ic.objectIndex; + for (int i = ic.objectIndex; i<objectCount(); ++i) { + const QV4::CompiledData::Object *obj = objectAt(i); + bool leftCurrentInlineComponent = + (i != lastICRoot && obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot) + || !(obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent); + if (leftCurrentInlineComponent) + break; + inlineComponentData[lastICRoot].totalBindingCount += obj->nBindings; + + if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { + if (typeRef->type.isValid() && typeRef->type.parserStatusCast() != -1) + ++inlineComponentData[lastICRoot].totalParserStatusCount; + + ++inlineComponentData[lastICRoot].totalObjectCount; + if (const auto compilationUnit = typeRef->compilationUnit()) { + // if the type is an inline component type, we have to extract the information from it + // This requires that inline components are visited in the correct order + auto icRoot = compilationUnit->icRoot; + if (typeRef->type.isInlineComponentType()) { + icRoot = typeRef->type.inlineComponendId(); + } + QScopedValueRollback<int> rollback {compilationUnit->icRoot, icRoot}; + inlineComponentData[lastICRoot].totalBindingCount += compilationUnit->totalBindingsCount(); + inlineComponentData[lastICRoot].totalParserStatusCount += compilationUnit->totalParserStatusCount(); + inlineComponentData[lastICRoot].totalObjectCount += compilationUnit->totalObjectCount(); + } + } + } + } int bindingCount = 0; int parserStatusCount = 0; int objectCount = 0; for (quint32 i = 0, count = this->objectCount(); i < count; ++i) { const QV4::CompiledData::Object *obj = objectAt(i); + if (obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent) { + continue; + } bindingCount += obj->nBindings; if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { - if (typeRef->type.isValid()) { - if (typeRef->type.parserStatusCast() != -1) - ++parserStatusCount; - } + if (typeRef->type.isValid() && typeRef->type.parserStatusCast() != -1) + ++parserStatusCount; ++objectCount; - if (typeRef->compilationUnit) { - bindingCount += typeRef->compilationUnit->totalBindingsCount; - parserStatusCount += typeRef->compilationUnit->totalParserStatusCount; - objectCount += typeRef->compilationUnit->totalObjectCount; + if (const auto compilationUnit = typeRef->compilationUnit()) { + auto icRoot = compilationUnit->icRoot; + if (typeRef->type.isInlineComponentType()) { + icRoot = typeRef->type.inlineComponendId(); + } + QScopedValueRollback<int> rollback {compilationUnit->icRoot, icRoot}; + bindingCount += compilationUnit->totalBindingsCount(); + parserStatusCount += compilationUnit->totalParserStatusCount(); + objectCount += compilationUnit->totalObjectCount(); } } } - totalBindingsCount = bindingCount; - totalParserStatusCount = parserStatusCount; - totalObjectCount = objectCount; + m_totalBindingsCount = bindingCount; + m_totalParserStatusCount = parserStatusCount; + m_totalObjectCount = objectCount; +} + +int ExecutableCompilationUnit::totalBindingsCount() const { + if (icRoot == -1) + return m_totalBindingsCount; + return inlineComponentData[icRoot].totalBindingCount; +} + +int ExecutableCompilationUnit::totalObjectCount() const { + if (icRoot == -1) + return m_totalObjectCount; + return inlineComponentData[icRoot].totalObjectCount; +} + +int ExecutableCompilationUnit::totalParserStatusCount() const { + if (icRoot == -1) + return m_totalParserStatusCount; + return inlineComponentData[icRoot].totalParserStatusCount; } bool ExecutableCompilationUnit::verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const @@ -445,6 +532,13 @@ bool ExecutableCompilationUnit::verifyChecksum(const CompiledData::DependentType sizeof(data->dependencyMD5Checksum)) == 0; } +CompositeMetaTypeIds ExecutableCompilationUnit::typeIdsForComponent(int objectid) const +{ + if (objectid == 0) + return {metaTypeId, listMetaTypeId}; + return inlineComponentData[objectid].typeIds; +} + QStringList ExecutableCompilationUnit::moduleRequests() const { QStringList requests; @@ -712,7 +806,7 @@ QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::propertyCache() const if (type.isValid()) return typePropertyCache; else - return compilationUnit->rootPropertyCache(); + return m_compilationUnit->rootPropertyCache(); } /*! @@ -726,21 +820,22 @@ QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache(QQm typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type.metaObject(), minorVersion); return typePropertyCache; } else { - return compilationUnit->rootPropertyCache(); + Q_ASSERT(m_compilationUnit); + return m_compilationUnit->rootPropertyCache(); } } bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engine) { - if (type.isValid()) { + if (type.isValid() && !type.isInlineComponentType()) { bool ok = false; hash->addData(createPropertyCache(engine)->checksum(&ok)); return ok; } - if (!compilationUnit) + if (!m_compilationUnit) return false; - hash->addData(compilationUnit->data->md5Checksum, - sizeof(compilationUnit->data->md5Checksum)); + hash->addData(m_compilationUnit->data->md5Checksum, + sizeof(m_compilationUnit->data->md5Checksum)); return true; } @@ -761,8 +856,8 @@ void ResolvedTypeReference::doDynamicTypeCheck() mo = typePropertyCache->firstCppMetaObject(); else if (type.isValid()) mo = type.metaObject(); - else if (compilationUnit) - mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); + else if (m_compilationUnit) + mo = m_compilationUnit->rootPropertyCache()->firstCppMetaObject(); isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo); } diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h index 6eef3b12c3..7bfb68da9d 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit_p.h +++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h @@ -58,11 +58,32 @@ #include <private/qqmlpropertycachevector_p.h> #include <private/qqmltype_p.h> #include <private/qqmlnullablevalue_p.h> +#include <private/qqmlmetatype_p.h> QT_BEGIN_NAMESPACE class QQmlScriptData; class QQmlEnginePrivate; + +struct InlineComponentData { + + InlineComponentData() = default; + InlineComponentData(const CompositeMetaTypeIds &typeIds, int objectIndex, int nameIndex, int totalObjectCount, int totalBindingCount, int totalParserStatusCount) + : typeIds(typeIds) + , objectIndex(objectIndex) + , nameIndex(nameIndex) + , totalObjectCount(totalObjectCount) + , totalBindingCount(totalBindingCount) + , totalParserStatusCount(totalParserStatusCount) {} + + CompositeMetaTypeIds typeIds; + int objectIndex = -1; + int nameIndex = -1; + int totalObjectCount = 0; + int totalBindingCount = 0; + int totalParserStatusCount = 0; +}; + namespace QV4 { // index is per-object binding index @@ -142,11 +163,16 @@ public: QHash<int, IdentifierHash> namedObjectsPerComponentCache; inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex); - void finalizeCompositeType(QQmlEnginePrivate *qmlEngine); + void finalizeCompositeType(QQmlEnginePrivate *qmlEngine, CompositeMetaTypeIds typeIdsForComponent); - int totalBindingsCount = 0; // Number of bindings used in this type - int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses - int totalObjectCount = 0; // Number of objects explicitly instantiated + int m_totalBindingsCount = 0; // Number of bindings used in this type + int m_totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses + int m_totalObjectCount = 0; // Number of objects explicitly instantiated + int icRoot = -1; + + int totalBindingsCount() const; + int totalParserStatusCount() const; + int totalObjectCount() const; QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts; ResolvedTypeReferenceMap resolvedTypes; @@ -154,10 +180,14 @@ public: bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const; + CompositeMetaTypeIds typeIdsForComponent(int objectid = 0) const; + int metaTypeId = -1; int listMetaTypeId = -1; bool isRegisteredWithEngine = false; + QHash<int, InlineComponentData> inlineComponentData; + QScopedPointer<CompilationUnitMapper> backingFile; // --- interface for QQmlPropertyCacheCreator @@ -295,27 +325,73 @@ private: struct ResolvedTypeReference { +public: ResolvedTypeReference() - : majorVersion(0) - , minorVersion(0) - , isFullyDynamicType(false) + : m_compilationUnit(nullptr) + , m_stronglyReferencesCompilationUnit(true) + , majorVersion(0) + , minorVersion(0) + , isFullyDynamicType(false) {} + ~ResolvedTypeReference() + { + if (m_stronglyReferencesCompilationUnit && m_compilationUnit) + m_compilationUnit->release(); + } + + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit() { return m_compilationUnit; } + void setCompilationUnit(QQmlRefPointer<QV4::ExecutableCompilationUnit> unit) + { + if (m_compilationUnit == unit.data()) + return; + if (m_stronglyReferencesCompilationUnit) { + if (m_compilationUnit) + m_compilationUnit->release(); + m_compilationUnit = unit.take(); + } else { + m_compilationUnit = unit.data(); + } + } + + bool referencesCompilationUnit() const { return m_stronglyReferencesCompilationUnit; } + void setReferencesCompilationUnit(bool doReference) + { + if (doReference == m_stronglyReferencesCompilationUnit) + return; + m_stronglyReferencesCompilationUnit = doReference; + if (!m_compilationUnit) + return; + if (doReference) { + m_compilationUnit->addref(); + } else if (m_compilationUnit->count() == 1) { + m_compilationUnit->release(); + m_compilationUnit = nullptr; + } else { + m_compilationUnit->release(); + } + } + + QQmlRefPointer<QQmlPropertyCache> propertyCache() const; + QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *); + bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); + + void doDynamicTypeCheck(); + QQmlType type; QQmlRefPointer<QQmlPropertyCache> typePropertyCache; - QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; +private: + Q_DISABLE_COPY_MOVE(ResolvedTypeReference) + QV4::ExecutableCompilationUnit *m_compilationUnit; + bool m_stronglyReferencesCompilationUnit; + +public: int majorVersion; int minorVersion; // Types such as QQmlPropertyMap can add properties dynamically at run-time and // therefore cannot have a property cache installed when instantiated. bool isFullyDynamicType; - - QQmlRefPointer<QQmlPropertyCache> propertyCache() const; - QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *); - bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); - - void doDynamicTypeCheck(); }; IdentifierHash ExecutableCompilationUnit::namedObjectsPerComponent(int componentObjectIndex) diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp index 79c372348f..f4901e3e4d 100644 --- a/src/qml/jsruntime/qv4persistent.cpp +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -240,7 +240,6 @@ void PersistentValueStorage::mark(MarkStack *markStack) if (Managed *m = p->values[i].as<Managed>()) m->mark(markStack); } - markStack->drain(); p = p->header.next; } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 9d0b65cc0a..66b79aa0e8 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -638,10 +638,11 @@ void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack) if (!ddata) return; - if (ddata->jsEngineId == markStack->engine->m_engineId) + const QV4::ExecutionEngine *engine = markStack->engine(); + if (ddata->jsEngineId == engine->m_engineId) ddata->jsWrapper.markOnce(markStack); - else if (markStack->engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object) - markStack->engine->m_multiplyWrappedQObjects->mark(object, markStack); + else if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object) + engine->m_multiplyWrappedQObjects->mark(object, markStack); } void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value) @@ -1654,7 +1655,7 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const } CallArgument::CallArgument() -: type(QVariant::Invalid) +: type(QMetaType::UnknownType) { } @@ -2077,7 +2078,7 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value * if (!d()->valueTypeWrapper) return Encode::undefined(); - object = QQmlObjectOrGadget(d()->propertyCache(), d()->valueTypeWrapper->gadgetPtr); + object = QQmlObjectOrGadget(d()->propertyCache(), d()->valueTypeWrapper->gadgetPtr()); } QQmlPropertyData method; diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index f9e4d258fc..5fc94b9ddd 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -2336,6 +2336,133 @@ Bool Runtime::CompareStrictNotEqual::call(const Value &left, const Value &right) return ! RuntimeHelpers::strictEqual(left, right); } +template<typename Operation> +static inline const void *symbol() +{ + return reinterpret_cast<void *>(&Operation::call); +} + +QHash<const void *, const char *> Runtime::symbolTable() +{ + static const QHash<const void *, const char *> symbols({ +#ifndef V4_BOOTSTRAP + {symbol<CallGlobalLookup>(), "CallGlobalLookup" }, + {symbol<CallQmlContextPropertyLookup>(), "CallQmlContextPropertyLookup" }, + {symbol<CallName>(), "CallName" }, + {symbol<CallProperty>(), "CallProperty" }, + {symbol<CallPropertyLookup>(), "CallPropertyLookup" }, + {symbol<CallElement>(), "CallElement" }, + {symbol<CallValue>(), "CallValue" }, + {symbol<CallWithReceiver>(), "CallWithReceiver" }, + {symbol<CallPossiblyDirectEval>(), "CallPossiblyDirectEval" }, + {symbol<CallWithSpread>(), "CallWithSpread" }, + {symbol<TailCall>(), "TailCall" }, + + {symbol<Construct>(), "Construct" }, + {symbol<ConstructWithSpread>(), "ConstructWithSpread" }, + + {symbol<StoreNameStrict>(), "StoreNameStrict" }, + {symbol<StoreNameSloppy>(), "StoreNameSloppy" }, + {symbol<StoreProperty>(), "StoreProperty" }, + {symbol<StoreElement>(), "StoreElement" }, + {symbol<LoadProperty>(), "LoadProperty" }, + {symbol<LoadName>(), "LoadName" }, + {symbol<LoadElement>(), "LoadElement" }, + {symbol<LoadSuperProperty>(), "LoadSuperProperty" }, + {symbol<StoreSuperProperty>(), "StoreSuperProperty" }, + {symbol<LoadSuperConstructor>(), "LoadSuperConstructor" }, + {symbol<LoadGlobalLookup>(), "LoadGlobalLookup" }, + {symbol<LoadQmlContextPropertyLookup>(), "LoadQmlContextPropertyLookup" }, + {symbol<GetLookup>(), "GetLookup" }, + {symbol<SetLookupStrict>(), "SetLookupStrict" }, + {symbol<SetLookupSloppy>(), "SetLookupSloppy" }, + + {symbol<TypeofValue>(), "TypeofValue" }, + {symbol<TypeofName>(), "TypeofName" }, + + {symbol<DeleteProperty_NoThrow>(), "DeleteProperty_NoThrow" }, + {symbol<DeleteProperty>(), "DeleteProperty" }, + {symbol<DeleteName_NoThrow>(), "DeleteName_NoThrow" }, + {symbol<DeleteName>(), "DeleteName" }, + + {symbol<ThrowException>(), "ThrowException" }, + {symbol<PushCallContext>(), "PushCallContext" }, + {symbol<PushWithContext>(), "PushWithContext" }, + {symbol<PushCatchContext>(), "PushCatchContext" }, + {symbol<PushBlockContext>(), "PushBlockContext" }, + {symbol<CloneBlockContext>(), "CloneBlockContext" }, + {symbol<PushScriptContext>(), "PushScriptContext" }, + {symbol<PopScriptContext>(), "PopScriptContext" }, + {symbol<ThrowReferenceError>(), "ThrowReferenceError" }, + {symbol<ThrowOnNullOrUndefined>(), "ThrowOnNullOrUndefined" }, + + {symbol<Closure>(), "Closure" }, + + {symbol<ConvertThisToObject>(), "ConvertThisToObject" }, + {symbol<DeclareVar>(), "DeclareVar" }, + {symbol<CreateMappedArgumentsObject>(), "CreateMappedArgumentsObject" }, + {symbol<CreateUnmappedArgumentsObject>(), "CreateUnmappedArgumentsObject" }, + {symbol<CreateRestParameter>(), "CreateRestParameter" }, + + {symbol<ArrayLiteral>(), "ArrayLiteral" }, + {symbol<ObjectLiteral>(), "ObjectLiteral" }, + {symbol<CreateClass>(), "CreateClass" }, + + {symbol<GetIterator>(), "GetIterator" }, + {symbol<IteratorNext>(), "IteratorNext" }, + {symbol<IteratorNextForYieldStar>(), "IteratorNextForYieldStar" }, + {symbol<IteratorClose>(), "IteratorClose" }, + {symbol<DestructureRestElement>(), "DestructureRestElement" }, + + {symbol<ToObject>(), "ToObject" }, + {symbol<ToBoolean>(), "ToBoolean" }, + {symbol<ToNumber>(), "ToNumber" }, + + {symbol<UMinus>(), "UMinus" }, + + {symbol<Instanceof>(), "Instanceof" }, + {symbol<In>(), "In" }, + {symbol<Add>(), "Add" }, + {symbol<Sub>(), "Sub" }, + {symbol<Mul>(), "Mul" }, + {symbol<Div>(), "Div" }, + {symbol<Mod>(), "Mod" }, + {symbol<Exp>(), "Exp" }, + {symbol<BitAnd>(), "BitAnd" }, + {symbol<BitOr>(), "BitOr" }, + {symbol<BitXor>(), "BitXor" }, + {symbol<Shl>(), "Shl" }, + {symbol<Shr>(), "Shr" }, + {symbol<UShr>(), "UShr" }, + {symbol<GreaterThan>(), "GreaterThan" }, + {symbol<LessThan>(), "LessThan" }, + {symbol<GreaterEqual>(), "GreaterEqual" }, + {symbol<LessEqual>(), "LessEqual" }, + {symbol<Equal>(), "Equal" }, + {symbol<NotEqual>(), "NotEqual" }, + {symbol<StrictEqual>(), "StrictEqual" }, + {symbol<StrictNotEqual>(), "StrictNotEqual" }, + + {symbol<CompareGreaterThan>(), "CompareGreaterThan" }, + {symbol<CompareLessThan>(), "CompareLessThan" }, + {symbol<CompareGreaterEqual>(), "CompareGreaterEqual" }, + {symbol<CompareLessEqual>(), "CompareLessEqual" }, + {symbol<CompareEqual>(), "CompareEqual" }, + {symbol<CompareNotEqual>(), "CompareNotEqual" }, + {symbol<CompareStrictEqual>(), "CompareStrictEqual" }, + {symbol<CompareStrictNotEqual>(), "CompareStrictNotEqual" }, + + {symbol<CompareInstanceof>(), "CompareInstanceOf" }, + {symbol<CompareIn>(), "CompareIn" }, + + {symbol<RegexpLiteral>(), "RegexpLiteral" }, + {symbol<GetTemplateObject>(), "GetTemplateObject" } +#endif + }); + + return symbols; +} + } // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 05ffb84d58..d155187e48 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -501,6 +501,8 @@ struct Q_QML_PRIVATE_EXPORT Runtime { static const int tailCall_argv = -3; static const int tailCall_argc = -4; }; + + static QHash<const void *, const char *> symbolTable(); }; static_assert(std::is_standard_layout<Runtime>::value, "Runtime needs to be standard layout in order for us to be able to use offsetof"); diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp index 162d75db63..b7737982b7 100644 --- a/src/qml/jsruntime/qv4runtimecodegen.cpp +++ b/src/qml/jsruntime/qv4runtimecodegen.cpp @@ -67,7 +67,7 @@ void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName, _module->rootContext = _module->functions.at(index); } -void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) +void RuntimeCodegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) { if (hasError()) return; @@ -76,7 +76,7 @@ void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QStr engine->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn); } -void RuntimeCodegen::throwReferenceError(const AST::SourceLocation &loc, const QString &detail) +void RuntimeCodegen::throwReferenceError(const SourceLocation &loc, const QString &detail) { if (hasError()) return; diff --git a/src/qml/jsruntime/qv4runtimecodegen_p.h b/src/qml/jsruntime/qv4runtimecodegen_p.h index 71aaf1fb55..d3a30ce35d 100644 --- a/src/qml/jsruntime/qv4runtimecodegen_p.h +++ b/src/qml/jsruntime/qv4runtimecodegen_p.h @@ -69,8 +69,8 @@ public: QQmlJS::AST::FunctionExpression *ast, Compiler::Module *module); - void throwSyntaxError(const QQmlJS::AST::SourceLocation &loc, const QString &detail) override; - void throwReferenceError(const QQmlJS::AST::SourceLocation &loc, const QString &detail) override; + void throwSyntaxError(const QQmlJS::SourceLocation &loc, const QString &detail) override; + void throwReferenceError(const QQmlJS::SourceLocation &loc, const QString &detail) override; private: ExecutionEngine *engine; diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 2fab9e4b7b..2add1222ea 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -52,6 +52,7 @@ #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> #include <private/qqmlengine_p.h> +#include <private/qqmlsourcecoordinate_p.h> #include <private/qv4profiling_p.h> #include <qv4runtimecodegen_p.h> @@ -109,10 +110,10 @@ void Script::parse() const auto diagnosticMessages = parser.diagnosticMessages(); for (const DiagnosticMessage &m : diagnosticMessages) { if (m.isError()) { - valueScope.engine->throwSyntaxError(m.message, sourceFile, m.line, m.column); + valueScope.engine->throwSyntaxError(m.message, sourceFile, m.loc.startLine, m.loc.startColumn); return; } else { - qWarning() << sourceFile << ':' << m.line << ':' << m.column + qWarning() << sourceFile << ':' << m.loc.startLine << ':' << m.loc.startColumn << ": warning: " << m.message; } } @@ -209,8 +210,8 @@ QV4::CompiledData::CompilationUnit Script::precompile( const auto v4Error = cg.error(); QQmlError error; error.setUrl(cg.url()); - error.setLine(v4Error.line); - error.setColumn(v4Error.column); + error.setLine(qmlConvertSourceCoordinate<quint32, int>(v4Error.loc.startLine)); + error.setColumn(qmlConvertSourceCoordinate<quint32, int>(v4Error.loc.startColumn)); error.setDescription(v4Error.message); reportedErrors->append(error); } diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index 6a1b003b25..d03b67aa27 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -1070,51 +1070,44 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_indexOf(const FunctionObject return Encode(-1); } -ReturnedValue IntrinsicTypedArrayPrototype::method_join(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +ReturnedValue IntrinsicTypedArrayPrototype::method_join( + const FunctionObject *functionObject, const Value *thisObject, const Value *argv, int argc) { - Scope scope(b); - Scoped<TypedArray> v(scope, thisObject); - if (!v || v->d()->buffer->isDetachedBuffer()) + Scope scope(functionObject); + Scoped<TypedArray> typedArray(scope, thisObject); + if (!typedArray || typedArray->d()->buffer->isDetachedBuffer()) return scope.engine->throwTypeError(); - uint len = v->length(); - - ScopedValue arg(scope, argc ? argv[0] : Value::undefinedValue()); + // We cannot optimize the resolution of the argument away if length is 0. + // It may have side effects. + ScopedValue argument(scope, argc ? argv[0] : Value::undefinedValue()); + const QString separator = argument->isUndefined() + ? QStringLiteral(",") + : argument->toQString(); - QString r4; - if (arg->isUndefined()) - r4 = QStringLiteral(","); - else - r4 = arg->toQString(); - - const quint32 r2 = len; - - if (!r2) + const quint32 length = typedArray->length(); + if (!length) return Encode(scope.engine->newString()); - QString R; + QString result; - // - // crazy! - // ScopedString name(scope, scope.engine->newString(QStringLiteral("0"))); - ScopedValue r6(scope, v->get(name)); - if (!r6->isNullOrUndefined()) - R = r6->toQString(); + ScopedValue value(scope, typedArray->get(name)); + if (!value->isNullOrUndefined()) + result = value->toQString(); - ScopedValue r12(scope); - for (quint32 k = 1; k < r2; ++k) { - R += r4; + for (quint32 i = 1; i < length; ++i) { + result += separator; - name = Value::fromDouble(k).toString(scope.engine); - r12 = v->get(name); + name = Value::fromDouble(i).toString(scope.engine); + value = typedArray->get(name); CHECK_EXCEPTION(); - if (!r12->isNullOrUndefined()) - R += r12->toQString(); + if (!value->isNullOrUndefined()) + result += value->toQString(); } - return Encode(scope.engine->newString(R)); + return Encode(scope.engine->newString(result)); } ReturnedValue IntrinsicTypedArrayPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -1657,6 +1650,158 @@ ReturnedValue IntrinsicTypedArrayCtor::method_of(const FunctionObject *f, const return newObj->asReturnedValue(); } +ReturnedValue IntrinsicTypedArrayCtor::method_from(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(f); + ScopedObject itemsObject(scope, argv[0]); + bool usingIterator = false; + + ScopedFunctionObject mapfn(scope, Value::undefinedValue()); + Value *mapArguments = nullptr; + if (argc > 1) { + mapfn = ScopedFunctionObject(scope, argv[1]); + if (!mapfn) + return scope.engine->throwTypeError(QString::fromLatin1("%1 is not a function").arg(argv[1].toQStringNoThrow())); + mapArguments = scope.alloc(2); + } + + // Iterator validity check goes after map function validity has been checked. + if (itemsObject) { + // If the object claims to support iterators, then let's try use them. + ScopedValue it(scope, itemsObject->get(scope.engine->symbol_iterator())); + CHECK_EXCEPTION(); + if (!it->isNullOrUndefined()) { + ScopedFunctionObject itfunc(scope, it); + if (!itfunc) + return scope.engine->throwTypeError(); + usingIterator = true; + } + } + + ScopedValue thisArg(scope); + if (argc > 2) + thisArg = argv[2]; + + const FunctionObject *C = thisObject->as<FunctionObject>(); + + if (usingIterator) { + // Item iteration supported, so let's go ahead and try use that. + CHECK_EXCEPTION(); + + qint64 iterableLength = 0; + Value *nextValue = scope.alloc(1); + ScopedValue done(scope); + + ScopedObject lengthIterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true)); + CHECK_EXCEPTION(); // symbol_iterator threw; whoops. + if (!lengthIterator) { + return scope.engine->throwTypeError(); // symbol_iterator wasn't an object. + } + + forever { + // Here we calculate the length of the iterable range. + if (iterableLength > (static_cast<qint64>(1) << 53) - 1) { + ScopedValue falsey(scope, Encode(false)); + ScopedValue error(scope, scope.engine->throwTypeError()); + return Runtime::IteratorClose::call(scope.engine, lengthIterator, falsey); + } + // Retrieve the next value. If the iteration ends, we're done here. + done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, lengthIterator, nextValue)); + if (scope.engine->hasException) + return Runtime::IteratorClose::call(scope.engine, lengthIterator, Value::fromBoolean(false)); + if (done->toBoolean()) { + break; + } + iterableLength++; + } + + // Constructor validity check goes after we have calculated the length, because that calculation can throw + // errors that are not type errors and at least the tests expect those rather than type errors. + if (!C || !C->isConstructor()) + return scope.engine->throwTypeError(); + + ScopedObject iterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true)); + CHECK_EXCEPTION(); // symbol_iterator can throw. + if (!iterator) { + return scope.engine->throwTypeError(); // symbol_iterator wasn't an object. + } + + ScopedObject a(scope, Value::undefinedValue()); + ScopedValue ctorArgument(scope, Value::fromReturnedValue(QV4::Encode(int(iterableLength)))); + a = C->callAsConstructor(ctorArgument, 1); + CHECK_EXCEPTION(); + + // We check exceptions above, and only after doing so, check the array's validity after construction. + if (!::validateTypedArray(a) || (a->getLength() < iterableLength)) + return scope.engine->throwTypeError(); + + + // The loop below traverses the iterator, and puts elements into the created array. + ScopedValue mappedValue(scope, Value::undefinedValue()); + for (qint64 k = 0; k < iterableLength; ++k) { + done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, iterator, nextValue)); + if (scope.engine->hasException) + return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false)); + + if (mapfn) { + mapArguments[0] = *nextValue; + mapArguments[1] = Value::fromDouble(k); + mappedValue = mapfn->call(thisArg, mapArguments, 2); + if (scope.engine->hasException) + return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false)); + } else { + mappedValue = *nextValue; + } + + a->put(k, mappedValue); + if (scope.engine->hasException) + return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false)); + } + return a.asReturnedValue(); + } else { + // Array-like fallback. We request elements by index, and put them into the created array. + ScopedObject arrayLike(scope, argv[0].toObject(scope.engine)); + if (!arrayLike) + return scope.engine->throwTypeError(QString::fromLatin1("Cannot convert %1 to object").arg(argv[0].toQStringNoThrow())); + + int len = arrayLike->getLength(); + CHECK_EXCEPTION(); + + // Getting the length may throw, and must do so before we check the constructor validity. + if (!C || !C->isConstructor()) + return scope.engine->throwTypeError(); + + ScopedObject a(scope, Value::undefinedValue()); + ScopedValue ctorArgument(scope, Value::fromReturnedValue(QV4::Encode(len))); + a = C->callAsConstructor(ctorArgument, 1); + CHECK_EXCEPTION(); + + // We check exceptions above, and only after doing so, check the array's validity after construction. + if (!::validateTypedArray(a) || (a->getLength() < len)) + return scope.engine->throwTypeError(); + + ScopedValue mappedValue(scope, Value::undefinedValue()); + ScopedValue kValue(scope); + for (int k = 0; k < len; ++k) { + kValue = arrayLike->get(k); + CHECK_EXCEPTION(); + + if (mapfn) { + mapArguments[0] = kValue; + mapArguments[1] = Value::fromDouble(k); + mappedValue = mapfn->call(thisArg, mapArguments, 2); + CHECK_EXCEPTION(); + } else { + mappedValue = kValue; + } + + a->put(k, mappedValue); + CHECK_EXCEPTION(); + } + return a.asReturnedValue(); + } +} + void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor) { Scope scope(engine); @@ -1666,6 +1811,8 @@ void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedA ctor->defineReadonlyConfigurableProperty(engine->id_name(), s); s = scope.engine->newString(QStringLiteral("of")); ctor->defineDefaultProperty(s, IntrinsicTypedArrayCtor::method_of); + s = scope.engine->newString(QStringLiteral("from")); + ctor->defineDefaultProperty(s, IntrinsicTypedArrayCtor::method_from, 1); ctor->addSymbolSpecies(); defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr); diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index 64792f23a2..65656e3389 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -181,6 +181,7 @@ struct IntrinsicTypedArrayCtor: FunctionObject static constexpr VTable::Call virtualCall = nullptr; static ReturnedValue method_of(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_from(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct TypedArrayCtor: FunctionObject diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index d29b060b9e..3449603a8a 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -250,6 +250,8 @@ bool Value::sameValue(Value other) const { if (isDouble() && other.isInteger()) return other.int_32() ? (doubleValue() == double(other.int_32())) : (doubleValue() == 0 && !std::signbit(doubleValue())); + if (isManaged()) + return other.isManaged() && cast<Managed>()->isEqualTo(other.cast<Managed>()); return false; } @@ -269,6 +271,8 @@ bool Value::sameValueZero(Value other) const { return true; } } + if (isManaged()) + return other.isManaged() && cast<Managed>()->isEqualTo(other.cast<Managed>()); return false; } diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index ae12033eb4..42e97b1d36 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -537,29 +537,8 @@ struct ValueArray { } void mark(MarkStack *markStack) { - Value *v = values; - const Value *end = v + alloc; - if (alloc > 32*1024) { - // drain from time to time to avoid overflows in the js stack - Value::HeapBasePtr *currentBase = markStack->top; - while (v < end) { - v->mark(markStack); - ++v; - if (markStack->top >= currentBase + 32*1024) { - Value::HeapBasePtr *oldBase = markStack->base; - markStack->base = currentBase; - markStack->drain(); - markStack->base = oldBase; - } - } - } else { - while (v < end) { - v->mark(markStack); - if (markStack->top >= markStack->limit) - markStack->drain(); - ++v; - } - } + for (Value *v = values, *end = values + alloc; v < end; ++v) + v->mark(markStack); } }; diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index e117e509ab..9d7b3c6e9a 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -65,8 +65,8 @@ void Heap::VariantObject::init(const QVariant &value) bool VariantObject::Data::isScarce() const { - QVariant::Type t = data().type(); - return t == QVariant::Pixmap || t == QVariant::Image; + int t = data().userType(); + return t == QMetaType::QPixmap || t == QMetaType::QImage; } bool VariantObject::virtualIsEqualTo(Managed *m, Managed *other) @@ -139,7 +139,7 @@ ReturnedValue VariantPrototype::method_toString(const FunctionObject *b, const V RETURN_UNDEFINED(); const QVariant variant = o->d()->data(); QString result = variant.toString(); - if (result.isEmpty() && !variant.canConvert(QVariant::String)) { + if (result.isEmpty() && !variant.canConvert(QMetaType::QString)) { QDebug dbg(&result); dbg << variant; // QDebug appends a space, we're not interested in continuing the stream so we chop it off. @@ -154,17 +154,17 @@ ReturnedValue VariantPrototype::method_valueOf(const FunctionObject *b, const Va const VariantObject *o = thisObject->as<QV4::VariantObject>(); if (o) { QVariant v = o->d()->data(); - switch (v.type()) { - case QVariant::Invalid: + switch (v.userType()) { + case QMetaType::UnknownType: return Encode::undefined(); - case QVariant::String: + case QMetaType::QString: return Encode(b->engine()->newString(v.toString())); - case QVariant::Int: + case QMetaType::Int: return Encode(v.toInt()); - case QVariant::Double: - case QVariant::UInt: + case QMetaType::Double: + case QMetaType::UInt: return Encode(v.toDouble()); - case QVariant::Bool: + case QMetaType::Bool: return Encode(v.toBool()); default: if (QMetaType::typeFlags(v.userType()) & QMetaType::IsEnumeration) diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index e143523165..fb103d492d 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -434,7 +434,7 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) Function *function = frame->v4Function; Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(), - function->compilationUnit->fileName(), + function->executableCompilationUnit()->fileName(), function->compiledFunction->location.line, function->compiledFunction->location.column); Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling |