diff options
Diffstat (limited to 'src/qml')
259 files changed, 20673 insertions, 12867 deletions
diff --git a/src/qml/animations/qabstractanimationjob.cpp b/src/qml/animations/qabstractanimationjob.cpp index 3f33e7e81b..4e82c7a062 100644 --- a/src/qml/animations/qabstractanimationjob.cpp +++ b/src/qml/animations/qabstractanimationjob.cpp @@ -78,7 +78,7 @@ QQmlAnimationTimer *QQmlAnimationTimer::instance(bool create) inst = animationTimer() ? animationTimer()->localData() : 0; } #else - static QAnimationTimer unifiedTimer; + static QQmlAnimationTimer unifiedTimer; inst = &unifiedTimer; #endif return inst; diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h index 63fd4b0dac..0be6ca96ea 100644 --- a/src/qml/animations/qabstractanimationjob_p.h +++ b/src/qml/animations/qabstractanimationjob_p.h @@ -56,6 +56,8 @@ #include <QtCore/private/qabstractanimation_p.h> #include <vector> +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class QAnimationGroupJob; diff --git a/src/qml/animations/qanimationgroupjob_p.h b/src/qml/animations/qanimationgroupjob_p.h index fb567dc019..b01b2f3b36 100644 --- a/src/qml/animations/qanimationgroupjob_p.h +++ b/src/qml/animations/qanimationgroupjob_p.h @@ -54,6 +54,8 @@ #include "private/qabstractanimationjob_p.h" #include <QtCore/qdebug.h> +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QAnimationGroupJob : public QAbstractAnimationJob diff --git a/src/qml/animations/qanimationjobutil_p.h b/src/qml/animations/qanimationjobutil_p.h index 0bb9e83b2d..e3d6fe9178 100644 --- a/src/qml/animations/qanimationjobutil_p.h +++ b/src/qml/animations/qanimationjobutil_p.h @@ -51,6 +51,8 @@ // We mean it. // +QT_REQUIRE_CONFIG(qml_animation); + #define RETURN_IF_DELETED(func) \ { \ bool *prevWasDeleted = m_wasDeleted; \ diff --git a/src/qml/animations/qcontinuinganimationgroupjob_p.h b/src/qml/animations/qcontinuinganimationgroupjob_p.h index baf4ff1ae5..c67b8d39ad 100644 --- a/src/qml/animations/qcontinuinganimationgroupjob_p.h +++ b/src/qml/animations/qcontinuinganimationgroupjob_p.h @@ -53,6 +53,8 @@ #include "private/qanimationgroupjob_p.h" +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QContinuingAnimationGroupJob : public QAnimationGroupJob diff --git a/src/qml/animations/qparallelanimationgroupjob_p.h b/src/qml/animations/qparallelanimationgroupjob_p.h index 67ba626247..0265fe3274 100644 --- a/src/qml/animations/qparallelanimationgroupjob_p.h +++ b/src/qml/animations/qparallelanimationgroupjob_p.h @@ -53,6 +53,8 @@ #include "private/qanimationgroupjob_p.h" +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QParallelAnimationGroupJob : public QAnimationGroupJob diff --git a/src/qml/animations/qpauseanimationjob_p.h b/src/qml/animations/qpauseanimationjob_p.h index d0e8d57fc7..6c9bbf0dab 100644 --- a/src/qml/animations/qpauseanimationjob_p.h +++ b/src/qml/animations/qpauseanimationjob_p.h @@ -53,6 +53,8 @@ #include <private/qanimationgroupjob_p.h> +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QPauseAnimationJob : public QAbstractAnimationJob diff --git a/src/qml/animations/qsequentialanimationgroupjob_p.h b/src/qml/animations/qsequentialanimationgroupjob_p.h index 800f0c3b90..13f9806be1 100644 --- a/src/qml/animations/qsequentialanimationgroupjob_p.h +++ b/src/qml/animations/qsequentialanimationgroupjob_p.h @@ -53,6 +53,8 @@ #include <private/qanimationgroupjob_p.h> +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class QPauseAnimationJob; diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 95096db51d..da3c173545 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -11,7 +11,8 @@ HEADERS += \ $$PWD/qv4codegen_p.h \ $$PWD/qqmlirbuilder_p.h \ $$PWD/qqmltypecompiler_p.h \ - $$PWD/qv4instr_moth_p.h + $$PWD/qv4instr_moth_p.h \ + $$PWD/qv4bytecodehandler_p.h SOURCES += \ $$PWD/qv4bytecodegenerator.cpp \ @@ -21,7 +22,8 @@ SOURCES += \ $$PWD/qv4compilerscanfunctions.cpp \ $$PWD/qv4codegen.cpp \ $$PWD/qqmlirbuilder.cpp \ - $$PWD/qv4instr_moth.cpp + $$PWD/qv4instr_moth.cpp \ + $$PWD/qv4bytecodehandler.cpp !qmldevtools_build { diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 8a1b3744ee..820f127331 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -96,23 +96,24 @@ void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, cons declarationsOverride = nullptr; } -QString Object::sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation) +QString IRBuilder::sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation) { QSet<int> functionNames; - for (Function *f = functions->first; f; f = f->next) { - QQmlJS::AST::FunctionDeclaration *function = f->functionDeclaration; - Q_ASSERT(function); - *errorLocation = function->identifierToken; + for (auto functionit = obj->functionsBegin(); functionit != obj->functionsEnd(); ++functionit) { + Function *f = functionit.ptr; + errorLocation->startLine = f->location.line; + errorLocation->startColumn = f->location.column; if (functionNames.contains(f->nameIndex)) return tr("Duplicate method name"); functionNames.insert(f->nameIndex); - for (QmlIR::Signal *s = qmlSignals->first; s; s = s->next) { + for (auto signalit = obj->signalsBegin(); signalit != obj->signalsEnd(); ++signalit) { + QmlIR::Signal *s = signalit.ptr; if (s->nameIndex == f->nameIndex) return tr("Duplicate method name"); } - const QString name = function->name.toString(); + const QString name = stringAt(f->nameIndex); if (name.at(0).isUpper()) return tr("Method names cannot begin with an upper case letter"); if (illegalNames.contains(name)) @@ -497,7 +498,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiObjectBinding *node) bool IRBuilder::visit(QQmlJS::AST::UiScriptBinding *node) { - appendBinding(node->qualifiedId, node->statement); + appendBinding(node->qualifiedId, node->statement, node); return false; } @@ -601,7 +602,7 @@ bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qu return false; QQmlJS::AST::SourceLocation loc; - QString error = obj->sanityCheckFunctionNames(illegalNames, &loc); + QString error = sanityCheckFunctionNames(obj, illegalNames, &loc); if (!error.isEmpty()) { recordError(loc, error); return false; @@ -957,7 +958,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) QQmlJS::AST::Node::accept(node->binding, this); } else if (node->statement) { if (!isRedundantNullInitializerForPropertyDeclaration(_propertyDeclaration, node->statement)) - appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement); + appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement, node); } qSwap(_propertyDeclaration, property); } @@ -971,26 +972,27 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) if (QQmlJS::AST::FunctionDeclaration *funDecl = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration *>(node->sourceElement)) { CompiledFunctionOrExpression *foe = New<CompiledFunctionOrExpression>(); foe->node = funDecl; + foe->parentNode = funDecl; foe->nameIndex = registerString(funDecl->name.toString()); foe->disableAcceleratedLookups = false; const int index = _object->functionsAndExpressions->append(foe); Function *f = New<Function>(); - f->functionDeclaration = funDecl; QQmlJS::AST::SourceLocation loc = funDecl->identifierToken; f->location.line = loc.startLine; f->location.column = loc.startColumn; f->index = index; f->nameIndex = registerString(funDecl->name.toString()); - int formalsCount = 0; - for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next) - ++formalsCount; + const QStringList formals = funDecl->formals ? funDecl->formals->formals() : QStringList(); + int formalsCount = formals.size(); f->formals.allocate(pool, formalsCount); int i = 0; - for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next, ++i) - f->formals[i] = registerString(it->name.toString()); + for (const QString &arg : formals) { + f->formals[i] = registerString(arg); + ++i; + } _object->appendFunction(f); } else { @@ -1044,7 +1046,7 @@ QStringRef IRBuilder::textRefAt(const QQmlJS::AST::SourceLocation &first, const return QStringRef(&sourceCode, first.offset, last.offset + last.length - first.offset); } -void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement) +void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, QQmlJS::AST::Node *parentNode) { QQmlJS::AST::SourceLocation loc = statement->firstSourceLocation(); binding->valueLocation.line = loc.startLine; @@ -1090,6 +1092,7 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST CompiledFunctionOrExpression *expr = New<CompiledFunctionOrExpression>(); expr->node = statement; + expr->parentNode = parentNode; expr->nameIndex = registerString(QLatin1String("expression for ") + stringAt(binding->propertyNameIndex)); expr->disableAcceleratedLookups = false; @@ -1216,7 +1219,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg } } -void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value) +void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode) { const QQmlJS::AST::SourceLocation qualifiedNameLocation = name->identifierToken; Object *object = nullptr; @@ -1227,7 +1230,7 @@ void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Sta return; } qSwap(_object, object); - appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), value); + appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), value, parentNode); qSwap(_object, object); } @@ -1242,7 +1245,8 @@ void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, qSwap(_object, object); } -void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value) +void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, + QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode) { Binding *binding = New<Binding>(); binding->propertyNameIndex = propertyNameIndex; @@ -1250,7 +1254,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo binding->location.line = nameLocation.startLine; binding->location.column = nameLocation.startColumn; binding->flags = 0; - setBindingValue(binding, value); + setBindingValue(binding, value, parentNode); QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false); if (!error.isEmpty()) { recordError(qualifiedNameLocation, error); @@ -1806,24 +1810,36 @@ void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject) QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions) { + auto qmlName = [&](const CompiledFunctionOrExpression &c) { + if (c.nameIndex != 0) + return stringPool->stringForIndex(c.nameIndex); + else + return QStringLiteral("%qml-expression-entry"); + }; QVector<int> runtimeFunctionIndices(functions.size()); - QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::GlobalCode); - scan.enterGlobalEnvironment(QV4::Compiler::QmlBinding); + QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::ContextType::Global); + scan.enterGlobalEnvironment(QV4::Compiler::ContextType::Binding); for (const CompiledFunctionOrExpression &f : functions) { Q_ASSERT(f.node != qmlRoot); + Q_ASSERT(f.parentNode && f.parentNode != qmlRoot); QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(f.node); - if (function) + if (function) { scan.enterQmlFunction(function); - else - scan.enterEnvironment(f.node, QV4::Compiler::QmlBinding); + } else { + Q_ASSERT(f.node != f.parentNode); + scan.enterEnvironment(f.parentNode, QV4::Compiler::ContextType::Binding, qmlName(f)); + } scan(function ? function->body : f.node); scan.leaveEnvironment(); } scan.leaveEnvironment(); + if (hasError) + return QVector<int>(); + _context = nullptr; for (int i = 0; i < functions.count(); ++i) { @@ -1836,15 +1852,13 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil QString name; if (function) name = function->name.toString(); - else if (qmlFunction.nameIndex != 0) - name = stringPool->stringForIndex(qmlFunction.nameIndex); else - name = QStringLiteral("%qml-expression-entry"); + name = qmlName(qmlFunction); - QQmlJS::AST::SourceElements *body; - if (function) - body = function->body ? function->body->elements : nullptr; - else { + QQmlJS::AST::StatementList *body; + if (function) { + body = function->body; + } else { // Synthesize source elements. QQmlJS::MemoryPool *pool = jsEngine->pool(); @@ -1854,13 +1868,12 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil QQmlJS::AST::ExpressionNode *expr = node->expressionCast(); stmt = new (pool) QQmlJS::AST::ExpressionStatement(expr); } - QQmlJS::AST::SourceElement *element = new (pool) QQmlJS::AST::StatementSourceElement(stmt); - body = new (pool) QQmlJS::AST::SourceElements(element); + body = new (pool) QQmlJS::AST::StatementList(stmt); body = body->finish(); } _disableAcceleratedLookups = qmlFunction.disableAcceleratedLookups; - int idx = defineFunction(name, node, + int idx = defineFunction(name, function ? function : qmlFunction.parentNode, function ? function->formals : nullptr, body); runtimeFunctionIndices[i] = idx; @@ -1869,7 +1882,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil return runtimeFunctionIndices; } -int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::SourceElements *body) +int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::StatementList *body) { int qmlContextTemp = -1; int importedScriptsTemp = -1; @@ -2190,7 +2203,7 @@ QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &n // Look for IDs first. for (const IdMapping &mapping : qAsConst(_idObjects)) { if (name == mapping.name) { - if (_context->compilationMode == QV4::Compiler::QmlBinding) + if (_context->contextType == QV4::Compiler::ContextType::Binding) _context->idObjectDependencies.insert(mapping.idIndex); Instruction::LoadIdObject load; @@ -2446,8 +2459,6 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO } } - QQmlJS::Engine *jsParserEngine = &output->jsParserEngine; - const quint32_le *functionIdx = serializedObject->functionOffsetTable(); for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) { QmlIR::Function *f = pool->New<QmlIR::Function>(); @@ -2458,26 +2469,10 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO f->location = compiledFunction->location; f->nameIndex = compiledFunction->nameIndex; - QQmlJS::AST::FormalParameterList *paramList = nullptr; - const quint32_le *formalNameIdx = compiledFunction->formalsTable(); - for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) { - const QString formal = unit->stringAt(*formalNameIdx); - QStringRef paramNameRef = jsParserEngine->newStringRef(formal); - - if (paramList) - paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, paramNameRef); - else - paramList = new (pool) QQmlJS::AST::FormalParameterList(paramNameRef); - } - - if (paramList) - paramList = paramList->finish(); - const QString name = unit->stringAt(compiledFunction->nameIndex); - f->functionDeclaration = new(pool) QQmlJS::AST::FunctionDeclaration(jsParserEngine->newStringRef(name), paramList, /*body*/nullptr); f->formals.allocate(pool, int(compiledFunction->nFormals)); - formalNameIdx = compiledFunction->formalsTable(); + const quint32_le *formalNameIdx = compiledFunction->formalsTable(); for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) f->formals[i] = *formalNameIdx; diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 689b232b1c..f8d481e14f 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -57,7 +57,6 @@ #include <private/qqmljsmemorypool_p.h> #include <private/qv4codegen_p.h> #include <private/qv4compiler_p.h> -#include <private/qqmljslexer_p.h> #include <QTextStream> #include <QCoreApplication> @@ -325,7 +324,6 @@ struct Alias : public QV4::CompiledData::Alias struct Function { - QQmlJS::AST::FunctionDeclaration *functionDeclaration; QV4::CompiledData::Location location; int nameIndex; quint32 index; // index in parsedQML::functions @@ -342,12 +340,9 @@ struct Function struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression { CompiledFunctionOrExpression() - {} - CompiledFunctionOrExpression(QQmlJS::AST::Node *n) - : node(n) - {} + QQmlJS::AST::Node *parentNode = nullptr; // FunctionDeclaration, Statement or Expression QQmlJS::AST::Node *node = nullptr; // FunctionDeclaration, Statement or Expression quint32 nameIndex = 0; bool disableAcceleratedLookups = false; @@ -400,8 +395,6 @@ public: void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &location = QQmlJS::AST::SourceLocation()); - QString sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation); - QString appendEnum(Enum *enumeration); QString appendSignal(Signal *signal); QString appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation); @@ -520,12 +513,12 @@ public: QStringRef textRefAt(const QQmlJS::AST::SourceLocation &first, const QQmlJS::AST::SourceLocation &last) const; - void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement); + void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, AST::Node *parentNode); void tryGeneratingTranslationBinding(const QStringRef &base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding); - void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value); + void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, AST::Node *parentNode); void appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false); - void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value); + void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value, AST::Node *parentNode); void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false); bool appendAlias(QQmlJS::AST::UiPublicMember *node); @@ -548,6 +541,8 @@ public: static bool isStatementNodeScript(QQmlJS::AST::Statement *statement); static bool isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement); + QString sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation); + QList<QQmlJS::DiagnosticMessage> errors; QSet<QString> illegalNames; @@ -578,7 +573,7 @@ private: #ifndef V4_BOOTSTRAP struct Q_QML_EXPORT PropertyResolver { - PropertyResolver(const QQmlPropertyCache *cache) + PropertyResolver(const QQmlRefPointer<QQmlPropertyCache> &cache) : cache(cache) {} @@ -597,7 +592,7 @@ struct Q_QML_EXPORT PropertyResolver // This code must match the semantics of QQmlPropertyPrivate::findSignalByName QQmlPropertyData *signal(const QString &name, bool *notInRevision) const; - const QQmlPropertyCache *cache; + QQmlRefPointer<QQmlPropertyCache> cache; }; #endif @@ -623,7 +618,7 @@ struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen int defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, - AST::SourceElements *body) override; + AST::StatementList *body) override; protected: void beginFunctionBodyHook() override; diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 8bbc8291b4..02517ea6bb 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -99,8 +99,8 @@ public: protected: QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context); - QQmlPropertyCache *propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const; - QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache); + QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const; + QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache); QString stringAt(int index) const { return objectContainer->stringAt(index); } @@ -152,7 +152,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex); auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); - QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache); if (error.isSet()) return error; @@ -166,7 +166,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje } } - QQmlPropertyCache *baseTypeCache; + QQmlRefPointer<QQmlPropertyCache> baseTypeCache; { QQmlCompileError error; baseTypeCache = propertyCacheForObject(obj, context, &error); @@ -209,7 +209,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje } template <typename ObjectContainer> -inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const +inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const { if (context.instantiatingProperty) { return context.instantiatingPropertyCache(enginePrivate); @@ -241,14 +241,12 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCac QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex); if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) { if (qmltype.isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); + QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); auto compilationUnit = tdata->compilationUnit(); qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - - tdata->release(); } } } @@ -264,7 +262,7 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCac } template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache) +inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache) { QQmlRefPointer<QQmlPropertyCache> cache; cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), @@ -314,7 +312,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj } } if (newClassName.isEmpty()) { - newClassName = QQmlMetaObject(baseTypeCache).className(); + newClassName = QQmlMetaObject(baseTypeCache.data()).className(); newClassName.append("_QML_"); newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); } @@ -354,7 +352,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj // 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; + QQmlPropertyCache *parentCache = cache.data(); while ((parentCache = parentCache->parent())) { if (int pSigCount = parentCache->signalCount()) { int pSigOffset = parentCache->signalOffset(); @@ -440,15 +438,13 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName)); if (qmltype.isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); + QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); auto compilationUnit = tdata->compilationUnit(); paramTypes[i + 1] = compilationUnit->metaTypeId; - - tdata->release(); } else { paramTypes[i + 1] = qmltype.typeId(); } @@ -523,7 +519,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj Q_ASSERT(qmltype.isValid()); if (qmltype.isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); + QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); @@ -534,8 +530,6 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj } else { propertyType = compilationUnit->listMetaTypeId; } - - tdata->release(); } else { if (p->type == QV4::CompiledData::Property::Custom) { propertyType = qmltype.typeId(); diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp index ffd3b5975a..afa2e4ad81 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -45,8 +45,9 @@ QT_BEGIN_NAMESPACE -QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit) +QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) : enginePrivate(enginePrivate) + , compilationUnit(compilationUnit) , imports(imports) , qmlUnit(compilationUnit->data) , resolvedTypes(compilationUnit->resolvedTypes) @@ -629,7 +630,7 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex); if (auto *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex)) { - QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + QQmlRefPointer<QQmlPropertyCache> cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); const QMetaObject *mo = cache->firstCppMetaObject(); QQmlType qmlType; while (mo && !qmlType.isValid()) { @@ -670,7 +671,7 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { return noError; } else if (QQmlValueTypeFactory::isValueType(property->propType())) { - return QQmlCompileError(binding->location, tr("Unexpected object assignment")); + return QQmlCompileError(binding->location, tr("Unexpected object assignment for property \"%1\"").arg(propertyName)); } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) { return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); } else { @@ -680,7 +681,7 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * // Using -1 for the minor version ensures that we get the raw metaObject. QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType(), -1); - // Will be true if the assgned type inherits propertyMetaObject + // Will be true if the assigned type inherits propertyMetaObject bool isAssignable = false; // Determine isAssignable value if (propertyMetaObject) { @@ -692,7 +693,8 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * } if (!isAssignable) { - return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to property")); + return QQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.") + .arg(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex)).arg(QLatin1String(QMetaType::typeName(property->propType())))); } } return noError; diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h index e37b8141f4..a8f75a0a7e 100644 --- a/src/qml/compiler/qqmlpropertyvalidator_p.h +++ b/src/qml/compiler/qqmlpropertyvalidator_p.h @@ -58,7 +58,7 @@ class QQmlPropertyValidator { Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) public: - QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit); + QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit); QVector<QQmlCompileError> validate(); @@ -74,6 +74,7 @@ private: QString stringAt(int index) const { return qmlUnit->stringAt(index); } QQmlEnginePrivate *enginePrivate; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; const QQmlImports &imports; const QV4::CompiledData::Unit *qmlUnit; const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index a896745b3f..37271e2c54 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -67,7 +67,7 @@ QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *type { } -QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() +QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile() { // Build property caches and VME meta object data @@ -147,7 +147,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() document->jsModule.fileName = typeData->urlString(); document->jsModule.finalUrl = typeData->finalUrlString(); QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine, - document->program, typeNameCache, &document->jsGenerator.stringTable, engine->v8engine()->illegalNames()); + document->program, typeNameCache.data(), &document->jsGenerator.stringTable, engine->v8engine()->illegalNames()); v4CodeGenerator.setUseFastLookups(false); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) @@ -165,7 +165,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() // The js unit owns the data and will free the qml unit. document->javaScriptCompilationUnit->data = qmlUnit; - QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = document->javaScriptCompilationUnit; compilationUnit = document->javaScriptCompilationUnit; compilationUnit->typeNameCache = typeNameCache; compilationUnit->resolvedTypes = resolvedTypes; @@ -339,14 +339,12 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio if (!type.isValid()) { if (imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr)) { if (type.isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(type.sourceUrl()); + QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(type.sourceUrl()); Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); auto compilationUnit = tdata->compilationUnit(); type = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - - tdata->release(); } } } @@ -466,14 +464,12 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio for (const QString ¶m : qAsConst(parameters)) { QStringRef paramNameRef = compiler->newStringRef(param); - if (paramList) - paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, paramNameRef); - else - paramList = new (pool) QQmlJS::AST::FormalParameterList(paramNameRef); + QQmlJS::AST::PatternElement *b = new (pool) QQmlJS::AST::PatternElement(paramNameRef, nullptr); + paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, b); } if (paramList) - paramList = paramList->finish(); + paramList = paramList->finish(pool); QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex); QQmlJS::AST::FunctionDeclaration *functionDeclaration = nullptr; @@ -490,11 +486,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio } if (!functionDeclaration) { QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node); - QQmlJS::AST::SourceElement *sourceElement = new (pool) QQmlJS::AST::StatementSourceElement(statement); - QQmlJS::AST::SourceElements *elements = new (pool) QQmlJS::AST::SourceElements(sourceElement); - elements = elements->finish(); - - QQmlJS::AST::FunctionBody *body = new (pool) QQmlJS::AST::FunctionBody(elements); + QQmlJS::AST::StatementList *body = new (pool) QQmlJS::AST::StatementList(statement); + body = body->finish(); functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body); functionDeclaration->lbraceToken = functionDeclaration->functionToken diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index b8eddcb9b2..537f87ab4c 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -92,7 +92,7 @@ public: QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypes; // --- - QV4::CompiledData::CompilationUnit *compile(); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile(); QList<QQmlError> compilationErrors() const { return errors; } void recordError(QQmlError error); diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp index 4d50654d27..7e1f49ee86 100644 --- a/src/qml/compiler/qv4bytecodegenerator.cpp +++ b/src/qml/compiler/qv4bytecodegenerator.cpp @@ -70,14 +70,14 @@ int BytecodeGenerator::newRegisterArray(int n) void BytecodeGenerator::packInstruction(I &i) { - uchar type = *reinterpret_cast<uchar *>(i.packed); - Q_ASSERT(type >= MOTH_NUM_INSTRUCTIONS()); - if (type >= MOTH_NUM_INSTRUCTIONS()) - type -= MOTH_NUM_INSTRUCTIONS(); + Instr::Type type = Instr::unpack(i.packed); + Q_ASSERT(int(type) < MOTH_NUM_INSTRUCTIONS()); + type = Instr::narrowInstructionType(type); int instructionsAsInts[sizeof(Instr)/sizeof(int)] = {}; int nMembers = Moth::InstrInfo::argumentCount[static_cast<int>(i.type)]; + uchar *code = i.packed + Instr::encodedLength(type); for (int j = 0; j < nMembers; ++j) { - instructionsAsInts[j] = qFromLittleEndian<qint32>(i.packed + 1 + j * sizeof(int)); + instructionsAsInts[j] = qFromLittleEndian<qint32>(code + j * sizeof(int)); } enum { Normal, @@ -89,11 +89,10 @@ void BytecodeGenerator::packInstruction(I &i) break; } } - char *code = i.packed; + code = i.packed; switch (width) { case Normal: - *reinterpret_cast<uchar *>(code) = type; - ++code; + code = Instr::pack(code, type); for (int n = 0; n < nMembers; ++n) { qint8 v = static_cast<qint8>(instructionsAsInts[n]); memcpy(code, &v, 1); @@ -122,7 +121,7 @@ void BytecodeGenerator::adjustJumpOffsets() // qDebug() << "adjusting jump offset for instruction" << index << i.position << i.size << "offsetForJump" << i.offsetForJump << "target" // << labels.at(i.linkedLabel) << linkedInstruction.position << "jumpOffset" << jumpOffset; uchar type = *reinterpret_cast<const uchar *>(i.packed); - if (type >= MOTH_NUM_INSTRUCTIONS()) { + if (Instr::isWide(Instr::Type(type))) { Q_ASSERT(i.offsetForJump == i.size - 4); qToLittleEndian<qint32>(jumpOffset, c); } else { @@ -177,7 +176,7 @@ void BytecodeGenerator::finalize(Compiler::Context *context) entry.line = currentLine; lineNumbers.append(entry); } - code.append(i.packed, i.size); + code.append(reinterpret_cast<const char *>(i.packed), i.size); } context->code = code; @@ -185,6 +184,25 @@ void BytecodeGenerator::finalize(Compiler::Context *context) } int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) { + if (lastInstrType == int(Instr::Type::StoreReg)) { + if (type == Instr::Type::LoadReg) { + if (i.LoadReg.reg == lastInstr.StoreReg.reg) { + // value is already in the accumulator + return -1; + } + } + if (type == Instr::Type::MoveReg) { + if (i.MoveReg.srcReg == lastInstr.StoreReg.reg) { + Instruction::StoreReg store; + store.reg = i.MoveReg.destReg; + addInstruction(store); + return -1; + } + } + } + lastInstrType = int(type); + lastInstr = i; + #if QT_CONFIG(qml_debug) if (debugMode && type != Instr::Type::Debug) { QT_WARNING_PUSH @@ -207,12 +225,10 @@ QT_WARNING_POP const int argCount = Moth::InstrInfo::argumentCount[static_cast<int>(type)]; int s = argCount*sizeof(int); if (offsetOfOffset != -1) - offsetOfOffset += 1; - I instr{type, static_cast<short>(s + 1), 0, currentLine, offsetOfOffset, -1, "\0\0" }; - char *code = instr.packed; - *reinterpret_cast<uchar *>(code) = static_cast<uchar>(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type)); - ++code; - Q_ASSERT(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type) < 256); + offsetOfOffset += Instr::encodedLength(type); + I instr{type, static_cast<short>(s + Instr::encodedLength(type)), 0, currentLine, offsetOfOffset, -1, "\0\0" }; + uchar *code = instr.packed; + code = Instr::pack(code, Instr::wideInstructionType(type)); for (int j = 0; j < argCount; ++j) { qToLittleEndian<qint32>(i.argumentsAsInts[j], code); diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index e69f2cd310..dca5771356 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -77,22 +77,18 @@ public: Label(BytecodeGenerator *generator, LinkMode mode = LinkNow) : generator(generator), index(generator->labels.size()) { - generator->labels.append(mode == LinkNow ? generator->instructions.size() : -1); - } - static Label returnLabel() { - Label l; - l.index = INT_MAX; - return l; - } - bool isReturn() const { - return index == INT_MAX; + generator->labels.append(-1); + if (mode == LinkNow) + link(); } void link() { Q_ASSERT(index >= 0); Q_ASSERT(generator->labels[index] == -1); generator->labels[index] = generator->instructions.size(); + generator->clearLastInstruction(); } + bool isValid() const { return generator != nullptr; } BytecodeGenerator *generator = nullptr; int index = -1; @@ -133,14 +129,16 @@ public: }; struct ExceptionHandler : public Label { + ExceptionHandler() = default; ExceptionHandler(BytecodeGenerator *generator) : Label(generator, LinkLater) { } ~ExceptionHandler() { - Q_ASSERT(generator->currentExceptionHandler != this); + Q_ASSERT(!generator || generator->currentExceptionHandler != this); } + bool isValid() const { return generator != nullptr; } }; Label label() { @@ -181,6 +179,18 @@ public: return addJumpInstruction(data); } + Q_REQUIRED_RESULT Jump jumpNotUndefined() + { + Instruction::JumpNotUndefined data; + return addJumpInstruction(data); + } + + Q_REQUIRED_RESULT Jump jumpNoException() + { + Instruction::JumpNoException data; + return addJumpInstruction(data); + } + void jumpStrictEqual(const StackSlot &lhs, const Label &target) { Instruction::CmpStrictEqual cmp; @@ -197,26 +207,10 @@ public: addJumpInstruction(Instruction::JumpTrue()).link(target); } - Q_REQUIRED_RESULT Jump jumpStrictEqualStackSlotInt(const StackSlot &lhs, int rhs) - { - Instruction::JumpStrictEqualStackSlotInt data; - data.lhs = lhs; - data.rhs = rhs; - return addJumpInstruction(data); - } - - Q_REQUIRED_RESULT Jump jumpStrictNotEqualStackSlotInt(const StackSlot &lhs, int rhs) - { - Instruction::JumpStrictNotEqualStackSlotInt data; - data.lhs = lhs; - data.rhs = rhs; - return addJumpInstruction(data); - } - - void setExceptionHandler(ExceptionHandler *handler) + void setUnwindHandler(ExceptionHandler *handler) { currentExceptionHandler = handler; - Instruction::SetExceptionHandler data; + Instruction::SetUnwindHandler data; data.offset = 0; if (!handler) addInstruction(data); @@ -224,6 +218,19 @@ public: addJumpInstruction(data).link(*handler); } + void unwindToLabel(int level, const Label &target) + { + if (level) { + Instruction::UnwindToLabel unwind; + unwind.level = level; + addJumpInstruction(unwind).link(target); + } else { + jump().link(target); + } + } + + + void setLocation(const QQmlJS::AST::SourceLocation &loc); ExceptionHandler *exceptionHandler() const { @@ -233,6 +240,7 @@ public: int newRegister(); int newRegisterArray(int n); int registerCount() const { return regCount; } + int currentRegister() const { return currentReg; } void finalize(Compiler::Context *context); @@ -252,6 +260,11 @@ public: addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); } + void clearLastInstruction() + { + lastInstrType = -1; + } + private: friend struct Jump; friend struct Label; @@ -266,7 +279,7 @@ private: int line; int offsetForJump; int linkedLabel; - char packed[sizeof(Instr) + 2]; // 2 for instruction and prefix + unsigned char packed[sizeof(Instr) + 2]; // 2 for instruction type }; void compressInstructions(); @@ -275,7 +288,7 @@ private: QVector<I> instructions; QVector<int> labels; - ExceptionHandler *currentExceptionHandler; + ExceptionHandler *currentExceptionHandler = nullptr; int regCount = 0; public: int currentReg = 0; @@ -283,6 +296,9 @@ private: int startLine = 0; int currentLine = 0; bool debugMode = false; + + int lastInstrType = -1; + Moth::Instr lastInstr; }; } diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp new file mode 100644 index 0000000000..23f7051718 --- /dev/null +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -0,0 +1,529 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qv4bytecodehandler_p.h> + +QT_USE_NAMESPACE +using namespace QV4; +using namespace Moth; + +ByteCodeHandler::~ByteCodeHandler() +{ +} + +#define DISPATCH_INSTRUCTION(name, nargs, ...) \ + generate_##name( \ + __VA_ARGS__ \ + ); + +#define DECODE_AND_DISPATCH(instr) \ + { \ + INSTR_##instr(MOTH_DECODE_WITH_BASE) \ + Q_UNUSED(base_ptr); \ + startInstruction(Instr::Type::instr); \ + _offset = code - start; \ + INSTR_##instr(DISPATCH) \ + endInstruction(Instr::Type::instr); \ + continue; \ + } + +void ByteCodeHandler::decode(const char *code, uint len) +{ + MOTH_JUMP_TABLE; + + const char *start = code; + const char *end = code + len; + while (code < end) { + MOTH_DISPATCH() + + FOR_EACH_MOTH_INSTR(DECODE_AND_DISPATCH) + } +} + +#undef DECODE_AND_DISPATCH +#undef DISPATCH_INSTRUCTION + +#define MOTH_UNUSED_ARGS0() +#define MOTH_UNUSED_ARGS1(arg) \ + Q_UNUSED(arg); +#define MOTH_UNUSED_ARGS2(arg1, arg2) \ + Q_UNUSED(arg1); \ + Q_UNUSED(arg2); +#define MOTH_UNUSED_ARGS3(arg1, arg2, arg3) \ + Q_UNUSED(arg1); \ + Q_UNUSED(arg2); \ + Q_UNUSED(arg3); +#define MOTH_UNUSED_ARGS4(arg1, arg2, arg3, arg4) \ + Q_UNUSED(arg1); \ + Q_UNUSED(arg2); \ + Q_UNUSED(arg3); \ + Q_UNUSED(arg4); + +#define MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, ...) \ + MOTH_EXPAND_FOR_MSVC(MOTH_UNUSED_ARGS##nargs(__VA_ARGS__)) + +#define MOTH_MARK_ARGS_UNUSED_INSTRUCTION(name, nargs, ...) \ + MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, __VA_ARGS__) + +#define COLLECTOR_BEGIN_INSTR(instr) \ + { \ + INSTR_##instr(MOTH_DECODE_WITH_BASE) \ + INSTR_##instr(MOTH_MARK_ARGS_UNUSED) \ + Q_UNUSED(base_ptr); + +#define COLLECTOR_END_INSTR(instr) \ + continue; \ + } + +std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint len) +{ + MOTH_JUMP_TABLE; + + std::vector<int> labels; + + const auto addLabel = [&labels,len](int offset) { + Q_ASSERT(offset >= 0 && offset < static_cast<int>(len)); + labels.push_back(offset); + }; + + const char *start = code; + const char *end = code + len; + while (code < end) { + MOTH_DISPATCH() + Q_UNREACHABLE(); + + COLLECTOR_BEGIN_INSTR(LoadReg) + COLLECTOR_END_INSTR(LoadReg) + + COLLECTOR_BEGIN_INSTR(StoreReg) + COLLECTOR_END_INSTR(StoreReg) + + COLLECTOR_BEGIN_INSTR(MoveReg) + COLLECTOR_END_INSTR(MoveReg) + + COLLECTOR_BEGIN_INSTR(LoadConst) + COLLECTOR_END_INSTR(LoadConst) + + COLLECTOR_BEGIN_INSTR(LoadNull) + COLLECTOR_END_INSTR(LoadNull) + + COLLECTOR_BEGIN_INSTR(LoadZero) + COLLECTOR_END_INSTR(LoadZero) + + COLLECTOR_BEGIN_INSTR(LoadTrue) + COLLECTOR_END_INSTR(LoadTrue) + + COLLECTOR_BEGIN_INSTR(LoadFalse) + COLLECTOR_END_INSTR(LoadFalse) + + COLLECTOR_BEGIN_INSTR(LoadUndefined) + COLLECTOR_END_INSTR(LoadUndefined) + + COLLECTOR_BEGIN_INSTR(LoadInt) + COLLECTOR_END_INSTR(LoadInt) + + COLLECTOR_BEGIN_INSTR(MoveConst) + COLLECTOR_END_INSTR(MoveConst) + + COLLECTOR_BEGIN_INSTR(LoadLocal) + COLLECTOR_END_INSTR(LoadLocal) + + COLLECTOR_BEGIN_INSTR(StoreLocal) + COLLECTOR_END_INSTR(StoreLocal) + + COLLECTOR_BEGIN_INSTR(LoadScopedLocal) + COLLECTOR_END_INSTR(LoadScopedLocal) + + COLLECTOR_BEGIN_INSTR(StoreScopedLocal) + COLLECTOR_END_INSTR(StoreScopedLocal) + + COLLECTOR_BEGIN_INSTR(LoadRuntimeString) + COLLECTOR_END_INSTR(LoadRuntimeString) + + COLLECTOR_BEGIN_INSTR(MoveRegExp) + COLLECTOR_END_INSTR(MoveRegExp) + + COLLECTOR_BEGIN_INSTR(LoadClosure) + COLLECTOR_END_INSTR(LoadClosure) + + COLLECTOR_BEGIN_INSTR(LoadName) + COLLECTOR_END_INSTR(LoadName) + + COLLECTOR_BEGIN_INSTR(LoadGlobalLookup) + COLLECTOR_END_INSTR(LoadGlobalLookup) + + COLLECTOR_BEGIN_INSTR(StoreNameSloppy) + COLLECTOR_END_INSTR(StoreNameSloppy) + + COLLECTOR_BEGIN_INSTR(StoreNameStrict) + COLLECTOR_END_INSTR(StoreNameStrict) + + COLLECTOR_BEGIN_INSTR(LoadElement) + COLLECTOR_END_INSTR(LoadElement) + + COLLECTOR_BEGIN_INSTR(StoreElement) + COLLECTOR_END_INSTR(StoreElement) + + COLLECTOR_BEGIN_INSTR(LoadProperty) + COLLECTOR_END_INSTR(LoadProperty) + + COLLECTOR_BEGIN_INSTR(GetLookup) + COLLECTOR_END_INSTR(GetLookup) + + COLLECTOR_BEGIN_INSTR(StoreProperty) + COLLECTOR_END_INSTR(StoreProperty) + + COLLECTOR_BEGIN_INSTR(SetLookup) + COLLECTOR_END_INSTR(SetLookup) + + COLLECTOR_BEGIN_INSTR(LoadSuperProperty) + COLLECTOR_END_INSTR(LoadSuperProperty) + + COLLECTOR_BEGIN_INSTR(StoreSuperProperty) + COLLECTOR_END_INSTR(StoreSuperProperty) + + COLLECTOR_BEGIN_INSTR(StoreScopeObjectProperty) + COLLECTOR_END_INSTR(StoreScopeObjectProperty) + + COLLECTOR_BEGIN_INSTR(LoadScopeObjectProperty) + COLLECTOR_END_INSTR(LoadScopeObjectProperty) + + COLLECTOR_BEGIN_INSTR(StoreContextObjectProperty) + COLLECTOR_END_INSTR(StoreContextObjectProperty) + + COLLECTOR_BEGIN_INSTR(LoadContextObjectProperty) + COLLECTOR_END_INSTR(LoadContextObjectProperty) + + COLLECTOR_BEGIN_INSTR(LoadIdObject) + COLLECTOR_END_INSTR(LoadIdObject) + + COLLECTOR_BEGIN_INSTR(Yield) + COLLECTOR_END_INSTR(Yield) + + COLLECTOR_BEGIN_INSTR(Resume) + COLLECTOR_END_INSTR(Resume) + + COLLECTOR_BEGIN_INSTR(CallValue) + COLLECTOR_END_INSTR(CallValue) + + COLLECTOR_BEGIN_INSTR(CallProperty) + COLLECTOR_END_INSTR(CallProperty) + + COLLECTOR_BEGIN_INSTR(CallPropertyLookup) + COLLECTOR_END_INSTR(CallPropertyLookup) + + COLLECTOR_BEGIN_INSTR(CallElement) + COLLECTOR_END_INSTR(CallElement) + + COLLECTOR_BEGIN_INSTR(CallName) + COLLECTOR_END_INSTR(CallName) + + COLLECTOR_BEGIN_INSTR(CallPossiblyDirectEval) + COLLECTOR_END_INSTR(CallPossiblyDirectEval) + + COLLECTOR_BEGIN_INSTR(CallGlobalLookup) + COLLECTOR_END_INSTR(CallGlobalLookup) + + COLLECTOR_BEGIN_INSTR(CallScopeObjectProperty) + COLLECTOR_END_INSTR(CallScopeObjectProperty) + + COLLECTOR_BEGIN_INSTR(CallContextObjectProperty) + COLLECTOR_END_INSTR(CallContextObjectProperty) + + COLLECTOR_BEGIN_INSTR(CallWithSpread) + COLLECTOR_END_INSTR(CallWithSpread) + + COLLECTOR_BEGIN_INSTR(Construct) + COLLECTOR_END_INSTR(Construct) + + COLLECTOR_BEGIN_INSTR(ConstructWithSpread) + COLLECTOR_END_INSTR(ConstructWithSpread) + + COLLECTOR_BEGIN_INSTR(SetUnwindHandler) + addLabel(code - start + offset); + COLLECTOR_END_INSTR(SetUnwindHandler) + + COLLECTOR_BEGIN_INSTR(UnwindDispatch) + COLLECTOR_END_INSTR(UnwindDispatch) + + COLLECTOR_BEGIN_INSTR(UnwindToLabel) + addLabel(code - start + offset); + COLLECTOR_END_INSTR(UnwindToLabel) + + COLLECTOR_BEGIN_INSTR(ThrowException) + COLLECTOR_END_INSTR(ThrowException) + + COLLECTOR_BEGIN_INSTR(GetException) + COLLECTOR_END_INSTR(HasException) + + COLLECTOR_BEGIN_INSTR(SetException) + COLLECTOR_END_INSTR(SetExceptionFlag) + + COLLECTOR_BEGIN_INSTR(CreateCallContext) + COLLECTOR_END_INSTR(CreateCallContext) + + COLLECTOR_BEGIN_INSTR(PushCatchContext) + COLLECTOR_END_INSTR(PushCatchContext) + + COLLECTOR_BEGIN_INSTR(PushWithContext) + COLLECTOR_END_INSTR(PushWithContext) + + COLLECTOR_BEGIN_INSTR(PushBlockContext) + COLLECTOR_END_INSTR(PushBlockContext) + + COLLECTOR_BEGIN_INSTR(CloneBlockContext) + COLLECTOR_END_INSTR(CloneBlockContext) + + COLLECTOR_BEGIN_INSTR(PushScriptContext) + COLLECTOR_END_INSTR(PushScriptContext) + + COLLECTOR_BEGIN_INSTR(PopScriptContext) + COLLECTOR_END_INSTR(PopScriptContext) + + COLLECTOR_BEGIN_INSTR(PopContext) + COLLECTOR_END_INSTR(PopContext) + + COLLECTOR_BEGIN_INSTR(GetIterator) + COLLECTOR_END_INSTR(GetIterator) + + COLLECTOR_BEGIN_INSTR(IteratorNext) + COLLECTOR_END_INSTR(IteratorNext) + + COLLECTOR_BEGIN_INSTR(IteratorClose) + COLLECTOR_END_INSTR(IteratorClose) + + COLLECTOR_BEGIN_INSTR(DestructureRestElement) + COLLECTOR_END_INSTR(DestructureRestElement) + + COLLECTOR_BEGIN_INSTR(DeleteProperty) + COLLECTOR_END_INSTR(DeleteProperty) + + COLLECTOR_BEGIN_INSTR(DeleteName) + COLLECTOR_END_INSTR(DeleteName) + + COLLECTOR_BEGIN_INSTR(TypeofName) + COLLECTOR_END_INSTR(TypeofName) + + COLLECTOR_BEGIN_INSTR(TypeofValue) + COLLECTOR_END_INSTR(TypeofValue) + + COLLECTOR_BEGIN_INSTR(DeclareVar) + COLLECTOR_END_INSTR(DeclareVar) + + COLLECTOR_BEGIN_INSTR(DefineArray) + COLLECTOR_END_INSTR(DefineArray) + + COLLECTOR_BEGIN_INSTR(DefineObjectLiteral) + COLLECTOR_END_INSTR(DefineObjectLiteral) + + COLLECTOR_BEGIN_INSTR(CreateClass) + COLLECTOR_END_INSTR(CreateClass) + + COLLECTOR_BEGIN_INSTR(CreateMappedArgumentsObject) + COLLECTOR_END_INSTR(CreateMappedArgumentsObject) + + COLLECTOR_BEGIN_INSTR(CreateUnmappedArgumentsObject) + COLLECTOR_END_INSTR(CreateUnmappedArgumentsObject) + + COLLECTOR_BEGIN_INSTR(CreateRestParameter) + COLLECTOR_END_INSTR(CreateRestParameter) + + COLLECTOR_BEGIN_INSTR(ConvertThisToObject) + COLLECTOR_END_INSTR(ConvertThisToObject) + + COLLECTOR_BEGIN_INSTR(LoadSuperConstructor) + COLLECTOR_END_INSTR(LoadSuperConstructor) + + COLLECTOR_BEGIN_INSTR(ToObject) + COLLECTOR_END_INSTR(ToObject) + + COLLECTOR_BEGIN_INSTR(Jump) + addLabel(code - start + offset); + COLLECTOR_END_INSTR(Jump) + + COLLECTOR_BEGIN_INSTR(JumpTrue) + addLabel(code - start + offset); + COLLECTOR_END_INSTR(JumpTrue) + + COLLECTOR_BEGIN_INSTR(JumpFalse) + addLabel(code - start + offset); + COLLECTOR_END_INSTR(JumpFalse) + + COLLECTOR_BEGIN_INSTR(JumpNoException) + addLabel(code - start + offset); + COLLECTOR_END_INSTR(JumpNoException) + + COLLECTOR_BEGIN_INSTR(JumpNotUndefined) + addLabel(code - start + offset); + COLLECTOR_END_INSTR(JumpNotUndefined) + + COLLECTOR_BEGIN_INSTR(CmpEqNull) + COLLECTOR_END_INSTR(CmpEqNull) + + COLLECTOR_BEGIN_INSTR(CmpNeNull) + COLLECTOR_END_INSTR(CmpNeNull) + + COLLECTOR_BEGIN_INSTR(CmpEqInt) + COLLECTOR_END_INSTR(CmpEq) + + COLLECTOR_BEGIN_INSTR(CmpNeInt) + COLLECTOR_END_INSTR(CmpNeInt) + + COLLECTOR_BEGIN_INSTR(CmpEq) + COLLECTOR_END_INSTR(CmpEq) + + COLLECTOR_BEGIN_INSTR(CmpNe) + COLLECTOR_END_INSTR(CmpNe) + + COLLECTOR_BEGIN_INSTR(CmpGt) + COLLECTOR_END_INSTR(CmpGt) + + COLLECTOR_BEGIN_INSTR(CmpGe) + COLLECTOR_END_INSTR(CmpGe) + + COLLECTOR_BEGIN_INSTR(CmpLt) + COLLECTOR_END_INSTR(CmpLt) + + COLLECTOR_BEGIN_INSTR(CmpLe) + COLLECTOR_END_INSTR(CmpLe) + + COLLECTOR_BEGIN_INSTR(CmpStrictEqual) + COLLECTOR_END_INSTR(CmpStrictEqual) + + COLLECTOR_BEGIN_INSTR(CmpStrictNotEqual) + COLLECTOR_END_INSTR(CmpStrictNotEqual) + + COLLECTOR_BEGIN_INSTR(CmpIn) + COLLECTOR_END_INSTR(CmpIn) + + COLLECTOR_BEGIN_INSTR(CmpInstanceOf) + COLLECTOR_END_INSTR(CmpInstanceOf) + + COLLECTOR_BEGIN_INSTR(UNot) + COLLECTOR_END_INSTR(UNot) + + COLLECTOR_BEGIN_INSTR(UPlus) + COLLECTOR_END_INSTR(UPlus) + + COLLECTOR_BEGIN_INSTR(UMinus) + COLLECTOR_END_INSTR(UMinus) + + COLLECTOR_BEGIN_INSTR(UCompl) + COLLECTOR_END_INSTR(UCompl) + + COLLECTOR_BEGIN_INSTR(Increment) + COLLECTOR_END_INSTR(PreIncrement) + + COLLECTOR_BEGIN_INSTR(Decrement) + COLLECTOR_END_INSTR(PreDecrement) + + COLLECTOR_BEGIN_INSTR(Add) + COLLECTOR_END_INSTR(Add) + + COLLECTOR_BEGIN_INSTR(BitAnd) + COLLECTOR_END_INSTR(BitAnd) + + COLLECTOR_BEGIN_INSTR(BitOr) + COLLECTOR_END_INSTR(BitOr) + + COLLECTOR_BEGIN_INSTR(BitXor) + COLLECTOR_END_INSTR(BitXor) + + COLLECTOR_BEGIN_INSTR(UShr) + COLLECTOR_END_INSTR(UShr) + + COLLECTOR_BEGIN_INSTR(Shr) + COLLECTOR_END_INSTR(Shr) + + COLLECTOR_BEGIN_INSTR(Shl) + COLLECTOR_END_INSTR(Shl) + + COLLECTOR_BEGIN_INSTR(BitAndConst) + COLLECTOR_END_INSTR(BitAndConst) + + COLLECTOR_BEGIN_INSTR(BitOrConst) + COLLECTOR_END_INSTR(BitOr) + + COLLECTOR_BEGIN_INSTR(BitXorConst) + COLLECTOR_END_INSTR(BitXor) + + COLLECTOR_BEGIN_INSTR(UShrConst) + COLLECTOR_END_INSTR(UShrConst) + + COLLECTOR_BEGIN_INSTR(ShrConst) + COLLECTOR_END_INSTR(ShrConst) + + COLLECTOR_BEGIN_INSTR(ShlConst) + COLLECTOR_END_INSTR(ShlConst) + + COLLECTOR_BEGIN_INSTR(Exp) + COLLECTOR_END_INSTR(Exp) + + COLLECTOR_BEGIN_INSTR(Mul) + COLLECTOR_END_INSTR(Mul) + + COLLECTOR_BEGIN_INSTR(Div) + COLLECTOR_END_INSTR(Div) + + COLLECTOR_BEGIN_INSTR(Mod) + COLLECTOR_END_INSTR(Mod) + + COLLECTOR_BEGIN_INSTR(Sub) + COLLECTOR_END_INSTR(Sub) + + COLLECTOR_BEGIN_INSTR(Ret) + COLLECTOR_END_INSTR(Ret) + +#ifndef QT_NO_QML_DEBUGGER + COLLECTOR_BEGIN_INSTR(Debug) + COLLECTOR_END_INSTR(Debug) +#endif // QT_NO_QML_DEBUGGER + + COLLECTOR_BEGIN_INSTR(LoadQmlContext) + COLLECTOR_END_INSTR(LoadQmlContext) + + COLLECTOR_BEGIN_INSTR(LoadQmlImportedScripts) + COLLECTOR_END_INSTR(LoadQmlImportedScripts) + } + + return labels; +} + +#undef COLLECTOR_BEGIN_INSTR +#undef COLLECTOR_END_INSTR diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h new file mode 100644 index 0000000000..5f1121306f --- /dev/null +++ b/src/qml/compiler/qv4bytecodehandler_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4BYTECODEHANDLER_P_H +#define QV4BYTECODEHANDLER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +#include <private/qv4instr_moth_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace Moth { + +#define BYTECODE_HANDLER_DEFINE_ARGS(nargs, ...) \ + MOTH_EXPAND_FOR_MSVC(BYTECODE_HANDLER_DEFINE_ARGS##nargs(__VA_ARGS__)) + +#define BYTECODE_HANDLER_DEFINE_ARGS0() +#define BYTECODE_HANDLER_DEFINE_ARGS1(arg) \ + int arg +#define BYTECODE_HANDLER_DEFINE_ARGS2(arg1, arg2) \ + int arg1, \ + int arg2 +#define BYTECODE_HANDLER_DEFINE_ARGS3(arg1, arg2, arg3) \ + int arg1, \ + int arg2, \ + int arg3 +#define BYTECODE_HANDLER_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \ + int arg1, \ + int arg2, \ + int arg3, \ + int arg4 + +#define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER_INSTRUCTION(name, nargs, ...) \ + virtual void generate_##name( \ + BYTECODE_HANDLER_DEFINE_ARGS(nargs, __VA_ARGS__) \ + ) = 0; + +#define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \ + INSTR_##instr(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER) + +class ByteCodeHandler +{ +public: + virtual ~ByteCodeHandler(); + + void decode(const char *code, uint len); + + int instructionOffset() const { return _offset; } + + static std::vector<int> collectLabelsInBytecode(const char *code, uint len); + +protected: + FOR_EACH_MOTH_INSTR(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER) + + virtual void startInstruction(Moth::Instr::Type instr) = 0; + virtual void endInstruction(Moth::Instr::Type instr) = 0; + +private: + int _offset = 0; +}; + +} // Moth namespace +} // QV4 namespace + +QT_END_NAMESPACE + +#endif // QV4BYTECODEHANDLER_P_H diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index e831fd48f5..4b766accbd 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -58,6 +58,8 @@ #include <cmath> #include <iostream> +static const bool disable_lookups = false; + #ifdef CONST #undef CONST #endif @@ -77,8 +79,6 @@ static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGene case Statement::Kind_ForEachStatement: case Statement::Kind_ForStatement: case Statement::Kind_IfStatement: - case Statement::Kind_LocalForEachStatement: - case Statement::Kind_LocalForStatement: case Statement::Kind_WhileStatement: bytecodeGenerator->setLocation(fallback); break; @@ -90,7 +90,7 @@ static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGene Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) : _module(nullptr) - , _returnAddress(0) + , _returnAddress(-1) , _context(nullptr) , _labelledStatement(nullptr) , jsUnitGenerator(jsUnitGenerator) @@ -106,7 +106,7 @@ void Codegen::generateFromProgram(const QString &fileName, const QString &sourceCode, Program *node, Module *module, - CompilationMode mode) + ContextType contextType) { Q_ASSERT(node); @@ -117,10 +117,13 @@ void Codegen::generateFromProgram(const QString &fileName, _module->fileName = fileName; _module->finalUrl = finalUrl; - ScanFunctions scan(this, sourceCode, mode); + ScanFunctions scan(this, sourceCode, contextType); scan(node); - defineFunction(QStringLiteral("%entry"), node, nullptr, node->elements); + if (hasError) + return; + + defineFunction(QStringLiteral("%entry"), node, nullptr, node->statements); } void Codegen::enterContext(Node *node) @@ -132,19 +135,24 @@ void Codegen::enterContext(Node *node) int Codegen::leaveContext() { Q_ASSERT(_context); - Q_ASSERT(!_context->controlFlow); int functionIndex = _context->functionIndex; _context = _context->parent; return functionIndex; } +Context *Codegen::enterBlock(Node *node) +{ + enterContext(node); + return _context; +} + Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) { if (hasError) return _expr.result(); #ifndef V4_BOOTSTRAP - if (expr.isConst()) { + if (expr.isConstant()) { auto v = Value::fromReturnedValue(expr.constant); if (v.isNumber()) { switch (op) { @@ -267,9 +275,9 @@ void Codegen::statement(Statement *ast) bytecodeGenerator->setLocation(ast->firstSourceLocation()); VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); - qSwap(_volataleMemoryLocations, vLocs); + qSwap(_volatileMemoryLocations, vLocs); accept(ast); - qSwap(_volataleMemoryLocations, vLocs); + qSwap(_volatileMemoryLocations, vLocs); } void Codegen::statement(ExpressionNode *ast) @@ -282,11 +290,11 @@ void Codegen::statement(ExpressionNode *ast) Result r(nx); qSwap(_expr, r); VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); - qSwap(_volataleMemoryLocations, vLocs); + qSwap(_volatileMemoryLocations, vLocs); accept(ast); - qSwap(_volataleMemoryLocations, vLocs); + qSwap(_volatileMemoryLocations, vLocs); qSwap(_expr, r); if (hasError) @@ -337,60 +345,103 @@ Codegen::Reference Codegen::expression(ExpressionNode *ast) return r.result(); } -Codegen::Result Codegen::sourceElement(SourceElement *ast) +void Codegen::program(Program *ast) { - Result r(nx); if (ast) { - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); + statementList(ast->statements); } - return r; } -void Codegen::functionBody(FunctionBody *ast) -{ - if (ast) - sourceElements(ast->elements); -} +enum class CompletionState { + Empty, + EmptyAbrupt, + NonEmpty +}; -void Codegen::program(Program *ast) -{ - if (ast) { - sourceElements(ast->elements); +static CompletionState completionState(StatementList *list) +{ + for (StatementList *it = list; it; it = it->next) { + if (it->statement->kind == Statement::Kind_BreakStatement || + it->statement->kind == Statement::Kind_ContinueStatement) + return CompletionState::EmptyAbrupt; + if (it->statement->kind == Statement::Kind_EmptyStatement || + it->statement->kind == Statement::Kind_VariableDeclaration || + it->statement->kind == Statement::Kind_FunctionDeclaration) + continue; + if (it->statement->kind == Statement::Kind_Block) { + CompletionState subState = completionState(static_cast<Block *>(it->statement)->statements); + if (subState != CompletionState::Empty) + return subState; + continue; + } + return CompletionState::NonEmpty; } + return CompletionState::Empty; } -void Codegen::sourceElements(SourceElements *ast) +static Node *completionStatement(StatementList *list) { - bool _requiresReturnValue = false; - qSwap(_requiresReturnValue, requiresReturnValue); - for (SourceElements *it = ast; it; it = it->next) { - if (!it->next) - qSwap(_requiresReturnValue, requiresReturnValue); - sourceElement(it->element); - if (hasError) - return; - if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(it->element)) { - if (AST::cast<ThrowStatement *>(sse->statement) || - AST::cast<ReturnStatement *>(sse->statement)) - return; + Node *completionStatement = nullptr; + for (StatementList *it = list; it; it = it->next) { + if (it->statement->kind == Statement::Kind_BreakStatement || + it->statement->kind == Statement::Kind_ContinueStatement) + return completionStatement; + if (it->statement->kind == Statement::Kind_ThrowStatement || + it->statement->kind == Statement::Kind_ReturnStatement) + return it->statement; + if (it->statement->kind == Statement::Kind_EmptyStatement || + it->statement->kind == Statement::Kind_VariableStatement || + it->statement->kind == Statement::Kind_FunctionDeclaration) + continue; + if (it->statement->kind == Statement::Kind_Block) { + CompletionState state = completionState(static_cast<Block *>(it->statement)->statements); + switch (state) { + case CompletionState::Empty: + continue; + case CompletionState::EmptyAbrupt: + return it->statement; + case CompletionState::NonEmpty: + break; + } } + completionStatement = it->statement; } + return completionStatement; } void Codegen::statementList(StatementList *ast) { + if (!ast) + return; + bool _requiresReturnValue = requiresReturnValue; - requiresReturnValue = false; - for (StatementList *it = ast; it; it = it->next) { - if (!it->next || - it->next->statement->kind == Statement::Kind_BreakStatement || - it->next->statement->kind == Statement::Kind_ContinueStatement || - it->next->statement->kind == Statement::Kind_ReturnStatement) - requiresReturnValue = _requiresReturnValue; - statement(it->statement); + // ### the next line is pessimizing a bit too much, as there are many cases, where the complietion from the break + // statement will not be used, but it's at least spec compliant + if (!controlFlow || !controlFlow->hasLoop()) requiresReturnValue = false; + + Node *needsCompletion = nullptr; + + if (_requiresReturnValue && !requiresReturnValue) + needsCompletion = completionStatement(ast); + + if (requiresReturnValue && !needsCompletion && !insideSwitch) { + // break or continue is the first real statement, set the return value to undefined + Reference::fromConst(this, Encode::undefined()).storeOnStack(_returnAddress); + } + + bool _insideSwitch = insideSwitch; + insideSwitch = false; + + for (StatementList *it = ast; it; it = it->next) { + if (it->statement == needsCompletion) + requiresReturnValue = true; + if (Statement *s = it->statement->statementCast()) + statement(s); + else + statement(static_cast<ExpressionNode *>(it->statement)); + if (it->statement == needsCompletion) + requiresReturnValue = false; if (it->statement->kind == Statement::Kind_ThrowStatement || it->statement->kind == Statement::Kind_BreakStatement || it->statement->kind == Statement::Kind_ContinueStatement || @@ -399,22 +450,16 @@ void Codegen::statementList(StatementList *ast) break; } requiresReturnValue = _requiresReturnValue; + insideSwitch = _insideSwitch; } -void Codegen::variableDeclaration(VariableDeclaration *ast) +void Codegen::variableDeclaration(PatternElement *ast) { RegisterScope scope(this); - if (!ast->expression) + if (!ast->initializer) return; - Reference rhs = expression(ast->expression); - if (hasError) - return; - - Reference lhs = referenceForName(ast->name.toString(), true); - //### if lhs is a temp, this won't generate a temp-to-temp move. Same for when rhs is a const - rhs.loadInAccumulator(); - lhs.storeConsumeAccumulator(); + initializeAndDestructureBindingElement(ast, Reference(), /*isDefinition*/ true); } void Codegen::variableDeclarationList(VariableDeclarationList *ast) @@ -424,6 +469,200 @@ void Codegen::variableDeclarationList(VariableDeclarationList *ast) } } +Codegen::Reference Codegen::targetForPatternElement(AST::PatternElement *p) +{ + if (!p->bindingIdentifier.isNull()) + return referenceForName(p->bindingIdentifier.toString(), true); + if (!p->bindingTarget || p->destructuringPattern()) + return Codegen::Reference::fromStackSlot(this); + Reference lhs = expression(p->bindingTarget); + if (hasError) + return lhs; + lhs = lhs.asLValue(); + return lhs; +} + +void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &base, bool isDefinition) +{ + Q_ASSERT(e->type == AST::PatternElement::Binding || e->type == AST::PatternElement::RestElement); + RegisterScope scope(this); + Reference baseRef = (base.isAccumulator()) ? base.storeOnStack() : base; + Reference varToStore = targetForPatternElement(e); + if (isDefinition) + varToStore.isReferenceToConst = false; + if (hasError) + return; + + if (e->initializer) { + if (!baseRef.isValid()) { + // assignment + Reference expr = expression(e->initializer); + if (hasError) + return; + expr.loadInAccumulator(); + varToStore.storeConsumeAccumulator(); + } else if (baseRef == varToStore) { + baseRef.loadInAccumulator(); + BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined(); + Reference expr = expression(e->initializer); + if (hasError) { + jump.link(); + return; + } + expr.loadInAccumulator(); + varToStore.storeConsumeAccumulator(); + jump.link(); + } else { + baseRef.loadInAccumulator(); + BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined(); + Reference expr = expression(e->initializer); + if (hasError) { + jump.link(); + return; + } + expr.loadInAccumulator(); + jump.link(); + varToStore.storeConsumeAccumulator(); + } + } else if (baseRef != varToStore && baseRef.isValid()) { + baseRef.loadInAccumulator(); + varToStore.storeConsumeAccumulator(); + } + Pattern *p = e->destructuringPattern(); + if (!p) + return; + + if (!varToStore.isStackSlot()) + varToStore = varToStore.storeOnStack(); + if (PatternElementList *l = e->elementList()) { + destructureElementList(varToStore, l, isDefinition); + } else if (PatternPropertyList *p = e->propertyList()) { + destructurePropertyList(varToStore, p, isDefinition); + } else if (e->bindingTarget) { + // empty binding pattern. For spec compatibility, try to coerce the argument to an object + varToStore.loadInAccumulator(); + Instruction::ToObject toObject; + bytecodeGenerator->addInstruction(toObject); + return; + } +} + +Codegen::Reference Codegen::referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name) +{ + AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(name); + Reference property; + if (cname) { + Reference computedName = expression(cname->expression); + if (hasError) + return Reference(); + computedName = computedName.storeOnStack(); + property = Reference::fromSubscript(object, computedName).asLValue(); + } else { + QString propertyName = name->asString(); + property = Reference::fromMember(object, propertyName); + } + return property; +} + +void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternPropertyList *bindingList, bool isDefinition) +{ + RegisterScope scope(this); + + for (PatternPropertyList *it = bindingList; it; it = it->next) { + PatternProperty *p = it->property; + RegisterScope scope(this); + Reference property = referenceForPropertyName(object, p->name); + if (hasError) + return; + initializeAndDestructureBindingElement(p, property, isDefinition); + if (hasError) + return; + } +} + +void Codegen::destructureElementList(const Codegen::Reference &array, PatternElementList *bindingList, bool isDefinition) +{ + RegisterScope scope(this); + + Reference iterator = Reference::fromStackSlot(this); + Reference iteratorValue = Reference::fromStackSlot(this); + Reference iteratorDone = Reference::fromStackSlot(this); + + array.loadInAccumulator(); + Instruction::GetIterator iteratorObjInstr; + iteratorObjInstr.iterator = 1; // ForEachType::Of + bytecodeGenerator->addInstruction(iteratorObjInstr); + iterator.storeConsumeAccumulator(); + + bool hadNext = false; + bool hasRest = false; + + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); + + for (PatternElementList *p = bindingList; p; p = p->next) { + PatternElement *e = p->element; + for (Elision *elision = p->elision; elision; elision = elision->next) { + iterator.loadInAccumulator(); + Instruction::IteratorNext next; + next.value = iteratorValue.stackSlot(); + bytecodeGenerator->addInstruction(next); + hadNext = true; + bool last = !elision->next && !e && !p->next; + if (last) + iteratorDone.storeConsumeAccumulator(); + } + + if (!e) + continue; + + hadNext = true; + RegisterScope scope(this); + iterator.loadInAccumulator(); + + if (e->type == PatternElement::RestElement) { + bytecodeGenerator->addInstruction(Instruction::DestructureRestElement()); + initializeAndDestructureBindingElement(e, Reference::fromAccumulator(this), isDefinition); + hasRest = true; + } else { + Instruction::IteratorNext next; + next.value = iteratorValue.stackSlot(); + bytecodeGenerator->addInstruction(next); + bool last = !p->next || (!p->next->elision && !p->next->element); + if (last) + iteratorDone.storeConsumeAccumulator(); + initializeAndDestructureBindingElement(e, iteratorValue, isDefinition); + if (hasError) { + end.link(); + return; + } + } + } + + if (!hadNext) { + Reference::storeConstOnStack(this, Encode(false), iteratorDone.stackSlot()); + } + + if (!hasRest) { + iterator.loadInAccumulator(); + Instruction::IteratorClose close; + close.done = iteratorDone.stackSlot(); + bytecodeGenerator->addInstruction(close); + } + + end.link(); +} + +void Codegen::destructurePattern(Pattern *p, const Reference &rhs) +{ + RegisterScope scope(this); + if (auto *o = AST::cast<ObjectPattern *>(p)) + destructurePropertyList(rhs, o->properties); + else if (auto *a = AST::cast<ArrayPattern *>(p)) + destructureElementList(rhs, a->elements); + else + Q_UNREACHABLE(); +} + bool Codegen::visit(ArgumentList *) { @@ -461,12 +700,6 @@ bool Codegen::visit(DefaultClause *) return false; } -bool Codegen::visit(ElementList *) -{ - Q_UNREACHABLE(); - return false; -} - bool Codegen::visit(Elision *) { Q_UNREACHABLE(); @@ -485,37 +718,31 @@ bool Codegen::visit(FormalParameterList *) return false; } -bool Codegen::visit(FunctionBody *) -{ - Q_UNREACHABLE(); - return false; -} - bool Codegen::visit(Program *) { Q_UNREACHABLE(); return false; } -bool Codegen::visit(PropertyAssignmentList *) +bool Codegen::visit(PatternElement *) { Q_UNREACHABLE(); return false; } -bool Codegen::visit(PropertyNameAndValue *) +bool Codegen::visit(PatternElementList *) { Q_UNREACHABLE(); return false; } -bool Codegen::visit(PropertyGetterSetter *) +bool Codegen::visit(PatternProperty *) { Q_UNREACHABLE(); return false; } -bool Codegen::visit(SourceElements *) +bool Codegen::visit(PatternPropertyList *) { Q_UNREACHABLE(); return false; @@ -587,15 +814,121 @@ bool Codegen::visit(UiQualifiedPragmaId *) return false; } -bool Codegen::visit(VariableDeclaration *) +bool Codegen::visit(VariableDeclarationList *) { Q_UNREACHABLE(); return false; } -bool Codegen::visit(VariableDeclarationList *) +bool Codegen::visit(ClassExpression *ast) { - Q_UNREACHABLE(); + Compiler::Class jsClass; + jsClass.nameIndex = registerString(ast->name.toString()); + + ClassElementList *constructor = nullptr; + int nComputedNames = 0; + int nStaticComputedNames = 0; + + RegisterScope scope(this); + ControlFlowBlock controlFlow(this, ast); + + for (auto *member = ast->elements; member; member = member->next) { + PatternProperty *p = member->property; + FunctionExpression *f = p->initializer->asFunctionDefinition(); + Q_ASSERT(f); + AST::ComputedPropertyName *cname = AST::cast<ComputedPropertyName *>(p->name); + if (cname) { + ++nComputedNames; + if (member->isStatic) + ++nStaticComputedNames; + } + QString name = p->name->asString(); + uint nameIndex = cname ? UINT_MAX : registerString(name); + Compiler::Class::Method::Type type = Compiler::Class::Method::Regular; + if (p->type == PatternProperty::Getter) + type = Compiler::Class::Method::Getter; + else if (p->type == PatternProperty::Setter) + type = Compiler::Class::Method::Setter; + Compiler::Class::Method m{ nameIndex, type, static_cast<uint>(defineFunction(name, f, f->formals, f->body)) }; + + if (member->isStatic) { + if (name == QStringLiteral("prototype")) { + throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a static method named 'prototype'.")); + return false; + } + jsClass.staticMethods << m; + } else { + if (name == QStringLiteral("constructor")) { + if (constructor) { + throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a multiple constructors in a class.")); + return false; + } + if (m.type != Compiler::Class::Method::Regular) { + throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a getter or setter named 'constructor'.")); + return false; + } + constructor = member; + jsClass.constructorIndex = m.functionIndex; + continue; + } + + jsClass.methods << m; + } + } + + int classIndex = _module->classes.size(); + _module->classes.append(jsClass); + + Reference heritage = Reference::fromStackSlot(this); + if (ast->heritage) { + bytecodeGenerator->setLocation(ast->heritage->firstSourceLocation()); + Reference r = expression(ast->heritage); + if (hasError) + return false; + r.storeOnStack(heritage.stackSlot()); + } else { + Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()).loadInAccumulator(); + heritage.storeConsumeAccumulator(); + } + + int computedNames = nComputedNames ? bytecodeGenerator->newRegisterArray(nComputedNames) : 0; + int currentStaticName = computedNames; + int currentNonStaticName = computedNames + nStaticComputedNames; + + for (auto *member = ast->elements; member; member = member->next) { + AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(member->property->name); + if (!cname) + continue; + RegisterScope scope(this); + bytecodeGenerator->setLocation(cname->firstSourceLocation()); + Reference computedName = expression(cname->expression); + if (hasError) + return false; + computedName.storeOnStack(member->isStatic ? currentStaticName++ : currentNonStaticName++); + } + + Instruction::CreateClass createClass; + createClass.classIndex = classIndex; + createClass.heritage = heritage.stackSlot(); + createClass.computedNames = computedNames; + + bytecodeGenerator->addInstruction(createClass); + + if (!ast->name.isEmpty()) { + Reference ctor = referenceForName(ast->name.toString(), true); + ctor.isReferenceToConst = false; // this is the definition + (void) ctor.storeRetainAccumulator(); + } + + _expr.setResult(Reference::fromAccumulator(this)); + return false; +} + +bool Codegen::visit(ClassDeclaration *ast) +{ + Reference outerVar = referenceForName(ast->name.toString(), true); + visit(static_cast<ClassExpression *>(ast)); + (void) outerVar.storeRetainAccumulator(); return false; } @@ -609,50 +942,160 @@ bool Codegen::visit(Expression *ast) return false; } -bool Codegen::visit(ArrayLiteral *ast) +bool Codegen::visit(ArrayPattern *ast) { if (hasError) return false; - RegisterScope scope(this); + PatternElementList *it = ast->elements; int argc = 0; - int args = -1; - auto push = [this, &argc, &args](AST::ExpressionNode *arg) { - int temp = bytecodeGenerator->newRegister(); - if (args == -1) - args = temp; - if (!arg) { - auto c = Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()); - (void) c.storeOnStack(temp); - } else { - RegisterScope scope(this); - (void) expression(arg).storeOnStack(temp); + { + RegisterScope scope(this); + + int args = -1; + auto push = [this, &argc, &args](AST::ExpressionNode *arg) { + int temp = bytecodeGenerator->newRegister(); + if (args == -1) + args = temp; + if (!arg) { + auto c = Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()); + (void) c.storeOnStack(temp); + } else { + RegisterScope scope(this); + Reference r = expression(arg); + if (hasError) + return; + (void) r.storeOnStack(temp); + } + ++argc; + }; + + for (; it; it = it->next) { + PatternElement *e = it->element; + if (e && e->type == PatternElement::SpreadElement) + break; + for (Elision *elision = it->elision; elision; elision = elision->next) + push(nullptr); + + if (!e) + continue; + + push(e->initializer); + if (hasError) + return false; } - ++argc; - }; - for (ElementList *it = ast->elements; it; it = it->next) { + if (args == -1) { + Q_ASSERT(argc == 0); + args = 0; + } - for (Elision *elision = it->elision; elision; elision = elision->next) - push(nullptr); + Instruction::DefineArray call; + call.argc = argc; + call.args = Moth::StackSlot::createRegister(args); + bytecodeGenerator->addInstruction(call); + } - push(it->expression); - if (hasError) - return false; + if (!it) { + _expr.setResult(Reference::fromAccumulator(this)); + return false; } - for (Elision *elision = ast->elision; elision; elision = elision->next) - push(nullptr); + Q_ASSERT(it->element && it->element->type == PatternElement::SpreadElement); - if (args == -1) { - Q_ASSERT(argc == 0); - args = 0; + RegisterScope scope(this); + Reference array = Reference::fromStackSlot(this); + array.storeConsumeAccumulator(); + Reference index = Reference::storeConstOnStack(this, Encode(argc)); + + auto pushAccumulator = [&]() { + Reference slot = Reference::fromSubscript(array, index); + slot.storeConsumeAccumulator(); + + index.loadInAccumulator(); + Instruction::Increment inc; + bytecodeGenerator->addInstruction(inc); + index.storeConsumeAccumulator(); + }; + + while (it) { + for (Elision *elision = it->elision; elision; elision = elision->next) { + Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()).loadInAccumulator(); + pushAccumulator(); + } + + if (!it->element) { + it = it->next; + continue; + } + + // handle spread element + if (it->element->type == PatternElement::SpreadElement) { + RegisterScope scope(this); + + Reference iterator = Reference::fromStackSlot(this); + Reference lhsValue = Reference::fromStackSlot(this); + + // There should be a temporal block, so that variables declared in lhs shadow outside vars. + // This block should define a temporal dead zone for those variables, which is not yet implemented. + { + RegisterScope innerScope(this); + Reference expr = expression(it->element->initializer); + if (hasError) + return false; + + expr.loadInAccumulator(); + Instruction::GetIterator iteratorObjInstr; + iteratorObjInstr.iterator = /*ForEachType::Of*/ 1; + bytecodeGenerator->addInstruction(iteratorObjInstr); + iterator.storeConsumeAccumulator(); + } + + BytecodeGenerator::Label in = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label done = bytecodeGenerator->newLabel(); + + { + ControlFlowLoop flow(this, &end, &in, /*requiresUnwind*/ true); + bytecodeGenerator->jump().link(in); + + BytecodeGenerator::Label body = bytecodeGenerator->label(); + + lhsValue.loadInAccumulator(); + pushAccumulator(); + + in.link(); + iterator.loadInAccumulator(); + Instruction::IteratorNext next; + next.value = lhsValue.stackSlot(); + bytecodeGenerator->addInstruction(next); + bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body); + bytecodeGenerator->jump().link(done); + } + + end.link(); + + Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack(); + iterator.loadInAccumulator(); + Instruction::IteratorClose close; + close.done = iteratorDone.stackSlot(); + bytecodeGenerator->addInstruction(close); + + done.link(); + } else { + RegisterScope innerScope(this); + Reference expr = expression(it->element->initializer); + if (hasError) + return false; + + expr.loadInAccumulator(); + pushAccumulator(); + } + + it = it->next; } - Instruction::DefineArray call; - call.argc = argc; - call.args = Moth::StackSlot::createRegister(args); - bytecodeGenerator->addInstruction(call); + array.loadInAccumulator(); _expr.setResult(Reference::fromAccumulator(this)); return false; @@ -666,7 +1109,14 @@ bool Codegen::visit(ArrayMemberExpression *ast) Reference base = expression(ast->base); if (hasError) return false; + if (base.isSuper()) { + Reference index = expression(ast->expression).storeOnStack(); + _expr.setResult(Reference::fromSuperProperty(index)); + return false; + } base = base.storeOnStack(); + if (hasError) + return false; if (AST::StringLiteral *str = AST::cast<AST::StringLiteral *>(ast->expression)) { QString s = str->value.toString(); uint arrayIndex = QV4::String::toArrayIndex(s); @@ -692,6 +1142,7 @@ static QSOperator::Op baseOp(int op) case QSOperator::InplaceAdd: return QSOperator::Add; case QSOperator::InplaceLeftShift: return QSOperator::LShift; case QSOperator::InplaceMod: return QSOperator::Mod; + case QSOperator::InplaceExp: return QSOperator::Exp; case QSOperator::InplaceMul: return QSOperator::Mul; case QSOperator::InplaceOr: return QSOperator::BitOr; case QSOperator::InplaceRightShift: return QSOperator::RShift; @@ -764,19 +1215,21 @@ bool Codegen::visit(BinaryExpression *ast) _expr.setResult(Reference::fromAccumulator(this)); } return false; - } - - Reference left = expression(ast->left); - if (hasError) - return false; - - switch (ast->op) { - case QSOperator::Or: - case QSOperator::And: - Q_UNREACHABLE(); // handled separately above - break; + } else if (ast->op == QSOperator::Assign) { + if (AST::Pattern *p = ast->left->patternCast()) { + RegisterScope scope(this); + Reference right = expression(ast->right).storeOnStack(); + destructurePattern(p, right); + if (!_expr.accept(nx)) { + right.loadInAccumulator(); + _expr.setResult(Reference::fromAccumulator(this)); + } + return false; + } + Reference left = expression(ast->left); + if (hasError) + return false; - case QSOperator::Assign: { if (!left.isLValue()) { throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue")); return false; @@ -792,15 +1245,27 @@ bool Codegen::visit(BinaryExpression *ast) _expr.setResult(left.storeConsumeAccumulator()); else _expr.setResult(left.storeRetainAccumulator()); - break; + return false; } + Reference left = expression(ast->left); + if (hasError) + return false; + + switch (ast->op) { + case QSOperator::Or: + case QSOperator::And: + case QSOperator::Assign: + Q_UNREACHABLE(); // handled separately above + break; + case QSOperator::InplaceAnd: case QSOperator::InplaceSub: case QSOperator::InplaceDiv: case QSOperator::InplaceAdd: case QSOperator::InplaceLeftShift: case QSOperator::InplaceMod: + case QSOperator::InplaceExp: case QSOperator::InplaceMul: case QSOperator::InplaceOr: case QSOperator::InplaceRightShift: @@ -830,7 +1295,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::BitAnd: case QSOperator::BitOr: case QSOperator::BitXor: - if (left.isConst()) { + if (left.isConstant()) { Reference right = expression(ast->right); if (hasError) return false; @@ -850,6 +1315,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::StrictNotEqual: case QSOperator::Add: case QSOperator::Div: + case QSOperator::Exp: case QSOperator::Mod: case QSOperator::Mul: case QSOperator::Sub: @@ -890,7 +1356,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; } case QSOperator::Sub: { - if (right.isConst() && right.constant == Encode(int(1))) { + if (right.isConstant() && right.constant == Encode(int(1))) { left.loadInAccumulator(); bytecodeGenerator->addInstruction(Instruction::Decrement()); } else { @@ -902,6 +1368,14 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re } break; } + case QSOperator::Exp: { + left = left.storeOnStack(); + right.loadInAccumulator(); + Instruction::Exp exp; + exp.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(exp); + break; + } case QSOperator::Mul: { left = left.storeOnStack(); right.loadInAccumulator(); @@ -927,9 +1401,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; } case QSOperator::BitAnd: - if (right.isConst()) { + if (right.isConstant()) { int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32(); - if (left.isConst()) { + if (left.isConstant()) { int result = Primitive::fromReturnedValue(left.constant).toInt32() & rightAsInt; return Reference::fromConst(this, Encode(result)); } @@ -945,9 +1419,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re } break; case QSOperator::BitOr: - if (right.isConst()) { + if (right.isConstant()) { int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32(); - if (left.isConst()) { + if (left.isConstant()) { int result = Primitive::fromReturnedValue(left.constant).toInt32() | rightAsInt; return Reference::fromConst(this, Encode(result)); } @@ -963,9 +1437,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re } break; case QSOperator::BitXor: - if (right.isConst()) { + if (right.isConstant()) { int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32(); - if (left.isConst()) { + if (left.isConstant()) { int result = Primitive::fromReturnedValue(left.constant).toInt32() ^ rightAsInt; return Reference::fromConst(this, Encode(result)); } @@ -981,7 +1455,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re } break; case QSOperator::URShift: - if (right.isConst()) { + if (right.isConstant()) { left.loadInAccumulator(); Instruction::UShrConst ushr; ushr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f; @@ -994,7 +1468,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re } break; case QSOperator::RShift: - if (right.isConst()) { + if (right.isConstant()) { left.loadInAccumulator(); Instruction::ShrConst shr; shr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f; @@ -1007,7 +1481,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re } break; case QSOperator::LShift: - if (right.isConst()) { + if (right.isConstant()) { left.loadInAccumulator(); Instruction::ShlConst shl; shl.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f; @@ -1146,12 +1620,12 @@ static QSOperator::Op operatorForSwappedOperands(QSOperator::Op oper) Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Reference &right) { - if (left.isConst()) { + if (left.isConstant()) { oper = operatorForSwappedOperands(oper); qSwap(left, right); } - if (right.isConst() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) { + if (right.isConstant() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) { Value c = Primitive::fromReturnedValue(right.constant); if (c.isNull() || c.isUndefined()) { left.loadInAccumulator(); @@ -1259,6 +1733,7 @@ bool Codegen::visit(CallExpression *ast) RegisterScope scope(this); Reference base = expression(ast->base); + if (hasError) return false; switch (base.type) { @@ -1270,15 +1745,50 @@ bool Codegen::visit(CallExpression *ast) break; case Reference::Name: break; + case Reference::Super: + handleConstruct(base, ast->arguments); + return false; default: base = base.storeOnStack(); break; } + int thisObject = bytecodeGenerator->newRegister(); + int functionObject = bytecodeGenerator->newRegister(); + auto calldata = pushArgs(ast->arguments); if (hasError) return false; + if (calldata.hasSpread) { + Reference baseObject = base.baseObject(); + if (!baseObject.isStackSlot()) { + baseObject.storeOnStack(thisObject); + baseObject = Reference::fromStackSlot(this, thisObject); + } + if (!base.isStackSlot()) { + base.storeOnStack(functionObject); + base = Reference::fromStackSlot(this, functionObject); + } + + Instruction::CallWithSpread call; + call.func = base.stackSlot(); + call.thisObject = baseObject.stackSlot(); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + + _expr.setResult(Reference::fromAccumulator(this)); + return false; + + } + + handleCall(base, calldata); + return false; +} + +void Codegen::handleCall(Reference &base, Arguments calldata) +{ //### Do we really need all these call instructions? can's we load the callee in a temp? if (base.type == Reference::QmlScopeObject) { Instruction::CallScopeObjectProperty call; @@ -1295,7 +1805,7 @@ bool Codegen::visit(CallExpression *ast) call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); } else if (base.type == Reference::Member) { - if (useFastLookups) { + if (!disable_lookups && useFastLookups) { Instruction::CallPropertyLookup call; call.base = base.propertyBase.stackSlot(); call.lookupIndex = registerGetterLookup(base.propertyNameIndex); @@ -1323,7 +1833,7 @@ bool Codegen::visit(CallExpression *ast) call.argc = calldata.argc; call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); - } else if (useFastLookups && base.global) { + } else if (!disable_lookups && useFastLookups && base.global) { Instruction::CallGlobalLookup call; call.index = registerGlobalGetterLookup(base.nameAsIndex()); call.argc = calldata.argc; @@ -1346,36 +1856,69 @@ bool Codegen::visit(CallExpression *ast) } _expr.setResult(Reference::fromAccumulator(this)); - return false; } Codegen::Arguments Codegen::pushArgs(ArgumentList *args) { + bool hasSpread = false; int argc = 0; - for (ArgumentList *it = args; it; it = it->next) + for (ArgumentList *it = args; it; it = it->next) { + if (it->isSpreadElement) { + hasSpread = true; + ++argc; + } ++argc; + } if (!argc) - return { 0, 0 }; + return { 0, 0, false }; int calldata = bytecodeGenerator->newRegisterArray(argc); argc = 0; for (ArgumentList *it = args; it; it = it->next) { + if (it->isSpreadElement) { + Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()).storeOnStack(calldata + argc); + ++argc; + } RegisterScope scope(this); Reference e = expression(it->expression); if (hasError) break; - if (!argc && !it->next) { + if (!argc && !it->next && !hasSpread) { // avoid copy for functions taking a single argument if (e.isStackSlot()) - return { 1, e.stackSlot() }; + return { 1, e.stackSlot(), hasSpread }; } (void) e.storeOnStack(calldata + argc); ++argc; } - return { argc, calldata }; + return { argc, calldata, hasSpread }; +} + +Codegen::Arguments Codegen::pushTemplateArgs(TemplateLiteral *args) +{ + int argc = 0; + for (TemplateLiteral *it = args; it; it = it->next) + ++argc; + + if (!argc) + return { 0, 0, false }; + + int calldata = bytecodeGenerator->newRegisterArray(argc); + + argc = 0; + for (TemplateLiteral *it = args; it && it->expression; it = it->next) { + RegisterScope scope(this); + Reference e = expression(it->expression); + if (hasError) + break; + (void) e.storeOnStack(calldata + argc); + ++argc; + } + + return { argc, calldata, false }; } bool Codegen::visit(ConditionalExpression *ast) @@ -1413,11 +1956,15 @@ bool Codegen::visit(DeleteExpression *ast) if (hasError) return false; + RegisterScope scope(this); Reference expr = expression(ast->expression); if (hasError) return false; switch (expr.type) { + case Reference::SuperProperty: + // ### this should throw a reference error at runtime. + return false; case Reference::StackSlot: if (!expr.stackSlotIsLocalOrArgument) break; @@ -1444,9 +1991,14 @@ bool Codegen::visit(DeleteExpression *ast) case Reference::Member: { //### maybe add a variant where the base can be in the accumulator? expr = expr.asLValue(); - Instruction::DeleteMember del; + Instruction::LoadRuntimeString instr; + instr.stringId = expr.propertyNameIndex; + bytecodeGenerator->addInstruction(instr); + Reference index = Reference::fromStackSlot(this); + index.storeConsumeAccumulator(); + Instruction::DeleteProperty del; del.base = expr.propertyBase.stackSlot(); - del.member = expr.propertyNameIndex; + del.index = index.stackSlot(); bytecodeGenerator->addInstruction(del); _expr.setResult(Reference::fromAccumulator(this)); return false; @@ -1454,7 +2006,7 @@ bool Codegen::visit(DeleteExpression *ast) case Reference::Subscript: { //### maybe add a variant where the index can be in the accumulator? expr = expr.asLValue(); - Instruction::DeleteSubscript del; + Instruction::DeleteProperty del; del.base = expr.elementBase; del.index = expr.elementSubscript.stackSlot(); bytecodeGenerator->addInstruction(del); @@ -1478,109 +2030,169 @@ bool Codegen::visit(FalseLiteral *) return false; } +bool Codegen::visit(SuperLiteral *) +{ + if (hasError) + return false; + + _expr.setResult(Reference::fromSuper(this)); + return false; +} + bool Codegen::visit(FieldMemberExpression *ast) { if (hasError) return false; + if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast->base)) { + if (id->name == QLatin1String("new")) { + // new.target + if (ast->name != QLatin1String("target")) { + throwSyntaxError(ast->identifierToken, QLatin1String("Expected 'target' after 'new.'.")); + return false; + } + Reference r = Reference::fromStackSlot(this, CallData::NewTarget); + _expr.setResult(r); + return false; + } + } + Reference base = expression(ast->base); if (hasError) return false; + if (base.isSuper()) { + Instruction::LoadRuntimeString load; + load.stringId = registerString(ast->name.toString()); + bytecodeGenerator->addInstruction(load); + Reference property = Reference::fromAccumulator(this).storeOnStack(); + _expr.setResult(Reference::fromSuperProperty(property)); + return false; + } _expr.setResult(Reference::fromMember(base, ast->name.toString())); return false; } -bool Codegen::visit(FunctionExpression *ast) +bool Codegen::visit(TaggedTemplate *ast) { if (hasError) return false; RegisterScope scope(this); - int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : nullptr); - loadClosure(function); - _expr.setResult(Reference::fromAccumulator(this)); + Reference base = expression(ast->base); + if (hasError) + return false; + switch (base.type) { + case Reference::Member: + case Reference::Subscript: + base = base.asLValue(); + break; + case Reference::Name: + break; + default: + base = base.storeOnStack(); + break; + } + + int arrayTemp = createTemplateArray(ast->templateLiteral); + Q_UNUSED(arrayTemp); + auto calldata = pushTemplateArgs(ast->templateLiteral); + if (hasError) + return false; + ++calldata.argc; + Q_ASSERT(calldata.argv == arrayTemp + 1); + --calldata.argv; + + handleCall(base, calldata); + return false; + + return false; } -Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs) +int Codegen::createTemplateArray(TemplateLiteral *t) { - int scope = 0; - Context *c = _context; - - // skip the innermost context if it's simple (as the runtime won't - // create a context for it - if (c->canUseSimpleCall()) { - Context::Member m = c->findMember(name); - if (m.type != Context::UndefinedMember) { - Q_ASSERT((!m.canEscape)); - Reference r = Reference::fromStackSlot(this, m.index, true /*isLocal*/); - if (name == QLatin1String("arguments") || name == QLatin1String("eval")) { - r.isArgOrEval = true; - if (isLhs && c->isStrict) - // ### add correct source location - throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode")); - } - return r; - } - const int argIdx = c->findArgument(name); - if (argIdx != -1) { - Q_ASSERT(!c->argumentsCanEscape && (c->usesArgumentsObject != Context::ArgumentsObjectUsed || c->isStrict)); - return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name)); - } - c = c->parent; - } + int arrayTemp = bytecodeGenerator->newRegister(); - while (c->parent) { - if (c->forceLookupByName()) - goto loadByName; + RegisterScope scope(this); - Context::Member m = c->findMember(name); - if (m.type != Context::UndefinedMember) { - Reference r = m.canEscape ? Reference::fromScopedLocal(this, m.index, scope) - : Reference::fromStackSlot(this, m.index, true /*isLocal*/); - if (name == QLatin1String("arguments") || name == QLatin1String("eval")) { - r.isArgOrEval = true; - if (isLhs && c->isStrict) - // ### add correct source location - throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode")); - } - return r; - } - const int argIdx = c->findArgument(name); - if (argIdx != -1) { - if (c->argumentsCanEscape || c->usesArgumentsObject == Context::ArgumentsObjectUsed) { - int idx = argIdx + c->locals.size(); - return Reference::fromScopedLocal(this, idx, scope); - } else { - Q_ASSERT(scope == 0); - return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name)); - } - } + int argc = 0; + int args = -1; + auto push = [this, &argc, &args](const QStringRef &arg) { + int temp = bytecodeGenerator->newRegister(); + if (args == -1) + args = temp; + Instruction::LoadRuntimeString instr; + instr.stringId = registerString(arg.toString()); + bytecodeGenerator->addInstruction(instr); + Instruction::StoreReg store; + store.reg = temp; + bytecodeGenerator->addInstruction(store); - if (!c->isStrict && c->hasDirectEval) - goto loadByName; + ++argc; + }; - ++scope; - c = c->parent; - } + for (TemplateLiteral *it = t; it; it = it->next) + push(it->value); - { - // This hook allows implementing QML lookup semantics - Reference fallback = fallbackNameLookup(name); - if (fallback.type != Reference::Invalid) - return fallback; + if (args == -1) { + Q_ASSERT(argc == 0); + args = 0; } - if (!c->parent && !c->forceLookupByName() && _context->compilationMode != EvalCode && c->compilationMode != QmlBinding) { - Reference r = Reference::fromName(this, name); - r.global = true; + Instruction::DefineArray call; + call.argc = argc; + call.args = Moth::StackSlot::createRegister(args); + bytecodeGenerator->addInstruction(call); + + Instruction::StoreReg store; + store.reg = arrayTemp; + bytecodeGenerator->addInstruction(store); + + return arrayTemp; +} + +bool Codegen::visit(FunctionExpression *ast) +{ + if (hasError) + return false; + + RegisterScope scope(this); + + int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body); + if (hasError) + return false; + loadClosure(function); + _expr.setResult(Reference::fromAccumulator(this)); + return false; +} + +Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs) +{ + Context::ResolvedName resolved = _context->resolveName(name); + + if (resolved.type == Context::ResolvedName::Local || resolved.type == Context::ResolvedName::Stack) { + if (resolved.isArgOrEval && isLhs) + // ### add correct source location + throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode")); + Reference r = (resolved.type == Context::ResolvedName::Local) ? + Reference::fromScopedLocal(this, resolved.index, resolved.scope) : + Reference::fromStackSlot(this, resolved.index, true /*isLocal*/); + if (r.isStackSlot() && _volatileMemoryLocations.isVolatile(name)) + r.isVolatile = true; + r.isArgOrEval = resolved.isArgOrEval; + r.isReferenceToConst = resolved.isConst; return r; } - // global context or with. Lookup by name - loadByName: - return Reference::fromName(this, name); + // This hook allows implementing QML lookup semantics + Reference fallback = fallbackNameLookup(name); + if (fallback.type != Reference::Invalid) + return fallback; + + Reference r = Reference::fromName(this, name); + r.global = (resolved.type == Context::ResolvedName::Global); + return r; } void Codegen::loadClosure(int closureId) @@ -1618,6 +2230,47 @@ bool Codegen::visit(NestedExpression *ast) return false; } +void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments) +{ + Reference constructor; + if (base.isSuper()) { + Instruction::LoadSuperConstructor super; + bytecodeGenerator->addInstruction(super); + constructor = Reference::fromAccumulator(this).storeOnStack(); + } else { + constructor = base.storeOnStack(); + } + + auto calldata = pushArgs(arguments); + if (hasError) + return; + + if (base.isSuper()) { + Reference::fromStackSlot(this, CallData::Function).loadInAccumulator(); + } else { + constructor.loadInAccumulator(); + } + + if (calldata.hasSpread) { + Instruction::ConstructWithSpread create; + create.func = constructor.stackSlot(); + create.argc = calldata.argc; + create.argv = calldata.argv; + bytecodeGenerator->addInstruction(create); + } else { + Instruction::Construct create; + create.func = constructor.stackSlot(); + create.argc = calldata.argc; + create.argv = calldata.argv; + bytecodeGenerator->addInstruction(create); + } + if (base.isSuper()) + // set the result up as the thisObject + Reference::fromAccumulator(this).storeOnStack(CallData::This); + + _expr.setResult(Reference::fromAccumulator(this)); +} + bool Codegen::visit(NewExpression *ast) { if (hasError) @@ -1628,15 +2281,12 @@ bool Codegen::visit(NewExpression *ast) Reference base = expression(ast->expression); if (hasError) return false; - //### Maybe create a ConstructA that takes an accumulator? - base = base.storeOnStack(); + if (base.isSuper()) { + throwSyntaxError(ast->expression->firstSourceLocation(), QStringLiteral("Cannot use new with super.")); + return false; + } - Instruction::Construct create; - create.func = base.stackSlot(); - create.argc = 0; - create.argv = 0; - bytecodeGenerator->addInstruction(create); - _expr.setResult(Reference::fromAccumulator(this)); + handleConstruct(base, nullptr); return false; } @@ -1650,18 +2300,12 @@ bool Codegen::visit(NewMemberExpression *ast) Reference base = expression(ast->base); if (hasError) return false; - base = base.storeOnStack(); - - auto calldata = pushArgs(ast->arguments); - if (hasError) + if (base.isSuper()) { + throwSyntaxError(ast->base->firstSourceLocation(), QStringLiteral("Cannot use new with super.")); return false; + } - Instruction::Construct create; - create.func = base.stackSlot(); - create.argc = calldata.argc; - create.argv = calldata.argv; - bytecodeGenerator->addInstruction(create); - _expr.setResult(Reference::fromAccumulator(this)); + handleConstruct(base, ast->arguments); return false; } @@ -1696,133 +2340,105 @@ bool Codegen::visit(NumericLiteral *ast) return false; } -bool Codegen::visit(ObjectLiteral *ast) +bool Codegen::visit(ObjectPattern *ast) { if (hasError) return false; + QVector<QPair<Reference, ObjectPropertyValue>> computedProperties; QMap<QString, ObjectPropertyValue> valueMap; RegisterScope scope(this); - for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { - QString name = it->assignment->name->asString(); - if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) { - Reference value = expression(nv->value); - if (hasError) - return false; - - ObjectPropertyValue &v = valueMap[name]; - if (v.hasGetter() || v.hasSetter() || (_context->isStrict && v.rvalue.isValid())) { - throwSyntaxError(nv->lastSourceLocation(), - QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name)); - return false; - } - - v.rvalue = value.storeOnStack(); - } else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) { - const int function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : nullptr); - ObjectPropertyValue &v = valueMap[name]; - if (v.rvalue.isValid() || - (gs->type == PropertyGetterSetter::Getter && v.hasGetter()) || - (gs->type == PropertyGetterSetter::Setter && v.hasSetter())) { - throwSyntaxError(gs->lastSourceLocation(), - QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name)); - return false; - } - if (gs->type == PropertyGetterSetter::Getter) - v.getter = function; - else - v.setter = function; - } else { - Q_UNREACHABLE(); - } - } + QStringList members; - QVector<QString> nonArrayKey, arrayKeyWithValue, arrayKeyWithGetterSetter; - bool needSparseArray = false; // set to true if any array index is bigger than 16 - - for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(), eit = valueMap.end(); - it != eit; ++it) { - QString name = it.key(); - uint keyAsIndex = QV4::String::toArrayIndex(name); - if (keyAsIndex != std::numeric_limits<uint>::max()) { - it->keyAsIndex = keyAsIndex; - if (keyAsIndex > 16) - needSparseArray = true; - if (it->hasSetter() || it->hasGetter()) - arrayKeyWithGetterSetter.append(name); - else - arrayKeyWithValue.append(name); - } else { - nonArrayKey.append(name); - } - } - - int args = -1; - auto push = [this, &args](const Reference &arg) { + int argc = 0; + int args = 0; + auto push = [this, &args, &argc](const Reference &arg) { int temp = bytecodeGenerator->newRegister(); - if (args == -1) + if (argc == 0) args = temp; (void) arg.storeOnStack(temp); + ++argc; }; - QVector<QV4::Compiler::JSUnitGenerator::MemberInfo> members; + PatternPropertyList *it = ast->properties; + for (; it; it = it->next) { + PatternProperty *p = it->property; + AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name); + if (cname || p->type == PatternProperty::Getter || p->type == PatternProperty::Setter) + break; + QString name = p->name->asString(); + uint arrayIndex = QV4::String::toArrayIndex(name); + if (arrayIndex != UINT_MAX) + break; + if (members.contains(name)) + break; + members.append(name); - Reference acc = Reference::fromAccumulator(this); - // generate the key/value pairs - for (const QString &key : qAsConst(nonArrayKey)) { - const ObjectPropertyValue &prop = valueMap[key]; - - if (prop.hasGetter() || prop.hasSetter()) { - Q_ASSERT(!prop.rvalue.isValid()); - loadClosure(prop.getter); - push(acc); - loadClosure(prop.setter); - push(acc); - members.append({ key, true }); - } else { - Q_ASSERT(prop.rvalue.isValid()); - push(prop.rvalue); - members.append({ key, false }); + { + RegisterScope innerScope(this); + Reference value = expression(p->initializer); + if (hasError) + return false; + value.loadInAccumulator(); } - } - - // generate array entries with values - for (const QString &key : qAsConst(arrayKeyWithValue)) { - const ObjectPropertyValue &prop = valueMap[key]; - Q_ASSERT(!prop.hasGetter() && !prop.hasSetter()); - push(Reference::fromConst(this, Encode(prop.keyAsIndex))); - push(prop.rvalue); - } - - // generate array entries with both a value and a setter - for (const QString &key : qAsConst(arrayKeyWithGetterSetter)) { - const ObjectPropertyValue &prop = valueMap[key]; - Q_ASSERT(!prop.rvalue.isValid()); - push(Reference::fromConst(this, Encode(prop.keyAsIndex))); - loadClosure(prop.getter); - push(acc); - loadClosure(prop.setter); - push(acc); + push(Reference::fromAccumulator(this)); } int classId = jsUnitGenerator->registerJSClass(members); - uint arrayGetterSetterCountAndFlags = arrayKeyWithGetterSetter.size(); - arrayGetterSetterCountAndFlags |= needSparseArray << 30; - - if (args == -1) - args = 0; + // handle complex property setters + for (; it; it = it->next) { + PatternProperty *p = it->property; + AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name); + ObjectLiteralArgument argType = ObjectLiteralArgument::Value; + if (p->type == PatternProperty::Getter) + argType = ObjectLiteralArgument::Getter; + else if (p->type == PatternProperty::Setter) + argType = ObjectLiteralArgument::Setter; + + Reference::fromConst(this, Encode(int(argType))).loadInAccumulator(); + push(Reference::fromAccumulator(this)); + + if (cname) { + RegisterScope innerScope(this); + Reference name = expression(cname->expression); + if (hasError) + return false; + name.loadInAccumulator(); + } else { + QString name = p->name->asString(); +#if 0 + uint arrayIndex = QV4::String::toArrayIndex(name); + if (arrayIndex != UINT_MAX) { + Reference::fromConst(this, Encode(arrayIndex)).loadInAccumulator(); + } else +#endif + { + Instruction::LoadRuntimeString instr; + instr.stringId = registerString(name); + bytecodeGenerator->addInstruction(instr); + } + } + push(Reference::fromAccumulator(this)); + { + RegisterScope innerScope(this); + Reference value = expression(p->initializer); + if (hasError) + return false; + value.loadInAccumulator(); + } + push(Reference::fromAccumulator(this)); + } Instruction::DefineObjectLiteral call; call.internalClassId = classId; - call.arrayValueCount = arrayKeyWithValue.size(); - call.arrayGetterSetterCountAndFlags = arrayGetterSetterCountAndFlags; + call.argc = argc; call.args = Moth::StackSlot::createRegister(args); bytecodeGenerator->addInstruction(call); - - _expr.setResult(Reference::fromAccumulator(this)); + Reference result = Reference::fromAccumulator(this); + _expr.setResult(result); return false; } @@ -1933,6 +2549,49 @@ bool Codegen::visit(StringLiteral *ast) return false; } +bool Codegen::visit(TemplateLiteral *ast) +{ + if (hasError) + return false; + + Instruction::LoadRuntimeString instr; + instr.stringId = registerString(ast->value.toString()); + bytecodeGenerator->addInstruction(instr); + + if (ast->expression) { + RegisterScope scope(this); + int temp = bytecodeGenerator->newRegister(); + Instruction::StoreReg store; + store.reg = temp; + bytecodeGenerator->addInstruction(store); + + Reference expr = expression(ast->expression); + + if (ast->next) { + int temp2 = bytecodeGenerator->newRegister(); + expr.storeOnStack(temp2); + visit(ast->next); + + Instruction::Add instr; + instr.lhs = temp2; + bytecodeGenerator->addInstruction(instr); + } else { + expr.loadInAccumulator(); + } + + Instruction::Add instr; + instr.lhs = temp; + bytecodeGenerator->addInstruction(instr); + } + + auto r = Reference::fromAccumulator(this); + r.isReadonly = true; + + _expr.setResult(r); + return false; + +} + bool Codegen::visit(ThisExpression *) { if (hasError) @@ -2023,13 +2682,40 @@ bool Codegen::visit(FunctionDeclaration * ast) RegisterScope scope(this); - if (_context->compilationMode == QmlBinding) - Reference::fromName(this, ast->name.toString()).loadInAccumulator(); + if (_functionContext->contextType == ContextType::Binding) + referenceForName(ast->name.toString(), true).loadInAccumulator(); _expr.accept(nx); return false; } -static bool endsWithReturn(Node *node) +bool Codegen::visit(YieldExpression *ast) +{ + if (ast->isYieldStar) { + throwSyntaxError(ast->firstSourceLocation(), QLatin1String("yield* is not currently supported")); + return false; + } + if (inFormalParameterList) { + throwSyntaxError(ast->firstSourceLocation(), QLatin1String("yield is not allowed inside parameter lists")); + return false; + } + + + Reference result = ast->expression ? expression(ast->expression) : Reference::fromConst(this, Encode::undefined()); + if (hasError) + return false; + result.loadInAccumulator(); + Instruction::Yield yield; + bytecodeGenerator->addInstruction(yield); + Instruction::Resume resume; + BytecodeGenerator::Jump jump = bytecodeGenerator->addJumpInstruction(resume); + Reference acc = Reference::fromAccumulator(this); + emitReturn(acc); + jump.link(); + _expr.setResult(acc); + return false; +} + +static bool endsWithReturn(Module *module, Node *node) { if (!node) return false; @@ -2038,32 +2724,29 @@ static bool endsWithReturn(Node *node) if (AST::cast<ThrowStatement *>(node)) return true; if (Program *p = AST::cast<Program *>(node)) - return endsWithReturn(p->elements); - if (SourceElements *se = AST::cast<SourceElements *>(node)) { - while (se->next) - se = se->next; - return endsWithReturn(se->element); - } - if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(node)) - return endsWithReturn(sse->statement); + return endsWithReturn(module, p->statements); if (StatementList *sl = AST::cast<StatementList *>(node)) { while (sl->next) sl = sl->next; - return endsWithReturn(sl->statement); + return endsWithReturn(module, sl->statement); + } + if (Block *b = AST::cast<Block *>(node)) { + Context *blockContext = module->contextMap.value(node); + if (blockContext->requiresExecutionContext) + // we need to emit a return statement here, because of the + // unwind handler + return false; + return endsWithReturn(module, b->statements); } - if (Block *b = AST::cast<Block *>(node)) - return endsWithReturn(b->statements); if (IfStatement *is = AST::cast<IfStatement *>(node)) - return is->ko && endsWithReturn(is->ok) && endsWithReturn(is->ko); + return is->ko && endsWithReturn(module, is->ok) && endsWithReturn(module, is->ko); return false; } int Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, - AST::SourceElements *body) + AST::StatementList *body) { - Q_UNUSED(formals); - enterContext(ast); if (_context->functionIndex >= 0) @@ -2074,7 +2757,15 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, _module->functions.append(_context); _context->functionIndex = _module->functions.count() - 1; - _context->hasDirectEval |= (_context->compilationMode == EvalCode || _context->compilationMode == GlobalCode || _module->debugMode); // Conditional breakpoints are like eval in the function + Context *savedFunctionContext = _functionContext; + _functionContext = _context; + ControlFlow *savedControlFlow = controlFlow; + controlFlow = nullptr; + + if (_context->contextType == ContextType::Global) { + _module->blocks.append(_context); + _context->blockIndex = _module->blocks.count() - 1; + } if (_module->debugMode) // allow the debugger to see overwritten arguments _context->argumentsCanEscape = true; @@ -2084,132 +2775,104 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, // at all, because if the onSignal is a signal handler, the user is actually making it explicit // that the binding is a function, so we should execute that. However, we don't know that during // AOT compilation, so mark the surrounding function as only-returning-a-closure. - _context->returnsClosure = cast<ExpressionStatement *>(ast) && cast<FunctionExpression *>(cast<ExpressionStatement *>(ast)->expression); + _context->returnsClosure = body && body->statement && cast<ExpressionStatement *>(body->statement) && cast<FunctionExpression *>(cast<ExpressionStatement *>(body->statement)->expression); BytecodeGenerator bytecode(_context->line, _module->debugMode); BytecodeGenerator *savedBytecodeGenerator; savedBytecodeGenerator = bytecodeGenerator; bytecodeGenerator = &bytecode; bytecodeGenerator->setLocation(ast->firstSourceLocation()); + BytecodeGenerator::Label *savedReturnLabel = _returnLabel; + _returnLabel = nullptr; + + bool savedFunctionEndsWithReturn = functionEndsWithReturn; + functionEndsWithReturn = endsWithReturn(_module, body); // reserve the js stack frame (Context & js Function & accumulator) bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size()); + bool _inFormalParameterList = false; + qSwap(_inFormalParameterList, inFormalParameterList); + int returnAddress = -1; - bool _requiresReturnValue = (_context->compilationMode == QmlBinding || _context->compilationMode == EvalCode || _context->compilationMode == GlobalCode); + bool _requiresReturnValue = _context->requiresImplicitReturnValue(); qSwap(requiresReturnValue, _requiresReturnValue); - if (requiresReturnValue) - returnAddress = bytecodeGenerator->newRegister(); - if (!_context->parent || _context->usesArgumentsObject == Context::ArgumentsObjectUnknown) - _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; - if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed) - _context->addLocalVar(QStringLiteral("arguments"), Context::VariableDeclaration, AST::VariableDeclaration::FunctionScope); - - bool allVarsEscape = _context->hasWith || _context->hasTry || _context->hasDirectEval; - bool needsCallContext = false; - const QLatin1String exprForOn("expression for on"); - if (!_context->canUseSimpleCall() && _context->compilationMode != GlobalCode && (_context->compilationMode != EvalCode || _context->isStrict)) - needsCallContext = true; - else if (_context->compilationMode == QmlBinding && name.length() > exprForOn.size() && name.startsWith(exprForOn) && name.at(exprForOn.size()).isUpper()) - // we don't really need this for bindings, but we do for signal handlers, and we don't know if the code is a signal handler or not. - needsCallContext = true; - if (needsCallContext) { - Instruction::CreateCallContext createContext; - bytecodeGenerator->addInstruction(createContext); - } - - // variables in global code are properties of the global context object, not locals as with other functions. - if (_context->compilationMode == FunctionCode || _context->compilationMode == QmlBinding) { - for (Context::MemberMap::iterator it = _context->members.begin(), end = _context->members.end(); it != end; ++it) { - const QString &local = it.key(); - if (allVarsEscape) - it->canEscape = true; - if (it->canEscape) { - it->index = _context->locals.size(); - _context->locals.append(local); - if (it->type == Context::ThisFunctionName) { - // move the name from the stack to the call context - Instruction::LoadReg load; - load.reg = CallData::Function; - bytecodeGenerator->addInstruction(load); - Instruction::StoreLocal store; - store.index = it->index; - bytecodeGenerator->addInstruction(store); - } - } else { - if (it->type == Context::ThisFunctionName) - it->index = CallData::Function; - else - it->index = bytecodeGenerator->newRegister(); - } - } - } else { - for (Context::MemberMap::const_iterator it = _context->members.constBegin(), cend = _context->members.constEnd(); it != cend; ++it) { - const QString &local = it.key(); + returnAddress = bytecodeGenerator->newRegister(); + qSwap(_returnAddress, returnAddress); - Instruction::DeclareVar declareVar; - declareVar.isDeletable = false; - declareVar.varName = registerString(local); - bytecodeGenerator->addInstruction(declareVar); - } + // register the lexical scope for global code + if (!_context->parent && _context->requiresExecutionContext) { + _module->blocks.append(_context); + _context->blockIndex = _module->blocks.count() - 1; } - qSwap(_returnAddress, returnAddress); - for (const Context::Member &member : qAsConst(_context->members)) { - if (member.function) { - const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals, - member.function->body ? member.function->body->elements : nullptr); - loadClosure(function); - if (! _context->parent) { - Reference::fromName(this, member.function->name.toString()).storeConsumeAccumulator(); - } else { - Q_ASSERT(member.index >= 0); - Reference local = member.canEscape ? Reference::fromScopedLocal(this, member.index, 0) - : Reference::fromStackSlot(this, member.index, true); - local.storeConsumeAccumulator(); - } + RegisterScope registerScope(this); + _context->emitBlockHeader(this); + + inFormalParameterList = true; + int argc = 0; + while (formals) { + PatternElement *e = formals->element; + if (!e) { + if (!formals->next) + // trailing comma + break; + Q_UNREACHABLE(); } - } - if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed) { - if (_context->isStrict) { - Instruction::CreateUnmappedArgumentsObject setup; - bytecodeGenerator->addInstruction(setup); + + Reference arg = referenceForName(e->bindingIdentifier.toString(), true); + if (e->type == PatternElement::RestElement) { + Q_ASSERT(!formals->next); + Instruction::CreateRestParameter rest; + rest.argIndex = argc; + bytecodeGenerator->addInstruction(rest); + arg.storeConsumeAccumulator(); } else { - Instruction::CreateMappedArgumentsObject setup; - bytecodeGenerator->addInstruction(setup); + if (e->bindingTarget || e->initializer) { + initializeAndDestructureBindingElement(e, arg); + if (hasError) + break; + } } - referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator(); + formals = formals->next; + ++argc; } - if (_context->usesThis && !_context->isStrict) { - // make sure we convert this to an object - Instruction::ConvertThisToObject convert; - bytecodeGenerator->addInstruction(convert); + inFormalParameterList = false; + + if (_context->isGenerator) { + Instruction::Yield yield; + bytecodeGenerator->addInstruction(yield); } beginFunctionBodyHook(); - sourceElements(body); + statementList(body); - if (hasError || !endsWithReturn(body)) { - bytecodeGenerator->setLocation(ast->lastSourceLocation()); - if (requiresReturnValue) { - if (_returnAddress >= 0) { - Instruction::LoadReg load; - load.reg = Moth::StackSlot::createRegister(_returnAddress); - bytecodeGenerator->addInstruction(load); - } + bytecodeGenerator->setLocation(ast->lastSourceLocation()); + _context->emitBlockFooter(this); + + if (_returnLabel || hasError || !functionEndsWithReturn) { + if (_returnLabel) + _returnLabel->link(); + + if (_returnLabel || requiresReturnValue) { + Instruction::LoadReg load; + load.reg = Moth::StackSlot::createRegister(_returnAddress); + bytecodeGenerator->addInstruction(load); } else { Reference::fromConst(this, Encode::undefined()).loadInAccumulator(); } + bytecodeGenerator->addInstruction(Instruction::Ret()); } + Q_ASSERT(_context == _functionContext); bytecodeGenerator->finalize(_context); - _context->registerCount = bytecodeGenerator->registerCount(); + _context->registerCountInFunction = bytecodeGenerator->registerCount(); static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); if (showCode) { qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict - << "register count" << _context->registerCount; + << "register count" << _context->registerCountInFunction << "implicit return" << requiresReturnValue; QV4::Moth::dumpBytecode(_context->code, _context->locals.size(), _context->arguments.size(), _context->line, _context->lineNumberMapping); qDebug(); @@ -2217,29 +2880,17 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, qSwap(_returnAddress, returnAddress); qSwap(requiresReturnValue, _requiresReturnValue); + qSwap(_inFormalParameterList, inFormalParameterList); bytecodeGenerator = savedBytecodeGenerator; + delete _returnLabel; + _returnLabel = savedReturnLabel; + controlFlow = savedControlFlow; + functionEndsWithReturn = savedFunctionEndsWithReturn; + _functionContext = savedFunctionContext; return leaveContext(); } -bool Codegen::visit(FunctionSourceElement *ast) -{ - if (hasError) - return false; - - statement(ast->declaration); - return false; -} - -bool Codegen::visit(StatementSourceElement *ast) -{ - if (hasError) - return false; - - statement(ast->statement); - return false; -} - bool Codegen::visit(Block *ast) { if (hasError) @@ -2247,6 +2898,7 @@ bool Codegen::visit(Block *ast) RegisterScope scope(this); + ControlFlowBlock controlFlow(this, ast); statementList(ast->statements); return false; } @@ -2256,13 +2908,13 @@ bool Codegen::visit(BreakStatement *ast) if (hasError) return false; - if (!_context->controlFlow) { + if (!controlFlow) { throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop")); return false; } - ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Break, ast->label.toString()); - if (h.type == ControlFlow::Invalid) { + ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Break, ast->label.toString()); + if (!target.linkLabel.isValid()) { if (ast->label.isEmpty()) throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop")); else @@ -2270,7 +2922,7 @@ bool Codegen::visit(BreakStatement *ast) return false; } - _context->controlFlow->jumpToHandler(h); + bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel); return false; } @@ -2282,13 +2934,13 @@ bool Codegen::visit(ContinueStatement *ast) RegisterScope scope(this); - if (!_context->controlFlow) { + if (!controlFlow) { throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Continue outside of loop")); return false; } - ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Continue, ast->label.toString()); - if (h.type == ControlFlow::Invalid) { + ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Continue, ast->label.toString()); + if (!target.linkLabel.isValid()) { if (ast->label.isEmpty()) throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString())); else @@ -2296,7 +2948,7 @@ bool Codegen::visit(ContinueStatement *ast) return false; } - _context->controlFlow->jumpToHandler(h); + bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel); return false; } @@ -2370,45 +3022,86 @@ bool Codegen::visit(ForEachStatement *ast) RegisterScope scope(this); - Reference nextIterObj = Reference::fromStackSlot(this); - Reference iterObj = Reference::fromStackSlot(this); - Reference expr = expression(ast->expression); - if (hasError) - return true; + Reference iterator = Reference::fromStackSlot(this); + Reference lhsValue = Reference::fromStackSlot(this); - expr.loadInAccumulator(); - Instruction::ForeachIteratorObject iteratorObjInstr; - bytecodeGenerator->addInstruction(iteratorObjInstr); - iterObj.storeConsumeAccumulator(); + // There should be a temporal block, so that variables declared in lhs shadow outside vars. + // This block should define a temporal dead zone for those variables, which is not yet implemented. + { + RegisterScope innerScope(this); + ControlFlowBlock controlFlow(this, ast); + Reference expr = expression(ast->expression); + if (hasError) + return true; - Reference lhs = expression(ast->initialiser).asLValue(); + expr.loadInAccumulator(); + Instruction::GetIterator iteratorObjInstr; + iteratorObjInstr.iterator = (ast->type == ForEachType::Of) ? 1 : 0; + bytecodeGenerator->addInstruction(iteratorObjInstr); + iterator.storeConsumeAccumulator(); + } BytecodeGenerator::Label in = bytecodeGenerator->newLabel(); BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label done = bytecodeGenerator->newLabel(); - bytecodeGenerator->jump().link(in); - - ControlFlowLoop flow(this, &end, &in); + { + ControlFlowLoop flow(this, &end, &in, /*requiresUnwind*/ true); + bytecodeGenerator->jump().link(in); + + BytecodeGenerator::Label body = bytecodeGenerator->label(); + + // each iteration gets it's own context, as per spec + { + RegisterScope innerScope(this); + ControlFlowBlock controlFlow(this, ast); + + if (ExpressionNode *e = ast->lhs->expressionCast()) { + if (AST::Pattern *p = e->patternCast()) { + RegisterScope scope(this); + destructurePattern(p, lhsValue); + } else { + Reference lhs = expression(e); + if (hasError) + goto error; + lhs = lhs.asLValue(); + lhsValue.loadInAccumulator(); + lhs.storeConsumeAccumulator(); + } + } else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) { + initializeAndDestructureBindingElement(p, lhsValue, /*isDefinition =*/ true); + if (hasError) + goto error; + } else { + Q_UNREACHABLE(); + } - BytecodeGenerator::Label body = bytecodeGenerator->label(); + statement(ast->statement); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); - nextIterObj.loadInAccumulator(); - lhs.storeConsumeAccumulator(); + } - statement(ast->statement); - setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); + error: + in.link(); + iterator.loadInAccumulator(); + Instruction::IteratorNext next; + next.value = lhsValue.stackSlot(); + bytecodeGenerator->addInstruction(next); + bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body); + bytecodeGenerator->jump().link(done); + } - in.link(); + end.link(); - iterObj.loadInAccumulator(); - Instruction::ForeachNextPropertyName nextPropInstr; - bytecodeGenerator->addInstruction(nextPropInstr); - nextIterObj.storeConsumeAccumulator(); + if (ast->type == ForEachType::Of) { + Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack(); + iterator.loadInAccumulator(); + Instruction::IteratorClose close; + close.done = iteratorDone.stackSlot(); + bytecodeGenerator->addInstruction(close); + } - Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator(); - bytecodeGenerator->jumpStrictNotEqual(nextIterObj.stackSlot(), body); - - end.link(); + done.link(); return false; } @@ -2420,7 +3113,12 @@ bool Codegen::visit(ForStatement *ast) RegisterScope scope(this); - statement(ast->initialiser); + ControlFlowBlock controlFlow(this, ast); + + if (ast->initialiser) + statement(ast->initialiser); + else if (ast->declarations) + variableDeclarationList(ast->declarations); BytecodeGenerator::Label cond = bytecodeGenerator->label(); BytecodeGenerator::Label body = bytecodeGenerator->newLabel(); @@ -2436,6 +3134,10 @@ bool Codegen::visit(ForStatement *ast) setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); step.link(); + if (_context->requiresExecutionContext) { + Instruction::CloneBlockContext clone; + bytecodeGenerator->addInstruction(clone); + } statement(ast->expression); bytecodeGenerator->jump().link(cond); @@ -2458,7 +3160,7 @@ bool Codegen::visit(IfStatement *ast) trueLabel.link(); statement(ast->ok); if (ast->ko) { - if (endsWithReturn(ast)) { + if (endsWithReturn(_module, ast)) { falseLabel.link(); statement(ast->ko); } else { @@ -2482,7 +3184,7 @@ bool Codegen::visit(LabelledStatement *ast) RegisterScope scope(this); // check that no outer loop contains the label - ControlFlow *l = _context->controlFlow; + ControlFlow *l = controlFlow; while (l) { if (l->label() == ast->label) { QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString()); @@ -2497,9 +3199,7 @@ bool Codegen::visit(LabelledStatement *ast) AST::cast<AST::WhileStatement *>(ast->statement) || AST::cast<AST::DoWhileStatement *>(ast->statement) || AST::cast<AST::ForStatement *>(ast->statement) || - AST::cast<AST::ForEachStatement *>(ast->statement) || - AST::cast<AST::LocalForStatement *>(ast->statement) || - AST::cast<AST::LocalForEachStatement *>(ast->statement)) { + AST::cast<AST::ForEachStatement *>(ast->statement)) { statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. } else { BytecodeGenerator::Label breakLabel = bytecodeGenerator->newLabel(); @@ -2511,85 +3211,17 @@ bool Codegen::visit(LabelledStatement *ast) return false; } -bool Codegen::visit(LocalForEachStatement *ast) +void Codegen::emitReturn(const Reference &expr) { - if (hasError) - return true; - - RegisterScope scope(this); - - Reference nextIterObj = Reference::fromStackSlot(this); - Reference iterObj = Reference::fromStackSlot(this); - Reference expr = expression(ast->expression); - if (hasError) - return true; - - variableDeclaration(ast->declaration); - - expr.loadInAccumulator(); - Instruction::ForeachIteratorObject iteratorObjInstr; - bytecodeGenerator->addInstruction(iteratorObjInstr); - iterObj.storeConsumeAccumulator(); - - BytecodeGenerator::Label in = bytecodeGenerator->newLabel(); - BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); - - bytecodeGenerator->jump().link(in); - ControlFlowLoop flow(this, &end, &in); - - BytecodeGenerator::Label body = bytecodeGenerator->label(); - - Reference it = referenceForName(ast->declaration->name.toString(), true).asLValue(); - - nextIterObj.loadInAccumulator(); - it.storeConsumeAccumulator(); - - statement(ast->statement); - setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); - - in.link(); - - iterObj.loadInAccumulator(); - Instruction::ForeachNextPropertyName nextPropInstr; - bytecodeGenerator->addInstruction(nextPropInstr); - nextIterObj.storeConsumeAccumulator(); - - Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator(); - bytecodeGenerator->jumpStrictNotEqual(nextIterObj.stackSlot(), body); - - end.link(); - - return false; -} - -bool Codegen::visit(LocalForStatement *ast) -{ - if (hasError) - return true; - - RegisterScope scope(this); - - variableDeclarationList(ast->declarations); - - BytecodeGenerator::Label cond = bytecodeGenerator->label(); - BytecodeGenerator::Label body = bytecodeGenerator->newLabel(); - BytecodeGenerator::Label step = bytecodeGenerator->newLabel(); - BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); - - ControlFlowLoop flow(this, &end, &step); - - condition(ast->condition, &body, &end, true); - - body.link(); - statement(ast->statement); - setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); - - step.link(); - statement(ast->expression); - bytecodeGenerator->jump().link(cond); - end.link(); - - return false; + ControlFlow::UnwindTarget target = controlFlow ? controlFlow->unwindTarget(ControlFlow::Return) : ControlFlow::UnwindTarget(); + if (target.linkLabel.isValid() && target.unwindLevel) { + Q_ASSERT(_returnAddress >= 0); + (void) expr.storeOnStack(_returnAddress); + bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel); + } else { + expr.loadInAccumulator(); + bytecodeGenerator->addInstruction(Instruction::Ret()); + } } bool Codegen::visit(ReturnStatement *ast) @@ -2597,7 +3229,7 @@ bool Codegen::visit(ReturnStatement *ast) if (hasError) return true; - if (_context->compilationMode != FunctionCode && _context->compilationMode != QmlBinding) { + if (_functionContext->contextType != ContextType::Function && _functionContext->contextType != ContextType::Binding) { throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function")); return false; } @@ -2610,17 +3242,8 @@ bool Codegen::visit(ReturnStatement *ast) expr = Reference::fromConst(this, Encode::undefined()); } - if (_context->controlFlow && _context->controlFlow->returnRequiresUnwind()) { - if (_returnAddress >= 0) - (void) expr.storeOnStack(_returnAddress); - else - expr.loadInAccumulator(); - ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Return); - _context->controlFlow->jumpToHandler(h); - } else { - expr.loadInAccumulator(); - bytecodeGenerator->addInstruction(Instruction::Ret()); - } + emitReturn(expr); + return false; } @@ -2629,9 +3252,14 @@ bool Codegen::visit(SwitchStatement *ast) if (hasError) return true; + if (requiresReturnValue) + Reference::fromConst(this, Encode::undefined()).storeOnStack(_returnAddress); + RegisterScope scope(this); if (ast->block) { + ControlFlowBlock controlFlow(this, ast->block); + BytecodeGenerator::Label switchEnd = bytecodeGenerator->newLabel(); Reference lhs = expression(ast->expression); @@ -2674,6 +3302,7 @@ bool Codegen::visit(SwitchStatement *ast) ControlFlowLoop flow(this, &switchEnd); + insideSwitch = true; for (CaseClauses *it = ast->block->clauses; it; it = it->next) { CaseClause *clause = it->clause; blockMap[clause].link(); @@ -2694,6 +3323,7 @@ bool Codegen::visit(SwitchStatement *ast) statementList(clause->statements); } + insideSwitch = false; switchEnd.link(); @@ -2713,25 +3343,15 @@ bool Codegen::visit(ThrowStatement *ast) if (hasError) return false; - if (_context->controlFlow) { - _context->controlFlow->handleThrow(expr); - } else { - expr.loadInAccumulator(); - Instruction::ThrowException instr; - bytecodeGenerator->addInstruction(instr); - } + expr.loadInAccumulator(); + Instruction::ThrowException instr; + bytecodeGenerator->addInstruction(instr); return false; } void Codegen::handleTryCatch(TryStatement *ast) { Q_ASSERT(ast); - if (_context->isStrict && - (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) { - throwSyntaxError(ast->catchExpression->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode")); - return; - } - RegisterScope scope(this); BytecodeGenerator::Label noException = bytecodeGenerator->newLabel(); { @@ -2761,8 +3381,6 @@ bool Codegen::visit(TryStatement *ast) if (hasError) return true; - Q_ASSERT(_context->hasTry); - RegisterScope scope(this); if (ast->finallyExpression && ast->finallyExpression->statement) { @@ -2817,17 +3435,18 @@ bool Codegen::visit(WithStatement *ast) RegisterScope scope(this); - _context->hasWith = true; - Reference src = expression(ast->expression); if (hasError) return false; src = src.storeOnStack(); // trigger load before we setup the exception handler, so exceptions here go to the right place src.loadInAccumulator(); - ControlFlowWith flow(this); - - statement(ast->statement); + enterContext(ast); + { + ControlFlowWith flow(this); + statement(ast->statement); + } + leaveContext(); return false; } @@ -3080,6 +3699,22 @@ Codegen::RValue Codegen::RValue::storeOnStack() const } } +void Codegen::RValue::loadInAccumulator() const +{ + switch (type) { + case Accumulator: + // nothing to do + return; + case StackSlot: + return Reference::fromStackSlot(codegen, theStackSlot).loadInAccumulator(); + case Const: + return Reference::fromConst(codegen, constant).loadInAccumulator(); + default: + Q_UNREACHABLE(); + } + +} + Codegen::Reference::Reference(const Codegen::Reference &other) { *this = other; @@ -3093,6 +3728,11 @@ Codegen::Reference &Codegen::Reference::operator =(const Reference &other) case Invalid: case Accumulator: break; + case Super: + break; + case SuperProperty: + property = other.property; + break; case StackSlot: theStackSlot = other.theStackSlot; break; @@ -3127,6 +3767,7 @@ Codegen::Reference &Codegen::Reference::operator =(const Reference &other) isArgOrEval = other.isArgOrEval; codegen = other.codegen; isReadonly = other.isReadonly; + isReferenceToConst = other.isReferenceToConst; stackSlotIsLocalOrArgument = other.stackSlotIsLocalOrArgument; isVolatile = other.isVolatile; global = other.global; @@ -3141,6 +3782,10 @@ bool Codegen::Reference::operator==(const Codegen::Reference &other) const case Invalid: case Accumulator: break; + case Super: + return true; + case SuperProperty: + return property == other.property; case StackSlot: return theStackSlot == other.theStackSlot; case ScopedLocal: @@ -3184,6 +3829,9 @@ Codegen::Reference Codegen::Reference::asLValue() const case Invalid: case Accumulator: Q_UNREACHABLE(); + case Super: + codegen->throwSyntaxError(AST::SourceLocation(), QStringLiteral("Super lvalues not implemented.")); + return *this; case Member: if (!propertyBase.isStackSlot()) { Reference r = *this; @@ -3209,6 +3857,28 @@ Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const return Reference(); } +Codegen::Reference Codegen::Reference::baseObject() const +{ + if (type == Reference::QmlScopeObject || type == Reference::QmlContextObject) { + return Reference::fromStackSlot(codegen, qmlBase.stackSlot()); + } else if (type == Reference::Member) { + RValue rval = propertyBase; + if (!rval.isValid()) + return Reference::fromConst(codegen, Encode::undefined()); + if (rval.isAccumulator()) + return Reference::fromAccumulator(codegen); + if (rval.isStackSlot()) + Reference::fromStackSlot(codegen, rval.stackSlot()); + if (rval.isConst()) + return Reference::fromConst(codegen, rval.constantValue()); + Q_UNREACHABLE(); + } else if (type == Reference::Subscript) { + return Reference::fromStackSlot(codegen, elementBase.stackSlot()); + } else { + return Reference::fromConst(codegen, Encode::undefined()); + } +} + Codegen::Reference Codegen::Reference::storeOnStack() const { return doStoreOnStack(-1); } @@ -3230,7 +3900,7 @@ Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const } Reference slot = Reference::fromStackSlot(codegen, slotIndex); - if (isConst()) { + if (isConstant()) { Instruction::MoveConst move; move.constIndex = codegen->registerConstant(constant); move.destTemp = slot.stackSlot(); @@ -3280,7 +3950,29 @@ bool Codegen::Reference::storeWipesAccumulator() const void Codegen::Reference::storeAccumulator() const { + if (isReferenceToConst) { + // throw a type error + RegisterScope scope(codegen); + Reference r = codegen->referenceForName(QStringLiteral("TypeError"), false); + r = r.storeOnStack(); + Instruction::Construct construct; + construct.func = r.stackSlot(); + construct.argc = 0; + construct.argv = 0; + codegen->bytecodeGenerator->addInstruction(construct); + Instruction::ThrowException throwException; + codegen->bytecodeGenerator->addInstruction(throwException); + return; + } switch (type) { + case Super: + Q_UNREACHABLE(); + return; + case SuperProperty: + Instruction::StoreSuperProperty store; + store.property = property.stackSlot(); + codegen->bytecodeGenerator->addInstruction(store); + return; case StackSlot: { Instruction::StoreReg store; store.reg = theStackSlot; @@ -3313,7 +4005,7 @@ void Codegen::Reference::storeAccumulator() const } } return; case Member: - if (codegen->useFastLookups) { + if (!disable_lookups && codegen->useFastLookups) { Instruction::SetLookup store; store.base = propertyBase.stackSlot(); store.index = codegen->registerSetterLookup(propertyNameIndex); @@ -3358,6 +4050,14 @@ void Codegen::Reference::loadInAccumulator() const switch (type) { case Accumulator: return; + case Super: + Q_UNREACHABLE(); + return; + case SuperProperty: + Instruction::LoadSuperProperty load; + load.property = property.stackSlot(); + codegen->bytecodeGenerator->addInstruction(load); + return; case Const: { QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs. @@ -3429,7 +4129,7 @@ QT_WARNING_POP return; } } - if (codegen->useFastLookups && global) { + if (!disable_lookups && codegen->useFastLookups && global) { Instruction::LoadGlobalLookup load; load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); codegen->bytecodeGenerator->addInstruction(load); @@ -3440,46 +4140,23 @@ QT_WARNING_POP } return; case Member: - if (codegen->useFastLookups) { - if (propertyBase.isAccumulator()) { - Instruction::GetLookupA load; - load.index = codegen->registerGetterLookup(propertyNameIndex); - codegen->bytecodeGenerator->addInstruction(load); - } else { - Instruction::GetLookup load; - load.base = propertyBase.storeOnStack().stackSlot(); - load.index = codegen->registerGetterLookup(propertyNameIndex); - codegen->bytecodeGenerator->addInstruction(load); - } - } else { - if (propertyBase.isAccumulator()) { - Instruction::LoadPropertyA load; - load.name = propertyNameIndex; - codegen->bytecodeGenerator->addInstruction(load); - } else { - Instruction::LoadProperty load; - load.base = propertyBase.storeOnStack().stackSlot(); - load.name = propertyNameIndex; - codegen->bytecodeGenerator->addInstruction(load); - } - } - return; - case Subscript: { - if (elementSubscript.isAccumulator()) { - Instruction::LoadElementA load; - load.base = elementBase; - codegen->bytecodeGenerator->addInstruction(load); - } else if (elementSubscript.isConst()) { - Reference::fromConst(codegen, elementSubscript.constantValue()).loadInAccumulator(); - Instruction::LoadElementA load; - load.base = elementBase; + if (!disable_lookups && codegen->useFastLookups) { + propertyBase.loadInAccumulator(); + Instruction::GetLookup load; + load.index = codegen->registerGetterLookup(propertyNameIndex); codegen->bytecodeGenerator->addInstruction(load); } else { - Instruction::LoadElement load; - load.base = elementBase; - load.index = elementSubscript.storeOnStack().stackSlot(); + propertyBase.loadInAccumulator(); + Instruction::LoadProperty load; + load.name = propertyNameIndex; codegen->bytecodeGenerator->addInstruction(load); } + return; + case Subscript: { + elementSubscript.loadInAccumulator(); + Instruction::LoadElement load; + load.base = elementBase; + codegen->bytecodeGenerator->addInstruction(load); } return; case QmlScopeObject: { Instruction::LoadScopeObjectProperty load; diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index d51dc29517..337c4dbfe3 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -66,6 +66,7 @@ #endif #include <private/qv4util_p.h> #include <private/qv4bytecodegenerator_p.h> +#include <private/qv4stackframe_p.h> QT_BEGIN_NAMESPACE @@ -101,7 +102,7 @@ public: const QString &sourceCode, AST::Program *ast, Module *module, - CompilationMode mode = GlobalCode); + ContextType contextType = ContextType::Global); public: class VolatileMemoryLocationScanner; @@ -172,11 +173,14 @@ public: } Q_REQUIRED_RESULT RValue storeOnStack() const; + void loadInAccumulator() const; }; struct Reference { enum Type { Invalid, Accumulator, + Super, + SuperProperty, StackSlot, ScopedLocal, Name, @@ -197,6 +201,8 @@ public: Reference &operator =(const Reference &other); bool operator==(const Reference &other) const; + bool operator!=(const Reference &other) const + { return !(*this == other); } bool isValid() const { return type != Invalid; } bool loadTriggersSideEffect() const { @@ -209,8 +215,10 @@ public: return false; } } - bool isConst() const { return type == Const; } + bool isConstant() const { return type == Const; } bool isAccumulator() const { return type == Accumulator; } + bool isSuper() const { return type == Super; } + bool isSuperProperty() const { return type == SuperProperty; } bool isStackSlot() const { return type == StackSlot; } bool isRegister() const { return isStackSlot(); @@ -241,6 +249,9 @@ public: static Reference fromAccumulator(Codegen *cg) { return Reference(cg, Accumulator); } + static Reference fromSuper(Codegen *cg) { + return Reference(cg, Super); + } static Reference fromStackSlot(Codegen *cg, int tempIndex = -1, bool isLocal = false) { Reference r(cg, StackSlot); if (tempIndex == -1) @@ -273,6 +284,12 @@ public: r.propertyNameIndex = r.codegen->registerString(name); return r; } + static Reference fromSuperProperty(const Reference &property) { + Q_ASSERT(property.isStackSlot()); + Reference r(property.codegen, SuperProperty); + r.property = property.stackSlot(); + return r; + } static Reference fromSubscript(const Reference &baseRef, const Reference &subscript) { Q_ASSERT(baseRef.isStackSlot()); Reference r(baseRef.codegen, Subscript); @@ -322,6 +339,8 @@ public: Q_REQUIRED_RESULT Reference storeRetainAccumulator() const; Reference storeConsumeAccumulator() const; + Q_REQUIRED_RESULT Reference baseObject() const; + bool storeWipesAccumulator() const; void loadInAccumulator() const; @@ -357,10 +376,12 @@ public: qint16 qmlNotifyIndex; PropertyCapturePolicy capturePolicy; }; + Moth::StackSlot property; // super property }; QString name; mutable bool isArgOrEval = false; bool isReadonly = false; + bool isReferenceToConst = false; bool stackSlotIsLocalOrArgument = false; bool isVolatile = false; bool global = false; @@ -464,7 +485,10 @@ protected: void enterContext(AST::Node *node); int leaveContext(); - +public: + Context *enterBlock(AST::Node *node); + int leaveBlock() { return leaveContext(); } +protected: void leaveLoop(); enum UnaryOperation { @@ -482,6 +506,7 @@ protected: void addCJump(); +public: int registerString(const QString &name) { return jsUnitGenerator->registerString(name); } @@ -493,33 +518,37 @@ protected: // Returns index in _module->functions virtual int defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, - AST::SourceElements *body); + AST::StatementList *body); +protected: void statement(AST::Statement *ast); void statement(AST::ExpressionNode *ast); void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue, const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition); Reference expression(AST::ExpressionNode *ast); - Result sourceElement(AST::SourceElement *ast); void accept(AST::Node *node); - void functionBody(AST::FunctionBody *ast); void program(AST::Program *ast); - void sourceElements(AST::SourceElements *ast); void statementList(AST::StatementList *ast); - void variableDeclaration(AST::VariableDeclaration *ast); + void variableDeclaration(AST::PatternElement *ast); void variableDeclarationList(AST::VariableDeclarationList *ast); - Reference referenceForName(const QString &name, bool lhs); + Reference targetForPatternElement(AST::PatternElement *p); + void initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &baseRef = Reference(), bool isDefinition = false); + void destructurePropertyList(const Reference &object, AST::PatternPropertyList *bindingList, bool isDefinition = false); + void destructureElementList(const Reference &array, AST::PatternElementList *bindingList, bool isDefinition = false); + void destructurePattern(AST::Pattern *p, const Reference &rhs); - void loadClosure(int index); + Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name); // Hook provided to implement QML lookup semantics virtual Reference fallbackNameLookup(const QString &name); virtual void beginFunctionBodyHook() {} + void emitReturn(const Reference &expr); + // nodes bool visit(AST::ArgumentList *ast) override; bool visit(AST::CaseBlock *ast) override; @@ -527,16 +556,10 @@ protected: bool visit(AST::CaseClauses *ast) override; bool visit(AST::Catch *ast) override; bool visit(AST::DefaultClause *ast) override; - bool visit(AST::ElementList *ast) override; bool visit(AST::Elision *ast) override; bool visit(AST::Finally *ast) override; bool visit(AST::FormalParameterList *ast) override; - bool visit(AST::FunctionBody *ast) override; bool visit(AST::Program *ast) override; - bool visit(AST::PropertyNameAndValue *ast) override; - bool visit(AST::PropertyAssignmentList *ast) override; - bool visit(AST::PropertyGetterSetter *ast) override; - bool visit(AST::SourceElements *ast) override; bool visit(AST::StatementList *ast) override; bool visit(AST::UiArrayMemberList *ast) override; bool visit(AST::UiImport *ast) override; @@ -548,19 +571,25 @@ protected: bool visit(AST::UiProgram *ast) override; bool visit(AST::UiQualifiedId *ast) override; bool visit(AST::UiQualifiedPragmaId *ast) override; - bool visit(AST::VariableDeclaration *ast) override; bool visit(AST::VariableDeclarationList *ast) override; + bool visit(AST::PatternElement *ast) override; + bool visit(AST::PatternElementList *ast) override; + bool visit(AST::PatternProperty *ast) override; + bool visit(AST::PatternPropertyList *ast) override; + // expressions bool visit(AST::Expression *ast) override; - bool visit(AST::ArrayLiteral *ast) override; + bool visit(AST::ArrayPattern *ast) override; bool visit(AST::ArrayMemberExpression *ast) override; bool visit(AST::BinaryExpression *ast) override; bool visit(AST::CallExpression *ast) override; bool visit(AST::ConditionalExpression *ast) override; bool visit(AST::DeleteExpression *ast) override; bool visit(AST::FalseLiteral *ast) override; + bool visit(AST::SuperLiteral *ast) override; bool visit(AST::FieldMemberExpression *ast) override; + bool visit(AST::TaggedTemplate *ast) override; bool visit(AST::FunctionExpression *ast) override; bool visit(AST::IdentifierExpression *ast) override; bool visit(AST::NestedExpression *ast) override; @@ -569,13 +598,14 @@ protected: bool visit(AST::NotExpression *ast) override; bool visit(AST::NullExpression *ast) override; bool visit(AST::NumericLiteral *ast) override; - bool visit(AST::ObjectLiteral *ast) override; + bool visit(AST::ObjectPattern *ast) override; bool visit(AST::PostDecrementExpression *ast) override; bool visit(AST::PostIncrementExpression *ast) override; bool visit(AST::PreDecrementExpression *ast) override; bool visit(AST::PreIncrementExpression *ast) override; bool visit(AST::RegExpLiteral *ast) override; bool visit(AST::StringLiteral *ast) override; + bool visit(AST::TemplateLiteral *ast) override; bool visit(AST::ThisExpression *ast) override; bool visit(AST::TildeExpression *ast) override; bool visit(AST::TrueLiteral *ast) override; @@ -584,10 +614,9 @@ protected: bool visit(AST::UnaryPlusExpression *ast) override; bool visit(AST::VoidExpression *ast) override; bool visit(AST::FunctionDeclaration *ast) override; - - // source elements - bool visit(AST::FunctionSourceElement *ast) override; - bool visit(AST::StatementSourceElement *ast) override; + bool visit(AST::YieldExpression *ast) override; + bool visit(AST::ClassExpression *ast) override; + bool visit(AST::ClassDeclaration *ast) override; // statements bool visit(AST::Block *ast) override; @@ -601,8 +630,6 @@ protected: bool visit(AST::ForStatement *ast) override; bool visit(AST::IfStatement *ast) override; bool visit(AST::LabelledStatement *ast) override; - bool visit(AST::LocalForEachStatement *ast) override; - bool visit(AST::LocalForStatement *ast) override; bool visit(AST::ReturnStatement *ast) override; bool visit(AST::SwitchStatement *ast) override; bool visit(AST::ThrowStatement *ast) override; @@ -631,18 +658,36 @@ public: Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right); Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right); - struct Arguments { int argc; int argv; }; + struct Arguments { int argc; int argv; bool hasSpread; }; Arguments pushArgs(AST::ArgumentList *args); + void handleCall(Reference &base, Arguments calldata); + + Arguments pushTemplateArgs(AST::TemplateLiteral *args); + int createTemplateArray(AST::TemplateLiteral *t); void setUseFastLookups(bool b) { useFastLookups = b; } void handleTryCatch(AST::TryStatement *ast); void handleTryFinally(AST::TryStatement *ast); + + Reference referenceForName(const QString &name, bool lhs); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit(bool generateUnitData = true); static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading(); Context *currentContext() const { return _context; } + BytecodeGenerator *generator() const { return bytecodeGenerator; } + + void loadClosure(int index); + + Module *module() const { return _module; } + + BytecodeGenerator::Label returnLabel() { + if (!_returnLabel) + _returnLabel = new BytecodeGenerator::Label(bytecodeGenerator->newLabel()); + return *_returnLabel; + } protected: friend class ScanFunctions; @@ -650,16 +695,22 @@ protected: friend struct ControlFlowCatch; friend struct ControlFlowFinally; Result _expr; - VolatileMemoryLocations _volataleMemoryLocations; + VolatileMemoryLocations _volatileMemoryLocations; Module *_module; int _returnAddress; Context *_context; + Context *_functionContext = nullptr; AST::LabelledStatement *_labelledStatement; QV4::Compiler::JSUnitGenerator *jsUnitGenerator; BytecodeGenerator *bytecodeGenerator = nullptr; + Moth::BytecodeGenerator::Label *_returnLabel = nullptr; bool _strictMode; bool useFastLookups = true; bool requiresReturnValue = false; + bool insideSwitch = false; + bool inFormalParameterList = false; + bool functionEndsWithReturn = false; + ControlFlow *controlFlow = nullptr; bool _fileNameIsUrl; bool hasError; @@ -667,6 +718,7 @@ protected: private: VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const; + void handleConstruct(const Reference &base, AST::ArgumentList *args); }; } diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp index 8348613888..1fef4d38f4 100644 --- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp +++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp @@ -92,8 +92,16 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co void CompilationUnitMapper::close() { - if (dataPtr != nullptr) - munmap(dataPtr, length); + // Do not unmap the data here. + if (dataPtr != nullptr) { + // Do not unmap cache files that are built with the StaticData flag. That's the majority of + // them and it's necessary to benefit from the QString literal optimization. There might + // still be QString instances around that point into that memory area. The memory is backed + // on the disk, so the kernel is free to release the pages and all that remains is the + // address space allocation. + if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData)) + munmap(dataPtr, length); + } dataPtr = nullptr; } diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp index 8b000021f8..3e44d045fc 100644 --- a/src/qml/compiler/qv4compilationunitmapper_win.cpp +++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp @@ -90,14 +90,9 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co if (!header.verifyHeader(sourceTimeStamp, errorString)) return nullptr; - const uint mappingFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode - ? PAGE_EXECUTE_READ : PAGE_READONLY; - const uint viewFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode - ? (FILE_MAP_READ | FILE_MAP_EXECUTE) : FILE_MAP_READ; - // Data structure and qt version matched, so now we can access the rest of the file safely. - HANDLE fileMappingHandle = CreateFileMapping(handle, 0, mappingFlags, 0, 0, 0); + HANDLE fileMappingHandle = CreateFileMapping(handle, 0, PAGE_READONLY, 0, 0, 0); if (!fileMappingHandle) { *errorString = qt_error_string(GetLastError()); return nullptr; @@ -107,7 +102,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co CloseHandle(fileMappingHandle); }); - dataPtr = MapViewOfFile(fileMappingHandle, viewFlags, 0, 0, 0); + dataPtr = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 0); if (!dataPtr) { *errorString = qt_error_string(GetLastError()); return nullptr; @@ -118,8 +113,15 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co void CompilationUnitMapper::close() { - if (dataPtr != nullptr) - UnmapViewOfFile(dataPtr); + if (dataPtr != nullptr) { + // Do not unmap cache files that are built with the StaticData flag. That's the majority of + // them and it's necessary to benefit from the QString literal optimization. There might + // still be QString instances around that point into that memory area. The memory is backed + // on the disk, so the kernel is free to release the pages and all that remains is the + // address space allocation. + if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData)) + UnmapViewOfFile(dataPtr); + } dataPtr = nullptr; } diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 8dcc068a06..02aee47eea 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -78,26 +78,10 @@ namespace CompiledData { static_assert(sizeof(Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version"); -#if !defined(V4_BOOTSTRAP) -static QString cacheFilePath(const QUrl &url) +CompilationUnit::CompilationUnit(const Unit *unitData) { - const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); - const QString localCachePath = localSourcePath + QLatin1Char('c'); -#ifndef Q_OS_ANDROID - if (QFile::exists(localCachePath) || QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable()) - return localCachePath; -#endif - QCryptographicHash fileNameHash(QCryptographicHash::Sha1); - fileNameHash.addData(localSourcePath.toUtf8()); - QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); - QDir::root().mkpath(directory); - return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix(); + data = unitData; } -#endif - -CompilationUnit::CompilationUnit(const Unit *unitData) - : data(unitData) -{} #ifndef V4_BOOTSTRAP CompilationUnit::~CompilationUnit() @@ -108,6 +92,17 @@ CompilationUnit::~CompilationUnit() data = nullptr; } +QString CompilationUnit::localCacheFilePath(const QUrl &url) +{ + 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/"); + QDir::root().mkpath(directory); + return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix; +} + QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) { this->engine = engine; @@ -157,15 +152,15 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) } if (data->jsClassTableSize) { - runtimeClasses = (QV4::InternalClass**)malloc(data->jsClassTableSize * sizeof(QV4::InternalClass*)); + runtimeClasses = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *)); + // memset the regexps to 0 in case a GC run happens while we're within the loop below + memset(runtimeClasses, 0, data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *)); for (uint i = 0; i < data->jsClassTableSize; ++i) { int memberCount = 0; const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount); - QV4::InternalClass *klass = engine->internalClasses[QV4::ExecutionEngine::Class_Object]; + runtimeClasses[i] = engine->internalClasses(QV4::ExecutionEngine::Class_Object); for (int j = 0; j < memberCount; ++j, ++member) - klass = klass->addMember(engine->identifierTable->identifier(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data); - - runtimeClasses[i] = klass; + runtimeClasses[i] = runtimeClasses[i]->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data); } } @@ -215,8 +210,6 @@ void CompilationUnit::unlink() propertyCaches.clear(); - for (int ii = 0; ii < dependentScripts.count(); ++ii) - dependentScripts.at(ii)->release(); dependentScripts.clear(); typeNameCache = nullptr; @@ -244,29 +237,43 @@ void CompilationUnit::unlink() void CompilationUnit::markObjects(QV4::MarkStack *markStack) { - for (uint i = 0; i < data->stringTableSize; ++i) - if (runtimeStrings[i]) - runtimeStrings[i]->mark(markStack); + if (runtimeStrings) { + for (uint i = 0; i < data->stringTableSize; ++i) + if (runtimeStrings[i]) + runtimeStrings[i]->mark(markStack); + } if (runtimeRegularExpressions) { for (uint i = 0; i < data->regexpTableSize; ++i) runtimeRegularExpressions[i].mark(markStack); } + if (runtimeClasses) { + for (uint i = 0; i < data->jsClassTableSize; ++i) + if (runtimeClasses[i]) + runtimeClasses[i]->mark(markStack); + } + for (QV4::Function *f : qAsConst(runtimeFunctions)) + if (f && f->internalClass) + f->internalClass->mark(markStack); + for (QV4::Heap::InternalClass *c : qAsConst(runtimeBlocks)) + if (c) + c->mark(markStack); + + if (runtimeLookups) { + for (uint i = 0; i < data->lookupTableSize; ++i) + runtimeLookups[i].markObjects(markStack); + } } -IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex) +IdentifierHash CompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex) { - auto it = namedObjectsPerComponentCache.find(componentObjectIndex); - if (it == namedObjectsPerComponentCache.end()) { - IdentifierHash namedObjectCache(engine); - const CompiledData::Object *component = data->objectAt(componentObjectIndex); - const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable(); - for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) { - const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr); - namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id); - } - it = namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache); - } - return *it; + IdentifierHash namedObjectCache(engine); + const CompiledData::Object *component = data->objectAt(componentObjectIndex); + const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable(); + for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) { + const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr); + namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id); + } + return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache); } void CompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine) @@ -344,7 +351,11 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeS const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); - CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourceTimeStamp, errorString); + QString cachePath = sourcePath + QLatin1Char('c'); + if (!QFile::exists(cachePath)) + cachePath = localCacheFilePath(url); + + CompiledData::Unit *mappedUnit = cacheFile->open(cachePath, sourceTimeStamp, errorString); if (!mappedUnit) return false; @@ -367,7 +378,22 @@ void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) runtimeFunctions.resize(data->functionTableSize); for (int i = 0 ;i < runtimeFunctions.size(); ++i) { const QV4::CompiledData::Function *compiledFunction = data->functionAt(i); - runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction, &Moth::VME::exec); + runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction); + } + + Scope scope(engine); + Scoped<InternalClass> ic(scope); + + runtimeBlocks.resize(data->blockTableSize); + for (int i = 0 ;i < runtimeBlocks.size(); ++i) { + const QV4::CompiledData::Block *compiledBlock = data->blockAt(i); + ic = engine->internalClasses(EngineBase::Class_CallContext); + + // first locals + const quint32_le *localsIndices = compiledBlock->localsTable(); + for (quint32 i = 0; i < compiledBlock->nLocals; ++i) + ic = ic->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); + runtimeBlocks[i] = ic->d(); } } @@ -391,7 +417,7 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) *errorString = QStringLiteral("File has to be a local file."); return false; } - const QString outputFileName = cacheFilePath(unitUrl); + const QString outputFileName = localCacheFilePath(unitUrl); #endif #if QT_CONFIG(temporaryfile) @@ -476,8 +502,11 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) QQmlJS::AST::FormalParameterList *parameters = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node)->formals; changedSignalParameters << parameters; - for (; parameters; parameters = parameters->next) - stringTable.registerString(parameters->name.toString()); + if (parameters) { + const QStringList formals = parameters->formals(); + for (const QString &arg : formals) + stringTable.registerString(arg); + } } } @@ -500,14 +529,14 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) function->formalsOffset = signalParameterNameTableOffset - jsUnit->functionOffsetTable()[functionIndex]; - for (QQmlJS::AST::FormalParameterList *parameters = changedSignalParameters.at(i); - parameters; parameters = parameters->next) { - signalParameterNameTable.append(stringTable.getStringId(parameters->name.toString())); - function->nFormals = function->nFormals + 1; - } + if (QQmlJS::AST::FormalParameterList *parameters = changedSignalParameters.at(i)) { + const QStringList formals = parameters->formals(); + for (const QString &arg : formals) + signalParameterNameTable.append(stringTable.getStringId(arg)); - // Hack to ensure an activation is created. - function->flags |= QV4::CompiledData::Function::HasCatchOrWith | QV4::CompiledData::Function::HasDirectEval; + function->nFormals = formals.size(); + } + function->length = function->nFormals; signalParameterNameTableOffset += function->nFormals * sizeof(quint32); } @@ -626,7 +655,7 @@ QString Binding::valueAsScriptString(const Unit *unit) const /*! Returns the property cache, if one alread exists. The cache is not referenced. */ -QQmlPropertyCache *ResolvedTypeReference::propertyCache() const +QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::propertyCache() const { if (type.isValid()) return typePropertyCache; @@ -637,7 +666,7 @@ QQmlPropertyCache *ResolvedTypeReference::propertyCache() const /*! Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. */ -QQmlPropertyCache *ResolvedTypeReference::createPropertyCache(QQmlEngine *engine) +QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache(QQmlEngine *engine) { if (typePropertyCache) { return typePropertyCache; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 1df9d6794f..c1be00ea27 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -186,32 +186,72 @@ struct JSClass }; static_assert(sizeof(JSClass) == 4, "JSClass structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +// This data structure is intended to be binary compatible with QStringData/QStaticStringData on +// 64-bit and 32-bit little-endian architectures, in all directions. So the same structure mapped +// from a file must be castable to a QStringData regardless of the pointer size. With the first +// few fields that's easy, they're always 32-bit. However the offset field of QArrayData is a +// ptrdiff_t and thus variable in size. +// On 64-bit systems compilers enforce an 8-byte alignment and thus place it at offset 16, while +// on 32-bit systems offset 12 is sufficient. Therefore the two values don't overlap and contain +// the same value. struct String { + qint32_le refcount; // -1 qint32_le size; + quint32_le allocAndCapacityReservedFlag; // 0 + quint32_le offsetOn32Bit; + quint64_le offsetOn64Bit; // uint16 strdata[] static int calculateSize(const QString &str) { - return (sizeof(String) + str.length() * sizeof(quint16) + 7) & ~0x7; + return (sizeof(String) + (str.length() + 1) * sizeof(quint16) + 7) & ~0x7; } }; -static_assert(sizeof(String) == 4, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(String) == 24, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +// Ensure compatibility with QString +static_assert(offsetof(QArrayData, ref) == offsetof(String, refcount), "refcount must be at the same location"); +static_assert(offsetof(QArrayData, size) == offsetof(String, size), "size must be at the same location"); +static_assert(offsetof(String, offsetOn64Bit) == 16, "offset must be at 8-byte aligned location"); +static_assert(offsetof(String, offsetOn32Bit) == 12, "offset must be at 4-byte aligned location"); +#if QT_POINTER_SIZE == 8 +static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn64Bit), "offset must be at the same location"); +#else +static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn32Bit), "offset must be at the same location"); +#endif struct CodeOffsetToLine { quint32_le codeOffset; quint32_le line; }; +struct Block +{ + quint32_le nLocals; + quint32_le localsOffset; + + const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); } + + static int calculateSize(int nLocals) { + int trailingData = nLocals*sizeof (quint32); + size_t size = align(align(sizeof(Block)) + size_t(trailingData)); + Q_ASSERT(size < INT_MAX); + return int(size); + } + + static size_t align(size_t a) { + return (a + 7) & ~size_t(7); + } +}; + // Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties // for unaligned access. The ordering of the fields is also from largest to smallest. struct Function { enum Flags : unsigned int { IsStrict = 0x1, - HasDirectEval = 0x2, - UsesArgumentsObject = 0x4, -// Unused = 0x8, - HasCatchOrWith = 0x10 + IsArrowFunction = 0x2, + IsGenerator = 0x4 }; // Absolute offset into file where the code for this function is located. @@ -219,6 +259,7 @@ struct Function quint32_le codeSize; quint32_le nameIndex; + quint32_le length; quint32_le nFormals; quint32_le formalsOffset; quint32_le nLocals; @@ -238,16 +279,14 @@ struct Function quint32_le dependingScopePropertiesOffset; // Array of int pairs (property index and notify index) // Qml Extensions End -// quint32 formalsIndex[nFormals] -// quint32 localsIndex[nLocals] -// quint32 offsetForInnerFunctions[nInnerFunctions] -// Function[nInnerFunctions] - // Keep all unaligned data at the end quint8 flags; quint8 padding1; quint16_le padding2; + // quint32 formalsIndex[nFormals] + // quint32 localsIndex[nLocals] + const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + formalsOffset); } const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); } const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset); } @@ -260,7 +299,7 @@ struct Function const quint32_le *formalsEnd() const { return formalsTable() + nFormals; } // --- - const uchar *code() const { return reinterpret_cast<const uchar *>(this) + codeOffset; } + const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; } inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; } @@ -276,7 +315,44 @@ struct Function return (a + 7) & ~size_t(7); } }; -static_assert(sizeof(Function) == 76, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Function) == 80, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct Method { + enum Type { + Regular, + Getter, + Setter + }; + + quint32_le name; + quint32_le type; + quint32_le function; +}; + +struct Class +{ + quint32_le nameIndex; + quint32_le scopeIndex; + quint32_le constructorFunction; + quint32_le nStaticMethods; + quint32_le nMethods; + quint32_le methodTableOffset; + + const Method *methodTable() const { return reinterpret_cast<const Method *>(reinterpret_cast<const char *>(this) + methodTableOffset); } + + static int calculateSize(int nStaticMethods, int nMethods) { + int trailingData = (nStaticMethods + nMethods) * sizeof(Method); + size_t size = align(sizeof(Class) + trailingData); + Q_ASSERT(size < INT_MAX); + return int(size); + } + + static size_t align(size_t a) { + return (a + 7) & ~size_t(7); + } +}; +static_assert(sizeof(Class) == 24, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + // Qml data structures @@ -385,8 +461,8 @@ struct Q_QML_PRIVATE_EXPORT Binding static QString escapedString(const QString &string); - bool containsTranslations() const { return type == Type_Translation || type == Type_TranslationById; } - bool evaluatesToString() const { return type == Type_String || containsTranslations(); } + bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; } + bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); } QString valueAsString(const Unit *unit) const; QString valueAsScriptString(const Unit *unit) const; @@ -701,14 +777,17 @@ struct Unit StaticData = 0x4, // Unit data persistent in memory? IsSingleton = 0x8, IsSharedLibrary = 0x10, // .pragma shared? - ContainsMachineCode = 0x20, // used to determine if we need to mmap with execute permissions - PendingTypeCompilation = 0x40 // the QML data structures present are incomplete and require type compilation + PendingTypeCompilation = 0x20 // the QML data structures present are incomplete and require type compilation }; quint32_le flags; quint32_le stringTableSize; quint32_le offsetToStringTable; quint32_le functionTableSize; quint32_le offsetToFunctionTable; + quint32_le classTableSize; + quint32_le offsetToClassTable; + quint32_le blockTableSize; + quint32_le offsetToBlockTable; quint32_le lookupTableSize; quint32_le offsetToLookupTable; quint32_le regexpTableSize; @@ -753,11 +832,11 @@ struct Unit if (str->size == 0) return QString(); #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + if (flags & StaticData) { + const QStringDataPtr holder = { const_cast<QStringData *>(reinterpret_cast<const QStringData*>(str)) }; + return QString(holder); + } const QChar *characters = reinterpret_cast<const QChar *>(str + 1); - // Too risky to do this while we unmap disk backed compilation but keep pointers to string - // data in the identifier tables. - // if (flags & StaticData) - // return QString::fromRawData(characters, str->size); return QString(characters, str->size); #else const quint16_le *characters = reinterpret_cast<const quint16_le *>(str + 1); @@ -770,6 +849,8 @@ struct Unit } const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); } + const quint32_le *classOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToClassTable); } + const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); } const Function *functionAt(int idx) const { const quint32_le *offsetTable = functionOffsetTable(); @@ -777,6 +858,18 @@ struct Unit return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset); } + const Class *classAt(int idx) const { + const quint32_le *offsetTable = classOffsetTable(); + const quint32_le offset = offsetTable[idx]; + return reinterpret_cast<const Class *>(reinterpret_cast<const char *>(this) + offset); + } + + const Block *blockAt(int idx) const { + const quint32_le *offsetTable = blockOffsetTable(); + const quint32_le offset = offsetTable[idx]; + return reinterpret_cast<const Block *>(reinterpret_cast<const char *>(this) + offset); + } + const Lookup *lookupTable() const { return reinterpret_cast<const Lookup*>(reinterpret_cast<const char *>(this) + offsetToLookupTable); } const RegExp *regexpAt(int index) const { return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp)); @@ -795,7 +888,7 @@ struct Unit } }; -static_assert(sizeof(Unit) == 192, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Unit) == 208, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct TypeReference { @@ -832,9 +925,7 @@ struct TypeReferenceMap : QHash<int, TypeReference> auto propEnd = obj->propertiesEnd(); for ( ; prop != propEnd; ++prop) { if (prop->type >= QV4::CompiledData::Property::Custom) { - // ### FIXME: We could report the more accurate location here by using prop->location, but the old - // compiler can't and the tests expect it to be the object location right now. - TypeReference &r = this->add(prop->customTypeNameIndex, obj->location); + TypeReference &r = this->add(prop->customTypeNameIndex, prop->location); r.errorWhenNotFound = true; } } @@ -881,6 +972,8 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnitBase QV4::Heap::String **runtimeStrings = nullptr; // Array const Value* constants = nullptr; QV4::Value *runtimeRegularExpressions = nullptr; + const Unit *data = nullptr; + QV4::Heap::InternalClass **runtimeClasses = nullptr; }; Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value); @@ -915,8 +1008,6 @@ public: return refCount.load(); } - const Unit *data = nullptr; - // Called only when building QML, when we build the header for JS first and append QML data QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument); @@ -943,14 +1034,14 @@ public: } QV4::Lookup *runtimeLookups = nullptr; - QV4::InternalClass **runtimeClasses = nullptr; QVector<QV4::Function *> runtimeFunctions; + QVector<QV4::Heap::InternalClass *> runtimeBlocks; mutable QQmlNullableValue<QUrl> m_url; mutable QQmlNullableValue<QUrl> m_finalUrl; // QML specific fields QQmlPropertyCacheVector propertyCaches; - QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(/*root object*/0); } + QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); } QQmlRefPointer<QQmlTypeNameCache> typeNameCache; @@ -962,7 +1053,7 @@ public: // mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects // this is initialized on-demand by QQmlContextData QHash<int, IdentifierHash> namedObjectsPerComponentCache; - IdentifierHash namedObjectsPerComponent(int componentObjectIndex); + inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex); void finalizeCompositeType(QQmlEnginePrivate *qmlEngine); @@ -970,7 +1061,7 @@ public: int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses int totalObjectCount = 0; // Number of objects explicitly instantiated - QVector<QQmlScriptData *> dependentScripts; + QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts; ResolvedTypeReferenceMap resolvedTypes; bool verifyChecksum(const DependentTypesHasher &dependencyHasher) const; @@ -1010,6 +1101,8 @@ public: bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString); + static QString localCacheFilePath(const QUrl &url); + protected: void linkBackendToEngine(QV4::ExecutionEngine *engine); #endif // V4_BOOTSTRAP @@ -1019,6 +1112,8 @@ private: QAtomicInt refCount = 1; + Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex); + public: #if defined(V4_BOOTSTRAP) bool saveToDisk(const QString &outputFileName, QString *errorString); @@ -1046,17 +1141,24 @@ struct ResolvedTypeReference // therefore cannot have a property cache installed when instantiated. bool isFullyDynamicType; - QQmlPropertyCache *propertyCache() const; - QQmlPropertyCache *createPropertyCache(QQmlEngine *); + QQmlRefPointer<QQmlPropertyCache> propertyCache() const; + QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *); bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); void doDynamicTypeCheck(); }; -#endif +IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex) +{ + auto it = namedObjectsPerComponentCache.find(componentObjectIndex); + if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end())) + return createNamedObjectsPerComponent(componentObjectIndex); + return *it; } +#endif // V4_BOOTSTRAP -} +} // CompiledData namespace +} // QV4 namespace Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE); diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index c9e535c93f..f1afad4965 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -90,7 +90,11 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) const QString &qstr = strings.at(i); QV4::CompiledData::String *s = reinterpret_cast<QV4::CompiledData::String *>(stringData); + s->refcount = -1; s->size = qstr.length(); + s->allocAndCapacityReservedFlag = 0; + s->offsetOn32Bit = sizeof(QV4::CompiledData::String); + s->offsetOn64Bit = sizeof(QV4::CompiledData::String); #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN memcpy(s + 1, qstr.constData(), qstr.length()*sizeof(ushort)); #else @@ -98,6 +102,7 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) for (int i = 0; i < qstr.length(); ++i) uc[i] = qToLittleEndian<ushort>(qstr.at(i).unicode()); #endif + reinterpret_cast<ushort *>(s + 1)[s->size] = 0; stringData += QV4::CompiledData::String::calculateSize(qstr); } @@ -183,7 +188,7 @@ QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx) return constants.at(idx); } -int QV4::Compiler::JSUnitGenerator::registerJSClass(const QVector<MemberInfo> &members) +int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members) { // ### re-use existing class definitions. @@ -197,31 +202,15 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(const QVector<MemberInfo> &m jsClass->nMembers = members.size(); CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1); - for (const MemberInfo &memberInfo : members) { - member->nameOffset = registerString(memberInfo.name); - member->isAccessor = memberInfo.isAccessor; + for (const auto &name : members) { + member->nameOffset = registerString(name); + member->isAccessor = false; ++member; } return jsClassOffsets.size() - 1; } -int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, CompiledData::JSClassMember *members) -{ - const int size = CompiledData::JSClass::calculateSize(count); - jsClassOffsets.append(jsClassData.size()); - const int oldSize = jsClassData.size(); - jsClassData.resize(jsClassData.size() + size); - memset(jsClassData.data() + oldSize, 0, size); - - CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize); - jsClass->nMembers = count; - CompiledData::JSClassMember *jsClassMembers = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1); - memcpy(jsClassMembers, members, sizeof(CompiledData::JSClassMember)*count); - - return jsClassOffsets.size() - 1; -} - QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option) { registerString(module->fileName); @@ -233,28 +222,46 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO for (int i = 0; i < f->locals.size(); ++i) registerString(f->locals.at(i)); } + for (Context *c : qAsConst(module->blocks)) { + for (int i = 0; i < c->locals.size(); ++i) + registerString(c->locals.at(i)); + } - Q_ALLOCA_VAR(quint32_le, functionOffsets, module->functions.size() * sizeof(quint32_le)); + Q_ALLOCA_VAR(quint32_le, blockClassAndFunctionOffsets, (module->functions.size() + module->classes.size() + module->blocks.size()) * sizeof(quint32_le)); uint jsClassDataOffset = 0; char *dataPtr; CompiledData::Unit *unit; { - QV4::CompiledData::Unit tempHeader = generateHeader(option, functionOffsets, &jsClassDataOffset); + QV4::CompiledData::Unit tempHeader = generateHeader(option, blockClassAndFunctionOffsets, &jsClassDataOffset); dataPtr = reinterpret_cast<char *>(malloc(tempHeader.unitSize)); memset(dataPtr, 0, tempHeader.unitSize); memcpy(&unit, &dataPtr, sizeof(CompiledData::Unit*)); memcpy(unit, &tempHeader, sizeof(tempHeader)); } - memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(quint32_le)); + memcpy(dataPtr + unit->offsetToFunctionTable, blockClassAndFunctionOffsets, unit->functionTableSize * sizeof(quint32_le)); + memcpy(dataPtr + unit->offsetToClassTable, blockClassAndFunctionOffsets + unit->functionTableSize, unit->classTableSize * sizeof(quint32_le)); + memcpy(dataPtr + unit->offsetToBlockTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize, unit->blockTableSize * sizeof(quint32_le)); for (int i = 0; i < module->functions.size(); ++i) { Context *function = module->functions.at(i); if (function == module->rootContext) unit->indexOfRootFunction = i; - writeFunction(dataPtr + functionOffsets[i], function); + writeFunction(dataPtr + blockClassAndFunctionOffsets[i], function); + } + + for (int i = 0; i < module->classes.size(); ++i) { + const Class &c = module->classes.at(i); + + writeClass(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size()], c); + } + + for (int i = 0; i < module->blocks.size(); ++i) { + Context *block = module->blocks.at(i); + + writeBlock(dataPtr + blockClassAndFunctionOffsets[i + module->classes.size() + module->functions.size()], block); } CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable); @@ -300,17 +307,16 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte function->nameIndex = getStringId(irFunction->name); function->flags = 0; - if (irFunction->hasDirectEval) - function->flags |= CompiledData::Function::HasDirectEval; - if (irFunction->usesArgumentsObject) - function->flags |= CompiledData::Function::UsesArgumentsObject; if (irFunction->isStrict) function->flags |= CompiledData::Function::IsStrict; - if (irFunction->hasTry || irFunction->hasWith) - function->flags |= CompiledData::Function::HasCatchOrWith; + if (irFunction->isArrowFunction) + function->flags |= CompiledData::Function::IsArrowFunction; + if (irFunction->isGenerator) + function->flags |= CompiledData::Function::IsGenerator; function->nestedFunctionIndex = irFunction->returnsClosure ? quint32(module->functions.indexOf(irFunction->nestedContexts.first())) : std::numeric_limits<uint32_t>::max(); + function->length = irFunction->formals ? irFunction->formals->length() : 0; function->nFormals = irFunction->arguments.size(); function->formalsOffset = currentOffset; currentOffset += function->nFormals * sizeof(quint32); @@ -324,7 +330,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine); - function->nRegisters = irFunction->registerCount; + function->nRegisters = irFunction->registerCountInFunction; function->nDependingIdObjects = 0; function->nDependingContextProperties = 0; @@ -390,7 +396,84 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size()); } -QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset) +static_assert(int(QV4::Compiler::Class::Method::Regular) == int(QV4::CompiledData::Method::Regular), "Incompatible layout"); +static_assert(int(QV4::Compiler::Class::Method::Getter) == int(QV4::CompiledData::Method::Getter), "Incompatible layout"); +static_assert(int(QV4::Compiler::Class::Method::Setter) == int(QV4::CompiledData::Method::Setter), "Incompatible layout"); + +void QV4::Compiler::JSUnitGenerator::writeClass(char *b, const QV4::Compiler::Class &c) +{ + QV4::CompiledData::Class *cls = reinterpret_cast<QV4::CompiledData::Class *>(b); + + quint32 currentOffset = sizeof(QV4::CompiledData::Class); + + QVector<Class::Method> allMethods = c.staticMethods; + allMethods += c.methods; + + cls->constructorFunction = c.constructorIndex; + cls->nameIndex = c.nameIndex; + cls->nMethods = c.methods.size(); + cls->nStaticMethods = c.staticMethods.size(); + cls->methodTableOffset = currentOffset; + CompiledData::Method *method = reinterpret_cast<CompiledData::Method *>(b + currentOffset); + + // write methods + for (int i = 0; i < allMethods.size(); ++i) { + method->name = allMethods.at(i).nameIndex; + method->type = allMethods.at(i).type; + method->function = allMethods.at(i).functionIndex; + ++method; + } + + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); + if (showCode) { + qDebug() << "=== Class " << stringForIndex(cls->nameIndex) << "static methods" << cls->nStaticMethods << "methods" << cls->nMethods; + qDebug() << " constructor:" << cls->constructorFunction; + const char *staticString = ": static "; + for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) { + if (i == cls->nStaticMethods) + staticString = ": "; + const char *type; + switch (cls->methodTable()[i].type) { + case CompiledData::Method::Getter: + type = "get "; break; + case CompiledData::Method::Setter: + type = "set "; break; + default: + type = ""; + + } + qDebug() << " " << i << staticString << type << stringForIndex(cls->methodTable()[i].name) << cls->methodTable()[i].function; + } + qDebug(); + } +} + +void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context *irBlock) const +{ + QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b); + + quint32 currentOffset = sizeof(QV4::CompiledData::Block); + currentOffset = (currentOffset + 7) & ~quint32(0x7); + + block->nLocals = irBlock->locals.size(); + block->localsOffset = currentOffset; + currentOffset += block->nLocals * sizeof(quint32); + + // write locals + quint32_le *locals = (quint32_le *)(b + block->localsOffset); + for (int i = 0; i < irBlock->locals.size(); ++i) + locals[i] = getStringId(irBlock->locals.at(i)); + + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); + if (showCode) { + qDebug() << "=== Variables for block" << irBlock->blockIndex; + for (int i = 0; i < irBlock->locals.size(); ++i) + qDebug() << " " << i << ":" << locals[i]; + qDebug(); + } +} + +QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *blockAndFunctionOffsets, uint *jsClassDataOffset) { CompiledData::Unit unit; memset(&unit, 0, sizeof(unit)); @@ -409,6 +492,14 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.offsetToFunctionTable = nextOffset; nextOffset += unit.functionTableSize * sizeof(uint); + unit.classTableSize = module->classes.size(); + unit.offsetToClassTable = nextOffset; + nextOffset += unit.classTableSize * sizeof(uint); + + unit.blockTableSize = module->blocks.size(); + unit.offsetToBlockTable = nextOffset; + nextOffset += unit.blockTableSize * sizeof(uint); + unit.lookupTableSize = lookups.count(); unit.offsetToLookupTable = nextOffset; nextOffset += unit.lookupTableSize * sizeof(CompiledData::Lookup); @@ -435,13 +526,29 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp for (int i = 0; i < module->functions.size(); ++i) { Context *f = module->functions.at(i); - functionOffsets[i] = nextOffset; + blockAndFunctionOffsets[i] = nextOffset; const int qmlIdDepsCount = f->idObjectDependencies.count(); const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count(); nextOffset += QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(), qmlIdDepsCount, qmlPropertyDepsCount, f->code.size()); } + blockAndFunctionOffsets += module->functions.size(); + + for (int i = 0; i < module->classes.size(); ++i) { + const Class &c = module->classes.at(i); + blockAndFunctionOffsets[i] = nextOffset; + + nextOffset += QV4::CompiledData::Class::calculateSize(c.staticMethods.size(), c.methods.size()); + } + blockAndFunctionOffsets += module->classes.size(); + + for (int i = 0; i < module->blocks.size(); ++i) { + Context *c = module->blocks.at(i); + blockAndFunctionOffsets[i] = nextOffset; + + nextOffset += QV4::CompiledData::Block::calculateSize(c->locals.size()); + } if (option == GenerateWithStringTable) { unit.stringTableSize = stringTable.stringCount(); diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 360af6540f..944c44b1ff 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -72,6 +72,8 @@ struct JSClassMember; namespace Compiler { +struct Class; + struct Q_QML_PRIVATE_EXPORT StringTableGenerator { StringTableGenerator(); @@ -116,8 +118,7 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { int registerConstant(ReturnedValue v); ReturnedValue constant(int idx); - int registerJSClass(const QVector<MemberInfo> &members); - int registerJSClass(int count, CompiledData::JSClassMember *members); + int registerJSClass(const QStringList &members); enum GeneratorOption { GenerateWithStringTable, @@ -125,8 +126,9 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { }; QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable); - // Returns bytes written void writeFunction(char *f, Context *irFunction) const; + void writeClass(char *f, const Class &c); + void writeBlock(char *f, Context *irBlock) const; StringTableGenerator stringTable; QString codeGeneratorName; diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index 0a9d3d8efe..9dfe3be7e0 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -39,6 +39,7 @@ #include "qv4compilercontext_p.h" #include "qv4compilercontrolflow_p.h" +#include "qv4bytecodegenerator_p.h" QT_USE_NAMESPACE using namespace QV4; @@ -47,11 +48,11 @@ using namespace QQmlJS::AST; QT_BEGIN_NAMESPACE -Context *Module::newContext(Node *node, Context *parent, CompilationMode compilationMode) +Context *Module::newContext(Node *node, Context *parent, ContextType contextType) { Q_ASSERT(!contextMap.contains(node)); - Context *c = new Context(parent, compilationMode); + Context *c = new Context(parent, contextType); if (node) { SourceLocation loc = node->firstSourceLocation(); c->line = loc.startLine; @@ -70,15 +71,252 @@ Context *Module::newContext(Node *node, Context *parent, CompilationMode compila return c; } -bool Context::forceLookupByName() +bool Context::addLocalVar(const QString &name, Context::MemberType type, VariableScope scope, FunctionExpression *function) { - ControlFlow *flow = controlFlow; - while (flow) { - if (flow->needsLookupByName) + // ### can this happen? + if (name.isEmpty()) + return true; + + if (type != FunctionDefinition) { + if (formals && formals->containsName(name)) + return (scope == VariableScope::Var); + } + if (!isCatchBlock || name != caughtVariable) { + MemberMap::iterator it = members.find(name); + if (it != members.end()) { + if (scope != VariableScope::Var || (*it).scope != VariableScope::Var) + return false; + if ((*it).type <= type) { + (*it).type = type; + (*it).function = function; + } return true; - flow = flow->parent; + } + } + + // hoist var declarations to the function level + if (contextType == ContextType::Block && (scope == VariableScope::Var && type != MemberType::FunctionDefinition)) + return parent->addLocalVar(name, type, scope, function); + + Member m; + m.type = type; + m.function = function; + m.scope = scope; + members.insert(name, m); + return true; +} + +Context::ResolvedName Context::resolveName(const QString &name) +{ + int scope = 0; + Context *c = this; + + ResolvedName result; + + while (c) { + if (c->isWithBlock) + return result; + + Context::Member m = c->findMember(name); + if (!c->parent && m.index < 0) + break; + + if (m.type != Context::UndefinedMember) { + result.type = m.canEscape ? ResolvedName::Local : ResolvedName::Stack; + result.scope = scope; + result.index = m.index; + result.isConst = (m.scope == VariableScope::Const); + if (c->isStrict && (name == QLatin1String("arguments") || name == QLatin1String("eval"))) + result.isArgOrEval = true; + return result; + } + const int argIdx = c->findArgument(name); + if (argIdx != -1) { + if (c->argumentsCanEscape) { + result.index = argIdx + c->locals.size(); + result.scope = scope; + result.type = ResolvedName::Local; + result.isConst = false; + return result; + } else { + result.index = argIdx + sizeof(CallData)/sizeof(Value) - 1; + result.scope = 0; + result.type = ResolvedName::Stack; + result.isConst = false; + return result; + } + } + if (c->hasDirectEval) { + Q_ASSERT(!c->isStrict && c->contextType != ContextType::Block); + return result; + } + + if (c->requiresExecutionContext) + ++scope; + c = c->parent; + } + + // ### can we relax the restrictions here? + if (contextType == ContextType::Eval || c->contextType == ContextType::Binding) + return result; + + result.type = ResolvedName::Global; + return result; +} + +void Context::emitBlockHeader(Codegen *codegen) +{ + using Instruction = Moth::Instruction; + Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator(); + + setupFunctionIndices(bytecodeGenerator); + + if (requiresExecutionContext) { + if (blockIndex < 0) { + codegen->module()->blocks.append(this); + blockIndex = codegen->module()->blocks.count() - 1; + } + + if (contextType == ContextType::Global) { + Instruction::PushScriptContext scriptContext; + scriptContext.index = blockIndex; + bytecodeGenerator->addInstruction(scriptContext); + } else if (contextType == ContextType::Block || (contextType == ContextType::Eval && !isStrict)) { + if (isCatchBlock) { + Instruction::PushCatchContext catchContext; + catchContext.index = blockIndex; + catchContext.name = codegen->registerString(caughtVariable); + bytecodeGenerator->addInstruction(catchContext); + } else { + Instruction::PushBlockContext blockContext; + blockContext.index = blockIndex; + bytecodeGenerator->addInstruction(blockContext); + } + } else { + Instruction::CreateCallContext createContext; + bytecodeGenerator->addInstruction(createContext); + } + } + + if (usesThis) { + Q_ASSERT(!isStrict); + // make sure we convert this to an object + Instruction::ConvertThisToObject convert; + bytecodeGenerator->addInstruction(convert); + } + + if (contextType == ContextType::Global || (contextType == ContextType::Eval && !isStrict)) { + // variables in global code are properties of the global context object, not locals as with other functions. + for (Context::MemberMap::const_iterator it = members.constBegin(), cend = members.constEnd(); it != cend; ++it) { + if (it->isLexicallyScoped()) + continue; + const QString &local = it.key(); + + Instruction::DeclareVar declareVar; + declareVar.isDeletable = (contextType == ContextType::Eval); + declareVar.varName = codegen->registerString(local); + bytecodeGenerator->addInstruction(declareVar); + } + } + + if (contextType == ContextType::Function || contextType == ContextType::Binding) { + for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) { + if (it->canEscape && it->type == Context::ThisFunctionName) { + // move the function from the stack to the call context + Instruction::LoadReg load; + load.reg = CallData::Function; + bytecodeGenerator->addInstruction(load); + Instruction::StoreLocal store; + store.index = it->index; + bytecodeGenerator->addInstruction(store); + } + } + } + + if (usesArgumentsObject == Context::ArgumentsObjectUsed) { + Q_ASSERT(contextType != ContextType::Block); + if (isStrict || (formals && !formals->isSimpleParameterList())) { + Instruction::CreateUnmappedArgumentsObject setup; + bytecodeGenerator->addInstruction(setup); + } else { + Instruction::CreateMappedArgumentsObject setup; + bytecodeGenerator->addInstruction(setup); + } + codegen->referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator(); + } + + for (const Context::Member &member : qAsConst(members)) { + if (member.function) { + const int function = codegen->defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body); + codegen->loadClosure(function); + Codegen::Reference r = codegen->referenceForName(member.function->name.toString(), true); + r.storeConsumeAccumulator(); + } + } +} + +void Context::emitBlockFooter(Codegen *codegen) +{ + using Instruction = Moth::Instruction; + Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator(); + + if (!requiresExecutionContext) + return; + +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs. + if (contextType == ContextType::Global) + bytecodeGenerator->addInstruction(Instruction::PopScriptContext()); + else + bytecodeGenerator->addInstruction(Instruction::PopContext()); +QT_WARNING_POP +} + +void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator) +{ + if (registerOffset != -1) { + // already computed, check for consistency + Q_ASSERT(registerOffset == bytecodeGenerator->currentRegister()); + bytecodeGenerator->newRegisterArray(nRegisters); + return; + } + Q_ASSERT(locals.size() == 0); + Q_ASSERT(nRegisters == 0); + registerOffset = bytecodeGenerator->currentRegister(); + + switch (contextType) { + case ContextType::Block: + case ContextType::Function: + case ContextType::Binding: { + for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) { + const QString &local = it.key(); + if (it->canEscape) { + it->index = locals.size(); + locals.append(local); + } else { + if (it->type == Context::ThisFunctionName) + it->index = CallData::Function; + else + it->index = bytecodeGenerator->newRegister(); + } + } + break; + } + case ContextType::Global: + case ContextType::Eval: + for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) { + if (!it->isLexicallyScoped() && (contextType == ContextType::Global || !isStrict)) + continue; + if (it->canEscape) { + it->index = locals.size(); + locals.append(it.key()); + } else { + it->index = bytecodeGenerator->newRegister(); + } + } + break; } - return false; + nRegisters = bytecodeGenerator->currentRegister() - registerOffset; } QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 455a76c729..52c3fc5b05 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -66,18 +66,37 @@ namespace Compiler { struct ControlFlow; -enum CompilationMode { - GlobalCode, - EvalCode, - FunctionCode, - QmlBinding // This is almost the same as EvalCode, except: +enum class ContextType { + Global, + Function, + Eval, + Binding, // This is almost the same as Eval, except: // * function declarations are moved to the return address when encountered // * return statements are allowed everywhere (like in FunctionCode) // * variable declarations are treated as true locals (like in FunctionCode) + Block }; struct Context; +struct Class { + struct Method { + enum Type { + Regular, + Getter, + Setter + }; + uint nameIndex; + Type type; + uint functionIndex; + }; + + uint nameIndex; + uint constructorIndex = UINT_MAX; + QVector<Method> staticMethods; + QVector<Method> methods; +}; + struct Module { Module(bool debugMode) : debugMode(debugMode) @@ -86,10 +105,12 @@ struct Module { qDeleteAll(contextMap); } - Context *newContext(QQmlJS::AST::Node *node, Context *parent, CompilationMode compilationMode); + Context *newContext(QQmlJS::AST::Node *node, Context *parent, ContextType compilationMode); QHash<QQmlJS::AST::Node *, Context *> contextMap; QList<Context *> functions; + QList<Context *> blocks; + QVector<Class> classes; Context *rootContext; QString fileName; QString finalUrl; @@ -104,8 +125,9 @@ struct Context { QString name; int line = 0; int column = 0; - int registerCount = 0; + int registerCountInFunction = 0; int functionIndex = -1; + int blockIndex = -1; enum MemberType { UndefinedMember, @@ -118,11 +140,11 @@ struct Context { struct Member { MemberType type = UndefinedMember; int index = -1; - QQmlJS::AST::VariableDeclaration::VariableScope scope = QQmlJS::AST::VariableDeclaration::FunctionScope; + QQmlJS::AST::VariableScope scope = QQmlJS::AST::VariableScope::Var; mutable bool canEscape = false; QQmlJS::AST::FunctionExpression *function = nullptr; - bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableDeclaration::FunctionScope; } + bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableScope::Var; } }; typedef QMap<QString, Member> MemberMap; @@ -137,15 +159,22 @@ struct Context { QByteArray code; QVector<CompiledData::CodeOffsetToLine> lineNumberMapping; - int maxNumberOfArguments = 0; + int nRegisters = 0; + int registerOffset = -1; bool hasDirectEval = false; + bool allVarsEscape = false; bool hasNestedFunctions = false; bool isStrict = false; + bool isArrowFunction = false; + bool isGenerator = false; bool usesThis = false; bool hasTry = false; - bool hasWith = false; bool returnsClosure = false; mutable bool argumentsCanEscape = false; + bool requiresExecutionContext = false; + bool isWithBlock = false; + bool isCatchBlock = false; + QString caughtVariable; enum UsesArgumentsObject { ArgumentsObjectUnknown, @@ -155,7 +184,7 @@ struct Context { UsesArgumentsObject usesArgumentsObject = ArgumentsObjectUnknown; - CompilationMode compilationMode; + ContextType contextType; template <typename T> class SmallSet: public QVarLengthArray<T, 8> @@ -204,24 +233,14 @@ struct Context { PropertyDependencyMap contextObjectPropertyDependencies; PropertyDependencyMap scopeObjectPropertyDependencies; - Context(Context *parent, CompilationMode mode) + Context(Context *parent, ContextType type) : parent(parent) - , compilationMode(mode) + , contextType(type) { if (parent && parent->isStrict) isStrict = true; } - bool forceLookupByName(); - - - bool canUseSimpleCall() const { - return nestedContexts.isEmpty() && - locals.isEmpty() && - !hasTry && !hasWith && - (usesArgumentsObject == ArgumentsObjectNotUsed || isStrict) && !hasDirectEval; - } - int findArgument(const QString &name) { // search backwards to handle duplicate argument names correctly @@ -253,37 +272,37 @@ struct Context { return true; } + bool requiresImplicitReturnValue() const { + return contextType == ContextType::Binding || + contextType == ContextType::Eval || + contextType == ContextType::Global; + } + void addUsedVariable(const QString &name) { usedVariables.insert(name); } - bool addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableDeclaration::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr) - { - if (name.isEmpty()) - return true; + bool addLocalVar(const QString &name, MemberType contextType, QQmlJS::AST::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr); + + struct ResolvedName { + enum Type { + Unresolved, + Global, + Local, + Stack + }; + Type type = Unresolved; + bool isArgOrEval = false; + bool isConst = false; + int scope = -1; + int index = -1; + bool isValid() const { return type != Unresolved; } + }; + ResolvedName resolveName(const QString &name); + void emitBlockHeader(Compiler::Codegen *codegen); + void emitBlockFooter(Compiler::Codegen *codegen); - if (type != FunctionDefinition) { - for (QQmlJS::AST::FormalParameterList *it = formals; it; it = it->next) - if (it->name == name) - return (scope == QQmlJS::AST::VariableDeclaration::FunctionScope); - } - MemberMap::iterator it = members.find(name); - if (it != members.end()) { - if (scope != QQmlJS::AST::VariableDeclaration::FunctionScope || (*it).scope != QQmlJS::AST::VariableDeclaration::FunctionScope) - return false; - if ((*it).type <= type) { - (*it).type = type; - (*it).function = function; - } - return true; - } - Member m; - m.type = type; - m.function = function; - m.scope = scope; - members.insert(name, m); - return true; - } + void setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator); }; diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h index 9bda20905a..1ef290ea56 100644 --- a/src/qml/compiler/qv4compilercontrolflow_p.h +++ b/src/qml/compiler/qv4compilercontrolflow_p.h @@ -53,6 +53,7 @@ #include <private/qv4global_p.h> #include <private/qv4codegen_p.h> #include <private/qqmljsast_p.h> +#include <private/qv4bytecodegenerator_p.h> QT_BEGIN_NAMESPACE @@ -68,117 +69,84 @@ struct ControlFlow { enum Type { Loop, With, + Block, Finally, Catch }; - enum HandlerType { - Invalid, + enum UnwindType { Break, Continue, - Return, - Throw + Return }; - struct Handler { - HandlerType type; - QString label; + struct UnwindTarget { BytecodeGenerator::Label linkLabel; - int tempIndex; - int value; + int unwindLevel; }; Codegen *cg; ControlFlow *parent; Type type; - bool needsLookupByName = false; ControlFlow(Codegen *cg, Type type) - : cg(cg), parent(cg->_context->controlFlow), type(type) + : cg(cg), parent(cg->controlFlow), type(type) { - cg->_context->controlFlow = this; + cg->controlFlow = this; } virtual ~ControlFlow() { - cg->_context->controlFlow = parent; + cg->controlFlow = parent; } - void emitReturnStatement() const { - if (cg->_returnAddress >= 0) { - Instruction::LoadReg load; - load.reg = Moth::StackSlot::createRegister(cg->_returnAddress); - generator()->addInstruction(load); + UnwindTarget unwindTarget(UnwindType type, const QString &label = QString()) + { + Q_ASSERT(type == Break || type == Continue || type == Return); + ControlFlow *flow = this; + int level = 0; + while (flow) { + BytecodeGenerator::Label l = flow->getUnwindTarget(type, label); + if (l.isValid()) + return UnwindTarget{l, level}; + if (flow->requiresUnwind()) + ++level; + flow = flow->parent; } - Instruction::Ret ret; - cg->bytecodeGenerator->addInstruction(ret); + if (type == Return) + return UnwindTarget{ cg->returnLabel(), level }; + return UnwindTarget(); } - void jumpToHandler(const Handler &h) { - if (h.linkLabel.isReturn()) { - emitReturnStatement(); - } else { - if (h.tempIndex >= 0) - Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex); - cg->bytecodeGenerator->jump().link(h.linkLabel); - } - } + virtual QString label() const { return QString(); } - bool returnRequiresUnwind() const { - const ControlFlow *f = this; - while (f) { - if (f->type == Finally) + bool hasLoop() const { + const ControlFlow *flow = this; + while (flow) { + if (flow->type == Loop) return true; - f = f->parent; + flow = flow->parent; } return false; } - virtual QString label() const { return QString(); } - - bool isSimple() const { - return type == Loop; +protected: + virtual BytecodeGenerator::Label getUnwindTarget(UnwindType, const QString & = QString()) { + return BytecodeGenerator::Label(); } - - Handler getParentHandler(HandlerType type, const QString &label = QString()) { - if (parent) - return parent->getHandler(type, label); - switch (type) { - case Break: - case Continue: - return { Invalid, QString(), {}, -1, 0 }; - case Return: - case Throw: - return { type, QString(), BytecodeGenerator::Label::returnLabel(), -1, 0 }; - case Invalid: - break; - } - Q_ASSERT(false); - Q_UNREACHABLE(); + virtual bool requiresUnwind() { + return false; } - virtual Handler getHandler(HandlerType type, const QString &label = QString()) = 0; - - BytecodeGenerator::ExceptionHandler *parentExceptionHandler() { - return parent ? parent->exceptionHandler() : nullptr; +public: + BytecodeGenerator::ExceptionHandler *parentUnwindHandler() { + return parent ? parent->unwindHandler() : nullptr; } - virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { - return parentExceptionHandler(); + virtual BytecodeGenerator::ExceptionHandler *unwindHandler() { + return parentUnwindHandler(); } - virtual void handleThrow(const Reference &expr) { - Reference e = expr; - Handler h = getHandler(ControlFlow::Throw); - if (h.tempIndex >= 0) { - e = e.storeOnStack(); - Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex); - } - e.loadInAccumulator(); - Instruction::ThrowException instr; - generator()->addInstruction(instr); - } - protected: QString loopLabel() const { QString label; @@ -193,136 +161,145 @@ protected: } }; -struct ControlFlowLoop : public ControlFlow +struct ControlFlowUnwind : public ControlFlow { - QString loopLabel; - BytecodeGenerator::Label *breakLabel = nullptr; - BytecodeGenerator::Label *continueLabel = nullptr; + BytecodeGenerator::ExceptionHandler unwindLabel; - ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr) - : ControlFlow(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel) + ControlFlowUnwind(Codegen *cg, Type type) + : ControlFlow(cg, type) { } - virtual QString label() const { return loopLabel; } + void setupUnwindHandler() + { + unwindLabel = generator()->newExceptionHandler(); + } - virtual Handler getHandler(HandlerType type, const QString &label = QString()) { - switch (type) { - case Break: - if (breakLabel && (label.isEmpty() || label == loopLabel)) - return { type, loopLabel, *breakLabel, -1, 0 }; - break; - case Continue: - if (continueLabel && (label.isEmpty() || label == loopLabel)) - return { type, loopLabel, *continueLabel, -1, 0 }; - break; - case Return: - case Throw: - break; - case Invalid: - Q_ASSERT(false); - Q_UNREACHABLE(); - } - return getParentHandler(type, label); + void emitUnwindHandler() + { + Q_ASSERT(requiresUnwind()); + + Instruction::UnwindDispatch dispatch; + generator()->addInstruction(dispatch); } + virtual BytecodeGenerator::ExceptionHandler *unwindHandler() override { + return unwindLabel.isValid() ? &unwindLabel : parentUnwindHandler(); + } }; -struct ControlFlowUnwind : public ControlFlow +struct ControlFlowLoop : public ControlFlowUnwind { - BytecodeGenerator::ExceptionHandler unwindLabel; - int controlFlowTemp; - QVector<Handler> handlers; + QString loopLabel; + BytecodeGenerator::Label *breakLabel = nullptr; + BytecodeGenerator::Label *continueLabel = nullptr; + bool _requiresUnwind; - ControlFlowUnwind(Codegen *cg, Type type) - : ControlFlow(cg, type), unwindLabel(generator()->newExceptionHandler()) + ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr, bool requiresUnwind = false) + : ControlFlowUnwind(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel), _requiresUnwind(requiresUnwind) { - Q_ASSERT(type != Loop); - controlFlowTemp = static_cast<int>(generator()->newRegister()); - Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp); - // we'll need at least a handler for throw - getHandler(Throw); + if (_requiresUnwind) { + setupUnwindHandler(); + generator()->setUnwindHandler(&unwindLabel); + } } - void emitUnwindHandler() - { - Q_ASSERT(!isSimple()); - - Reference temp = Reference::fromStackSlot(cg, controlFlowTemp); - for (const auto &h : qAsConst(handlers)) { - Handler parentHandler = getParentHandler(h.type, h.label); - - if (h.type == Throw || parentHandler.tempIndex >= 0) { - BytecodeGenerator::Label skip = generator()->newLabel(); - generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip); - if (h.type == Throw) - emitForThrowHandling(); - jumpToHandler(parentHandler); - skip.link(); - } else { - if (parentHandler.linkLabel.isReturn()) { - BytecodeGenerator::Label skip = generator()->newLabel(); - generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip); - emitReturnStatement(); - skip.link(); - } else { - generator()->jumpStrictEqualStackSlotInt(temp.stackSlot(), h.value).link(parentHandler.linkLabel); - } - } + ~ControlFlowLoop() { + if (_requiresUnwind) { + unwindLabel.link(); + generator()->setUnwindHandler(parentUnwindHandler()); + emitUnwindHandler(); } } - virtual Handler getHandler(HandlerType type, const QString &label = QString()) { - for (const auto &h : qAsConst(handlers)) { - if (h.type == type && h.label == label) - return h; - } - Handler h = { - type, - label, - unwindLabel, - controlFlowTemp, - handlers.size() - }; - handlers.append(h); - return h; + bool requiresUnwind() override { + return _requiresUnwind; } - virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { - return &unwindLabel; + BytecodeGenerator::Label getUnwindTarget(UnwindType type, const QString &label) override { + switch (type) { + case Break: + if (breakLabel && (label.isEmpty() || label == loopLabel)) + return *breakLabel; + break; + case Continue: + if (continueLabel && (label.isEmpty() || label == loopLabel)) + return *continueLabel; + break; + default: + break; + } + return BytecodeGenerator::Label(); } - virtual void emitForThrowHandling() { } + QString label() const override { return loopLabel; } }; + struct ControlFlowWith : public ControlFlowUnwind { ControlFlowWith(Codegen *cg) : ControlFlowUnwind(cg, With) { - needsLookupByName = true; - - savedContextRegister = Moth::StackSlot::createRegister(generator()->newRegister()); + setupUnwindHandler(); // assumes the with object is in the accumulator Instruction::PushWithContext pushScope; - pushScope.reg = savedContextRegister; generator()->addInstruction(pushScope); - generator()->setExceptionHandler(&unwindLabel); + generator()->setUnwindHandler(&unwindLabel); } - virtual ~ControlFlowWith() { + ~ControlFlowWith() { // emit code for unwinding unwindLabel.link(); - generator()->setExceptionHandler(parentExceptionHandler()); + generator()->setUnwindHandler(parentUnwindHandler()); Instruction::PopContext pop; - pop.reg = savedContextRegister; generator()->addInstruction(pop); emitUnwindHandler(); } - Moth::StackSlot savedContextRegister; + + bool requiresUnwind() override { + return true; + } + + +}; + +struct ControlFlowBlock : public ControlFlowUnwind +{ + ControlFlowBlock(Codegen *cg, AST::Node *ast) + : ControlFlowUnwind(cg, Block) + { + block = cg->enterBlock(ast); + block->emitBlockHeader(cg); + + if (block->requiresExecutionContext) { + setupUnwindHandler(); + generator()->setUnwindHandler(&unwindLabel); + } + } + + virtual ~ControlFlowBlock() { + // emit code for unwinding + if (block->requiresExecutionContext) { + unwindLabel.link(); + generator()->setUnwindHandler(parentUnwindHandler()); + } + + block->emitBlockFooter(cg); + + if (block->requiresExecutionContext ) + emitUnwindHandler(); + cg->leaveBlock(); + } + + virtual bool requiresUnwind() override { + return block->requiresExecutionContext; + } + + Context *block; }; struct ControlFlowCatch : public ControlFlowUnwind @@ -330,71 +307,57 @@ struct ControlFlowCatch : public ControlFlowUnwind AST::Catch *catchExpression; bool insideCatch = false; BytecodeGenerator::ExceptionHandler exceptionLabel; - BytecodeGenerator::ExceptionHandler catchUnwindLabel; + bool oldLookupByName; ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression) : ControlFlowUnwind(cg, Catch), catchExpression(catchExpression), - exceptionLabel(generator()->newExceptionHandler()), - catchUnwindLabel(generator()->newExceptionHandler()) + exceptionLabel(generator()->newExceptionHandler()) { - generator()->setExceptionHandler(&exceptionLabel); + generator()->setUnwindHandler(&exceptionLabel); } - virtual Handler getHandler(HandlerType type, const QString &label = QString()) { - Handler h = getParentHandler(type, label); - if (h.type == Invalid) - return h; - h = ControlFlowUnwind::getHandler(type, label); - if (insideCatch) - // if we're inside the catch block, we need to jump to the pop scope - // instruction at the end of the catch block, not the unwind handler - h.linkLabel = catchUnwindLabel; - else if (type == Throw) - // if we're inside the try block, we need to jump to the catch block, - // not the unwind handler - h.linkLabel = exceptionLabel; - return h; + virtual bool requiresUnwind() override { + return true; } - virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { - return insideCatch ? &catchUnwindLabel : &exceptionLabel; + BytecodeGenerator::ExceptionHandler *unwindHandler() override { + return insideCatch ? &unwindLabel : &exceptionLabel; } ~ControlFlowCatch() { // emit code for unwinding - - needsLookupByName = true; insideCatch = true; + setupUnwindHandler(); Codegen::RegisterScope scope(cg); // exceptions inside the try block go here exceptionLabel.link(); - Moth::StackSlot savedContextReg = Moth::StackSlot::createRegister(generator()->newRegister()); - Instruction::PushCatchContext pushCatch; - pushCatch.name = cg->registerString(catchExpression->name.toString()); - pushCatch.reg = savedContextReg; - generator()->addInstruction(pushCatch); - // clear the unwind temp for exceptions, we want to resume normal code flow afterwards - Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp); - generator()->setExceptionHandler(&catchUnwindLabel); + BytecodeGenerator::Jump noException = generator()->jumpNoException(); - cg->statement(catchExpression->statement); + Context *block = cg->enterBlock(catchExpression); - insideCatch = false; - needsLookupByName = false; + block->emitBlockHeader(cg); - // exceptions inside catch and break/return statements go here - catchUnwindLabel.link(); - Instruction::PopContext pop; - pop.reg = savedContextReg; - generator()->addInstruction(pop); + generator()->setUnwindHandler(&unwindLabel); + + if (catchExpression->patternElement->bindingIdentifier.isEmpty()) + // destructuring pattern + cg->initializeAndDestructureBindingElement(catchExpression->patternElement, Reference::fromName(cg, QStringLiteral("@caught"))); + // skip the additional block + cg->statementList(catchExpression->statement->statements); - // break/continue/return statements in try go here + // exceptions inside catch and break/return statements go here unwindLabel.link(); - generator()->setExceptionHandler(parentExceptionHandler()); + block->emitBlockFooter(cg); + + cg->leaveBlock(); + + noException.link(); + generator()->setUnwindHandler(parentUnwindHandler()); emitUnwindHandler(); + insideCatch = false; } }; @@ -402,25 +365,21 @@ struct ControlFlowFinally : public ControlFlowUnwind { AST::Finally *finally; bool insideFinally = false; - int exceptionTemp = -1; ControlFlowFinally(Codegen *cg, AST::Finally *finally) : ControlFlowUnwind(cg, Finally), finally(finally) { Q_ASSERT(finally != nullptr); - generator()->setExceptionHandler(&unwindLabel); + setupUnwindHandler(); + generator()->setUnwindHandler(&unwindLabel); } - virtual Handler getHandler(HandlerType type, const QString &label = QString()) { - // if we're inside the finally block, any exceptions etc. should - // go directly to the parent handler - if (insideFinally) - return getParentHandler(type, label); - return ControlFlowUnwind::getHandler(type, label); + virtual bool requiresUnwind() override { + return !insideFinally; } - virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { - return insideFinally ? parentExceptionHandler() : ControlFlowUnwind::exceptionHandler(); + BytecodeGenerator::ExceptionHandler *unwindHandler() override { + return insideFinally ? parentUnwindHandler() : ControlFlowUnwind::unwindHandler(); } ~ControlFlowFinally() { @@ -429,34 +388,35 @@ struct ControlFlowFinally : public ControlFlowUnwind Codegen::RegisterScope scope(cg); - Moth::StackSlot retVal = Moth::StackSlot::createRegister(generator()->newRegister()); - Instruction::StoreReg storeRetVal; - storeRetVal.reg = retVal; - generator()->addInstruction(storeRetVal); - insideFinally = true; - exceptionTemp = generator()->newRegister(); + int returnValueTemp = -1; + if (cg->requiresReturnValue) { + returnValueTemp = generator()->newRegister(); + Instruction::MoveReg move; + move.srcReg = cg->_returnAddress; + move.destReg = returnValueTemp; + generator()->addInstruction(move); + } + int exceptionTemp = generator()->newRegister(); Instruction::GetException instr; generator()->addInstruction(instr); Reference::fromStackSlot(cg, exceptionTemp).storeConsumeAccumulator(); - generator()->setExceptionHandler(parentExceptionHandler()); + generator()->setUnwindHandler(parentUnwindHandler()); cg->statement(finally->statement); insideFinally = false; - Instruction::LoadReg loadRetVal; - loadRetVal.reg = retVal; - generator()->addInstruction(loadRetVal); - - emitUnwindHandler(); - } - - virtual void emitForThrowHandling() { - // reset the exception flag, that got cleared before executing the statements in finally + if (cg->requiresReturnValue) { + Instruction::MoveReg move; + move.srcReg = returnValueTemp; + move.destReg = cg->_returnAddress; + generator()->addInstruction(move); + } Reference::fromStackSlot(cg, exceptionTemp).loadInAccumulator(); Instruction::SetException setException; - Q_ASSERT(exceptionTemp != -1); generator()->addInstruction(setException); + + emitUnwindHandler(); } }; diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index 84ee452332..9be55c6ad0 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -56,12 +56,12 @@ using namespace QV4; using namespace QV4::Compiler; using namespace QQmlJS::AST; -ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) +ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType) : _cg(cg) , _sourceCode(sourceCode) , _context(nullptr) , _allowFuncDecls(true) - , defaultProgramMode(defaultProgramMode) + , defaultProgramType(defaultProgramType) { } @@ -73,18 +73,19 @@ void ScanFunctions::operator()(Node *node) calcEscapingVariables(); } -void ScanFunctions::enterGlobalEnvironment(CompilationMode compilationMode) +void ScanFunctions::enterGlobalEnvironment(ContextType compilationMode) { - enterEnvironment(astNodeForGlobalEnvironment, compilationMode); + enterEnvironment(astNodeForGlobalEnvironment, compilationMode, QStringLiteral("%GlobalCode")); } -void ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode) +void ScanFunctions::enterEnvironment(Node *node, ContextType compilationMode, const QString &name) { Context *c = _cg->_module->contextMap.value(node); if (!c) c = _cg->_module->newContext(node, _context, compilationMode); if (!c->isStrict) c->isStrict = _cg->_strictMode; + c->name = name; _contextStack.append(c); _context = c; } @@ -92,28 +93,26 @@ void ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode void ScanFunctions::leaveEnvironment() { _contextStack.pop(); - _context = _contextStack.isEmpty() ? 0 : _contextStack.top(); -} - -void ScanFunctions::checkDirectivePrologue(SourceElements *ast) -{ - for (SourceElements *it = ast; it; it = it->next) { - if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) { - if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) { - if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) { - // Use the source code, because the StringLiteral's - // value might have escape sequences in it, which is not - // allowed. - if (strLit->literalToken.length < 2) - continue; - QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); - if (str == QLatin1String("use strict")) { - _context->isStrict = true; - } else { - // TODO: give a warning. - } + _context = _contextStack.isEmpty() ? nullptr : _contextStack.top(); +} + +void ScanFunctions::checkDirectivePrologue(StatementList *ast) +{ + for (StatementList *it = ast; it; it = it->next) { + if (ExpressionStatement *expr = cast<ExpressionStatement *>(it->statement)) { + if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) { + // Use the source code, because the StringLiteral's + // value might have escape sequences in it, which is not + // allowed. + if (strLit->literalToken.length < 2) continue; + QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); + if (str == QLatin1String("use strict")) { + _context->isStrict = true; + } else { + // TODO: give a warning. } + continue; } } @@ -138,20 +137,10 @@ void ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc) } } -bool ScanFunctions::formalsContainName(AST::FormalParameterList *parameters, const QString &name) -{ - while (parameters) { - if (parameters->name == name) - return true; - parameters = parameters->next; - } - return false; -} - bool ScanFunctions::visit(Program *ast) { - enterEnvironment(ast, defaultProgramMode); - checkDirectivePrologue(ast->elements); + enterEnvironment(ast, defaultProgramType, QStringLiteral("%ProgramCode")); + checkDirectivePrologue(ast->statements); return true; } @@ -162,7 +151,7 @@ void ScanFunctions::endVisit(Program *) bool ScanFunctions::visit(CallExpression *ast) { - if (! _context->hasDirectEval) { + if (!_context->hasDirectEval) { if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { if (id->name == QLatin1String("eval")) { if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown) @@ -171,53 +160,31 @@ bool ScanFunctions::visit(CallExpression *ast) } } } - int argc = 0; - for (ArgumentList *it = ast->arguments; it; it = it->next) - ++argc; - _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); return true; } -bool ScanFunctions::visit(NewMemberExpression *ast) +bool ScanFunctions::visit(PatternElement *ast) { - int argc = 0; - for (ArgumentList *it = ast->arguments; it; it = it->next) - ++argc; - _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); - return true; -} + if (!ast->isVariableDeclaration()) + return true; -bool ScanFunctions::visit(ArrayLiteral *ast) -{ - int index = 0; - for (ElementList *it = ast->elements; it; it = it->next) { - for (Elision *elision = it->elision; elision; elision = elision->next) - ++index; - ++index; - } - if (ast->elision) { - for (Elision *elision = ast->elision->next; elision; elision = elision->next) - ++index; - } - _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, index); - return true; -} + QStringList names; + ast->boundNames(&names); -bool ScanFunctions::visit(VariableDeclaration *ast) -{ - if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); - checkName(ast->name, ast->identifierToken); - if (ast->name == QLatin1String("arguments")) - _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; - if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) { - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); - return false; - } - QString name = ast->name.toString(); - if (!_context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope)) { - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); - return false; + for (const QString &name : qAsConst(names)) { + if (_context->isStrict && (name == QLatin1String("eval") || name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); + checkName(QStringRef(&name), ast->identifierToken); + if (name == QLatin1String("arguments")) + _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + if (ast->scope == VariableScope::Const && !ast->initializer && !ast->destructuringPattern()) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); + return false; + } + if (!_context->addLocalVar(name, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope)) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); + return false; + } } return true; } @@ -237,7 +204,8 @@ bool ScanFunctions::visit(ExpressionStatement *ast) if (!_allowFuncDecls) _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration")); - enterFunction(expr, /*enterName*/ true); + if (!enterFunction(expr, /*enterName*/ true)) + return false; Node::accept(expr->formals, this); Node::accept(expr->body, this); leaveEnvironment(); @@ -253,79 +221,98 @@ bool ScanFunctions::visit(ExpressionStatement *ast) bool ScanFunctions::visit(FunctionExpression *ast) { - enterFunction(ast, /*enterName*/ false); + return enterFunction(ast, /*enterName*/ false); +} + +bool ScanFunctions::visit(ClassExpression *ast) +{ + enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class")); + _context->isStrict = true; + _context->hasNestedFunctions = true; + if (!ast->name.isEmpty()) + _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const); return true; } -void ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName) +void ScanFunctions::endVisit(ClassExpression *) { - if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode")); - enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : nullptr); + leaveEnvironment(); } -void ScanFunctions::endVisit(FunctionExpression *) +bool ScanFunctions::visit(ClassDeclaration *ast) +{ + if (!ast->name.isEmpty()) + _context->addLocalVar(ast->name.toString(), Context::VariableDeclaration, AST::VariableScope::Let); + + enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class")); + _context->isStrict = true; + _context->hasNestedFunctions = true; + if (!ast->name.isEmpty()) + _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const); + return true; +} + +void ScanFunctions::endVisit(ClassDeclaration *) { leaveEnvironment(); } -bool ScanFunctions::visit(ObjectLiteral *ast) +bool ScanFunctions::visit(TemplateLiteral *ast) { - int argc = 0; - for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { - QString key = it->assignment->name->asString(); - if (QV4::String::toArrayIndex(key) != UINT_MAX) - ++argc; - ++argc; - if (AST::cast<AST::PropertyGetterSetter *>(it->assignment)) - ++argc; + while (ast) { + if (ast->expression) + Node::accept(ast->expression, this); + ast = ast->next; } - _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); + return true; - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); - Node::accept(ast->properties, this); - return false; } -bool ScanFunctions::visit(PropertyGetterSetter *ast) +bool ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName) { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); - enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/nullptr); - return true; + if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode")); + return enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName); } -void ScanFunctions::endVisit(PropertyGetterSetter *) +void ScanFunctions::endVisit(FunctionExpression *) { leaveEnvironment(); } -bool ScanFunctions::visit(FunctionDeclaration *ast) +bool ScanFunctions::visit(ObjectPattern *ast) { - enterFunction(ast, /*enterName*/ true); - return true; + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + Node::accept(ast->properties, this); + return false; } -void ScanFunctions::endVisit(FunctionDeclaration *) +bool ScanFunctions::visit(PatternProperty *ast) { - leaveEnvironment(); + Q_UNUSED(ast); + // ### Shouldn't be required anymore +// if (ast->type == PatternProperty::Getter || ast->type == PatternProperty::Setter) { +// TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); +// return enterFunction(ast, QString(), ast->formals, ast->functionBody, /*enterName */ false); +// } + return true; } -bool ScanFunctions::visit(TryStatement *) +void ScanFunctions::endVisit(PatternProperty *) { - // ### should limit to catch(), as try{} finally{} should be ok without - _context->hasTry = true; - return true; + // ### +// if (ast->type == PatternProperty::Getter || ast->type == PatternProperty::Setter) +// leaveEnvironment(); } -bool ScanFunctions::visit(WithStatement *ast) +bool ScanFunctions::visit(FunctionDeclaration *ast) { - if (_context->isStrict) { - _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode")); - return false; - } + return enterFunction(ast, /*enterName*/ true); +} - _context->hasWith = true; - return true; +void ScanFunctions::endVisit(FunctionDeclaration *) +{ + leaveEnvironment(); } bool ScanFunctions::visit(DoWhileStatement *ast) { @@ -338,7 +325,9 @@ bool ScanFunctions::visit(DoWhileStatement *ast) { } bool ScanFunctions::visit(ForStatement *ast) { + enterEnvironment(ast, ContextType::Block, QStringLiteral("%For")); Node::accept(ast->initialiser, this); + Node::accept(ast->declarations, this); Node::accept(ast->condition, this); Node::accept(ast->expression, this); @@ -348,9 +337,14 @@ bool ScanFunctions::visit(ForStatement *ast) { return false; } -bool ScanFunctions::visit(LocalForStatement *ast) { - Node::accept(ast->declarations, this); - Node::accept(ast->condition, this); +void ScanFunctions::endVisit(ForStatement *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(ForEachStatement *ast) { + enterEnvironment(ast, ContextType::Block, QStringLiteral("%Foreach")); + Node::accept(ast->lhs, this); Node::accept(ast->expression, this); TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); @@ -359,90 +353,154 @@ bool ScanFunctions::visit(LocalForStatement *ast) { return false; } -bool ScanFunctions::visit(ForEachStatement *ast) { - Node::accept(ast->initialiser, this); - Node::accept(ast->expression, this); +void ScanFunctions::endVisit(ForEachStatement *) +{ + leaveEnvironment(); +} - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); - Node::accept(ast->statement, this); +bool ScanFunctions::visit(ThisExpression *) +{ + _context->usesThis = true; + return false; +} +bool ScanFunctions::visit(Block *ast) +{ + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls); + enterEnvironment(ast, ContextType::Block, QStringLiteral("%Block")); + Node::accept(ast->statements, this); return false; } -bool ScanFunctions::visit(LocalForEachStatement *ast) { - Node::accept(ast->declaration, this); - Node::accept(ast->expression, this); +void ScanFunctions::endVisit(Block *) +{ + leaveEnvironment(); +} - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); - Node::accept(ast->statement, this); +bool ScanFunctions::visit(CaseBlock *ast) +{ + enterEnvironment(ast, ContextType::Block, QStringLiteral("%CaseBlock")); + return true; +} - return false; +void ScanFunctions::endVisit(CaseBlock *) +{ + leaveEnvironment(); } -bool ScanFunctions::visit(ThisExpression *) +bool ScanFunctions::visit(Catch *ast) { - _context->usesThis = true; + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls); + enterEnvironment(ast, ContextType::Block, QStringLiteral("%CatchBlock")); + _context->isCatchBlock = true; + QString caughtVar = ast->patternElement->bindingIdentifier.toString(); + if (caughtVar.isEmpty()) + caughtVar = QStringLiteral("@caught"); + _context->addLocalVar(caughtVar, Context::MemberType::VariableDefinition, VariableScope::Let); + + _context->caughtVariable = caughtVar; + if (_context->isStrict && + (caughtVar == QLatin1String("eval") || caughtVar == QLatin1String("arguments"))) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode")); + return false; + } + Node::accept(ast->patternElement, this); + // skip the block statement + Node::accept(ast->statement->statements, this); return false; } -bool ScanFunctions::visit(Block *ast) { +void ScanFunctions::endVisit(Catch *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(WithStatement *ast) +{ + Node::accept(ast->expression, this); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls); - Node::accept(ast->statements, this); + enterEnvironment(ast, ContextType::Block, QStringLiteral("%WithBlock")); + _context->isWithBlock = true; + + if (_context->isStrict) { + _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode")); + return false; + } + Node::accept(ast->statement, this); + return false; } -void ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr) +void ScanFunctions::endVisit(WithStatement *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, StatementList *body, bool enterName) { Context *outerContext = _context; - enterEnvironment(ast, FunctionCode); + enterEnvironment(ast, ContextType::Function, name); + FunctionExpression *expr = AST::cast<FunctionExpression *>(ast); + if (!expr) + expr = AST::cast<FunctionDeclaration *>(ast); if (outerContext) { outerContext->hasNestedFunctions = true; // The identifier of a function expression cannot be referenced from the enclosing environment. - if (expr) { - if (!outerContext->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr)) { + if (enterName) { + if (!outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr)) { _cg->throwSyntaxError(ast->firstSourceLocation(), QStringLiteral("Identifier %1 has already been declared").arg(name)); - return; + return false; } + outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr); } if (name == QLatin1String("arguments")) outerContext->usesArgumentsObject = Context::ArgumentsObjectNotUsed; } - if (formalsContainName(formals, QStringLiteral("arguments"))) + _context->name = name; + if (formals && formals->containsName(QStringLiteral("arguments"))) _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + if (expr) { + if (expr->isArrowFunction) + _context->isArrowFunction = true; + else if (expr->isGenerator) + _context->isGenerator = true; + } - if (!name.isEmpty() && !formalsContainName(formals, name)) - _context->addLocalVar(name, Context::ThisFunctionName, QQmlJS::AST::VariableDeclaration::FunctionScope); + if (!name.isEmpty() && (!formals || !formals->containsName(name))) + _context->addLocalVar(name, Context::ThisFunctionName, VariableScope::Var); _context->formals = formals; if (body && !_context->isStrict) - checkDirectivePrologue(body->elements); - - for (FormalParameterList *it = formals; it; it = it->next) { - QString arg = it->name.toString(); - int duplicateIndex = _context->arguments.indexOf(arg); - if (duplicateIndex != -1) { - if (_context->isStrict) { - _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); - return; - } else { - // change the name of the earlier argument to enforce the specified lookup semantics - QString modified = arg; - while (_context->arguments.contains(modified)) - modified += QString(0xfffe); - _context->arguments[duplicateIndex] = modified; + checkDirectivePrologue(body); + + bool isSimpleParameterList = formals && formals->isSimpleParameterList(); + + _context->arguments = formals ? formals->formals() : QStringList(); + + const QStringList boundNames = formals ? formals->boundNames() : QStringList(); + for (int i = 0; i < boundNames.size(); ++i) { + const QString &arg = boundNames.at(i); + if (_context->isStrict || !isSimpleParameterList) { + bool duplicate = (boundNames.indexOf(arg, i + 1) != -1); + if (duplicate) { + _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("Duplicate parameter name '%1' is not allowed.").arg(arg)); + return false; } } if (_context->isStrict) { if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) { - _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg)); - return; + _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg)); + return false; } } - _context->arguments += arg; + if (!_context->arguments.contains(arg)) + _context->addLocalVar(arg, Context::VariableDefinition, VariableScope::Var); } + return true; } void ScanFunctions::calcEscapingVariables() @@ -450,27 +508,114 @@ void ScanFunctions::calcEscapingVariables() Module *m = _cg->_module; for (Context *inner : qAsConst(m->contextMap)) { + if (inner->contextType == ContextType::Block && inner->usesArgumentsObject == Context::ArgumentsObjectUsed) { + Context *c = inner->parent; + while (c->contextType == ContextType::Block) + c = c->parent; + c->usesArgumentsObject = Context::ArgumentsObjectUsed; + inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + } + } + for (Context *inner : qAsConst(m->contextMap)) { + if (!inner->parent || inner->usesArgumentsObject == Context::ArgumentsObjectUnknown) + inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + if (inner->usesArgumentsObject == Context::ArgumentsObjectUsed) { + QString arguments = QStringLiteral("arguments"); + inner->addLocalVar(arguments, Context::VariableDeclaration, AST::VariableScope::Var); + if (!inner->isStrict) { + inner->argumentsCanEscape = true; + inner->requiresExecutionContext = true; + } + } + } + + for (Context *inner : qAsConst(m->contextMap)) { for (const QString &var : qAsConst(inner->usedVariables)) { Context *c = inner; while (c) { + Context *current = c; + c = c->parent; + if (current->isWithBlock || current->contextType != ContextType::Block) + break; + } + Q_ASSERT(c != inner); + while (c) { Context::MemberMap::const_iterator it = c->members.find(var); if (it != c->members.end()) { - if (c != inner) + if (c->parent || it->isLexicallyScoped()) { it->canEscape = true; + c->requiresExecutionContext = true; + } break; } if (c->findArgument(var) != -1) { - if (c != inner) - c->argumentsCanEscape = true; + c->argumentsCanEscape = true; + c->requiresExecutionContext = true; break; } c = c->parent; } } - Context *c = inner->parent; - while (c) { - c->hasDirectEval |= inner->hasDirectEval; - c = c->parent; + if (inner->hasDirectEval) { + inner->hasDirectEval = false; + if (!inner->isStrict) { + Context *c = inner; + while (c->contextType == ContextType::Block) { + c = c->parent; + } + Q_ASSERT(c); + c->hasDirectEval = true; + } + Context *c = inner; + while (c) { + c->allVarsEscape = true; + c = c->parent; + } + } + if (inner->usesThis) { + inner->usesThis = false; + if (!inner->isStrict) { + Context *c = inner; + while (c->contextType == ContextType::Block) { + c = c->parent; + } + Q_ASSERT(c); + c->usesThis = true; + } + } + } + for (Context *c : qAsConst(m->contextMap)) { + if (c->allVarsEscape && c->contextType == ContextType::Block && c->members.isEmpty()) + c->allVarsEscape = false; + if (c->contextType == ContextType::Global || (!c->isStrict && c->contextType == ContextType::Eval) || m->debugMode) + c->allVarsEscape = true; + if (c->allVarsEscape) { + if (c->parent) { + c->requiresExecutionContext = true; + c->argumentsCanEscape = true; + } else { + for (const auto &m : qAsConst(c->members)) { + if (m.isLexicallyScoped()) { + c->requiresExecutionContext = true; + break; + } + } + } + } + if (c->contextType == ContextType::Block && c->isCatchBlock) { + c->requiresExecutionContext = true; + auto m = c->members.find(c->caughtVariable); + m->canEscape = true; + } + const QLatin1String exprForOn("expression for on"); + if (c->contextType == ContextType::Binding && c->name.length() > exprForOn.size() && + c->name.startsWith(exprForOn) && c->name.at(exprForOn.size()).isUpper()) + // we don't really need this for bindings, but we do for signal handlers, and in this case, + // we don't know if the code is a signal handler or not. + c->requiresExecutionContext = true; + if (c->allVarsEscape) { + for (auto &m : c->members) + m.canEscape = true; } } @@ -478,10 +623,12 @@ void ScanFunctions::calcEscapingVariables() if (showEscapingVars) { qDebug() << "==== escaping variables ===="; for (Context *c : qAsConst(m->contextMap)) { - qDebug() << "Context" << c->name << ":"; - qDebug() << " Arguments escape" << c->argumentsCanEscape; + qDebug() << "Context" << c << c->name << "requiresExecutionContext" << c->requiresExecutionContext << "isStrict" << c->isStrict; + qDebug() << " parent:" << c->parent; + if (c->argumentsCanEscape) + qDebug() << " Arguments escape"; for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) { - qDebug() << " " << it.key() << it.value().canEscape; + qDebug() << " " << it.key() << it.value().index << it.value().canEscape << "isLexicallyScoped:" << it.value().isLexicallyScoped(); } } } diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h index 87b7210879..4c273600b3 100644 --- a/src/qml/compiler/qv4compilerscanfunctions_p.h +++ b/src/qml/compiler/qv4compilerscanfunctions_p.h @@ -58,6 +58,7 @@ #include <private/qv4util_p.h> #include <QtCore/QStringList> #include <QStack> +#include <QScopedValueRollback> QT_BEGIN_NAMESPACE @@ -79,13 +80,13 @@ class Codegen; class ScanFunctions: protected QQmlJS::AST::Visitor { - typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment; + typedef QScopedValueRollback<bool> TemporaryBoolAssignment; public: - ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode); + ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType); void operator()(AST::Node *node); - void enterGlobalEnvironment(CompilationMode compilationMode); - void enterEnvironment(AST::Node *node, CompilationMode compilationMode); + void enterGlobalEnvironment(ContextType compilationMode); + void enterEnvironment(AST::Node *node, ContextType compilationMode, const QString &name); void leaveEnvironment(); void enterQmlFunction(AST::FunctionDeclaration *ast) @@ -95,48 +96,60 @@ protected: using Visitor::visit; using Visitor::endVisit; - void checkDirectivePrologue(AST::SourceElements *ast); + void checkDirectivePrologue(AST::StatementList *ast); void checkName(const QStringRef &name, const AST::SourceLocation &loc); - bool formalsContainName(AST::FormalParameterList *parameters, const QString &name); bool visit(AST::Program *ast) override; void endVisit(AST::Program *) override; bool visit(AST::CallExpression *ast) override; - bool visit(AST::NewMemberExpression *ast) override; - bool visit(AST::ArrayLiteral *ast) override; - bool visit(AST::VariableDeclaration *ast) override; + bool visit(AST::PatternElement *ast) override; bool visit(AST::IdentifierExpression *ast) override; bool visit(AST::ExpressionStatement *ast) override; bool visit(AST::FunctionExpression *ast) override; + bool visit(AST::TemplateLiteral *ast) override; - void enterFunction(AST::FunctionExpression *ast, bool enterName); + bool enterFunction(AST::FunctionExpression *ast, bool enterName); void endVisit(AST::FunctionExpression *) override; - bool visit(AST::ObjectLiteral *ast) override; + bool visit(AST::ObjectPattern *ast) override; - bool visit(AST::PropertyGetterSetter *ast) override; - void endVisit(AST::PropertyGetterSetter *) override; + bool visit(AST::PatternProperty *ast) override; + void endVisit(AST::PatternProperty *) override; bool visit(AST::FunctionDeclaration *ast) override; void endVisit(AST::FunctionDeclaration *) override; - bool visit(AST::TryStatement *ast) override; - bool visit(AST::WithStatement *ast) override; + bool visit(AST::ClassExpression *ast) override; + void endVisit(AST::ClassExpression *) override; + + bool visit(AST::ClassDeclaration *ast) override; + void endVisit(AST::ClassDeclaration *) override; bool visit(AST::DoWhileStatement *ast) override; bool visit(AST::ForStatement *ast) override; - bool visit(AST::LocalForStatement *ast) override; + void endVisit(AST::ForStatement *) override; bool visit(AST::ForEachStatement *ast) override; - bool visit(AST::LocalForEachStatement *ast) override; + void endVisit(AST::ForEachStatement *) override; + bool visit(AST::ThisExpression *ast) override; bool visit(AST::Block *ast) override; + void endVisit(AST::Block *ast) override; + + bool visit(AST::CaseBlock *ast) override; + void endVisit(AST::CaseBlock *ast) override; + + bool visit(AST::Catch *ast) override; + void endVisit(AST::Catch *ast) override; + + bool visit(AST::WithStatement *ast) override; + void endVisit(AST::WithStatement *ast) override; protected: - void enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::FunctionBody *body, AST::FunctionExpression *expr); + bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName); void calcEscapingVariables(); // fields: @@ -146,7 +159,7 @@ protected: QStack<Context *> _contextStack; bool _allowFuncDecls; - CompilationMode defaultProgramMode; + ContextType defaultProgramType; private: static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr; diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index 450fa50528..8e474b3783 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -39,15 +39,16 @@ #include "qv4instr_moth_p.h" #include <private/qv4compileddata_p.h> +#include <private/qv4stackframe_p.h> using namespace QV4; using namespace QV4::Moth; int InstrInfo::size(Instr::Type type) { -#define MOTH_RETURN_INSTR_SIZE(I) case Instr::Type::I: return InstrMeta<int(Instr::Type::I)>::Size; +#define MOTH_RETURN_INSTR_SIZE(I) case Instr::Type::I: case Instr::Type::I##_Wide: return InstrMeta<int(Instr::Type::I)>::Size; switch (type) { - FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE) + FOR_EACH_MOTH_INSTR_ALL(MOTH_RETURN_INSTR_SIZE) } #undef MOTH_RETURN_INSTR_SIZE Q_UNREACHABLE(); @@ -110,6 +111,8 @@ static QString toString(QV4::ReturnedValue v) QDebug d = qDebug(); \ d.noquote(); \ d.nospace(); \ + if (static_cast<int>(Instr::Type::instr) >= 0x100) \ + --base_ptr; \ d << alignedLineNumber(line) << alignedNumber(codeOffset).constData() << ": " \ << rawBytes(base_ptr, int(code - base_ptr)) << #instr << " "; @@ -122,7 +125,7 @@ namespace QV4 { namespace Moth { const int InstrInfo::argumentCount[] = { - FOR_EACH_MOTH_INSTR(MOTH_COLLECT_NARGS) + FOR_EACH_MOTH_INSTR_ALL(MOTH_COLLECT_NARGS) }; @@ -147,6 +150,8 @@ QString dumpRegister(int reg, int nFormals) return QStringLiteral("(context)"); else if (reg == CallData::Accumulator) return QStringLiteral("(accumulator)"); + else if (reg == CallData::NewTarget) + return QStringLiteral("(new.target)"); else if (reg == CallData::This) return QStringLiteral("(this)"); else if (reg == CallData::Argc) @@ -286,10 +291,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(StoreNameStrict) MOTH_BEGIN_INSTR(LoadElement) - d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; - MOTH_END_INSTR(LoadElement) - - MOTH_BEGIN_INSTR(LoadElementA) d << dumpRegister(base, nFormals) << "[acc]"; MOTH_END_INSTR(LoadElement) @@ -298,20 +299,12 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) - d << dumpRegister(base, nFormals) << "[" << name << "]"; - MOTH_END_INSTR(LoadProperty) - - MOTH_BEGIN_INSTR(LoadPropertyA) d << "acc[" << name << "]"; - MOTH_END_INSTR(LoadElementA) + MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) - d << dumpRegister(base, nFormals) << "(" << index << ")"; - MOTH_END_INSTR(GetLookup) - - MOTH_BEGIN_INSTR(GetLookupA) d << "acc(" << index << ")"; - MOTH_END_INSTR(GetLookupA) + MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) d << dumpRegister(base, nFormals) << "[" << name<< "]"; @@ -321,6 +314,14 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << dumpRegister(base, nFormals) << "(" << index << ")"; MOTH_END_INSTR(SetLookup) + MOTH_BEGIN_INSTR(LoadSuperProperty) + d << dumpRegister(property, nFormals); + MOTH_END_INSTR(LoadSuperProperty) + + MOTH_BEGIN_INSTR(StoreSuperProperty) + d << dumpRegister(property, nFormals); + MOTH_END_INSTR(StoreSuperProperty) + MOTH_BEGIN_INSTR(StoreScopeObjectProperty) d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]"; MOTH_END_INSTR(StoreScopeObjectProperty) @@ -341,6 +342,13 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << dumpRegister(base, nFormals) << "[" << index << "]"; MOTH_END_INSTR(LoadIdObject) + MOTH_BEGIN_INSTR(Yield) + MOTH_END_INSTR(Yield) + + MOTH_BEGIN_INSTR(Resume) + d << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(Resume) + MOTH_BEGIN_INSTR(CallValue) d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallValue) @@ -377,12 +385,31 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallContextObjectProperty) - MOTH_BEGIN_INSTR(SetExceptionHandler) + MOTH_BEGIN_INSTR(CallWithSpread) + d << "new" << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallWithSpread) + + MOTH_BEGIN_INSTR(Construct) + d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(Construct) + + MOTH_BEGIN_INSTR(ConstructWithSpread) + d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(ConstructWithSpread) + + MOTH_BEGIN_INSTR(SetUnwindHandler) if (offset) d << ABSOLUTE_OFFSET(); else d << "<null>"; - MOTH_END_INSTR(SetExceptionHandler) + MOTH_END_INSTR(SetUnwindHandler) + + MOTH_BEGIN_INSTR(UnwindDispatch) + MOTH_END_INSTR(UnwindDispatch) + + MOTH_BEGIN_INSTR(UnwindToLabel) + d << "(" << level << ") " << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(UnwindToLabel) MOTH_BEGIN_INSTR(ThrowException) MOTH_END_INSTR(ThrowException) @@ -397,30 +424,47 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(CreateCallContext) MOTH_BEGIN_INSTR(PushCatchContext) - d << dumpRegister(reg, nFormals) << ", " << name; + d << index << ", " << name; MOTH_END_INSTR(PushCatchContext) MOTH_BEGIN_INSTR(PushWithContext) - d << dumpRegister(reg, nFormals); MOTH_END_INSTR(PushWithContext) + MOTH_BEGIN_INSTR(PushBlockContext) + d << index; + MOTH_END_INSTR(PushBlockContext) + + MOTH_BEGIN_INSTR(CloneBlockContext) + MOTH_END_INSTR(CloneBlockContext) + + MOTH_BEGIN_INSTR(PushScriptContext) + d << index; + MOTH_END_INSTR(PushScriptContext) + + MOTH_BEGIN_INSTR(PopScriptContext) + MOTH_END_INSTR(PopScriptContext) + MOTH_BEGIN_INSTR(PopContext) - d << dumpRegister(reg, nFormals); MOTH_END_INSTR(PopContext) - MOTH_BEGIN_INSTR(ForeachIteratorObject) - MOTH_END_INSTR(ForeachIteratorObject) + MOTH_BEGIN_INSTR(GetIterator) + d << iterator; + MOTH_END_INSTR(GetIterator) + + MOTH_BEGIN_INSTR(IteratorNext) + d << dumpRegister(value, nFormals); + MOTH_END_INSTR(IteratorNext) - MOTH_BEGIN_INSTR(ForeachNextPropertyName) - MOTH_END_INSTR(ForeachNextPropertyName) + MOTH_BEGIN_INSTR(IteratorClose) + d << dumpRegister(done, nFormals); + MOTH_END_INSTR(IteratorClose) - MOTH_BEGIN_INSTR(DeleteMember) - d << dumpRegister(base, nFormals) << "[" << member << "]"; - MOTH_END_INSTR(DeleteMember) + MOTH_BEGIN_INSTR(DestructureRestElement) + MOTH_END_INSTR(DestructureRestElement) - MOTH_BEGIN_INSTR(DeleteSubscript) + MOTH_BEGIN_INSTR(DeleteProperty) d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; - MOTH_END_INSTR(DeleteSubscript) + MOTH_END_INSTR(DeleteProperty) MOTH_BEGIN_INSTR(DeleteName) d << name; @@ -442,24 +486,35 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(DefineArray) MOTH_BEGIN_INSTR(DefineObjectLiteral) - d << dumpRegister(args, nFormals) - << ", " << internalClassId - << ", " << arrayValueCount - << ", " << arrayGetterSetterCountAndFlags; + d << internalClassId + << ", " << argc + << ", " << dumpRegister(args, nFormals); MOTH_END_INSTR(DefineObjectLiteral) + MOTH_BEGIN_INSTR(CreateClass) + d << classIndex + << ", " << dumpRegister(heritage, nFormals) + << ", " << dumpRegister(computedNames, nFormals); + MOTH_END_INSTR(CreateClass) + MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) MOTH_END_INSTR(CreateMappedArgumentsObject) MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject) MOTH_END_INSTR(CreateUnmappedArgumentsObject) + MOTH_BEGIN_INSTR(CreateRestParameter) + d << argIndex; + MOTH_END_INSTR(CreateRestParameter) + MOTH_BEGIN_INSTR(ConvertThisToObject) MOTH_END_INSTR(ConvertThisToObject) - MOTH_BEGIN_INSTR(Construct) - d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); - MOTH_END_INSTR(Construct) + MOTH_BEGIN_INSTR(LoadSuperConstructor) + MOTH_END_INSTR(LoadSuperConstructor) + + MOTH_BEGIN_INSTR(ToObject) + MOTH_END_INSTR(ToObject) MOTH_BEGIN_INSTR(Jump) d << ABSOLUTE_OFFSET(); @@ -473,6 +528,14 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpFalse) + MOTH_BEGIN_INSTR(JumpNotUndefined) + d << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(JumpNotUndefined) + + MOTH_BEGIN_INSTR(JumpNoException) + d << ABSOLUTE_OFFSET(); + MOTH_END_INSTR(JumpNoException) + MOTH_BEGIN_INSTR(CmpEqNull) MOTH_END_INSTR(CmpEqNull) @@ -519,14 +582,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << dumpRegister(lhs, nFormals); MOTH_END_INSTR(CmpStrictNotEqual) - MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt) - d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET(); - MOTH_END_INSTR(JumpStrictEqualStackSlotInt) - - MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt) - d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET(); - MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) - MOTH_BEGIN_INSTR(UNot) MOTH_END_INSTR(UNot) @@ -597,6 +652,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << "acc, " << rhs; MOTH_END_INSTR(ShlConst) + MOTH_BEGIN_INSTR(Exp) + d << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Exp) + MOTH_BEGIN_INSTR(Mul) d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Mul) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index df9182e924..ce92a31590 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -53,6 +53,8 @@ #include <private/qv4global_p.h> #include <private/qv4value_p.h> #include <private/qv4runtime_p.h> +#include <private/qv4compileddata_p.h> // for CompiledData::CodeOffsetToLine used by the dumper +#include <qendian.h> QT_BEGIN_NAMESPACE @@ -60,6 +62,7 @@ QT_BEGIN_NAMESPACE op##_INSTRUCTION(name, nargs, __VA_ARGS__) /* for all jump instructions, the offset has to come last, to simplify the job of the bytecode generator */ +#define INSTR_Nop(op) INSTRUCTION(op, Nop, 0) #define INSTR_Ret(op) INSTRUCTION(op, Ret, 0) #define INSTR_Debug(op) INSTRUCTION(op, Debug, 0) #define INSTR_LoadConst(op) INSTRUCTION(op, LoadConst, 1, index) @@ -84,19 +87,20 @@ QT_BEGIN_NAMESPACE #define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index) #define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name) #define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name) -#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, base) -#define INSTR_LoadPropertyA(op) INSTRUCTION(op, LoadPropertyA, 1, name) -#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, base) -#define INSTR_GetLookupA(op) INSTRUCTION(op, GetLookupA, 1, index) +#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name) +#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 1, index) #define INSTR_LoadScopeObjectProperty(op) INSTRUCTION(op, LoadScopeObjectProperty, 3, propertyIndex, base, captureRequired) #define INSTR_LoadContextObjectProperty(op) INSTRUCTION(op, LoadContextObjectProperty, 3, propertyIndex, base, captureRequired) #define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base) +#define INSTR_Yield(op) INSTRUCTION(op, Yield, 0) +#define INSTR_Resume(op) INSTRUCTION(op, Resume, 1, offset) #define INSTR_StoreProperty(op) INSTRUCTION(op, StoreProperty, 2, name, base) #define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base) +#define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property) +#define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property) #define INSTR_StoreScopeObjectProperty(op) INSTRUCTION(op, StoreScopeObjectProperty, 2, base, propertyIndex) #define INSTR_StoreContextObjectProperty(op) INSTRUCTION(op, StoreContextObjectProperty, 2, base, propertyIndex) -#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, index) -#define INSTR_LoadElementA(op) INSTRUCTION(op, LoadElementA, 1, base) +#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 1, base) #define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index) #define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 3, name, argc, argv) #define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv) @@ -107,32 +111,46 @@ QT_BEGIN_NAMESPACE #define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv) #define INSTR_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 4, name, base, argc, argv) #define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 4, name, base, argc, argv) -#define INSTR_SetExceptionHandler(op) INSTRUCTION(op, SetExceptionHandler, 1, offset) +#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 4, func, thisObject, argc, argv) +#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv) +#define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv) +#define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset) +#define INSTR_UnwindDispatch(op) INSTRUCTION(op, UnwindDispatch, 0) +#define INSTR_UnwindToLabel(op) INSTRUCTION(op, UnwindToLabel, 2, level, offset) #define INSTR_ThrowException(op) INSTRUCTION(op, ThrowException, 0) #define INSTR_GetException(op) INSTRUCTION(op, GetException, 0) #define INSTR_SetException(op) INSTRUCTION(op, SetException, 0) #define INSTR_CreateCallContext(op) INSTRUCTION(op, CreateCallContext, 0) -#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 2, name, reg) -#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 1, reg) -#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 1, reg) -#define INSTR_ForeachIteratorObject(op) INSTRUCTION(op, ForeachIteratorObject, 0) -#define INSTR_ForeachNextPropertyName(op) INSTRUCTION(op, ForeachNextPropertyName, 0) -#define INSTR_DeleteMember(op) INSTRUCTION(op, DeleteMember, 2, member, base) -#define INSTR_DeleteSubscript(op) INSTRUCTION(op, DeleteSubscript, 2, base, index) +#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 2, index, name) +#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 0) +#define INSTR_PushBlockContext(op) INSTRUCTION(op, PushBlockContext, 1, index) +#define INSTR_CloneBlockContext(op) INSTRUCTION(op, CloneBlockContext, 0) +#define INSTR_PushScriptContext(op) INSTRUCTION(op, PushScriptContext, 1, index) +#define INSTR_PopScriptContext(op) INSTRUCTION(op, PopScriptContext, 0) +#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 0) +#define INSTR_GetIterator(op) INSTRUCTION(op, GetIterator, 1, iterator) +#define INSTR_IteratorNext(op) INSTRUCTION(op, IteratorNext, 1, value) +#define INSTR_IteratorClose(op) INSTRUCTION(op, IteratorClose, 1, done) +#define INSTR_DestructureRestElement(op) INSTRUCTION(op, DestructureRestElement, 0) +#define INSTR_DeleteProperty(op) INSTRUCTION(op, DeleteProperty, 2, base, index) #define INSTR_DeleteName(op) INSTRUCTION(op, DeleteName, 1, name) #define INSTR_TypeofName(op) INSTRUCTION(op, TypeofName, 1, name) #define INSTR_TypeofValue(op) INSTRUCTION(op, TypeofValue, 0) #define INSTR_DeclareVar(op) INSTRUCTION(op, DeclareVar, 2, varName, isDeletable) #define INSTR_DefineArray(op) INSTRUCTION(op, DefineArray, 2, argc, args) -// arrayGetterSetterCountAndFlags contains 30 bits for count, 1 bit for needsSparseArray boolean -#define INSTR_DefineObjectLiteral(op) INSTRUCTION(op, DefineObjectLiteral, 4, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags, args) +#define INSTR_DefineObjectLiteral(op) INSTRUCTION(op, DefineObjectLiteral, 3, internalClassId, argc, args) +#define INSTR_CreateClass(op) INSTRUCTION(op, CreateClass, 3, classIndex, heritage, computedNames) #define INSTR_CreateMappedArgumentsObject(op) INSTRUCTION(op, CreateMappedArgumentsObject, 0) #define INSTR_CreateUnmappedArgumentsObject(op) INSTRUCTION(op, CreateUnmappedArgumentsObject, 0) +#define INSTR_CreateRestParameter(op) INSTRUCTION(op, CreateRestParameter, 1, argIndex) #define INSTR_ConvertThisToObject(op) INSTRUCTION(op, ConvertThisToObject, 0) -#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv) +#define INSTR_LoadSuperConstructor(op) INSTRUCTION(op, LoadSuperConstructor, 0) +#define INSTR_ToObject(op) INSTRUCTION(op, ToObject, 0) #define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset) #define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset) #define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset) +#define INSTR_JumpNotUndefined(op) INSTRUCTION(op, JumpNotUndefined, 1, offset) +#define INSTR_JumpNoException(op) INSTRUCTION(op, JumpNoException, 1, offset) #define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0) #define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0) #define INSTR_CmpEqInt(op) INSTRUCTION(op, CmpEqInt, 1, lhs) @@ -147,8 +165,6 @@ QT_BEGIN_NAMESPACE #define INSTR_CmpStrictNotEqual(op) INSTRUCTION(op, CmpStrictNotEqual, 1, lhs) #define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs) #define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs) -#define INSTR_JumpStrictEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictEqualStackSlotInt, 3, lhs, rhs, offset) -#define INSTR_JumpStrictNotEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictNotEqualStackSlotInt, 3, lhs, rhs, offset) #define INSTR_UNot(op) INSTRUCTION(op, UNot, 0) #define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0) #define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0) @@ -168,6 +184,7 @@ QT_BEGIN_NAMESPACE #define INSTR_UShrConst(op) INSTRUCTION(op, UShrConst, 1, rhs) #define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs) #define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs) +#define INSTR_Exp(op) INSTRUCTION(op, Exp, 1, lhs) #define INSTR_Mul(op) INSTRUCTION(op, Mul, 1, lhs) #define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs) #define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs) @@ -175,10 +192,12 @@ QT_BEGIN_NAMESPACE #define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result) #define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) +#define FOR_EACH_MOTH_INSTR_ALL(F) \ + F(Nop) \ + FOR_EACH_MOTH_INSTR(F) #define FOR_EACH_MOTH_INSTR(F) \ F(Ret) \ - F(Debug) \ F(LoadConst) \ F(LoadZero) \ F(LoadTrue) \ @@ -186,6 +205,7 @@ QT_BEGIN_NAMESPACE F(LoadNull) \ F(LoadUndefined) \ F(LoadInt) \ + F(LoadRuntimeString) \ F(MoveConst) \ F(LoadReg) \ F(StoreReg) \ @@ -194,7 +214,6 @@ QT_BEGIN_NAMESPACE F(StoreLocal) \ F(LoadScopedLocal) \ F(StoreScopedLocal) \ - F(LoadRuntimeString) \ F(MoveRegExp) \ F(LoadClosure) \ F(LoadName) \ @@ -202,53 +221,25 @@ QT_BEGIN_NAMESPACE F(StoreNameSloppy) \ F(StoreNameStrict) \ F(LoadElement) \ - F(LoadElementA) \ F(StoreElement) \ F(LoadProperty) \ - F(LoadPropertyA) \ F(GetLookup) \ - F(GetLookupA) \ F(StoreProperty) \ F(SetLookup) \ + F(LoadSuperProperty) \ + F(StoreSuperProperty) \ F(StoreScopeObjectProperty) \ F(StoreContextObjectProperty) \ F(LoadScopeObjectProperty) \ F(LoadContextObjectProperty) \ F(LoadIdObject) \ - F(CallValue) \ - F(CallProperty) \ - F(CallPropertyLookup) \ - F(CallElement) \ - F(CallName) \ - F(CallPossiblyDirectEval) \ - F(CallGlobalLookup) \ - F(CallScopeObjectProperty) \ - F(CallContextObjectProperty) \ - F(SetExceptionHandler) \ - F(ThrowException) \ - F(GetException) \ - F(SetException) \ - F(CreateCallContext) \ - F(PushCatchContext) \ - F(PushWithContext) \ - F(PopContext) \ - F(ForeachIteratorObject) \ - F(ForeachNextPropertyName) \ - F(DeleteMember) \ - F(DeleteSubscript) \ - F(DeleteName) \ - F(TypeofName) \ - F(TypeofValue) \ - F(DeclareVar) \ - F(DefineArray) \ - F(DefineObjectLiteral) \ - F(CreateMappedArgumentsObject) \ - F(CreateUnmappedArgumentsObject) \ F(ConvertThisToObject) \ - F(Construct) \ + F(ToObject) \ F(Jump) \ F(JumpTrue) \ F(JumpFalse) \ + F(JumpNoException) \ + F(JumpNotUndefined) \ F(CmpEqNull) \ F(CmpNeNull) \ F(CmpEqInt) \ @@ -263,8 +254,6 @@ QT_BEGIN_NAMESPACE F(CmpStrictNotEqual) \ F(CmpIn) \ F(CmpInstanceOf) \ - F(JumpStrictEqualStackSlotInt) \ - F(JumpStrictNotEqualStackSlotInt) \ F(UNot) \ F(UPlus) \ F(UMinus) \ @@ -284,13 +273,60 @@ QT_BEGIN_NAMESPACE F(UShrConst) \ F(ShrConst) \ F(ShlConst) \ + F(Exp) \ F(Mul) \ F(Div) \ F(Mod) \ F(Sub) \ + F(CallValue) \ + F(CallProperty) \ + F(CallPropertyLookup) \ + F(CallElement) \ + F(CallName) \ + F(CallPossiblyDirectEval) \ + F(CallGlobalLookup) \ + F(CallScopeObjectProperty) \ + F(CallContextObjectProperty) \ + F(CallWithSpread) \ + F(Construct) \ + F(ConstructWithSpread) \ + F(SetUnwindHandler) \ + F(UnwindDispatch) \ + F(UnwindToLabel) \ + F(ThrowException) \ + F(GetException) \ + F(SetException) \ + F(CreateCallContext) \ + F(PushCatchContext) \ + F(PushWithContext) \ + F(PushBlockContext) \ + F(CloneBlockContext) \ + F(PopContext) \ + F(GetIterator) \ + F(IteratorNext) \ + F(IteratorClose) \ + F(DestructureRestElement) \ + F(DeleteProperty) \ + F(DeleteName) \ + F(TypeofName) \ + F(TypeofValue) \ + F(DeclareVar) \ + F(DefineArray) \ + F(DefineObjectLiteral) \ + F(CreateMappedArgumentsObject) \ + F(CreateUnmappedArgumentsObject) \ + F(CreateRestParameter) \ F(LoadQmlContext) \ - F(LoadQmlImportedScripts) -#define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::LoadQmlImportedScripts) + 1) + F(LoadQmlImportedScripts) \ + F(Yield) \ + F(Resume) \ + F(CreateClass) \ + F(LoadSuperConstructor) \ + F(PushScriptContext) \ + F(PopScriptContext) \ + F(Debug) \ + +#define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::Debug_Wide) + 1) #if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) // icc before version 1200 doesn't support computed goto, and at least up to version 18.0.0 the @@ -301,7 +337,7 @@ QT_BEGIN_NAMESPACE #define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1) -#define MOTH_INSTR_ENUM(I) I, +#define MOTH_INSTR_ENUM(I) I, I##_Wide, #define MOTH_INSTR_SIZE(I) (sizeof(QV4::Moth::Instr::instr_##I)) #define MOTH_EXPAND_FOR_MSVC(x) x @@ -344,7 +380,7 @@ QT_BEGIN_NAMESPACE #define MOTH_COLLECT_NARGS(instr) \ INSTR_##instr(MOTH_COLLECT_ARG_COUNT) #define MOTH_COLLECT_ARG_COUNT_INSTRUCTION(name, nargs, ...) \ - nargs, + nargs, nargs, #define MOTH_DECODE_ARG(arg, type, nargs, offset) \ arg = qFromLittleEndian<type>(qFromUnaligned<type>(reinterpret_cast<const type *>(code) - nargs + offset)); @@ -397,38 +433,53 @@ QT_BEGIN_NAMESPACE #ifdef MOTH_COMPUTED_GOTO /* collect jump labels */ #define COLLECT_LABELS(instr) \ - INSTR_##instr(GET_LABEL) + INSTR_##instr(GET_LABEL) \ + INSTR_##instr(GET_LABEL_WIDE) #define GET_LABEL_INSTRUCTION(name, ...) \ &&op_byte_##name, -#define COLLECT_LABELS_WIDE(instr) \ - INSTR_##instr(GET_LABEL_WIDE) #define GET_LABEL_WIDE_INSTRUCTION(name, ...) \ &&op_int_##name, #define MOTH_JUMP_TABLE \ static const void *jumpTable[] = { \ - FOR_EACH_MOTH_INSTR(COLLECT_LABELS) \ - FOR_EACH_MOTH_INSTR(COLLECT_LABELS_WIDE) \ + FOR_EACH_MOTH_INSTR_ALL(COLLECT_LABELS) \ }; -#define MOTH_DISPATCH() \ +#define MOTH_DISPATCH_SINGLE() \ goto *jumpTable[*reinterpret_cast<const uchar *>(code)]; + +#define MOTH_DISPATCH() \ + MOTH_DISPATCH_SINGLE() \ + op_byte_Nop: \ + ++code; \ + MOTH_DISPATCH_SINGLE() \ + op_int_Nop: /* wide prefix */ \ + ++code; \ + goto *jumpTable[0x100 | *reinterpret_cast<const uchar *>(code)]; #else #define MOTH_JUMP_TABLE #define MOTH_INSTR_CASE_AND_JUMP(instr) \ - INSTR_##instr(GET_CASE_AND_JUMP) -#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \ - case static_cast<uchar>(Instr::Type::name): goto op_byte_##name; -#define MOTH_INSTR_CASE_AND_JUMP_WIDE(instr) \ + INSTR_##instr(GET_CASE_AND_JUMP) \ INSTR_##instr(GET_CASE_AND_JUMP_WIDE) +#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \ + case Instr::Type::name: goto op_byte_##name; #define GET_CASE_AND_JUMP_WIDE_INSTRUCTION(name, ...) \ - case (static_cast<uchar>(Instr::Type::name) + MOTH_NUM_INSTRUCTIONS()): goto op_int_##name; + case Instr::Type::name##_Wide: goto op_int_##name; #define MOTH_DISPATCH() \ - switch (static_cast<uchar>(*code)) { \ + Instr::Type type = Instr::Type(static_cast<uchar>(*code)); \ + dispatch: \ + switch (type) { \ + case Instr::Type::Nop: \ + ++code; \ + type = Instr::Type(static_cast<uchar>(*code)); \ + goto dispatch; \ + case Instr::Type::Nop_Wide: /* wide prefix */ \ + ++code; \ + type = Instr::Type(0x100 | static_cast<uchar>(*code)); \ + goto dispatch; \ FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP) \ - FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP_WIDE) \ } #endif @@ -471,12 +522,29 @@ inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals, union Instr { enum class Type { - FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) + FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_ENUM) }; - FOR_EACH_MOTH_INSTR(MOTH_EMIT_STRUCTS) + static Type wideInstructionType(Type t) { return Type(int(t) | 1); } + static Type narrowInstructionType(Type t) { return Type(int(t) & ~1); } + static bool isWide(Type t) { return int(t) & 1; } + static bool isNarrow(Type t) { return !(int(t) & 1); } + static int encodedLength(Type t) { return int(t) >= 256 ? 2 : 1; } + + static Type unpack(const uchar *c) { if (c[0] == 0x1) return Type(0x100 + c[1]); return Type(c[0]); } + static uchar *pack(uchar *c, Type t) { + if (uint(t) >= 256) { + c[0] = 0x1; + c[1] = uint(t) &0xff; + return c + 2; + } + c[0] = uchar(uint(t)); + return c + 1; + } + + FOR_EACH_MOTH_INSTR_ALL(MOTH_EMIT_STRUCTS) - FOR_EACH_MOTH_INSTR(MOTH_EMIT_INSTR_MEMBERS) + FOR_EACH_MOTH_INSTR_ALL(MOTH_EMIT_INSTR_MEMBERS) int argumentsAsInts[4]; }; @@ -487,8 +555,6 @@ struct InstrInfo static int size(Instr::Type type); }; -Q_STATIC_ASSERT(MOTH_NUM_INSTRUCTIONS() < 128); - template<int N> struct InstrMeta { }; @@ -506,7 +572,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") reinterpret_cast<const char *>(&v), \ Size); } \ }; -FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); +FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_META_TEMPLATE); #undef MOTH_INSTR_META_TEMPLATE QT_WARNING_POP @@ -517,7 +583,7 @@ class InstrData : public InstrMeta<InstrType>::DataType struct Instruction { #define MOTH_INSTR_DATA_TYPEDEF(I) typedef InstrData<int(Instr::Type::I)> I; -FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) +FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_DATA_TYPEDEF) #undef MOTH_INSTR_DATA_TYPEDEF private: Instruction(); diff --git a/src/qml/configure.json b/src/qml/configure.json index 681cecea99..481cc553ae 100644 --- a/src/qml/configure.json +++ b/src/qml/configure.json @@ -40,6 +40,59 @@ ], "output": [ "privateFeature" ] }, + "qml-preview": { + "label": "Command line QML Preview tool", + "purpose": "Updates QML documents in your application live as you change them on disk", + "section": "QML", + "condition": [ + "features.commandlineparser", + "features.localserver", + "features.process", + "features.qml-debug" + ], + "output": [ "privateFeature" ] + }, + "qml-devtools": { + "label": "QML Development Tools", + "purpose": "Provides the QmlDevtools library and various utilities.", + "section": "QML", + "output": [ "privateFeature" ] + }, + "qml-sequence-object": { + "label": "QML sequence object", + "purpose": "Supports mapping sequence types into QML.", + "section": "QML", + "output": [ "privateFeature" ] + }, + "qml-list-model": { + "label": "QML list model", + "purpose": "Provides the ListModel QML type.", + "section": "QML", + "output": [ "privateFeature" ] + }, + "qml-xml-http-request": { + "label": "QML XML http request", + "purpose": "Provides support for sending XML http requests.", + "section": "QML", + "condition": [ + "features.xmlstreamreader", + "features.qml-network" + ], + "output": [ "privateFeature" ] + }, + "qml-locale": { + "label": "QML Locale", + "purpose": "Provides support for locales in QML.", + "section": "QML", + "output": [ "privateFeature" ] + }, + "qml-animation": { + "label": "QML Animations", + "purpose": "Provides support for animations and timers in QML.", + "section": "QML", + "condition": "features.animation", + "output": [ "privateFeature" ] + }, "qml-delegate-model": { "label": "QML delegate model", "purpose": "Provides the DelegateModel QML type.", @@ -54,6 +107,10 @@ "entries": [ "qml-network", "qml-debug", + "qml-sequence-object", + "qml-list-model", + "qml-xml-http-request", + "qml-locale", "qml-delegate-model" ] } diff --git a/src/qml/debugger/qqmlabstractprofileradapter_p.h b/src/qml/debugger/qqmlabstractprofileradapter_p.h index c63e694c7e..f39f8fccd2 100644 --- a/src/qml/debugger/qqmlabstractprofileradapter_p.h +++ b/src/qml/debugger/qqmlabstractprofileradapter_p.h @@ -73,13 +73,13 @@ public: ~QQmlAbstractProfilerAdapter() override {} void setService(QQmlProfilerService *new_service) { service = new_service; } - virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages, bool trackLocations) = 0; + virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages) = 0; void startProfiling(quint64 features); void stopProfiling(); - void reportData(bool trackLocations) { emit dataRequested(trackLocations); } + void reportData() { emit dataRequested(); } void stopWaiting() { waiting = false; } void startWaiting() { waiting = true; } @@ -96,7 +96,7 @@ signals: void profilingDisabled(); void profilingDisabledWhileWaiting(); - void dataRequested(bool trackLocations); + void dataRequested(); void referenceTimeKnown(const QElapsedTimer &timer); protected: diff --git a/src/qml/debugger/qqmldebugconnector.cpp b/src/qml/debugger/qqmldebugconnector.cpp index d9f51ce09f..0ef40d6911 100644 --- a/src/qml/debugger/qqmldebugconnector.cpp +++ b/src/qml/debugger/qqmldebugconnector.cpp @@ -82,7 +82,7 @@ Q_GLOBAL_STATIC(QQmlDebugConnectorParams, qmlDebugConnectorParams) void QQmlDebugConnector::setPluginKey(const QString &key) { QQmlDebugConnectorParams *params = qmlDebugConnectorParams(); - if (params) { + if (params && params->pluginKey != key) { if (params->instance) qWarning() << "QML debugger: Cannot set plugin key after loading the plugin."; else diff --git a/src/qml/debugger/qqmlprofiler.cpp b/src/qml/debugger/qqmlprofiler.cpp index 8c0bd73822..da0b14dd85 100644 --- a/src/qml/debugger/qqmlprofiler.cpp +++ b/src/qml/debugger/qqmlprofiler.cpp @@ -59,19 +59,18 @@ void QQmlProfiler::startProfiling(quint64 features) void QQmlProfiler::stopProfiling() { featuresEnabled = false; - reportData(true); + reportData(); m_locations.clear(); } -void QQmlProfiler::reportData(bool trackLocations) +void QQmlProfiler::reportData() { LocationHash resolved; resolved.reserve(m_locations.size()); for (auto it = m_locations.begin(), end = m_locations.end(); it != end; ++it) { - if (!trackLocations || !it->sent) { + if (!it->sent) { resolved.insert(it.key(), it.value()); - if (trackLocations) - it->sent = true; + it->sent = true; } } diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 287c53ea05..d01e2bc429 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -383,7 +383,7 @@ public: void startProfiling(quint64 features); void stopProfiling(); - void reportData(bool trackLocations); + void reportData(); void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc index 8a606d672a..7fb4724f73 100644 --- a/src/qml/doc/src/cppintegration/data.qdoc +++ b/src/qml/doc/src/cppintegration/data.qdoc @@ -262,8 +262,8 @@ method. \section2 Sequence Type to JavaScript Array -Certain C++ sequence types are supported transparently in QML as JavaScript -\c Array types. +Certain C++ sequence types are supported transparently in QML to behave like +JavaScript \c Array types. In particular, QML currently supports: \list diff --git a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc index aefdfd9401..52534b4a62 100644 --- a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc +++ b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc @@ -255,9 +255,6 @@ type, and so cannot provide the necessary QML property characteristics through the Qt meta object system, such as signal notifications when a list is modified. -QQmlListProperty is a template class that can be conveniently constructed from -a QList value. - For example, the \c MessageBoard class below has a \c messages property of type QQmlListProperty that stores a list of \c Message instances: diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index 55ca040af6..62c0f5d81b 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -538,30 +538,6 @@ } \endqml - Since singleton types do not have an associated QQmlContext object, then within the functions of a QObject-derived - type that is registered as a singleton type implementation the QML context and engine information is not available. - The QQmlEngine::contextForObject() function returns NULL when supplied with a pointer to an QObject that - implements a singleton type. - - Extending the above example: - - \code - class SingletonTypeExample : public QObject - { - ... - - Q_INVOKABLE void doSomethingElse() - { - // QML Engine/Context information is not accessible here: - Q_ASSERT(QQmlEngine::contextForObject(this) == 0); - Q_ASSERT(qmlContext(this) == 0); - Q_ASSERT(qmlEngine(this) == 0); - } - - ... - } - \endcode - \sa {Choosing the Correct Integration Method Between C++ and QML} */ @@ -662,3 +638,25 @@ are registered for that version. This is particularly useful for keeping the versions of related modules in sync. */ + +/*! + \since 5.12 + \fn int qmlTypeId(const char* uri, int versionMajor, int versionMinor, const char *qmlName); + \relates QQmlEngine + + Returns the QML type id of a type that was registered with the + name \a qmlName in a particular \a uri and a version specified in \a + versionMajor and \a versionMinor. + + This function returns the same value as the QML type registration functions + such as qmlRegisterType() and qmlRegisterSingletonType(). + + If \a qmlName, \a uri and \a versionMajor match a registered type, but the + specified minor version in \a versionMinor is higher, then the id of the type + with the closest minor version is returned. + + Returns -1 if no matching type was found or one of the given parameters + was invalid. + + \sa qmlRegisterType(), qmlRegisterSingletonType() +*/ diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri index 2080844d93..9e3cf44387 100644 --- a/src/qml/jit/jit.pri +++ b/src/qml/jit/jit.pri @@ -2,9 +2,11 @@ INCLUDEPATH += $$PWD INCLUDEPATH += $$OUT_PWD SOURCES += \ - $$PWD/qv4jit.cpp \ + $$PWD/qv4jithelpers.cpp \ + $$PWD/qv4baselinejit.cpp \ $$PWD/qv4assembler.cpp HEADERS += \ - $$PWD/qv4jit_p.h \ + $$PWD/qv4jithelpers_p.h \ + $$PWD/qv4baselinejit_p.h \ $$PWD/qv4assembler_p.h diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 9786293e4c..7d668950d7 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -44,6 +44,7 @@ #include "qv4assembler_p.h" #include <private/qv4function_p.h> #include <private/qv4runtime_p.h> +#include <private/qv4stackframe_p.h> #include <wtf/Vector.h> #include <assembler/MacroAssembler.h> @@ -680,6 +681,12 @@ struct PlatformAssembler64 : PlatformAssemblerCommon store64(AccumulatorRegister, addr); } + void moveReg(Address sourceRegAddress, Address destRegAddress) + { + load64(sourceRegAddress, ScratchRegister); + store64(ScratchRegister, destRegAddress); + } + void loadString(int stringId) { loadAccumulator(loadStringAddress(stringId)); @@ -700,6 +707,17 @@ struct PlatformAssembler64 : PlatformAssemblerCommon PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();}); } + void jumpNotUndefined(int offset) + { + auto jump = branch64(NotEqual, AccumulatorRegister, TrustedImm64(0)); + patches.push_back({ jump, offset }); + } + + Jump jumpEmpty() + { + return branch64(Equal, AccumulatorRegister, TrustedImm64(Primitive::emptyValue().asReturnedValue())); + } + void toBoolean(std::function<void(RegisterID)> continuation) { urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerConvertible_Shift), ScratchRegister); @@ -803,26 +821,6 @@ struct PlatformAssembler64 : PlatformAssemblerCommon return branch32(Equal, TrustedImm32(3), ScratchRegister); } - void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset) - { - Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value))); - load64(lhsAddr, ScratchRegister); - Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0)); - Jump equal = branch32(Equal, TrustedImm32(rhs), ScratchRegister); - patches.push_back({ equal, offset }); - isUndef.link(this); - } - - void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset) - { - Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value))); - load64(lhsAddr, ScratchRegister); - Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0)); - patches.push_back({ isUndef, offset }); - Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister); - patches.push_back({ notEqual, offset }); - } - void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister) { if (sourceReg == NoRegister) @@ -960,6 +958,16 @@ struct PlatformAssembler32 : PlatformAssemblerCommon store32(AccumulatorRegisterTag, addr); } + void moveReg(Address sourceRegAddress, Address destRegAddress) + { + load32(sourceRegAddress, ReturnValueRegisterValue); + sourceRegAddress.offset += 4; + load32(sourceRegAddress, ReturnValueRegisterTag); + store32(ReturnValueRegisterValue, destRegAddress); + destRegAddress.offset += 4; + store32(ReturnValueRegisterTag, destRegAddress); + } + void loadString(int stringId) { load32(loadStringAddress(stringId), AccumulatorRegisterValue); @@ -1135,6 +1143,19 @@ struct PlatformAssembler32 : PlatformAssemblerCommon push(TrustedImm32(v)); } + void jumpNotUndefined(int offset) + { + move(AccumulatorRegisterTag, ScratchRegister); + or32(AccumulatorRegisterValue, ScratchRegister); + auto jump = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); + patches.push_back({ jump, offset }); + } + + Jump jumpEmpty() + { + return branch32(Equal, AccumulatorRegisterTag, TrustedImm32(Primitive::emptyValue().asReturnedValue() >> 32)); + } + void toBoolean(std::function<void(RegisterID)> continuation) { urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerConvertible_Shift - 32), @@ -1174,34 +1195,6 @@ struct PlatformAssembler32 : PlatformAssemblerCommon done.link(this); } - void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset) - { - Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value))); - load32(lhsAddr, ScratchRegister); - Jump notEqInt = branch32(NotEqual, ScratchRegister, TrustedImm32(rhs)); - Jump notEqUndefVal = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); - patches.push_back({ notEqUndefVal, offset }); - lhsAddr.offset += 4; - load32(lhsAddr, ScratchRegister); - Jump notEqUndefTag = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); - patches.push_back({ notEqUndefTag, offset }); - notEqInt.link(this); - } - - void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset) - { - Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value))); - load32(lhsAddr, ScratchRegister); - Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister); - patches.push_back({ notEqual, offset }); - Jump notUndefValue = branch32(NotEqual, TrustedImm32(0), ScratchRegister); - lhsAddr.offset += 4; - load32(lhsAddr, ScratchRegister); - Jump equalUndef = branch32(Equal, TrustedImm32(0), ScratchRegister); - patches.push_back({ equalUndef, offset }); - notUndefValue.link(this); - } - void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister) { if (sourceReg != NoRegister) @@ -1415,7 +1408,6 @@ void Assembler::link(Function *function) function->codeRef = new JSC::MacroAssemblerCodeRef(codeRef); function->jittedCode = reinterpret_cast<Function::JittedCode>(function->codeRef->code().executableAddress()); -#if defined(Q_OS_LINUX) // This implements writing of JIT'd addresses so that perf can find the // symbol names. // @@ -1423,7 +1415,7 @@ void Assembler::link(Function *function) // content, for more information, see: // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP"); - if (doProfile) { + if (Q_UNLIKELY(doProfile)) { static QFile perfMapFile(QString::fromLatin1("/tmp/perf-%1.map") .arg(QCoreApplication::applicationPid())); static const bool isOpen = perfMapFile.open(QIODevice::WriteOnly); @@ -1441,7 +1433,6 @@ void Assembler::link(Function *function) perfMapFile.flush(); } } -#endif } void Assembler::addLabel(int offset) @@ -1469,6 +1460,11 @@ void Assembler::loadReg(int reg) pasm()->loadAccumulator(regAddr(reg)); } +void JIT::Assembler::moveReg(int sourceReg, int destReg) +{ + pasm()->moveReg(regAddr(sourceReg), regAddr(destReg)); +} + void Assembler::storeReg(int reg) { pasm()->storeAccumulator(regAddr(reg)); @@ -1981,14 +1977,19 @@ void Assembler::jumpFalse(int offset) }); } -void Assembler::jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset) +void Assembler::jumpNoException(int offset) { - pasm()->jumpStrictEqualStackSlotInt(lhs, rhs, offset); + auto jump = pasm()->branch32( + PlatformAssembler::Equal, + PlatformAssembler::Address(PlatformAssembler::EngineRegister, + offsetof(EngineBase, hasException)), + TrustedImm32(0)); + pasm()->patches.push_back({ jump, offset }); } -void Assembler::jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset) +void Assembler::jumpNotUndefined(int offset) { - pasm()->jumpStrictNotEqualStackSlotInt(lhs, rhs, offset); + pasm()->jumpNotUndefined(offset); } void Assembler::prepareCallWithArgCount(int argc) @@ -2182,39 +2183,70 @@ void Assembler::getException() void Assembler::setException() { + auto noException = pasm()->jumpEmpty(); Address addr(PlatformAssembler::EngineRegister, offsetof(EngineBase, exceptionValue)); pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister); pasm()->storeAccumulator(Address(PlatformAssembler::ScratchRegister)); addr.offset = offsetof(EngineBase, hasException); Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1); pasm()->store8(TrustedImm32(1), addr); + noException.link(pasm()); } -void Assembler::setExceptionHandler(int offset) +void Assembler::setUnwindHandler(int offset) { auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress()); pasm()->ehTargets.push_back({ l, offset }); } -void Assembler::clearExceptionHandler() +void Assembler::clearUnwindHandler() { pasm()->storePtr(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress()); } -void Assembler::pushCatchContext(int name, int reg) +void JIT::Assembler::unwindDispatch() { - pasm()->copyReg(pasm()->contextAddress(), regAddr(reg)); - prepareCallWithArgCount(2); - passInt32AsArg(name, 1); + checkException(); + pasm()->load32(Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)), PlatformAssembler::ScratchRegister); + auto noUnwind = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0)); + pasm()->sub32(TrustedImm32(1), PlatformAssembler::ScratchRegister); + pasm()->store32(PlatformAssembler::ScratchRegister, Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel))); + auto jump = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0)); + gotoCatchException(); + jump.link(pasm()); + + pasm()->loadPtr(Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel)), PlatformAssembler::ScratchRegister); + pasm()->jump(PlatformAssembler::ScratchRegister); + + noUnwind.link(pasm()); +} + +void JIT::Assembler::unwindToLabel(int level, int offset) +{ + auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel))); + pasm()->ehTargets.push_back({ l, offset }); + pasm()->store32(TrustedImm32(level), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel))); + gotoCatchException(); +} + +void Assembler::pushCatchContext(int index, int name) +{ + prepareCallWithArgCount(3); + passInt32AsArg(name, 2); + passInt32AsArg(index, 1); passRegAsArg(CallData::Context, 0); IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_createCatchContext, ResultInAccumulator); pasm()->storeAccumulator(pasm()->contextAddress()); } -void Assembler::popContext(int reg) +void Assembler::popContext() { - pasm()->copyReg(regAddr(reg), pasm()->contextAddress()); + Heap::CallContext ctx; + Q_UNUSED(ctx) + pasm()->loadPointerFromValue(regAddr(CallData::Context), PlatformAssembler::ScratchRegister); + pasm()->loadAccumulator(Address(PlatformAssembler::ScratchRegister, ctx.outer.offset)); + pasm()->storeAccumulator(regAddr(CallData::Context)); } void Assembler::ret() diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 37d4232a17..2cf59f53ee 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -90,6 +90,7 @@ public: void loadConst(int constIndex); void copyConst(int constIndex, int destReg); void loadReg(int reg); + void moveReg(int sourceReg, int destReg); void storeReg(int reg); void loadLocal(int index, int level = 0); void storeLocal(int index, int level = 0); @@ -140,8 +141,8 @@ public: void jump(int offset); void jumpTrue(int offset); void jumpFalse(int offset); - void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset); - void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset); + void jumpNoException(int offset); + void jumpNotUndefined(int offset); // stuff for runtime calls void prepareCallWithArgCount(int argc); @@ -160,10 +161,12 @@ public: void gotoCatchException(); void getException(); void setException(); - void setExceptionHandler(int offset); - void clearExceptionHandler(); - void pushCatchContext(int name, int reg); - void popContext(int reg); + void setUnwindHandler(int offset); + void clearUnwindHandler(); + void unwindDispatch(); + void unwindToLabel(int level, int offset); + void pushCatchContext(int index, int name); + void popContext(); // other stuff void ret(); diff --git a/src/qml/jit/qv4jit.cpp b/src/qml/jit/qv4baselinejit.cpp index bc46c0ca1d..9132b194a5 100644 --- a/src/qml/jit/qv4jit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -37,9 +37,11 @@ ** ****************************************************************************/ -#include "qv4jit_p.h" +#include "qv4baselinejit_p.h" +#include "qv4jithelpers_p.h" #include "qv4assembler_p.h" #include <private/qv4lookup_p.h> +#include <private/qv4generatorobject_p.h> #ifdef V4_ENABLE_JIT @@ -48,42 +50,6 @@ using namespace QV4; using namespace QV4::JIT; using namespace QV4::Moth; -ByteCodeHandler::~ByteCodeHandler() -{ -} - -#define DISPATCH_INSTRUCTION(name, nargs, ...) \ - generate_##name( \ - __VA_ARGS__ \ - ); - -#define DECODE_AND_DISPATCH(instr) \ - { \ - INSTR_##instr(MOTH_DECODE_WITH_BASE) \ - Q_UNUSED(base_ptr); \ - startInstruction(Instr::Type::instr); \ - _offset = code - start; \ - INSTR_##instr(DISPATCH) \ - endInstruction(Instr::Type::instr); \ - continue; \ - } - -void ByteCodeHandler::decode(const char *code, uint len) -{ - MOTH_JUMP_TABLE; - - const char *start = code; - const char *end = code + len; - while (code < end) { - MOTH_DISPATCH() - - FOR_EACH_MOTH_INSTR(DECODE_AND_DISPATCH) - } -} - -#undef DECODE_AND_DISPATCH -#undef DISPATCH_INSTRUCTION - BaselineJIT::BaselineJIT(Function *function) : function(function) , as(new Assembler(function->compilationUnit->constants)) @@ -95,10 +61,12 @@ BaselineJIT::~BaselineJIT() void BaselineJIT::generate() { // qDebug()<<"jitting" << function->name()->toQString(); - collectLabelsInBytecode(); + const char *code = function->codeData; + uint len = function->compiledFunction->codeSize; + labels = collectLabelsInBytecode(code, len); as->generatePrologue(); - decode(reinterpret_cast<const char *>(function->codeData), function->compiledFunction->codeSize); + decode(code, len); as->generateEpilogue(); as->link(function); @@ -168,8 +136,8 @@ void BaselineJIT::generate_StoreReg(int reg) void BaselineJIT::generate_MoveReg(int srcReg, int destReg) { - as->loadReg(srcReg); - as->storeReg(destReg); + // Don't clobber the accumulator. + as->moveReg(srcReg, destReg); } void BaselineJIT::generate_LoadLocal(int index) @@ -226,19 +194,13 @@ void BaselineJIT::generate_LoadName(int name) as->checkException(); } -static ReturnedValue loadGlobalLookupHelper(ExecutionEngine *engine, QV4::Function *f, int index) -{ - QV4::Lookup *l = f->compilationUnit->runtimeLookups + index; - return l->globalGetter(l, engine); -} - void BaselineJIT::generate_LoadGlobalLookup(int index) { as->prepareCallWithArgCount(3); as->passInt32AsArg(index, 2); as->passFunctionAsArg(1); as->passEngineAsArg(0); - JIT_GENERATE_RUNTIME_CALL(loadGlobalLookupHelper, Assembler::ResultInAccumulator); + JIT_GENERATE_RUNTIME_CALL(Helpers::loadGlobalLookup, Assembler::ResultInAccumulator); as->checkException(); } @@ -266,18 +228,7 @@ void BaselineJIT::generate_StoreNameStrict(int name) as->checkException(); } -void BaselineJIT::generate_LoadElement(int base, int index) -{ - STORE_IP(); - as->prepareCallWithArgCount(3); - as->passRegAsArg(index, 2); - as->passRegAsArg(base, 1); - as->passEngineAsArg(0); - JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, Assembler::ResultInAccumulator); - as->checkException(); -} - -void BaselineJIT::generate_LoadElementA(int base) +void BaselineJIT::generate_LoadElement(int base) { STORE_IP(); STORE_ACC(); @@ -289,13 +240,6 @@ void BaselineJIT::generate_LoadElementA(int base) as->checkException(); } -static void storeElementHelper(QV4::Function *f, const Value &base, const Value &index, const Value &value) -{ - auto engine = f->internalClass->engine; - if (!Runtime::method_storeElement(engine, base, index, value) && f->isStrict()) - engine->throwTypeError(); -} - void BaselineJIT::generate_StoreElement(int base, int index) { STORE_IP(); @@ -304,22 +248,12 @@ void BaselineJIT::generate_StoreElement(int base, int index) as->passAccumulatorAsArg(3); as->passRegAsArg(index, 2); as->passRegAsArg(base, 1); - as->passFunctionAsArg(0); - JIT_GENERATE_RUNTIME_CALL(storeElementHelper, Assembler::IgnoreResult); - as->checkException(); -} - -void BaselineJIT::generate_LoadProperty(int name, int base) -{ - STORE_IP(); - as->prepareCallWithArgCount(3); - as->passInt32AsArg(name, 2); - as->passRegAsArg(base, 1); as->passEngineAsArg(0); - JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, Assembler::ResultInAccumulator); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeElement, Assembler::IgnoreResult); as->checkException(); } -void BaselineJIT::generate_LoadPropertyA(int name) + +void BaselineJIT::generate_LoadProperty(int name) { STORE_IP(); STORE_ACC(); @@ -331,78 +265,69 @@ void BaselineJIT::generate_LoadPropertyA(int name) as->checkException(); } -static ReturnedValue getLookupHelper(ExecutionEngine *engine, QV4::Function *f, int index, const QV4::Value &base) -{ - QV4::Lookup *l = f->compilationUnit->runtimeLookups + index; - return l->getter(l, engine, base); -} - -void BaselineJIT::generate_GetLookup(int index, int base) +void BaselineJIT::generate_GetLookup(int index) { STORE_IP(); + STORE_ACC(); as->prepareCallWithArgCount(4); - as->passRegAsArg(base, 3); + as->passAccumulatorAsArg(3); as->passInt32AsArg(index, 2); as->passFunctionAsArg(1); as->passEngineAsArg(0); - JIT_GENERATE_RUNTIME_CALL(getLookupHelper, Assembler::ResultInAccumulator); + JIT_GENERATE_RUNTIME_CALL(Helpers::getLookup, Assembler::ResultInAccumulator); as->checkException(); } -void BaselineJIT::generate_GetLookupA(int index) +void BaselineJIT::generate_StoreProperty(int name, int base) { STORE_IP(); STORE_ACC(); as->prepareCallWithArgCount(4); as->passAccumulatorAsArg(3); - as->passInt32AsArg(index, 2); - as->passFunctionAsArg(1); + as->passInt32AsArg(name, 2); + as->passRegAsArg(base, 1); as->passEngineAsArg(0); - JIT_GENERATE_RUNTIME_CALL(getLookupHelper, Assembler::ResultInAccumulator); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeProperty, Assembler::IgnoreResult); as->checkException(); } -static void storePropertyHelper(QV4::Function *f, const Value &base, int name, const Value &value) -{ - auto engine = f->internalClass->engine; - if (!Runtime::method_storeProperty(engine, base, name, value) && f->isStrict()) - engine->throwTypeError(); -} - -void BaselineJIT::generate_StoreProperty(int name, int base) +void BaselineJIT::generate_SetLookup(int index, int base) { STORE_IP(); STORE_ACC(); as->prepareCallWithArgCount(4); as->passAccumulatorAsArg(3); - as->passInt32AsArg(name, 2); - as->passRegAsArg(base, 1); + as->passRegAsArg(base, 2); + as->passInt32AsArg(index, 1); as->passFunctionAsArg(0); - JIT_GENERATE_RUNTIME_CALL(storePropertyHelper, Assembler::IgnoreResult); + JIT_GENERATE_RUNTIME_CALL(Helpers::setLookup, Assembler::ResultInAccumulator); as->checkException(); } -static void setLookupHelper(QV4::Function *f, int index, QV4::Value &base, const QV4::Value &value) +void BaselineJIT::generate_LoadSuperProperty(int property) { - ExecutionEngine *engine = f->internalClass->engine; - QV4::Lookup *l = f->compilationUnit->runtimeLookups + index; - if (!l->setter(l, engine, base, value) && f->isStrict()) - engine->throwTypeError(); + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(2); + as->passRegAsArg(property, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadSuperProperty, Assembler::ResultInAccumulator); + as->checkException(); } -void BaselineJIT::generate_SetLookup(int index, int base) +void BaselineJIT::generate_StoreSuperProperty(int property) { STORE_IP(); STORE_ACC(); - as->prepareCallWithArgCount(4); - as->passAccumulatorAsArg(3); - as->passRegAsArg(base, 2); - as->passInt32AsArg(index, 1); + as->prepareCallWithArgCount(3); + as->passAccumulatorAsArg(2); + as->passRegAsArg(property, 1); as->passFunctionAsArg(0); - JIT_GENERATE_RUNTIME_CALL(setLookupHelper, Assembler::ResultInAccumulator); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeSuperProperty, Assembler::IgnoreResult); as->checkException(); } + void BaselineJIT::generate_StoreScopeObjectProperty(int base, int propertyIndex) { STORE_ACC(); @@ -462,6 +387,18 @@ void BaselineJIT::generate_LoadIdObject(int index, int base) as->checkException(); } +void BaselineJIT::generate_Yield() +{ + // ##### + Q_UNREACHABLE(); +} + +void BaselineJIT::generate_Resume(int) +{ + // ##### + Q_UNREACHABLE(); +} + void BaselineJIT::generate_CallValue(int name, int argc, int argv) { STORE_IP(); @@ -574,14 +511,68 @@ void BaselineJIT::generate_CallContextObjectProperty(int propIdx, int base, int as->checkException(); } -void BaselineJIT::generate_SetExceptionHandler(int offset) + +void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(5); + as->passInt32AsArg(argc, 4); + as->passRegAsArg(argv, 3); + as->passRegAsArg(thisObject, 2); + as->passRegAsArg(func, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callWithSpread, Assembler::ResultInAccumulator); + as->checkException(); +} + + +void BaselineJIT::generate_Construct(int func, int argc, int argv) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(5); + as->passInt32AsArg(argc, 4); + as->passRegAsArg(argv, 3); + as->passAccumulatorAsArg(2); + as->passRegAsArg(func, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_construct, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_ConstructWithSpread(int func, int argc, int argv) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(5); + as->passInt32AsArg(argc, 4); + as->passRegAsArg(argv, 3); + as->passAccumulatorAsArg(2); + as->passRegAsArg(func, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_constructWithSpread, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_SetUnwindHandler(int offset) { if (offset) - as->setExceptionHandler(instructionOffset() + offset); + as->setUnwindHandler(instructionOffset() + offset); else - as->clearExceptionHandler(); + as->clearUnwindHandler(); } +void BaselineJIT::generate_UnwindDispatch() +{ + as->unwindDispatch(); +} + +void BaselineJIT::generate_UnwindToLabel(int level, int offset) +{ + as->unwindToLabel(level, instructionOffset() + offset); +} + + void BaselineJIT::generate_ThrowException() { STORE_IP(); @@ -604,118 +595,119 @@ void BaselineJIT::generate_CreateCallContext() as->storeHeapObject(CallData::Context); } -void BaselineJIT::generate_PushCatchContext(int name, int reg) { as->pushCatchContext(name, reg); } +void BaselineJIT::generate_PushCatchContext(int index, int name) { as->pushCatchContext(index, name); } -static void pushWithContextHelper(ExecutionEngine *engine, QV4::Value *stack, int reg) -{ - QV4::Value &accumulator = stack[CallData::Accumulator]; - accumulator = accumulator.toObject(engine); - if (engine->hasException) - return; - stack[reg] = stack[CallData::Context]; - ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); - stack[CallData::Context] = Runtime::method_createWithContext(c, accumulator); -} - -void BaselineJIT::generate_PushWithContext(int reg) +void BaselineJIT::generate_PushWithContext() { STORE_IP(); as->saveAccumulatorInFrame(); - as->prepareCallWithArgCount(3); - as->passInt32AsArg(reg, 2); + as->prepareCallWithArgCount(2); as->passRegAsArg(0, 1); as->passEngineAsArg(0); - JIT_GENERATE_RUNTIME_CALL(pushWithContextHelper, Assembler::IgnoreResult); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_createWithContext, Assembler::IgnoreResult); // keeps result in return value register as->checkException(); + as->storeHeapObject(CallData::Context); } -void BaselineJIT::generate_PopContext(int reg) { as->popContext(reg); } - -void BaselineJIT::generate_ForeachIteratorObject() +void BaselineJIT::generate_PushBlockContext(int index) { as->saveAccumulatorInFrame(); as->prepareCallWithArgCount(2); - as->passAccumulatorAsArg(1); - as->passEngineAsArg(0); - JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachIterator, Assembler::ResultInAccumulator); - as->checkException(); + as->passInt32AsArg(index, 1); + as->passRegAsArg(0, 0); + JIT_GENERATE_RUNTIME_CALL(Helpers::pushBlockContext, Assembler::IgnoreResult); } -void BaselineJIT::generate_ForeachNextPropertyName() +void BaselineJIT::generate_CloneBlockContext() { as->saveAccumulatorInFrame(); as->prepareCallWithArgCount(1); - as->passAccumulatorAsArg(0); - JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachNextPropertyName, - Assembler::ResultInAccumulator); + as->passRegAsArg(CallData::Context, 0); + JIT_GENERATE_RUNTIME_CALL(Helpers::cloneBlockContext, Assembler::IgnoreResult); +} + +void BaselineJIT::generate_PushScriptContext(int index) +{ + as->saveAccumulatorInFrame(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(index, 2); + as->passEngineAsArg(1); + as->passRegAsArg(0, 0); + JIT_GENERATE_RUNTIME_CALL(Helpers::pushScriptContext, Assembler::IgnoreResult); +} + +void BaselineJIT::generate_PopScriptContext() +{ + as->saveAccumulatorInFrame(); + as->prepareCallWithArgCount(2); + as->passEngineAsArg(1); + as->passRegAsArg(0, 0); + JIT_GENERATE_RUNTIME_CALL(Helpers::popScriptContext, Assembler::IgnoreResult); +} + +void BaselineJIT::generate_PopContext() { as->popContext(); } + +void BaselineJIT::generate_GetIterator(int iterator) +{ + as->saveAccumulatorInFrame(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(iterator, 2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_getIterator, Assembler::ResultInAccumulator); as->checkException(); } -static ReturnedValue deleteMemberHelper(QV4::Function *function, const QV4::Value &base, int member) +void BaselineJIT::generate_IteratorNext(int value) { - auto engine = function->internalClass->engine; - if (!Runtime::method_deleteMember(engine, base, member)) { - if (function->isStrict()) - engine->throwTypeError(); - return Encode(false); - } else { - return Encode(true); - } + as->saveAccumulatorInFrame(); + as->prepareCallWithArgCount(3); + as->passRegAsArg(value, 2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorNext, Assembler::ResultInAccumulator); + as->checkException(); } -void BaselineJIT::generate_DeleteMember(int member, int base) +void BaselineJIT::generate_IteratorClose(int done) { - STORE_IP(); + as->saveAccumulatorInFrame(); as->prepareCallWithArgCount(3); - as->passInt32AsArg(member, 2); - as->passRegAsArg(base, 1); - as->passFunctionAsArg(0); - JIT_GENERATE_RUNTIME_CALL(deleteMemberHelper, Assembler::ResultInAccumulator); + as->passRegAsArg(done, 2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorClose, Assembler::ResultInAccumulator); as->checkException(); } -static ReturnedValue deleteSubscriptHelper(QV4::Function *function, const QV4::Value &base, const QV4::Value &index) +void BaselineJIT::generate_DestructureRestElement() { - auto engine = function->internalClass->engine; - if (!Runtime::method_deleteElement(engine, base, index)) { - if (function->isStrict()) - engine->throwTypeError(); - return Encode(false); - } else { - return Encode(true); - } + as->saveAccumulatorInFrame(); + as->prepareCallWithArgCount(2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_destructureRestElement, Assembler::ResultInAccumulator); + as->checkException(); } -void BaselineJIT::generate_DeleteSubscript(int base, int index) +void BaselineJIT::generate_DeleteProperty(int base, int index) { STORE_IP(); as->prepareCallWithArgCount(3); as->passRegAsArg(index, 2); as->passRegAsArg(base, 1); as->passFunctionAsArg(0); - JIT_GENERATE_RUNTIME_CALL(deleteSubscriptHelper, Assembler::ResultInAccumulator); + JIT_GENERATE_RUNTIME_CALL(Helpers::deleteProperty, Assembler::ResultInAccumulator); as->checkException(); } -static ReturnedValue deleteNameHelper(QV4::Function *function, int name) -{ - auto engine = function->internalClass->engine; - if (!Runtime::method_deleteName(engine, name)) { - if (function->isStrict()) - engine->throwTypeError(); - return Encode(false); - } else { - return Encode(true); - } -} - void BaselineJIT::generate_DeleteName(int name) { STORE_IP(); as->prepareCallWithArgCount(2); as->passInt32AsArg(name, 1); as->passFunctionAsArg(0); - JIT_GENERATE_RUNTIME_CALL(deleteNameHelper, Assembler::ResultInAccumulator); + JIT_GENERATE_RUNTIME_CALL(Helpers::deleteName, Assembler::ResultInAccumulator); as->checkException(); } @@ -754,17 +746,26 @@ void BaselineJIT::generate_DefineArray(int argc, int args) JIT_GENERATE_RUNTIME_CALL(Runtime::method_arrayLiteral, Assembler::ResultInAccumulator); } -void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int arrayValueCount, - int arrayGetterSetterCountAndFlags, int args) +void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int argc, int args) { - as->prepareCallWithArgCount(5); - as->passInt32AsArg(arrayGetterSetterCountAndFlags, 4); - as->passInt32AsArg(arrayValueCount, 3); - as->passInt32AsArg(internalClassId, 2); - as->passRegAsArg(args, 1); + as->prepareCallWithArgCount(4); + as->passRegAsArg(args, 3); + as->passInt32AsArg(argc, 2); + as->passInt32AsArg(internalClassId, 1); as->passEngineAsArg(0); JIT_GENERATE_RUNTIME_CALL(Runtime::method_objectLiteral, Assembler::ResultInAccumulator); } + +void BaselineJIT::generate_CreateClass(int classIndex, int heritage, int computedNames) +{ + as->prepareCallWithArgCount(4); + as->passRegAsArg(computedNames, 3); + as->passRegAsArg(heritage, 2); + as->passInt32AsArg(classIndex, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_createClass, Assembler::ResultInAccumulator); +} + void BaselineJIT::generate_CreateMappedArgumentsObject() { as->prepareCallWithArgCount(1); @@ -781,15 +782,12 @@ void BaselineJIT::generate_CreateUnmappedArgumentsObject() Assembler::ResultInAccumulator); } -static void convertThisToObjectHelper(ExecutionEngine *engine, Value *t) +void BaselineJIT::generate_CreateRestParameter(int argIndex) { - if (!t->isObject()) { - if (t->isNullOrUndefined()) { - *t = engine->globalObject->asReturnedValue(); - } else { - *t = t->toObject(engine)->asReturnedValue(); - } - } + as->prepareCallWithArgCount(2); + as->passInt32AsArg(argIndex, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_createRestParameter, Assembler::ResultInAccumulator); } void BaselineJIT::generate_ConvertThisToObject() @@ -797,25 +795,35 @@ void BaselineJIT::generate_ConvertThisToObject() as->prepareCallWithArgCount(2); as->passRegAsArg(CallData::This, 1); as->passEngineAsArg(0); - JIT_GENERATE_RUNTIME_CALL(convertThisToObjectHelper, Assembler::IgnoreResult); + JIT_GENERATE_RUNTIME_CALL(Helpers::convertThisToObject, Assembler::IgnoreResult); as->checkException(); } -void BaselineJIT::generate_Construct(int func, int argc, int argv) +void BaselineJIT::generate_LoadSuperConstructor() { - STORE_IP(); - as->prepareCallWithArgCount(4); - as->passInt32AsArg(argc, 3); - as->passRegAsArg(argv, 2); - as->passRegAsArg(func, 1); + as->prepareCallWithArgCount(2); + as->passRegAsArg(CallData::Function, 1); as->passEngineAsArg(0); - JIT_GENERATE_RUNTIME_CALL(Runtime::method_construct, Assembler::ResultInAccumulator); + JIT_GENERATE_RUNTIME_CALL(Helpers::loadSuperConstructor, Assembler::ResultInAccumulator); as->checkException(); } +void BaselineJIT::generate_ToObject() +{ + STORE_ACC(); + as->prepareCallWithArgCount(2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Helpers::toObject, Assembler::ResultInAccumulator); + as->checkException(); + +} + void BaselineJIT::generate_Jump(int offset) { as->jump(instructionOffset() + offset); } void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(instructionOffset() + offset); } void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(instructionOffset() + offset); } +void BaselineJIT::generate_JumpNoException(int offset) { as->jumpNoException(instructionOffset() + offset); } +void BaselineJIT::generate_JumpNotUndefined(int offset) { as->jumpNotUndefined(instructionOffset() + offset); } void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); } void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); } @@ -852,16 +860,6 @@ void BaselineJIT::generate_CmpInstanceOf(int lhs) as->checkException(); } -void BaselineJIT::generate_JumpStrictEqualStackSlotInt(int lhs, int rhs, int offset) -{ - as->jumpStrictEqualStackSlotInt(lhs, rhs, instructionOffset() + offset); -} - -void BaselineJIT::generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset) -{ - as->jumpStrictNotEqualStackSlotInt(lhs, rhs, instructionOffset() + offset); -} - void BaselineJIT::generate_UNot() { as->unot(); } void BaselineJIT::generate_UPlus() { as->toNumber(); } void BaselineJIT::generate_UMinus() { as->uminus(); } @@ -884,6 +882,15 @@ void BaselineJIT::generate_UShrConst(int rhs) { as->ushrConst(rhs); } void BaselineJIT::generate_ShrConst(int rhs) { as->shrConst(rhs); } void BaselineJIT::generate_ShlConst(int rhs) { as->shlConst(rhs); } +void BaselineJIT::generate_Exp(int lhs) { + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(2); + as->passAccumulatorAsArg(1); + as->passRegAsArg(lhs, 0); + JIT_GENERATE_RUNTIME_CALL(Helpers::exp, Assembler::ResultInAccumulator); + as->checkException(); +} void BaselineJIT::generate_Mul(int lhs) { as->mul(lhs); } void BaselineJIT::generate_Div(int lhs) { as->div(lhs); } void BaselineJIT::generate_Mod(int lhs) { as->mod(lhs); } @@ -928,401 +935,4 @@ void BaselineJIT::endInstruction(Instr::Type instr) Q_UNUSED(instr); } -#define MOTH_UNUSED_ARGS0() -#define MOTH_UNUSED_ARGS1(arg) \ - Q_UNUSED(arg); -#define MOTH_UNUSED_ARGS2(arg1, arg2) \ - Q_UNUSED(arg1); \ - Q_UNUSED(arg2); -#define MOTH_UNUSED_ARGS3(arg1, arg2, arg3) \ - Q_UNUSED(arg1); \ - Q_UNUSED(arg2); \ - Q_UNUSED(arg3); -#define MOTH_UNUSED_ARGS4(arg1, arg2, arg3, arg4) \ - Q_UNUSED(arg1); \ - Q_UNUSED(arg2); \ - Q_UNUSED(arg3); \ - Q_UNUSED(arg4); - -#define MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, ...) \ - MOTH_EXPAND_FOR_MSVC(MOTH_UNUSED_ARGS##nargs(__VA_ARGS__)) - -#define MOTH_MARK_ARGS_UNUSED_INSTRUCTION(name, nargs, ...) \ - MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, __VA_ARGS__) - -#define MOTH_BEGIN_INSTR(instr) \ - { \ - INSTR_##instr(MOTH_DECODE_WITH_BASE) \ - INSTR_##instr(MOTH_MARK_ARGS_UNUSED) \ - Q_UNUSED(base_ptr); - -#define MOTH_END_INSTR(instr) \ - continue; \ - } - -void BaselineJIT::collectLabelsInBytecode() -{ - MOTH_JUMP_TABLE; - - const auto addLabel = [&](int offset) { - Q_ASSERT(offset >= 0 && offset < static_cast<int>(function->compiledFunction->codeSize)); - labels.push_back(offset); - }; - - const char *code = reinterpret_cast<const char *>(function->codeData); - const char *start = code; - const char *end = code + function->compiledFunction->codeSize; - while (code < end) { - MOTH_DISPATCH() - Q_UNREACHABLE(); - - MOTH_BEGIN_INSTR(LoadReg) - MOTH_END_INSTR(LoadReg) - - MOTH_BEGIN_INSTR(StoreReg) - MOTH_END_INSTR(StoreReg) - - MOTH_BEGIN_INSTR(MoveReg) - MOTH_END_INSTR(MoveReg) - - MOTH_BEGIN_INSTR(LoadConst) - MOTH_END_INSTR(LoadConst) - - MOTH_BEGIN_INSTR(LoadNull) - MOTH_END_INSTR(LoadNull) - - MOTH_BEGIN_INSTR(LoadZero) - MOTH_END_INSTR(LoadZero) - - MOTH_BEGIN_INSTR(LoadTrue) - MOTH_END_INSTR(LoadTrue) - - MOTH_BEGIN_INSTR(LoadFalse) - MOTH_END_INSTR(LoadFalse) - - MOTH_BEGIN_INSTR(LoadUndefined) - MOTH_END_INSTR(LoadUndefined) - - MOTH_BEGIN_INSTR(LoadInt) - MOTH_END_INSTR(LoadInt) - - MOTH_BEGIN_INSTR(MoveConst) - MOTH_END_INSTR(MoveConst) - - MOTH_BEGIN_INSTR(LoadLocal) - MOTH_END_INSTR(LoadLocal) - - MOTH_BEGIN_INSTR(StoreLocal) - MOTH_END_INSTR(StoreLocal) - - MOTH_BEGIN_INSTR(LoadScopedLocal) - MOTH_END_INSTR(LoadScopedLocal) - - MOTH_BEGIN_INSTR(StoreScopedLocal) - MOTH_END_INSTR(StoreScopedLocal) - - MOTH_BEGIN_INSTR(LoadRuntimeString) - MOTH_END_INSTR(LoadRuntimeString) - - MOTH_BEGIN_INSTR(MoveRegExp) - MOTH_END_INSTR(MoveRegExp) - - MOTH_BEGIN_INSTR(LoadClosure) - MOTH_END_INSTR(LoadClosure) - - MOTH_BEGIN_INSTR(LoadName) - MOTH_END_INSTR(LoadName) - - MOTH_BEGIN_INSTR(LoadGlobalLookup) - MOTH_END_INSTR(LoadGlobalLookup) - - MOTH_BEGIN_INSTR(StoreNameSloppy) - MOTH_END_INSTR(StoreNameSloppy) - - MOTH_BEGIN_INSTR(StoreNameStrict) - MOTH_END_INSTR(StoreNameStrict) - - MOTH_BEGIN_INSTR(LoadElement) - MOTH_END_INSTR(LoadElement) - - MOTH_BEGIN_INSTR(LoadElementA) - MOTH_END_INSTR(LoadElement) - - MOTH_BEGIN_INSTR(StoreElement) - MOTH_END_INSTR(StoreElement) - - MOTH_BEGIN_INSTR(LoadProperty) - MOTH_END_INSTR(LoadProperty) - - MOTH_BEGIN_INSTR(LoadPropertyA) - MOTH_END_INSTR(LoadElementA) - - MOTH_BEGIN_INSTR(GetLookup) - MOTH_END_INSTR(GetLookup) - - MOTH_BEGIN_INSTR(GetLookupA) - MOTH_END_INSTR(GetLookupA) - - MOTH_BEGIN_INSTR(StoreProperty) - MOTH_END_INSTR(StoreProperty) - - MOTH_BEGIN_INSTR(SetLookup) - MOTH_END_INSTR(SetLookup) - - MOTH_BEGIN_INSTR(StoreScopeObjectProperty) - MOTH_END_INSTR(StoreScopeObjectProperty) - - MOTH_BEGIN_INSTR(LoadScopeObjectProperty) - MOTH_END_INSTR(LoadScopeObjectProperty) - - MOTH_BEGIN_INSTR(StoreContextObjectProperty) - MOTH_END_INSTR(StoreContextObjectProperty) - - MOTH_BEGIN_INSTR(LoadContextObjectProperty) - MOTH_END_INSTR(LoadContextObjectProperty) - - MOTH_BEGIN_INSTR(LoadIdObject) - MOTH_END_INSTR(LoadIdObject) - - MOTH_BEGIN_INSTR(CallValue) - MOTH_END_INSTR(CallValue) - - MOTH_BEGIN_INSTR(CallProperty) - MOTH_END_INSTR(CallProperty) - - MOTH_BEGIN_INSTR(CallPropertyLookup) - MOTH_END_INSTR(CallPropertyLookup) - - MOTH_BEGIN_INSTR(CallElement) - MOTH_END_INSTR(CallElement) - - MOTH_BEGIN_INSTR(CallName) - MOTH_END_INSTR(CallName) - - MOTH_BEGIN_INSTR(CallPossiblyDirectEval) - MOTH_END_INSTR(CallPossiblyDirectEval) - - MOTH_BEGIN_INSTR(CallGlobalLookup) - MOTH_END_INSTR(CallGlobalLookup) - - MOTH_BEGIN_INSTR(CallScopeObjectProperty) - MOTH_END_INSTR(CallScopeObjectProperty) - - MOTH_BEGIN_INSTR(CallContextObjectProperty) - MOTH_END_INSTR(CallContextObjectProperty) - - MOTH_BEGIN_INSTR(SetExceptionHandler) - addLabel(code - start + offset); - MOTH_END_INSTR(SetExceptionHandler) - - MOTH_BEGIN_INSTR(ThrowException) - MOTH_END_INSTR(ThrowException) - - MOTH_BEGIN_INSTR(GetException) - MOTH_END_INSTR(HasException) - - MOTH_BEGIN_INSTR(SetException) - MOTH_END_INSTR(SetExceptionFlag) - - MOTH_BEGIN_INSTR(CreateCallContext) - MOTH_END_INSTR(CreateCallContext) - - MOTH_BEGIN_INSTR(PushCatchContext) - MOTH_END_INSTR(PushCatchContext) - - MOTH_BEGIN_INSTR(PushWithContext) - MOTH_END_INSTR(PushWithContext) - - MOTH_BEGIN_INSTR(PopContext) - MOTH_END_INSTR(PopContext) - - MOTH_BEGIN_INSTR(ForeachIteratorObject) - MOTH_END_INSTR(ForeachIteratorObject) - - MOTH_BEGIN_INSTR(ForeachNextPropertyName) - MOTH_END_INSTR(ForeachNextPropertyName) - - MOTH_BEGIN_INSTR(DeleteMember) - MOTH_END_INSTR(DeleteMember) - - MOTH_BEGIN_INSTR(DeleteSubscript) - MOTH_END_INSTR(DeleteSubscript) - - MOTH_BEGIN_INSTR(DeleteName) - MOTH_END_INSTR(DeleteName) - - MOTH_BEGIN_INSTR(TypeofName) - MOTH_END_INSTR(TypeofName) - - MOTH_BEGIN_INSTR(TypeofValue) - MOTH_END_INSTR(TypeofValue) - - MOTH_BEGIN_INSTR(DeclareVar) - MOTH_END_INSTR(DeclareVar) - - MOTH_BEGIN_INSTR(DefineArray) - MOTH_END_INSTR(DefineArray) - - MOTH_BEGIN_INSTR(DefineObjectLiteral) - MOTH_END_INSTR(DefineObjectLiteral) - - MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) - MOTH_END_INSTR(CreateMappedArgumentsObject) - - MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject) - MOTH_END_INSTR(CreateUnmappedArgumentsObject) - - MOTH_BEGIN_INSTR(ConvertThisToObject) - MOTH_END_INSTR(ConvertThisToObject) - - MOTH_BEGIN_INSTR(Construct) - MOTH_END_INSTR(Construct) - - MOTH_BEGIN_INSTR(Jump) - addLabel(code - start + offset); - MOTH_END_INSTR(Jump) - - MOTH_BEGIN_INSTR(JumpTrue) - addLabel(code - start + offset); - MOTH_END_INSTR(JumpTrue) - - MOTH_BEGIN_INSTR(JumpFalse) - addLabel(code - start + offset); - MOTH_END_INSTR(JumpFalse) - - MOTH_BEGIN_INSTR(CmpEqNull) - MOTH_END_INSTR(CmpEqNull) - - MOTH_BEGIN_INSTR(CmpNeNull) - MOTH_END_INSTR(CmpNeNull) - - MOTH_BEGIN_INSTR(CmpEqInt) - MOTH_END_INSTR(CmpEq) - - MOTH_BEGIN_INSTR(CmpNeInt) - MOTH_END_INSTR(CmpNeInt) - - MOTH_BEGIN_INSTR(CmpEq) - MOTH_END_INSTR(CmpEq) - - MOTH_BEGIN_INSTR(CmpNe) - MOTH_END_INSTR(CmpNe) - - MOTH_BEGIN_INSTR(CmpGt) - MOTH_END_INSTR(CmpGt) - - MOTH_BEGIN_INSTR(CmpGe) - MOTH_END_INSTR(CmpGe) - - MOTH_BEGIN_INSTR(CmpLt) - MOTH_END_INSTR(CmpLt) - - MOTH_BEGIN_INSTR(CmpLe) - MOTH_END_INSTR(CmpLe) - - MOTH_BEGIN_INSTR(CmpStrictEqual) - MOTH_END_INSTR(CmpStrictEqual) - - MOTH_BEGIN_INSTR(CmpStrictNotEqual) - MOTH_END_INSTR(CmpStrictNotEqual) - - MOTH_BEGIN_INSTR(CmpIn) - MOTH_END_INSTR(CmpIn) - - MOTH_BEGIN_INSTR(CmpInstanceOf) - MOTH_END_INSTR(CmpInstanceOf) - - MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt) - addLabel(code - start + offset); - MOTH_END_INSTR(JumpStrictEqualStackSlotInt) - - MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt) - addLabel(code - start + offset); - MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) - - MOTH_BEGIN_INSTR(UNot) - MOTH_END_INSTR(UNot) - - MOTH_BEGIN_INSTR(UPlus) - MOTH_END_INSTR(UPlus) - - MOTH_BEGIN_INSTR(UMinus) - MOTH_END_INSTR(UMinus) - - MOTH_BEGIN_INSTR(UCompl) - MOTH_END_INSTR(UCompl) - - MOTH_BEGIN_INSTR(Increment) - MOTH_END_INSTR(PreIncrement) - - MOTH_BEGIN_INSTR(Decrement) - MOTH_END_INSTR(PreDecrement) - - MOTH_BEGIN_INSTR(Add) - MOTH_END_INSTR(Add) - - MOTH_BEGIN_INSTR(BitAnd) - MOTH_END_INSTR(BitAnd) - - MOTH_BEGIN_INSTR(BitOr) - MOTH_END_INSTR(BitOr) - - MOTH_BEGIN_INSTR(BitXor) - MOTH_END_INSTR(BitXor) - - MOTH_BEGIN_INSTR(UShr) - MOTH_END_INSTR(UShr) - - MOTH_BEGIN_INSTR(Shr) - MOTH_END_INSTR(Shr) - - MOTH_BEGIN_INSTR(Shl) - MOTH_END_INSTR(Shl) - - MOTH_BEGIN_INSTR(BitAndConst) - MOTH_END_INSTR(BitAndConst) - - MOTH_BEGIN_INSTR(BitOrConst) - MOTH_END_INSTR(BitOr) - - MOTH_BEGIN_INSTR(BitXorConst) - MOTH_END_INSTR(BitXor) - - MOTH_BEGIN_INSTR(UShrConst) - MOTH_END_INSTR(UShrConst) - - MOTH_BEGIN_INSTR(ShrConst) - MOTH_END_INSTR(ShrConst) - - MOTH_BEGIN_INSTR(ShlConst) - MOTH_END_INSTR(ShlConst) - - MOTH_BEGIN_INSTR(Mul) - MOTH_END_INSTR(Mul) - - MOTH_BEGIN_INSTR(Div) - MOTH_END_INSTR(Div) - - MOTH_BEGIN_INSTR(Mod) - MOTH_END_INSTR(Mod) - - MOTH_BEGIN_INSTR(Sub) - MOTH_END_INSTR(Sub) - - MOTH_BEGIN_INSTR(Ret) - MOTH_END_INSTR(Ret) - - MOTH_BEGIN_INSTR(Debug) - MOTH_END_INSTR(Debug) - - MOTH_BEGIN_INSTR(LoadQmlContext) - MOTH_END_INSTR(LoadQmlContext) - - MOTH_BEGIN_INSTR(LoadQmlImportedScripts) - MOTH_END_INSTR(LoadQmlImportedScripts) - } -} -#undef MOTH_BEGIN_INSTR -#undef MOTH_END_INSTR - #endif // V4_ENABLE_JIT diff --git a/src/qml/jit/qv4jit_p.h b/src/qml/jit/qv4baselinejit_p.h index 5aebf78a8d..d96fd6ea6a 100644 --- a/src/qml/jit/qv4jit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -54,36 +54,10 @@ #include <private/qv4global_p.h> #include <private/qv4function_p.h> #include <private/qv4instr_moth_p.h> +#include <private/qv4bytecodehandler_p.h> //QT_REQUIRE_CONFIG(qml_jit); -#define JIT_DEFINE_ARGS(nargs, ...) \ - MOTH_EXPAND_FOR_MSVC(JIT_DEFINE_ARGS##nargs(__VA_ARGS__)) - -#define JIT_DEFINE_ARGS0() -#define JIT_DEFINE_ARGS1(arg) \ - int arg -#define JIT_DEFINE_ARGS2(arg1, arg2) \ - int arg1, \ - int arg2 -#define JIT_DEFINE_ARGS3(arg1, arg2, arg3) \ - int arg1, \ - int arg2, \ - int arg3 -#define JIT_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \ - int arg1, \ - int arg2, \ - int arg3, \ - int arg4 - -#define JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER_INSTRUCTION(name, nargs, ...) \ - virtual void generate_##name( \ - JIT_DEFINE_ARGS(nargs, __VA_ARGS__) \ - ) = 0; - -#define JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \ - INSTR_##instr(JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER) - QT_BEGIN_NAMESPACE namespace QV4 { @@ -91,31 +65,12 @@ namespace JIT { class Assembler; -class ByteCodeHandler -{ -public: - virtual ~ByteCodeHandler(); - - void decode(const char *code, uint len); - - int instructionOffset() const { return _offset; } - -protected: - FOR_EACH_MOTH_INSTR(JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER) - - virtual void startInstruction(Moth::Instr::Type instr) = 0; - virtual void endInstruction(Moth::Instr::Type instr) = 0; - -private: - int _offset = 0; -}; - #ifdef V4_ENABLE_JIT -class BaselineJIT final: public ByteCodeHandler +class BaselineJIT final: public Moth::ByteCodeHandler { public: BaselineJIT(QV4::Function *); - virtual ~BaselineJIT(); + virtual ~BaselineJIT() Q_DECL_OVERRIDE; void generate(); @@ -143,15 +98,14 @@ public: void generate_LoadGlobalLookup(int index) override; void generate_StoreNameSloppy(int name) override; void generate_StoreNameStrict(int name) override; - void generate_LoadElement(int base, int index) override; - void generate_LoadElementA(int base) override; + void generate_LoadElement(int base) override; void generate_StoreElement(int base, int index) override; - void generate_LoadProperty(int name, int base) override; - void generate_LoadPropertyA(int name) override; - void generate_GetLookup(int index, int base) override; - void generate_GetLookupA(int index) override; + void generate_LoadProperty(int name) override; + void generate_GetLookup(int index) override; void generate_StoreProperty(int name, int base) override; void generate_SetLookup(int index, int base) override; + void generate_LoadSuperProperty(int property) override; + void generate_StoreSuperProperty(int property) override; void generate_StoreScopeObjectProperty(int base, int propertyIndex) override; void generate_StoreContextObjectProperty(int base, @@ -161,6 +115,9 @@ public: void generate_LoadContextObjectProperty(int propertyIndex, int base, int captureRequired) override; void generate_LoadIdObject(int index, int base) override; + void generate_Yield() override; + void generate_Resume(int) override; + void generate_CallValue(int name, int argc, int argv) override; void generate_CallProperty(int name, int base, int argc, int argv) override; void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) override; @@ -170,33 +127,46 @@ public: void generate_CallGlobalLookup(int index, int argc, int argv) override; void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv) override; void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv) override; - void generate_SetExceptionHandler(int offset) override; + void generate_CallWithSpread(int func, int thisObject, int argc, int argv) override; + void generate_Construct(int func, int argc, int argv) override; + void generate_ConstructWithSpread(int func, int argc, int argv) override; + void generate_SetUnwindHandler(int offset) override; + void generate_UnwindDispatch() override; + void generate_UnwindToLabel(int level, int offset) override; void generate_ThrowException() override; void generate_GetException() override; void generate_SetException() override; void generate_CreateCallContext() override; - void generate_PushCatchContext(int name, int reg) override; - void generate_PushWithContext(int reg) override; - void generate_PopContext(int reg) override; - void generate_ForeachIteratorObject() override; - void generate_ForeachNextPropertyName() override; - void generate_DeleteMember(int member, int base) override; - void generate_DeleteSubscript(int base, int index) override; + void generate_PushCatchContext(int index, int name) override; + void generate_PushWithContext() override; + void generate_PushBlockContext(int index) override; + void generate_CloneBlockContext() override; + void generate_PushScriptContext(int index) override; + void generate_PopScriptContext() override; + void generate_PopContext() override; + void generate_GetIterator(int iterator) override; + void generate_IteratorNext(int value) override; + void generate_IteratorClose(int done) override; + void generate_DestructureRestElement() override; + void generate_DeleteProperty(int base, int index) override; void generate_DeleteName(int name) override; void generate_TypeofName(int name) override; void generate_TypeofValue() override; void generate_DeclareVar(int varName, int isDeletable) override; void generate_DefineArray(int argc, int args) override; - void generate_DefineObjectLiteral(int internalClassId, int arrayValueCount, - int arrayGetterSetterCountAndFlags, - int args) override; + void generate_DefineObjectLiteral(int internalClassId, int argc, int args) override; + void generate_CreateClass(int classIndex, int heritage, int computedNames) override; void generate_CreateMappedArgumentsObject() override; void generate_CreateUnmappedArgumentsObject() override; + void generate_CreateRestParameter(int argIndex) override; void generate_ConvertThisToObject() override; - void generate_Construct(int func, int argc, int argv) override; + void generate_LoadSuperConstructor() override; + void generate_ToObject() override; void generate_Jump(int offset) override; void generate_JumpTrue(int offset) override; void generate_JumpFalse(int offset) override; + void generate_JumpNoException(int offset) override; + void generate_JumpNotUndefined(int offset) override; void generate_CmpEqNull() override; void generate_CmpNeNull() override; void generate_CmpEqInt(int lhs) override; @@ -211,10 +181,6 @@ public: void generate_CmpStrictNotEqual(int lhs) override; void generate_CmpIn(int lhs) override; void generate_CmpInstanceOf(int lhs) override; - void generate_JumpStrictEqualStackSlotInt(int lhs, int rhs, - int offset) override; - void generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs, - int offset) override; void generate_UNot() override; void generate_UPlus() override; void generate_UMinus() override; @@ -234,6 +200,7 @@ public: void generate_UShrConst(int rhs) override; void generate_ShrConst(int rhs) override; void generate_ShlConst(int rhs) override; + void generate_Exp(int lhs) override; void generate_Mul(int lhs) override; void generate_Div(int lhs) override; void generate_Mod(int lhs) override; @@ -249,9 +216,6 @@ protected: { return std::find(labels.cbegin(), labels.cend(), instructionOffset()) != labels.cend(); } private: - void collectLabelsInBytecode(); - -private: QV4::Function *function; QScopedPointer<Assembler> as; std::vector<int> labels; diff --git a/src/qml/jit/qv4jithelpers.cpp b/src/qml/jit/qv4jithelpers.cpp new file mode 100644 index 0000000000..23e3095a85 --- /dev/null +++ b/src/qml/jit/qv4jithelpers.cpp @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4jithelpers_p.h" +#include "qv4engine_p.h" +#include "qv4function_p.h" +#include "qv4value_p.h" +#include "qv4object_p.h" +#include "qv4lookup_p.h" +#include <QtCore/private/qnumeric_p.h> + +#ifdef V4_ENABLE_JIT + +QT_BEGIN_NAMESPACE +namespace QV4 { +namespace JIT { +namespace Helpers { + +void convertThisToObject(ExecutionEngine *engine, Value *t) +{ + if (!t->isObject()) { + if (t->isNullOrUndefined()) { + *t = engine->globalObject->asReturnedValue(); + } else { + *t = t->toObject(engine)->asReturnedValue(); + } + } +} + +ReturnedValue loadGlobalLookup(ExecutionEngine *engine, Function *f, int index) +{ + Lookup *l = f->compilationUnit->runtimeLookups + index; + return l->globalGetter(l, engine); +} + +ReturnedValue loadSuperConstructor(ExecutionEngine *engine, const Value *t) +{ + if (!t->isObject()) { + engine->throwTypeError(); + return Encode::undefined(); + } + return static_cast<const Object *>(t)->getPrototypeOf()->asReturnedValue(); +} + +ReturnedValue toObject(ExecutionEngine *engine, const Value &obj) +{ + if (obj.isObject()) + return obj.asReturnedValue(); + + return obj.toObject(engine)->asReturnedValue(); +} + +ReturnedValue exp(const Value &base, const Value &exp) +{ + double b = base.toNumber(); + double e = exp.toNumber(); + if (qt_is_inf(e) && (b == 1 || b == -1)) + return Encode(qt_snan()); + return Encode(pow(b,e)); +} + +ReturnedValue getLookup(ExecutionEngine *engine, Function *f, int index, const Value &base) +{ + Lookup *l = f->compilationUnit->runtimeLookups + index; + return l->getter(l, engine, base); +} + +void setLookup(Function *f, int index, Value &base, const Value &value) +{ + ExecutionEngine *engine = f->internalClass->engine; + QV4::Lookup *l = f->compilationUnit->runtimeLookups + index; + if (!l->setter(l, engine, base, value) && f->isStrict()) + engine->throwTypeError(); +} + +void pushBlockContext(Value *stack, int index) +{ + ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); + stack[CallData::Context] = Runtime::method_createBlockContext(c, index); +} + +void cloneBlockContext(Value *contextSlot) +{ + *contextSlot = Runtime::method_cloneBlockContext(static_cast<QV4::ExecutionContext *>(contextSlot)); +} + +void pushScriptContext(Value *stack, ExecutionEngine *engine, int index) +{ + stack[CallData::Context] = Runtime::method_createScriptContext(engine, index); +} + +void popScriptContext(Value *stack, ExecutionEngine *engine) +{ + stack[CallData::Context] = Runtime::method_popScriptContext(engine); +} + +ReturnedValue deleteProperty(QV4::Function *function, const QV4::Value &base, const QV4::Value &index) +{ + auto engine = function->internalClass->engine; + if (!Runtime::method_deleteProperty(engine, base, index)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +ReturnedValue deleteName(Function *function, int name) +{ + auto engine = function->internalClass->engine; + if (!Runtime::method_deleteName(engine, name)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +} // Helpers namespace +} // JIT namespace +} // QV4 namespace +QT_END_NAMESPACE + +#endif // V4_ENABLE_JIT diff --git a/src/qml/jit/qv4jithelpers_p.h b/src/qml/jit/qv4jithelpers_p.h new file mode 100644 index 0000000000..e0dfdc47d9 --- /dev/null +++ b/src/qml/jit/qv4jithelpers_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TEMPLATE_H +#define TEMPLATE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qv4global_p.h> + +//QT_REQUIRE_CONFIG(qml_jit); + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +#ifdef V4_ENABLE_JIT + +namespace JIT { +namespace Helpers { + +void convertThisToObject(ExecutionEngine *engine, Value *t); +ReturnedValue loadGlobalLookup(ExecutionEngine *engine, Function *f, int index); +ReturnedValue loadSuperConstructor(ExecutionEngine *engine, const Value *t); +ReturnedValue toObject(ExecutionEngine *engine, const Value &obj); +ReturnedValue exp(const Value &base, const Value &exp); +ReturnedValue getLookup(ExecutionEngine *engine, Function *f, int index, const Value &base); +void setLookup(Function *f, int index, Value &base, const Value &value); +void pushBlockContext(Value *stack, int index); +void cloneBlockContext(Value *contextSlot); +void pushScriptContext(Value *stack, ExecutionEngine *engine, int index); +void popScriptContext(Value *stack, ExecutionEngine *engine); +ReturnedValue deleteProperty(QV4::Function *function, const QV4::Value &base, const QV4::Value &index); +ReturnedValue deleteName(Function *function, int name); + +} // Helpers namespace +} // JIT namespace + +#endif // V4_ENABLE_JIT + +} // QV4 namespace + +QT_END_NAMESPACE + +#endif // TEMPLATE_H diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index c483af638b..5fa81ccc2a 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -51,6 +51,7 @@ #include <private/qqmlbuiltinfunctions_p.h> #include <private/qqmldebugconnector_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qv4stackframe_p.h> #include <QtCore/qdatetime.h> #include <QtCore/qmetaobject.h> @@ -304,9 +305,9 @@ QJSEngine::QJSEngine() QJSEngine::QJSEngine(QObject *parent) : QObject(*new QJSEnginePrivate, parent) - , m_v4Engine(new QV4::ExecutionEngine) + , m_v4Engine(new QV4::ExecutionEngine(this)) { - m_v4Engine->v8Engine = new QV8Engine(this, m_v4Engine); + m_v4Engine->v8Engine = new QV8Engine(m_v4Engine); checkForApplicationInstance(); QJSEnginePrivate::addToDebugServer(this); @@ -317,9 +318,9 @@ QJSEngine::QJSEngine(QObject *parent) */ QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent) : QObject(dd, parent) - , m_v4Engine(new QV4::ExecutionEngine) + , m_v4Engine(new QV4::ExecutionEngine(this)) { - m_v4Engine->v8Engine = new QV8Engine(this, m_v4Engine); + m_v4Engine->v8Engine = new QV8Engine(m_v4Engine); checkForApplicationInstance(); } @@ -462,7 +463,7 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in QV4::Scope scope(v4); QV4::ScopedValue result(scope); - QV4::Script script(v4->rootContext(), QV4::Compiler::GlobalCode, program, fileName, lineNumber); + QV4::Script script(v4->rootContext(), QV4::Compiler::ContextType::Global, program, fileName, lineNumber); script.strictMode = false; if (v4->currentStackFrame) script.strictMode = v4->currentStackFrame->v4Function->isStrict(); @@ -738,6 +739,74 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) \sa toScriptValue() */ +/*! + Throws a run-time error (exception) with the given \a message. + + This method is the C++ counterpart of a \c throw() expression in + JavaScript. It enables C++ code to report run-time errors to QJSEngine. + Therefore it should only be called from C++ code that was invoked by a + JavaScript function through QJSEngine. + + When returning from C++, the engine will interrupt the normal flow of + execution and call the the next pre-registered exception handler with + an error object that contains the given \a message. The error object + will point to the location of the top-most context on the JavaScript + caller stack; specifically, it will have properties \c lineNumber, + \c fileName and \c stack. These properties are described in + \l{Script Exceptions}. + + In the following example a C++ method in \e FileAccess.cpp throws an error + in \e qmlFile.qml at the position where \c readFileAsText() is called: + + \code + // qmlFile.qml + function someFunction() { + ... + var text = FileAccess.readFileAsText("/path/to/file.txt"); + } + \endcode + + \code + // FileAccess.cpp + // Assuming that FileAccess is a QObject-derived class that has been + // registered as a singleton type and provides an invokable method + // readFileAsText() + + QJSValue FileAccess::readFileAsText(const QString & filePath) { + QFile file(filePath); + + if (!file.open(QIODevice::ReadOnly)) { + jsEngine->throwError(file.errorString()); + return QString(); + } + + ... + return content; + } + \endcode + + It is also possible to catch the thrown error in JavaScript: + \code + // qmlFile.qml + function someFunction() { + ... + var text; + try { + text = FileAccess.readFileAsText("/path/to/file.txt"); + } catch (error) { + console.warn("In " + error.fileName + ":" + "error.lineNumber" + + ": " + error.message); + } + } + \endcode + + \since Qt 5.12 + \sa {Script Exceptions} +*/ +void QJSEngine::throwError(const QString &message) +{ + m_v4Engine->throwError(message); +} QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e) { diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h index 3ba2b52e89..36a3e475f2 100644 --- a/src/qml/jsapi/qjsengine.h +++ b/src/qml/jsapi/qjsengine.h @@ -111,6 +111,8 @@ public: QV4::ExecutionEngine *handle() const { return m_v4Engine; } + void throwError(const QString &message); + private: QJSValue create(int type, const void *ptr); diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 348ddb25d9..bf8000bdd8 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -852,7 +852,7 @@ QJSValue QJSValue::prototype() const ScopedObject o(scope, QJSValuePrivate::getValue(this)->as<QV4::Object>()); if (!o) return QJSValue(); - ScopedObject p(scope, o->prototype()); + ScopedObject p(scope, o->getPrototypeOf()); if (!p) return QJSValue(NullValue); return QJSValue(o->internalClass()->engine, p.asReturnedValue()); @@ -884,7 +884,7 @@ void QJSValue::setPrototype(const QJSValue& prototype) if (!val) return; if (val->isNull()) { - o->setPrototype(nullptr); + o->setPrototypeOf(nullptr); return; } @@ -895,7 +895,7 @@ void QJSValue::setPrototype(const QJSValue& prototype) qWarning("QJSValue::setPrototype() failed: cannot set a prototype created in a different engine"); return; } - if (!o->setPrototype(p)) + if (!o->setPrototypeOf(p)) qWarning("QJSValue::setPrototype() failed: cyclic prototype value"); } @@ -1058,12 +1058,7 @@ QJSValue QJSValue::property(const QString& name) const return QJSValue(); ScopedString s(scope, engine->newString(name)); - uint idx = s->asArrayIndex(); - if (idx < UINT_MAX) - return property(idx); - - s->makeIdentifier(); - QV4::ScopedValue result(scope, o->get(s)); + QV4::ScopedValue result(scope, o->get(s->toPropertyKey())); if (engine->hasException) result = engine->catchException(); @@ -1110,7 +1105,7 @@ QJSValue QJSValue::property(quint32 arrayIndex) const if (!o) return QJSValue(); - QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(engine->id_uintMax()) : o->getIndexed(arrayIndex)); + QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(engine->id_uintMax()) : o->get(arrayIndex)); if (engine->hasException) engine->catchException(); return QJSValue(engine, result->asReturnedValue()); @@ -1148,15 +1143,8 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value) } ScopedString s(scope, engine->newString(name)); - uint idx = s->asArrayIndex(); - if (idx < UINT_MAX) { - setProperty(idx, value); - return; - } - - s->makeIdentifier(); QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value)); - o->put(s, v); + o->put(s->toPropertyKey(), v); if (engine->hasException) engine->catchException(); } @@ -1209,10 +1197,8 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) } QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value)); - if (arrayIndex != UINT_MAX) - o->putIndexed(arrayIndex, v); - else - o->put(engine->id_uintMax(), v); + PropertyKey id = arrayIndex != UINT_MAX ? PropertyKey::fromArrayIndex(arrayIndex) : engine->id_uintMax()->propertyKey(); + o->put(id, v); if (engine->hasException) engine->catchException(); } @@ -1249,7 +1235,7 @@ bool QJSValue::deleteProperty(const QString &name) return false; ScopedString s(scope, engine->newString(name)); - return o->deleteProperty(s); + return o->deleteProperty(s->toPropertyKey()); } /*! @@ -1269,8 +1255,8 @@ bool QJSValue::hasProperty(const QString &name) const if (!o) return false; - ScopedString s(scope, engine->newIdentifier(name)); - return o->hasProperty(s); + ScopedString s(scope, engine->newString(name)); + return o->hasProperty(s->toPropertyKey()); } /*! @@ -1291,7 +1277,7 @@ bool QJSValue::hasOwnProperty(const QString &name) const return false; ScopedString s(scope, engine->newIdentifier(name)); - return o->hasOwnProperty(s); + return o->getOwnProperty(s->propertyKey()) != Attr_Invalid; } /*! diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h index 62e09f72be..bcf0a9d12d 100644 --- a/src/qml/jsapi/qjsvalue_p.h +++ b/src/qml/jsapi/qjsvalue_p.h @@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE -class QJSValuePrivate +class Q_AUTOTEST_EXPORT QJSValuePrivate { public: static inline QV4::Value *getValue(const QJSValue *jsval) diff --git a/src/qml/jsapi/qjsvalueiterator.cpp b/src/qml/jsapi/qjsvalueiterator.cpp index ce472ce7e5..35c6ecc668 100644 --- a/src/qml/jsapi/qjsvalueiterator.cpp +++ b/src/qml/jsapi/qjsvalueiterator.cpp @@ -57,7 +57,7 @@ QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValue &v) QV4::Scope scope(e); QV4::ScopedObject o(scope, QJSValuePrivate::getValue(&v)); - iterator.set(e, e->newForEachIteratorObject(o)); + iterator.set(e, e->newForInIteratorObject(o)); } @@ -102,7 +102,7 @@ QJSValueIterator::QJSValueIterator(const QJSValue& object) if (!v4) return; QV4::Scope scope(v4); - QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value()); + QV4::Scoped<QV4::ForInIteratorObject> it(scope, d_ptr->iterator.value()); it->d()->it().flags = QV4::ObjectIterator::NoFlags; QV4::ScopedString nm(scope); QV4::Property nextProperty; @@ -153,7 +153,7 @@ bool QJSValueIterator::next() if (!v4) return false; QV4::Scope scope(v4); - QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value()); + QV4::Scoped<QV4::ForInIteratorObject> it(scope, d_ptr->iterator.value()); QV4::ScopedString nm(scope); QV4::Property nextProperty; QV4::PropertyAttributes nextAttributes; @@ -200,7 +200,7 @@ QJSValue QJSValueIterator::value() const if (!d_ptr->currentName.as<QV4::String>() && d_ptr->currentIndex == UINT_MAX) return QJSValue(); - QV4::ScopedValue v(scope, d_ptr->currentIndex == UINT_MAX ? obj->get(d_ptr->currentName.as<QV4::String>()) : obj->getIndexed(d_ptr->currentIndex)); + QV4::ScopedValue v(scope, d_ptr->currentIndex == UINT_MAX ? obj->get(d_ptr->currentName.as<QV4::String>()) : obj->get(d_ptr->currentIndex)); if (scope.hasException()) { engine->catchException(); return QJSValue(); @@ -229,8 +229,8 @@ QJSValueIterator& QJSValueIterator::operator=(QJSValue& object) QV4::Scope scope(v4); QV4::ScopedObject o(scope, QJSValuePrivate::getValue(&object)); - d_ptr->iterator.set(v4, v4->newForEachIteratorObject(o)); - QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value()); + d_ptr->iterator.set(v4, v4->newForInIteratorObject(o)); + QV4::Scoped<QV4::ForInIteratorObject> it(scope, d_ptr->iterator.value()); it->d()->it().flags = QV4::ObjectIterator::NoFlags; QV4::ScopedString nm(scope); QV4::Property nextProperty; diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 4bc877bd9d..ec5803b2df 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -14,21 +14,29 @@ SOURCES += \ $$PWD/qv4sparsearray.cpp \ $$PWD/qv4arraydata.cpp \ $$PWD/qv4arrayobject.cpp \ + $$PWD/qv4arrayiterator.cpp \ $$PWD/qv4argumentsobject.cpp \ $$PWD/qv4booleanobject.cpp \ $$PWD/qv4dateobject.cpp \ $$PWD/qv4errorobject.cpp \ $$PWD/qv4function.cpp \ $$PWD/qv4functionobject.cpp \ + $$PWD/qv4generatorobject.cpp \ $$PWD/qv4globalobject.cpp \ + $$PWD/qv4iterator.cpp \ $$PWD/qv4jsonobject.cpp \ $$PWD/qv4mathobject.cpp \ $$PWD/qv4memberdata.cpp \ $$PWD/qv4numberobject.cpp \ $$PWD/qv4object.cpp \ $$PWD/qv4objectproto.cpp \ + $$PWD/qv4propertykey.cpp \ + $$PWD/qv4proxy.cpp \ $$PWD/qv4qmlcontext.cpp \ + $$PWD/qv4reflect.cpp \ $$PWD/qv4regexpobject.cpp \ + $$PWD/qv4stackframe.cpp \ + $$PWD/qv4stringiterator.cpp \ $$PWD/qv4stringobject.cpp \ $$PWD/qv4variantobject.cpp \ $$PWD/qv4objectiterator.cpp \ @@ -36,13 +44,18 @@ SOURCES += \ $$PWD/qv4runtimecodegen.cpp \ $$PWD/qv4serialize.cpp \ $$PWD/qv4script.cpp \ - $$PWD/qv4sequenceobject.cpp \ + $$PWD/qv4symbol.cpp \ + $$PWD/qv4setobject.cpp \ + $$PWD/qv4setiterator.cpp \ $$PWD/qv4include.cpp \ $$PWD/qv4qobjectwrapper.cpp \ $$PWD/qv4arraybuffer.cpp \ $$PWD/qv4typedarray.cpp \ $$PWD/qv4dataview.cpp \ - $$PWD/qv4vme_moth.cpp + $$PWD/qv4vme_moth.cpp \ + $$PWD/qv4mapobject.cpp \ + $$PWD/qv4mapiterator.cpp \ + $$PWD/qv4estable.cpp qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp @@ -64,22 +77,30 @@ HEADERS += \ $$PWD/qv4sparsearray_p.h \ $$PWD/qv4arraydata_p.h \ $$PWD/qv4arrayobject_p.h \ + $$PWD/qv4arrayiterator_p.h \ $$PWD/qv4argumentsobject_p.h \ $$PWD/qv4booleanobject_p.h \ $$PWD/qv4dateobject_p.h \ $$PWD/qv4errorobject_p.h \ $$PWD/qv4function_p.h \ $$PWD/qv4functionobject_p.h \ + $$PWD/qv4generatorobject_p.h \ $$PWD/qv4globalobject_p.h \ + $$PWD/qv4iterator_p.h \ $$PWD/qv4jsonobject_p.h \ $$PWD/qv4mathobject_p.h \ $$PWD/qv4memberdata_p.h \ $$PWD/qv4numberobject_p.h \ $$PWD/qv4object_p.h \ $$PWD/qv4objectproto_p.h \ + $$PWD/qv4propertykey_p.h \ + $$PWD/qv4proxy_p.h \ $$PWD/qv4qmlcontext_p.h \ + $$PWD/qv4reflect_p.h \ $$PWD/qv4regexpobject_p.h \ $$PWD/qv4runtimecodegen_p.h \ + $$PWD/qv4stackframe_p.h \ + $$PWD/qv4stringiterator_p.h \ $$PWD/qv4stringobject_p.h \ $$PWD/qv4variantobject_p.h \ $$PWD/qv4property_p.h \ @@ -87,16 +108,30 @@ HEADERS += \ $$PWD/qv4regexp_p.h \ $$PWD/qv4serialize_p.h \ $$PWD/qv4script_p.h \ + $$PWD/qv4symbol_p.h \ + $$PWD/qv4setobject_p.h \ + $$PWD/qv4setiterator_p.h \ $$PWD/qv4scopedvalue_p.h \ $$PWD/qv4executableallocator_p.h \ - $$PWD/qv4sequenceobject_p.h \ $$PWD/qv4include_p.h \ $$PWD/qv4qobjectwrapper_p.h \ $$PWD/qv4profiling_p.h \ $$PWD/qv4arraybuffer_p.h \ $$PWD/qv4typedarray_p.h \ $$PWD/qv4dataview_p.h \ - $$PWD/qv4vme_moth_p.h + $$PWD/qv4vme_moth_p.h \ + $$PWD/qv4mapobject_p.h \ + $$PWD/qv4mapiterator_p.h \ + $$PWD/qv4estable_p.h \ + $$PWD/qv4vtable_p.h + +qtConfig(qml-sequence-object) { + HEADERS += \ + $$PWD/qv4sequenceobject_p.h + + SOURCES += \ + $$PWD/qv4sequenceobject.cpp +} } diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 075e7afd8a..7b501b9fbb 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -37,11 +37,13 @@ ** ****************************************************************************/ #include <qv4argumentsobject_p.h> +#include <qv4arrayobject_p.h> #include <qv4alloca_p.h> #include <qv4scopedvalue_p.h> #include <qv4string_p.h> #include <qv4function_p.h> #include <qv4jscall_p.h> +#include <qv4symbol_p.h> using namespace QV4; @@ -61,10 +63,12 @@ void Heap::ArgumentsObject::init(QV4::CppStackFrame *frame) this->context.set(v4, context->d()); Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable()); - Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee())); + Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()->propertyKey())); setProperty(v4, CalleePropertyIndex, context->d()->function); - Q_ASSERT(LengthPropertyIndex == internalClass->find(v4->id_length())); + Q_ASSERT(LengthPropertyIndex == internalClass->find(v4->id_length()->propertyKey())); setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(context->argc())); + Q_ASSERT(SymbolIteratorPropertyIndex == internalClass->find(v4->symbol_iterator()->propertyKey())); + setProperty(v4, SymbolIteratorPropertyIndex, *v4->arrayProtoValues()); } void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame) @@ -74,19 +78,18 @@ void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame) Object::init(); - Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee())); - Q_ASSERT(CallerPropertyIndex == internalClass->find(v4->id_caller())); + Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()->propertyKey())); + Q_ASSERT(SymbolIteratorPropertyIndex == internalClass->find(v4->symbol_iterator()->propertyKey())); + setProperty(v4, SymbolIteratorPropertyIndex, *v4->arrayProtoValues()); setProperty(v4, CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); setProperty(v4, CalleePropertyIndex + QV4::Object::SetterOffset, *v4->thrower()); - setProperty(v4, CallerPropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); - setProperty(v4, CallerPropertyIndex + QV4::Object::SetterOffset, *v4->thrower()); Scope scope(v4); Scoped<QV4::StrictArgumentsObject> args(scope, this); args->arrayReserve(frame->originalArgumentsCount); args->arrayPut(0, frame->originalArguments, frame->originalArgumentsCount); - Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length())); + Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length()->propertyKey())); setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(frame->originalArgumentsCount)); } @@ -117,106 +120,111 @@ void ArgumentsObject::fullyCreate() d()->fullyCreated = true; } -bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs) +bool ArgumentsObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *desc, PropertyAttributes attrs) { - fullyCreate(); + if (!id.isArrayIndex()) + return Object::virtualDefineOwnProperty(m, id, desc, attrs); - Scope scope(engine); + ArgumentsObject *a = static_cast<ArgumentsObject *>(m); + a->fullyCreate(); + + uint index = id.asArrayIndex(); + Scope scope(m); ScopedProperty map(scope); PropertyAttributes mapAttrs; - uint numAccessors = qMin(d()->nFormals, context()->argc()); + uint numAccessors = qMin(a->d()->nFormals, a->context()->argc()); bool isMapped = false; - if (arrayData() && index < numAccessors && - arrayData()->attributes(index).isAccessor() && - arrayData()->get(index) == scope.engine->argumentsAccessors[index].getter()->asReturnedValue()) + if (a->arrayData() && index < numAccessors && + a->arrayData()->attributes(index).isAccessor() && + a->arrayData()->get(index) == scope.engine->argumentsAccessors[index].getter()->asReturnedValue()) isMapped = true; if (isMapped) { - Q_ASSERT(arrayData()); - mapAttrs = arrayData()->attributes(index); - arrayData()->getProperty(index, map, &mapAttrs); - setArrayAttributes(index, Attr_Data); - ArrayData::Index arrayIndex{ arrayData(), arrayData()->mappedIndex(index) }; - arrayIndex.set(scope.engine, d()->mappedArguments->values[index]); + Q_ASSERT(a->arrayData()); + mapAttrs = a->arrayData()->attributes(index); + a->arrayData()->getProperty(index, map, &mapAttrs); + a->setArrayAttributes(index, Attr_Data); + PropertyIndex arrayIndex{ a->arrayData(), a->arrayData()->values.values + a->arrayData()->mappedIndex(index) }; + arrayIndex.set(scope.engine, a->d()->mappedArguments->values[index]); } - bool result = Object::defineOwnProperty2(scope.engine, index, desc, attrs); - if (!result) { + bool result = Object::virtualDefineOwnProperty(m, id, desc, attrs); + if (!result) return false; - } if (isMapped && attrs.isData()) { - Q_ASSERT(arrayData()); + Q_ASSERT(a->arrayData()); ScopedFunctionObject setter(scope, map->setter()); JSCallData jsCallData(scope, 1); - *jsCallData->thisObject = this->asReturnedValue(); + *jsCallData->thisObject = a->asReturnedValue(); jsCallData->args[0] = desc->value; setter->call(jsCallData); if (attrs.isWritable()) { - setArrayAttributes(index, mapAttrs); - arrayData()->setProperty(engine, index, map); + a->setArrayAttributes(index, mapAttrs); + a->arrayData()->setProperty(m->engine(), index, map); } } return result; } -ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *hasProperty) +ReturnedValue ArgumentsObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); - if (args->fullyCreated()) - return Object::getIndexed(m, index, hasProperty); - - if (index < static_cast<uint>(args->context()->argc())) { - if (hasProperty) - *hasProperty = true; - return args->context()->args()[index].asReturnedValue(); + if (id.isArrayIndex() && !args->fullyCreated()) { + uint index = id.asArrayIndex(); + if (index < static_cast<uint>(args->context()->argc())) { + if (hasProperty) + *hasProperty = true; + return args->context()->args()[index].asReturnedValue(); + } } - if (hasProperty) - *hasProperty = false; - return Encode::undefined(); + return Object::virtualGet(m, id, receiver, hasProperty); } -bool ArgumentsObject::putIndexed(Managed *m, uint index, const Value &value) +bool ArgumentsObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { ArgumentsObject *args = static_cast<ArgumentsObject *>(m); - if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->argc())) - args->fullyCreate(); - - if (args->fullyCreated()) - return Object::putIndexed(m, index, value); - - args->context()->setArg(index, value); - return true; + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->argc())) + args->fullyCreate(); + + if (!args->fullyCreated()) { + args->context()->setArg(index, value); + return true; + } + } + return Object::virtualPut(m, id, value, receiver); } -bool ArgumentsObject::deleteIndexedProperty(Managed *m, uint index) +bool ArgumentsObject::virtualDeleteProperty(Managed *m, PropertyKey id) { ArgumentsObject *args = static_cast<ArgumentsObject *>(m); if (!args->fullyCreated()) args->fullyCreate(); - return Object::deleteIndexedProperty(m, index); + return Object::virtualDeleteProperty(m, id); } -PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) +PropertyAttributes ArgumentsObject::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p) { const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); - if (args->fullyCreated()) - return Object::queryIndexed(m, index); + if (!id.isArrayIndex() || args->fullyCreated()) + return Object::virtualGetOwnProperty(m, id, p); - uint numAccessors = qMin(args->d()->nFormals, args->context()->argc()); + uint index = id.asArrayIndex(); uint argCount = args->context()->argc(); if (index >= argCount) return PropertyAttributes(); - if (index >= numAccessors) - return Attr_Data; - return Attr_Accessor; + if (p) + p->value = args->context()->args()[index]; + return Attr_Data; } DEFINE_OBJECT_VTABLE(ArgumentsGetterFunction); -ReturnedValue ArgumentsGetterFunction::call(const FunctionObject *getter, const Value *thisObject, const Value *, int) +ReturnedValue ArgumentsGetterFunction::virtualCall(const FunctionObject *getter, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = getter->engine(); Scope scope(v4); @@ -231,7 +239,7 @@ ReturnedValue ArgumentsGetterFunction::call(const FunctionObject *getter, const DEFINE_OBJECT_VTABLE(ArgumentsSetterFunction); -ReturnedValue ArgumentsSetterFunction::call(const FunctionObject *setter, const Value *thisObject, const Value *argv, int argc) +ReturnedValue ArgumentsSetterFunction::virtualCall(const FunctionObject *setter, const Value *thisObject, const Value *argv, int argc) { ExecutionEngine *v4 = setter->engine(); Scope scope(v4); @@ -245,10 +253,8 @@ ReturnedValue ArgumentsSetterFunction::call(const FunctionObject *setter, const return Encode::undefined(); } -uint ArgumentsObject::getLength(const Managed *m) +qint64 ArgumentsObject::virtualGetLength(const Managed *m) { const ArgumentsObject *a = static_cast<const ArgumentsObject *>(m); - if (a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->isInteger()) - return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->integerValue(); - return Primitive::toUInt32(a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->doubleValue()); + return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->toLength(); } diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index ac281f555a..9ac9ac5d8b 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -85,7 +85,8 @@ DECLARE_HEAP_OBJECT(ArgumentsObject, Object) { DECLARE_MARKOBJECTS(ArgumentsObject); enum { LengthPropertyIndex = 0, - CalleePropertyIndex = 1 + SymbolIteratorPropertyIndex = 1, + CalleePropertyIndex = 2 }; void init(CppStackFrame *frame); }; @@ -95,8 +96,8 @@ DECLARE_HEAP_OBJECT(ArgumentsObject, Object) { DECLARE_HEAP_OBJECT(StrictArgumentsObject, Object) { enum { LengthPropertyIndex = 0, - CalleePropertyIndex = 1, - CallerPropertyIndex = 3 + SymbolIteratorPropertyIndex = 1, + CalleePropertyIndex = 2 }; void init(CppStackFrame *frame); }; @@ -108,7 +109,7 @@ struct ArgumentsGetterFunction: FunctionObject V4_OBJECT2(ArgumentsGetterFunction, FunctionObject) uint index() const { return d()->index; } - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; inline void @@ -123,7 +124,7 @@ struct ArgumentsSetterFunction: FunctionObject V4_OBJECT2(ArgumentsSetterFunction, FunctionObject) uint index() const { return d()->index; } - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; inline void @@ -142,15 +143,15 @@ struct ArgumentsObject: Object { bool fullyCreated() const { return d()->fullyCreated; } static bool isNonStrictArgumentsObject(Managed *m) { - return m->d()->vtable() == staticVTable(); + return m->vtable() == staticVTable(); } - bool defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs); - static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static bool putIndexed(Managed *m, uint index, const Value &value); - static bool deleteIndexedProperty(Managed *m, uint index); - static PropertyAttributes queryIndexed(const Managed *m, uint index); - static uint getLength(const Managed *m); + static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *desc, PropertyAttributes attrs); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); + static bool virtualDeleteProperty(Managed *m, PropertyKey id); + static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); + static qint64 virtualGetLength(const Managed *m); void fullyCreate(); diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index 59a2b9d913..f80c9a0ab2 100644 --- a/src/qml/jsruntime/qv4arraybuffer.cpp +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -41,6 +41,7 @@ #include "qv4dataview_p.h" #include "qv4string_p.h" #include "qv4jscall_p.h" +#include "qv4symbol_p.h" using namespace QV4; @@ -52,7 +53,7 @@ void Heap::ArrayBufferCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("ArrayBuffer")); } -ReturnedValue ArrayBufferCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue ArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { ExecutionEngine *v4 = f->engine(); Scope scope(v4); @@ -73,9 +74,9 @@ ReturnedValue ArrayBufferCtor::callAsConstructor(const FunctionObject *f, const } -ReturnedValue ArrayBufferCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue ArrayBufferCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) { - return callAsConstructor(f, argv, argc); + return virtualCallAsConstructor(f, argv, argc, f); } ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -94,7 +95,8 @@ ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value void Heap::ArrayBuffer::init(size_t length) { Object::init(); - data = QTypedArrayData<char>::allocate(length + 1); + if (length < UINT_MAX) + data = QTypedArrayData<char>::allocate(length + 1); if (!data) { internalClass->engine->throwRangeError(QStringLiteral("ArrayBuffer: out of memory")); return; @@ -147,13 +149,17 @@ void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); ctor->defineDefaultProperty(QStringLiteral("isView"), ArrayBufferCtor::method_isView, 1); + ctor->addSymbolSpecies(); + defineDefaultProperty(engine->id_constructor(), (o = ctor)); defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr); defineDefaultProperty(QStringLiteral("slice"), method_slice, 2); defineDefaultProperty(QStringLiteral("toString"), method_toString, 0); + ScopedString name(scope, engine->newString(QStringLiteral("ArrayBuffer"))); + defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name); } ReturnedValue ArrayBufferPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int) diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h index 59e78ee85f..089dbc522f 100644 --- a/src/qml/jsruntime/qv4arraybuffer_p.h +++ b/src/qml/jsruntime/qv4arraybuffer_p.h @@ -78,8 +78,8 @@ struct ArrayBufferCtor: FunctionObject { V4_OBJECT2(ArrayBufferCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_isView(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index 855407e6f7..ce1d0503df 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -47,25 +47,7 @@ using namespace QV4; -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON - -const QV4::VTable QV4::ArrayData::static_vtbl = { - nullptr, - 0, - 0, - QV4::ArrayData::IsExecutionContext, - QV4::ArrayData::IsString, - QV4::ArrayData::IsObject, - QV4::ArrayData::IsFunctionObject, - QV4::ArrayData::IsErrorObject, - QV4::ArrayData::IsArrayData, - 0, - QV4::ArrayData::MyType, - "ArrayData", - Q_VTABLE_FUNCTION(QV4::ArrayData, destroy), - ArrayData::Data::markObjects, - isEqualTo -}; +DEFINE_MANAGED_VTABLE(ArrayData); const ArrayVTable SimpleArrayData::static_vtbl = { @@ -99,18 +81,9 @@ const ArrayVTable SparseArrayData::static_vtbl = SparseArrayData::length }; -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF - Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SimpleArrayData)); Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SparseArrayData)); -static Q_ALWAYS_INLINE void storeValue(ReturnedValue *target, uint value) -{ - Value v; - v.setEmpty(value); - *target = v.asReturnedValue(); -} - void Heap::ArrayData::markObjects(Heap::Base *base, MarkStack *stack) { ArrayData *a = static_cast<ArrayData *>(base); @@ -195,7 +168,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt Heap::SparseArrayData *sparse = static_cast<Heap::SparseArrayData *>(newData->d()); - ReturnedValue *lastFree; + Value *lastFree; if (d && d->type() == Heap::ArrayData::Sparse) { Heap::SparseArrayData *old = static_cast<Heap::SparseArrayData *>(d->d()); sparse->sparse = old->sparse; @@ -204,29 +177,29 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt } else { sparse->sparse = new SparseArray; lastFree = &sparse->sparse->freeList; - storeValue(lastFree, 0); + *lastFree = Encode(0); for (uint i = 0; i < toCopy; ++i) { if (!sparse->values[i].isEmpty()) { SparseArrayNode *n = sparse->sparse->insert(i); n->value = i; } else { - storeValue(lastFree, i); + *lastFree = Encode(i); sparse->values.values[i].setEmpty(); - lastFree = &sparse->values.values[i].rawValueRef(); + lastFree = &sparse->values.values[i]; } } } if (toCopy < sparse->values.alloc) { for (uint i = toCopy; i < sparse->values.alloc; ++i) { - storeValue(lastFree, i); + *lastFree = Encode(i); sparse->values.values[i].setEmpty(); - lastFree = &sparse->values.values[i].rawValueRef(); + lastFree = &sparse->values.values[i]; } } - storeValue(lastFree, UINT_MAX); + *lastFree = Encode(-1); - Q_ASSERT(Value::fromReturnedValue(sparse->sparse->freeList).isEmpty()); + Q_ASSERT(sparse->sparse->freeList.isInteger()); // ### Could explicitly free the old data } @@ -368,12 +341,12 @@ void SparseArrayData::free(Heap::ArrayData *d, uint idx) Value *v = d->values.values + idx; if (d->attrs && d->attrs[idx].isAccessor()) { // double slot, free both. Order is important, so we have a double slot for allocation again afterwards. - v[1].setEmpty(Value::fromReturnedValue(d->sparse->freeList).emptyValue()); - v[0].setEmpty(idx + 1); + v[1] = d->sparse->freeList; + v[0] = Encode(idx + 1); } else { - v->setEmpty(Value::fromReturnedValue(d->sparse->freeList).emptyValue()); + *v = d->sparse->freeList; } - d->sparse->freeList = Primitive::emptyValue(idx).asReturnedValue(); + d->sparse->freeList = Encode(idx); if (d->attrs) d->attrs[idx].clear(); } @@ -390,36 +363,34 @@ uint SparseArrayData::allocate(Object *o, bool doubleSlot) Q_ASSERT(o->d()->arrayData->type == Heap::ArrayData::Sparse); Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (doubleSlot) { - ReturnedValue *last = &dd->sparse->freeList; + Value *last = &dd->sparse->freeList; while (1) { - if (Value::fromReturnedValue(*last).value() == UINT_MAX) { + if (last->int_32() == -1) { reallocate(o, dd->values.alloc + 2, true); dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); last = &dd->sparse->freeList; - Q_ASSERT(Value::fromReturnedValue(*last).value() != UINT_MAX); + Q_ASSERT(last->int_32() != -1); } - Q_ASSERT(dd->values[Value::fromReturnedValue(*last).value()].value() != Value::fromReturnedValue(*last).value()); - if (dd->values[Value::fromReturnedValue(*last).value()].value() == (Value::fromReturnedValue(*last).value() + 1)) { + Q_ASSERT(dd->values[static_cast<uint>(last->int_32())].int_32() != last->int_32()); + if (dd->values[static_cast<uint>(last->int_32())].int_32() == last->int_32() + 1) { // found two slots in a row - uint idx = Value::fromReturnedValue(*last).emptyValue(); - Value lastV = Value::fromReturnedValue(*last); - lastV.setEmpty(dd->values[lastV.emptyValue() + 1].value()); - *last = lastV.rawValue(); + uint idx = static_cast<uint>(last->int_32()); + *last = Encode(dd->values[static_cast<uint>(last->int_32()) + 1].int_32()); dd->attrs[idx] = Attr_Accessor; return idx; } - last = &dd->values.values[Value::fromReturnedValue(*last).value()].rawValueRef(); + last = &dd->values.values[last->int_32()]; } } else { - if (Value::fromReturnedValue(dd->sparse->freeList).value() == UINT_MAX) { + if (dd->sparse->freeList.int_32() == -1) { reallocate(o, dd->values.alloc + 1, false); dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } - uint idx = Value::fromReturnedValue(dd->sparse->freeList).value(); - Q_ASSERT(idx != UINT_MAX); - dd->sparse->freeList = dd->values[idx].asReturnedValue(); - Q_ASSERT(Value::fromReturnedValue(dd->sparse->freeList).isEmpty()); + Q_ASSERT(dd->sparse->freeList.int_32() != -1); + uint idx = static_cast<uint>(dd->sparse->freeList.int_32()); + dd->sparse->freeList = dd->values[idx]; + Q_ASSERT(dd->sparse->freeList.isInteger()); if (dd->attrs) dd->attrs[idx] = Attr_Data; return idx; @@ -474,14 +445,14 @@ bool SparseArrayData::del(Object *o, uint index) if (isAccessor) { // free up both indices - dd->values.values[pidx + 1].setEmpty(Value::fromReturnedValue(dd->sparse->freeList).emptyValue()); - dd->values.values[pidx].setEmpty(pidx + 1); + dd->values.values[pidx + 1] = dd->sparse->freeList; + dd->values.values[pidx] = Encode(pidx + 1); } else { Q_ASSERT(dd->type == Heap::ArrayData::Sparse); - dd->values.values[pidx].setEmpty(Value::fromReturnedValue(dd->sparse->freeList).emptyValue()); + dd->values.values[pidx] = dd->sparse->freeList; } - dd->sparse->freeList = Primitive::emptyValue(pidx).asReturnedValue(); + dd->sparse->freeList = Encode(pidx); dd->sparse->erase(n); return true; } @@ -593,7 +564,7 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n) if (!other || ArgumentsObject::isNonStrictArgumentsObject(otherObj)) { ScopedValue v(scope); for (uint i = 0; i < n; ++i) - obj->arraySet(oldSize + i, (v = otherObj->getIndexed(i))); + obj->arraySet(oldSize + i, (v = otherObj->get(i))); } else if (other && other->isSparse()) { Heap::SparseArrayData *os = static_cast<Heap::SparseArrayData *>(other->d()); if (other->hasAttributes()) { diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index 7ec060f9c6..ac5b430356 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -103,28 +103,16 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) { enum Type { Simple = 0, Complex = 1, Sparse = 2, Custom = 3 }; - struct Index { - Heap::ArrayData *arrayData; - uint index; - - void set(EngineBase *e, Value newVal) { - arrayData->values.set(e, index, newVal); - } - const Value *operator->() const { return &arrayData->values[index]; } - const Value &operator*() const { return arrayData->values[index]; } - bool isNull() const { return !arrayData; } - }; - bool isSparse() const { return type == Sparse; } - const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(Base::vtable()); } + const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(internalClass->vtable); } inline ReturnedValue get(uint i) const { return vtable()->get(this, i); } inline bool getProperty(uint index, Property *p, PropertyAttributes *attrs); inline void setProperty(EngineBase *e, uint index, const Property *p); - inline Index getValueOrSetter(uint index, PropertyAttributes *attrs); + inline PropertyIndex getValueOrSetter(uint index, PropertyAttributes *attrs); inline PropertyAttributes attributes(uint i) const; bool isEmpty(uint i) const { @@ -187,8 +175,6 @@ struct Q_QML_EXPORT ArrayData : public Managed IsArrayData = true }; - typedef Heap::ArrayData::Index Index; - uint alloc() const { return d()->values.alloc; } uint &alloc() { return d()->values.alloc; } void setAlloc(uint a) { d()->values.alloc = a; } @@ -303,9 +289,9 @@ bool ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs) *attrs = attributes(index); if (p) { - p->value = *(Index{ this, mapped }); + p->value = *(PropertyIndex{ this, values.values + mapped }); if (attrs->isAccessor()) - p->set = *(Index{ this, mapped + 1 /*Object::SetterOffset*/ }); + p->set = *(PropertyIndex{ this, values.values + mapped + 1 /*Object::SetterOffset*/ }); } return true; } @@ -326,16 +312,18 @@ inline PropertyAttributes ArrayData::attributes(uint i) const return static_cast<const SimpleArrayData *>(this)->attributes(i); } -ArrayData::Index ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs) +PropertyIndex ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs) { uint idx = mappedIndex(index); if (idx == UINT_MAX) { *attrs = Attr_Invalid; - return { nullptr, 0 }; + return { nullptr, nullptr }; } *attrs = attributes(index); - return { this, attrs->isAccessor() ? idx + 1 /* QV4::Object::SetterOffset*/ : idx }; + if (attrs->isAccessor()) + ++idx; + return { this, values.values + idx }; } diff --git a/src/qml/jsruntime/qv4arrayiterator.cpp b/src/qml/jsruntime/qv4arrayiterator.cpp new file mode 100644 index 0000000000..650f58463e --- /dev/null +++ b/src/qml/jsruntime/qv4arrayiterator.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Crimson AS <info@crimson.no> +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qv4iterator_p.h> +#include <private/qv4arrayiterator_p.h> +#include <private/qv4typedarray_p.h> +#include <private/qv4symbol_p.h> + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(ArrayIteratorObject); + +void ArrayIteratorPrototype::init(ExecutionEngine *e) +{ + defineDefaultProperty(QStringLiteral("next"), method_next, 0); + + Scope scope(e); + ScopedString val(scope, e->newString(QLatin1String("Array Iterator"))); + defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val); +} + +ReturnedValue ArrayIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int) +{ + Scope scope(b); + const ArrayIteratorObject *thisObject = that->as<ArrayIteratorObject>(); + if (!thisObject) + return scope.engine->throwTypeError(QLatin1String("Not an Array Iterator instance")); + + ScopedObject a(scope, thisObject->d()->iteratedObject); + if (!a) { + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + quint32 index = thisObject->d()->nextIndex; + IteratorKind itemKind = thisObject->d()->iterationKind; + + Scoped<TypedArray> ta(scope, a->as<TypedArray>()); + quint32 len = a->getLength(); + + if (index >= len) { + thisObject->d()->iteratedObject.set(scope.engine, nullptr); + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + thisObject->d()->nextIndex = index + 1; + if (itemKind == KeyIteratorKind) { + return IteratorPrototype::createIterResultObject(scope.engine, Primitive::fromInt32(index), false); + } + + ReturnedValue elementValue = a->get(index); + CHECK_EXCEPTION(); + + if (itemKind == ValueIteratorKind) { + return IteratorPrototype::createIterResultObject(scope.engine, Value::fromReturnedValue(elementValue), false); + } else { + Q_ASSERT(itemKind == KeyValueIteratorKind); + + ScopedArrayObject resultArray(scope, scope.engine->newArrayObject()); + resultArray->arrayReserve(2); + resultArray->arrayPut(0, Primitive::fromInt32(index)); + resultArray->arrayPut(1, Value::fromReturnedValue(elementValue)); + resultArray->setArrayLengthUnchecked(2); + + return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false); + } +} + diff --git a/src/qml/jsruntime/qv4arrayiterator_p.h b/src/qml/jsruntime/qv4arrayiterator_p.h new file mode 100644 index 0000000000..6d6bb466f1 --- /dev/null +++ b/src/qml/jsruntime/qv4arrayiterator_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Crimson AS <info@crimson.no> +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ARRAYITERATOR_P_H +#define QV4ARRAYITERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4iterator_p.h" +#include "qv4arraydata_p.h" + +QT_BEGIN_NAMESPACE + + +namespace QV4 { + +namespace Heap { + +#define ArrayIteratorObjectMembers(class, Member) \ + Member(class, Pointer, Object *, iteratedObject) \ + Member(class, NoMark, IteratorKind, iterationKind) \ + Member(class, NoMark, quint32, nextIndex) + +DECLARE_HEAP_OBJECT(ArrayIteratorObject, Object) { + DECLARE_MARKOBJECTS(ArrayIteratorObject); + void init(Object *obj, QV4::ExecutionEngine *engine) + { + Object::init(); + this->iteratedObject.set(engine, obj); + this->nextIndex = 0; + } +}; + +} + +struct ArrayIteratorPrototype : Object +{ + V4_PROTOTYPE(iteratorPrototype) + void init(ExecutionEngine *engine); + + static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +}; + +struct ArrayIteratorObject : Object +{ + V4_OBJECT2(ArrayIteratorObject, Object) + Q_MANAGED_TYPE(ArrayIteratorObject) + V4_PROTOTYPE(arrayIteratorPrototype) + + void init(ExecutionEngine *engine); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4ARRAYITERATOR_P_H + diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index bd019d3bcb..05f6b7dfec 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2018 Crimson AS <info@crimson.no> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -38,12 +39,15 @@ ****************************************************************************/ #include "qv4arrayobject_p.h" +#include "qv4objectiterator_p.h" +#include "qv4arrayiterator_p.h" #include "qv4sparsearray_p.h" #include "qv4objectproto_p.h" #include "qv4jscall_p.h" #include "qv4argumentsobject_p.h" #include "qv4runtime_p.h" #include "qv4string_p.h" +#include "qv4symbol_p.h" #include <QtCore/qscopedvaluerollback.h> using namespace QV4; @@ -55,7 +59,7 @@ void Heap::ArrayCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Array")); } -ReturnedValue ArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue ArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { ExecutionEngine *v4 = static_cast<const ArrayCtor *>(f)->engine(); Scope scope(v4); @@ -80,22 +84,28 @@ ReturnedValue ArrayCtor::callAsConstructor(const FunctionObject *f, const Value return a.asReturnedValue(); } -ReturnedValue ArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue ArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) { - return callAsConstructor(f, argv, argc); + return virtualCallAsConstructor(f, argv, argc, f); } void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); ctor->defineDefaultProperty(QStringLiteral("isArray"), method_isArray, 1); + ctor->defineDefaultProperty(QStringLiteral("of"), method_of, 0); + ctor->defineDefaultProperty(QStringLiteral("from"), method_from, 1); + ctor->addSymbolSpecies(); + defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0); defineDefaultProperty(QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(QStringLiteral("copyWithin"), method_copyWithin, 2); + defineDefaultProperty(QStringLiteral("entries"), method_entries, 0); defineDefaultProperty(QStringLiteral("find"), method_find, 1); defineDefaultProperty(QStringLiteral("findIndex"), method_findIndex, 1); defineDefaultProperty(QStringLiteral("join"), method_join, 1); @@ -107,15 +117,23 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("sort"), method_sort, 1); defineDefaultProperty(QStringLiteral("splice"), method_splice, 2); defineDefaultProperty(QStringLiteral("unshift"), method_unshift, 1); + defineDefaultProperty(QStringLiteral("includes"), method_includes, 1); defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(QStringLiteral("keys"), method_keys, 0); defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); defineDefaultProperty(QStringLiteral("every"), method_every, 1); + defineDefaultProperty(QStringLiteral("fill"), method_fill, 1); defineDefaultProperty(QStringLiteral("some"), method_some, 1); defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1); defineDefaultProperty(QStringLiteral("map"), method_map, 1); defineDefaultProperty(QStringLiteral("filter"), method_filter, 1); defineDefaultProperty(QStringLiteral("reduce"), method_reduce, 1); defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1); + ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values"))); + ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0)); + engine->jsObjects[ExecutionEngine::ArrayProtoValues] = values; + defineDefaultProperty(QStringLiteral("values"), values); + defineDefaultProperty(engine->symbol_iterator(), values); } ReturnedValue ArrayPrototype::method_isArray(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -124,6 +142,200 @@ ReturnedValue ArrayPrototype::method_isArray(const FunctionObject *, const Value return Encode(isArray); } +ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObject ctor, bool useLen, int len) +{ + ScopedObject a(scope, Primitive::undefinedValue()); + + if (ctor) { + // ### the spec says that we should only call constructors if + // IsConstructor(that), but we have no way of knowing if a builtin is a + // constructor. so for the time being, just try call it, and silence any + // exceptions-- this is not ideal, as the spec also says that we should + // return on exception. + // + // this also isn't completely kosher. for instance: + // Array.from.call(Object, []).constructor == Object + // is expected by the tests, but naturally, we get Number. + ScopedValue argument(scope, useLen ? QV4::Encode(len) : Primitive::undefinedValue()); + a = ctor->callAsConstructor(argument, useLen ? 1 : 0); + if (scope.engine->hasException) + scope.engine->catchException(); // probably not a constructor, then. + } + + if (!a) { + a = scope.engine->newArrayObject(len); + } + + return a; +} + +ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(builtin); + ScopedFunctionObject thatCtor(scope, thisObject); + ScopedObject itemsObject(scope, argv[0]); + bool usingIterator = false; + + if (itemsObject) { + // If the object claims to support iterators, then let's try use them. + ScopedValue it(scope, itemsObject->get(scope.engine->symbol_iterator())); + if (!it->isNullOrUndefined()) { + ScopedFunctionObject itfunc(scope, it); + if (!itfunc) + return scope.engine->throwTypeError(); + usingIterator = true; + } + } + + ScopedFunctionObject mapfn(scope, Primitive::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); + } + + ScopedValue thisArg(scope); + if (argc > 2) + thisArg = argv[2]; + + if (usingIterator) { + // Item iteration supported, so let's go ahead and try use that. + ScopedObject a(createObjectFromCtorOrArray(scope, thatCtor, false, 0)); + CHECK_EXCEPTION(); + ScopedObject iterator(scope, Runtime::method_getIterator(scope.engine, itemsObject, true)); + CHECK_EXCEPTION(); // symbol_iterator threw; whoops. + if (!iterator) { + return scope.engine->throwTypeError(); // symbol_iterator wasn't an object. + } + + qint64 k = 0; + ScopedValue mappedValue(scope); + Value *nextValue = scope.alloc(1); + ScopedValue done(scope); + + // The loop below pulls out all the properties using the iterator, and + // sets them into the created array. + forever { + if (k > (static_cast<qint64>(1) << 53) - 1) { + ScopedValue falsey(scope, Encode(false)); + ScopedValue error(scope, scope.engine->throwTypeError()); + return Runtime::method_iteratorClose(scope.engine, iterator, falsey); + } + + // Retrieve the next value. If the iteration ends, we're done here. + done = Value::fromReturnedValue(Runtime::method_iteratorNext(scope.engine, iterator, nextValue)); + CHECK_EXCEPTION(); + if (done->toBoolean()) { + if (ArrayObject *ao = a->as<ArrayObject>()) { + ao->setArrayLengthUnchecked(k); + } else { + a->set(scope.engine->id_length(), Primitive::fromDouble(k), QV4::Object::DoThrowOnRejection); + CHECK_EXCEPTION(); + } + return a.asReturnedValue(); + } + + if (mapfn) { + mapArguments[0] = *nextValue; + mapArguments[1] = Primitive::fromDouble(k); + mappedValue = mapfn->call(thisArg, mapArguments, 2); + if (scope.engine->hasException) + return Runtime::method_iteratorClose(scope.engine, iterator, Primitive::fromBoolean(false)); + } else { + mappedValue = *nextValue; + } + + if (a->getOwnProperty(PropertyKey::fromArrayIndex(k)) == Attr_Invalid) { + a->arraySet(k, mappedValue); + } else { + // Don't return: we need to close the iterator. + scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k)); + } + + if (scope.engine->hasException) { + ScopedValue falsey(scope, Encode(false)); + return Runtime::method_iteratorClose(scope.engine, iterator, falsey); + } + + k++; + } + + // the return is hidden up in the loop above, when iteration finishes. + } else { + // Array-like fallback. We request properties by index, and set them on + // the return object. + 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())); + qint64 len = arrayLike->getLength(); + ScopedObject a(createObjectFromCtorOrArray(scope, thatCtor, true, len)); + CHECK_EXCEPTION(); + + qint64 k = 0; + ScopedValue mappedValue(scope, Primitive::undefinedValue()); + ScopedValue kValue(scope); + while (k < len) { + kValue = arrayLike->get(k); + CHECK_EXCEPTION(); + + if (mapfn) { + mapArguments[0] = kValue; + mapArguments[1] = Primitive::fromDouble(k); + mappedValue = mapfn->call(thisArg, mapArguments, 2); + CHECK_EXCEPTION(); + } else { + mappedValue = kValue; + } + + if (a->getOwnProperty(PropertyKey::fromArrayIndex(k)) != Attr_Invalid) + return scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k)); + + a->arraySet(k, mappedValue); + CHECK_EXCEPTION(); + + k++; + } + + if (ArrayObject *ao = a->as<ArrayObject>()) { + ao->setArrayLengthUnchecked(k); + } else { + a->set(scope.engine->id_length(), Primitive::fromDouble(k), QV4::Object::DoThrowOnRejection); + CHECK_EXCEPTION(); + } + return a.asReturnedValue(); + } + +} + +ReturnedValue ArrayPrototype::method_of(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(builtin); + ScopedFunctionObject that(scope, thisObject); + ScopedObject a(createObjectFromCtorOrArray(scope, that, true, argc)); + CHECK_EXCEPTION(); + + int k = 0; + while (k < argc) { + if (a->getOwnProperty(PropertyKey::fromArrayIndex(k)) != Attr_Invalid) { + return scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k)); + } + a->arraySet(k, argv[k]); + CHECK_EXCEPTION(); + + k++; + } + + // ArrayObject updates its own length, and will throw if we try touch it. + if (!a->as<ArrayObject>()) { + a->set(scope.engine->id_length(), Primitive::fromDouble(argc), QV4::Object::DoThrowOnRejection); + CHECK_EXCEPTION(); + } + + return a.asReturnedValue(); +} + ReturnedValue ArrayPrototype::method_toString(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) { Scope scope(builtin); @@ -171,9 +383,9 @@ ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value } else if (eltAsObj && eltAsObj->isListType()) { const uint startIndex = result->getLength(); for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) { - entry = eltAsObj->getIndexed(i); + entry = eltAsObj->get(i); // spec says not to throw if this fails - result->putIndexed(startIndex + i, entry); + result->put(startIndex + i, entry); } } else { result->arraySet(result->getLength(), argv[i]); @@ -183,6 +395,88 @@ ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value return result.asReturnedValue(); } +ReturnedValue ArrayPrototype::method_copyWithin(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); + if (!instance) + RETURN_UNDEFINED(); + + double len = instance->getLength(); + double target = argv[0].toInteger(); + double start = argv[1].toInteger(); + double end = len; + + if (argc > 2 && !argv[2].isUndefined()) { + end = argv[2].toInteger(); + } + + double relativeTarget = target; + double relativeStart = start; + double relativeEnd = end; + double from = 0; + double to = 0; + + if (relativeTarget < 0) { + to = std::max(len+relativeTarget, 0.0); + } else { + to = std::min(relativeTarget, len); + } + if (relativeStart < 0) { + from = std::max(len+relativeStart, 0.0); + } else { + from = std::min(relativeStart, len); + } + + double fin = 0; + if (relativeEnd < 0) { + fin = std::max(len+relativeEnd, 0.0); + } else { + fin = std::min(relativeEnd, len); + } + double count = std::min(fin-from, len-to); + double direction = 1; + if (from < to && to < from+count) { + direction = -1; + from = from + count - 1; + to = to + count - 1; + } + + while (count > 0) { + bool fromPresent = false; + ScopedValue fromVal(scope, instance->get(from, &fromPresent)); + + if (fromPresent) { + instance->setIndexed(to, fromVal, QV4::Object::DoThrowOnRejection); + CHECK_EXCEPTION(); + } else { + bool didDelete = instance->deleteProperty(PropertyKey::fromArrayIndex(to)); + CHECK_EXCEPTION(); + if (!didDelete) { + return scope.engine->throwTypeError(); + } + } + + from = from + direction; + to = to + direction; + count = count - 1; + } + + return instance.asReturnedValue(); +} + +ReturnedValue ArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + ScopedObject O(scope, thisObject->toObject(scope.engine)); + if (!O) + RETURN_UNDEFINED(); + + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind; + return ao->asReturnedValue(); +} + ReturnedValue ArrayPrototype::method_find(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); @@ -202,7 +496,7 @@ ReturnedValue ArrayPrototype::method_find(const FunctionObject *b, const Value * ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); for (uint k = 0; k < len; ++k) { - arguments[0] = instance->getIndexed(k); + arguments[0] = instance->get(k); CHECK_EXCEPTION(); arguments[1] = Primitive::fromDouble(k); @@ -236,7 +530,7 @@ ReturnedValue ArrayPrototype::method_findIndex(const FunctionObject *b, const Va ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); for (uint k = 0; k < len; ++k) { - arguments[0] = instance->getIndexed(k); + arguments[0] = instance->get(k); CHECK_EXCEPTION(); arguments[1] = Primitive::fromDouble(k); @@ -282,7 +576,7 @@ ReturnedValue ArrayPrototype::method_join(const FunctionObject *b, const Value * if (i) R += r4; - e = a->getIndexed(i); + e = a->get(i); CHECK_EXCEPTION(); if (!e->isNullOrUndefined()) R += e->toQString(); @@ -327,10 +621,10 @@ ReturnedValue ArrayPrototype::method_pop(const FunctionObject *b, const Value *t RETURN_UNDEFINED(); } - ScopedValue result(scope, instance->getIndexed(len - 1)); + ScopedValue result(scope, instance->get(len - 1)); CHECK_EXCEPTION(); - if (!instance->deleteIndexedProperty(len - 1)) + if (!instance->deleteProperty(PropertyKey::fromArrayIndex(len - 1))) return scope.engine->throwTypeError(); if (instance->isArrayObject()) @@ -352,9 +646,9 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value * instance->arrayCreate(); Q_ASSERT(instance->arrayData()); - uint len = instance->getLength(); + qint64 len = instance->getLength(); - if (len + argc < len) { + if (len + quint64(argc) >= UINT_MAX) { // ughh... this goes beyond UINT_MAX double l = len; ScopedString s(scope); @@ -381,7 +675,7 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value * len = instance->arrayData()->length(); } else { for (int i = 0, ei = argc; i < ei; ++i) { - if (!instance->putIndexed(len + i, argv[i])) + if (!instance->put(len + i, argv[i])) return scope.engine->throwTypeError(); } len += argc; @@ -393,7 +687,7 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value * return scope.engine->throwTypeError(); } - return Encode(len); + return Encode(uint(len)); } ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -403,7 +697,10 @@ ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Valu if (!instance) RETURN_UNDEFINED(); - uint length = instance->getLength(); + qint64 length = instance->getLength(); + // ### FIXME + if (length >= UINT_MAX) + return scope.engine->throwRangeError(QLatin1String("Array.prototype.reverse: Length out of range.")); int lo = 0, hi = length - 1; @@ -411,19 +708,19 @@ ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Valu ScopedValue hval(scope); for (; lo < hi; ++lo, --hi) { bool loExists, hiExists; - lval = instance->getIndexed(lo, &loExists); - hval = instance->getIndexed(hi, &hiExists); + lval = instance->get(lo, &loExists); + hval = instance->get(hi, &hiExists); CHECK_EXCEPTION(); bool ok; if (hiExists) - ok = instance->putIndexed(lo, hval); + ok = instance->put(lo, hval); else - ok = instance->deleteIndexedProperty(lo); + ok = instance->deleteProperty(PropertyKey::fromArrayIndex(lo)); if (ok) { if (loExists) - ok = instance->putIndexed(hi, lval); + ok = instance->put(hi, lval); else - ok = instance->deleteIndexedProperty(hi); + ok = instance->deleteProperty(PropertyKey::fromArrayIndex(hi)); } if (!ok) return scope.engine->throwTypeError(); @@ -454,23 +751,23 @@ ReturnedValue ArrayPrototype::method_shift(const FunctionObject *b, const Value if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len && instance->arrayData()->type != Heap::ArrayData::Custom) { result = instance->arrayData()->vtable()->pop_front(instance); } else { - result = instance->getIndexed(0); + result = instance->get(uint(0)); CHECK_EXCEPTION(); ScopedValue v(scope); // do it the slow way for (uint k = 1; k < len; ++k) { bool exists; - v = instance->getIndexed(k, &exists); + v = instance->get(k, &exists); CHECK_EXCEPTION(); bool ok; if (exists) - ok = instance->putIndexed(k - 1, v); + ok = instance->put(k - 1, v); else - ok = instance->deleteIndexedProperty(k - 1); + ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k - 1)); if (!ok) return scope.engine->throwTypeError(); } - bool ok = instance->deleteIndexedProperty(len - 1); + bool ok = instance->deleteProperty(PropertyKey::fromArrayIndex(len - 1)); if (!ok) return scope.engine->throwTypeError(); } @@ -518,7 +815,7 @@ ReturnedValue ArrayPrototype::method_slice(const FunctionObject *b, const Value uint n = 0; for (uint i = start; i < end; ++i) { bool exists; - v = o->getIndexed(i, &exists); + v = o->get(i, &exists); CHECK_EXCEPTION(); if (exists) result->arraySet(n, v); @@ -548,60 +845,71 @@ ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value if (!instance) RETURN_UNDEFINED(); - uint len = instance->getLength(); - - ScopedArrayObject newArray(scope, scope.engine->newArrayObject()); + qint64 len = instance->getLength(); double rs = (argc ? argv[0] : Primitive::undefinedValue()).toInteger(); - uint start; + qint64 start; if (rs < 0) - start = (uint) qMax(0., len + rs); + start = static_cast<qint64>(qMax(0., len + rs)); else - start = (uint) qMin(rs, (double)len); + start = static_cast<qint64>(qMin(rs, static_cast<double>(len))); + + qint64 deleteCount = 0; + qint64 itemCount = 0; + if (argc == 1) { + deleteCount = len - start; + } else if (argc > 1){ + itemCount = argc - 2; + double dc = argv[1].toInteger(); + deleteCount = static_cast<qint64>(qMin(qMax(dc, 0.), double(len - start))); + } - uint deleteCount = (uint)qMin(qMax((argc > 1 ? argv[1] : Primitive::undefinedValue()).toInteger(), 0.), (double)(len - start)); + if (len + itemCount - deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1) + return scope.engine->throwTypeError(); + if (deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1) + return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range.")); + ScopedArrayObject newArray(scope, scope.engine->newArrayObject()); newArray->arrayReserve(deleteCount); ScopedValue v(scope); for (uint i = 0; i < deleteCount; ++i) { bool exists; - v = instance->getIndexed(start + i, &exists); + v = instance->get(start + i, &exists); CHECK_EXCEPTION(); if (exists) newArray->arrayPut(i, v); } newArray->setArrayLengthUnchecked(deleteCount); - uint itemCount = argc < 2 ? 0 : argc - 2; if (itemCount < deleteCount) { for (uint k = start; k < len - deleteCount; ++k) { bool exists; - v = instance->getIndexed(k + deleteCount, &exists); + v = instance->get(k + deleteCount, &exists); CHECK_EXCEPTION(); bool ok; if (exists) - ok = instance->putIndexed(k + itemCount, v); + ok = instance->put(k + itemCount, v); else - ok = instance->deleteIndexedProperty(k + itemCount); + ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + itemCount)); if (!ok) return scope.engine->throwTypeError(); } for (uint k = len; k > len - deleteCount + itemCount; --k) { - if (!instance->deleteIndexedProperty(k - 1)) + if (!instance->deleteProperty(PropertyKey::fromArrayIndex(k - 1))) return scope.engine->throwTypeError(); } } else if (itemCount > deleteCount) { uint k = len - deleteCount; while (k > start) { bool exists; - v = instance->getIndexed(k + deleteCount - 1, &exists); + v = instance->get(k + deleteCount - 1, &exists); CHECK_EXCEPTION(); bool ok; if (exists) - ok = instance->putIndexed(k + itemCount - 1, v); + ok = instance->put(k + itemCount - 1, v); else - ok = instance->deleteIndexedProperty(k + itemCount - 1); + ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + itemCount - 1)); if (!ok) return scope.engine->throwTypeError(); --k; @@ -609,7 +917,7 @@ ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value } for (uint i = 0; i < itemCount; ++i) - instance->putIndexed(start + i, argv[i + 2]); + instance->put(start + i, argv[i + 2]); if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount)))) return scope.engine->throwTypeError(); @@ -636,17 +944,17 @@ ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Valu ScopedValue v(scope); for (uint k = len; k > 0; --k) { bool exists; - v = instance->getIndexed(k - 1, &exists); + v = instance->get(k - 1, &exists); bool ok; if (exists) - ok = instance->putIndexed(k + argc - 1, v); + ok = instance->put(k + argc - 1, v); else - ok = instance->deleteIndexedProperty(k + argc - 1); + ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + argc - 1)); if (!ok) return scope.engine->throwTypeError(); } for (int i = 0, ei = argc; i < ei; ++i) { - bool ok = instance->putIndexed(i, argv[i]); + bool ok = instance->put(i, argv[i]); if (!ok) return scope.engine->throwTypeError(); } @@ -663,6 +971,44 @@ ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Valu return Encode(newLen); } +ReturnedValue ArrayPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); + if (!instance) + RETURN_UNDEFINED(); + + qint64 len = instance->getLength(); + if (len == 0) { + return Encode(false); + } + + double n = 0; + if (argc > 1 && !argv[1].isUndefined()) { + n = argv[1].toInteger(); + } + + double k = 0; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) { + k = 0; + } + } + + while (k < len) { + ScopedValue val(scope, instance->get(k)); + if (val->sameValueZero(argv[0])) { + return Encode(true); + } + k++; + } + + return Encode(false); +} + ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); @@ -691,7 +1037,7 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu ScopedValue v(scope); for (uint k = fromIndex; k < len; ++k) { bool exists; - v = instance->getIndexed(k, &exists); + v = instance->get(k, &exists); if (exists && RuntimeHelpers::strictEqual(v, searchValue)) return Encode(k); } @@ -705,7 +1051,7 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu // lets be safe and slow for (uint i = fromIndex; i < len; ++i) { bool exists; - value = instance->getIndexed(i, &exists); + value = instance->get(i, &exists); CHECK_EXCEPTION(); if (exists && RuntimeHelpers::strictEqual(value, searchValue)) return Encode(i); @@ -729,6 +1075,18 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu return Encode(-1); } +ReturnedValue ArrayPrototype::method_keys(const FunctionObject *f, const Value *thisObject, const Value *, int) +{ + Scope scope(f); + ScopedObject O(scope, thisObject->toObject(scope.engine)); + if (!O) + RETURN_UNDEFINED(); + + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + ao->d()->iterationKind = IteratorKind::KeyIteratorKind; + return ao->asReturnedValue(); +} + ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); @@ -765,7 +1123,7 @@ ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const for (uint k = fromIndex; k > 0;) { --k; bool exists; - v = instance->getIndexed(k, &exists); + v = instance->get(k, &exists); CHECK_EXCEPTION(); if (exists && RuntimeHelpers::strictEqual(v, searchValue)) return Encode(k); @@ -793,7 +1151,7 @@ ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value bool ok = true; for (uint k = 0; ok && k < len; ++k) { bool exists; - arguments[0] = instance->getIndexed(k, &exists); + arguments[0] = instance->get(k, &exists); if (!exists) continue; @@ -805,6 +1163,42 @@ ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value return Encode(ok); } +ReturnedValue ArrayPrototype::method_fill(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); + if (!instance) + RETURN_UNDEFINED(); + + uint len = instance->getLength(); + int relativeStart = argc > 1 ? argv[1].toInteger() : 0; + int relativeEnd = len; + if (argc > 2 && !argv[2].isUndefined()) { + relativeEnd = argv[2].toInteger(); + } + uint k = 0; + uint fin = 0; + + if (relativeStart < 0) { + k = std::max(len+relativeStart, uint(0)); + } else { + k = std::min(uint(relativeStart), len); + } + + if (relativeEnd < 0) { + fin = std::max(len + relativeEnd, uint(0)); + } else { + fin = std::min(uint(relativeEnd), len); + } + + while (k < fin) { + instance->setIndexed(k, argv[0], QV4::Object::DoThrowOnRejection); + k++; + } + + return instance.asReturnedValue(); +} + ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); @@ -824,7 +1218,7 @@ ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value * for (uint k = 0; k < len; ++k) { bool exists; - arguments[0] = instance->getIndexed(k, &exists); + arguments[0] = instance->get(k, &exists); if (!exists) continue; @@ -855,7 +1249,7 @@ ReturnedValue ArrayPrototype::method_forEach(const FunctionObject *b, const Valu for (uint k = 0; k < len; ++k) { bool exists; - arguments[0] = instance->getIndexed(k, &exists); + arguments[0] = instance->get(k, &exists); if (!exists) continue; @@ -873,12 +1267,15 @@ ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *t if (!instance) RETURN_UNDEFINED(); - uint len = instance->getLength(); + qint64 len = instance->getLength(); if (!argc || !argv->isFunctionObject()) THROW_TYPE_ERROR(); const FunctionObject *callback = static_cast<const FunctionObject *>(argv); + if (len > UINT_MAX - 1) + return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range.")); + ScopedArrayObject a(scope, scope.engine->newArrayObject()); a->arrayReserve(len); a->setArrayLengthUnchecked(len); @@ -890,7 +1287,7 @@ ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *t for (uint k = 0; k < len; ++k) { bool exists; - arguments[0] = instance->getIndexed(k, &exists); + arguments[0] = instance->get(k, &exists); if (!exists) continue; @@ -925,7 +1322,7 @@ ReturnedValue ArrayPrototype::method_filter(const FunctionObject *b, const Value uint to = 0; for (uint k = 0; k < len; ++k) { bool exists; - arguments[0] = instance->getIndexed(k, &exists); + arguments[0] = instance->get(k, &exists); if (!exists) continue; @@ -962,7 +1359,7 @@ ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value } else { bool kPresent = false; while (k < len && !kPresent) { - v = instance->getIndexed(k, &kPresent); + v = instance->get(k, &kPresent); if (kPresent) acc = v; ++k; @@ -975,7 +1372,7 @@ ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value while (k < len) { bool kPresent; - v = instance->getIndexed(k, &kPresent); + v = instance->get(k, &kPresent); if (kPresent) { arguments[0] = acc; arguments[1] = v; @@ -1015,7 +1412,7 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const } else { bool kPresent = false; while (k > 0 && !kPresent) { - v = instance->getIndexed(k - 1, &kPresent); + v = instance->get(k - 1, &kPresent); if (kPresent) acc = v; --k; @@ -1028,7 +1425,7 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const while (k > 0) { bool kPresent; - v = instance->getIndexed(k - 1, &kPresent); + v = instance->get(k - 1, &kPresent); if (kPresent) { arguments[0] = acc; arguments[1] = v; @@ -1041,3 +1438,20 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const return acc->asReturnedValue(); } +ReturnedValue ArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + ScopedObject O(scope, thisObject->toObject(scope.engine)); + if (!O) + RETURN_UNDEFINED(); + + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + ao->d()->iterationKind = IteratorKind::ValueIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue ArrayPrototype::method_get_species(const FunctionObject *, const Value *thisObject, const Value *, int) +{ + return thisObject->asReturnedValue(); +} + diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index 3825a600a2..04ec7e1607 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -70,8 +70,8 @@ struct ArrayCtor: FunctionObject { V4_OBJECT2(ArrayCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct ArrayPrototype: ArrayObject @@ -79,9 +79,13 @@ struct ArrayPrototype: ArrayObject void init(ExecutionEngine *engine, Object *ctor); static ReturnedValue method_isArray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_from(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_of(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_concat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_copyWithin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_find(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_findIndex(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_join(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); @@ -93,15 +97,23 @@ struct ArrayPrototype: ArrayObject static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_splice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_unshift(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_includes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_every(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_fill(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_some(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_map(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_filter(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_reduce(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_reduceRight(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + // while this function is implemented here, it's the same for many other JS classes, so the corresponding JS function + // is instantiated in the engine, and it can be added to any JS object through Object::addSymbolSpecies() + static ReturnedValue method_get_species(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index eb83f902db..f00abad871 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -50,13 +50,13 @@ void Heap::BooleanCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Boolean")); } -ReturnedValue BooleanCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc) +ReturnedValue BooleanCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *) { bool n = argc ? argv[0].toBoolean() : false; return Encode(that->engine()->newBooleanObject(n)); } -ReturnedValue BooleanCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc) +ReturnedValue BooleanCtor::virtualCall(const FunctionObject *, const Value *, const Value *argv, int argc) { bool value = argc ? argv[0].toBoolean() : 0; return Encode(value); @@ -66,7 +66,7 @@ void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString); diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h index 3cf09b2667..276ec8393b 100644 --- a/src/qml/jsruntime/qv4booleanobject_p.h +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -70,8 +70,8 @@ struct BooleanCtor: FunctionObject { V4_OBJECT2(BooleanCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct BooleanPrototype: BooleanObject diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 020e519e74..bb08d2786d 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -48,12 +48,48 @@ #include "qv4errorobject_p.h" #include "qv4string_p.h" #include "qv4qmlcontext_p.h" +#include "qv4stackframe_p.h" using namespace QV4; DEFINE_MANAGED_VTABLE(ExecutionContext); DEFINE_MANAGED_VTABLE(CallContext); -DEFINE_MANAGED_VTABLE(CatchContext); + +Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int blockIndex) +{ + Function *function = frame->v4Function; + + Heap::InternalClass *ic = function->compilationUnit->runtimeBlocks.at(blockIndex); + uint nLocals = ic->size; + size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals; + + ExecutionEngine *v4 = function->internalClass->engine; + Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, ic); + c->init(); + c->type = Heap::ExecutionContext::Type_BlockContext; + + Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m()); + c->outer.set(v4, outer); + c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m())); + + c->locals.size = nLocals; + c->locals.alloc = nLocals; + + return c; +} + +Heap::CallContext *ExecutionContext::cloneBlockContext(Heap::CallContext *context) +{ + uint nLocals = context->locals.alloc; + size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals; + + ExecutionEngine *v4 = context->internalClass->engine; + Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, context->internalClass); + memcpy(c, context, requiredMemory); + + return c; + +} Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) { @@ -75,7 +111,7 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) uint nLocals = compiledFunction->nLocals; c->locals.size = nLocals; c->locals.alloc = localsAndFormals; - // memory allocated from the JS heap is 0 initialized, so check if undefined is 0 + // memory allocated from the JS heap is 0 initialized, so check if empty is 0 Q_ASSERT(Primitive::undefinedValue().asReturnedValue() == 0); Value *args = c->locals.values + nLocals; @@ -96,11 +132,14 @@ Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with) return c; } -Heap::CatchContext *ExecutionContext::newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue) +Heap::ExecutionContext *ExecutionContext::newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName) { - Scope scope(this); - ScopedValue e(scope, exceptionValue); - return engine()->memoryManager->alloc<CatchContext>(d(), exceptionVarName, e); + Scope scope(frame->context()); + ScopedString name(scope, exceptionVarName); + ScopedValue val(scope, scope.engine->catchException(nullptr)); + ScopedContext ctx(scope, newBlockContext(frame, blockIndex)); + ctx->setProperty(name, val); + return ctx->d(); } void ExecutionContext::createMutableBinding(String *name, bool deletable) @@ -132,44 +171,32 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) activation = ctx->d()->activation; break; } + case Heap::ExecutionContext::Type_BlockContext: + // never create activation records on block contexts default: break; } ctx = ctx->d()->outer; } - if (activation->hasOwnProperty(name)) + PropertyKey id = name->toPropertyKey(); + if (activation->getOwnProperty(id) != Attr_Invalid) return; ScopedProperty desc(scope); PropertyAttributes attrs(Attr_Data); attrs.setConfigurable(deletable); - activation->__defineOwnProperty__(scope.engine, name, desc, attrs); -} - -void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName, - const Value &exceptionValue) -{ - Heap::ExecutionContext::init(Heap::ExecutionContext::Type_CatchContext); - outer.set(internalClass->engine, outerContext); - - this->exceptionVarName.set(internalClass->engine, exceptionVarName); - this->exceptionValue.set(internalClass->engine, exceptionValue); + if (!activation->defineOwnProperty(id, desc, attrs)) + scope.engine->throwTypeError(); } bool ExecutionContext::deleteProperty(String *name) { - name->makeIdentifier(); - Identifier *id = name->identifier(); + PropertyKey id = name->toPropertyKey(); Heap::ExecutionContext *ctx = d(); for (; ctx; ctx = ctx->outer) { switch (ctx->type) { - case Heap::ExecutionContext::Type_CatchContext: { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); - if (c->exceptionVarName->isEqualTo(name->d())) - return false; - break; - } + case Heap::ExecutionContext::Type_BlockContext: case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); uint index = c->internalClass->find(id); @@ -183,8 +210,8 @@ bool ExecutionContext::deleteProperty(String *name) if (ctx->activation) { Scope scope(this); ScopedObject object(scope, ctx->activation); - if (object && object->hasProperty(name)) - return object->deleteProperty(name); + if (object && object->hasProperty(name->toPropertyKey())) + return object->deleteProperty(name->toPropertyKey()); } break; } @@ -199,32 +226,24 @@ bool ExecutionContext::deleteProperty(String *name) ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value &value) { - name->makeIdentifier(); - Identifier *id = name->identifier(); + PropertyKey id = name->toPropertyKey(); QV4::ExecutionEngine *v4 = engine(); Heap::ExecutionContext *ctx = d(); for (; ctx; ctx = ctx->outer) { switch (ctx->type) { - case Heap::ExecutionContext::Type_CatchContext: { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); - if (c->exceptionVarName->isEqualTo(name->d())) { - c->exceptionValue.set(v4, value); - return NoError; - } - break; - } case Heap::ExecutionContext::Type_WithContext: { Scope scope(v4); ScopedObject w(scope, ctx->activation); - if (w->hasProperty(name)) { + if (w->hasProperty(name->toPropertyKey())) { if (!w->put(name, value)) return TypeError; return NoError; } break; } + case Heap::ExecutionContext::Type_BlockContext: case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); uint index = c->internalClass->find(id); @@ -262,20 +281,14 @@ ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value ReturnedValue ExecutionContext::getProperty(String *name) { - name->makeIdentifier(); + PropertyKey id = name->toPropertyKey(); Heap::ExecutionContext *ctx = d(); for (; ctx; ctx = ctx->outer) { switch (ctx->type) { - case Heap::ExecutionContext::Type_CatchContext: { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); - if (c->exceptionVarName->isEqualTo(name->d())) - return c->exceptionValue.asReturnedValue(); - break; - } + case Heap::ExecutionContext::Type_BlockContext: case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); - Identifier *id = name->identifier(); uint index = c->internalClass->find(id); if (index < UINT_MAX) @@ -289,7 +302,7 @@ ReturnedValue ExecutionContext::getProperty(String *name) Scope scope(this); ScopedObject activation(scope, ctx->activation); bool hasProperty = false; - ReturnedValue v = activation->get(name, &hasProperty); + ReturnedValue v = activation->get(id, nullptr, &hasProperty); if (hasProperty) return v; } @@ -303,21 +316,14 @@ ReturnedValue ExecutionContext::getProperty(String *name) ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) { base->setM(nullptr); - name->makeIdentifier(); + PropertyKey id = name->toPropertyKey(); Heap::ExecutionContext *ctx = d(); for (; ctx; ctx = ctx->outer) { switch (ctx->type) { - case Heap::ExecutionContext::Type_CatchContext: { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); - if (c->exceptionVarName->isEqualTo(name->d())) - return c->exceptionValue.asReturnedValue(); - break; - } + case Heap::ExecutionContext::Type_BlockContext: case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); - name->makeIdentifier(); - Identifier *id = name->identifier(); uint index = c->internalClass->find(id); if (index < UINT_MAX) @@ -340,7 +346,7 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) Scope scope(this); ScopedObject o(scope, ctx->activation); bool hasProperty = false; - ReturnedValue v = o->get(name, &hasProperty); + ReturnedValue v = o->get(id, nullptr, &hasProperty); if (hasProperty) { base->setM(o->d()); return v; diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 512bfa06d8..fbb4168e9b 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -55,66 +55,11 @@ QT_BEGIN_NAMESPACE -class QObject; -class QQmlContextData; - namespace QV4 { -namespace CompiledData { -struct CompilationUnitBase; -struct Function; -} - -struct Function; -struct Identifier; -struct CallContext; -struct CatchContext; -struct QmlContext; -struct QQmlContextWrapper; - -struct CallData -{ - enum Offsets { - Function = 0, - Context = 1, - Accumulator = 2, - This = 3, - Argc = 4 - }; - - Value function; - Value context; - Value accumulator; - Value thisObject; - Value _argc; - - int argc() const { - Q_ASSERT(_argc.isInteger()); - return _argc.int_32(); - } - - void setArgc(int argc) { - Q_ASSERT(argc >= 0); - _argc.setInt_32(argc); - } - - inline ReturnedValue argument(int i) const { - return i < argc() ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); - } - - Value args[1]; - - static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); } -}; - -Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value); -Q_STATIC_ASSERT(offsetof(CallData, thisObject) == CallData::This*sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, args) == 5*sizeof(Value)); namespace Heap { -struct QmlContext; - #define ExecutionContextMembers(class, Member) \ Member(class, Pointer, ExecutionContext *, outer) \ Member(class, Pointer, Object *, activation) @@ -124,9 +69,9 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) { enum ContextType { Type_GlobalContext = 0x1, - Type_CatchContext = 0x2, - Type_WithContext = 0x3, - Type_QmlContext = 0x4, + Type_WithContext = 0x2, + Type_QmlContext = 0x3, + Type_BlockContext = 0x4, Type_CallContext = 0x5 }; @@ -137,6 +82,10 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) { type = t; } + const VTable *vtable() const { + return internalClass->vtable; + } + quint32 type : 8; quint32 nArgs : 24; #if QT_POINTER_SIZE == 8 @@ -181,16 +130,6 @@ Q_STATIC_ASSERT(offsetof(CallContextData, function) == 0); //Q_STATIC_ASSERT(sizeof(CallContext) == sizeof(ExecutionContext) + sizeof(CallContextData)); //#endif -#define CatchContextMembers(class, Member) \ - Member(class, Pointer, String *, exceptionVarName) \ - Member(class, HeapValue, HeapValue, exceptionValue) - -DECLARE_HEAP_OBJECT(CatchContext, ExecutionContext) { - DECLARE_MARKOBJECTS(CatchContext); - - void init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue); -}; -Q_STATIC_ASSERT(std::is_trivial< CatchContext >::value); } @@ -204,9 +143,11 @@ struct Q_QML_EXPORT ExecutionContext : public Managed Q_MANAGED_TYPE(ExecutionContext) V4_INTERNALCLASS(ExecutionContext) + static Heap::CallContext *newBlockContext(QV4::CppStackFrame *frame, int blockIndex); + static Heap::CallContext *cloneBlockContext(Heap::CallContext *context); static Heap::CallContext *newCallContext(QV4::CppStackFrame *frame); Heap::ExecutionContext *newWithContext(Heap::Object *with); - Heap::CatchContext *newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue); + static Heap::ExecutionContext *newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName); void createMutableBinding(String *name, bool deletable); @@ -224,6 +165,12 @@ struct Q_QML_EXPORT ExecutionContext : public Managed inline CallContext *asCallContext(); inline const CallContext *asCallContext() const; + +protected: + // vtable method required for compilation + static bool virtualDeleteProperty(Managed *, PropertyKey) { + Q_UNREACHABLE(); + } }; struct Q_QML_EXPORT CallContext : public ExecutionContext @@ -239,11 +186,6 @@ struct Q_QML_EXPORT CallContext : public ExecutionContext } }; -struct CatchContext : public ExecutionContext -{ - V4_MANAGED(CatchContext, ExecutionContext) -}; - inline CallContext *ExecutionContext::asCallContext() { return d()->type == Heap::ExecutionContext::Type_CallContext ? static_cast<CallContext *>(this) : nullptr; diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index d894d909ff..d550e559d3 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -40,6 +40,7 @@ #include "qv4dataview_p.h" #include "qv4arraybuffer_p.h" #include "qv4string_p.h" +#include "qv4symbol_p.h" #include <QtCore/private/qnumeric_p.h> #include "qendian.h" @@ -54,7 +55,7 @@ void Heap::DataViewCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("DataView")); } -ReturnedValue DataViewCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue DataViewCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Scope scope(f->engine()); Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Primitive::undefinedValue()); @@ -69,54 +70,57 @@ ReturnedValue DataViewCtor::callAsConstructor(const FunctionObject *f, const Val if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); - Scoped<DataView> a(scope, scope.engine->memoryManager->allocObject<DataView>()); + Scoped<DataView> a(scope, scope.engine->memoryManager->allocate<DataView>()); a->d()->buffer.set(scope.engine, buffer->d()); a->d()->byteLength = byteLength; a->d()->byteOffset = byteOffset; return a.asReturnedValue(); } -ReturnedValue DataViewCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue DataViewCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) { - return callAsConstructor(f, argv, argc); + return virtualCallAsConstructor(f, argv, argc, f); } void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(3)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); defineDefaultProperty(engine->id_constructor(), (o = ctor)); defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr); defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr); defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr); - defineDefaultProperty(QStringLiteral("getInt8"), method_getChar<signed char>, 0); - defineDefaultProperty(QStringLiteral("getUint8"), method_getChar<unsigned char>, 0); - defineDefaultProperty(QStringLiteral("getInt16"), method_get<short>, 0); - defineDefaultProperty(QStringLiteral("getUint16"), method_get<unsigned short>, 0); - defineDefaultProperty(QStringLiteral("getInt32"), method_get<int>, 0); - defineDefaultProperty(QStringLiteral("getUint32"), method_get<unsigned int>, 0); - defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat<float>, 0); - defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat<double>, 0); - - defineDefaultProperty(QStringLiteral("setInt8"), method_setChar<signed char>, 0); - defineDefaultProperty(QStringLiteral("setUint8"), method_setChar<unsigned char>, 0); - defineDefaultProperty(QStringLiteral("setInt16"), method_set<short>, 0); - defineDefaultProperty(QStringLiteral("setUint16"), method_set<unsigned short>, 0); - defineDefaultProperty(QStringLiteral("setInt32"), method_set<int>, 0); - defineDefaultProperty(QStringLiteral("setUint32"), method_set<unsigned int>, 0); - defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat<float>, 0); - defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat<double>, 0); + defineDefaultProperty(QStringLiteral("getInt8"), method_getChar<signed char>, 1); + defineDefaultProperty(QStringLiteral("getUint8"), method_getChar<unsigned char>, 1); + defineDefaultProperty(QStringLiteral("getInt16"), method_get<short>, 1); + defineDefaultProperty(QStringLiteral("getUint16"), method_get<unsigned short>, 1); + defineDefaultProperty(QStringLiteral("getInt32"), method_get<int>, 1); + defineDefaultProperty(QStringLiteral("getUint32"), method_get<unsigned int>, 1); + defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat<float>, 1); + defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat<double>, 1); + + defineDefaultProperty(QStringLiteral("setInt8"), method_setChar<signed char>, 2); + defineDefaultProperty(QStringLiteral("setUint8"), method_setChar<unsigned char>, 2); + defineDefaultProperty(QStringLiteral("setInt16"), method_set<short>, 2); + defineDefaultProperty(QStringLiteral("setUint16"), method_set<unsigned short>, 2); + defineDefaultProperty(QStringLiteral("setInt32"), method_set<int>, 2); + defineDefaultProperty(QStringLiteral("setUint32"), method_set<unsigned int>, 2); + defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat<float>, 2); + defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat<double>, 2); + + ScopedString name(scope, engine->newString(QStringLiteral("DataView"))); + defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name); // For backword compatibility - defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 0); - defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 0); - defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 0); - defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 0); - defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 0); - defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 0); + defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 1); + defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 1); + defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 1); + defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 1); + defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 1); + defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 1); } ReturnedValue DataViewPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int) diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 1e07d85118..6a9f865c0f 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -79,8 +79,8 @@ struct DataViewCtor: FunctionObject { V4_OBJECT2(DataViewCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct DataView : Object diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 5bbe312146..3efd626685 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -44,6 +44,7 @@ #include "qv4runtime_p.h" #include "qv4string_p.h" #include "qv4jscall_p.h" +#include "qv4symbol_p.h" #include <QtCore/QDebug> #include <QtCore/QDateTime> @@ -89,9 +90,6 @@ static const double msPerMinute = 60000.0; static const double msPerHour = 3600000.0; static const double msPerDay = 86400000.0; -// The current *standard* time offset, regardless of DST: -static double LocalTZA = 0.0; // initialized at startup - static inline double TimeWithinDay(double t) { double r = ::fmod(t, msPerDay); @@ -318,14 +316,14 @@ static inline double MakeDate(double day, double time) against the ECMAScript spec is https://github.com/tc39/ecma262/issues/725 */ -static inline double DaylightSavingTA(double t) // t is a UTC time +static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time { return QTimeZone::systemTimeZone().offsetFromUtc( - QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - LocalTZA; + QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - localTZA; } #else // This implementation fails to take account of past changes in standard offset. -static inline double DaylightSavingTA(double t) +static inline double DaylightSavingTA(double t, double /*localTZA*/) { struct tm tmtm; #if defined(Q_CC_MSVC) @@ -348,19 +346,19 @@ static inline double DaylightSavingTA(double t) } #endif // USE_QTZ_SYSTEM_ZONE -static inline double LocalTime(double t) +static inline double LocalTime(double t, double localTZA) { // Flawed, yet verbatim from the spec: - return t + LocalTZA + DaylightSavingTA(t); + return t + localTZA + DaylightSavingTA(t, localTZA); } // The spec does note [*] that UTC and LocalTime are not quite mutually inverse. // [*] http://www.ecma-international.org/ecma-262/7.0/index.html#sec-utc-t -static inline double UTC(double t) +static inline double UTC(double t, double localTZA) { // Flawed, yet verbatim from the spec: - return t - LocalTZA - DaylightSavingTA(t - LocalTZA); + return t - localTZA - DaylightSavingTA(t - localTZA, localTZA); } static inline double currentTime() @@ -377,7 +375,7 @@ static inline double TimeClip(double t) return Primitive::toInteger(t) + 0; } -static inline double ParseString(const QString &s) +static inline double ParseString(const QString &s, double localTZA) { /* First, try the format defined in ECMA 262's "Date Time String Format"; @@ -533,9 +531,9 @@ static inline double ParseString(const QString &s) if (seenZ) t -= offset * offsetSign * 60 * 1000; else if (seenT) // No zone specified, treat date-time as local time - t = UTC(t); + t = UTC(t, localTZA); // else: treat plain date as already in UTC - return t; + return TimeClip(t); } QDateTime dt = QDateTime::fromString(s, Qt::TextDate); @@ -605,7 +603,7 @@ static inline double ParseString(const QString &s) } if (!dt.isValid()) return qt_qnan(); - return dt.toMSecsSinceEpoch(); + return TimeClip(dt.toMSecsSinceEpoch()); } /*! @@ -621,12 +619,12 @@ static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) return QDateTime::fromMSecsSinceEpoch(t, Qt::UTC).toTimeSpec(spec); } -static inline QString ToString(double t) +static inline QString ToString(double t, double localTZA) { if (std::isnan(t)) return QStringLiteral("Invalid Date"); QString str = ToDateTime(t, Qt::LocalTime).toString() + QLatin1String(" GMT"); - double tzoffset = LocalTZA + DaylightSavingTA(t); + double tzoffset = localTZA + DaylightSavingTA(t, localTZA); if (tzoffset) { int hours = static_cast<int>(::fabs(tzoffset) / 1000 / 60 / 60); int mins = int(::fabs(tzoffset) / 1000 / 60) % 60; @@ -705,7 +703,7 @@ DEFINE_OBJECT_VTABLE(DateObject); void Heap::DateObject::init(const QDateTime &date) { Object::init(); - this->date = date.isValid() ? date.toMSecsSinceEpoch() : qt_qnan(); + this->date = date.isValid() ? TimeClip(date.toMSecsSinceEpoch()) : qt_qnan(); } void Heap::DateObject::init(const QTime &time) @@ -730,7 +728,7 @@ void Heap::DateObject::init(const QTime &time) */ static const double d = MakeDay(1925, 5, 8); double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec()); - date = TimeClip(UTC(MakeDate(d, t))); + date = TimeClip(UTC(MakeDate(d, t), internalClass->engine->localTZA)); } QDateTime DateObject::toQDateTime() const @@ -745,15 +743,16 @@ void Heap::DateCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Date")); } -ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc) +ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *) { + ExecutionEngine *e = that->engine(); double t = 0; if (argc == 0) t = currentTime(); else if (argc == 1) { - Scope scope(that->engine()); + Scope scope(e); ScopedValue arg(scope, argv[0]); if (DateObject *d = arg->as<DateObject>()) { t = d->date(); @@ -761,7 +760,7 @@ ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Valu arg = RuntimeHelpers::toPrimitive(arg, PREFERREDTYPE_HINT); if (String *s = arg->stringValue()) - t = ParseString(s->toQString()); + t = ParseString(s->toQString(), e->localTZA); else t = TimeClip(arg->toNumber()); } @@ -778,16 +777,17 @@ ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Valu if (year >= 0 && year <= 99) year += 1900; t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); - t = TimeClip(UTC(t)); + t = TimeClip(UTC(t, e->localTZA)); } - return Encode(that->engine()->newDateObject(Primitive::fromDouble(t))); + return Encode(e->newDateObject(Primitive::fromDouble(t))); } -ReturnedValue DateCtor::call(const FunctionObject *m, const Value *, const Value *, int) +ReturnedValue DateCtor::virtualCall(const FunctionObject *m, const Value *, const Value *, int) { + ExecutionEngine *e = m->engine(); double t = currentTime(); - return m->engine()->newString(ToString(t))->asReturnedValue(); + return e->newString(ToString(t, e->localTZA))->asReturnedValue(); } void DatePrototype::init(ExecutionEngine *engine, Object *ctor) @@ -796,7 +796,7 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) ScopedObject o(scope); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(7)); - LocalTZA = getLocalTZA(); + engine->localTZA = getLocalTZA(); ctor->defineDefaultProperty(QStringLiteral("parse"), method_parse, 1); ctor->defineDefaultProperty(QStringLiteral("UTC"), method_UTC, 7); @@ -853,15 +853,14 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) QString toGmtString(QStringLiteral("toGMTString")); ScopedString us(scope, engine->newIdentifier(toUtcString)); ScopedString gs(scope, engine->newIdentifier(toGmtString)); - ExecutionContext *global = engine->rootContext(); - ScopedFunctionObject toUtcGmtStringFn(scope, FunctionObject::createBuiltinFunction(global, us, method_toUTCString)); - toUtcGmtStringFn->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + ScopedFunctionObject toUtcGmtStringFn(scope, FunctionObject::createBuiltinFunction(engine, us, method_toUTCString, 0)); defineDefaultProperty(us, toUtcGmtStringFn); defineDefaultProperty(gs, toUtcGmtStringFn); } defineDefaultProperty(QStringLiteral("toISOString"), method_toISOString, 0); defineDefaultProperty(QStringLiteral("toJSON"), method_toJSON, 1); + defineDefaultProperty(engine->symbol_toPrimitive(), method_symbolToPrimitive, 1, Attr_ReadOnly_ButConfigurable); } double DatePrototype::getThisDate(ExecutionEngine *v4, const Value *thisObject) @@ -872,12 +871,12 @@ double DatePrototype::getThisDate(ExecutionEngine *v4, const Value *thisObject) return 0; } -ReturnedValue DatePrototype::method_parse(const FunctionObject *, const Value *, const Value *argv, int argc) +ReturnedValue DatePrototype::method_parse(const FunctionObject *f, const Value *, const Value *argv, int argc) { if (!argc) return Encode(qt_qnan()); else - return Encode(ParseString(argv[0].toQString())); + return Encode(ParseString(argv[0].toQString(), f->engine()->localTZA)); } ReturnedValue DatePrototype::method_UTC(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -909,7 +908,7 @@ ReturnedValue DatePrototype::method_toString(const FunctionObject *b, const Valu { ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); - return Encode(v4->newString(ToString(t))); + return Encode(v4->newString(ToString(t, v4->localTZA))); } ReturnedValue DatePrototype::method_toDateString(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -966,7 +965,7 @@ ReturnedValue DatePrototype::method_getYear(const FunctionObject *b, const Value ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = YearFromTime(LocalTime(t)) - 1900; + t = YearFromTime(LocalTime(t, v4->localTZA)) - 1900; return Encode(t); } @@ -975,7 +974,7 @@ ReturnedValue DatePrototype::method_getFullYear(const FunctionObject *b, const V ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = YearFromTime(LocalTime(t)); + t = YearFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -993,7 +992,7 @@ ReturnedValue DatePrototype::method_getMonth(const FunctionObject *b, const Valu ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = MonthFromTime(LocalTime(t)); + t = MonthFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1011,7 +1010,7 @@ ReturnedValue DatePrototype::method_getDate(const FunctionObject *b, const Value ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = DateFromTime(LocalTime(t)); + t = DateFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1029,7 +1028,7 @@ ReturnedValue DatePrototype::method_getDay(const FunctionObject *b, const Value ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = WeekDay(LocalTime(t)); + t = WeekDay(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1047,7 +1046,7 @@ ReturnedValue DatePrototype::method_getHours(const FunctionObject *b, const Valu ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = HourFromTime(LocalTime(t)); + t = HourFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1065,7 +1064,7 @@ ReturnedValue DatePrototype::method_getMinutes(const FunctionObject *b, const Va ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = MinFromTime(LocalTime(t)); + t = MinFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1083,7 +1082,7 @@ ReturnedValue DatePrototype::method_getSeconds(const FunctionObject *b, const Va ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = SecFromTime(LocalTime(t)); + t = SecFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1101,7 +1100,7 @@ ReturnedValue DatePrototype::method_getMilliseconds(const FunctionObject *b, con ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = msFromTime(LocalTime(t)); + t = msFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1119,7 +1118,7 @@ ReturnedValue DatePrototype::method_getTimezoneOffset(const FunctionObject *b, c ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = (t - LocalTime(t)) / msPerMinute; + t = (t - LocalTime(t, v4->localTZA)) / msPerMinute; return Encode(t); } @@ -1144,13 +1143,13 @@ ReturnedValue DatePrototype::method_setMilliseconds(const FunctionObject *b, con if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); double ms = argc ? argv[0].toNumber() : qt_qnan(); if (v4->hasException) return QV4::Encode::undefined(); - self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)), v4->localTZA))); return Encode(self->date()); } @@ -1178,7 +1177,7 @@ ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Va if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); double sec = argc ? argv[0].toNumber() : qt_qnan(); @@ -1187,7 +1186,7 @@ ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Va double ms = (argc < 2) ? msFromTime(t) : argv[1].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)), v4->localTZA)); self->setDate(t); return Encode(self->date()); } @@ -1214,7 +1213,7 @@ ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Va if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); double min = argc ? argv[0].toNumber() : qt_qnan(); @@ -1226,7 +1225,7 @@ ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Va double ms = (argc < 3) ? msFromTime(t) : argv[2].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)), v4->localTZA)); self->setDate(t); return Encode(self->date()); } @@ -1254,7 +1253,7 @@ ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Valu if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); double hour = argc ? argv[0].toNumber() : qt_qnan(); @@ -1269,7 +1268,7 @@ ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Valu double ms = (argc < 4) ? msFromTime(t) : argv[3].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)), v4->localTZA)); self->setDate(t); return Encode(self->date()); } @@ -1298,13 +1297,13 @@ ReturnedValue DatePrototype::method_setDate(const FunctionObject *b, const Value if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); double date = argc ? argv[0].toNumber() : qt_qnan(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)), v4->localTZA)); self->setDate(t); return Encode(self->date()); } @@ -1334,7 +1333,7 @@ ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Valu if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); double month = argc ? argv[0].toNumber() : qt_qnan(); @@ -1343,7 +1342,7 @@ ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Valu double date = (argc < 2) ? DateFromTime(t) : argv[1].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)), v4->localTZA)); self->setDate(t); return Encode(self->date()); } @@ -1374,7 +1373,7 @@ ReturnedValue DatePrototype::method_setYear(const FunctionObject *b, const Value if (std::isnan(t)) t = 0; else - t = LocalTime(t); + t = LocalTime(t, v4->localTZA); double year = argc ? argv[0].toNumber() : qt_qnan(); double r; if (std::isnan(year)) { @@ -1383,7 +1382,7 @@ ReturnedValue DatePrototype::method_setYear(const FunctionObject *b, const Value if ((Primitive::toInteger(year) >= 0) && (Primitive::toInteger(year) <= 99)) year += 1900; r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); - r = UTC(MakeDate(r, TimeWithinDay(t))); + r = UTC(MakeDate(r, TimeWithinDay(t)), v4->localTZA); r = TimeClip(r); } self->setDate(r); @@ -1413,7 +1412,7 @@ ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const V if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); if (std::isnan(t)) @@ -1427,7 +1426,7 @@ ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const V double date = (argc < 3) ? DateFromTime(t) : argv[2].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)), v4->localTZA)); self->setDate(t); return Encode(self->date()); } @@ -1471,7 +1470,7 @@ ReturnedValue DatePrototype::method_toISOString(const FunctionObject *b, const V int year = (int)YearFromTime(t); if (year < 0 || year > 9999) { if (qAbs(year) >= 1000000) - RETURN_RESULT(v4->newString(QStringLiteral("Invalid Date"))); + RETURN_RESULT(v4->throwRangeError(*thisObject)); result += year < 0 ? QLatin1Char('-') : QLatin1Char('+'); year = qAbs(year); addZeroPrefixedInt(result, year, 6); @@ -1518,7 +1517,23 @@ ReturnedValue DatePrototype::method_toJSON(const FunctionObject *b, const Value return toIso->call(O, nullptr, 0); } -void DatePrototype::timezoneUpdated() +ReturnedValue DatePrototype::method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *e = f->engine(); + if (!thisObject->isObject() || !argc || !argv->isString()) + return e->throwTypeError(); + + String *hint = argv->stringValue(); + PropertyKey id = hint->toPropertyKey(); + if (id == e->id_default()->propertyKey()) + hint = e->id_string(); + else if (id != e->id_string()->propertyKey() && id != e->id_number()->propertyKey()) + return e->throwTypeError(); + + return RuntimeHelpers::ordinaryToPrimitive(e, static_cast<const Object *>(thisObject), hint); +} + +void DatePrototype::timezoneUpdated(ExecutionEngine *e) { - LocalTZA = getLocalTZA(); + e->localTZA = getLocalTZA(); } diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index 2b9a580288..5b9934282c 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -101,15 +101,15 @@ struct DateObject: Object { template<> inline const DateObject *Value::as() const { - return isManaged() && m()->vtable()->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : nullptr; + return isManaged() && m()->internalClass->vtable->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : nullptr; } struct DateCtor: FunctionObject { V4_OBJECT2(DateCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int); }; struct DatePrototype: Object @@ -169,8 +169,9 @@ struct DatePrototype: Object static ReturnedValue method_toUTCString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toISOString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toJSON(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *, int); - static void timezoneUpdated(); + static void timezoneUpdated(ExecutionEngine *e); }; } diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 0ed0df89a9..f9ef6b45a1 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -42,6 +42,9 @@ #include <qv4object_p.h> #include <qv4objectproto_p.h> #include <qv4objectiterator_p.h> +#include <qv4setiterator_p.h> +#include <qv4mapiterator_p.h> +#include <qv4arrayiterator_p.h> #include <qv4arrayobject_p.h> #include <qv4booleanobject_p.h> #include <qv4globalobject_p.h> @@ -52,6 +55,9 @@ #include <qv4numberobject_p.h> #include <qv4regexpobject_p.h> #include <qv4regexp_p.h> +#include "qv4symbol_p.h" +#include "qv4setobject_p.h" +#include "qv4mapobject_p.h" #include <qv4variantobject_p.h> #include <qv4runtime_p.h> #include <private/qv4mm_p.h> @@ -63,7 +69,17 @@ #include "qv4debugging_p.h" #include "qv4profiling_p.h" #include "qv4executableallocator_p.h" +#include "qv4iterator_p.h" +#include "qv4stringiterator_p.h" +#include "qv4generatorobject_p.h" +#include "qv4reflect_p.h" +#include "qv4proxy_p.h" +#include "qv4stackframe_p.h" + +#if QT_CONFIG(qml_sequence_object) #include "qv4sequenceobject_p.h" +#endif + #include "qv4qobjectwrapper_p.h" #include "qv4memberdata_p.h" #include "qv4arraybuffer_p.h" @@ -76,7 +92,9 @@ #include <private/qqmlvaluetype_p.h> #include <private/qqmllistwrapper_p.h> #include <private/qqmllist_p.h> +#if QT_CONFIG(qml_locale) #include <private/qqmllocale_p.h> +#endif #include <QtCore/QTextStream> #include <QDateTime> @@ -110,7 +128,7 @@ ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const #ifdef V4_BOOTSTRAP QJSEngine *ExecutionEngine::jsEngine() const { - return v8Engine->publicEngine(); + return publicEngine; } QQmlEngine *ExecutionEngine::qmlEngine() const @@ -121,7 +139,7 @@ QQmlEngine *ExecutionEngine::qmlEngine() const qint32 ExecutionEngine::maxCallDepth = -1; -ExecutionEngine::ExecutionEngine() +ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) : executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) , bumperPointerAllocator(new WTF::BumpPointerAllocator) @@ -129,6 +147,7 @@ ExecutionEngine::ExecutionEngine() , gcStack(new WTF::PageAllocation) , globalCode(nullptr) , v8Engine(nullptr) + , publicEngine(jsEngine) , argumentsAccessors(nullptr) , nArgumentsAccessors(0) , m_engineId(engineSerial.fetchAndAddOrdered(1)) @@ -181,27 +200,44 @@ ExecutionEngine::ExecutionEngine() } exceptionValue = jsAlloca(1); + *exceptionValue = Encode::undefined(); globalObject = static_cast<Object *>(jsAlloca(1)); jsObjects = jsAlloca(NJSObjects); typedArrayPrototype = static_cast<Object *>(jsAlloca(NTypedArrayTypes)); typedArrayCtors = static_cast<FunctionObject *>(jsAlloca(NTypedArrayTypes)); jsStrings = jsAlloca(NJSStrings); + jsSymbols = jsAlloca(NJSSymbols); // set up stack limits jsStackLimit = jsStackBase + JSStackLimit/sizeof(Value); identifierTable = new IdentifierTable(this); - classPool = new InternalClassPool; + memset(classes, 0, sizeof(classes)); + classes[Class_Empty] = memoryManager->allocIC<InternalClass>(); + classes[Class_Empty]->init(this); - internalClasses[Class_Empty] = new (classPool) InternalClass(this); - internalClasses[Class_String] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::String::staticVTable()); - internalClasses[Class_MemberData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::MemberData::staticVTable()); - internalClasses[Class_SimpleArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable()); - internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable()); - internalClasses[Class_ExecutionContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable()); - internalClasses[Class_QmlContext] = internalClasses[EngineBase::Class_ExecutionContext]->changeVTable(QV4::QmlContext::staticVTable()); - internalClasses[Class_CallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); + classes[Class_MemberData] = classes[Class_Empty]->changeVTable(QV4::MemberData::staticVTable()); + classes[Class_SimpleArrayData] = classes[Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable()); + classes[Class_SparseArrayData] = classes[Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable()); + classes[Class_ExecutionContext] = classes[Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable()); + classes[Class_CallContext] = classes[Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); + classes[Class_QmlContext] = classes[Class_Empty]->changeVTable(QV4::QmlContext::staticVTable()); + + Scope scope(this); + Scoped<InternalClass> ic(scope); + ic = classes[Class_Empty]->changeVTable(QV4::Object::staticVTable()); + jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic->d()); + classes[Class_Object] = ic->changePrototype(objectPrototype()->d()); + classes[Class_QmlContextWrapper] = classes[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable()); + + ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype()); + jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic->d(), /*init =*/ false); + classes[Class_String] = classes[Class_Empty]->changeVTable(QV4::String::staticVTable())->changePrototype(stringPrototype()->d()); + Q_ASSERT(stringPrototype()->d() && classes[Class_String]->prototype); + + jsObjects[SymbolProto] = memoryManager->allocate<SymbolPrototype>(); + classes[Class_Symbol] = classes[EngineBase::Class_Empty]->changeVTable(QV4::Symbol::staticVTable())->changePrototype(symbolPrototype()->d()); jsStrings[String_Empty] = newIdentifier(QString()); jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined")); @@ -211,6 +247,8 @@ ExecutionEngine::ExecutionEngine() jsStrings[String_boolean] = newIdentifier(QStringLiteral("boolean")); jsStrings[String_number] = newIdentifier(QStringLiteral("number")); jsStrings[String_string] = newIdentifier(QStringLiteral("string")); + jsStrings[String_default] = newIdentifier(QStringLiteral("default")); + jsStrings[String_symbol] = newIdentifier(QStringLiteral("symbol")); jsStrings[String_object] = newIdentifier(QStringLiteral("object")); jsStrings[String_function] = newIdentifier(QStringLiteral("function")); jsStrings[String_length] = newIdentifier(QStringLiteral("length")); @@ -239,141 +277,186 @@ ExecutionEngine::ExecutionEngine() jsStrings[String_byteOffset] = newIdentifier(QStringLiteral("byteOffset")); jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer")); jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex")); - - InternalClass *ic = internalClasses[Class_Empty]->changeVTable(QV4::Object::staticVTable()); - jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic); - internalClasses[Class_Object] = ic->changePrototype(objectPrototype()->d()); - internalClasses[EngineBase::Class_QmlContextWrapper] = internalClasses[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable()); + jsStrings[String_next] = newIdentifier(QStringLiteral("next")); + jsStrings[String_done] = newIdentifier(QStringLiteral("done")); + jsStrings[String_return] = newIdentifier(QStringLiteral("return")); + + jsSymbols[Symbol_hasInstance] = Symbol::create(this, QStringLiteral("@Symbol.hasInstance")); + jsSymbols[Symbol_isConcatSpreadable] = Symbol::create(this, QStringLiteral("@Symbol.isConcatSpreadable")); + jsSymbols[Symbol_iterator] = Symbol::create(this, QStringLiteral("@Symbol.iterator")); + jsSymbols[Symbol_match] = Symbol::create(this, QStringLiteral("@Symbol.match")); + jsSymbols[Symbol_replace] = Symbol::create(this, QStringLiteral("@Symbol.replace")); + jsSymbols[Symbol_search] = Symbol::create(this, QStringLiteral("@Symbol.search")); + jsSymbols[Symbol_species] = Symbol::create(this, QStringLiteral("@Symbol.species")); + jsSymbols[Symbol_split] = Symbol::create(this, QStringLiteral("@Symbol.split")); + jsSymbols[Symbol_toPrimitive] = Symbol::create(this, QStringLiteral("@Symbol.toPrimitive")); + jsSymbols[Symbol_toStringTag] = Symbol::create(this, QStringLiteral("@Symbol.toStringTag")); + jsSymbols[Symbol_unscopables] = Symbol::create(this, QStringLiteral("@Symbol.unscopables")); + jsSymbols[Symbol_revokableProxy] = Symbol::create(this, QStringLiteral("@Proxy.revokableProxy")); ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype()); - Q_ASSERT(ic->prototype); - ic = ic->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); - Q_ASSERT(ic->prototype); - jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic, objectPrototype()); - internalClasses[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d()); - jsObjects[PropertyListProto] = memoryManager->allocObject<PropertyListPrototype>(); - - InternalClass *argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype()); - argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable); - internalClasses[EngineBase::Class_ArgumentsObject] = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable); + Q_ASSERT(ic->d()->prototype); + ic = ic->addMember(id_length()->propertyKey(), Attr_NotConfigurable|Attr_NotEnumerable); + Q_ASSERT(ic->d()->prototype); + jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic->d()); + classes[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d()); + jsObjects[PropertyListProto] = memoryManager->allocate<PropertyListPrototype>(); + + Scoped<InternalClass> argsClass(scope); + argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype()); + argsClass = argsClass->addMember(id_length()->propertyKey(), Attr_NotEnumerable); + argsClass = argsClass->addMember(symbol_iterator()->propertyKey(), Attr_Data|Attr_NotEnumerable); + classes[Class_ArgumentsObject] = argsClass->addMember(id_callee()->propertyKey(), Attr_Data|Attr_NotEnumerable); argsClass = newInternalClass(StrictArgumentsObject::staticVTable(), objectPrototype()); - argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable); - argsClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - internalClasses[EngineBase::Class_StrictArgumentsObject] = argsClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + argsClass = argsClass->addMember(id_length()->propertyKey(), Attr_NotEnumerable); + argsClass = argsClass->addMember(symbol_iterator()->propertyKey(), Attr_Data|Attr_NotEnumerable); + classes[Class_StrictArgumentsObject] = argsClass->addMember(id_callee()->propertyKey(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); *static_cast<Value *>(globalObject) = newObject(); Q_ASSERT(globalObject->d()->vtable()); initRootContext(); ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype()); - ic = ic->addMember(id_length(), Attr_ReadOnly); - jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic); - internalClasses[Class_StringObject] = ic->changePrototype(stringPrototype()->d()); - Q_ASSERT(internalClasses[EngineBase::Class_StringObject]->find(id_length()) == Heap::StringObject::LengthPropertyIndex); + ic = ic->addMember(id_length()->propertyKey(), Attr_ReadOnly); + classes[Class_StringObject] = ic->changePrototype(stringPrototype()->d()); + Q_ASSERT(classes[Class_StringObject]->find(id_length()->propertyKey()) == Heap::StringObject::LengthPropertyIndex); - jsObjects[NumberProto] = memoryManager->allocObject<NumberPrototype>(); - jsObjects[BooleanProto] = memoryManager->allocObject<BooleanPrototype>(); - jsObjects[DateProto] = memoryManager->allocObject<DatePrototype>(); + classes[Class_SymbolObject] = newInternalClass(QV4::SymbolObject::staticVTable(), symbolPrototype()); + + jsObjects[NumberProto] = memoryManager->allocate<NumberPrototype>(); + jsObjects[BooleanProto] = memoryManager->allocate<BooleanPrototype>(); + jsObjects[DateProto] = memoryManager->allocate<DatePrototype>(); uint index; ic = newInternalClass(QV4::FunctionPrototype::staticVTable(), objectPrototype()); - ic = ic->addMember(id_prototype(), Attr_NotEnumerable, &index); + ic = ic->addMember(id_prototype()->propertyKey(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); - jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic, objectPrototype()); + jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic->d()); ic = newInternalClass(FunctionObject::staticVTable(), functionPrototype()); - ic = ic->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index); + ic = ic->addMember(id_prototype()->propertyKey(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); - internalClasses[EngineBase::Class_FunctionObject] = ic; - ic = ic->addMember(id_name(), Attr_ReadOnly, &index); + classes[Class_FunctionObject] = ic->d(); + ic = ic->addMember(id_name()->propertyKey(), Attr_ReadOnly, &index); Q_ASSERT(index == Heap::ScriptFunction::Index_Name); ic = ic->changeVTable(ScriptFunction::staticVTable()); - internalClasses[EngineBase::Class_ScriptFunction] = ic->addMember(id_length(), Attr_ReadOnly, &index); + ic = ic->addMember(id_length()->propertyKey(), Attr_ReadOnly_ButConfigurable, &index); Q_ASSERT(index == Heap::ScriptFunction::Index_Length); - internalClasses[EngineBase::Class_ObjectProto] = internalClasses[Class_Object]->addMember(id_constructor(), Attr_NotEnumerable, &index); + classes[Class_ScriptFunction] = ic->d(); + ic = ic->changeVTable(ConstructorFunction::staticVTable()); + classes[Class_ConstructorFunction] = ic->d(); + ic = ic->changeVTable(MemberFunction::staticVTable()); + classes[Class_MemberFunction] = ic->d(); + ic = ic->changeVTable(GeneratorFunction::staticVTable()); + classes[Class_MemberFunction] = ic->d(); + ic = ic->changeVTable(GeneratorFunction::staticVTable()); + classes[Class_GeneratorFunction] = ic->d(); + ic = ic->changeVTable(MemberGeneratorFunction::staticVTable()); + classes[Class_MemberGeneratorFunction] = ic->d(); + classes[Class_ObjectProto] = classes[Class_Object]->addMember(id_constructor()->propertyKey(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_ProtoConstructor); - Scope scope(this); + jsObjects[GeneratorProto] = memoryManager->allocObject<GeneratorPrototype>(classes[Class_Object]); + classes[Class_GeneratorObject] = newInternalClass(QV4::GeneratorObject::staticVTable(), generatorPrototype()); + ScopedString str(scope); - internalClasses[Class_RegExp] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::RegExp::staticVTable()); + classes[Class_RegExp] = classes[Class_Empty]->changeVTable(QV4::RegExp::staticVTable()); ic = newInternalClass(QV4::RegExpObject::staticVTable(), objectPrototype()); - ic = ic->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index); + ic = ic->addMember(id_lastIndex()->propertyKey(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == RegExpObject::Index_LastIndex); - ic = ic->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("source")))->propertyKey(), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Source); - ic = ic->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("global")))->propertyKey(), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Global); - ic = ic->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("ignoreCase")))->propertyKey(), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_IgnoreCase); - ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline")))->propertyKey(), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Multiline); - jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic, objectPrototype()); - internalClasses[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d()); + jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic->d()); + classes[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d()); - ic = internalClasses[Class_ArrayObject]->addMember(id_index(), Attr_Data, &index); + ic = classes[Class_ArrayObject]->addMember(id_index()->propertyKey(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayIndex); - internalClasses[EngineBase::Class_RegExpExecArray] = ic->addMember(id_input(), Attr_Data, &index); + classes[Class_RegExpExecArray] = ic->addMember(id_input()->propertyKey(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayInput); ic = newInternalClass(ErrorObject::staticVTable(), nullptr); - ic = ic->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("stack")))->propertyKey(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_Stack); - ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName")))->propertyKey(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_FileName); - ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index); - internalClasses[EngineBase::Class_ErrorObject] = ic; + ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber")))->propertyKey(), Attr_Data|Attr_NotEnumerable, &index); + classes[Class_ErrorObject] = ic->d(); Q_ASSERT(index == ErrorObject::Index_LineNumber); - internalClasses[EngineBase::Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); + classes[Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message")))->propertyKey(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_Message); ic = newInternalClass(ErrorObject::staticVTable(), objectPrototype()); - ic = ic->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index); + ic = ic->addMember(id_constructor()->propertyKey(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Constructor); - ic = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("message")))->propertyKey(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Message); - internalClasses[EngineBase::Class_ErrorProto] = ic->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index); + classes[Class_ErrorProto] = ic->addMember(id_name()->propertyKey(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Name); - jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(rootContext(), str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack); - getStackFunction()->defineReadonlyProperty(id_length(), Primitive::fromInt32(0)); + classes[Class_ProxyObject] = classes[Class_Empty]->changeVTable(ProxyObject::staticVTable()); + + jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(this, str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack, 0); - jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto], objectPrototype()); - jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); - jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); - jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); - jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); - jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); - jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); + jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(classes[Class_ErrorProto]); + ic = classes[Class_ErrorProto]->changePrototype(errorPrototype()->d()); + jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(ic->d()); + jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(ic->d()); + jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(ic->d()); + jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(ic->d()); + jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(ic->d()); + jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(ic->d()); - jsObjects[VariantProto] = memoryManager->allocObject<VariantPrototype>(); - Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d()); + jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>(); + Q_ASSERT(variantPrototype()->getPrototypeOf() == objectPrototype()->d()); +#if QT_CONFIG(qml_sequence_object) ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this)); - jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic, SequencePrototype::defaultPrototype(this))); + jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic->d())); +#endif ExecutionContext *global = rootContext(); - jsObjects[Object_Ctor] = memoryManager->allocObject<ObjectCtor>(global); - jsObjects[String_Ctor] = memoryManager->allocObject<StringCtor>(global); - jsObjects[Number_Ctor] = memoryManager->allocObject<NumberCtor>(global); - jsObjects[Boolean_Ctor] = memoryManager->allocObject<BooleanCtor>(global); - jsObjects[Array_Ctor] = memoryManager->allocObject<ArrayCtor>(global); - jsObjects[Function_Ctor] = memoryManager->allocObject<FunctionCtor>(global); - jsObjects[Date_Ctor] = memoryManager->allocObject<DateCtor>(global); - jsObjects[RegExp_Ctor] = memoryManager->allocObject<RegExpCtor>(global); - jsObjects[Error_Ctor] = memoryManager->allocObject<ErrorCtor>(global); - jsObjects[EvalError_Ctor] = memoryManager->allocObject<EvalErrorCtor>(global); - jsObjects[RangeError_Ctor] = memoryManager->allocObject<RangeErrorCtor>(global); - jsObjects[ReferenceError_Ctor] = memoryManager->allocObject<ReferenceErrorCtor>(global); - jsObjects[SyntaxError_Ctor] = memoryManager->allocObject<SyntaxErrorCtor>(global); - jsObjects[TypeError_Ctor] = memoryManager->allocObject<TypeErrorCtor>(global); - jsObjects[URIError_Ctor] = memoryManager->allocObject<URIErrorCtor>(global); + + jsObjects[Object_Ctor] = memoryManager->allocate<ObjectCtor>(global); + jsObjects[String_Ctor] = memoryManager->allocate<StringCtor>(global); + jsObjects[Symbol_Ctor] = memoryManager->allocate<SymbolCtor>(global); + jsObjects[Number_Ctor] = memoryManager->allocate<NumberCtor>(global); + jsObjects[Boolean_Ctor] = memoryManager->allocate<BooleanCtor>(global); + jsObjects[Array_Ctor] = memoryManager->allocate<ArrayCtor>(global); + jsObjects[Function_Ctor] = memoryManager->allocate<FunctionCtor>(global); + jsObjects[GeneratorFunction_Ctor] = memoryManager->allocate<GeneratorFunctionCtor>(global); + jsObjects[Date_Ctor] = memoryManager->allocate<DateCtor>(global); + jsObjects[RegExp_Ctor] = memoryManager->allocate<RegExpCtor>(global); + jsObjects[Error_Ctor] = memoryManager->allocate<ErrorCtor>(global); + jsObjects[EvalError_Ctor] = memoryManager->allocate<EvalErrorCtor>(global); + jsObjects[RangeError_Ctor] = memoryManager->allocate<RangeErrorCtor>(global); + jsObjects[ReferenceError_Ctor] = memoryManager->allocate<ReferenceErrorCtor>(global); + jsObjects[SyntaxError_Ctor] = memoryManager->allocate<SyntaxErrorCtor>(global); + jsObjects[TypeError_Ctor] = memoryManager->allocate<TypeErrorCtor>(global); + jsObjects[URIError_Ctor] = memoryManager->allocate<URIErrorCtor>(global); + jsObjects[IteratorProto] = memoryManager->allocate<IteratorPrototype>(); + jsObjects[ForInIteratorProto] = memoryManager->allocObject<ForInIteratorPrototype>(newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype())); + jsObjects[MapIteratorProto] = memoryManager->allocObject<MapIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype())); + jsObjects[SetIteratorProto] = memoryManager->allocObject<SetIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype())); + jsObjects[ArrayIteratorProto] = memoryManager->allocObject<ArrayIteratorPrototype>(newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype())); + jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype())); + + str = newString(QStringLiteral("get [Symbol.species]")); + jsObjects[GetSymbolSpecies] = FunctionObject::createBuiltinFunction(this, str, ArrayPrototype::method_get_species, 0); static_cast<ObjectPrototype *>(objectPrototype())->init(this, objectCtor()); static_cast<StringPrototype *>(stringPrototype())->init(this, stringCtor()); + static_cast<SymbolPrototype *>(symbolPrototype())->init(this, symbolCtor()); static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor()); static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor()); static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor()); static_cast<PropertyListPrototype *>(propertyListPrototype())->init(this); static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor()); static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor()); + static_cast<GeneratorPrototype *>(generatorPrototype())->init(this, generatorFunctionCtor()); static_cast<RegExpPrototype *>(regExpPrototype())->init(this, regExpCtor()); static_cast<ErrorPrototype *>(errorPrototype())->init(this, errorCtor()); static_cast<EvalErrorPrototype *>(evalErrorPrototype())->init(this, evalErrorCtor()); @@ -383,25 +466,47 @@ ExecutionEngine::ExecutionEngine() static_cast<TypeErrorPrototype *>(typeErrorPrototype())->init(this, typeErrorCtor()); static_cast<URIErrorPrototype *>(uRIErrorPrototype())->init(this, uRIErrorCtor()); + static_cast<IteratorPrototype *>(iteratorPrototype())->init(this); + static_cast<ForInIteratorPrototype *>(forInIteratorPrototype())->init(this); + static_cast<MapIteratorPrototype *>(mapIteratorPrototype())->init(this); + static_cast<SetIteratorPrototype *>(setIteratorPrototype())->init(this); + static_cast<ArrayIteratorPrototype *>(arrayIteratorPrototype())->init(this); + static_cast<StringIteratorPrototype *>(stringIteratorPrototype())->init(this); + static_cast<VariantPrototype *>(variantPrototype())->init(); + +#if QT_CONFIG(qml_sequence_object) sequencePrototype()->cast<SequencePrototype>()->init(); +#endif + + jsObjects[Map_Ctor] = memoryManager->allocate<MapCtor>(global); + jsObjects[MapProto] = memoryManager->allocate<MapPrototype>(); + static_cast<MapPrototype *>(mapPrototype())->init(this, mapCtor()); + jsObjects[Set_Ctor] = memoryManager->allocate<SetCtor>(global); + jsObjects[SetProto] = memoryManager->allocate<SetPrototype>(); + static_cast<SetPrototype *>(setPrototype())->init(this, setCtor()); // typed arrays - jsObjects[ArrayBuffer_Ctor] = memoryManager->allocObject<ArrayBufferCtor>(global); - jsObjects[ArrayBufferProto] = memoryManager->allocObject<ArrayBufferPrototype>(); + jsObjects[ArrayBuffer_Ctor] = memoryManager->allocate<ArrayBufferCtor>(global); + jsObjects[ArrayBufferProto] = memoryManager->allocate<ArrayBufferPrototype>(); static_cast<ArrayBufferPrototype *>(arrayBufferPrototype())->init(this, arrayBufferCtor()); - jsObjects[DataView_Ctor] = memoryManager->allocObject<DataViewCtor>(global); - jsObjects[DataViewProto] = memoryManager->allocObject<DataViewPrototype>(); + jsObjects[DataView_Ctor] = memoryManager->allocate<DataViewCtor>(global); + jsObjects[DataViewProto] = memoryManager->allocate<DataViewPrototype>(); static_cast<DataViewPrototype *>(dataViewPrototype())->init(this, dataViewCtor()); jsObjects[ValueTypeProto] = (Heap::Base *) nullptr; jsObjects[SignalHandlerProto] = (Heap::Base *) nullptr; + jsObjects[IntrinsicTypedArray_Ctor] = memoryManager->allocate<IntrinsicTypedArrayCtor>(global); + jsObjects[IntrinsicTypedArrayProto] = memoryManager->allocate<IntrinsicTypedArrayPrototype>(); + static_cast<IntrinsicTypedArrayPrototype *>(intrinsicTypedArrayPrototype()) + ->init(this, static_cast<IntrinsicTypedArrayCtor *>(intrinsicTypedArrayCtor())); + for (int i = 0; i < Heap::TypedArray::NTypes; ++i) { - static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocObject<TypedArrayCtor>(global, Heap::TypedArray::Type(i)); - static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocObject<TypedArrayPrototype>(Heap::TypedArray::Type(i)); + static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocate<TypedArrayCtor>(global, Heap::TypedArray::Type(i)); + static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocate<TypedArrayPrototype>(Heap::TypedArray::Type(i)); typedArrayPrototype[i].as<TypedArrayPrototype>()->init(this, static_cast<TypedArrayCtor *>(typedArrayCtors[i].as<Object>())); } @@ -413,6 +518,7 @@ ExecutionEngine::ExecutionEngine() globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor()); globalObject->defineDefaultProperty(QStringLiteral("String"), *stringCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Symbol"), *symbolCtor()); FunctionObject *numberObject = numberCtor(); globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberObject); globalObject->defineDefaultProperty(QStringLiteral("Boolean"), *booleanCtor()); @@ -430,18 +536,23 @@ ExecutionEngine::ExecutionEngine() globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor()); globalObject->defineDefaultProperty(QStringLiteral("DataView"), *dataViewCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Set"), *setCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Map"), *mapCtor()); + for (int i = 0; i < Heap::TypedArray::NTypes; ++i) globalObject->defineDefaultProperty((str = typedArrayCtors[i].as<FunctionObject>()->name())->toQString(), typedArrayCtors[i]); ScopedObject o(scope); - globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocObject<MathObject>())); - globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocObject<JsonObject>())); + globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocate<MathObject>())); + globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocate<JsonObject>())); + globalObject->defineDefaultProperty(QStringLiteral("Reflect"), (o = memoryManager->allocate<Reflect>())); + globalObject->defineDefaultProperty(QStringLiteral("Proxy"), (o = memoryManager->allocate<Proxy>(rootContext()))); globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Primitive::undefinedValue()); globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(std::numeric_limits<double>::quiet_NaN())); globalObject->defineReadonlyProperty(QStringLiteral("Infinity"), Primitive::fromDouble(Q_INFINITY)); - jsObjects[Eval_Function] = memoryManager->allocObject<EvalFunction>(global); + jsObjects[Eval_Function] = memoryManager->allocate<EvalFunction>(global); globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction()); // ES6: 20.1.2.12 & 20.1.2.13: @@ -453,11 +564,8 @@ ExecutionEngine::ExecutionEngine() Scope scope(this); ScopedString pi(scope, newIdentifier(piString)); ScopedString pf(scope, newIdentifier(pfString)); - ExecutionContext *global = rootContext(); - ScopedFunctionObject parseIntFn(scope, FunctionObject::createBuiltinFunction(global, pi, GlobalFunctions::method_parseInt)); - ScopedFunctionObject parseFloatFn(scope, FunctionObject::createBuiltinFunction(global, pf, GlobalFunctions::method_parseFloat)); - parseIntFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(2)); - parseFloatFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(1)); + ScopedFunctionObject parseIntFn(scope, FunctionObject::createBuiltinFunction(this, pi, GlobalFunctions::method_parseInt, 2)); + ScopedFunctionObject parseFloatFn(scope, FunctionObject::createBuiltinFunction(this, pf, GlobalFunctions::method_parseFloat, 1)); globalObject->defineDefaultProperty(piString, parseIntFn); globalObject->defineDefaultProperty(pfString, parseFloatFn); numberObject->defineDefaultProperty(piString, parseIntFn); @@ -473,8 +581,16 @@ ExecutionEngine::ExecutionEngine() globalObject->defineDefaultProperty(QStringLiteral("escape"), GlobalFunctions::method_escape, 1); globalObject->defineDefaultProperty(QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); - ScopedString name(scope, newString(QStringLiteral("thrower"))); - jsObjects[ThrowerObject] = FunctionObject::createBuiltinFunction(global, name, ::throwTypeError); + ScopedFunctionObject t(scope, memoryManager->allocate<FunctionObject>(rootContext(), nullptr, ::throwTypeError)); + t->defineReadonlyProperty(id_length(), Primitive::fromInt32(0)); + t->setInternalClass(t->internalClass()->frozen()); + jsObjects[ThrowerObject] = t; + + ScopedProperty pd(scope); + pd->value = thrower(); + pd->set = thrower(); + functionPrototype()->insertMember(id_caller(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable); + functionPrototype()->insertMember(id_arguments(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable); } ExecutionEngine::~ExecutionEngine() @@ -487,8 +603,6 @@ ExecutionEngine::~ExecutionEngine() while (!compilationUnits.isEmpty()) (*compilationUnits.begin())->unlink(); - internalClasses[Class_Empty]->destroy(); - delete classPool; delete bumperPointerAllocator; delete regExpCache; delete regExpAllocator; @@ -500,6 +614,11 @@ ExecutionEngine::~ExecutionEngine() delete [] argumentsAccessors; } +ExecutionContext *ExecutionEngine::currentContext() const +{ + return static_cast<ExecutionContext *>(¤tStackFrame->jsFrame->context); +} + #if QT_CONFIG(qml_debug) void ExecutionEngine::setDebugger(Debugging::Debugger *debugger) { @@ -521,59 +640,71 @@ void ExecutionEngine::initRootContext() r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext); r->d()->activation.set(this, globalObject->d()); jsObjects[RootContext] = r; + jsObjects[ScriptContext] = r; jsObjects[IntegerNull] = Encode((int)0); } -InternalClass *ExecutionEngine::newClass(const InternalClass &other) +Heap::InternalClass *ExecutionEngine::newClass(Heap::InternalClass *other) { - return new (classPool) InternalClass(other); + Heap::InternalClass *ic = memoryManager->allocIC<InternalClass>(); + ic->init(other); + return ic; } -InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype) +Heap::InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype) { - return internalClasses[EngineBase::Class_Empty]->changeVTable(vtable)->changePrototype(prototype ? prototype->d() : nullptr); + Scope scope(this); + Scoped<InternalClass> ic(scope, internalClasses(Class_Empty)->changeVTable(vtable)); + return ic->changePrototype(prototype ? prototype->d() : nullptr); } Heap::Object *ExecutionEngine::newObject() { - return memoryManager->allocObject<Object>(); + return memoryManager->allocate<Object>(); } -Heap::Object *ExecutionEngine::newObject(InternalClass *internalClass, QV4::Object *prototype) +Heap::Object *ExecutionEngine::newObject(Heap::InternalClass *internalClass) { - return memoryManager->allocObject<Object>(internalClass, prototype); + return memoryManager->allocObject<Object>(internalClass); } Heap::String *ExecutionEngine::newString(const QString &s) { - Scope scope(this); - return ScopedString(scope, memoryManager->allocWithStringData<String>(s.length() * sizeof(QChar), s))->d(); + return memoryManager->allocWithStringData<String>(s.length() * sizeof(QChar), s); } Heap::String *ExecutionEngine::newIdentifier(const QString &text) { - return identifierTable->insertString(text); + Scope scope(this); + ScopedString s(scope, memoryManager->allocWithStringData<String>(text.length() * sizeof(QChar), text)); + s->toPropertyKey(); + return s->d(); } Heap::Object *ExecutionEngine::newStringObject(const String *string) { - return memoryManager->allocObject<StringObject>(string); + return memoryManager->allocate<StringObject>(string); +} + +Heap::Object *ExecutionEngine::newSymbolObject(const Symbol *symbol) +{ + return memoryManager->allocObject<SymbolObject>(classes[Class_SymbolObject], symbol); } Heap::Object *ExecutionEngine::newNumberObject(double value) { - return memoryManager->allocObject<NumberObject>(value); + return memoryManager->allocate<NumberObject>(value); } Heap::Object *ExecutionEngine::newBooleanObject(bool b) { - return memoryManager->allocObject<BooleanObject>(b); + return memoryManager->allocate<BooleanObject>(b); } Heap::ArrayObject *ExecutionEngine::newArrayObject(int count) { Scope scope(this); - ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>()); + ScopedArrayObject object(scope, memoryManager->allocate<ArrayObject>()); if (count) { if (count < 0x1000) @@ -586,7 +717,7 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(int count) Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int length) { Scope scope(this); - ScopedArrayObject a(scope, memoryManager->allocObject<ArrayObject>()); + ScopedArrayObject a(scope, memoryManager->allocate<ArrayObject>()); if (length) { size_t size = sizeof(Heap::ArrayData) + (length-1)*sizeof(Value); @@ -607,45 +738,41 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int leng Heap::ArrayObject *ExecutionEngine::newArrayObject(const QStringList &list) { - Scope scope(this); - ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>(list)); - return object->d(); + return memoryManager->allocate<ArrayObject>(list); } -Heap::ArrayObject *ExecutionEngine::newArrayObject(InternalClass *internalClass, Object *prototype) +Heap::ArrayObject *ExecutionEngine::newArrayObject(Heap::InternalClass *internalClass) { - Scope scope(this); - ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>(internalClass, prototype)); - return object->d(); + return memoryManager->allocObject<ArrayObject>(internalClass); } Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(const QByteArray &array) { - return memoryManager->allocObject<ArrayBuffer>(array); + return memoryManager->allocate<ArrayBuffer>(array); } Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(size_t length) { - return memoryManager->allocObject<ArrayBuffer>(length); + return memoryManager->allocate<ArrayBuffer>(length); } Heap::DateObject *ExecutionEngine::newDateObject(const Value &value) { - return memoryManager->allocObject<DateObject>(value); + return memoryManager->allocate<DateObject>(value); } Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dt) { Scope scope(this); - Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(dt)); + Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(dt)); return object->d(); } Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t) { Scope scope(this); - Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(t)); + Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(t)); return object->d(); } @@ -662,12 +789,12 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re) { - return memoryManager->allocObject<RegExpObject>(re); + return memoryManager->allocate<RegExpObject>(re); } Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re) { - return memoryManager->allocObject<RegExpObject>(re); + return memoryManager->allocate<RegExpObject>(re); } Heap::Object *ExecutionEngine::newErrorObject(const Value &value) @@ -714,16 +841,31 @@ Heap::Object *ExecutionEngine::newURIErrorObject(const Value &message) Heap::Object *ExecutionEngine::newVariantObject(const QVariant &v) { - return memoryManager->allocObject<VariantObject>(v); + return memoryManager->allocate<VariantObject>(v); } -Heap::Object *ExecutionEngine::newForEachIteratorObject(Object *o) +Heap::Object *ExecutionEngine::newForInIteratorObject(Object *o) { Scope scope(this); - ScopedObject obj(scope, memoryManager->allocObject<ForEachIteratorObject>(o)); + ScopedObject obj(scope, memoryManager->allocate<ForInIteratorObject>(o)); return obj->d(); } +Heap::Object *ExecutionEngine::newMapIteratorObject(Object *o) +{ + return memoryManager->allocate<MapIteratorObject>(o->d(), this); +} + +Heap::Object *ExecutionEngine::newSetIteratorObject(Object *o) +{ + return memoryManager->allocate<SetIteratorObject>(o->d(), this); +} + +Heap::Object *ExecutionEngine::newArrayIteratorObject(Object *o) +{ + return memoryManager->allocate<ArrayIteratorObject>(o->d(), this); +} + Heap::QmlContext *ExecutionEngine::qmlContext() const { if (!currentStackFrame) @@ -761,37 +903,6 @@ QQmlContextData *ExecutionEngine::callingQmlContext() const return ctx->qml()->context->contextData(); } -QString CppStackFrame::source() const -{ - return v4Function ? v4Function->sourceFile() : QString(); -} - -QString CppStackFrame::function() const -{ - return v4Function ? v4Function->name()->toQString() : QString(); -} - -int CppStackFrame::lineNumber() const -{ - if (!v4Function) - return -1; - - auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) { - return entry.codeOffset < offset; - }; - - const QV4::CompiledData::Function *cf = v4Function->compiledFunction; - uint offset = instructionPointer; - const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable(); - uint nLineNumbers = cf->nLineNumbers; - const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1; - return line->line; -} - -ReturnedValue CppStackFrame::thisObject() const { - return jsFrame->thisObject.asReturnedValue(); -} - StackTrace ExecutionEngine::stackTrace(int frameLimit) const { Scope scope(const_cast<ExecutionEngine *>(this)); @@ -891,16 +1002,14 @@ void ExecutionEngine::requireArgumentsAccessors(int n) } ExecutionContext *global = rootContext(); for (int i = oldSize; i < nArgumentsAccessors; ++i) { - argumentsAccessors[i].value = ScopedValue(scope, memoryManager->allocObject<ArgumentsGetterFunction>(global, i)); - argumentsAccessors[i].set = ScopedValue(scope, memoryManager->allocObject<ArgumentsSetterFunction>(global, i)); + argumentsAccessors[i].value = ScopedValue(scope, memoryManager->allocate<ArgumentsGetterFunction>(global, i)); + argumentsAccessors[i].set = ScopedValue(scope, memoryManager->allocate<ArgumentsSetterFunction>(global, i)); } } } void ExecutionEngine::markObjects(MarkStack *markStack) { - identifierTable->mark(markStack); - for (int i = 0; i < nArgumentsAccessors; ++i) { const Property &pd = argumentsAccessors[i]; if (Heap::FunctionObject *getter = pd.getter()) @@ -909,9 +1018,13 @@ void ExecutionEngine::markObjects(MarkStack *markStack) setter->mark(markStack); } - classPool->markObjects(markStack); + for (int i = 0; i < NClasses; ++i) + if (classes[i]) + classes[i]->mark(markStack); markStack->drain(); + identifierTable->markObjects(markStack); + for (auto compilationUnit: compilationUnits) { compilationUnit->markObjects(markStack); markStack->drain(); @@ -1053,12 +1166,7 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError() error.setColumn(frame.column); } QV4::Scoped<QV4::ErrorObject> errorObj(scope, exception); - if (!!errorObj && errorObj->asSyntaxError()) { - QV4::ScopedString m(scope, newString(QStringLiteral("message"))); - QV4::ScopedValue v(scope, errorObj->get(m)); - error.setDescription(v->toQStringNoThrow()); - } else - error.setDescription(exception->toQStringNoThrow()); + error.setDescription(exception->toQStringNoThrow()); return error; } @@ -1117,8 +1225,11 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return v->toVariant(); } else if (QV4::QmlListWrapper *l = object->as<QV4::QmlListWrapper>()) { return l->toVariant(); - } else if (object->isListType()) +#if QT_CONFIG(qml_sequence_object) + } else if (object->isListType()) { return QV4::SequencePrototype::toVariant(object); +#endif + } } if (value.as<ArrayObject>()) { @@ -1128,7 +1239,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int uint length = a->getLength(); QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope); for (uint ii = 0; ii < length; ++ii) { - qobjectWrapper = a->getIndexed(ii); + qobjectWrapper = a->get(ii); if (!!qobjectWrapper) { list << qobjectWrapper->object(); } else { @@ -1141,10 +1252,12 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return QVariant::fromValue(QV4::JsonObject::toJsonArray(a)); } +#if QT_CONFIG(qml_sequence_object) bool succeeded = false; QVariant retn = QV4::SequencePrototype::toVariant(value, typeHint, &succeeded); if (succeeded) return retn; +#endif } if (value.isUndefined()) @@ -1164,8 +1277,10 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return str.at(0); return str; } +#if QT_CONFIG(qml_locale) if (const QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>()) return *ld->d()->locale; +#endif if (const QV4::DateObject *d = value.as<DateObject>()) return d->toQDateTime(); if (const ArrayBuffer *d = value.as<ArrayBuffer>()) @@ -1211,7 +1326,7 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V int length = a->getLength(); for (int ii = 0; ii < length; ++ii) { - v = a->getIndexed(ii); + v = a->get(ii); list << ::toVariant(e, v, -1, /*createJSValueForObjects*/false, visitedObjects); } @@ -1260,9 +1375,6 @@ static QV4::ReturnedValue objectFromVariantMap(QV4::ExecutionEngine *e, const QV QV4::ScopedValue v(scope); for (QVariantMap::const_iterator iter = map.begin(), cend = map.end(); iter != cend; ++iter) { s = e->newString(iter.key()); - uint idx = s->asArrayIndex(); - if (idx > 16 && (!o->arrayData() || idx > o->arrayData()->length() * 2)) - o->initSparseArray(); o->put(s, (v = e->fromVariant(iter.value()))); } return o.asReturnedValue(); @@ -1321,6 +1433,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr))); case QMetaType::QObjectStar: return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr)); +#if QT_CONFIG(qml_sequence_object) case QMetaType::QStringList: { bool succeeded = false; @@ -1330,6 +1443,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return retn->asReturnedValue(); return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(ptr))); } +#endif case QMetaType::QVariantList: return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr)); case QMetaType::QVariantMap: @@ -1340,8 +1454,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(ptr)); case QMetaType::QJsonArray: return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(ptr)); +#if QT_CONFIG(qml_locale) case QMetaType::QLocale: return QQmlLocale::wrap(this, *reinterpret_cast<const QLocale*>(ptr)); +#endif default: break; } @@ -1381,10 +1497,12 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) if (objOk) return QV4::QObjectWrapper::wrap(this, obj); +#if QT_CONFIG(qml_sequence_object) bool succeeded = false; QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromVariant(this, variant, &succeeded)); if (succeeded) return retn->asReturnedValue(); +#endif if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(type)) return QV4::QQmlValueTypeWrapper::create(this, variant, vtmo, type); @@ -1428,11 +1546,13 @@ static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVarian QV4::Scope scope(v4); QV4::ScopedObject o(scope, v4->newObject()); QV4::ScopedString s(scope); + QV4::ScopedPropertyKey key(scope); QV4::ScopedValue v(scope); for (QVariantMap::const_iterator it = vmap.constBegin(), cend = vmap.constEnd(); it != cend; ++it) { s = v4->newIdentifier(it.key()); + key = s->propertyKey(); v = variantToJS(v4, it.value()); - uint idx = s->asArrayIndex(); + uint idx = key->asArrayIndex(); if (idx < UINT_MAX) o->arraySet(idx, v); else @@ -1707,7 +1827,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) } else if (Object *o = value->objectValue()) { // Look in the prototype chain. QV4::Scope scope(this); - QV4::ScopedObject proto(scope, o->prototype()); + QV4::ScopedObject proto(scope, o->getPrototypeOf()); while (proto) { bool canCast = false; if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) { @@ -1728,7 +1848,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) *reinterpret_cast<void* *>(data) = var.data(); return true; } - proto = proto->prototype(); + proto = proto->getPrototypeOf(); } } } else if (value->isNull() && name.endsWith('*')) { diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index c7fb743088..fc0c93eaad 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -56,7 +56,6 @@ #include <private/qintrusivelist_p.h> #include "qv4enginebase_p.h" - #ifndef V4_BOOTSTRAP # include "qv4function_p.h" # include <private/qv8engine_p.h> @@ -88,33 +87,6 @@ struct CompilationUnit; } struct Function; -struct InternalClass; -struct InternalClassPool; - -struct Q_QML_EXPORT CppStackFrame { - CppStackFrame *parent; - Function *v4Function; - CallData *jsFrame; - const Value *originalArguments; - int originalArgumentsCount; - int instructionPointer; - - QString source() const; - QString function() const; - inline QV4::ExecutionContext *context() const { - return static_cast<ExecutionContext *>(&jsFrame->context); - } - int lineNumber() const; - - inline QV4::Heap::CallContext *callContext() const { - Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\ - while (ctx->type != Heap::ExecutionContext::Type_CallContext) - ctx = ctx->outer; - return static_cast<Heap::CallContext *>(ctx); - } - ReturnedValue thisObject() const; -}; - struct Q_QML_EXPORT ExecutionEngine : public EngineBase @@ -142,8 +114,6 @@ public: QML_NEARLY_ALWAYS_INLINE Value *jsAlloca(int nValues) { Value *ptr = jsStackTop; jsStackTop = ptr + nValues; - for (int i = 0; i < nValues; ++i) - ptr[i] = Primitive::undefinedValue(); return ptr; } @@ -153,22 +123,27 @@ public: QJSEngine *jsEngine() const; QQmlEngine *qmlEngine() const; #else // !V4_BOOTSTRAP - QJSEngine *jsEngine() const { return v8Engine->publicEngine(); } + QJSEngine *jsEngine() const { return publicEngine; } QQmlEngine *qmlEngine() const { return v8Engine ? v8Engine->engine() : nullptr; } #endif // V4_BOOTSTRAP QV8Engine *v8Engine; + QJSEngine *publicEngine; enum JSObjects { RootContext, + ScriptContext, IntegerNull, // Has to come after the RootContext to make the context stack safe ObjectProto, + SymbolProto, ArrayProto, + ArrayProtoValues, PropertyListProto, StringProto, NumberProto, BooleanProto, DateProto, FunctionProto, + GeneratorProto, RegExpProto, ErrorProto, EvalErrorProto, @@ -178,18 +153,31 @@ public: TypeErrorProto, URIErrorProto, VariantProto, +#if QT_CONFIG(qml_sequence_object) SequenceProto, +#endif ArrayBufferProto, DataViewProto, + SetProto, + MapProto, + IntrinsicTypedArrayProto, ValueTypeProto, SignalHandlerProto, + IteratorProto, + ForInIteratorProto, + SetIteratorProto, + MapIteratorProto, + ArrayIteratorProto, + StringIteratorProto, Object_Ctor, String_Ctor, + Symbol_Ctor, Number_Ctor, Boolean_Ctor, Array_Ctor, Function_Ctor, + GeneratorFunction_Ctor, Date_Ctor, RegExp_Ctor, Error_Ctor, @@ -201,6 +189,11 @@ public: URIError_Ctor, ArrayBuffer_Ctor, DataView_Ctor, + Set_Ctor, + Map_Ctor, + IntrinsicTypedArray_Ctor, + + GetSymbolSpecies, Eval_Function, GetStack_Function, @@ -211,12 +204,16 @@ public: enum { NTypedArrayTypes = 9 }; // == TypedArray::NValues, avoid header dependency ExecutionContext *rootContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + RootContext); } + ExecutionContext *scriptContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + ScriptContext); } + void setScriptContext(ReturnedValue c) { jsObjects[ScriptContext] = c; } FunctionObject *objectCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Object_Ctor); } FunctionObject *stringCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + String_Ctor); } + FunctionObject *symbolCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Symbol_Ctor); } FunctionObject *numberCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Number_Ctor); } FunctionObject *booleanCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Boolean_Ctor); } FunctionObject *arrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Array_Ctor); } FunctionObject *functionCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Function_Ctor); } + FunctionObject *generatorFunctionCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + GeneratorFunction_Ctor); } FunctionObject *dateCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Date_Ctor); } FunctionObject *regExpCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + RegExp_Ctor); } FunctionObject *errorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Error_Ctor); } @@ -228,16 +225,24 @@ public: FunctionObject *uRIErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + URIError_Ctor); } FunctionObject *arrayBufferCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + ArrayBuffer_Ctor); } FunctionObject *dataViewCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + DataView_Ctor); } + FunctionObject *setCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Set_Ctor); } + FunctionObject *mapCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Map_Ctor); } + FunctionObject *intrinsicTypedArrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + IntrinsicTypedArray_Ctor); } FunctionObject *typedArrayCtors; + FunctionObject *getSymbolSpecies() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetSymbolSpecies); } + Object *objectPrototype() const { return reinterpret_cast<Object *>(jsObjects + ObjectProto); } + Object *symbolPrototype() const { return reinterpret_cast<Object *>(jsObjects + SymbolProto); } Object *arrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayProto); } + Object *arrayProtoValues() const { return reinterpret_cast<Object *>(jsObjects + ArrayProtoValues); } Object *propertyListPrototype() const { return reinterpret_cast<Object *>(jsObjects + PropertyListProto); } Object *stringPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringProto); } Object *numberPrototype() const { return reinterpret_cast<Object *>(jsObjects + NumberProto); } Object *booleanPrototype() const { return reinterpret_cast<Object *>(jsObjects + BooleanProto); } Object *datePrototype() const { return reinterpret_cast<Object *>(jsObjects + DateProto); } Object *functionPrototype() const { return reinterpret_cast<Object *>(jsObjects + FunctionProto); } + Object *generatorPrototype() const { return reinterpret_cast<Object *>(jsObjects + GeneratorProto); } Object *regExpPrototype() const { return reinterpret_cast<Object *>(jsObjects + RegExpProto); } Object *errorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ErrorProto); } Object *evalErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + EvalErrorProto); } @@ -247,16 +252,26 @@ public: Object *typeErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + TypeErrorProto); } Object *uRIErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + URIErrorProto); } Object *variantPrototype() const { return reinterpret_cast<Object *>(jsObjects + VariantProto); } +#if QT_CONFIG(qml_sequence_object) Object *sequencePrototype() const { return reinterpret_cast<Object *>(jsObjects + SequenceProto); } +#endif Object *arrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayBufferProto); } Object *dataViewPrototype() const { return reinterpret_cast<Object *>(jsObjects + DataViewProto); } + Object *setPrototype() const { return reinterpret_cast<Object *>(jsObjects + SetProto); } + Object *mapPrototype() const { return reinterpret_cast<Object *>(jsObjects + MapProto); } + Object *intrinsicTypedArrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + IntrinsicTypedArrayProto); } Object *typedArrayPrototype; Object *valueTypeWrapperPrototype() const { return reinterpret_cast<Object *>(jsObjects + ValueTypeProto); } Object *signalHandlerPrototype() const { return reinterpret_cast<Object *>(jsObjects + SignalHandlerProto); } + Object *iteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + IteratorProto); } + Object *forInIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ForInIteratorProto); } + Object *setIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + SetIteratorProto); } + Object *mapIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + MapIteratorProto); } + Object *arrayIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayIteratorProto); } + Object *stringIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringIteratorProto); } - InternalClassPool *classPool; EvalFunction *evalFunction() const { return reinterpret_cast<EvalFunction *>(jsObjects + Eval_Function); } FunctionObject *getStackFunction() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetStack_Function); } FunctionObject *thrower() const { return reinterpret_cast<FunctionObject *>(jsObjects + ThrowerObject); } @@ -273,6 +288,8 @@ public: String_boolean, String_number, String_string, + String_default, + String_symbol, String_object, String_function, String_length, @@ -301,10 +318,31 @@ public: String_byteOffset, String_buffer, String_lastIndex, + String_next, + String_done, + String_return, + NJSStrings }; Value *jsStrings; + enum JSSymbols { + Symbol_hasInstance, + Symbol_isConcatSpreadable, + Symbol_iterator, + Symbol_match, + Symbol_replace, + Symbol_search, + Symbol_species, + Symbol_split, + Symbol_toPrimitive, + Symbol_toStringTag, + Symbol_unscopables, + Symbol_revokableProxy, + NJSSymbols + }; + Value *jsSymbols; + String *id_empty() const { return reinterpret_cast<String *>(jsStrings + String_Empty); } String *id_undefined() const { return reinterpret_cast<String *>(jsStrings + String_undefined); } String *id_null() const { return reinterpret_cast<String *>(jsStrings + String_null); } @@ -313,6 +351,8 @@ public: String *id_boolean() const { return reinterpret_cast<String *>(jsStrings + String_boolean); } String *id_number() const { return reinterpret_cast<String *>(jsStrings + String_number); } String *id_string() const { return reinterpret_cast<String *>(jsStrings + String_string); } + String *id_default() const { return reinterpret_cast<String *>(jsStrings + String_default); } + String *id_symbol() const { return reinterpret_cast<String *>(jsStrings + String_symbol); } String *id_object() const { return reinterpret_cast<String *>(jsStrings + String_object); } String *id_function() const { return reinterpret_cast<String *>(jsStrings + String_function); } String *id_length() const { return reinterpret_cast<String *>(jsStrings + String_length); } @@ -341,6 +381,22 @@ public: String *id_byteOffset() const { return reinterpret_cast<String *>(jsStrings + String_byteOffset); } String *id_buffer() const { return reinterpret_cast<String *>(jsStrings + String_buffer); } String *id_lastIndex() const { return reinterpret_cast<String *>(jsStrings + String_lastIndex); } + String *id_next() const { return reinterpret_cast<String *>(jsStrings + String_next); } + String *id_done() const { return reinterpret_cast<String *>(jsStrings + String_done); } + String *id_return() const { return reinterpret_cast<String *>(jsStrings + String_return); } + + Symbol *symbol_hasInstance() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_hasInstance); } + Symbol *symbol_isConcatSpreadable() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_isConcatSpreadable); } + Symbol *symbol_iterator() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_iterator); } + Symbol *symbol_match() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_match); } + Symbol *symbol_replace() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_replace); } + Symbol *symbol_search() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_search); } + Symbol *symbol_species() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_species); } + Symbol *symbol_split() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_split); } + Symbol *symbol_toPrimitive() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_toPrimitive); } + Symbol *symbol_toStringTag() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_toStringTag); } + Symbol *symbol_unscopables() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_unscopables); } + Symbol *symbol_revokableProxy() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_revokableProxy); } #ifndef V4_BOOTSTRAP QIntrusiveList<CompiledData::CompilationUnit, &CompiledData::CompilationUnit::nextCompilationUnit> compilationUnits; @@ -372,9 +428,9 @@ public: const bool m_canAllocateExecutableMemory; #endif - int internalClassIdCount = 0; + quintptr protoIdCount = 1; - ExecutionEngine(); + ExecutionEngine(QJSEngine *jsEngine = nullptr); ~ExecutionEngine(); #if !QT_CONFIG(qml_debug) @@ -391,29 +447,28 @@ public: void setProfiler(Profiling::Profiler *profiler); #endif // QT_CONFIG(qml_debug) - void setCurrentContext(Heap::ExecutionContext *context); - ExecutionContext *currentContext() const { - return static_cast<ExecutionContext *>(¤tStackFrame->jsFrame->context); - } + ExecutionContext *currentContext() const; - int newInternalClassId() { return ++internalClassIdCount; } + // ensure we always get odd prototype IDs. This helps make marking in QV4::Lookup fast + quintptr newProtoId() { return (protoIdCount += 2); } - InternalClass *newInternalClass(const VTable *vtable, Object *prototype); + Heap::InternalClass *newInternalClass(const VTable *vtable, Object *prototype); Heap::Object *newObject(); - Heap::Object *newObject(InternalClass *internalClass, Object *prototype); + Heap::Object *newObject(Heap::InternalClass *internalClass); Heap::String *newString(const QString &s = QString()); Heap::String *newIdentifier(const QString &text); Heap::Object *newStringObject(const String *string); + Heap::Object *newSymbolObject(const Symbol *symbol); Heap::Object *newNumberObject(double value); Heap::Object *newBooleanObject(bool b); Heap::ArrayObject *newArrayObject(int count = 0); Heap::ArrayObject *newArrayObject(const Value *values, int length); Heap::ArrayObject *newArrayObject(const QStringList &list); - Heap::ArrayObject *newArrayObject(InternalClass *ic, Object *prototype); + Heap::ArrayObject *newArrayObject(Heap::InternalClass *ic); Heap::ArrayBuffer *newArrayBuffer(const QByteArray &array); Heap::ArrayBuffer *newArrayBuffer(size_t length); @@ -437,7 +492,10 @@ public: Heap::Object *newVariantObject(const QVariant &v); - Heap::Object *newForEachIteratorObject(Object *o); + Heap::Object *newForInIteratorObject(Object *o); + Heap::Object *newSetIteratorObject(Object *o); + Heap::Object *newMapIteratorObject(Object *o); + Heap::Object *newArrayIteratorObject(Object *o); Heap::QmlContext *qmlContext() const; QObject *qmlScopeObject() const; @@ -453,7 +511,7 @@ public: void initRootContext(); - InternalClass *newClass(const InternalClass &other); + Heap::InternalClass *newClass(Heap::InternalClass *other); StackTrace exceptionStackTrace; @@ -492,7 +550,7 @@ public: if (!m_canAllocateExecutableMemory) return false; if (f) - return f->interpreterCallCount >= jitCallCountThreshold; + return !f->isGenerator() && f->interpreterCallCount >= jitCallCountThreshold; return true; #else Q_UNUSED(f); @@ -502,6 +560,7 @@ public: QV4::ReturnedValue global(); + double localTZA = 0.0; // local timezone, initialized at startup private: #if QT_CONFIG(qml_debug) QScopedPointer<QV4::Debugging::Debugger> m_debugger; @@ -521,11 +580,6 @@ struct NoThrowEngine; #endif -inline void ExecutionEngine::setCurrentContext(Heap::ExecutionContext *context) -{ - currentStackFrame->jsFrame->context = context; -} - #define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \ ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4); diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 59fb4a564a..3e89e57abb 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -88,7 +88,7 @@ struct Q_QML_EXPORT EngineBase { // Exception handling Value *exceptionValue = nullptr; - enum { + enum InternalClassType { Class_Empty, Class_String, Class_MemberData, @@ -96,11 +96,18 @@ struct Q_QML_EXPORT EngineBase { Class_SparseArrayData, Class_ExecutionContext, Class_CallContext, + Class_QmlContext, Class_Object, Class_ArrayObject, Class_FunctionObject, + Class_GeneratorFunction, + Class_GeneratorObject, Class_StringObject, + Class_SymbolObject, Class_ScriptFunction, + Class_ConstructorFunction, + Class_MemberFunction, + Class_MemberGeneratorFunction, Class_ObjectProto, Class_RegExp, Class_RegExpObject, @@ -111,10 +118,12 @@ struct Q_QML_EXPORT EngineBase { Class_ErrorObjectWithMessage, Class_ErrorProto, Class_QmlContextWrapper, - Class_QmlContext, + Class_ProxyObject, + Class_Symbol, NClasses }; - InternalClass *internalClasses[NClasses]; + Heap::InternalClass *classes[NClasses]; + Heap::InternalClass *internalClasses(InternalClassType icType) { return classes[icType]; } }; #if defined(Q_CC_MSVC) || defined(Q_CC_GNU) #pragma pack(pop) diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index 90e158ba37..e8b4e04e83 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -47,10 +47,6 @@ #include "qv4string_p.h" #include <private/qv4mm_p.h> -#include <private/qqmljsengine_p.h> -#include <private/qqmljslexer_p.h> -#include <private/qqmljsparser_p.h> -#include <private/qqmljsast_p.h> #include <qv4codegen_p.h> #ifndef Q_OS_WIN @@ -74,7 +70,7 @@ void Heap::ErrorObject::init() Scope scope(internalClass->engine); Scoped<QV4::ErrorObject> e(scope, this); - if (internalClass == scope.engine->internalClasses[EngineBase::Class_ErrorProto]) + if (internalClass == scope.engine->internalClasses(EngineBase::Class_ErrorProto)) return; setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d()); @@ -233,13 +229,13 @@ void Heap::ErrorCtor::init(QV4::ExecutionContext *scope, const QString &name) Heap::FunctionObject::init(scope, name); } -ReturnedValue ErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue ErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<ErrorObject>(f->engine(), v)->asReturnedValue(); } -ReturnedValue ErrorCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue ErrorCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) { return f->callAsConstructor(argv, argc); } @@ -249,7 +245,7 @@ void Heap::EvalErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("EvalError")); } -ReturnedValue EvalErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue EvalErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<EvalErrorObject>(f->engine(), v)->asReturnedValue(); @@ -260,7 +256,7 @@ void Heap::RangeErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("RangeError")); } -ReturnedValue RangeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue RangeErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<RangeErrorObject>(f->engine(), v)->asReturnedValue(); @@ -271,7 +267,7 @@ void Heap::ReferenceErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("ReferenceError")); } -ReturnedValue ReferenceErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue ReferenceErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<ReferenceErrorObject>(f->engine(), v)->asReturnedValue(); @@ -282,7 +278,7 @@ void Heap::SyntaxErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("SyntaxError")); } -ReturnedValue SyntaxErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue SyntaxErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<SyntaxErrorObject>(f->engine(), v)->asReturnedValue(); @@ -293,7 +289,7 @@ void Heap::TypeErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("TypeError")); } -ReturnedValue TypeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue TypeErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<TypeErrorObject>(f->engine(), v)->asReturnedValue(); @@ -304,7 +300,7 @@ void Heap::URIErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("URIError")); } -ReturnedValue URIErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue URIErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<URIErrorObject>(f->engine(), v)->asReturnedValue(); @@ -316,7 +312,7 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He ScopedString s(scope); ScopedObject o(scope); ctor->defineReadonlyProperty(engine->id_prototype(), (o = obj)); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); obj->setProperty(Index_Constructor, ctor->d()); obj->setProperty(Index_Message, engine->id_empty()->d()); obj->setProperty(Index_Name, engine->newString(QString::fromLatin1(ErrorObject::className(t)))); diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 6b578e8c38..90ef4dd842 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -180,7 +180,7 @@ struct ErrorObject: Object { template<> inline const ErrorObject *Value::as() const { - return isManaged() && m()->vtable()->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : nullptr; + return isManaged() && m()->internalClass->vtable->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : nullptr; } struct EvalErrorObject: ErrorObject { @@ -229,50 +229,50 @@ struct ErrorCtor: FunctionObject { V4_OBJECT2(ErrorCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct EvalErrorCtor: ErrorCtor { V4_OBJECT2(EvalErrorCtor, ErrorCtor) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; struct RangeErrorCtor: ErrorCtor { V4_OBJECT2(RangeErrorCtor, ErrorCtor) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; struct ReferenceErrorCtor: ErrorCtor { V4_OBJECT2(ReferenceErrorCtor, ErrorCtor) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; struct SyntaxErrorCtor: ErrorCtor { V4_OBJECT2(SyntaxErrorCtor, ErrorCtor) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; struct TypeErrorCtor: ErrorCtor { V4_OBJECT2(TypeErrorCtor, ErrorCtor) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; struct URIErrorCtor: ErrorCtor { V4_OBJECT2(URIErrorCtor, ErrorCtor) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; @@ -328,25 +328,26 @@ inline SyntaxErrorObject *ErrorObject::asSyntaxError() template <typename T> Heap::Object *ErrorObject::create(ExecutionEngine *e, const Value &message) { - InternalClass *ic = e->internalClasses[message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage]; - ic = ic->changePrototype(T::defaultPrototype(e)->d()); - return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), message); + EngineBase::InternalClassType klass = message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage; + Scope scope(e); + Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d())); + return e->memoryManager->allocObject<T>(ic->d(), message); } template <typename T> Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message) { Scope scope(e); ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue()); - InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage]; - ic = ic->changePrototype(T::defaultPrototype(e)->d()); - return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v); + EngineBase::InternalClassType klass = v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage; + Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d())); + return e->memoryManager->allocObject<T>(ic->d(), v); } template <typename T> Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message, const QString &filename, int line, int column) { Scope scope(e); ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue()); - InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage]; - ic = ic->changePrototype(T::defaultPrototype(e)->d()); - return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v, filename, line, column); + EngineBase::InternalClassType klass = v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage; + Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d())); + return e->memoryManager->allocObject<T>(ic->d(), v, filename, line, column); } diff --git a/src/qml/jsruntime/qv4estable.cpp b/src/qml/jsruntime/qv4estable.cpp new file mode 100644 index 0000000000..55b7407000 --- /dev/null +++ b/src/qml/jsruntime/qv4estable.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4estable_p.h" + +using namespace QV4; + +// The ES spec requires that Map/Set be implemented using a data structure that +// is a little different from most; it requires nonlinear access, and must also +// preserve the order of insertion of items in a deterministic way. +// +// This class implements those requirements, except for fast access: that +// will be addressed in a followup patch. + +ESTable::ESTable() + : m_capacity(8) +{ + m_keys = (Value*)malloc(m_capacity * sizeof(Value)); + m_values = (Value*)malloc(m_capacity * sizeof(Value)); + memset(m_keys, 0, m_capacity); + memset(m_values, 0, m_capacity); +} + +ESTable::~ESTable() +{ + free(m_keys); + free(m_values); + m_size = 0; + m_capacity = 0; + m_keys = nullptr; + m_values = nullptr; +} + +void ESTable::markObjects(MarkStack *s) +{ + for (uint i = 0; i < m_size; ++i) { + m_keys[i].mark(s); + m_values[i].mark(s); + } +} + +// Pretends that there's nothing in the table. Doesn't actually free memory, as +// it will almost certainly be reused again anyway. +void ESTable::clear() +{ + m_size = 0; +} + +// Update the table to contain \a value for a given \a key. The key is +// normalized, as required by the ES spec. +void ESTable::set(const Value &key, const Value &value) +{ + for (uint i = 0; i < m_size; ++i) { + if (m_keys[i].sameValueZero(key)) { + m_values[i] = value; + return; + } + } + + if (m_capacity == m_size) { + uint oldCap = m_capacity; + m_capacity *= 2; + m_keys = (Value*)realloc(m_keys, m_capacity * sizeof(Value)); + m_values = (Value*)realloc(m_values, m_capacity * sizeof(Value)); + memset(m_keys + oldCap, 0, m_capacity - oldCap); + memset(m_values + oldCap, 0, m_capacity - oldCap); + } + + Value nk = key; + if (nk.isDouble()) { + if (nk.doubleValue() == 0 && std::signbit(nk.doubleValue())) + nk = Primitive::fromDouble(+0); + } + + m_keys[m_size] = nk; + m_values[m_size] = value; + + m_size++; +} + +// Returns true if the table contains \a key, false otherwise. +bool ESTable::has(const Value &key) const +{ + for (uint i = 0; i < m_size; ++i) { + if (m_keys[i].sameValueZero(key)) + return true; + } + + return false; +} + +// Fetches the value for the given \a key, and if \a hasValue is passed in, +// it is set depending on whether or not the given key was found. +ReturnedValue ESTable::get(const Value &key, bool *hasValue) const +{ + for (uint i = 0; i < m_size; ++i) { + if (m_keys[i].sameValueZero(key)) { + if (hasValue) + *hasValue = true; + return m_values[i].asReturnedValue(); + } + } + + if (hasValue) + *hasValue = false; + return Encode::undefined(); +} + +// Removes the given \a key from the table +bool ESTable::remove(const Value &key) +{ + bool found = false; + uint idx = 0; + for (; idx < m_size; ++idx) { + if (m_keys[idx].sameValueZero(key)) { + found = true; + break; + } + } + + if (found == true) { + memmove(m_keys + idx, m_keys + idx + 1, m_size - idx); + memmove(m_values + idx, m_values + idx + 1, m_size - idx); + m_size--; + } + return found; +} + +// Returns the size of the table. Note that the size may not match the underlying allocation. +uint ESTable::size() const +{ + return m_size; +} + +// Retrieves a key and value for a given \a idx, and places them in \a key and +// \a value. They must be valid pointers. +void ESTable::iterate(uint idx, Value *key, Value *value) +{ + Q_ASSERT(idx < m_size); + Q_ASSERT(key); + Q_ASSERT(value); + *key = m_keys[idx]; + *value = m_values[idx]; +} + diff --git a/src/qml/jsruntime/qv4estable_p.h b/src/qml/jsruntime/qv4estable_p.h new file mode 100644 index 0000000000..c665467760 --- /dev/null +++ b/src/qml/jsruntime/qv4estable_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QV4ESTABLE_P_H +#define QV4ESTABLE_P_H + +#include "qv4value_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 +{ + +class ESTable +{ +public: + ESTable(); + ~ESTable(); + + void markObjects(MarkStack *s); + void clear(); + void set(const Value &k, const Value &v); + bool has(const Value &k) const; + ReturnedValue get(const Value &k, bool *hasValue = nullptr) const; + bool remove(const Value &k); + uint size() const; + void iterate(uint idx, Value *k, Value *v); + +private: + Value *m_keys = nullptr; + Value *m_values = nullptr; + uint m_size = 0; + uint m_capacity = 0; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 6fca9ecd45..5e3860a660 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -47,32 +47,51 @@ #include <private/qv4mm_p.h> #include <private/qv4identifiertable_p.h> #include <assembler/MacroAssemblerCodeRef.h> +#include <private/qv4vme_moth_p.h> +#include <private/qqmlglobal_p.h> QT_BEGIN_NAMESPACE using namespace QV4; -Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr) - : compiledFunction(function) - , compilationUnit(unit) - , code(codePtr) - , codeData(function->code()) +ReturnedValue Function::call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) { + ExecutionEngine *engine = context->engine(); + CppStackFrame frame; + frame.init(engine, this, argv, argc); + frame.setupJSFrame(engine->jsStackTop, Primitive::undefinedValue(), context->d(), + thisObject ? *thisObject : Primitive::undefinedValue(), + Primitive::undefinedValue()); + + frame.push(); + engine->jsStackTop += frame.requiredJSStackFrameSize(); + + ReturnedValue result = Moth::VME::exec(&frame, engine); + + frame.pop(); + + return result; +} + +Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) + : compiledFunction(function) + , compilationUnit(unit) + , codeData(function->code()) , jittedCode(nullptr) , codeRef(nullptr) , hasQmlDependencies(function->hasQmlDependencies()) { - Q_UNUSED(engine); - - internalClass = engine->internalClasses[EngineBase::Class_CallContext]; + Scope scope(engine); + Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext)); // first locals const quint32_le *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) - internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); + ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); const quint32_le *formalsIndices = compiledFunction->formalsTable(); for (quint32 i = 0; i < compiledFunction->nFormals; ++i) - internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable); + ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable); + internalClass = ic->d(); nFormals = compiledFunction->nFormals; } @@ -110,20 +129,25 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr } - internalClass = engine->internalClasses[EngineBase::Class_CallContext]; + internalClass = engine->internalClasses(EngineBase::Class_CallContext); // first locals const quint32_le *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) - internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); + internalClass = internalClass->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); Scope scope(engine); ScopedString arg(scope); for (const QString ¶meterName : parameterNames) { - arg = engine->newString(parameterName); - internalClass = internalClass->addMember(arg, Attr_NotConfigurable); + arg = engine->newIdentifier(parameterName); + internalClass = internalClass->addMember(arg->propertyKey(), Attr_NotConfigurable); } nFormals = parameters.size(); } +QQmlSourceLocation Function::sourceLocation() const +{ + return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); +} + QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 59a94e5dde..d542ce752f 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -51,10 +51,8 @@ // #include "qv4global_p.h" -#include <private/qqmlglobal_p.h> #include <private/qv4compileddata_p.h> #include <private/qv4context_p.h> -#include <private/qv4vme_moth_p.h> namespace JSC { class MacroAssemblerCodeRef; @@ -62,31 +60,29 @@ class MacroAssemblerCodeRef; QT_BEGIN_NAMESPACE +struct QQmlSourceLocation; + namespace QV4 { struct Q_QML_EXPORT Function { const CompiledData::Function *compiledFunction; CompiledData::CompilationUnit *compilationUnit; - ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) { - return Moth::VME::exec(this, thisObject, argv, argc, context); - } + ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context); - typedef ReturnedValue (*Code)(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc); - Code code; - const uchar *codeData; + const char *codeData; typedef ReturnedValue (*JittedCode)(CppStackFrame *, ExecutionEngine *); JittedCode jittedCode; JSC::MacroAssemblerCodeRef *codeRef; // first nArguments names in internalClass are the actual arguments - InternalClass *internalClass; + Heap::InternalClass *internalClass; uint nFormals; int interpreterCallCount = 0; bool hasQmlDependencies; - Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr); + Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); ~Function(); // used when dynamically assigning signal handlers (QQmlConnection) @@ -98,13 +94,11 @@ struct Q_QML_EXPORT Function { inline QString sourceFile() const { return compilationUnit->fileName(); } inline QUrl finalUrl() const { return compilationUnit->finalUrl(); } - inline bool usesArgumentsObject() const { return compiledFunction->flags & CompiledData::Function::UsesArgumentsObject; } inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; } + inline bool isArrowFunction() const { return compiledFunction->flags & CompiledData::Function::IsArrowFunction; } + inline bool isGenerator() const { return compiledFunction->flags & CompiledData::Function::IsGenerator; } - QQmlSourceLocation sourceLocation() const - { - return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); - } + QQmlSourceLocation sourceLocation() const; Function *nestedFunction() const { diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 83608070ec..2aca7c2849 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -41,6 +41,7 @@ #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" #include "qv4function_p.h" +#include "qv4symbol_p.h" #include <private/qv4mm_p.h> #include "qv4arrayobject_p.h" @@ -57,6 +58,7 @@ #include "private/qlocale_tools_p.h" #include "private/qqmlbuiltinfunctions_p.h" #include <private/qv4jscall_p.h> +#include <private/qv4vme_moth_p.h> #include <QtCore/QDebug> #include <algorithm> @@ -72,31 +74,38 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(const QV4::FunctionObject *, const Value *thisObject, const Value *argv, int argc)) { jsCall = code; - jsConstruct = QV4::FunctionObject::callAsConstructor; + jsConstruct = QV4::FunctionObject::virtualCallAsConstructor; Object::init(); this->scope.set(scope->engine(), scope->d()); Scope s(scope->engine()); ScopedFunctionObject f(s, this); - f->init(name, false); + if (name) + f->setName(name); } void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, bool createProto) { - jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call; - jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor; + jsCall = vtable()->call; + jsConstruct = vtable()->callAsConstructor; Object::init(); this->scope.set(scope->engine(), scope->d()); Scope s(scope->engine()); ScopedFunctionObject f(s, this); - f->init(name, createProto); + if (name) + f->setName(name); + + if (createProto) + f->createDefaultPrototypeProperty(Heap::FunctionObject::Index_Prototype, Heap::FunctionObject::Index_ProtoConstructor); } + + void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, bool createProto) { - jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call; - jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor; + jsCall = vtable()->call; + jsConstruct = vtable()->callAsConstructor; Object::init(); setFunction(function); @@ -104,7 +113,11 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function Scope s(scope->engine()); ScopedString name(s, function->name()); ScopedFunctionObject f(s, this); - f->init(name, createProto); + if (name) + f->setName(name); + + if (createProto) + f->createDefaultPrototypeProperty(Heap::FunctionObject::Index_Prototype, Heap::FunctionObject::Index_ProtoConstructor); } void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &name, bool createProto) @@ -116,12 +129,12 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &nam void Heap::FunctionObject::init() { - jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call; - jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor; + jsCall = vtable()->call; + jsConstruct = vtable()->callAsConstructor; Object::init(); this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d()); - Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()) == Index_Prototype); + Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()->propertyKey()) == Index_Prototype); setProperty(internalClass->engine, Index_Prototype, Primitive::undefinedValue()); } @@ -139,23 +152,16 @@ void Heap::FunctionObject::destroy() Object::destroy(); } -void FunctionObject::init(String *n, bool createProto) +void FunctionObject::createDefaultPrototypeProperty(uint protoSlot, uint protoConstructorSlot) { - Scope s(internalClass()->engine); - ScopedValue protectThis(s, this); + Scope s(this); - Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()) == Heap::FunctionObject::Index_Prototype); - if (createProto) { - ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses[EngineBase::Class_ObjectProto], s.engine->objectPrototype())); - Q_ASSERT(s.engine->internalClasses[EngineBase::Class_ObjectProto]->find(s.engine->id_constructor()) == Heap::FunctionObject::Index_ProtoConstructor); - proto->setProperty(Heap::FunctionObject::Index_ProtoConstructor, d()); - setProperty(Heap::FunctionObject::Index_Prototype, proto); - } else { - setProperty(Heap::FunctionObject::Index_Prototype, Primitive::undefinedValue()); - } + Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()->propertyKey()) == protoSlot); + Q_ASSERT(s.engine->internalClasses(EngineBase::Class_ObjectProto)->find(s.engine->id_constructor()->propertyKey()) == protoConstructorSlot); - if (n) - defineReadonlyConfigurableProperty(s.engine->id_name(), *n); + ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses(EngineBase::Class_ObjectProto))); + proto->setProperty(protoConstructorSlot, d()); + setProperty(protoSlot, proto); } ReturnedValue FunctionObject::name() const @@ -163,19 +169,48 @@ ReturnedValue FunctionObject::name() const return get(scope()->internalClass->engine->id_name()); } -ReturnedValue FunctionObject::callAsConstructor(const FunctionObject *f, const Value *, int) +ReturnedValue FunctionObject::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) { return f->engine()->throwTypeError(); } -ReturnedValue FunctionObject::call(const FunctionObject *, const Value *, const Value *, int) +ReturnedValue FunctionObject::virtualCall(const FunctionObject *, const Value *, const Value *, int) { return Encode::undefined(); } Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function) { - return scope->engine()->memoryManager->allocObject<ScriptFunction>(scope, function); + return scope->engine()->memoryManager->allocate<ScriptFunction>(scope, function); +} + +Heap::FunctionObject *FunctionObject::createConstructorFunction(ExecutionContext *scope, Function *function, bool isDerivedConstructor) +{ + if (!function) { + Heap::DefaultClassConstructorFunction *c = scope->engine()->memoryManager->allocate<DefaultClassConstructorFunction>(scope); + c->isDerivedConstructor = isDerivedConstructor; + return c; + } + Heap::ConstructorFunction *c = scope->engine()->memoryManager->allocate<ConstructorFunction>(scope, function); + c->isDerivedConstructor = isDerivedConstructor; + return c; +} + +Heap::FunctionObject *FunctionObject::createMemberFunction(ExecutionContext *scope, Function *function) +{ + return scope->engine()->memoryManager->allocate<MemberFunction>(scope, function); +} + +Heap::FunctionObject *FunctionObject::createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount) +{ + Scope scope(engine); + ScopedString name(scope, nameOrSymbol); + if (!name) + name = engine->newString(QChar::fromLatin1('[') + nameOrSymbol->toQString().midRef(1) + QChar::fromLatin1(']')); + + ScopedFunctionObject function(scope, engine->memoryManager->allocate<FunctionObject>(engine->rootContext(), name, code)); + function->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(argumentCount)); + return function->d(); } bool FunctionObject::isBinding() const @@ -201,10 +236,8 @@ void Heap::FunctionCtor::init(QV4::ExecutionContext *scope) } // 15.3.2 -ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +QQmlRefPointer<CompiledData::CompilationUnit> FunctionCtor::parse(ExecutionEngine *engine, const Value *argv, int argc, Type t) { - Scope scope(f->engine()); - QString arguments; QString body; if (argc > 0) { @@ -215,42 +248,58 @@ ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Val } body = argv[argc - 1].toQString(); } - if (scope.engine->hasException) - return Encode::undefined(); + if (engine->hasException) + return nullptr; - QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1Char('}'); + QString function = (t == Type_Function ? QLatin1String("function anonymous(") : QLatin1String("function* anonymous(")) + arguments + QLatin1String("\n){") + body + QLatin1String("\n}"); - QQmlJS::Engine ee, *engine = ⅇ - QQmlJS::Lexer lexer(engine); + QQmlJS::Engine ee; + QQmlJS::Lexer lexer(&ee); lexer.setCode(function, 1, false); - QQmlJS::Parser parser(engine); + QQmlJS::Parser parser(&ee); const bool parsed = parser.parseExpression(); - if (!parsed) - return scope.engine->throwSyntaxError(QLatin1String("Parse error")); + if (!parsed) { + engine->throwSyntaxError(QLatin1String("Parse error")); + return nullptr; + } QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(parser.rootNode()); - if (!fe) - return scope.engine->throwSyntaxError(QLatin1String("Parse error")); + if (!fe) { + engine->throwSyntaxError(QLatin1String("Parse error")); + return nullptr; + } - Compiler::Module module(scope.engine->debugger() != nullptr); + Compiler::Module module(engine->debugger() != nullptr); Compiler::JSUnitGenerator jsGenerator(&module); - RuntimeCodegen cg(scope.engine, &jsGenerator, false); + RuntimeCodegen cg(engine, &jsGenerator, false); cg.generateFromFunctionExpression(QString(), function, fe, &module); - QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = cg.generateCompilationUnit(); - Function *vmf = compilationUnit->linkToEngine(scope.engine); + if (engine->hasException) + return nullptr; - ExecutionContext *global = scope.engine->rootContext(); + return cg.generateCompilationUnit(); +} + +ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +{ + ExecutionEngine *engine = f->engine(); + + QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Function); + if (engine->hasException) + return Encode::undefined(); + + Function *vmf = compilationUnit->linkToEngine(engine); + ExecutionContext *global = engine->scriptContext(); return Encode(FunctionObject::createScriptFunction(global, vmf)); } // 15.3.1: This is equivalent to new Function(...) -ReturnedValue FunctionCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue FunctionCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) { - return callAsConstructor(f, argv, argc); + return virtualCallAsConstructor(f, argv, argc, f); } DEFINE_OBJECT_VTABLE(FunctionPrototype); @@ -268,13 +317,14 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + defineReadonlyConfigurableProperty(engine->id_name(), *engine->id_empty()); defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("apply"), method_apply, 2); defineDefaultProperty(QStringLiteral("call"), method_call, 1); defineDefaultProperty(QStringLiteral("bind"), method_bind, 1); - + defineDefaultProperty(engine->symbol_hasInstance(), method_hasInstance, 1, Attr_ReadOnly); } ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -284,7 +334,19 @@ ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const if (!fun) return v4->throwTypeError(); - return Encode(v4->newString(QStringLiteral("function() { [code] }"))); + const Scope scope(fun->engine()); + const ScopedString scopedFunctionName(scope, fun->name()); + const QString functionName(scopedFunctionName ? scopedFunctionName->toQString() : QString()); + QString functionAsString = QStringLiteral("function"); + + // If fun->name() is empty, then there is no function name + // to append because the function is anonymous. + if (!functionName.isEmpty()) + functionAsString.append(QLatin1Char(' ') + functionName); + + functionAsString.append(QStringLiteral("() { [code] }")); + + return Encode(v4->newString(functionAsString)); } ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc) @@ -304,7 +366,7 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons uint len = arr->getLength(); Scope scope(v4); - Value *arguments = v4->jsAlloca(len); + Value *arguments = scope.alloc(len); if (len) { if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) { QV4::ArgumentsObject *a = arr->cast<ArgumentsObject>(); @@ -323,7 +385,7 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons arguments[i] = Primitive::undefinedValue(); } else { for (quint32 i = 0; i < len; ++i) - arguments[i] = arr->getIndexed(i); + arguments[i] = arr->get(i); } } @@ -383,18 +445,50 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu return bound->asReturnedValue(); } +ReturnedValue FunctionPrototype::method_hasInstance(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + if (!argc) + return false; + const Object *o = thisObject->as<Object>(); + if (!o) + return f->engine()->throwTypeError(); + + return Object::virtualInstanceOf(o, argv[0]); +} + DEFINE_OBJECT_VTABLE(ScriptFunction); -ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc) +ReturnedValue ScriptFunction::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *v4 = fo->engine(); const ScriptFunction *f = static_cast<const ScriptFunction *>(fo); + Q_ASSERT(newTarget->isFunctionObject()); + const FunctionObject *nt = static_cast<const FunctionObject *>(newTarget); Scope scope(v4); - InternalClass *ic = f->classForConstructor(); + Scoped<InternalClass> ic(scope); + if (nt->d() == f->d()) { + ic = f->classForConstructor(); + } else { + const Object *o = nt->d()->protoProperty(); + ic = scope.engine->internalClasses(EngineBase::Class_Object); + if (o) + ic = ic->changePrototype(o->d()); + } ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(ic)); - ReturnedValue result = Moth::VME::exec(fo, thisObject, argv, argc); + CppStackFrame frame; + frame.init(v4, f->function(), argv, argc); + frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), + thisObject, + newTarget ? *newTarget : Primitive::undefinedValue()); + + frame.push(); + v4->jsStackTop += frame.requiredJSStackFrameSize(); + + ReturnedValue result = Moth::VME::exec(&frame, v4); + + frame.pop(); if (Q_UNLIKELY(v4->hasException)) return Encode::undefined(); @@ -403,9 +497,23 @@ ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const return result; } -ReturnedValue ScriptFunction::call(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc) +ReturnedValue ScriptFunction::virtualCall(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc) { - return Moth::VME::exec(fo, thisObject, argv, argc); + ExecutionEngine *engine = fo->engine(); + CppStackFrame frame; + frame.init(engine, fo->function(), argv, argc); + frame.setupJSFrame(engine->jsStackTop, *fo, fo->scope(), + thisObject ? *thisObject : Primitive::undefinedValue(), + Primitive::undefinedValue()); + + frame.push(); + engine->jsStackTop += frame.requiredJSStackFrameSize(); + + ReturnedValue result = Moth::VME::exec(&frame, engine); + + frame.pop(); + + return result; } void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function) @@ -415,38 +523,123 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function setFunction(function); Q_ASSERT(function); - Q_ASSERT(function->code); Scope s(scope); ScopedFunctionObject f(s, this); ScopedString name(s, function->name()); - f->init(name, true); - Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()) == Index_Length); - setProperty(s.engine, Index_Length, Primitive::fromInt32(f->formalParameterCount())); - - if (function->isStrict()) { - ScopedProperty pd(s); - pd->value = s.engine->thrower(); - pd->set = s.engine->thrower(); - f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - f->insertMember(s.engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - } + if (name) + f->setName(name); + f->createDefaultPrototypeProperty(Heap::FunctionObject::Index_Prototype, Heap::FunctionObject::Index_ProtoConstructor); + + Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()->propertyKey()) == Index_Length); + setProperty(s.engine, Index_Length, Primitive::fromInt32(int(function->compiledFunction->length))); } -InternalClass *ScriptFunction::classForConstructor() const +Heap::InternalClass *ScriptFunction::classForConstructor() const { const Object *o = d()->protoProperty(); - InternalClass *ic = d()->cachedClassForConstructor; - if (ic && ic->prototype == o->d()) - return ic; + if (d()->cachedClassForConstructor && d()->cachedClassForConstructor->prototype == o->d()) + return d()->cachedClassForConstructor; - ic = engine()->internalClasses[EngineBase::Class_Object]; + Scope scope(engine()); + Scoped<InternalClass> ic(scope, engine()->internalClasses(EngineBase::Class_Object)); if (o) ic = ic->changePrototype(o->d()); - d()->cachedClassForConstructor = ic; + d()->cachedClassForConstructor.set(scope.engine, ic->d()); + + return ic->d(); +} + +DEFINE_OBJECT_VTABLE(ConstructorFunction); + +ReturnedValue ConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) +{ + const ConstructorFunction *c = static_cast<const ConstructorFunction *>(f); + if (!c->d()->isDerivedConstructor) + return ScriptFunction::virtualCallAsConstructor(f, argv, argc, newTarget); + + ExecutionEngine *v4 = f->engine(); + + CppStackFrame frame; + frame.init(v4, f->function(), argv, argc); + frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), + Primitive::undefinedValue(), + newTarget ? *newTarget : Primitive::undefinedValue()); + + frame.push(); + v4->jsStackTop += frame.requiredJSStackFrameSize(); + + ReturnedValue result = Moth::VME::exec(&frame, v4); + + frame.pop(); + + if (Q_UNLIKELY(v4->hasException)) + return Encode::undefined(); + else if (Value::fromReturnedValue(result).isObject()) + return result; + else if (!Value::fromReturnedValue(result).isUndefined()) + return v4->throwTypeError(); + return frame.jsFrame->thisObject.asReturnedValue(); +} + +ReturnedValue ConstructorFunction::virtualCall(const FunctionObject *f, const Value *, const Value *, int) +{ + return f->engine()->throwTypeError(QStringLiteral("Cannot call a class constructor without |new|")); +} - return ic; +DEFINE_OBJECT_VTABLE(MemberFunction); + +ReturnedValue MemberFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) +{ + return f->engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); +} + +DEFINE_OBJECT_VTABLE(DefaultClassConstructorFunction); + +ReturnedValue DefaultClassConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) +{ + const DefaultClassConstructorFunction *c = static_cast<const DefaultClassConstructorFunction *>(f); + ExecutionEngine *v4 = f->engine(); + + Scope scope(v4); + + if (!c->d()->isDerivedConstructor) { + ScopedObject proto(scope, static_cast<const Object *>(newTarget) ->get(scope.engine->id_prototype())); + ScopedObject c(scope, scope.engine->newObject()); + c->setPrototypeUnchecked(proto); + return c->asReturnedValue(); + } + + ScopedFunctionObject super(scope, f->getPrototypeOf()); + Q_ASSERT(super->isFunctionObject()); + + CppStackFrame frame; + frame.init(v4, nullptr, argv, argc); + frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), + Primitive::undefinedValue(), + newTarget ? *newTarget : Primitive::undefinedValue(), argc, argc); + + frame.push(); + v4->jsStackTop += frame.requiredJSStackFrameSize(argc); + + // Do a super call + ReturnedValue result = super->callAsConstructor(argv, argc, newTarget); + + frame.pop(); + + if (Q_UNLIKELY(v4->hasException)) + return Encode::undefined(); + else if (Value::fromReturnedValue(result).isObject()) + return result; + else if (!Value::fromReturnedValue(result).isUndefined()) + return v4->throwTypeError(); + return frame.jsFrame->thisObject.asReturnedValue(); +} + +ReturnedValue DefaultClassConstructorFunction::virtualCall(const FunctionObject *f, const Value *, const Value *, int) +{ + return f->engine()->throwTypeError(QStringLiteral("Cannot call a class constructor without |new|")); } DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); @@ -479,7 +672,7 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } -ReturnedValue BoundFunction::call(const FunctionObject *fo, const Value *, const Value *argv, int argc) +ReturnedValue BoundFunction::virtualCall(const FunctionObject *fo, const Value *, const Value *argv, int argc) { const BoundFunction *f = static_cast<const BoundFunction *>(fo); Scope scope(f->engine()); @@ -500,7 +693,7 @@ ReturnedValue BoundFunction::call(const FunctionObject *fo, const Value *, const return target->call(jsCallData); } -ReturnedValue BoundFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc) +ReturnedValue BoundFunction::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *) { const BoundFunction *f = static_cast<const BoundFunction *>(fo); Scope scope(f->engine()); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 32e71a175b..1acb1df0b4 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -64,17 +64,14 @@ namespace QV4 { struct IndexedBuiltinFunction; struct JSCallData; -typedef ReturnedValue (*jsCallFunction)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); -typedef ReturnedValue (*jsConstructFunction)(const FunctionObject *, const Value *argv, int argc); - namespace Heap { #define FunctionObjectMembers(class, Member) \ Member(class, Pointer, ExecutionContext *, scope) \ Member(class, NoMark, Function *, function) \ - Member(class, NoMark, jsCallFunction, jsCall) \ - Member(class, NoMark, jsConstructFunction, jsConstruct) + Member(class, NoMark, VTable::Call, jsCall) \ + Member(class, NoMark, VTable::CallAsConstructor, jsConstruct) DECLARE_HEAP_OBJECT(FunctionObject, Object) { DECLARE_MARKOBJECTS(FunctionObject); @@ -111,14 +108,30 @@ struct IndexedBuiltinFunction : FunctionObject { uint index; }; -struct ScriptFunction : FunctionObject { +#define ScriptFunctionMembers(class, Member) \ + Member(class, Pointer, InternalClass *, cachedClassForConstructor) + +DECLARE_HEAP_OBJECT(ScriptFunction, FunctionObject) { + DECLARE_MARKOBJECTS(ScriptFunction) enum { Index_Name = FunctionObject::Index_Prototype + 1, Index_Length }; void init(QV4::ExecutionContext *scope, Function *function); +}; - QV4::InternalClass *cachedClassForConstructor; +struct ConstructorFunction : ScriptFunction +{ + bool isDerivedConstructor; +}; + +struct MemberFunction : ScriptFunction +{ +}; + +struct DefaultClassConstructorFunction : FunctionObject +{ + bool isDerivedConstructor; }; #define BoundFunctionMembers(class, Member) \ @@ -152,25 +165,26 @@ struct Q_QML_EXPORT FunctionObject: Object { unsigned int formalParameterCount() const { return d()->formalParameterCount(); } unsigned int varCount() const { return d()->varCount(); } - void init(String *name, bool createProto); + void setName(String *name) { + defineReadonlyConfigurableProperty(engine()->id_name(), *name); + } + void createDefaultPrototypeProperty(uint protoSlot, uint protoConstructorSlot); inline ReturnedValue callAsConstructor(const JSCallData &data) const; - ReturnedValue callAsConstructor(const Value *argv, int argc) const { - return d()->jsConstruct(this, argv, argc); + ReturnedValue callAsConstructor(const Value *argv, int argc, const Value *newTarget = nullptr) const { + return d()->jsConstruct(this, argv, argc, newTarget ? newTarget : this); } inline ReturnedValue call(const JSCallData &data) const; ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const { return d()->jsCall(this, thisObject, argv, argc); } - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); - static Heap::FunctionObject *createBuiltinFunction(ExecutionContext *scope, String *name, - ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)) - { - return scope->engine()->memoryManager->allocObject<FunctionObject>(scope, name, code); - } + static Heap::FunctionObject *createConstructorFunction(ExecutionContext *scope, Function *function, bool isDerivedConstructor); + static Heap::FunctionObject *createMemberFunction(ExecutionContext *scope, Function *function); + static Heap::FunctionObject *createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount); bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } bool isBinding() const; @@ -181,7 +195,7 @@ struct Q_QML_EXPORT FunctionObject: Object { template<> inline const FunctionObject *Value::as() const { - return isManaged() && m()->vtable()->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : nullptr; + return isManaged() && m()->internalClass->vtable->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : nullptr; } @@ -189,8 +203,14 @@ struct FunctionCtor: FunctionObject { V4_OBJECT2(FunctionCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +protected: + enum Type { + Type_Function, + Type_Generator + }; + static QQmlRefPointer<CompiledData::CompilationUnit> parse(ExecutionEngine *engine, const Value *argv, int argc, Type t = Type_Function); }; struct FunctionPrototype: FunctionObject @@ -203,6 +223,7 @@ struct FunctionPrototype: FunctionObject static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_bind(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct IndexedBuiltinFunction : FunctionObject @@ -224,27 +245,46 @@ struct ScriptFunction : FunctionObject { V4_INTERNALCLASS(ScriptFunction) enum { NInlineProperties = 3 }; - static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + + Heap::InternalClass *classForConstructor() const; +}; + +struct ConstructorFunction : ScriptFunction { + V4_OBJECT2(ConstructorFunction, ScriptFunction) + V4_INTERNALCLASS(ConstructorFunction) + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; - InternalClass *classForConstructor() const; +struct MemberFunction : ScriptFunction { + V4_OBJECT2(MemberFunction, ScriptFunction) + V4_INTERNALCLASS(MemberFunction) + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); }; +struct DefaultClassConstructorFunction : FunctionObject { + V4_OBJECT2(DefaultClassConstructorFunction, FunctionObject) + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + struct BoundFunction: FunctionObject { V4_OBJECT2(BoundFunction, FunctionObject) static Heap::BoundFunction *create(ExecutionContext *scope, FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs) { - return scope->engine()->memoryManager->allocObject<BoundFunction>(scope, target, boundThis, boundArgs); + return scope->engine()->memoryManager->allocate<BoundFunction>(scope, target, boundThis, boundArgs); } Heap::FunctionObject *target() const { return d()->target; } Value boundThis() const { return d()->boundThis; } Heap::MemberData *boundArgs() const { return d()->boundArgs; } - static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; } diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp new file mode 100644 index 0000000000..8c3cd8b863 --- /dev/null +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qv4generatorobject_p.h> +#include <qv4symbol_p.h> +#include <qv4iterator_p.h> +#include <qv4jscall_p.h> +#include <qv4vme_moth_p.h> + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(GeneratorFunctionCtor); +DEFINE_OBJECT_VTABLE(GeneratorFunction); +DEFINE_OBJECT_VTABLE(GeneratorObject); + +void Heap::GeneratorFunctionCtor::init(QV4::ExecutionContext *scope) +{ + Heap::FunctionObject::init(scope, QStringLiteral("GeneratorFunction")); +} + +ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +{ + ExecutionEngine *engine = f->engine(); + + QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Generator); + if (engine->hasException) + return Encode::undefined(); + + Function *vmf = compilationUnit->linkToEngine(engine); + ExecutionContext *global = engine->scriptContext(); + return Encode(GeneratorFunction::create(global, vmf)); +} + +// 15.3.1: This is equivalent to new Function(...) +ReturnedValue GeneratorFunctionCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + return virtualCallAsConstructor(f, argv, argc, f); +} + +Heap::FunctionObject *GeneratorFunction::create(ExecutionContext *context, Function *function) +{ + Scope scope(context); + Scoped<GeneratorFunction> g(scope, context->engine()->memoryManager->allocate<GeneratorFunction>(context, function)); + ScopedObject proto(scope, scope.engine->newObject()); + proto->setPrototypeOf(scope.engine->generatorPrototype()); + g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable); + g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype()))); + return g->d(); +} + +ReturnedValue GeneratorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) +{ + return f->engine()->throwTypeError(); +} + +ReturnedValue GeneratorFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + const GeneratorFunction *gf = static_cast<const GeneratorFunction *>(f); + Function *function = gf->function(); + ExecutionEngine *engine = gf->engine(); + + // We need to set up a separate stack for the generator, as it's being re-entered + uint stackSize = argc // space for the original arguments + + CppStackFrame::requiredJSStackFrameSize(function); // space for the JS stack frame + + size_t requiredMemory = sizeof(GeneratorObject::Data) - sizeof(Value) + sizeof(Value) * stackSize; + + Scope scope(gf); + Scoped<GeneratorObject> g(scope, scope.engine->memoryManager->allocManaged<GeneratorObject>(requiredMemory, scope.engine->classes[EngineBase::Class_GeneratorObject])); + g->setPrototypeOf(ScopedObject(scope, gf->get(scope.engine->id_prototype()))); + + Heap::GeneratorObject *gp = g->d(); + gp->stack.size = stackSize; + gp->stack.alloc = stackSize; + + // copy original arguments + memcpy(gp->stack.values, argv, argc*sizeof(Value)); + gp->cppFrame.init(engine, function, gp->stack.values, argc); + gp->cppFrame.setupJSFrame(&gp->stack.values[argc], *gf, gf->scope(), + thisObject ? *thisObject : Primitive::undefinedValue(), + Primitive::undefinedValue()); + + gp->cppFrame.push(); + + Moth::VME::interpret(&gp->cppFrame, engine, function->codeData); + gp->state = GeneratorState::SuspendedStart; + + gp->cppFrame.pop(); + return g->asReturnedValue(); +} + + +void Heap::GeneratorPrototype::init() +{ + Heap::FunctionObject::init(); +} + + +void GeneratorPrototype::init(ExecutionEngine *engine, Object *ctor) +{ + Scope scope(engine); + ScopedValue v(scope); + + ScopedObject ctorProto(scope, engine->newObject(engine->newInternalClass(Object::staticVTable(), engine->functionPrototype()))); + + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), ctorProto); + + ctorProto->defineDefaultProperty(QStringLiteral("constructor"), (v = ctor), Attr_ReadOnly_ButConfigurable); + ctorProto->defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newIdentifier(QStringLiteral("GeneratorFunction"))), Attr_ReadOnly_ButConfigurable); + ctorProto->defineDefaultProperty(engine->id_prototype(), (v = this), Attr_ReadOnly_ButConfigurable); + + setPrototypeOf(engine->iteratorPrototype()); + defineDefaultProperty(QStringLiteral("constructor"), ctorProto, Attr_ReadOnly_ButConfigurable); + defineDefaultProperty(QStringLiteral("next"), method_next, 1); + defineDefaultProperty(QStringLiteral("return"), method_return, 1); + defineDefaultProperty(QStringLiteral("throw"), method_throw, 1); + defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newString(QStringLiteral("Generator"))), Attr_ReadOnly_ButConfigurable); +} + +ReturnedValue GeneratorPrototype::method_next(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *engine = f->engine(); + const GeneratorObject *g = thisObject->as<GeneratorObject>(); + if (!g || g->d()->state == GeneratorState::Executing) + return engine->throwTypeError(); + Heap::GeneratorObject *gp = g->d(); + + if (gp->state == GeneratorState::Completed) + return IteratorPrototype::createIterResultObject(engine, Primitive::undefinedValue(), true); + + return g->resume(engine, argc ? argv[0] : Primitive::undefinedValue()); +} + +ReturnedValue GeneratorPrototype::method_return(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *engine = f->engine(); + const GeneratorObject *g = thisObject->as<GeneratorObject>(); + if (!g || g->d()->state == GeneratorState::Executing) + return engine->throwTypeError(); + + Heap::GeneratorObject *gp = g->d(); + + if (gp->state == GeneratorState::SuspendedStart) + gp->state = GeneratorState::Completed; + + if (gp->state == GeneratorState::Completed) + return IteratorPrototype::createIterResultObject(engine, argc ? argv[0] : Primitive::undefinedValue(), true); + + // the bytecode interpreter interprets an exception with empty value as + // a yield called with return() + engine->throwError(Primitive::emptyValue()); + + return g->resume(engine, argc ? argv[0]: Primitive::undefinedValue()); +} + +ReturnedValue GeneratorPrototype::method_throw(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *engine = f->engine(); + const GeneratorObject *g = thisObject->as<GeneratorObject>(); + if (!g || g->d()->state == GeneratorState::Executing) + return engine->throwTypeError(); + + Heap::GeneratorObject *gp = g->d(); + + engine->throwError(argc ? argv[0]: Primitive::undefinedValue()); + + if (gp->state == GeneratorState::SuspendedStart || gp->state == GeneratorState::Completed) { + gp->state = GeneratorState::Completed; + return Encode::undefined(); + } + + return g->resume(engine, Primitive::undefinedValue()); +} + +ReturnedValue GeneratorObject::resume(ExecutionEngine *engine, const Value &arg) const +{ + Heap::GeneratorObject *gp = d(); + gp->state = GeneratorState::Executing; + gp->cppFrame.parent = engine->currentStackFrame; + engine->currentStackFrame = &gp->cppFrame; + + Q_ASSERT(gp->cppFrame.yield != nullptr); + const char *code = gp->cppFrame.yield; + gp->cppFrame.yield = nullptr; + gp->cppFrame.jsFrame->accumulator = arg; + + Scope scope(engine); + ScopedValue result(scope, Moth::VME::interpret(&gp->cppFrame, engine, code)); + + engine->currentStackFrame = gp->cppFrame.parent; + + bool done = (gp->cppFrame.yield == nullptr); + gp->state = done ? GeneratorState::Completed : GeneratorState::SuspendedYield; + if (engine->hasException) + return Encode::undefined(); + return IteratorPrototype::createIterResultObject(engine, result, done); +} + +DEFINE_OBJECT_VTABLE(MemberGeneratorFunction); + +Heap::FunctionObject *MemberGeneratorFunction::create(ExecutionContext *context, Function *function) +{ + Scope scope(context); + Scoped<GeneratorFunction> g(scope, context->engine()->memoryManager->allocate<MemberGeneratorFunction>(context, function)); + ScopedObject proto(scope, scope.engine->newObject()); + proto->setPrototypeOf(scope.engine->generatorPrototype()); + g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable); + g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype()))); + return g->d(); +} + +ReturnedValue MemberGeneratorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) +{ + return f->engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); +} diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h new file mode 100644 index 0000000000..a97050473c --- /dev/null +++ b/src/qml/jsruntime/qv4generatorobject_p.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4GENERATOROBJECT_P_H +#define QV4GENERATOROBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4functionobject_p.h" +#include "qv4stackframe_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +enum class GeneratorState { + Undefined, + SuspendedStart, + SuspendedYield, + Executing, + Completed +}; + +namespace Heap { + +struct GeneratorFunctionCtor : FunctionObject { + void init(QV4::ExecutionContext *scope); +}; + +struct GeneratorFunction : ScriptFunction { +}; + +struct MemberGeneratorFunction : ScriptFunction { +}; + +struct GeneratorPrototype : FunctionObject { + void init(); +}; + +#define GeneratorObjectMembers(class, Member) \ + Member(class, Pointer, ExecutionContext *, context) \ + Member(class, Pointer, GeneratorFunction *, function) \ + Member(class, NoMark, GeneratorState, state) \ + Member(class, NoMark, CppStackFrame, cppFrame) \ + Member(class, ValueArray, ValueArray, stack) + +DECLARE_HEAP_OBJECT(GeneratorObject, Object) { + DECLARE_MARKOBJECTS(GeneratorObject); +}; + +} + +struct GeneratorFunctionCtor : FunctionCtor +{ + V4_OBJECT2(GeneratorFunctionCtor, FunctionCtor) + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct GeneratorFunction : ScriptFunction +{ + V4_OBJECT2(GeneratorFunction, ScriptFunction) + V4_INTERNALCLASS(GeneratorFunction) + + static Heap::FunctionObject *create(ExecutionContext *scope, Function *function); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct MemberGeneratorFunction : GeneratorFunction +{ + V4_OBJECT2(MemberGeneratorFunction, GeneratorFunction) + V4_INTERNALCLASS(MemberGeneratorFunction) + + static Heap::FunctionObject *create(ExecutionContext *scope, Function *function); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); +}; + +struct GeneratorPrototype : Object +{ + void init(ExecutionEngine *engine, Object *ctor); + + static ReturnedValue method_next(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_return(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_throw(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + + +struct GeneratorObject : Object { + V4_OBJECT2(GeneratorObject, Object) + Q_MANAGED_TYPE(GeneratorObject) + V4_INTERNALCLASS(GeneratorObject) + V4_PROTOTYPE(generatorPrototype) + + ReturnedValue resume(ExecutionEngine *engine, const Value &arg) const; +}; + +} + +QT_END_NAMESPACE + +#endif // QV4GENERATORFUNCTION_P_H + diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 1fa4bae049..e3b3423a9d 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -153,6 +153,11 @@ namespace Compiler { struct Module; struct Context; struct JSUnitGenerator; + class Codegen; +} + +namespace Moth { + class BytecodeGenerator; } namespace Heap { @@ -160,13 +165,17 @@ namespace Heap { struct MemberData; struct ArrayData; + struct StringOrSymbol; struct String; + struct Symbol; struct Object; struct ObjectPrototype; struct ExecutionContext; struct CallContext; + struct QmlContext; struct ScriptFunction; + struct InternalClass; struct BooleanObject; struct NumberObject; @@ -188,14 +197,19 @@ namespace Heap { template <typename T, size_t> struct Pointer; } +struct CppStackFrame; class MemoryManager; class ExecutableAllocator; +struct PropertyKey; +struct StringOrSymbol; struct String; +struct Symbol; struct Object; struct ObjectPrototype; struct ObjectIterator; struct ExecutionContext; struct CallContext; +struct QmlContext; struct ScriptFunction; struct InternalClass; struct Property; @@ -236,6 +250,7 @@ struct Scope; struct ScopedValue; template<typename T> struct Scoped; typedef Scoped<String> ScopedString; +typedef Scoped<StringOrSymbol> ScopedStringOrSymbol; typedef Scoped<Object> ScopedObject; typedef Scoped<ArrayObject> ScopedArrayObject; typedef Scoped<FunctionObject> ScopedFunctionObject; @@ -244,6 +259,7 @@ typedef Scoped<ExecutionContext> ScopedContext; struct PersistentValueStorage; class PersistentValue; class WeakValue; +struct MarkStack; struct IdentifierTable; class RegExpCache; @@ -357,6 +373,12 @@ struct Q_QML_EXPORT StackFrame { }; typedef QVector<StackFrame> StackTrace; +enum class ObjectLiteralArgument { + Value, + Getter, + Setter +}; + } Q_DECLARE_TYPEINFO(QV4::PropertyAttributes, Q_PRIMITIVE_TYPE); diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index f419ab53fe..43895011f3 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -335,7 +335,7 @@ void Heap::EvalFunction::init(QV4::ExecutionContext *scope) Scope s(scope); Heap::FunctionObject::init(scope, s.engine->id_eval()); ScopedFunctionObject f(s, this); - f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1)); + f->defineReadonlyConfigurableProperty(s.engine->id_length(), Primitive::fromInt32(1)); } ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, bool directCall) const @@ -351,7 +351,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, if (!directCall) { // the context for eval should be the global scope - ctx = v4->rootContext(); + ctx = v4->scriptContext(); } String *scode = argv[0].stringValue(); @@ -361,7 +361,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, const QString code = scode->toQString(); bool inheritContext = !isStrict; - Script script(ctx, QV4::Compiler::EvalCode, code, QStringLiteral("eval code")); + Script script(ctx, QV4::Compiler::ContextType::Eval, code, QStringLiteral("eval code")); script.strictMode = (directCall && isStrict); script.inheritContext = inheritContext; script.parse(); @@ -384,7 +384,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, } -ReturnedValue EvalFunction::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +ReturnedValue EvalFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) { // indirect call return static_cast<const EvalFunction *>(f)->evalCall(thisObject, argv, argc, false); diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h index fd1820c23c..021b445955 100644 --- a/src/qml/jsruntime/qv4globalobject_p.h +++ b/src/qml/jsruntime/qv4globalobject_p.h @@ -71,7 +71,7 @@ struct Q_QML_EXPORT EvalFunction : FunctionObject ReturnedValue evalCall(const Value *thisObject, const Value *argv, int argc, bool directCall) const; - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct GlobalFunctions diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp index c122bcb51a..5db5bd46ec 100644 --- a/src/qml/jsruntime/qv4identifier.cpp +++ b/src/qml/jsruntime/qv4identifier.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qv4identifier_p.h" #include "qv4identifiertable_p.h" +#include "qv4string_p.h" QT_BEGIN_NAMESPACE @@ -54,14 +55,16 @@ static inline int primeForNumBits(int numBits) } -IdentifierHashData::IdentifierHashData(int numBits) +IdentifierHashData::IdentifierHashData(IdentifierTable *table, int numBits) : size(0) , numBits(numBits) + , identifierTable(table) { refCount.store(1); alloc = primeForNumBits(numBits); entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); memset(entries, 0, alloc*sizeof(IdentifierHashEntry)); + identifierTable->addIdentifierHash(this); } IdentifierHashData::IdentifierHashData(IdentifierHashData *other) @@ -73,12 +76,18 @@ IdentifierHashData::IdentifierHashData(IdentifierHashData *other) alloc = other->alloc; entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry)); + identifierTable->addIdentifierHash(this); +} + +IdentifierHashData::~IdentifierHashData() { + free(entries); + if (identifierTable) + identifierTable->removeIdentifierHash(this); } IdentifierHash::IdentifierHash(ExecutionEngine *engine) { - d = new IdentifierHashData(3); - d->identifierTable = engine->identifierTable; + d = new IdentifierHashData(engine->identifierTable, 3); } void IdentifierHash::detach() @@ -92,8 +101,10 @@ void IdentifierHash::detach() } -IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier) +IdentifierHashEntry *IdentifierHash::addEntry(PropertyKey identifier) { + Q_ASSERT(identifier.isStringOrSymbol()); + // fill up to max 50% bool grow = (d->alloc <= d->size*2); @@ -104,10 +115,10 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier) memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry)); for (int i = 0; i < d->alloc; ++i) { const IdentifierHashEntry &e = d->entries[i]; - if (!e.identifier) + if (!e.identifier.isValid()) continue; - uint idx = e.identifier->hashValue % newAlloc; - while (newEntries[idx].identifier) { + uint idx = e.identifier.id() % newAlloc; + while (newEntries[idx].identifier.isValid()) { ++idx; idx %= newAlloc; } @@ -118,8 +129,8 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier) d->alloc = newAlloc; } - uint idx = identifier->hashValue % d->alloc; - while (d->entries[idx].identifier) { + uint idx = identifier.id() % d->alloc; + while (d->entries[idx].identifier.isValid()) { Q_ASSERT(d->entries[idx].identifier != identifier); ++idx; idx %= d->alloc; @@ -129,15 +140,15 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier) return d->entries + idx; } -const IdentifierHashEntry *IdentifierHash::lookup(const Identifier *identifier) const +const IdentifierHashEntry *IdentifierHash::lookup(PropertyKey identifier) const { - if (!d) + if (!d || !identifier.isStringOrSymbol()) return nullptr; Q_ASSERT(d->entries); - uint idx = identifier->hashValue % d->alloc; + uint idx = identifier.id() % d->alloc; while (1) { - if (!d->entries[idx].identifier) + if (!d->entries[idx].identifier.isValid()) return nullptr; if (d->entries[idx].identifier == identifier) return d->entries + idx; @@ -150,39 +161,54 @@ const IdentifierHashEntry *IdentifierHash::lookup(const QString &str) const { if (!d) return nullptr; - Q_ASSERT(d->entries); - uint hash = String::createHashValue(str.constData(), str.length(), nullptr); - uint idx = hash % d->alloc; - while (1) { - if (!d->entries[idx].identifier) - return nullptr; - if (d->entries[idx].identifier->string == str) - return d->entries + idx; - ++idx; - idx %= d->alloc; - } + PropertyKey id = d->identifierTable->asPropertyKey(str); + return lookup(id); } const IdentifierHashEntry *IdentifierHash::lookup(String *str) const { if (!d) return nullptr; - if (str->d()->identifier) - return lookup(str->d()->identifier); + PropertyKey id = d->identifierTable->asPropertyKey(str); + if (id.isValid()) + return lookup(id); return lookup(str->toQString()); } -const Identifier *IdentifierHash::toIdentifier(const QString &str) const +const PropertyKey IdentifierHash::toIdentifier(const QString &str) const { Q_ASSERT(d); - return d->identifierTable->identifier(str); + return d->identifierTable->asPropertyKey(str); } -const Identifier *IdentifierHash::toIdentifier(Heap::String *str) const +const PropertyKey IdentifierHash::toIdentifier(Heap::String *str) const { Q_ASSERT(d); - return d->identifierTable->identifier(str); + return d->identifierTable->asPropertyKey(str); +} + +QString QV4::IdentifierHash::findId(int value) const +{ + IdentifierHashEntry *e = d->entries; + IdentifierHashEntry *end = e + d->alloc; + while (e < end) { + if (e->identifier.isValid() && e->value == value) + return e->identifier.toQString(); + ++e; + } + return QString(); +} + +void IdentifierHashData::markObjects(MarkStack *markStack) const +{ + IdentifierHashEntry *e = entries; + IdentifierHashEntry *end = e + alloc; + while (e < end) { + if (Heap::Base *o = e->identifier.asStringOrSymbol()) + o->mark(markStack); + ++e; + } } diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h index 82346d5f68..32de8b7c8d 100644 --- a/src/qml/jsruntime/qv4identifier_p.h +++ b/src/qml/jsruntime/qv4identifier_p.h @@ -51,38 +51,24 @@ // #include <qstring.h> +#include <private/qv4global_p.h> +#include <private/qv4propertykey_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -namespace Heap { - struct String; -} - -struct String; -struct IdentifierTable; -struct ExecutionEngine; - -struct Identifier -{ - QString string; - uint hashValue; -}; - - struct IdentifierHashEntry { - const Identifier *identifier; + PropertyKey identifier; int value; }; struct IdentifierHashData { - IdentifierHashData(int numBits); + IdentifierHashData(IdentifierTable *table, int numBits); explicit IdentifierHashData(IdentifierHashData *other); - ~IdentifierHashData() { - free(entries); - } + ~IdentifierHashData(); + void markObjects(MarkStack *markStack) const; QBasicAtomicInt refCount; int alloc; @@ -117,12 +103,12 @@ struct IdentifierHash QString findId(int value) const; protected: - IdentifierHashEntry *addEntry(const Identifier *i); - const IdentifierHashEntry *lookup(const Identifier *identifier) const; + IdentifierHashEntry *addEntry(PropertyKey i); + const IdentifierHashEntry *lookup(PropertyKey identifier) const; const IdentifierHashEntry *lookup(const QString &str) const; const IdentifierHashEntry *lookup(String *str) const; - const Identifier *toIdentifier(const QString &str) const; - const Identifier *toIdentifier(Heap::String *str) const; + const PropertyKey toIdentifier(const QString &str) const; + const PropertyKey toIdentifier(Heap::String *str) const; }; @@ -180,20 +166,6 @@ inline int IdentifierHash::value(String *str) const return e ? e->value : -1; } - -inline -QString IdentifierHash::findId(int value) const -{ - IdentifierHashEntry *e = d->entries; - IdentifierHashEntry *end = e + d->alloc; - while (e < end) { - if (e->identifier && e->value == value) - return e->identifier->string; - ++e; - } - return QString(); -} - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index b77f9478d3..0412695404 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -37,6 +37,7 @@ ** ****************************************************************************/ #include "qv4identifiertable_p.h" +#include "qv4symbol_p.h" QT_BEGIN_NAMESPACE @@ -53,44 +54,44 @@ static inline int primeForNumBits(int numBits) } -IdentifierTable::IdentifierTable(ExecutionEngine *engine) +IdentifierTable::IdentifierTable(ExecutionEngine *engine, int numBits) : engine(engine) , size(0) - , numBits(8) + , numBits(numBits) { alloc = primeForNumBits(numBits); - entries = (Heap::String **)malloc(alloc*sizeof(Heap::String *)); - memset(entries, 0, alloc*sizeof(Heap::String *)); + entriesByHash = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *)); + entriesById = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *)); + memset(entriesByHash, 0, alloc*sizeof(Heap::String *)); + memset(entriesById, 0, alloc*sizeof(Heap::String *)); } IdentifierTable::~IdentifierTable() { - for (int i = 0; i < alloc; ++i) - if (entries[i]) - delete entries[i]->identifier; - free(entries); + free(entriesByHash); + free(entriesById); + for (auto &h : idHashes) + h->identifierTable = nullptr; } -void IdentifierTable::addEntry(Heap::String *str) +void IdentifierTable::addEntry(Heap::StringOrSymbol *str) { uint hash = str->hashValue(); if (str->subtype == Heap::String::StringType_ArrayIndex) return; - str->identifier = new Identifier; - str->identifier->string = str->toQString(); - str->identifier->hashValue = hash; + str->identifier = PropertyKey::fromStringOrSymbol(str); bool grow = (alloc <= size*2); if (grow) { ++numBits; int newAlloc = primeForNumBits(numBits); - Heap::String **newEntries = (Heap::String **)malloc(newAlloc*sizeof(Heap::String *)); - memset(newEntries, 0, newAlloc*sizeof(Heap::String *)); + Heap::StringOrSymbol **newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *)); + memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *)); for (int i = 0; i < alloc; ++i) { - Heap::String *e = entries[i]; + Heap::StringOrSymbol *e = entriesByHash[i]; if (!e) continue; uint idx = e->stringHash % newAlloc; @@ -100,17 +101,42 @@ void IdentifierTable::addEntry(Heap::String *str) } newEntries[idx] = e; } - free(entries); - entries = newEntries; + free(entriesByHash); + entriesByHash = newEntries; + + newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *)); + memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *)); + for (int i = 0; i < alloc; ++i) { + Heap::StringOrSymbol *e = entriesById[i]; + if (!e) + continue; + uint idx = e->identifier.id() % newAlloc; + while (newEntries[idx]) { + ++idx; + idx %= newAlloc; + } + newEntries[idx] = e; + } + free(entriesById); + entriesById = newEntries; + alloc = newAlloc; } uint idx = hash % alloc; - while (entries[idx]) { + while (entriesByHash[idx]) { + ++idx; + idx %= alloc; + } + entriesByHash[idx] = str; + + idx = str->identifier.id() % alloc; + while (entriesById[idx]) { ++idx; idx %= alloc; } - entries[idx] = str; + entriesById[idx] = str; + ++size; } @@ -120,10 +146,16 @@ Heap::String *IdentifierTable::insertString(const QString &s) { uint subtype; uint hash = String::createHashValue(s.constData(), s.length(), &subtype); + if (subtype == Heap::String::StringType_ArrayIndex) { + Heap::String *str = engine->newString(s); + str->stringHash = hash; + str->subtype = subtype; + return str; + } uint idx = hash % alloc; - while (Heap::String *e = entries[idx]) { + while (Heap::StringOrSymbol *e = entriesByHash[idx]) { if (e->stringHash == hash && e->toQString() == s) - return e; + return static_cast<Heap::String *>(e); ++idx; idx %= alloc; } @@ -135,18 +167,42 @@ Heap::String *IdentifierTable::insertString(const QString &s) return str; } +Heap::Symbol *IdentifierTable::insertSymbol(const QString &s) +{ + Q_ASSERT(s.at(0) == QLatin1Char('@')); + + uint subtype; + uint hash = String::createHashValue(s.constData(), s.length(), &subtype); + uint idx = hash % alloc; + while (Heap::StringOrSymbol *e = entriesByHash[idx]) { + if (e->stringHash == hash && e->toQString() == s) + return static_cast<Heap::Symbol *>(e); + ++idx; + idx %= alloc; + } + + Heap::Symbol *str = Symbol::create(engine, s); + str->stringHash = hash; + str->subtype = subtype; + addEntry(str); + return str; -Identifier *IdentifierTable::identifierImpl(const Heap::String *str) +} + + +PropertyKey IdentifierTable::asPropertyKeyImpl(const Heap::String *str) { - if (str->identifier) + if (str->identifier.isValid()) return str->identifier; uint hash = str->hashValue(); - if (str->subtype == Heap::String::StringType_ArrayIndex) - return nullptr; + if (str->subtype == Heap::String::StringType_ArrayIndex) { + str->identifier = PropertyKey::fromArrayIndex(hash); + return str->identifier; + } uint idx = hash % alloc; - while (Heap::String *e = entries[idx]) { - if (e->stringHash == hash && e->isEqualTo(str)) { + while (Heap::StringOrSymbol *e = entriesByHash[idx]) { + if (e->stringHash == hash && e->toQString() == str->toQString()) { str->identifier = e->identifier; return e->identifier; } @@ -158,37 +214,117 @@ Identifier *IdentifierTable::identifierImpl(const Heap::String *str) return str->identifier; } -Heap::String *IdentifierTable::stringFromIdentifier(Identifier *i) +Heap::StringOrSymbol *IdentifierTable::resolveId(PropertyKey i) const { - if (!i) + uint arrayIdx = i.asArrayIndex(); + if (arrayIdx < UINT_MAX) + return engine->newString(QString::number(arrayIdx)); + if (!i.isValid()) return nullptr; - uint idx = i->hashValue % alloc; + uint idx = i.id() % alloc; while (1) { - Heap::String *e = entries[idx]; - Q_ASSERT(e); - if (e->identifier == i) + Heap::StringOrSymbol *e = entriesById[idx]; + if (!e || e->identifier == i) return e; ++idx; idx %= alloc; } } -Identifier *IdentifierTable::identifier(const QString &s) +Heap::String *IdentifierTable::stringForId(PropertyKey i) const +{ + Heap::StringOrSymbol *s = resolveId(i); + Q_ASSERT(s && s->internalClass->vtable->isString); + return static_cast<Heap::String *>(s); +} + +Heap::Symbol *IdentifierTable::symbolForId(PropertyKey i) const +{ + Heap::StringOrSymbol *s = resolveId(i); + Q_ASSERT(!s || !s->internalClass->vtable->isString); + return static_cast<Heap::Symbol *>(s); +} + +void IdentifierTable::markObjects(MarkStack *markStack) +{ + for (const auto &h : idHashes) + h->markObjects(markStack); +} + +template <typename Key> +int sweepTable(Heap::StringOrSymbol **table, int alloc, std::function<Key(Heap::StringOrSymbol *)> f) { + int freed = 0; + uint lastKey = 0; + + int lastEntry = -1; + int start = 0; + // start at an empty entry so we compress properly + for (; start < alloc; ++start) { + if (!table[start]) + break; + } + + for (int i = 0; i < alloc; ++i) { + int idx = (i + start) % alloc; + Heap::StringOrSymbol *entry = table[idx]; + if (!entry) { + lastEntry = -1; + continue; + } + if (entry->isMarked()) { + if (lastEntry >= 0 && lastKey == (f(entry) % alloc)) { + Q_ASSERT(table[lastEntry] == nullptr); + table[lastEntry] = entry; + table[idx] = nullptr; + + // find next free slot just like in addEntry() + do { + lastEntry = (lastEntry + 1) % alloc; + } while (table[lastEntry] != nullptr); + } + continue; + } + if (lastEntry == -1) { + lastEntry = idx; + lastKey = f(entry) % alloc; + } + table[idx] = nullptr; + ++freed; + } + for (int i = 0; i < alloc; ++i) { + Heap::StringOrSymbol *entry = table[i]; + if (!entry) + continue; + Q_ASSERT(entry->isMarked()); + } + return freed; +} + +void IdentifierTable::sweep() +{ + int f = sweepTable<uint>(entriesByHash, alloc, [](Heap::StringOrSymbol *entry) {return entry->hashValue(); }); + int freed = sweepTable<quint64>(entriesById, alloc, [](Heap::StringOrSymbol *entry) {return entry->identifier.id(); }); + Q_UNUSED(f); + Q_ASSERT(f == freed); + size -= freed; +} + +PropertyKey IdentifierTable::asPropertyKey(const QString &s) { return insertString(s)->identifier; } -Identifier *IdentifierTable::identifier(const char *s, int len) +PropertyKey IdentifierTable::asPropertyKey(const char *s, int len) { uint subtype; uint hash = String::createHashValue(s, len, &subtype); if (hash == UINT_MAX) - return identifier(QString::fromUtf8(s, len)); + return asPropertyKey(QString::fromUtf8(s, len)); QLatin1String latin(s, len); uint idx = hash % alloc; - while (Heap::String *e = entries[idx]) { + while (Heap::StringOrSymbol *e = entriesByHash[idx]) { if (e->stringHash == hash && e->toQString() == latin) return e->identifier; ++idx; diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h index b0b08f1e54..3674ece84f 100644 --- a/src/qml/jsruntime/qv4identifiertable_p.h +++ b/src/qml/jsruntime/qv4identifiertable_p.h @@ -53,55 +53,61 @@ #include "qv4identifier_p.h" #include "qv4string_p.h" #include "qv4engine_p.h" +#include <qset.h> #include <limits.h> QT_BEGIN_NAMESPACE namespace QV4 { -struct IdentifierTable +struct Q_QML_PRIVATE_EXPORT IdentifierTable { ExecutionEngine *engine; int alloc; int size; int numBits; - Heap::String **entries; + Heap::StringOrSymbol **entriesByHash; + Heap::StringOrSymbol **entriesById; - void addEntry(Heap::String *str); + QSet<IdentifierHashData *> idHashes; + + void addEntry(Heap::StringOrSymbol *str); public: - IdentifierTable(ExecutionEngine *engine); + IdentifierTable(ExecutionEngine *engine, int numBits = 8); ~IdentifierTable(); Heap::String *insertString(const QString &s); + Heap::Symbol *insertSymbol(const QString &s); - Identifier *identifier(const Heap::String *str) { - if (str->identifier) + PropertyKey asPropertyKey(const Heap::String *str) { + if (str->identifier.isValid()) return str->identifier; - return identifierImpl(str); + return asPropertyKeyImpl(str); } - Identifier *identifier(const QV4::String *str) { - return identifier(str->d()); + PropertyKey asPropertyKey(const QV4::String *str) { + return asPropertyKey(str->d()); } - Identifier *identifier(const QString &s); - Identifier *identifier(const char *s, int len); + PropertyKey asPropertyKey(const QString &s); + PropertyKey asPropertyKey(const char *s, int len); + + PropertyKey asPropertyKeyImpl(const Heap::String *str); - Identifier *identifierImpl(const Heap::String *str); + Heap::StringOrSymbol *resolveId(PropertyKey i) const; + Heap::String *stringForId(PropertyKey i) const; + Heap::Symbol *symbolForId(PropertyKey i) const; - Heap::String *stringFromIdentifier(Identifier *i); + void markObjects(MarkStack *markStack); + void sweep(); - void mark(MarkStack *markStack) { - for (int i = 0; i < alloc; ++i) { - Heap::String *entry = entries[i]; - if (!entry || entry->isMarked()) - continue; - entry->setMarkBit(); - Q_ASSERT(entry->vtable()->markObjects); - entry->vtable()->markObjects(entry, markStack); - } + void addIdentifierHash(IdentifierHashData *h) { + idHashes.insert(h); + } + void removeIdentifierHash(IdentifierHashData *h) { + idHashes.remove(h); } }; diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 3bfcf358bf..c890dc0550 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE -using namespace QV4; +namespace QV4 { static const uchar prime_deltas[] = { 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, @@ -74,27 +74,11 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) // fill up to max 50% bool grow = (d->alloc <= d->size*2); - if (classSize < d->size || grow) { - PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits); - for (int i = 0; i < d->alloc; ++i) { - const Entry &e = d->entries[i]; - if (!e.identifier || e.index >= static_cast<unsigned>(classSize)) - continue; - uint idx = e.identifier->hashValue % dd->alloc; - while (dd->entries[idx].identifier) { - ++idx; - idx %= dd->alloc; - } - dd->entries[idx] = e; - } - dd->size = classSize; - Q_ASSERT(d->refCount > 1); - --d->refCount; - d = dd; - } + if (classSize < d->size || grow) + detach(grow, classSize); - uint idx = entry.identifier->hashValue % d->alloc; - while (d->entries[idx].identifier) { + uint idx = entry.identifier.id() % d->alloc; + while (d->entries[idx].identifier.isValid()) { ++idx; idx %= d->alloc; } @@ -102,38 +86,125 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) ++d->size; } +int PropertyHash::removeIdentifier(PropertyKey identifier, int classSize) +{ + int val = -1; + PropertyHashData *dd = new PropertyHashData(d->numBits); + for (int i = 0; i < d->alloc; ++i) { + const Entry &e = d->entries[i]; + if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize)) + continue; + if (e.identifier == identifier) { + val = e.index; + continue; + } + uint idx = e.identifier.id() % dd->alloc; + while (dd->entries[idx].identifier.isValid()) { + ++idx; + idx %= dd->alloc; + } + dd->entries[idx] = e; + } + dd->size = classSize; + if (!--d->refCount) + delete d; + d = dd; + + Q_ASSERT(val != -1); + return val; +} + +void PropertyHash::detach(bool grow, int classSize) +{ + if (d->refCount == 1 && !grow) + return; -InternalClass::InternalClass(ExecutionEngine *engine) - : engine(engine) - , vtable(nullptr) - , prototype(nullptr) - , m_sealed(nullptr) - , m_frozen(nullptr) - , size(0) - , extensible(true) + PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits); + for (int i = 0; i < d->alloc; ++i) { + const Entry &e = d->entries[i]; + if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize)) + continue; + uint idx = e.identifier.id() % dd->alloc; + while (dd->entries[idx].identifier.isValid()) { + ++idx; + idx %= dd->alloc; + } + dd->entries[idx] = e; + } + dd->size = classSize; + if (!--d->refCount) + delete d; + d = dd; +} + +namespace Heap { + +void InternalClass::init(ExecutionEngine *engine) { - id = engine->newInternalClassId(); + Base::init(); + new (&propertyTable) PropertyHash(); + new (&nameMap) SharedInternalClassData<PropertyKey>(); + new (&propertyData) SharedInternalClassData<PropertyAttributes>(); + new (&transitions) std::vector<Transition>(); + + this->engine = engine; + vtable = QV4::InternalClass::staticVTable(); +// prototype = nullptr; +// parent = nullptr; +// size = 0; + extensible = true; + isFrozen = false; + isSealed = false; + isUsedAsProto = false; + protoId = engine->newProtoId(); + + // Also internal classes need an internal class pointer. Simply make it point to itself + internalClass.set(engine, this); } -InternalClass::InternalClass(const QV4::InternalClass &other) - : QQmlJS::Managed() - , engine(other.engine) - , vtable(other.vtable) - , prototype(other.prototype) - , propertyTable(other.propertyTable) - , nameMap(other.nameMap) - , propertyData(other.propertyData) - , m_sealed(nullptr) - , m_frozen(nullptr) - , size(other.size) - , extensible(other.extensible) - , isUsedAsProto(other.isUsedAsProto) +void InternalClass::init(Heap::InternalClass *other) +{ + Base::init(); + Q_ASSERT(!other->isFrozen); + new (&propertyTable) PropertyHash(other->propertyTable); + new (&nameMap) SharedInternalClassData<PropertyKey>(other->nameMap); + new (&propertyData) SharedInternalClassData<PropertyAttributes>(other->propertyData); + new (&transitions) std::vector<Transition>(); + + engine = other->engine; + vtable = other->vtable; + prototype = other->prototype; + parent = other; + size = other->size; + extensible = other->extensible; + isSealed = other->isSealed; + isFrozen = other->isFrozen; + isUsedAsProto = other->isUsedAsProto; + protoId = engine->newProtoId(); + + internalClass.set(engine, other->internalClass); +} + +void InternalClass::destroy() { - id = engine->newInternalClassId(); +#ifndef QT_NO_DEBUG + for (const auto &t : transitions) { + Q_ASSERT(!t.lookup || !t.lookup->isMarked()); + } +#endif + if (parent && parent->engine && parent->isMarked()) + parent->removeChildEntry(this); + + propertyTable.~PropertyHash(); + nameMap.~SharedInternalClassData<PropertyKey>(); + propertyData.~SharedInternalClassData<PropertyAttributes>(); + transitions.~vector<Transition>(); + engine = nullptr; + Base::destroy(); } -static void insertHoleIntoPropertyData(Object *object, int idx) +static void insertHoleIntoPropertyData(QV4::Object *object, int idx) { Heap::Object *o = object->d(); ExecutionEngine *v4 = o->internalClass->engine; @@ -142,7 +213,7 @@ static void insertHoleIntoPropertyData(Object *object, int idx) o->setProperty(v4, i, *o->propertyData(i - 1)); } -static void removeFromPropertyData(Object *object, int idx, bool accessor = false) +static void removeFromPropertyData(QV4::Object *object, int idx, bool accessor = false) { Heap::Object *o = object->d(); ExecutionEngine *v4 = o->internalClass->engine; @@ -154,20 +225,23 @@ static void removeFromPropertyData(Object *object, int idx, bool accessor = fals o->setProperty(v4, size + 1, Primitive::undefinedValue()); } -void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index) +void InternalClass::changeMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, uint *index) { + Q_ASSERT(id.isStringOrSymbol()); uint idx; - InternalClass *oldClass = object->internalClass(); - InternalClass *newClass = oldClass->changeMember(string->identifier(), data, &idx); + Heap::InternalClass *oldClass = object->internalClass(); + Heap::InternalClass *newClass = oldClass->changeMember(id, data, &idx); if (index) *index = idx; + uint oldSize = oldClass->size; object->setInternalClass(newClass); - if (newClass->size > oldClass->size) { - Q_ASSERT(newClass->size == oldClass->size + 1); + // don't use oldClass anymore, it could be GC'ed + if (newClass->size > oldSize) { + Q_ASSERT(newClass->size == oldSize + 1); insertHoleIntoPropertyData(object, idx); - } else if (newClass->size < oldClass->size) { - Q_ASSERT(newClass->size == oldClass->size - 1); + } else if (newClass->size < oldSize) { + Q_ASSERT(newClass->size == oldSize - 1); removeFromPropertyData(object, idx + 1); } } @@ -183,7 +257,16 @@ InternalClassTransition &InternalClass::lookupOrInsertTransition(const InternalC } } -InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttributes data, uint *index) +static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e) +{ + // add a dummy entry, since we need two entries for accessors + newClass->propertyTable.addEntry(e, newClass->size); + newClass->nameMap.add(newClass->size, PropertyKey::invalid()); + newClass->propertyData.add(newClass->size, PropertyAttributes()); + ++newClass->size; +} + +Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, PropertyAttributes data, uint *index) { data.resolve(); uint idx = find(identifier); @@ -193,7 +276,7 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri *index = idx; if (data == propertyData.at(idx)) - return this; + return static_cast<Heap::InternalClass *>(this); Transition temp = { { identifier }, nullptr, (int)data.flags() }; Transition &t = lookupOrInsertTransition(temp); @@ -201,14 +284,34 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri return t.lookup; // create a new class and add it to the tree - InternalClass *newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); - newClass = newClass->changePrototype(prototype); - for (uint i = 0; i < size; ++i) { - if (i == idx) { - newClass = newClass->addMember(nameMap.at(i), data); - } else if (!propertyData.at(i).isEmpty()) { - newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); + Heap::InternalClass *newClass = engine->newClass(this); + if (data.isAccessor() != propertyData.at(idx).isAccessor()) { + // this changes the layout of the class, so we need to rebuild the data + newClass->propertyTable = PropertyHash(); + newClass->nameMap = SharedInternalClassData<PropertyKey>(); + newClass->propertyData = SharedInternalClassData<PropertyAttributes>(); + newClass->size = 0; + for (uint i = 0; i < size; ++i) { + PropertyKey identifier = nameMap.at(i); + PropertyHash::Entry e = { identifier, newClass->size }; + if (i && !identifier.isValid()) + e.identifier = nameMap.at(i - 1); + newClass->propertyTable.addEntry(e, newClass->size); + newClass->nameMap.add(newClass->size, identifier); + if (i == idx) { + newClass->propertyData.add(newClass->size, data); + ++newClass->size; + if (data.isAccessor()) + addDummyEntry(newClass, e); + else + ++i; + } else { + newClass->propertyData.add(newClass->size, propertyData.at(i)); + ++newClass->size; + } } + } else { + newClass->propertyData.set(idx, data); } t.lookup = newClass; @@ -216,14 +319,16 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri return newClass; } -InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) +Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) { + Scope scope(engine); + ScopedValue protectThis(scope, this); if (proto) proto->setUsedAsProto(); Q_ASSERT(prototype != proto); Q_ASSERT(!proto || proto->internalClass->isUsedAsProto); - Transition temp = { { nullptr }, nullptr, Transition::PrototypeChange }; + Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::PrototypeChange }; temp.prototype = proto; Transition &t = lookupOrInsertTransition(temp); @@ -231,29 +336,19 @@ InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) return t.lookup; // create a new class and add it to the tree - InternalClass *newClass; - if (!size && !prototype) { - newClass = engine->newClass(*this); - newClass->prototype = proto; - } else { - newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); - newClass = newClass->changePrototype(proto); - for (uint i = 0; i < size; ++i) { - if (!propertyData.at(i).isEmpty()) - newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); - } - } + Heap::InternalClass *newClass = engine->newClass(this); + newClass->prototype = proto; t.lookup = newClass; return newClass; } -InternalClass *InternalClass::changeVTableImpl(const VTable *vt) +Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt) { Q_ASSERT(vtable != vt); - Transition temp = { { nullptr }, nullptr, Transition::VTableChange }; + Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::VTableChange }; temp.vtable = vt; Transition &t = lookupOrInsertTransition(temp); @@ -261,18 +356,8 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt) return t.lookup; // create a new class and add it to the tree - InternalClass *newClass; - if (this == engine->internalClasses[EngineBase::Class_Empty]) { - newClass = engine->newClass(*this); - newClass->vtable = vt; - } else { - newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vt); - newClass = newClass->changePrototype(prototype); - for (uint i = 0; i < size; ++i) { - if (!propertyData.at(i).isEmpty()) - newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); - } - } + Heap::InternalClass *newClass = engine->newClass(this); + newClass->vtable = vt; t.lookup = newClass; Q_ASSERT(t.lookup); @@ -280,17 +365,17 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt) return newClass; } -InternalClass *InternalClass::nonExtensible() +Heap::InternalClass *InternalClass::nonExtensible() { if (!extensible) return this; - Transition temp = { { nullptr }, nullptr, Transition::NotExtensible}; + Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::NotExtensible}; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) return t.lookup; - InternalClass *newClass = engine->newClass(*this); + Heap::InternalClass *newClass = engine->newClass(this); newClass->extensible = false; t.lookup = newClass; @@ -298,31 +383,26 @@ InternalClass *InternalClass::nonExtensible() return newClass; } -void InternalClass::addMember(Object *object, String *string, PropertyAttributes data, uint *index) +void InternalClass::addMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, uint *index) { + Q_ASSERT(id.isStringOrSymbol()); data.resolve(); - object->internalClass()->engine->identifierTable->identifier(string); - if (object->internalClass()->propertyTable.lookup(string->d()->identifier) < object->internalClass()->size) { - changeMember(object, string, data, index); + if (object->internalClass()->propertyTable.lookup(id) < object->internalClass()->size) { + changeMember(object, id, data, index); return; } uint idx; - InternalClass *newClass = object->internalClass()->addMemberImpl(string->identifier(), data, &idx); + Heap::InternalClass *newClass = object->internalClass()->addMemberImpl(id, data, &idx); if (index) *index = idx; object->setInternalClass(newClass); } -InternalClass *InternalClass::addMember(String *string, PropertyAttributes data, uint *index) -{ - engine->identifierTable->identifier(string); - return addMember(string->identifier(), data, index); -} - -InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttributes data, uint *index) +Heap::InternalClass *InternalClass::addMember(PropertyKey identifier, PropertyAttributes data, uint *index) { + Q_ASSERT(identifier.isStringOrSymbol()); data.resolve(); if (propertyTable.lookup(identifier) < size) @@ -331,7 +411,7 @@ InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttribut return addMemberImpl(identifier, data, index); } -InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index) +Heap::InternalClass *InternalClass::addMemberImpl(PropertyKey identifier, PropertyAttributes data, uint *index) { Transition temp = { { identifier }, nullptr, (int)data.flags() }; Transition &t = lookupOrInsertTransition(temp); @@ -343,109 +423,156 @@ InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttr return t.lookup; // create a new class and add it to the tree - InternalClass *newClass = engine->newClass(*this); + Heap::InternalClass *newClass = engine->newClass(this); PropertyHash::Entry e = { identifier, newClass->size }; newClass->propertyTable.addEntry(e, newClass->size); newClass->nameMap.add(newClass->size, identifier); newClass->propertyData.add(newClass->size, data); ++newClass->size; - if (data.isAccessor()) { - // add a dummy entry, since we need two entries for accessors - newClass->propertyTable.addEntry(e, newClass->size); - newClass->nameMap.add(newClass->size, 0); - newClass->propertyData.add(newClass->size, PropertyAttributes()); - ++newClass->size; - } + if (data.isAccessor()) + addDummyEntry(newClass, e); t.lookup = newClass; Q_ASSERT(t.lookup); return newClass; } -void InternalClass::removeMember(Object *object, Identifier *id) +void InternalClass::removeChildEntry(InternalClass *child) { - InternalClass *oldClass = object->internalClass(); - uint propIdx = oldClass->propertyTable.lookup(id); - Q_ASSERT(propIdx < oldClass->size); + Q_ASSERT(engine); + for (auto &t : transitions) { + if (t.lookup == child) { + t.lookup = nullptr; + return; + } + } + Q_UNREACHABLE(); - Transition temp = { { id }, nullptr, -1 }; - Transition &t = object->internalClass()->lookupOrInsertTransition(temp); +} - bool accessor = oldClass->propertyData.at(propIdx).isAccessor(); +void InternalClass::removeMember(QV4::Object *object, PropertyKey identifier) +{ + Heap::InternalClass *oldClass = object->internalClass(); + Q_ASSERT(oldClass->propertyTable.lookup(identifier) < oldClass->size); - if (t.lookup) { - object->setInternalClass(t.lookup); - } else { + Transition temp = { { identifier }, nullptr, Transition::RemoveMember }; + Transition &t = object->internalClass()->lookupOrInsertTransition(temp); + + if (!t.lookup) { // create a new class and add it to the tree - InternalClass *newClass = oldClass->engine->internalClasses[EngineBase::Class_Empty]->changeVTable(oldClass->vtable); - newClass = newClass->changePrototype(oldClass->prototype); - for (uint i = 0; i < oldClass->size; ++i) { - if (i == propIdx) - continue; - if (!oldClass->propertyData.at(i).isEmpty()) - newClass = newClass->addMember(oldClass->nameMap.at(i), oldClass->propertyData.at(i)); - } - object->setInternalClass(newClass); + Heap::InternalClass *newClass = oldClass->engine->newClass(oldClass); + // simply make the entry inaccessible + int idx = newClass->propertyTable.removeIdentifier(identifier, oldClass->size); + newClass->nameMap.set(idx, PropertyKey::invalid()); + newClass->propertyData.set(idx, PropertyAttributes()); + t.lookup = newClass; + Q_ASSERT(t.lookup); } + object->setInternalClass(t.lookup); - Q_ASSERT(object->internalClass()->size == oldClass->size - (accessor ? 2 : 1)); - - // remove the entry in the property data - removeFromPropertyData(object, propIdx, accessor); - - t.lookup = object->internalClass(); - Q_ASSERT(t.lookup); + // we didn't remove the data slot, just made it inaccessible + Q_ASSERT(object->internalClass()->size == oldClass->size); } -uint InternalClass::find(const String *string) +Heap::InternalClass *InternalClass::sealed() { - engine->identifierTable->identifier(string); - const Identifier *id = string->d()->identifier; + if (isSealed) + return this; - uint index = propertyTable.lookup(id); - if (index < size) - return index; + bool alreadySealed = !extensible; + for (uint i = 0; i < size; ++i) { + PropertyAttributes attrs = propertyData.at(i); + if (attrs.isEmpty()) + continue; + if (attrs.isConfigurable()) { + alreadySealed = false; + break; + } + } - return UINT_MAX; -} + if (alreadySealed) { + isSealed = true; + return this; + } -InternalClass *InternalClass::sealed() -{ - if (m_sealed) - return m_sealed; + Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Sealed }; + Transition &t = lookupOrInsertTransition(temp); + + if (t.lookup) { + Q_ASSERT(t.lookup && t.lookup->isSealed); + return t.lookup; + } + + Heap::InternalClass *s = engine->newClass(this); - m_sealed = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); - m_sealed = m_sealed->changePrototype(prototype); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) continue; attrs.setConfigurable(false); - m_sealed = m_sealed->addMember(nameMap.at(i), attrs); + s->propertyData.set(i, attrs); } - m_sealed = m_sealed->nonExtensible(); + s->extensible = false; + s->isSealed = true; - m_sealed->m_sealed = m_sealed; - return m_sealed; + t.lookup = s; + return s; } -InternalClass *InternalClass::frozen() +Heap::InternalClass *InternalClass::frozen() { - if (m_frozen) - return m_frozen; + if (isFrozen) + return this; + + bool alreadyFrozen = !extensible; + for (uint i = 0; i < size; ++i) { + PropertyAttributes attrs = propertyData.at(i); + if (attrs.isEmpty()) + continue; + if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable()) { + alreadyFrozen = false; + break; + } + } + + if (alreadyFrozen) { + isSealed = true; + isFrozen = true; + return this; + } + + Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Frozen }; + Transition &t = lookupOrInsertTransition(temp); + + if (t.lookup) { + Q_ASSERT(t.lookup && t.lookup->isSealed && t.lookup->isFrozen); + return t.lookup; + } + + Heap::InternalClass *f = engine->newClass(this); - m_frozen = propertiesFrozen(); - m_frozen = m_frozen->nonExtensible(); + for (uint i = 0; i < size; ++i) { + PropertyAttributes attrs = propertyData.at(i); + if (attrs.isEmpty()) + continue; + if (attrs.isData()) + attrs.setWritable(false); + attrs.setConfigurable(false); + f->propertyData.set(i, attrs); + } + f->extensible = false; + f->isSealed = true; + f->isFrozen = true; - m_frozen->m_frozen = m_frozen; - m_frozen->m_sealed = m_frozen; - return m_frozen; + t.lookup = f; + return f; } -InternalClass *InternalClass::propertiesFrozen() const +Heap::InternalClass *InternalClass::propertiesFrozen() const { - InternalClass *frozen = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); + Scope scope(engine); + Scoped<QV4::InternalClass> frozen(scope, engine->internalClasses(EngineBase::Class_Empty)->changeVTable(vtable)); frozen = frozen->changePrototype(prototype); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); @@ -455,20 +582,20 @@ InternalClass *InternalClass::propertiesFrozen() const attrs.setConfigurable(false); frozen = frozen->addMember(nameMap.at(i), attrs); } - return frozen; + return frozen->d(); } -InternalClass *InternalClass::asProtoClass() +Heap::InternalClass *InternalClass::asProtoClass() { if (isUsedAsProto) return this; - Transition temp = { { nullptr }, nullptr, Transition::ProtoClass }; + Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::ProtoClass }; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) return t.lookup; - InternalClass *newClass = engine->newClass(*this); + Heap::InternalClass *newClass = engine->newClass(this); newClass->isUsedAsProto = true; t.lookup = newClass; @@ -476,90 +603,43 @@ InternalClass *InternalClass::asProtoClass() return newClass; } -void InternalClass::destroy() +static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic) { - std::vector<InternalClass *> destroyStack; - destroyStack.reserve(64); - destroyStack.push_back(this); - - while (!destroyStack.empty()) { - InternalClass *next = destroyStack.back(); - destroyStack.pop_back(); - if (!next->engine) - continue; - next->engine = nullptr; - next->propertyTable.~PropertyHash(); - next->nameMap.~SharedInternalClassData<Identifier *>(); - next->propertyData.~SharedInternalClassData<PropertyAttributes>(); - if (next->m_sealed) - destroyStack.push_back(next->m_sealed); - if (next->m_frozen) - destroyStack.push_back(next->m_frozen); - - for (size_t i = 0; i < next->transitions.size(); ++i) { - Q_ASSERT(next->transitions.at(i).lookup); - destroyStack.push_back(next->transitions.at(i).lookup); - } - - next->transitions.~vector<Transition>(); + if (ic->prototype == o) + ic->protoId = ic->engine->newProtoId(); + for (auto &t : ic->transitions) { + if (t.lookup) + updateProtoUsage(o, t.lookup); } } + void InternalClass::updateProtoUsage(Heap::Object *o) { Q_ASSERT(isUsedAsProto); - InternalClass *ic = engine->internalClasses[EngineBase::Class_Empty]; + Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_Empty); Q_ASSERT(!ic->prototype); - // only need to go two levels into the IC hierarchy, as prototype changes - // can only happen there - for (auto &t : ic->transitions) { - Q_ASSERT(t.lookup); - if (t.flags == InternalClassTransition::VTableChange) { - InternalClass *ic2 = t.lookup; - for (auto &t2 : ic2->transitions) { - if (t2.flags == InternalClassTransition::PrototypeChange && - t2.lookup->prototype == o) - ic2->updateInternalClassIdRecursive(); - } - } else if (t.flags == InternalClassTransition::PrototypeChange && t.lookup->prototype == o) { - ic->updateInternalClassIdRecursive(); - } - } + Heap::updateProtoUsage(o, ic); } -void InternalClass::updateInternalClassIdRecursive() +void InternalClass::markObjects(Heap::Base *b, MarkStack *stack) { - id = engine->newInternalClassId(); - for (auto &t : transitions) { - Q_ASSERT(t.lookup); - if (t.flags == InternalClassTransition::VTableChange || t.flags == InternalClassTransition::PrototypeChange) - continue; - t.lookup->updateInternalClassIdRecursive(); + Heap::InternalClass *ic = static_cast<Heap::InternalClass *>(b); + if (ic->prototype) + ic->prototype->mark(stack); + if (ic->parent) + ic->parent->mark(stack); + + for (uint i = 0; i < ic->size; ++i) { + PropertyKey id = ic->nameMap.at(i); + if (Heap::Base *b = id.asStringOrSymbol()) + b->mark(stack); } } +} - -void InternalClassPool::markObjects(MarkStack *markStack) -{ - InternalClass *ic = markStack->engine->internalClasses[EngineBase::Class_Empty]; - Q_ASSERT(!ic->prototype); - - // only need to go two levels into the IC hierarchy, as prototype changes - // can only happen there - for (auto &t : ic->transitions) { - Q_ASSERT(t.lookup); - if (t.flags == InternalClassTransition::VTableChange) { - InternalClass *ic2 = t.lookup; - for (auto &t2 : ic2->transitions) { - if (t2.flags == InternalClassTransition::PrototypeChange) - t2.lookup->prototype->mark(markStack); - } - } else if (t.flags == InternalClassTransition::PrototypeChange) { - t.lookup->prototype->mark(markStack); - } - } } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index b689272006..888eedcaba 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -53,16 +53,13 @@ #include "qv4global_p.h" #include <QHash> -#include <private/qqmljsmemorypool_p.h> -#include <private/qv4identifier_p.h> +#include <private/qv4propertykey_p.h> +#include <private/qv4heap_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -struct String; -struct Object; -struct Identifier; struct VTable; struct MarkStack; @@ -70,7 +67,7 @@ struct PropertyHashData; struct PropertyHash { struct Entry { - const Identifier *identifier; + PropertyKey identifier; uint index; }; @@ -79,12 +76,12 @@ struct PropertyHash inline PropertyHash(); inline PropertyHash(const PropertyHash &other); inline ~PropertyHash(); + PropertyHash &operator=(const PropertyHash &other); void addEntry(const Entry &entry, int classSize); - uint lookup(const Identifier *identifier) const; - -private: - PropertyHash &operator=(const PropertyHash &other); + uint lookup(PropertyKey identifier) const; + int removeIdentifier(PropertyKey identifier, int classSize); + void detach(bool grow, int classSize); }; struct PropertyHashData @@ -118,15 +115,26 @@ inline PropertyHash::~PropertyHash() delete d; } -inline uint PropertyHash::lookup(const Identifier *identifier) const +inline PropertyHash &PropertyHash::operator=(const PropertyHash &other) +{ + ++other.d->refCount; + if (!--d->refCount) + delete d; + d = other.d; + return *this; +} + + + +inline uint PropertyHash::lookup(PropertyKey identifier) const { Q_ASSERT(d->entries); - uint idx = identifier->hashValue % d->alloc; + uint idx = identifier.id() % d->alloc; while (1) { if (d->entries[idx].identifier == identifier) return d->entries[idx].index; - if (!d->entries[idx].identifier) + if (!d->entries[idx].identifier.isValid()) return UINT_MAX; ++idx; idx %= d->alloc; @@ -163,6 +171,13 @@ struct SharedInternalClassData { if (!--d->refcount) delete d; } + SharedInternalClassData &operator=(const SharedInternalClassData &other) { + ++other.d->refcount; + if (!--d->refcount) + delete d; + d = other.d; + return *this; + } void add(uint pos, T value) { if (pos < d->size) { @@ -214,26 +229,26 @@ struct SharedInternalClassData { Q_ASSERT(i < d->size); return d->data[i]; } - -private: - SharedInternalClassData &operator=(const SharedInternalClassData &other); }; struct InternalClassTransition { union { - Identifier *id; + PropertyKey id; const VTable *vtable; Heap::Object *prototype; }; - InternalClass *lookup; + Heap::InternalClass *lookup; int flags; enum { // range 0-0xff is reserved for attribute changes NotExtensible = 0x100, VTableChange = 0x200, PrototypeChange = 0x201, - ProtoClass = 0x202 + ProtoClass = 0x202, + Sealed = 0x203, + Frozen = 0x204, + RemoveMember = -1 }; bool operator==(const InternalClassTransition &other) const @@ -243,48 +258,44 @@ struct InternalClassTransition { return id < other.id || (id == other.id && flags < other.flags); } }; -struct InternalClass : public QQmlJS::Managed { - int id = 0; // unique across the engine, gets changed also when proto chain changes +namespace Heap { + +struct InternalClass : Base { ExecutionEngine *engine; const VTable *vtable; + quintptr protoId; // unique across the engine, gets changed whenever the proto chain changes Heap::Object *prototype; + InternalClass *parent; PropertyHash propertyTable; // id to valueIndex - SharedInternalClassData<Identifier *> nameMap; + SharedInternalClassData<PropertyKey> nameMap; SharedInternalClassData<PropertyAttributes> propertyData; typedef InternalClassTransition Transition; std::vector<Transition> transitions; InternalClassTransition &lookupOrInsertTransition(const InternalClassTransition &t); - InternalClass *m_sealed; - InternalClass *m_frozen; - uint size; bool extensible; - bool isUsedAsProto = false; + bool isSealed; + bool isFrozen; + bool isUsedAsProto; + + void init(ExecutionEngine *engine); + void init(InternalClass *other); + void destroy(); Q_REQUIRED_RESULT InternalClass *nonExtensible(); - Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) { - if (vtable == vt) - return this; - return changeVTableImpl(vt); - } - Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) { - if (prototype == proto) - return this; - return changePrototypeImpl(proto); - } - static void addMember(Object *object, String *string, PropertyAttributes data, uint *index); - Q_REQUIRED_RESULT InternalClass *addMember(String *string, PropertyAttributes data, uint *index = nullptr); - Q_REQUIRED_RESULT InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = nullptr); - Q_REQUIRED_RESULT InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = nullptr); - static void changeMember(Object *object, String *string, PropertyAttributes data, uint *index = nullptr); - static void removeMember(Object *object, Identifier *id); - uint find(const String *string); - uint find(const Identifier *id) + static void addMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, uint *index); + Q_REQUIRED_RESULT InternalClass *addMember(PropertyKey identifier, PropertyAttributes data, uint *index = nullptr); + Q_REQUIRED_RESULT InternalClass *changeMember(PropertyKey identifier, PropertyAttributes data, uint *index = nullptr); + static void changeMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, uint *index = nullptr); + static void removeMember(QV4::Object *object, PropertyKey identifier); + uint find(const PropertyKey id) { + Q_ASSERT(id.isStringOrSymbol()); + uint index = propertyTable.lookup(id); if (index < size) return index; @@ -298,24 +309,37 @@ struct InternalClass : public QQmlJS::Managed { Q_REQUIRED_RESULT InternalClass *asProtoClass(); - void destroy(); + Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) { + if (vtable == vt) + return this; + return changeVTableImpl(vt); + } + Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) { + if (prototype == proto) + return this; + return changePrototypeImpl(proto); + } void updateProtoUsage(Heap::Object *o); + static void markObjects(Heap::Base *ic, MarkStack *stack); + private: Q_QML_EXPORT InternalClass *changeVTableImpl(const VTable *vt); Q_QML_EXPORT InternalClass *changePrototypeImpl(Heap::Object *proto); - InternalClass *addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index); - void updateInternalClassIdRecursive(); + InternalClass *addMemberImpl(PropertyKey identifier, PropertyAttributes data, uint *index); + + void removeChildEntry(InternalClass *child); friend struct ExecutionEngine; - InternalClass(ExecutionEngine *engine); - InternalClass(const InternalClass &other); }; -struct InternalClassPool : public QQmlJS::MemoryPool +inline +void Base::markObjects(Base *b, MarkStack *stack) { - void markObjects(MarkStack *markStack); -}; + b->internalClass->mark(stack); +} + +} } diff --git a/src/qml/jsruntime/qv4iterator.cpp b/src/qml/jsruntime/qv4iterator.cpp new file mode 100644 index 0000000000..df8000a8f7 --- /dev/null +++ b/src/qml/jsruntime/qv4iterator.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qv4iterator_p.h> +#include <qv4symbol_p.h> +#include <qv4engine_p.h> + +using namespace QV4; + +void IteratorPrototype::init(ExecutionEngine *engine) +{ + defineDefaultProperty(engine->symbol_iterator(), method_iterator, 0); +} + +ReturnedValue IteratorPrototype::method_iterator(const FunctionObject *, const Value *thisObject, const Value *, int) +{ + return thisObject->asReturnedValue(); +} + + +ReturnedValue IteratorPrototype::createIterResultObject(ExecutionEngine *engine, const Value &value, bool done) +{ + Scope scope(engine); + ScopedObject obj(scope, engine->newObject()); + obj->set(ScopedString(scope, engine->newString(QStringLiteral("value"))), value, Object::DoNotThrow); + obj->set(ScopedString(scope, engine->newString(QStringLiteral("done"))), Primitive::fromBoolean(done), Object::DoNotThrow); + return obj->asReturnedValue(); +} + diff --git a/src/qml/jsruntime/qv4iterator_p.h b/src/qml/jsruntime/qv4iterator_p.h new file mode 100644 index 0000000000..28e337d21b --- /dev/null +++ b/src/qml/jsruntime/qv4iterator_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ITERATOR_P_H +#define QV4ITERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4arraydata_p.h" + +QT_BEGIN_NAMESPACE + + +namespace QV4 { + +enum IteratorKind { + KeyIteratorKind, + ValueIteratorKind, + KeyValueIteratorKind +}; + +struct IteratorPrototype : Object +{ + void init(ExecutionEngine *engine); + + static ReturnedValue method_iterator(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue createIterResultObject(ExecutionEngine *engine, const Value &value, bool done); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4ARRAYITERATOR_P_H + diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h index c676b57c51..307eec9111 100644 --- a/src/qml/jsruntime/qv4jscall_p.h +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -55,6 +55,7 @@ #include "qv4functionobject_p.h" #include "qv4context_p.h" #include "qv4scopedvalue_p.h" +#include "qv4stackframe_p.h" QT_BEGIN_NAMESPACE @@ -67,7 +68,7 @@ struct JSCallData { if (thisObject) this->thisObject = const_cast<Value *>(thisObject); else - this->thisObject = scope.alloc(1); + this->thisObject = scope.alloc(); if (argv) this->args = const_cast<Value *>(argv); else @@ -80,8 +81,7 @@ struct JSCallData { CallData *callData(const FunctionObject *f = nullptr) const { int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + argc; - CallData *ptr = reinterpret_cast<CallData *>(scope.engine->jsStackTop); - scope.engine->jsStackTop += size; + CallData *ptr = reinterpret_cast<CallData *>(scope.alloc<Scope::Uninitialized>(size)); ptr->function = Encode::undefined(); ptr->context = Encode::undefined(); ptr->accumulator = Encode::undefined(); @@ -102,7 +102,9 @@ struct JSCallData { inline ReturnedValue FunctionObject::callAsConstructor(const JSCallData &data) const { - return d()->jsConstruct(this, data.args, data.argc); + if (!d()->jsConstruct) + return engine()->throwTypeError(QStringLiteral("Object is not a constructor.")); + return d()->jsConstruct(this, data.args, data.argc, this); } inline diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index c3569c29d2..2e2314cafe 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -47,6 +47,7 @@ #include <qv4variantobject_p.h> #include "qv4string_p.h" #include "qv4jscall_p.h" +#include <qv4symbol_p.h> #include <qstack.h> #include <qstringlist.h> @@ -260,11 +261,12 @@ bool JsonParser::parseMember(Object *o) if (!parseValue(val)) return false; - ScopedString s(scope, engine->newIdentifier(key)); - uint idx = s->asArrayIndex(); - if (idx < UINT_MAX) { - o->putIndexed(idx, val); + ScopedString s(scope, engine->newString(key)); + PropertyKey skey = s->toPropertyKey(); + if (skey.isArrayIndex()) { + o->put(skey.asArrayIndex(), val); } else { + // avoid trouble with properties named __proto__ o->insertMember(s, val); } @@ -743,7 +745,7 @@ QString Stringify::Str(const QString &key, const Value &v) o = value->asReturnedValue(); if (o) { if (!o->as<FunctionObject>()) { - if (o->as<ArrayObject>() || o->isListType()) { + if (o->isArrayLike()) { return JA(o.getPointer()); } else { return JO(o); @@ -846,7 +848,7 @@ QString Stringify::JA(Object *a) ScopedValue v(scope); for (uint i = 0; i < len; ++i) { bool exists; - v = a->getIndexed(i, &exists); + v = a->get(i, &exists); if (!exists) { partial += QStringLiteral("null"); continue; @@ -881,6 +883,8 @@ void Heap::JsonObject::init() o->defineDefaultProperty(QStringLiteral("parse"), QV4::JsonObject::method_parse, 2); o->defineDefaultProperty(QStringLiteral("stringify"), QV4::JsonObject::method_stringify, 3); + ScopedString json(scope, scope.engine->newString(QStringLiteral("JSON"))); + o->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), json); } @@ -916,7 +920,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value stringify.propertyList = static_cast<QV4::String *>(scope.alloc(arrayLen)); for (uint i = 0; i < arrayLen; ++i) { Value *v = stringify.propertyList + i; - *v = o->getIndexed(i); + *v = o->get(i); if (v->as<NumberObject>() || v->as<StringObject>() || v->isNumber()) *v = v->toString(scope.engine); if (!v->isString()) { @@ -1078,7 +1082,7 @@ QJsonArray JsonObject::toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObj ScopedValue v(scope); quint32 length = a->getLength(); for (quint32 i = 0; i < length; ++i) { - v = a->getIndexed(i); + v = a->get(i); if (v->as<FunctionObject>()) v = Encode::null(); result.append(toJsonValue(v, visitedObjects)); diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 52ab03cd94..daa5b2dfbd 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE using namespace QV4; -void Lookup::resolveProtoGetter(Identifier *name, const Heap::Object *proto) +void Lookup::resolveProtoGetter(PropertyKey name, const Heap::Object *proto) { while (proto) { uint index = proto->internalClass->find(name); @@ -70,7 +70,12 @@ void Lookup::resolveProtoGetter(Identifier *name, const Heap::Object *proto) ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object) { Heap::Object *obj = object->d(); - Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + if (name.isArrayIndex()) { + indexedLookup.index = name.asArrayIndex(); + getter = getterIndexed; + return getter(this, engine, *object); + } uint index = obj->internalClass->find(name); if (index != UINT_MAX) { @@ -92,7 +97,7 @@ ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *objec return getter(this, engine, *object); } - protoLookup.icIdentifier = obj->internalClass->id; + protoLookup.protoId = obj->internalClass->protoId; resolveProtoGetter(name, obj->prototype()); return getter(this, engine, *object); } @@ -109,11 +114,12 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu break; case Value::Managed_Type: { // ### Should move this over to the Object path, as strings also have an internalClass - Q_ASSERT(object.isString()); - primitiveLookup.proto = engine->stringPrototype()->d(); + Q_ASSERT(object.isStringOrSymbol()); + primitiveLookup.proto = static_cast<const Managed &>(object).internalClass()->prototype; + Q_ASSERT(primitiveLookup.proto); Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - if (name->equals(engine->id_length())) { + if (object.isString() && name->equals(engine->id_length())) { // special case, as the property is on the object itself getter = stringLengthGetter; return stringLengthGetter(this, engine, object); @@ -125,8 +131,8 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu primitiveLookup.proto = engine->numberPrototype()->d(); } - Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - protoLookup.icIdentifier = primitiveLookup.proto->internalClass->id; + PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + protoLookup.protoId = primitiveLookup.proto->internalClass->protoId; resolveProtoGetter(name, primitiveLookup.proto); if (getter == getterProto) @@ -139,8 +145,8 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu ReturnedValue Lookup::resolveGlobalGetter(ExecutionEngine *engine) { Object *o = engine->globalObject; - Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - protoLookup.icIdentifier = o->internalClass()->id; + PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + protoLookup.protoId = o->internalClass()->protoId; resolveProtoGetter(name, o->d()); if (getter == getterProto) @@ -188,16 +194,16 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const return result; } if (first.getter == getterProto && second.getter == getterProto) { - l->protoLookupTwoClasses.icIdentifier = first.protoLookup.icIdentifier; - l->protoLookupTwoClasses.icIdentifier2 = second.protoLookup.icIdentifier; + l->protoLookupTwoClasses.protoId = first.protoLookup.protoId; + l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId; l->protoLookupTwoClasses.data = first.protoLookup.data; l->protoLookupTwoClasses.data2 = second.protoLookup.data; l->getter = getterProtoTwoClasses; return result; } if (first.getter == getterProtoAccessor && second.getter == getterProtoAccessor) { - l->protoLookupTwoClasses.icIdentifier = first.protoLookup.icIdentifier; - l->protoLookupTwoClasses.icIdentifier2 = second.protoLookup.icIdentifier; + l->protoLookupTwoClasses.protoId = first.protoLookup.protoId; + l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId; l->protoLookupTwoClasses.data = first.protoLookup.data; l->protoLookupTwoClasses.data2 = second.protoLookup.data; l->getter = getterProtoAccessorTwoClasses; @@ -250,7 +256,7 @@ ReturnedValue Lookup::getterProto(Lookup *l, ExecutionEngine *engine, const Valu // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { - if (l->protoLookup.icIdentifier == o->internalClass->id) + if (l->protoLookup.protoId == o->internalClass->protoId) return l->protoLookup.data->asReturnedValue(); } return getterTwoClasses(l, engine, object); @@ -307,9 +313,9 @@ ReturnedValue Lookup::getterProtoTwoClasses(Lookup *l, ExecutionEngine *engine, // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { - if (l->protoLookupTwoClasses.icIdentifier == o->internalClass->id) + if (l->protoLookupTwoClasses.protoId == o->internalClass->protoId) return l->protoLookupTwoClasses.data->asReturnedValue(); - if (l->protoLookupTwoClasses.icIdentifier2 == o->internalClass->id) + if (l->protoLookupTwoClasses.protoId2 == o->internalClass->protoId) return l->protoLookupTwoClasses.data2->asReturnedValue(); return getterFallback(l, engine, object); } @@ -340,14 +346,13 @@ ReturnedValue Lookup::getterProtoAccessor(Lookup *l, ExecutionEngine *engine, co // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); - if (o && l->protoLookup.icIdentifier == o->internalClass->id) { + if (o && l->protoLookup.protoId == o->internalClass->protoId) { const Value *getter = l->protoLookup.data; if (!getter->isFunctionObject()) // ### catch at resolve time return Encode::undefined(); return static_cast<const FunctionObject *>(getter)->call(&object, nullptr, 0); } - l->getter = getterTwoClasses; return getterTwoClasses(l, engine, object); } @@ -358,9 +363,9 @@ ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine * Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { const Value *getter = nullptr; - if (l->protoLookupTwoClasses.icIdentifier == o->internalClass->id) + if (l->protoLookupTwoClasses.protoId == o->internalClass->protoId) getter = l->protoLookupTwoClasses.data; - else if (l->protoLookupTwoClasses.icIdentifier2 == o->internalClass->id) + else if (l->protoLookupTwoClasses.protoId2 == o->internalClass->protoId) getter = l->protoLookupTwoClasses.data2; if (getter) { if (!getter->isFunctionObject()) // ### catch at resolve time @@ -373,11 +378,29 @@ ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine * return getterFallback(l, engine, object); } +ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + Object *o = object.objectValue(); + if (o) { + Heap::Object *ho = o->d(); + if (ho->arrayData && ho->arrayData->type == Heap::ArrayData::Simple) { + Heap::SimpleArrayData *s = ho->arrayData.cast<Heap::SimpleArrayData>(); + if (l->indexedLookup.index < s->values.size) + if (!s->data(l->indexedLookup.index).isEmpty()) + return s->data(l->indexedLookup.index).asReturnedValue(); + } + return o->get(l->indexedLookup.index); + } + l->getter = getterFallback; + return getterFallback(l, engine, object); + +} + ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object) { if (object.type() == l->primitiveLookup.type) { Heap::Object *o = l->primitiveLookup.proto; - if (l->primitiveLookup.icIdentifier == o->internalClass->id) + if (l->primitiveLookup.protoId == o->internalClass->protoId) return l->primitiveLookup.data->asReturnedValue(); } l->getter = getterGeneric; @@ -388,7 +411,7 @@ ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine { if (object.type() == l->primitiveLookup.type) { Heap::Object *o = l->primitiveLookup.proto; - if (l->primitiveLookup.icIdentifier == o->internalClass->id) { + if (l->primitiveLookup.protoId == o->internalClass->protoId) { const Value *getter = l->primitiveLookup.data; if (!getter->isFunctionObject()) // ### catch at resolve time return Encode::undefined(); @@ -417,7 +440,7 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine) { Heap::Object *o = engine->globalObject->d(); - if (l->protoLookup.icIdentifier == o->internalClass->id) + if (l->protoLookup.protoId == o->internalClass->protoId) return l->protoLookup.data->asReturnedValue(); l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -426,7 +449,7 @@ ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engine) { Heap::Object *o = engine->globalObject->d(); - if (l->protoLookup.icIdentifier == o->internalClass->id) { + if (l->protoLookup.protoId == o->internalClass->protoId) { const Value *getter = l->protoLookup.data; if (!getter->isFunctionObject()) // ### catch at resolve time return Encode::undefined(); @@ -442,8 +465,9 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value Scope scope(engine); ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - InternalClass *c = object->internalClass(); - uint idx = c->find(name); + Heap::InternalClass *c = object->internalClass(); + PropertyKey key = name->toPropertyKey(); + uint idx = c->find(key); if (idx != UINT_MAX) { if (object->isArrayObject() && idx == Heap::ArrayObject::LengthPropertyIndex) { setter = arrayLengthSetter; @@ -460,8 +484,8 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value return setter(this, engine, *object, value); } - insertionLookup.icIdentifier = c->id; - if (!object->put(name, value)) { + insertionLookup.protoId = c->protoId; + if (!object->put(key, value)) { setter = Lookup::setterFallback; return false; } @@ -471,7 +495,7 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value setter = setterFallback; return true; } - idx = object->internalClass()->find(name); + idx = object->internalClass()->find(key); if (idx == UINT_MAX) { // ### can this even happen? setter = setterFallback; return false; @@ -574,7 +598,7 @@ bool Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, c bool Lookup::setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); - if (o && o->internalClass()->id == l->insertionLookup.icIdentifier) { + if (o && o->internalClass()->protoId == l->insertionLookup.protoId) { o->setInternalClass(l->insertionLookup.newClass); o->d()->setProperty(engine, l->insertionLookup.offset, value); return true; diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 5f507733fd..c7555539b4 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -65,7 +65,6 @@ QT_BEGIN_NAMESPACE namespace QV4 { struct Lookup { - enum { Size = 4 }; union { ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); @@ -73,44 +72,57 @@ struct Lookup { }; union { struct { - InternalClass *ic; + Heap::Base *h1; + Heap::Base *h2; + quintptr unused; + quintptr unused2; + } markDef; + struct { + Heap::InternalClass *ic; + quintptr _unused; int offset; } objectLookup; struct { + quintptr protoId; + quintptr _unused; const Value *data; - int icIdentifier; } protoLookup; struct { - InternalClass *ic; - InternalClass *ic2; + Heap::InternalClass *ic; + Heap::InternalClass *ic2; int offset; int offset2; } objectLookupTwoClasses; struct { + quintptr protoId; + quintptr protoId2; const Value *data; const Value *data2; - int icIdentifier; - int icIdentifier2; } protoLookupTwoClasses; struct { // Make sure the next two values are in sync with protoLookup - const Value *data; - int icIdentifier; - unsigned type; + quintptr protoId; Heap::Object *proto; + const Value *data; + quintptr type; } primitiveLookup; struct { - InternalClass *newClass; - int icIdentifier; + Heap::InternalClass *newClass; + quintptr protoId; int offset; } insertionLookup; + struct { + quintptr _unused; + quintptr _unused2; + uint index; + } indexedLookup; }; uint nameIndex; ReturnedValue resolveGetter(ExecutionEngine *engine, const Object *object); ReturnedValue resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object); ReturnedValue resolveGlobalGetter(ExecutionEngine *engine); - void resolveProtoGetter(Identifier *name, const Heap::Object *proto); + void resolveProtoGetter(PropertyKey name, const Heap::Object *proto); static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -126,6 +138,7 @@ struct Lookup { static ReturnedValue getterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterProtoAccessor(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -144,6 +157,17 @@ struct Lookup { static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool arrayLengthSetter(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + + void markObjects(MarkStack *stack) { + if (markDef.h1 && !(reinterpret_cast<quintptr>(markDef.h1) & 1)) + markDef.h1->mark(stack); + if (markDef.h2 && !(reinterpret_cast<quintptr>(markDef.h2) & 1)) + markDef.h2->mark(stack); + } + + void clear() { + memset(&markDef, 0, sizeof(markDef)); + } }; Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value); diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index b50e5f0355..bb7b8086e4 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -43,34 +43,23 @@ using namespace QV4; +DEFINE_MANAGED_VTABLE(Managed); -const VTable Managed::static_vtbl = -{ - nullptr, - 0, - 0, - Managed::IsExecutionContext, - Managed::IsString, - Managed::IsObject, - Managed::IsFunctionObject, - Managed::IsErrorObject, - Managed::IsArrayData, - 0, - Managed::MyType, - "Managed", - nullptr, - nullptr /*markObjects*/, - isEqualTo -}; +DEFINE_MANAGED_VTABLE(InternalClass); QString Managed::className() const { const char *s = nullptr; - switch (Type(d()->vtable()->type)) { + switch (Type(vtable()->type)) { case Type_Invalid: - case Type_String: return QString(); + case Type_String: + s = "String"; + break; + case Type_Symbol: + s = "Symbol"; + break; case Type_Object: s = "Object"; break; @@ -80,6 +69,9 @@ QString Managed::className() const case Type_FunctionObject: s = "Function"; break; + case Type_GeneratorObject: + s = "Generator"; + break; case Type_BooleanObject: s = "Boolean"; break; @@ -89,6 +81,9 @@ QString Managed::className() const case Type_StringObject: s = "String"; break; + case Type_SymbolObject: + s = "Symbol"; + break; case Type_DateObject: s = "Date"; break; @@ -96,7 +91,7 @@ QString Managed::className() const s = "RegExp"; break; case Type_ErrorObject: - s = ErrorObject::className(static_cast<Heap::ErrorObject *>(d())->errorType); + s = "Error"; break; case Type_ArgumentsObject: s = "Arguments"; @@ -104,6 +99,9 @@ QString Managed::className() const case Type_JsonObject: s = "JSON"; break; + case Type_ProxyObject: + s = "ProxyObject"; + break; case Type_MathObject: s = "Math"; break; @@ -111,8 +109,23 @@ QString Managed::className() const case Type_ExecutionContext: s = "__ExecutionContext"; break; - case Type_ForeachIteratorObject: - s = "__ForeachIterator"; + case Type_MapIteratorObject: + s = "Map Iterator"; + break; + case Type_SetIteratorObject: + s = "Set Iterator"; + break; + case Type_ArrayIteratorObject: + s = "Array Iterator"; + break; + case Type_StringIteratorObject: + s = "String Iterator"; + break; + case Type_ForInIterator: + s = "__ForIn Iterator"; + break; + case Type_InternalClass: + s = "__InternalClass"; break; case Type_RegExp: s = "__RegExp"; @@ -125,7 +138,7 @@ QString Managed::className() const return QString::fromLatin1(s); } -bool Managed::isEqualTo(Managed *, Managed *) +bool Managed::virtualIsEqualTo(Managed *, Managed *) { return false; } diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 092c61b81c..cacb262ba7 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -55,6 +55,7 @@ #include "qv4enginebase_p.h" #include <private/qv4heap_p.h> #include <private/qv4writebarrier_p.h> +#include <private/qv4vtable_p.h> QT_BEGIN_NAMESPACE @@ -70,13 +71,9 @@ inline int qYouForgotTheQ_MANAGED_Macro(T, T) { return 0; } template <typename T1, typename T2> inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} -#ifdef Q_COMPILER_STATIC_ASSERT -#define V4_MANAGED_SIZE_TEST void __dataTest() { Q_STATIC_ASSERT(sizeof(*this) == sizeof(Managed)); } -#else -#define V4_MANAGED_SIZE_TEST -#endif +#define V4_MANAGED_SIZE_TEST void __dataTest() { static_assert (sizeof(*this) == sizeof(Managed), "Classes derived from Managed can't have own data members."); } -#define V4_NEEDS_DESTROY static void destroy(QV4::Heap::Base *b) { static_cast<Data *>(b)->destroy(); } +#define V4_NEEDS_DESTROY static void virtualDestroy(QV4::Heap::Base *b) { static_cast<Data *>(b)->destroy(); } #define V4_MANAGED_ITSELF(DataClass, superClass) \ @@ -92,77 +89,30 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} QV4::Heap::DataClass *dptr = d_unchecked(); \ dptr->_checkIsInitialized(); \ return dptr; \ - } \ - Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value); + } #define V4_MANAGED(DataClass, superClass) \ private: \ DataClass() Q_DECL_EQ_DELETE; \ Q_DISABLE_COPY(DataClass) \ - V4_MANAGED_ITSELF(DataClass, superClass) + V4_MANAGED_ITSELF(DataClass, superClass) \ + Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value); #define Q_MANAGED_TYPE(type) \ public: \ enum { MyType = Type_##type }; -#define Q_VTABLE_FUNCTION(classname, func) \ - (classname::func == QV4::Managed::func ? 0 : classname::func) - -// Q_VTABLE_FUNCTION triggers a bogus tautological-compare warning in GCC6+ -#if (defined(Q_CC_GNU) && Q_CC_GNU >= 600) -#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \ - QT_WARNING_PUSH; \ - QT_WARNING_DISABLE_GCC("-Wtautological-compare") - -#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF \ - ;QT_WARNING_POP -#elif defined(Q_CC_CLANG) && Q_CC_CLANG >= 306 -#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \ - QT_WARNING_PUSH; \ - QT_WARNING_DISABLE_CLANG("-Wtautological-compare") - -#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF \ - ;QT_WARNING_POP -#else -#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON -#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF -#endif - -#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ -{ \ - parentVTable, \ - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ - (sizeof(classname::Data) + (classname::NInlineProperties*sizeof(QV4::Value)) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \ - - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ - classname::IsExecutionContext, \ - classname::IsString, \ - classname::IsObject, \ - classname::IsFunctionObject, \ - classname::IsErrorObject, \ - classname::IsArrayData, \ - 0, \ - classname::MyType, \ - #classname, \ - Q_VTABLE_FUNCTION(classname, destroy), \ - classname::Data::markObjects, \ - isEqualTo \ -} \ - -#define DEFINE_MANAGED_VTABLE(classname) \ -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \ -const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0) \ -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF - #define V4_INTERNALCLASS(c) \ - static QV4::InternalClass *defaultInternalClass(QV4::EngineBase *e) \ - { return e->internalClasses[QV4::EngineBase::Class_##c]; } + static Heap::InternalClass *defaultInternalClass(QV4::EngineBase *e) \ + { return e->internalClasses(QV4::EngineBase::Class_##c); } -struct Q_QML_PRIVATE_EXPORT Managed : Value +struct Q_QML_PRIVATE_EXPORT Managed : Value, VTableBase { V4_MANAGED_ITSELF(Base, Managed) enum { IsExecutionContext = false, IsString = false, + IsStringOrSymbol = false, IsObject = false, IsFunctionObject = false, IsErrorObject = false, @@ -180,47 +130,55 @@ public: Type_Invalid, Type_String, Type_Object, + Type_Symbol, Type_ArrayObject, Type_FunctionObject, + Type_GeneratorObject, Type_BooleanObject, Type_NumberObject, Type_StringObject, + Type_SymbolObject, Type_DateObject, Type_RegExpObject, Type_ErrorObject, Type_ArgumentsObject, Type_JsonObject, Type_MathObject, + Type_ProxyObject, Type_ExecutionContext, - Type_ForeachIteratorObject, + Type_InternalClass, + Type_SetIteratorObject, + Type_MapIteratorObject, + Type_ArrayIteratorObject, + Type_StringIteratorObject, + Type_ForInIterator, Type_RegExp, Type_QmlSequence }; Q_MANAGED_TYPE(Invalid) - InternalClass *internalClass() const { return d()->internalClass; } + Heap::InternalClass *internalClass() const { return d()->internalClass; } + const VTable *vtable() const { return d()->internalClass->vtable; } inline ExecutionEngine *engine() const { return internalClass()->engine; } - bool isListType() const { return d()->vtable()->type == Type_QmlSequence; } + bool isListType() const { return d()->internalClass->vtable->type == Type_QmlSequence; } + bool isArrayLike() const { return isArrayObject() || isListType(); } - bool isArrayObject() const { return d()->vtable()->type == Type_ArrayObject; } - bool isStringObject() const { return d()->vtable()->type == Type_StringObject; } + bool isArrayObject() const { return d()->internalClass->vtable->type == Type_ArrayObject; } + bool isStringObject() const { return d()->internalClass->vtable->type == Type_StringObject; } + bool isSymbolObject() const { return d()->internalClass->vtable->type == Type_SymbolObject; } QString className() const; bool isEqualTo(const Managed *other) const - { return d()->vtable()->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); } - - static bool isEqualTo(Managed *m, Managed *other); + { return d()->internalClass->vtable->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); } bool inUse() const { return d()->inUse(); } bool markBit() const { return d()->isMarked(); } inline void mark(MarkStack *markStack); - static void destroy(Heap::Base *) {} - Q_ALWAYS_INLINE Heap::Base *heapObject() const { return m(); } @@ -232,6 +190,9 @@ public: return static_cast<const T *>(this); } +protected: + static bool virtualIsEqualTo(Managed *m, Managed *other); + private: friend class MemoryManager; friend struct Identifiers; @@ -254,6 +215,29 @@ inline const Object *Value::as() const { return objectValue(); } + +struct InternalClass : Managed +{ + V4_MANAGED_ITSELF(InternalClass, Managed) + Q_MANAGED_TYPE(InternalClass) + V4_INTERNALCLASS(Empty) + V4_NEEDS_DESTROY + + Q_REQUIRED_RESULT Heap::InternalClass *changeVTable(const VTable *vt) { + return d()->changeVTable(vt); + } + Q_REQUIRED_RESULT Heap::InternalClass *changePrototype(Heap::Object *proto) { + return d()->changePrototype(proto); + } + Q_REQUIRED_RESULT Heap::InternalClass *addMember(PropertyKey identifier, PropertyAttributes data, uint *index = 0) { + return d()->addMember(identifier, data, index); + } + + void operator =(Heap::InternalClass *ic) { + Value::operator=(ic); + } +}; + } diff --git a/src/qml/jsruntime/qv4mapiterator.cpp b/src/qml/jsruntime/qv4mapiterator.cpp new file mode 100644 index 0000000000..7be7416e4a --- /dev/null +++ b/src/qml/jsruntime/qv4mapiterator.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qv4iterator_p.h> +#include <private/qv4estable_p.h> +#include <private/qv4mapiterator_p.h> +#include <private/qv4mapobject_p.h> +#include <private/qv4symbol_p.h> + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(MapIteratorObject); + +void MapIteratorPrototype::init(ExecutionEngine *e) +{ + defineDefaultProperty(QStringLiteral("next"), method_next, 0); + + Scope scope(e); + ScopedString val(scope, e->newString(QLatin1String("Map Iterator"))); + defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val); +} + +ReturnedValue MapIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int) +{ + Scope scope(b); + const MapIteratorObject *thisObject = that->as<MapIteratorObject>(); + if (!thisObject) + return scope.engine->throwTypeError(QLatin1String("Not a Map Iterator instance")); + + Scoped<MapObject> s(scope, thisObject->d()->iteratedMap); + uint index = thisObject->d()->mapNextIndex; + IteratorKind itemKind = thisObject->d()->iterationKind; + + if (!s) { + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + Value *arguments = scope.alloc(2); + + while (index < s->d()->esTable->size()) { + s->d()->esTable->iterate(index, &arguments[0], &arguments[1]); + thisObject->d()->mapNextIndex = index + 1; + + ScopedValue result(scope); + + if (itemKind == KeyIteratorKind) { + result = arguments[0]; + } else if (itemKind == ValueIteratorKind) { + result = arguments[1]; + } else { + Q_ASSERT(itemKind == KeyValueIteratorKind); + + result = scope.engine->newArrayObject(); + + Scoped<ArrayObject> resultArray(scope, result); + resultArray->arrayReserve(2); + resultArray->arrayPut(0, arguments[0]); + resultArray->arrayPut(1, arguments[1]); + resultArray->setArrayLengthUnchecked(2); + } + + return IteratorPrototype::createIterResultObject(scope.engine, result, false); + } + + thisObject->d()->iteratedMap.set(scope.engine, nullptr); + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); +} + + diff --git a/src/qml/jsruntime/qv4mapiterator_p.h b/src/qml/jsruntime/qv4mapiterator_p.h new file mode 100644 index 0000000000..836ba14663 --- /dev/null +++ b/src/qml/jsruntime/qv4mapiterator_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4MAPITERATOR_P_H +#define QV4MAPITERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4iterator_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +#define MapIteratorObjectMembers(class, Member) \ + Member(class, Pointer, Object *, iteratedMap) \ + Member(class, NoMark, IteratorKind, iterationKind) \ + Member(class, NoMark, quint32, mapNextIndex) + +DECLARE_HEAP_OBJECT(MapIteratorObject, Object) { + DECLARE_MARKOBJECTS(MapIteratorObject); + void init(Object *obj, QV4::ExecutionEngine *engine) + { + Object::init(); + this->iteratedMap.set(engine, obj); + this->mapNextIndex = 0; + } +}; + +} + +struct MapIteratorPrototype : Object +{ + V4_PROTOTYPE(iteratorPrototype) + void init(ExecutionEngine *engine); + + static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +}; + +struct MapIteratorObject : Object +{ + V4_OBJECT2(MapIteratorObject, Object) + Q_MANAGED_TYPE(MapIteratorObject) + V4_PROTOTYPE(mapIteratorPrototype) + + void init(ExecutionEngine *engine); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4MAPITERATOR_P_H + + diff --git a/src/qml/jsruntime/qv4mapobject.cpp b/src/qml/jsruntime/qv4mapobject.cpp new file mode 100644 index 0000000000..ca9e1723f9 --- /dev/null +++ b/src/qml/jsruntime/qv4mapobject.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4setobject_p.h" // ### temporary +#include "qv4mapobject_p.h" +#include "qv4mapiterator_p.h" +#include "qv4estable_p.h" +#include "qv4symbol_p.h" + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(MapCtor); +DEFINE_OBJECT_VTABLE(MapObject); + +void Heap::MapCtor::init(QV4::ExecutionContext *scope) +{ + Heap::FunctionObject::init(scope, QStringLiteral("Map")); +} + +ReturnedValue MapCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +{ + Scope scope(f); + Scoped<MapObject> a(scope, scope.engine->memoryManager->allocate<MapObject>()); + + if (argc > 0) { + ScopedValue iterable(scope, argv[0]); + + // ### beware, hack alert! + // Object iteration seems broken right now. if we allow any object to + // iterate, it endlessly loops in the Map/prototype tests in test262... + // disable these for now until Object iteration is fixed, just so we can + // test this. + Scoped<MapObject> mapObjectCheck(scope, argv[0]); + Scoped<SetObject> setObjectCheck(scope, argv[0]); + + if (!iterable->isUndefined() && !iterable->isNull() && (mapObjectCheck || setObjectCheck)) { + ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("set"))))); + if (!adder) + return scope.engine->throwTypeError(); + ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true)); + + CHECK_EXCEPTION(); + if (!iter) + return a.asReturnedValue(); + + Value *nextValue = scope.alloc(1); + ScopedValue done(scope); + forever { + done = Runtime::method_iteratorNext(scope.engine, iter, nextValue); + CHECK_EXCEPTION(); + if (done->toBoolean()) + return a.asReturnedValue(); + + adder->call(a, nextValue, 1); + if (scope.engine->hasException) { + ScopedValue falsey(scope, Encode(false)); + return Runtime::method_iteratorClose(scope.engine, iter, falsey); + } + } + } + } + return a.asReturnedValue(); +} + +ReturnedValue MapCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int) +{ + Scope scope(f); + return scope.engine->throwTypeError(QString::fromLatin1("Map requires new")); +} + +void MapPrototype::init(ExecutionEngine *engine, Object *ctor) +{ + Scope scope(engine); + ScopedObject o(scope); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + ctor->addSymbolSpecies(); + defineDefaultProperty(engine->id_constructor(), (o = ctor)); + + defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); + defineDefaultProperty(QStringLiteral("delete"), method_delete, 1); + defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1); + defineDefaultProperty(QStringLiteral("get"), method_get, 1); + defineDefaultProperty(QStringLiteral("has"), method_has, 1); + defineDefaultProperty(QStringLiteral("keys"), method_keys, 0); + defineDefaultProperty(QStringLiteral("set"), method_set, 0); + defineAccessorProperty(QStringLiteral("size"), method_get_size, nullptr); + defineDefaultProperty(QStringLiteral("values"), method_values, 0); + + // Per the spec, the value for entries/@@iterator is the same + ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("entries"))); + ScopedFunctionObject entriesFn(scope, FunctionObject::createBuiltinFunction(engine, valString, MapPrototype::method_entries, 0)); + defineDefaultProperty(QStringLiteral("entries"), entriesFn); + defineDefaultProperty(engine->symbol_iterator(), entriesFn); + + ScopedString val(scope, engine->newString(QLatin1String("Map"))); + defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val); +} + +void Heap::MapObject::init() +{ + Object::init(); + esTable = new ESTable(); +} + +void Heap::MapObject::destroy() +{ + delete esTable; + esTable = 0; +} + +void Heap::MapObject::markObjects(Heap::Base *that, MarkStack *markStack) +{ + MapObject *m = static_cast<MapObject *>(that); + m->esTable->markObjects(markStack); + Object::markObjects(that, markStack); +} + +ReturnedValue MapPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + that->d()->esTable->clear(); + return Encode::undefined(); +} + +ReturnedValue MapPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + return Encode(that->d()->esTable->remove(argv[0])); +} + +ReturnedValue MapPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that)); + ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue MapPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + ScopedFunctionObject callbackfn(scope, argv[0]); + if (!callbackfn) + return scope.engine->throwTypeError(); + + ScopedValue thisArg(scope, Primitive::undefinedValue()); + if (argc > 1) + thisArg = ScopedValue(scope, argv[1]); + + Value *arguments = scope.alloc(3); + for (uint i = 0; i < that->d()->esTable->size(); ++i) { + that->d()->esTable->iterate(i, &arguments[0], &arguments[1]); // fill in key (0), value (1) + + arguments[2] = that; + callbackfn->call(thisArg, arguments, 3); + CHECK_EXCEPTION(); + } + return Encode::undefined(); +} + +ReturnedValue MapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + return that->d()->esTable->get(argv[0]); +} + +ReturnedValue MapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + return Encode(that->d()->esTable->has(argv[0])); +} + +ReturnedValue MapPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that)); + ao->d()->iterationKind = IteratorKind::KeyIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue MapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + that->d()->esTable->set(argv[0], argv[1]); + return that.asReturnedValue(); +} + +ReturnedValue MapPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + return Encode(that->d()->esTable->size()); +} + +ReturnedValue MapPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that)); + ao->d()->iterationKind = IteratorKind::ValueIteratorKind; + return ao->asReturnedValue(); +} + + diff --git a/src/qml/jsruntime/qv4mapobject_p.h b/src/qml/jsruntime/qv4mapobject_p.h new file mode 100644 index 0000000000..6793612bcb --- /dev/null +++ b/src/qml/jsruntime/qv4mapobject_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4MAPOBJECT_P_H +#define QV4MAPOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4objectproto_p.h" +#include "qv4functionobject_p.h" +#include "qv4string_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +class ESTable; + +namespace Heap { + +struct MapCtor : FunctionObject { + void init(QV4::ExecutionContext *scope); +}; + +struct MapObject : Object { + static void markObjects(Heap::Base *that, MarkStack *markStack); + void init(); + void destroy(); + ESTable *esTable; +}; + +} + +struct MapCtor: FunctionObject +{ + V4_OBJECT2(MapCtor, FunctionObject) + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct MapObject : Object +{ + V4_OBJECT2(MapObject, Object) + V4_PROTOTYPE(mapPrototype) + V4_NEEDS_DESTROY +}; + +struct MapPrototype : Object +{ + void init(ExecutionEngine *engine, Object *ctor); + + static ReturnedValue method_clear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_size(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + + +} // namespace QV4 + + +QT_END_NAMESPACE + +#endif // QV4MAPOBJECT_P_H + diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index 0c18d908de..e176235786 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -39,6 +39,7 @@ #include "qv4mathobject_p.h" #include "qv4objectproto_p.h" +#include "qv4symbol_p.h" #include <QtCore/qdatetime.h> #include <QtCore/qmath.h> @@ -70,14 +71,27 @@ void Heap::MathObject::init() m->defineDefaultProperty(QStringLiteral("abs"), QV4::MathObject::method_abs, 1); m->defineDefaultProperty(QStringLiteral("acos"), QV4::MathObject::method_acos, 1); - m->defineDefaultProperty(QStringLiteral("asin"), QV4::MathObject::method_asin, 0); + m->defineDefaultProperty(QStringLiteral("acosh"), QV4::MathObject::method_acosh, 1); + m->defineDefaultProperty(QStringLiteral("asin"), QV4::MathObject::method_asin, 1); + m->defineDefaultProperty(QStringLiteral("asinh"), QV4::MathObject::method_asinh, 1); m->defineDefaultProperty(QStringLiteral("atan"), QV4::MathObject::method_atan, 1); + m->defineDefaultProperty(QStringLiteral("atanh"), QV4::MathObject::method_atanh, 1); m->defineDefaultProperty(QStringLiteral("atan2"), QV4::MathObject::method_atan2, 2); + m->defineDefaultProperty(QStringLiteral("cbrt"), QV4::MathObject::method_cbrt, 1); m->defineDefaultProperty(QStringLiteral("ceil"), QV4::MathObject::method_ceil, 1); + m->defineDefaultProperty(QStringLiteral("clz32"), QV4::MathObject::method_clz32, 1); m->defineDefaultProperty(QStringLiteral("cos"), QV4::MathObject::method_cos, 1); + m->defineDefaultProperty(QStringLiteral("cosh"), QV4::MathObject::method_cosh, 1); m->defineDefaultProperty(QStringLiteral("exp"), QV4::MathObject::method_exp, 1); + m->defineDefaultProperty(QStringLiteral("expm1"), QV4::MathObject::method_expm1, 1); m->defineDefaultProperty(QStringLiteral("floor"), QV4::MathObject::method_floor, 1); + m->defineDefaultProperty(QStringLiteral("fround"), QV4::MathObject::method_fround, 1); + m->defineDefaultProperty(QStringLiteral("hypot"), QV4::MathObject::method_hypot, 2); + m->defineDefaultProperty(QStringLiteral("imul"), QV4::MathObject::method_imul, 2); m->defineDefaultProperty(QStringLiteral("log"), QV4::MathObject::method_log, 1); + m->defineDefaultProperty(QStringLiteral("log10"), QV4::MathObject::method_log10, 1); + m->defineDefaultProperty(QStringLiteral("log1p"), QV4::MathObject::method_log1p, 1); + m->defineDefaultProperty(QStringLiteral("log2"), QV4::MathObject::method_log2, 1); m->defineDefaultProperty(QStringLiteral("max"), QV4::MathObject::method_max, 2); m->defineDefaultProperty(QStringLiteral("min"), QV4::MathObject::method_min, 2); m->defineDefaultProperty(QStringLiteral("pow"), QV4::MathObject::method_pow, 2); @@ -85,8 +99,14 @@ void Heap::MathObject::init() m->defineDefaultProperty(QStringLiteral("round"), QV4::MathObject::method_round, 1); m->defineDefaultProperty(QStringLiteral("sign"), QV4::MathObject::method_sign, 1); m->defineDefaultProperty(QStringLiteral("sin"), QV4::MathObject::method_sin, 1); + m->defineDefaultProperty(QStringLiteral("sinh"), QV4::MathObject::method_sinh, 1); m->defineDefaultProperty(QStringLiteral("sqrt"), QV4::MathObject::method_sqrt, 1); m->defineDefaultProperty(QStringLiteral("tan"), QV4::MathObject::method_tan, 1); + m->defineDefaultProperty(QStringLiteral("tanh"), QV4::MathObject::method_tanh, 1); + m->defineDefaultProperty(QStringLiteral("trunc"), QV4::MathObject::method_trunc, 1); + + ScopedString name(scope, scope.engine->newString(QStringLiteral("Math"))); + m->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name); } static Q_ALWAYS_INLINE double copySign(double x, double y) @@ -120,6 +140,19 @@ ReturnedValue MathObject::method_acos(const FunctionObject *, const Value *, con RETURN_RESULT(Encode(std::acos(v))); } +ReturnedValue MathObject::method_acosh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : 2; + if (v < 1) + RETURN_RESULT(Encode(qt_qnan())); + +#ifdef Q_OS_ANDROID // incomplete std :-( + RETURN_RESULT(Encode(std::log(v +std::sqrt(v + 1) * std::sqrt(v - 1)))); +#else + RETURN_RESULT(Encode(std::acosh(v))); +#endif +} + ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : 2; @@ -129,6 +162,19 @@ ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, con RETURN_RESULT(Encode(std::asin(v))); } +ReturnedValue MathObject::method_asinh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : 2; + if (v == 0.0) + RETURN_RESULT(Encode(v)); + +#ifdef Q_OS_ANDROID // incomplete std :-( + RETURN_RESULT(Encode(std::log(v +std::sqrt(1 + v * v)))); +#else + RETURN_RESULT(Encode(std::asinh(v))); +#endif +} + ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -138,6 +184,25 @@ ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, con RETURN_RESULT(Encode(std::atan(v))); } +ReturnedValue MathObject::method_atanh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v == 0.0) + RETURN_RESULT(Encode(v)); + +#ifdef Q_OS_ANDROID // incomplete std :-( + if (-1 < v && v < 1) + RETURN_RESULT(Encode(0.5 * (std::log(v + 1) - std::log(v - 1)))); + + if (v > 1 || v < -1) + RETURN_RESULT(Encode(qt_qnan())); + + RETURN_RESULT(Encode(copySign(qt_inf(), v))); +#else + RETURN_RESULT(Encode(std::atanh(v))); +#endif +} + ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, const Value *argv, int argc) { double v1 = argc ? argv[0].toNumber() : qt_qnan(); @@ -156,6 +221,16 @@ ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, co RETURN_RESULT(Encode(std::atan2(v1, v2))); } +ReturnedValue MathObject::method_cbrt(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); +#ifdef Q_OS_ANDROID // incomplete std :-( + RETURN_RESULT(Encode(copySign(std::exp(std::log(std::abs(v)) / 3), v))); +#else + RETURN_RESULT(Encode(std::cbrt(v))); // cube root +#endif +} + ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -165,12 +240,24 @@ ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, con RETURN_RESULT(Encode(std::ceil(v))); } +ReturnedValue MathObject::method_clz32(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + quint32 v = argc ? argv[0].toUInt32() : 0; + RETURN_RESULT(Encode(qint32(qCountLeadingZeroBits(v)))); +} + ReturnedValue MathObject::method_cos(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); RETURN_RESULT(Encode(std::cos(v))); } +ReturnedValue MathObject::method_cosh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + RETURN_RESULT(Encode(std::cosh(v))); +} + ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -184,6 +271,25 @@ ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, cons } } +ReturnedValue MathObject::method_expm1(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (std::isnan(v) || qIsNull(v)) { + RETURN_RESULT(Encode(v)); + } else if (qt_is_inf(v)) { + if (copySign(1.0, v) == -1.0) + RETURN_RESULT(Encode(-1.0)); + else + RETURN_RESULT(Encode(qt_inf())); + } else { +#ifdef Q_OS_ANDROID // incomplete std :-( + RETURN_RESULT(Encode(std::exp(v) - 1)); +#else + RETURN_RESULT(Encode(std::expm1(v))); +#endif + } +} + ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -192,6 +298,53 @@ ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, co RETURN_RESULT(result); } +ReturnedValue MathObject::method_fround(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (std::isnan(v) || qt_is_inf(v) || qIsNull(v)) + RETURN_RESULT(Encode(v)); + else // convert to 32-bit float using roundTiesToEven, then convert back to 64-bit double + RETURN_RESULT(Encode(double(float(v)))); +} + +ReturnedValue MathObject::method_hypot(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + // ES6 Math.hypot(v1, ..., vn) -> sqrt(sum(vi**2)) but "should take care to + // avoid the loss of precision from overflows and underflows" (as std::hypot does). + double v = argc ? argv[0].toNumber() : 0; + // Spec mandates +0 on no args; and says nothing about what to do if toNumber() signals ... +#ifdef Q_OS_ANDROID // incomplete std :-( + bool big = qt_is_inf(v), bad = std::isnan(v); + v *= v; + for (int i = 1; !big && i < argc; i++) { + double u = argv[i].toNumber(); + if (qt_is_inf(u)) + big = true; + if (std::isnan(u)) + bad = true; + v += u * u; + } + if (big) + RETURN_RESULT(Encode(qt_inf())); + if (bad) + RETURN_RESULT(Encode(qt_qnan())); + // Should actually check for {und,ov}erflow, but too fiddly ! + RETURN_RESULT(Primitive::fromDouble(sqrt(v))); +#else + for (int i = 1; i < argc; i++) + v = std::hypot(v, argv[i].toNumber()); +#endif + RETURN_RESULT(Primitive::fromDouble(v)); +} + +ReturnedValue MathObject::method_imul(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + quint32 a = argc ? argv[0].toUInt32() : 0; + quint32 b = argc > 0 ? argv[1].toUInt32() : 0; + qint32 product = a * b; + RETURN_RESULT(Encode(product)); +} + ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -201,6 +354,43 @@ ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, cons RETURN_RESULT(Encode(std::log(v))); } +ReturnedValue MathObject::method_log10(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v < 0) + RETURN_RESULT(Encode(qt_qnan())); + else + RETURN_RESULT(Encode(std::log10(v))); +} + +ReturnedValue MathObject::method_log1p(const FunctionObject *, const Value *, const Value *argv, int argc) +{ +#if !defined(__ANDROID__) + using std::log1p; +#endif + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v < -1) + RETURN_RESULT(Encode(qt_qnan())); + else + RETURN_RESULT(Encode(log1p(v))); +} + +ReturnedValue MathObject::method_log2(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v < 0) { + RETURN_RESULT(Encode(qt_qnan())); + } else { +#ifdef Q_OS_ANDROID // incomplete std :-( + // Android ndk r10e doesn't have std::log2, so fall back. + const double ln2 = std::log(2.0); + RETURN_RESULT(Encode(std::log(v) / ln2)); +#else + RETURN_RESULT(Encode(std::log2(v))); +#endif + } +} + ReturnedValue MathObject::method_max(const FunctionObject *, const Value *, const Value *argv, int argc) { double mx = -qt_inf(); @@ -209,7 +399,7 @@ ReturnedValue MathObject::method_max(const FunctionObject *, const Value *, cons if (x > mx || std::isnan(x)) mx = x; } - RETURN_RESULT(Encode(mx)); + RETURN_RESULT(Encode::smallestNumber(mx)); } ReturnedValue MathObject::method_min(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -222,7 +412,7 @@ ReturnedValue MathObject::method_min(const FunctionObject *, const Value *, cons mx = x; } } - RETURN_RESULT(Encode(mx)); + RETURN_RESULT(Encode::smallestNumber(mx)); } ReturnedValue MathObject::method_pow(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -283,8 +473,11 @@ ReturnedValue MathObject::method_random(const FunctionObject *, const Value *, c ReturnedValue MathObject::method_round(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); - v = copySign(std::floor(v + 0.5), v); - RETURN_RESULT(Encode(v)); + if (std::isnan(v) || qt_is_inf(v) || qIsNull(v)) + RETURN_RESULT(Encode(v)); + + v = copySign(std::floor(v + 0.5), v); + RETURN_RESULT(Encode(v)); } ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -303,7 +496,19 @@ ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, con ReturnedValue MathObject::method_sin(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); - RETURN_RESULT(Encode(std::sin(v))); + if (v == 0.0) + RETURN_RESULT(Encode(v)); + else + RETURN_RESULT(Encode(std::sin(v))); +} + +ReturnedValue MathObject::method_sinh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v == 0.0) + RETURN_RESULT(Encode(v)); + else + RETURN_RESULT(Encode(std::sinh(v))); } ReturnedValue MathObject::method_sqrt(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -321,3 +526,25 @@ ReturnedValue MathObject::method_tan(const FunctionObject *, const Value *, cons RETURN_RESULT(Encode(std::tan(v))); } +ReturnedValue MathObject::method_tanh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v == 0.0) + RETURN_RESULT(Encode(v)); + else + RETURN_RESULT(Encode(std::tanh(v))); +} + +ReturnedValue MathObject::method_trunc(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); +#ifdef Q_OS_ANDROID // incomplete std :-( + if (std::isnan(v) || qt_is_inf(v) || qIsNull(v)) + RETURN_RESULT(Encode(v)); + // Nearest integer not greater in magnitude: + quint64 whole = std::abs(v); + RETURN_RESULT(Encode(copySign(whole, v))); +#else + RETURN_RESULT(Encode(std::trunc(v))); +#endif +} diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h index 0bf5da9404..2658e25438 100644 --- a/src/qml/jsruntime/qv4mathobject_p.h +++ b/src/qml/jsruntime/qv4mathobject_p.h @@ -71,14 +71,27 @@ struct MathObject: Object static ReturnedValue method_abs(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_acos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_acosh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_asin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_asinh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_atan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_atanh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_atan2(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_cbrt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_ceil(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_clz32(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_cos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_cosh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_exp(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_expm1(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_floor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_fround(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_hypot(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_imul(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_log(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_log10(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_log1p(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_log2(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_max(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_min(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_pow(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); @@ -86,8 +99,11 @@ struct MathObject: Object static ReturnedValue method_round(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_sign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_sin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_sinh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_sqrt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_tan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_tanh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_trunc(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; } diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h index ac9671254d..186083b83a 100644 --- a/src/qml/jsruntime/qv4memberdata_p.h +++ b/src/qml/jsruntime/qv4memberdata_p.h @@ -74,18 +74,6 @@ struct MemberData : Managed V4_MANAGED(MemberData, Managed) V4_INTERNALCLASS(MemberData) - struct Index { - Heap::Base *base; - Value *slot; - - void set(EngineBase *e, Value newVal) { - WriteBarrier::write(e, base, slot->data_ptr(), newVal.asReturnedValue()); - } - const Value *operator->() const { return slot; } - const Value &operator*() const { return *slot; } - bool isNull() const { return !slot; } - }; - const Value &operator[] (uint idx) const { return d()->values[idx]; } const Value *data() const { return d()->values.data(); } void set(EngineBase *e, uint index, Value v) { d()->values.set(e, index, v); } diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index f58ff45801..d103f65d47 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -78,13 +78,13 @@ void Heap::NumberCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Number")); } -ReturnedValue NumberCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue NumberCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { double dbl = argc ? argv[0].toNumber() : 0.; return Encode(f->engine()->newNumberObject(dbl)); } -ReturnedValue NumberCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc) +ReturnedValue NumberCtor::virtualCall(const FunctionObject *, const Value *, const Value *argv, int argc) { double dbl = argc ? argv[0].toNumber() : 0.; return Encode(dbl); @@ -95,7 +95,7 @@ void NumberPrototype::init(ExecutionEngine *engine, Object *ctor) Scope scope(engine); ScopedObject o(scope); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(qt_qnan())); ctor->defineReadonlyProperty(QStringLiteral("NEGATIVE_INFINITY"), Primitive::fromDouble(-qInf())); diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index cfdcf9bc1d..576817cf36 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -79,8 +79,8 @@ struct NumberCtor: FunctionObject { V4_OBJECT2(NumberCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct NumberPrototype: NumberObject diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 0c6cde84ad..e4d670d4f3 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -50,6 +50,7 @@ #include "qv4string_p.h" #include "qv4identifiertable_p.h" #include "qv4jscall_p.h" +#include "qv4symbol_p.h" #include <stdint.h> @@ -57,9 +58,9 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(Object); -void Object::setInternalClass(InternalClass *ic) +void Object::setInternalClass(Heap::InternalClass *ic) { - d()->internalClass = ic; + d()->internalClass.set(engine(), ic); if (ic->isUsedAsProto) ic->updateProtoUsage(d()); Q_ASSERT(ic && ic->vtable); @@ -89,20 +90,7 @@ void Object::setProperty(uint index, const Property *p) void Heap::Object::setUsedAsProto() { - internalClass = internalClass->asProtoClass(); -} - -bool Object::setPrototype(Object *proto) -{ - Heap::Object *p = proto ? proto->d() : nullptr; - Heap::Object *pp = p; - while (pp) { - if (pp == d()) - return false; - pp = pp->prototype(); - } - setInternalClass(internalClass()->changePrototype(p)); - return true; + internalClass.set(internalClass->engine, internalClass->asProtoClass()); } ReturnedValue Object::getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs) @@ -121,7 +109,7 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property bool Object::putValue(uint memberIndex, const Value &value) { - QV4::InternalClass *ic = internalClass(); + Heap::InternalClass *ic = internalClass(); if (ic->engine->hasException) return false; @@ -148,37 +136,34 @@ bool Object::putValue(uint memberIndex, const Value &value) return true; } -void Object::defineDefaultProperty(const QString &name, const Value &value) +void Object::defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes) { ExecutionEngine *e = engine(); Scope scope(e); ScopedString s(scope, e->newIdentifier(name)); - defineDefaultProperty(s, value); + defineDefaultProperty(s, value, attributes); } -void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount) +void Object::defineDefaultProperty(const QString &name, VTable::Call code, + int argumentCount, PropertyAttributes attributes) { ExecutionEngine *e = engine(); Scope scope(e); ScopedString s(scope, e->newIdentifier(name)); - ExecutionContext *global = e->rootContext(); - ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(global, s, code)); - function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); - defineDefaultProperty(s, function); + ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(e, s, code, argumentCount)); + defineDefaultProperty(s, function, attributes); } -void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount) +void Object::defineDefaultProperty(StringOrSymbol *nameOrSymbol, VTable::Call code, + int argumentCount, PropertyAttributes attributes) { ExecutionEngine *e = engine(); Scope scope(e); - ExecutionContext *global = e->rootContext(); - ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(global, name, code)); - function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); - defineDefaultProperty(name, function); + ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(e, nameOrSymbol, code, argumentCount)); + defineDefaultProperty(nameOrSymbol, function, attributes); } -void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int), - ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int)) +void Object::defineAccessorProperty(const QString &name, VTable::Call getter, VTable::Call setter) { ExecutionEngine *e = engine(); Scope scope(e); @@ -186,16 +171,27 @@ void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter) defineAccessorProperty(s, getter, setter); } -void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int), - ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int)) +void Object::defineAccessorProperty(StringOrSymbol *name, VTable::Call getter, VTable::Call setter) { ExecutionEngine *v4 = engine(); QV4::Scope scope(v4); ScopedProperty p(scope); - ExecutionContext *global = v4->rootContext(); - p->setGetter(ScopedFunctionObject(scope, (getter ? FunctionObject::createBuiltinFunction(global, name, getter) : nullptr))); - p->setSetter(ScopedFunctionObject(scope, (setter ? FunctionObject::createBuiltinFunction(global, name, setter) : nullptr))); - insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); + QString n = name->toQString(); + if (n.at(0) == QLatin1Char('@')) + n = QChar::fromLatin1('[') + n.midRef(1) + QChar::fromLatin1(']'); + if (getter) { + ScopedString getName(scope, v4->newString(QString::fromLatin1("get ") + n)); + p->setGetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, getName, getter, 0))); + } else { + p->setGetter(nullptr); + } + if (setter) { + ScopedString setName(scope, v4->newString(QString::fromLatin1("set ") + n)); + p->setSetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, setName, setter, 0))); + } else { + p->setSetter(nullptr); + } + insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable); } @@ -221,13 +217,23 @@ void Object::defineReadonlyConfigurableProperty(const QString &name, const Value defineReadonlyConfigurableProperty(s, value); } -void Object::defineReadonlyConfigurableProperty(String *name, const Value &value) +void Object::defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value) { insertMember(name, value, Attr_ReadOnly_ButConfigurable); } +void Object::addSymbolSpecies() +{ + Scope scope(engine()); + ScopedProperty p(scope); + p->setGetter(scope.engine->getSymbolSpecies()); + p->setSetter(nullptr); + insertMember(scope.engine->symbol_species(), p, QV4::Attr_Accessor|QV4::Attr_NotWritable|QV4::Attr_NotEnumerable); +} + void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack) { + Base::markObjects(b, stack); Object *o = static_cast<Object *>(b); if (o->memberData) o->memberData->mark(stack); @@ -242,10 +248,11 @@ void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack) } } -void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes) +void Object::insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes) { uint idx; - InternalClass::addMember(this, s, attributes, &idx); + PropertyKey key = s->toPropertyKey(); + Heap::InternalClass::addMember(this, key, attributes, &idx); if (attributes.isAccessor()) { setProperty(idx + GetterOffset, p->value); @@ -255,230 +262,81 @@ void Object::insertMember(String *s, const Property *p, PropertyAttributes attri } } -// Section 8.12.1 -void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p) +void Object::setPrototypeUnchecked(const Object *p) { - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return getOwnProperty(idx, attrs, p); - - name->makeIdentifier(); - Identifier *id = name->identifier(); - - uint member = internalClass()->find(id); - if (member < UINT_MAX) { - *attrs = internalClass()->propertyData[member]; - if (p) { - p->value = *propertyData(member); - if (attrs->isAccessor()) - p->set = *propertyData(member + SetterOffset); - } - return; - } - - if (attrs) - *attrs = Attr_Invalid; - return; -} - -void Object::getOwnProperty(uint index, PropertyAttributes *attrs, Property *p) -{ - if (arrayData()) { - if (arrayData()->getProperty(index, p, attrs)) - return; - } - if (isStringObject()) { - *attrs = Attr_NotConfigurable|Attr_NotWritable; - if (p) - p->value = static_cast<StringObject *>(this)->getIndex(index); - return; - } - - if (attrs) - *attrs = Attr_Invalid; - return; + setInternalClass(internalClass()->changePrototype(p ? p->d() : nullptr)); } // Section 8.12.2 -MemberData::Index Object::getValueOrSetter(String *name, PropertyAttributes *attrs) -{ - Q_ASSERT(name->asArrayIndex() == UINT_MAX); - - name->makeIdentifier(); - Identifier *id = name->identifier(); - - Heap::Object *o = d(); - while (o) { - uint idx = o->internalClass->find(id); - if (idx < UINT_MAX) { - *attrs = o->internalClass->propertyData[idx]; - return o->writablePropertyData(attrs->isAccessor() ? idx + SetterOffset : idx ); - } - - o = o->prototype(); - } - *attrs = Attr_Invalid; - return { nullptr, nullptr }; -} - -ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs) +PropertyIndex Object::getValueOrSetter(PropertyKey id, PropertyAttributes *attrs) { - Heap::Object *o = d(); - while (o) { - if (o->arrayData) { - uint idx = o->arrayData->mappedIndex(index); - if (idx != UINT_MAX) { - *attrs = o->arrayData->attributes(index); - return { o->arrayData , attrs->isAccessor() ? idx + SetterOffset : idx }; + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + Heap::Object *o = d(); + while (o) { + if (o->arrayData) { + uint idx = o->arrayData->mappedIndex(index); + if (idx != UINT_MAX) { + *attrs = o->arrayData->attributes(index); + return { o->arrayData , o->arrayData->values.values + (attrs->isAccessor() ? idx + SetterOffset : idx) }; + } } + if (o->vtable()->type == Type_StringObject) { + if (index < static_cast<const Heap::StringObject *>(o)->length()) { + // this is an evil hack, but it works, as the method is only ever called from put, + // where we don't use the returned pointer there for non writable attributes + *attrs = (Attr_NotWritable|Attr_NotConfigurable); + return { reinterpret_cast<Heap::ArrayData *>(0x1), nullptr }; + } + } + o = o->prototype(); } - if (o->vtable()->type == Type_StringObject) { - if (index < static_cast<const Heap::StringObject *>(o)->length()) { - // this is an evil hack, but it works, as the method is only ever called from putIndexed, - // where we don't use the returned pointer there for non writable attributes - *attrs = (Attr_NotWritable|Attr_NotConfigurable); - return { reinterpret_cast<Heap::ArrayData *>(0x1), 0 }; + } else { + Heap::Object *o = d(); + while (o) { + uint idx = o->internalClass->find(id); + if (idx < UINT_MAX) { + *attrs = o->internalClass->propertyData[idx]; + return o->writablePropertyData(attrs->isAccessor() ? idx + SetterOffset : idx ); } + + o = o->prototype(); } - o = o->prototype(); } *attrs = Attr_Invalid; - return { nullptr, 0 }; -} - -bool Object::hasProperty(String *name) const -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return hasProperty(idx); - - Scope scope(engine()); - ScopedObject o(scope, d()); - while (o) { - if (o->hasOwnProperty(name)) - return true; - - o = o->prototype(); - } - - return false; -} - -bool Object::hasProperty(uint index) const -{ - Scope scope(engine()); - ScopedObject o(scope, d()); - while (o) { - if (o->hasOwnProperty(index)) - return true; - - o = o->prototype(); - } - - return false; -} - -bool Object::hasOwnProperty(String *name) const -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return hasOwnProperty(idx); - - name->makeIdentifier(); - Identifier *id = name->identifier(); - - if (internalClass()->find(id) < UINT_MAX) - return true; - if (!query(name).isEmpty()) - return true; - return false; -} - -bool Object::hasOwnProperty(uint index) const -{ - if (arrayData() && !arrayData()->isEmpty(index)) - return true; - - if (isStringObject()) { - if (index < static_cast<const StringObject *>(this)->length()) - return true; - } - if (!queryIndexed(index).isEmpty()) - return true; - return false; + return { nullptr, nullptr }; } -ReturnedValue Object::callAsConstructor(const FunctionObject *f, const Value *, int) +ReturnedValue Object::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) { return f->engine()->throwTypeError(); } -ReturnedValue Object::call(const FunctionObject *f, const Value *, const Value *, int) +ReturnedValue Object::virtualCall(const FunctionObject *f, const Value *, const Value *, int) { return f->engine()->throwTypeError(); } -ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue Object::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { - return static_cast<const Object *>(m)->internalGet(name, hasProperty); + if (id.isArrayIndex()) + return static_cast<const Object *>(m)->internalGetIndexed(id.asArrayIndex(), receiver, hasProperty); + Scope scope(m); + Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol()); + return static_cast<const Object *>(m)->internalGet(name, receiver, hasProperty); } -ReturnedValue Object::getIndexed(const Managed *m, uint index, bool *hasProperty) +bool Object::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { - return static_cast<const Object *>(m)->internalGetIndexed(index, hasProperty); + return static_cast<Object *>(m)->internalPut(id, value, receiver); } -bool Object::put(Managed *m, String *name, const Value &value) +bool Object::virtualDeleteProperty(Managed *m, PropertyKey id) { - return static_cast<Object *>(m)->internalPut(name, value); + return static_cast<Object *>(m)->internalDeleteProperty(id); } -bool Object::putIndexed(Managed *m, uint index, const Value &value) -{ - return static_cast<Object *>(m)->internalPutIndexed(index, value); -} - -PropertyAttributes Object::query(const Managed *m, String *name) -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return queryIndexed(m, idx); - - name->makeIdentifier(); - Identifier *id = name->identifier(); - - const Object *o = static_cast<const Object *>(m); - idx = o->internalClass()->find(id); - if (idx < UINT_MAX) - return o->internalClass()->propertyData[idx]; - - return Attr_Invalid; -} - -PropertyAttributes Object::queryIndexed(const Managed *m, uint index) -{ - const Object *o = static_cast<const Object *>(m); - if (o->arrayData() && !o->arrayData()->isEmpty(index)) - return o->arrayData()->attributes(index); - - if (o->isStringObject()) { - if (index < static_cast<const StringObject *>(o)->length()) - return (Attr_NotWritable|Attr_NotConfigurable); - } - return Attr_Invalid; -} - -bool Object::deleteProperty(Managed *m, String *name) -{ - return static_cast<Object *>(m)->internalDeleteProperty(name); -} - -bool Object::deleteIndexedProperty(Managed *m, uint index) -{ - return static_cast<Object *>(m)->internalDeleteIndexedProperty(index); -} - -void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *pd, PropertyAttributes *attrs) +void Object::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *pd, PropertyAttributes *attrs) { Object *o = static_cast<Object *>(m); name->setM(nullptr); @@ -525,9 +383,10 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint * } while (it->memberIndex < o->internalClass()->size) { - Identifier *n = o->internalClass()->nameMap.at(it->memberIndex); - if (!n) { + PropertyKey n = o->internalClass()->nameMap.at(it->memberIndex); + if (!n.isStringOrSymbol() || !n.asStringOrSymbol()->internalClass->vtable->isString) { // accessor properties have a dummy entry with n == 0 + // symbol entries are supposed to be skipped ++it->memberIndex; continue; } @@ -536,7 +395,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint * PropertyAttributes a = o->internalClass()->propertyData[it->memberIndex]; ++it->memberIndex; if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { - name->setM(o->engine()->identifierTable->stringFromIdentifier(n)); + name->setM(n.asStringOrSymbol()); *attrs = a; pd->value = *o->propertyData(idx); if (a.isAccessor()) @@ -549,14 +408,11 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint * } // Section 8.12.3 -ReturnedValue Object::internalGet(String *name, bool *hasProperty) const +ReturnedValue Object::internalGet(StringOrSymbol *name, const Value *receiver, bool *hasProperty) const { - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return getIndexed(idx, hasProperty); + PropertyKey id = name->toPropertyKey(); - name->makeIdentifier(); - Identifier *id = name->identifier(); + Q_ASSERT(!id.isArrayIndex()); Heap::Object *o = d(); while (o) { @@ -564,7 +420,7 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const if (idx < UINT_MAX) { if (hasProperty) *hasProperty = true; - return getValue(*o->propertyData(idx), o->internalClass->propertyData.at(idx)); + return Object::getValue(*receiver, *o->propertyData(idx), o->internalClass->propertyData.at(idx)); } o = o->prototype(); @@ -575,7 +431,7 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const return Encode::undefined(); } -ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const +ReturnedValue Object::internalGetIndexed(uint index, const Value *receiver, bool *hasProperty) const { PropertyAttributes attrs; Scope scope(engine()); @@ -596,13 +452,13 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const return str.asReturnedValue(); } } - o = o->prototype(); + o = o->getPrototypeOf(); } if (exists) { if (hasProperty) *hasProperty = true; - return getValue(pd->value, attrs); + return Object::getValue(*receiver, pd->value, attrs); } if (hasProperty) @@ -612,36 +468,44 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const // Section 8.12.5 -bool Object::internalPut(String *name, const Value &value) +bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver) { ExecutionEngine *engine = this->engine(); if (engine->hasException) return false; - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return putIndexed(idx, value); - - name->makeIdentifier(); - Identifier *id = name->identifier(); + uint index = id.asArrayIndex(); + Scope scope(engine); - MemberData::Index memberIndex{nullptr, nullptr}; - uint member = internalClass()->find(id); PropertyAttributes attrs; - if (member < UINT_MAX) { - attrs = internalClass()->propertyData[member]; - memberIndex = d()->writablePropertyData(attrs.isAccessor() ? member + SetterOffset : member); + PropertyIndex propertyIndex{nullptr, nullptr}; + + if (index != UINT_MAX) { + if (arrayData()) + propertyIndex = arrayData()->getValueOrSetter(index, &attrs); + + if (propertyIndex.isNull() && isStringObject()) { + if (index < static_cast<StringObject *>(this)->length()) + // not writable + return false; + } + } else { + uint member = internalClass()->find(id); + if (member < UINT_MAX) { + attrs = internalClass()->propertyData[member]; + propertyIndex = d()->writablePropertyData(attrs.isAccessor() ? member + SetterOffset : member); + } } // clause 1 - if (!memberIndex.isNull()) { + if (!propertyIndex.isNull()) { if (attrs.isAccessor()) { - if (memberIndex->as<FunctionObject>()) + if (propertyIndex->as<FunctionObject>()) goto cont; return false; } else if (!attrs.isWritable()) return false; - else if (isArrayObject() && name->equals(engine->id_length())) { + else if (isArrayObject() && id == engine->id_length()->propertyKey()) { bool ok; uint l = value.asArrayLength(&ok); if (!ok) { @@ -652,19 +516,18 @@ bool Object::internalPut(String *name, const Value &value) if (!ok) return false; } else { - memberIndex.set(engine, value); + propertyIndex.set(engine, value); } return true; - } else if (!prototype()) { + } else if (!getPrototypeOf()) { if (!isExtensible()) return false; } else { // clause 4 - Scope scope(engine); - memberIndex = ScopedObject(scope, prototype())->getValueOrSetter(name, &attrs); - if (!memberIndex.isNull()) { + propertyIndex = ScopedObject(scope, getPrototypeOf())->getValueOrSetter(id, &attrs); + if (!propertyIndex.isNull()) { if (attrs.isAccessor()) { - if (!memberIndex->as<FunctionObject>()) + if (!propertyIndex->as<FunctionObject>()) return false; } else if (!isExtensible() || !attrs.isWritable()) { return false; @@ -677,228 +540,59 @@ bool Object::internalPut(String *name, const Value &value) cont: // Clause 5 - if (!memberIndex.isNull() && attrs.isAccessor()) { - Q_ASSERT(memberIndex->as<FunctionObject>()); + if (!propertyIndex.isNull() && attrs.isAccessor()) { + Q_ASSERT(propertyIndex->as<FunctionObject>()); Scope scope(engine); - ScopedFunctionObject setter(scope, *memberIndex); + ScopedFunctionObject setter(scope, *propertyIndex); JSCallData jsCallData(scope, 1); jsCallData->args[0] = value; - *jsCallData->thisObject = this; + *jsCallData->thisObject = *receiver; setter->call(jsCallData); return !engine->hasException; } - insertMember(name, value); - return true; -} - -bool Object::internalPutIndexed(uint index, const Value &value) -{ - ExecutionEngine *engine = this->engine(); - if (engine->hasException) - return false; - - PropertyAttributes attrs; - - ArrayData::Index arrayIndex = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : ArrayData::Index{ nullptr, 0 }; - - if (arrayIndex.isNull() && isStringObject()) { - if (index < static_cast<StringObject *>(this)->length()) - // not writable - return false; - } - - // clause 1 - if (!arrayIndex.isNull()) { - if (attrs.isAccessor()) { - if (arrayIndex->as<FunctionObject>()) - goto cont; - return false; - } else if (!attrs.isWritable()) - return false; - - arrayIndex.set(engine, value); - return true; - } else if (!prototype()) { - if (!isExtensible()) - return false; + if (index != UINT_MAX) { + arraySet(index, value); } else { - // clause 4 - Scope scope(engine); - arrayIndex = ScopedObject(scope, prototype())->getValueOrSetter(index, &attrs); - if (!arrayIndex.isNull()) { - if (attrs.isAccessor()) { - if (!arrayIndex->as<FunctionObject>()) - return false; - } else if (!isExtensible() || !attrs.isWritable()) { - return false; - } - } else if (!isExtensible()) { - return false; - } - } - - cont: - - // Clause 5 - if (!arrayIndex.isNull() && attrs.isAccessor()) { - Q_ASSERT(arrayIndex->as<FunctionObject>()); - - Scope scope(engine); - ScopedFunctionObject setter(scope, *arrayIndex); - JSCallData jsCallData(scope, 1); - jsCallData->args[0] = value; - *jsCallData->thisObject = this; - setter->call(jsCallData); - return !engine->hasException; + Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol()); + insertMember(name, value); } - - arraySet(index, value); return true; } // Section 8.12.7 -bool Object::internalDeleteProperty(String *name) +bool Object::internalDeleteProperty(PropertyKey id) { if (internalClass()->engine->hasException) return false; - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return deleteIndexedProperty(idx); - - name->makeIdentifier(); + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + Scope scope(engine()); + if (scope.engine->hasException) + return false; - uint memberIdx = internalClass()->find(name->identifier()); - if (memberIdx != UINT_MAX) { - if (internalClass()->propertyData[memberIdx].isConfigurable()) { - InternalClass::removeMember(this, name->identifier()); + Scoped<ArrayData> ad(scope, arrayData()); + if (!ad || ad->vtable()->del(this, index)) return true; - } - return false; - } - return true; -} - -bool Object::internalDeleteIndexedProperty(uint index) -{ - Scope scope(engine()); - if (scope.engine->hasException) return false; + } - Scoped<ArrayData> ad(scope, arrayData()); - if (!ad || ad->vtable()->del(this, index)) - return true; - - return false; -} - -// Section 8.12.9 -bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const Property *p, PropertyAttributes attrs) -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return __defineOwnProperty__(engine, idx, p, attrs); - - Scope scope(engine); - name->makeIdentifier(); - - uint memberIndex; - - if (isArrayObject() && name->equals(engine->id_length())) { - Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == internalClass()->find(engine->id_length())); - ScopedProperty lp(scope); - PropertyAttributes cattrs; - getProperty(Heap::ArrayObject::LengthPropertyIndex, lp, &cattrs); - if (attrs.isEmpty() || p->isSubset(attrs, lp, cattrs)) + uint memberIdx = internalClass()->find(id); + if (memberIdx != UINT_MAX) { + if (internalClass()->propertyData[memberIdx].isConfigurable()) { + Heap::InternalClass::removeMember(this, id); return true; - if (!cattrs.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) - return false; - bool succeeded = true; - if (attrs.type() == PropertyAttributes::Data) { - bool ok; - uint l = p->value.asArrayLength(&ok); - if (!ok) { - ScopedValue v(scope, p->value); - engine->throwRangeError(v); - return false; - } - succeeded = setArrayLength(l); } - if (attrs.hasWritable() && !attrs.isWritable()) { - cattrs.setWritable(false); - InternalClass::changeMember(this, engine->id_length(), cattrs); - } - if (!succeeded) - return false; - return true; - } - - // Clause 1 - memberIndex = internalClass()->find(name->identifier()); - - if (memberIndex == UINT_MAX) { - // clause 3 - if (!isExtensible()) - return false; - // clause 4 - ScopedProperty pd(scope); - pd->copy(p, attrs); - pd->fullyPopulated(&attrs); - insertMember(name, pd, attrs); - return true; - } - - return __defineOwnProperty__(engine, memberIndex, name, p, attrs); -} - -bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs) -{ - // 15.4.5.1, 4b - if (isArrayObject() && index >= getLength() && !internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable()) return false; - - if (ArgumentsObject::isNonStrictArgumentsObject(this)) - return static_cast<ArgumentsObject *>(this)->defineOwnProperty(engine, index, p, attrs); - - return defineOwnProperty2(engine, index, p, attrs); -} - -bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs) -{ - bool hasProperty = 0; - - // Clause 1 - if (arrayData()) { - hasProperty = arrayData()->mappedIndex(index) != UINT_MAX; - if (!hasProperty && isStringObject()) - hasProperty = (index < static_cast<StringObject *>(this)->length()); } - if (!hasProperty) { - // clause 3 - if (!isExtensible()) - return false; - // clause 4 - Scope scope(engine); - ScopedProperty pp(scope); - pp->copy(p, attrs); - pp->fullyPopulated(&attrs); - if (attrs == Attr_Data) { - ScopedValue v(scope, pp->value); - arraySet(index, v); - } else { - arraySet(index, pp, attrs); - } - return true; - } - - return __defineOwnProperty__(engine, index, nullptr, p, attrs); + return true; } -bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs) +bool Object::internalDefineOwnProperty(ExecutionEngine *engine, uint index, StringOrSymbol *member, const Property *p, PropertyAttributes attrs) { // clause 5 if (attrs.isEmpty()) @@ -977,7 +671,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * current->merge(cattrs, p, attrs); if (member) { - InternalClass::changeMember(this, member, cattrs); + Heap::InternalClass::changeMember(this, member->propertyKey(), cattrs); setProperty(index, current); } else { setArrayAttributes(index, cattrs); @@ -986,15 +680,6 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * return true; } - -bool Object::__defineOwnProperty__(ExecutionEngine *engine, const QString &name, const Property *p, PropertyAttributes attrs) -{ - Scope scope(engine); - ScopedString s(scope, engine->newString(name)); - return __defineOwnProperty__(engine, s, p, attrs); -} - - void Object::copyArrayData(Object *other) { Q_ASSERT(isArrayObject()); @@ -1007,7 +692,7 @@ void Object::copyArrayData(Object *other) ScopedValue v(scope); for (uint i = 0; i < len; ++i) { - arraySet(i, (v = other->getIndexed(i))); + arraySet(i, (v = other->get(i))); } } else if (!other->arrayData()) { ; @@ -1030,15 +715,15 @@ void Object::copyArrayData(Object *other) setArrayLengthUnchecked(other->getLength()); } -uint Object::getLength(const Managed *m) +qint64 Object::virtualGetLength(const Managed *m) { Scope scope(static_cast<const Object *>(m)->engine()); ScopedValue v(scope, static_cast<Object *>(const_cast<Managed *>(m))->get(scope.engine->id_length())); - return v->toUInt32(); + return v->toLength(); } // 'var' is 'V' in 15.3.5.3. -ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var) +ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &var) { QV4::ExecutionEngine *engine = typeObject->internalClass()->engine; @@ -1080,6 +765,141 @@ ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var) return Encode(false); } +bool Object::virtualHasProperty(const Managed *m, PropertyKey id) +{ + Scope scope(m->engine()); + ScopedObject o(scope, m); + ScopedProperty p(scope); + while (o) { + if (o->getOwnProperty(id, p) != Attr_Invalid) + return true; + + o = o->getPrototypeOf(); + } + + return false; +} + +PropertyAttributes Object::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p) +{ + PropertyAttributes attrs; + Object *o = static_cast<Object *>(m); + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + if (o->arrayData()) { + if (o->arrayData()->getProperty(index, p, &attrs)) + return attrs; + } + } else { + Q_ASSERT(id.asStringOrSymbol()); + + uint member = o->internalClass()->find(id); + if (member < UINT_MAX) { + attrs = o->internalClass()->propertyData[member]; + if (p) { + p->value = *o->propertyData(member); + if (attrs.isAccessor()) + p->set = *o->propertyData(member + SetterOffset); + } + return attrs; + } + } + + return Attr_Invalid; +} + +bool Object::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs) +{ + Object *o = static_cast<Object *>(m); + Scope scope(o); + + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + + bool hasProperty = false; + + if (o->arrayData()) { + hasProperty = o->arrayData()->mappedIndex(index) != UINT_MAX; + if (!hasProperty && o->isStringObject()) + hasProperty = (index < static_cast<StringObject *>(o)->length()); + } + + if (!hasProperty) { + if (!o->isExtensible()) + return false; + + ScopedProperty pp(scope); + pp->copy(p, attrs); + pp->fullyPopulated(&attrs); + if (attrs == Attr_Data) { + ScopedValue v(scope, pp->value); + o->arraySet(index, v); + } else { + o->arraySet(index, pp, attrs); + } + return true; + } + + return o->internalDefineOwnProperty(scope.engine, index, nullptr, p, attrs); + } + + uint memberIndex = o->internalClass()->find(id); + Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol()); + + if (memberIndex == UINT_MAX) { + if (!o->isExtensible()) + return false; + + ScopedProperty pd(scope); + pd->copy(p, attrs); + pd->fullyPopulated(&attrs); + o->insertMember(name, pd, attrs); + return true; + } + + return o->internalDefineOwnProperty(scope.engine, memberIndex, name, p, attrs); +} + +bool Object::virtualIsExtensible(const Managed *m) +{ + return m->d()->internalClass->extensible; +} + +bool Object::virtualPreventExtensions(Managed *m) +{ + Q_ASSERT(m->isObject()); + Object *o = static_cast<Object *>(m); + o->setInternalClass(o->internalClass()->nonExtensible()); + return true; +} + +Heap::Object *Object::virtualGetPrototypeOf(const Managed *m) +{ + return m->internalClass()->prototype; +} + +bool Object::virtualSetPrototypeOf(Managed *m, const Object *proto) +{ + Q_ASSERT(m->isObject()); + Object *o = static_cast<Object *>(m); + Heap::Object *current = o->internalClass()->prototype; + Heap::Object *protod = proto ? proto->d() : nullptr; + if (current == protod) + return true; + if (!o->internalClass()->extensible) + return false; + Heap::Object *p = protod; + while (p) { + if (p == o->d()) + return false; + if (p->vtable()->getPrototypeOf != Object::staticVTable()->getPrototypeOf) + break; + p = p->prototype(); + } + o->setInternalClass(o->internalClass()->changePrototype(protod)); + return true; +} + bool Object::setArrayLength(uint newLen) { Q_ASSERT(isArrayObject()); @@ -1132,12 +952,10 @@ void Heap::ArrayObject::init(const QStringList &list) a->setArrayLengthUnchecked(len); } -uint ArrayObject::getLength(const Managed *m) +qint64 ArrayObject::virtualGetLength(const Managed *m) { const ArrayObject *a = static_cast<const ArrayObject *>(m); - if (a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->isInteger()) - return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->integerValue(); - return Primitive::toUInt32(a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->doubleValue()); + return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->toLength(); } QStringList ArrayObject::toQStringList() const @@ -1150,8 +968,62 @@ QStringList ArrayObject::toQStringList() const uint length = getLength(); for (uint i = 0; i < length; ++i) { - v = const_cast<ArrayObject *>(this)->getIndexed(i); + v = const_cast<ArrayObject *>(this)->get(i); result.append(v->toQStringNoThrow()); } return result; } + +bool ArrayObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs) +{ + Q_ASSERT(m->isArrayObject()); + ArrayObject *a = static_cast<ArrayObject *>(m); + + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + uint len = a->getLength(); + if (index >= len && !a->internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable()) + return false; + + bool succeeded = Object::virtualDefineOwnProperty(m, id, p, attrs); + if (!succeeded) + return false; + + if (index >= len) + a->setArrayLengthUnchecked(index + 1); + + return true; + } + + ExecutionEngine *engine = m->engine(); + if (id == engine->id_length()->propertyKey()) { + Scope scope(engine); + Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == a->internalClass()->find(engine->id_length()->propertyKey())); + ScopedProperty lp(scope); + PropertyAttributes cattrs; + a->getProperty(Heap::ArrayObject::LengthPropertyIndex, lp, &cattrs); + if (attrs.isEmpty() || p->isSubset(attrs, lp, cattrs)) + return true; + if (!cattrs.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) + return false; + bool succeeded = true; + if (attrs.type() == PropertyAttributes::Data) { + bool ok; + uint l = p->value.asArrayLength(&ok); + if (!ok) { + ScopedValue v(scope, p->value); + engine->throwRangeError(v); + return false; + } + succeeded = a->setArrayLength(l); + } + if (attrs.hasWritable() && !attrs.isWritable()) { + cattrs.setWritable(false); + Heap::InternalClass::changeMember(a, engine->id_length()->propertyKey(), cattrs); + } + if (!succeeded) + return false; + return true; + } + return Object::virtualDefineOwnProperty(m, id, p, attrs); +} diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 1731ae3c76..dea080f98a 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -74,6 +74,10 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) { static void markObjects(Heap::Base *base, MarkStack *stack); void init() { Base::init(); } + const VTable *vtable() const { + return internalClass->vtable; + } + const Value *inlinePropertyDataWithOffset(uint indexWithOffset) const { Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < vtable()->inlinePropertyOffset + vtable()->nInlineProperties); return reinterpret_cast<const Value *>(this) + indexWithOffset; @@ -90,15 +94,15 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) { void setInlineProperty(ExecutionEngine *e, uint index, Heap::Base *b) { Q_ASSERT(index < vtable()->nInlineProperties); Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index; - WriteBarrier::write(e, this, prop->data_ptr(), b->asReturnedValue()); + WriteBarrier::write(e, this, prop->data_ptr(), Value::fromHeapObject(b).asReturnedValue()); } - QV4::MemberData::Index writablePropertyData(uint index) { + PropertyIndex writablePropertyData(uint index) { uint nInline = vtable()->nInlineProperties; if (index < nInline) - return { this, reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index}; + return PropertyIndex{ this, reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index}; index -= nInline; - return { memberData, memberData->values.values + index }; + return PropertyIndex{ memberData, memberData->values.values + index }; } const Value *propertyData(uint index) const { @@ -134,76 +138,6 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) { } -#define V4_OBJECT2(DataClass, superClass) \ - private: \ - DataClass() Q_DECL_EQ_DELETE; \ - Q_DISABLE_COPY(DataClass) \ - public: \ - Q_MANAGED_CHECK \ - typedef QV4::Heap::DataClass Data; \ - typedef superClass SuperClass; \ - static const QV4::ObjectVTable static_vtbl; \ - static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \ - V4_MANAGED_SIZE_TEST \ - QV4::Heap::DataClass *d_unchecked() const { return static_cast<QV4::Heap::DataClass *>(m()); } \ - QV4::Heap::DataClass *d() const { \ - QV4::Heap::DataClass *dptr = d_unchecked(); \ - dptr->_checkIsInitialized(); \ - return dptr; \ - } \ - Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value); - -#define V4_PROTOTYPE(p) \ - static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \ - { return e->p(); } - -struct ObjectVTable -{ - VTable vTable; - ReturnedValue (*call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - ReturnedValue (*callAsConstructor)(const FunctionObject *, const Value *argv, int argc); - ReturnedValue (*get)(const Managed *, String *name, bool *hasProperty); - ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty); - bool (*put)(Managed *, String *name, const Value &value); - bool (*putIndexed)(Managed *, uint index, const Value &value); - PropertyAttributes (*query)(const Managed *, String *name); - PropertyAttributes (*queryIndexed)(const Managed *, uint index); - bool (*deleteProperty)(Managed *m, String *name); - bool (*deleteIndexedProperty)(Managed *m, uint index); - uint (*getLength)(const Managed *m); - void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); - ReturnedValue (*instanceOf)(const Object *typeObject, const Value &var); -}; - -#define DEFINE_OBJECT_VTABLE_BASE(classname) \ -const QV4::ObjectVTable classname::static_vtbl = \ -{ \ - DEFINE_MANAGED_VTABLE_INT(classname, (std::is_same<classname::SuperClass, Object>::value) ? nullptr : &classname::SuperClass::static_vtbl.vTable), \ - call, \ - callAsConstructor, \ - get, \ - getIndexed, \ - put, \ - putIndexed, \ - query, \ - queryIndexed, \ - deleteProperty, \ - deleteIndexedProperty, \ - getLength, \ - advanceIterator, \ - instanceOf \ -} - -#define DEFINE_OBJECT_VTABLE(classname) \ -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \ -DEFINE_OBJECT_VTABLE_BASE(classname) \ -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF - -#define DEFINE_OBJECT_TEMPLATE_VTABLE(classname) \ -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \ -template<> DEFINE_OBJECT_VTABLE_BASE(classname) \ -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF - struct Q_QML_EXPORT Object: Managed { V4_OBJECT2(Object, Object) Q_MANAGED_TYPE(Object) @@ -218,7 +152,7 @@ struct Q_QML_EXPORT Object: Managed { SetterOffset = 1 }; - void setInternalClass(InternalClass *ic); + void setInternalClass(Heap::InternalClass *ic); const Value *propertyData(uint index) const { return d()->propertyData(index); } @@ -232,27 +166,21 @@ struct Q_QML_EXPORT Object: Managed { void setProperty(ExecutionEngine *engine, uint index, Value v) const { d()->setProperty(engine, index, v); } void setProperty(ExecutionEngine *engine, uint index, Heap::Base *b) const { d()->setProperty(engine, index, b); } - const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable()); } - Heap::Object *prototype() const { return d()->prototype(); } - bool setPrototype(Object *proto); + const VTable *vtable() const { return d()->vtable(); } - void getOwnProperty(String *name, PropertyAttributes *attrs, Property *p = nullptr); - void getOwnProperty(uint index, PropertyAttributes *attrs, Property *p = nullptr); - - MemberData::Index getValueOrSetter(String *name, PropertyAttributes *attrs); - ArrayData::Index getValueOrSetter(uint index, PropertyAttributes *attrs); + PropertyAttributes getOwnProperty(PropertyKey id, Property *p = nullptr) { + return vtable()->getOwnProperty(this, id, p); + } - bool hasProperty(String *name) const; - bool hasProperty(uint index) const; + PropertyIndex getValueOrSetter(PropertyKey id, PropertyAttributes *attrs); - bool hasOwnProperty(String *name) const; - bool hasOwnProperty(uint index) const; + bool hasProperty(PropertyKey id) const { + return vtable()->hasProperty(this, id); + } - bool __defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs); - bool __defineOwnProperty__(ExecutionEngine *engine, String *name, const Property *p, PropertyAttributes attrs); - bool __defineOwnProperty__(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs); - bool __defineOwnProperty__(ExecutionEngine *engine, const QString &name, const Property *p, PropertyAttributes attrs); - bool defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs); + bool defineOwnProperty(PropertyKey id, const Property *p, PropertyAttributes attrs) { + return vtable()->defineOwnProperty(this, id, p, attrs); + } // // helpers @@ -267,33 +195,39 @@ struct Q_QML_EXPORT Object: Managed { bool putValue(uint memberIndex, const Value &value); /* The spec default: Writable: true, Enumerable: false, Configurable: true */ - void defineDefaultProperty(String *name, const Value &value) { - insertMember(name, value, Attr_Data|Attr_NotEnumerable); + void defineDefaultProperty(StringOrSymbol *name, const Value &value, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable) { + insertMember(name, value, attributes); } - void defineDefaultProperty(const QString &name, const Value &value); - void defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0); - void defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0); - void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int), - ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int)); - void defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int), - ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int)); + void defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable); + void defineDefaultProperty(const QString &name, VTable::Call code, + int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable); + void defineDefaultProperty(StringOrSymbol *name, VTable::Call code, + int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable); + void defineAccessorProperty(const QString &name, VTable::Call getter, VTable::Call setter); + void defineAccessorProperty(StringOrSymbol *name, VTable::Call getter, VTable::Call setter); /* Fixed: Writable: false, Enumerable: false, Configurable: false */ void defineReadonlyProperty(const QString &name, const Value &value); void defineReadonlyProperty(String *name, const Value &value); /* Fixed: Writable: false, Enumerable: false, Configurable: true */ void defineReadonlyConfigurableProperty(const QString &name, const Value &value); - void defineReadonlyConfigurableProperty(String *name, const Value &value); + void defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value); + + void addSymbolSpecies(); - void insertMember(String *s, const Value &v, PropertyAttributes attributes = Attr_Data) { + void insertMember(StringOrSymbol *s, const Value &v, PropertyAttributes attributes = Attr_Data) { Scope scope(engine()); ScopedProperty p(scope); p->value = v; insertMember(s, p, attributes); } - void insertMember(String *s, const Property *p, PropertyAttributes attributes); + void insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes); - bool isExtensible() const { return d()->internalClass->extensible; } + bool isExtensible() const { return vtable()->isExtensible(this); } + bool preventExtensions() { return vtable()->preventExtensions(this); } + Heap::Object *getPrototypeOf() const { return vtable()->getPrototypeOf(this); } + bool setPrototypeOf(const Object *p) { return vtable()->setPrototypeOf(this, p); } + void setPrototypeUnchecked(const Object *p); // Array handling @@ -353,33 +287,58 @@ public: Scope scope(engine()); ScopedObject p(scope, this); - while ((p = p->prototype())) + while ((p = p->getPrototypeOf())) if (p->arrayData()) return true; return false; } - inline ReturnedValue get(String *name, bool *hasProperty = nullptr) const - { return vtable()->get(this, name, hasProperty); } - inline ReturnedValue getIndexed(uint idx, bool *hasProperty = nullptr) const - { return vtable()->getIndexed(this, idx, hasProperty); } + inline ReturnedValue get(StringOrSymbol *name, bool *hasProperty = nullptr, const Value *receiver = nullptr) const + { if (!receiver) receiver = this; return vtable()->get(this, name->toPropertyKey(), receiver, hasProperty); } + inline ReturnedValue get(uint idx, bool *hasProperty = nullptr, const Value *receiver = nullptr) const + { if (!receiver) receiver = this; return vtable()->get(this, PropertyKey::fromArrayIndex(idx), receiver, hasProperty); } + QT_DEPRECATED inline ReturnedValue getIndexed(uint idx, bool *hasProperty = nullptr) const + { return get(idx, hasProperty); } + inline ReturnedValue get(PropertyKey id, const Value *receiver = nullptr, bool *hasProperty = nullptr) const + { if (!receiver) receiver = this; return vtable()->get(this, id, receiver, hasProperty); } // use the set variants instead, to customize throw behavior - inline bool put(String *name, const Value &v) - { return vtable()->put(this, name, v); } - inline bool putIndexed(uint idx, const Value &v) - { return vtable()->putIndexed(this, idx, v); } + inline bool put(StringOrSymbol *name, const Value &v, Value *receiver = nullptr) + { if (!receiver) receiver = this; return vtable()->put(this, name->toPropertyKey(), v, receiver); } + inline bool put(uint idx, const Value &v, Value *receiver = nullptr) + { if (!receiver) receiver = this; return vtable()->put(this, PropertyKey::fromArrayIndex(idx), v, receiver); } + QT_DEPRECATED inline bool putIndexed(uint idx, const Value &v) + { return put(idx, v); } + inline bool put(PropertyKey id, const Value &v, Value *receiver = nullptr) + { if (!receiver) receiver = this; return vtable()->put(this, id, v, receiver); } enum ThrowOnFailure { DoThrowOnRejection, DoNotThrow }; + // This is the same as set(), but it doesn't require creating a string key, + // which is much more efficient for the array case. + inline bool setIndexed(uint idx, const Value &v, ThrowOnFailure shouldThrow) + { + bool ret = vtable()->put(this, PropertyKey::fromArrayIndex(idx), v, this); + // ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception. + if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) { + ExecutionEngine *e = engine(); + if (!e->hasException) { // allow a custom set impl to throw itself + QString message = QLatin1String("Cannot assign to read-only property \"") + + QString::number(idx) + QLatin1Char('\"'); + e->throwTypeError(message); + } + } + return ret; + } + // ES6: 7.3.3 Set (O, P, V, Throw) - inline bool set(String *name, const Value &v, ThrowOnFailure shouldThrow) + inline bool set(StringOrSymbol *name, const Value &v, ThrowOnFailure shouldThrow) { - bool ret = vtable()->put(this, name, v); + bool ret = vtable()->put(this, name->toPropertyKey(), v, this); // ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception. if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) { ExecutionEngine *e = engine(); @@ -392,42 +351,37 @@ public: return ret; } - PropertyAttributes query(String *name) const - { return vtable()->query(this, name); } - PropertyAttributes queryIndexed(uint index) const - { return vtable()->queryIndexed(this, index); } - bool deleteProperty(String *name) - { return vtable()->deleteProperty(this, name); } - bool deleteIndexedProperty(uint index) - { return vtable()->deleteIndexedProperty(this, index); } + bool deleteProperty(PropertyKey id) + { return vtable()->deleteProperty(this, id); } void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { vtable()->advanceIterator(this, it, name, index, p, attributes); } - uint getLength() const { return vtable()->getLength(this); } + qint64 getLength() const { return vtable()->getLength(this); } ReturnedValue instanceOf(const Value &var) const { return vtable()->instanceOf(this, var); } protected: - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static bool put(Managed *m, String *name, const Value &value); - static bool putIndexed(Managed *m, uint index, const Value &value); - static PropertyAttributes query(const Managed *m, String *name); - static PropertyAttributes queryIndexed(const Managed *m, uint index); - static bool deleteProperty(Managed *m, String *name); - static bool deleteIndexedProperty(Managed *m, uint index); - static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); - static uint getLength(const Managed *m); - static ReturnedValue instanceOf(const Object *typeObject, const Value &var); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver,bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); + static bool virtualDeleteProperty(Managed *m, PropertyKey id); + static bool virtualHasProperty(const Managed *m, PropertyKey id); + static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); + static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs); + static bool virtualIsExtensible(const Managed *m); + static bool virtualPreventExtensions(Managed *); + static Heap::Object *virtualGetPrototypeOf(const Managed *); + static bool virtualSetPrototypeOf(Managed *, const Object *); + static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); + static qint64 virtualGetLength(const Managed *m); + static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var); private: - ReturnedValue internalGet(String *name, bool *hasProperty) const; - ReturnedValue internalGetIndexed(uint index, bool *hasProperty) const; - bool internalPut(String *name, const Value &value); - bool internalPutIndexed(uint index, const Value &value); - bool internalDeleteProperty(String *name); - bool internalDeleteIndexedProperty(uint index); + bool internalDefineOwnProperty(ExecutionEngine *engine, uint index, StringOrSymbol *member, const Property *p, PropertyAttributes attrs); + ReturnedValue internalGet(StringOrSymbol *name, const Value *receiver, bool *hasProperty) const; + ReturnedValue internalGetIndexed(uint index, const Value *receiver, bool *hasProperty) const; + bool internalPut(PropertyKey id, const Value &value, Value *receiver); + bool internalDeleteProperty(PropertyKey id); friend struct ObjectIterator; friend struct ObjectPrototype; @@ -499,10 +453,12 @@ struct ArrayObject: Object { void init(ExecutionEngine *engine); - using Object::getLength; - static uint getLength(const Managed *m); + static qint64 virtualGetLength(const Managed *m); QStringList toQStringList() const; +protected: + static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs); + }; inline void Object::setArrayLengthUnchecked(uint l) @@ -551,7 +507,7 @@ inline void Object::arraySet(uint index, const Value &value) template<> inline const ArrayObject *Value::as() const { - return isManaged() && m()->vtable()->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr; + return isManaged() && m()->internalClass->vtable->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr; } #ifndef V4_BOOTSTRAP diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 7bf7e1aa04..73c09a864a 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -42,9 +42,29 @@ #include "qv4identifier_p.h" #include "qv4argumentsobject_p.h" #include "qv4string_p.h" +#include "qv4iterator_p.h" using namespace QV4; +void ForInIteratorPrototype::init(ExecutionEngine *) +{ + defineDefaultProperty(QStringLiteral("next"), method_next, 0); +} + +ReturnedValue ForInIteratorPrototype::method_next(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + const ForInIteratorObject *forIn = thisObject->as<ForInIteratorObject>(); + Q_ASSERT(forIn); + Scope scope(b->engine()); + ScopedValue n(scope, forIn->nextPropertyName()); + bool done = false; + if (n->asReturnedValue() == Encode::null()) { + done = true; + n = Primitive::undefinedValue(); + } + return IteratorPrototype::createIterResultObject(scope.engine, n, done); +} + void ObjectIterator::init(const Object *o) { object->setM(o ? o->m() : nullptr); @@ -84,12 +104,12 @@ void ObjectIterator::next(Value *name, uint *index, Property *pd, PropertyAttrib n = *name; bool shadowed = false; while (o->d() != current->heapObject()) { - if ((!!n && o->hasOwnProperty(n)) || - (*index != UINT_MAX && o->hasOwnProperty(*index))) { + PropertyKey id = n ? (n->toPropertyKey()) : PropertyKey::fromArrayIndex(*index); + if (id.isValid() && o->getOwnProperty(id) != Attr_Invalid) { shadowed = true; break; } - o = o->prototype(); + o = o->getPrototypeOf(); } if (shadowed) continue; @@ -98,7 +118,7 @@ void ObjectIterator::next(Value *name, uint *index, Property *pd, PropertyAttrib } if (flags & WithProtoChain) - current->setM(co->prototype()); + current->setM(co->getPrototypeOf()); else current->setM(nullptr); @@ -175,11 +195,11 @@ ReturnedValue ObjectIterator::nextPropertyNameAsString() } -DEFINE_OBJECT_VTABLE(ForEachIteratorObject); +DEFINE_OBJECT_VTABLE(ForInIteratorObject); -void Heap::ForEachIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack) +void Heap::ForInIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack) { - ForEachIteratorObject *o = static_cast<ForEachIteratorObject *>(that); + ForInIteratorObject *o = static_cast<ForInIteratorObject *>(that); o->workArea[0].mark(markStack); o->workArea[1].mark(markStack); Object::markObjects(that, markStack); diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h index 744d16301a..1e7000ad1f 100644 --- a/src/qml/jsruntime/qv4objectiterator_p.h +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -92,8 +92,8 @@ struct Q_QML_EXPORT ObjectIterator: ObjectIteratorData ObjectIterator(Scope &scope, const Object *o, uint flags) { engine = scope.engine; - object = scope.alloc(1); - current = scope.alloc(1); + object = scope.alloc(); + current = scope.alloc(); arrayNode = nullptr; arrayIndex = 0; memberIndex = 0; @@ -111,7 +111,7 @@ private: }; namespace Heap { -struct ForEachIteratorObject : Object { +struct ForInIteratorObject : Object { void init(QV4::Object *o); ObjectIterator &it() { return *reinterpret_cast<ObjectIterator*>(&itData); } Value workArea[2]; @@ -123,15 +123,24 @@ private: } -struct ForEachIteratorObject: Object { - V4_OBJECT2(ForEachIteratorObject, Object) - Q_MANAGED_TYPE(ForeachIteratorObject) +struct ForInIteratorPrototype : Object +{ + V4_PROTOTYPE(iteratorPrototype) + void init(ExecutionEngine *engine); + + static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +}; + +struct ForInIteratorObject: Object { + V4_OBJECT2(ForInIteratorObject, Object) + Q_MANAGED_TYPE(ForInIterator) + V4_PROTOTYPE(forInIteratorPrototype) - ReturnedValue nextPropertyName() { return d()->it().nextPropertyNameAsString(); } + ReturnedValue nextPropertyName() const { return d()->it().nextPropertyNameAsString(); } }; inline -void Heap::ForEachIteratorObject::init(QV4::Object *o) +void Heap::ForInIteratorObject::init(QV4::Object *o) { Object::init(); it() = ObjectIterator(internalClass->engine, workArea, workArea + 1, o, diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index b998b78520..65e9b836d1 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -47,6 +47,8 @@ #include "qv4objectiterator_p.h" #include "qv4string_p.h" #include "qv4jscall_p.h" +#include "qv4symbol_p.h" +#include "qv4propertykey_p.h" #include <QtCore/QDateTime> #include <QtCore/QStringList> @@ -61,23 +63,23 @@ void Heap::ObjectCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Object")); } -ReturnedValue ObjectCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue ObjectCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *v4 = f->engine(); - const ObjectCtor *ctor = static_cast<const ObjectCtor *>(f); + const ObjectCtor *nt = static_cast<const ObjectCtor *>(newTarget); if (!argc || argv[0].isUndefined() || argv[0].isNull()) { Scope scope(v4); ScopedObject obj(scope, scope.engine->newObject()); - ScopedObject proto(scope, ctor->get(scope.engine->id_prototype())); + ScopedObject proto(scope, nt->get(scope.engine->id_prototype())); if (!!proto) - obj->setPrototype(proto); + obj->setPrototypeOf(proto); return obj.asReturnedValue(); } else { return argv[0].toObject(v4)->asReturnedValue(); } } -ReturnedValue ObjectCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc) +ReturnedValue ObjectCtor::virtualCall(const FunctionObject *m, const Value *, const Value *argv, int argc) { ExecutionEngine *v4 = m->engine(); if (!argc || argv[0].isUndefined() || argv[0].isNull()) { @@ -97,6 +99,7 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) ctor->defineDefaultProperty(QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); + ctor->defineDefaultProperty(QStringLiteral("getOwnPropertySymbols"), method_getOwnPropertySymbols, 1); ctor->defineDefaultProperty(QStringLiteral("assign"), method_assign, 2); ctor->defineDefaultProperty(QStringLiteral("create"), method_create, 2); ctor->defineDefaultProperty(QStringLiteral("defineProperty"), method_defineProperty, 3); @@ -104,10 +107,12 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) ctor->defineDefaultProperty(QStringLiteral("seal"), method_seal, 1); ctor->defineDefaultProperty(QStringLiteral("freeze"), method_freeze, 1); ctor->defineDefaultProperty(QStringLiteral("preventExtensions"), method_preventExtensions, 1); + ctor->defineDefaultProperty(QStringLiteral("is"), method_is, 2); ctor->defineDefaultProperty(QStringLiteral("isSealed"), method_isSealed, 1); ctor->defineDefaultProperty(QStringLiteral("isFrozen"), method_isFrozen, 1); ctor->defineDefaultProperty(QStringLiteral("isExtensible"), method_isExtensible, 1); ctor->defineDefaultProperty(QStringLiteral("keys"), method_keys, 1); + ctor->defineDefaultProperty(QStringLiteral("setPrototypeOf"), method_setPrototypeOf, 2); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(v4->id_toString(), method_toString, 0); @@ -119,11 +124,7 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) defineDefaultProperty(QStringLiteral("__defineGetter__"), method_defineGetter, 2); defineDefaultProperty(QStringLiteral("__defineSetter__"), method_defineSetter, 2); - ExecutionContext *global = v4->rootContext(); - ScopedProperty p(scope); - p->value = FunctionObject::createBuiltinFunction(global, v4->id___proto__(), method_get_proto); - p->set = FunctionObject::createBuiltinFunction(global, v4->id___proto__(), method_set_proto); - insertMember(v4->id___proto__(), p, Attr_Accessor|Attr_NotEnumerable); + defineAccessorProperty(v4->id___proto__(), method_get_proto, method_set_proto); } ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, const Value *, const Value *argv, int argc) @@ -136,10 +137,19 @@ ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, co if (scope.engine->hasException) return QV4::Encode::undefined(); - ScopedObject p(scope, o->prototype()); + ScopedObject p(scope, o->getPrototypeOf()); return (!!p ? p->asReturnedValue() : Encode::null()); } +ReturnedValue ObjectPrototype::method_is(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + if (!argc) + return Encode(true); + if (argc == 1) + return Encode((argv[0].isUndefined() ? true : false)); + return Encode(argv[0].sameValue(argv[1])); +} + ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObject *b, const Value *, const Value *argv, int argc) { Scope scope(b); @@ -154,13 +164,12 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObj static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate(); ScopedValue v(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); - ScopedString name(scope, v->toString(scope.engine)); + ScopedPropertyKey name(scope, v->toPropertyKey(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); - PropertyAttributes attrs; ScopedProperty desc(scope); - O->getOwnProperty(name, &attrs, desc); + PropertyAttributes attrs = O->getOwnProperty(name, desc); return fromPropertyDescriptor(scope.engine, desc, attrs); } @@ -177,6 +186,28 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyNames(const FunctionObject * return Encode(getOwnPropertyNames(scope.engine, argv[0])); } +ReturnedValue ObjectPrototype::method_getOwnPropertySymbols(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc) + return scope.engine->throwTypeError(); + + ScopedObject O(scope, argv[0].toObject(scope.engine)); + if (!O) + return Encode::undefined(); + Heap::InternalClass *ic = O->d()->internalClass; + ScopedValue n(scope); + ScopedArrayObject array(scope, scope.engine->newArrayObject()); + for (uint i = 0; i < ic->size; ++i) { + PropertyKey id = ic->nameMap.at(i); + n = id.asStringOrSymbol(); + if (!n || !n->isSymbol()) + continue; + array->push_back(n); + } + return array->asReturnedValue(); +} + // 19.1.2.1 ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Value *, const Value *argv, int argc) { @@ -204,11 +235,10 @@ ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Valu ScopedString nextKey(scope); ScopedValue propValue(scope); for (quint32 i = 0; i < length; ++i) { - nextKey = Value::fromReturnedValue(keys->getIndexed(i)).toString(scope.engine); + nextKey = Value::fromReturnedValue(keys->get(i)).toString(scope.engine); - PropertyAttributes attrs; ScopedProperty prop(scope); - from->getOwnProperty(nextKey, &attrs, prop); + PropertyAttributes attrs = from->getOwnProperty(nextKey->toPropertyKey(), prop); if (attrs == PropertyFlag::Attr_Invalid) continue; @@ -235,7 +265,7 @@ ReturnedValue ObjectPrototype::method_create(const FunctionObject *builtin, cons ScopedObject O(scope, argv[0]); ScopedObject newObject(scope, scope.engine->newObject()); - newObject->setPrototype(O); + newObject->setPrototypeOf(O); if (argc > 1 && !argv[1].isUndefined()) { @@ -255,7 +285,7 @@ ReturnedValue ObjectPrototype::method_defineProperty(const FunctionObject *b, co return scope.engine->throwTypeError(); ScopedObject O(scope, argv[0]); - ScopedString name(scope, argc > 1 ? argv[1] : Primitive::undefinedValue(), ScopedString::Convert); + ScopedPropertyKey name(scope, (argc > 1 ? argv[1] : Primitive::undefinedValue()).toPropertyKey(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); @@ -266,7 +296,7 @@ ReturnedValue ObjectPrototype::method_defineProperty(const FunctionObject *b, co if (scope.engine->hasException) return QV4::Encode::undefined(); - if (!O->__defineOwnProperty__(scope.engine, name, pd, attrs)) + if (!O->defineOwnProperty(name, pd, attrs)) THROW_TYPE_ERROR(); return O.asReturnedValue(); @@ -287,7 +317,7 @@ ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b, ScopedValue val(scope); ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); - ScopedString name(scope); + ScopedStringOrSymbol name(scope); ScopedProperty pd(scope); ScopedProperty n(scope); while (1) { @@ -303,9 +333,9 @@ ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b, return QV4::Encode::undefined(); bool ok; if (name) - ok = O->__defineOwnProperty__(scope.engine, name, n, nattrs); + ok = O->defineOwnProperty(name->toPropertyKey(), n, nattrs); else - ok = O->__defineOwnProperty__(scope.engine, index, n, nattrs); + ok = O->defineOwnProperty(PropertyKey::fromArrayIndex(index), n, nattrs); if (!ok) THROW_TYPE_ERROR(); } @@ -372,7 +402,7 @@ ReturnedValue ObjectPrototype::method_preventExtensions(const FunctionObject *b, if (!o) return argv[0].asReturnedValue(); - o->setInternalClass(o->internalClass()->nonExtensible()); + o->preventExtensions(); return o.asReturnedValue(); } @@ -477,19 +507,53 @@ ReturnedValue ObjectPrototype::method_keys(const FunctionObject *b, const Value return a.asReturnedValue(); } +ReturnedValue ObjectPrototype::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f->engine()); + if (argc < 2 || argv[0].isNullOrUndefined() || !(argv[1].isObject() || argv[1].isNull())) + return scope.engine->throwTypeError(); + + if (!argv[0].isObject()) + return argv[0].asReturnedValue(); + + ScopedObject o(scope, argv[0]); + const Object *p = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1); + bool ok = o->setPrototypeOf(p); + if (!ok) + return scope.engine->throwTypeError(QStringLiteral("Could not change prototype.")); + return o->asReturnedValue(); +} + ReturnedValue ObjectPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); + QString string; if (thisObject->isUndefined()) { - return Encode(v4->newString(QStringLiteral("[object Undefined]"))); + string = QStringLiteral("[object Undefined]"); } else if (thisObject->isNull()) { - return Encode(v4->newString(QStringLiteral("[object Null]"))); + string = QStringLiteral("[object Null]"); } else { + const Object *o = thisObject->as<Object>(); + if (!o) { + // primitive, get the proper prototype + if (thisObject->isBoolean()) + o = v4->booleanPrototype(); + else if (thisObject->isNumber()) + o = v4->numberPrototype(); + else if (thisObject->isString()) + o = v4->stringPrototype(); + else if (thisObject->isSymbol()) + o = v4->symbolPrototype(); + Q_ASSERT(o); + } + QString name = o->className(); Scope scope(v4); - ScopedObject obj(scope, thisObject->toObject(scope.engine)); - QString className = obj->className(); - return Encode(v4->newString(QStringLiteral("[object %1]").arg(className))); + ScopedString toStringTag(scope, o->get(v4->symbol_toStringTag())); + if (toStringTag) + name = toStringTag->toQString(); + string = QStringLiteral("[object %1]").arg(name); } + return Encode(v4->newString(string)); } ReturnedValue ObjectPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) @@ -514,15 +578,13 @@ ReturnedValue ObjectPrototype::method_valueOf(const FunctionObject *b, const Val ReturnedValue ObjectPrototype::method_hasOwnProperty(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); - ScopedString P(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert); + ScopedPropertyKey P(scope, (argc ? argv[0] : Primitive::undefinedValue()).toPropertyKey(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); ScopedObject O(scope, thisObject->toObject(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); - bool r = O->hasOwnProperty(P); - if (!r) - r = !O->query(P).isEmpty(); + bool r = O->getOwnProperty(P) != Attr_Invalid; return Encode(r); } @@ -536,11 +598,11 @@ ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, con ScopedObject O(scope, thisObject->toObject(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); - ScopedObject proto(scope, V->prototype()); + ScopedObject proto(scope, V->getPrototypeOf()); while (proto) { if (O->d() == proto->d()) return Encode(true); - proto = proto->prototype(); + proto = proto->getPrototypeOf(); } return Encode(false); } @@ -548,15 +610,14 @@ ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, con ReturnedValue ObjectPrototype::method_propertyIsEnumerable(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); - ScopedString p(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert); + ScopedPropertyKey p(scope, (argc ? argv[0] : Primitive::undefinedValue()).toPropertyKey(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); ScopedObject o(scope, thisObject->toObject(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); - PropertyAttributes attrs; - o->getOwnProperty(p, &attrs); + PropertyAttributes attrs = o->getOwnProperty(p); return Encode(attrs.isEnumerable()); } @@ -584,7 +645,7 @@ ReturnedValue ObjectPrototype::method_defineGetter(const FunctionObject *b, cons ScopedProperty pd(scope); pd->value = f; pd->set = Primitive::emptyValue(); - bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); + bool ok = o->defineOwnProperty(prop->toPropertyKey(), pd, Attr_Accessor); if (!ok) THROW_TYPE_ERROR(); RETURN_UNDEFINED(); @@ -614,7 +675,7 @@ ReturnedValue ObjectPrototype::method_defineSetter(const FunctionObject *b, cons ScopedProperty pd(scope); pd->value = Primitive::emptyValue(); pd->set = f; - bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); + bool ok = o->defineOwnProperty(prop->toPropertyKey(), pd, Attr_Accessor); if (!ok) THROW_TYPE_ERROR(); RETURN_UNDEFINED(); @@ -627,32 +688,21 @@ ReturnedValue ObjectPrototype::method_get_proto(const FunctionObject *b, const V if (!o) THROW_TYPE_ERROR(); - return Encode(o->prototype()); + return Encode(o->getPrototypeOf()); } ReturnedValue ObjectPrototype::method_set_proto(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); ScopedObject o(scope, thisObject); - if (!o || !argc) + if (!o || !argc || (!argv[0].isObject() && !argv[0].isNull())) THROW_TYPE_ERROR(); - if (argv[0].isNull()) { - o->setPrototype(nullptr); - RETURN_UNDEFINED(); - } - - ScopedObject p(scope, argv[0]); - bool ok = false; - if (!!p) { - if (o->prototype() == p->d()) { - ok = true; - } else if (o->isExtensible()) { - ok = o->setPrototype(p); - } - } + const Object *p = argv[0].isNull() ? nullptr : static_cast<const Object *>(argv); + bool ok = o->setPrototypeOf(p); if (!ok) - return scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value")); + return scope.engine->throwTypeError(QStringLiteral("Could not change prototype.")); + return Encode::undefined(); RETURN_UNDEFINED(); } @@ -670,13 +720,13 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value desc->set = Primitive::emptyValue(); ScopedValue tmp(scope); - if (o->hasProperty(engine->id_enumerable())) + if (o->hasProperty(engine->id_enumerable()->toPropertyKey())) attrs->setEnumerable((tmp = o->get(engine->id_enumerable()))->toBoolean()); - if (o->hasProperty(engine->id_configurable())) + if (o->hasProperty(engine->id_configurable()->toPropertyKey())) attrs->setConfigurable((tmp = o->get(engine->id_configurable()))->toBoolean()); - if (o->hasProperty(engine->id_get())) { + if (o->hasProperty(engine->id_get()->toPropertyKey())) { ScopedValue get(scope, o->get(engine->id_get())); FunctionObject *f = get->as<FunctionObject>(); if (f || get->isUndefined()) { @@ -688,7 +738,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value attrs->setType(PropertyAttributes::Accessor); } - if (o->hasProperty(engine->id_set())) { + if (o->hasProperty(engine->id_set()->toPropertyKey())) { ScopedValue set(scope, o->get(engine->id_set())); FunctionObject *f = set->as<FunctionObject>(); if (f || set->isUndefined()) { @@ -700,7 +750,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value attrs->setType(PropertyAttributes::Accessor); } - if (o->hasProperty(engine->id_writable())) { + if (o->hasProperty(engine->id_writable()->toPropertyKey())) { if (attrs->isAccessor()) { engine->throwTypeError(); return; @@ -710,7 +760,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value desc->value = Primitive::undefinedValue(); } - if (o->hasProperty(engine->id_value())) { + if (o->hasProperty(engine->id_value()->toPropertyKey())) { if (attrs->isAccessor()) { engine->throwTypeError(); return; @@ -734,29 +784,28 @@ ReturnedValue ObjectPrototype::fromPropertyDescriptor(ExecutionEngine *engine, c // is the standard built-in constructor with that name. ScopedObject o(scope, engine->newObject()); ScopedString s(scope); + ScopedValue v(scope); - ScopedProperty pd(scope); if (attrs.isData()) { - pd->value = desc->value; s = engine->newString(QStringLiteral("value")); - o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data); - pd->value = Primitive::fromBoolean(attrs.isWritable()); + o->put(s, desc->value); + v = Primitive::fromBoolean(attrs.isWritable()); s = engine->newString(QStringLiteral("writable")); - o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data); + o->put(s, v); } else { - pd->value = desc->getter() ? desc->getter()->asReturnedValue() : Encode::undefined(); + v = desc->getter() ? desc->getter()->asReturnedValue() : Encode::undefined(); s = engine->newString(QStringLiteral("get")); - o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data); - pd->value = desc->setter() ? desc->setter()->asReturnedValue() : Encode::undefined(); + o->put(s, v); + v = desc->setter() ? desc->setter()->asReturnedValue() : Encode::undefined(); s = engine->newString(QStringLiteral("set")); - o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data); + o->put(s, v); } - pd->value = Primitive::fromBoolean(attrs.isEnumerable()); + v = Primitive::fromBoolean(attrs.isEnumerable()); s = engine->newString(QStringLiteral("enumerable")); - o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data); - pd->value = Primitive::fromBoolean(attrs.isConfigurable()); + o->put(s, v); + v = Primitive::fromBoolean(attrs.isConfigurable()); s = engine->newString(QStringLiteral("configurable")); - o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data); + o->put(s, v); return o.asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index 2b231d46ad..0314c05766 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -70,28 +70,31 @@ struct ObjectCtor: FunctionObject { V4_OBJECT2(ObjectCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc); }; struct ObjectPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); - static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_getOwnPropertyNames(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_assign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_create(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_defineProperties(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_seal(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_freeze(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_isSealed(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_isFrozen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getOwnPropertyNames(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getOwnPropertySymbols(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_is(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isFrozen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isSealed(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_seal(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp index 7fc74173e3..f8bc28160e 100644 --- a/src/qml/jsruntime/qv4persistent.cpp +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -98,9 +98,9 @@ Page *allocatePage(PersistentValueStorage *storage) p->header.freeList = 0; insertInFront(storage, p); for (int i = 0; i < kEntriesPerPage - 1; ++i) { - p->values[i].setEmpty(i + 1); + p->values[i] = Encode(i + 1); } - p->values[kEntriesPerPage - 1].setEmpty(-1); + p->values[kEntriesPerPage - 1] = Encode(-1); return p; } @@ -226,7 +226,7 @@ void PersistentValueStorage::free(Value *v) Page *p = getPage(v); - v->setEmpty(p->header.freeList); + *v = Encode(p->header.freeList); p->header.freeList = v - p->values; if (!--p->header.refCount) freePage(p); diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index 5fd200efc1..b337243204 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -78,7 +78,7 @@ Profiler::Profiler(QV4::ExecutionEngine *engine) : featuresEnabled(0), m_engine( void Profiler::stopProfiling() { featuresEnabled = 0; - reportData(true); + reportData(); m_sentLocations.clear(); } @@ -89,7 +89,7 @@ bool operator<(const FunctionCall &call1, const FunctionCall &call2) (call1.m_end == call2.m_end && call1.m_function < call2.m_function))); } -void Profiler::reportData(bool trackLocations) +void Profiler::reportData() { std::sort(m_data.begin(), m_data.end()); QVector<FunctionCallProperties> properties; @@ -100,12 +100,11 @@ void Profiler::reportData(bool trackLocations) properties.append(call.properties()); Function *function = call.function(); SentMarker &marker = m_sentLocations[reinterpret_cast<quintptr>(function)]; - if (!trackLocations || !marker.isValid()) { + if (!marker.isValid()) { FunctionLocation &location = locations[properties.constLast().id]; if (!location.isValid()) location = call.resolveLocation(); - if (trackLocations) - marker.setFunction(function); + marker.setFunction(function); } } diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index e8c154e4e7..8461384e9a 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -251,7 +251,7 @@ public: void stopProfiling(); void startProfiling(quint64 features); - void reportData(bool trackLocations); + void reportData(); void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h index 7cb106c424..26dc7a83c3 100644 --- a/src/qml/jsruntime/qv4property_p.h +++ b/src/qml/jsruntime/qv4property_p.h @@ -142,6 +142,19 @@ inline void Property::merge(PropertyAttributes &attrs, const Property *other, Pr } } +struct PropertyIndex { + Heap::Base *base; + Value *slot; + + void set(EngineBase *e, Value newVal) { + WriteBarrier::write(e, base, slot->data_ptr(), newVal.asReturnedValue()); + } + const Value *operator->() const { return slot; } + const Value &operator*() const { return *slot; } + bool isNull() const { return !slot; } +}; + + } Q_DECLARE_TYPEINFO(QV4::Property, Q_MOVABLE_TYPE); diff --git a/src/qml/jsruntime/qv4propertykey.cpp b/src/qml/jsruntime/qv4propertykey.cpp new file mode 100644 index 0000000000..e5e96bedb8 --- /dev/null +++ b/src/qml/jsruntime/qv4propertykey.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4propertykey_p.h" + +#include <QtCore/qstring.h> +#include <qv4string_p.h> + +QV4::Heap::StringOrSymbol *QV4::PropertyKey::toStringOrSymbol(QV4::ExecutionEngine *e) +{ + if (isArrayIndex()) + return Primitive::fromUInt32(asArrayIndex()).toString(e); + return static_cast<Heap::StringOrSymbol *>(asStringOrSymbol()); +} + +bool QV4::PropertyKey::isString() const { + Heap::StringOrSymbol *s = asStringOrSymbol(); + return s && s->internalClass->vtable->isString; +} + +bool QV4::PropertyKey::isSymbol() const { + Heap::Base *s = asStringOrSymbol(); + return s && !s->internalClass->vtable->isString && s->internalClass->vtable->isStringOrSymbol; +} + +QString QV4::PropertyKey::toQString() const +{ + if (isArrayIndex()) + return QString::number(asArrayIndex()); + Heap::Base *b = asStringOrSymbol(); + Q_ASSERT(b->internalClass->vtable->isStringOrSymbol); + Heap::StringOrSymbol *s = static_cast<Heap::StringOrSymbol *>(b); + return s->toQString(); +} diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h new file mode 100644 index 0000000000..00bf8fb195 --- /dev/null +++ b/src/qml/jsruntime/qv4propertykey_p.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4PROPERTYKEY_H +#define QV4PROPERTYKEY_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qv4global_p.h> + +QT_BEGIN_NAMESPACE + +class QString; + +namespace QV4 { + +struct PropertyKey +{ +private: + // Property keys are Strings, Symbols or unsigned integers. + // For convenience we derive them from Values, allowing us to store them + // on the JS stack + // + // They do however behave somewhat different than a Value: + // * If the key is a String, the pointer to the string is stored in the identifier + // table and thus unique. + // * If the key is a Symbol it simply points to the referenced symbol object + // * if the key is an array index (a uint < UINT_MAX), it's encoded as an + // integer value + quint64 val; + + // Important: Always keep this in sync with the definitions for Integers and heap objects in Value + static const quint64 ArrayIndexMask = 0x3800000000000ull; + enum { + IsManagedOrUndefined_Shift = 64-15, + }; + inline bool isManaged() const { return (val >> IsManagedOrUndefined_Shift) == 0; } + inline quint32 value() const { return val & quint64(~quint32(0)); } + +#if QT_POINTER_SIZE == 8 + QML_NEARLY_ALWAYS_INLINE Heap::StringOrSymbol *m() const + { + Heap::StringOrSymbol *b; + memcpy(&b, &val, 8); + return b; + } + QML_NEARLY_ALWAYS_INLINE void setM(Heap::StringOrSymbol *b) + { + memcpy(&val, &b, 8); + } +#elif QT_POINTER_SIZE == 4 + QML_NEARLY_ALWAYS_INLINE Heap::StringOrSymbol *m() const + { + Q_STATIC_ASSERT(sizeof(Heap::StringOrSymbol*) == sizeof(quint32)); + Heap::StringOrSymbol *b; + quint32 v = value(); + memcpy(&b, &v, 4); + return b; + } + QML_NEARLY_ALWAYS_INLINE void setM(Heap::StringOrSymbol *b) + { + quint32 v; + memcpy(&v, &b, 4); + val = v; + } +#endif + +public: + static PropertyKey invalid() { PropertyKey key; key.val = 0; return key; } + static PropertyKey fromArrayIndex(uint idx) { PropertyKey key; key.val = ArrayIndexMask | static_cast<quint64>(idx); return key; } + bool isStringOrSymbol() const { return isManaged() && val != 0; } + uint asArrayIndex() const { return (isManaged() || val == 0) ? std::numeric_limits<uint>::max() : static_cast<uint>(val & 0xffffffff); } + uint isArrayIndex() const { return !isManaged() && val != 0; } + bool isValid() const { return val != 0; } + static PropertyKey fromStringOrSymbol(Heap::StringOrSymbol *b) + { PropertyKey key; key.setM(b); return key; } + Heap::StringOrSymbol *asStringOrSymbol() const { + if (!isManaged()) + return nullptr; + return m(); + } + + bool isString() const; + bool isSymbol() const; + + Q_QML_EXPORT QString toQString() const; + Heap::StringOrSymbol *toStringOrSymbol(ExecutionEngine *e); + quint64 id() const { return val; } + + bool operator ==(const PropertyKey &other) const { return val == other.val; } + bool operator !=(const PropertyKey &other) const { return val != other.val; } + bool operator <(const PropertyKey &other) const { return val < other.val; } +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4proxy.cpp b/src/qml/jsruntime/qv4proxy.cpp new file mode 100644 index 0000000000..794aee8c24 --- /dev/null +++ b/src/qml/jsruntime/qv4proxy.cpp @@ -0,0 +1,553 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4proxy_p.h" +#include "qv4symbol_p.h" +#include "qv4jscall_p.h" +#include "qv4objectproto_p.h" + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(ProxyObject); + +void Heap::ProxyObject::init(const QV4::Object *target, const QV4::Object *handler) +{ + Object::init(); + ExecutionEngine *e = internalClass->engine; + this->target.set(e, target->d()); + this->handler.set(e, handler->d()); +} + +ReturnedValue ProxyObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedValue trap(scope, handler->get(scope.engine->id_get())); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->get(id, receiver, hasProperty); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + if (hasProperty) + *hasProperty = true; + + JSCallData cdata(scope, 3, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.toStringOrSymbol(scope.engine); + cdata.args[2] = *receiver; + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + ScopedProperty targetDesc(scope); + PropertyAttributes attributes = target->getOwnProperty(id, targetDesc); + if (attributes != Attr_Invalid && !attributes.isConfigurable()) { + if (attributes.isData() && !attributes.isWritable()) { + if (!trapResult->sameValue(targetDesc->value)) + return scope.engine->throwTypeError(); + } + if (attributes.isAccessor() && targetDesc->value.isUndefined()) { + if (!trapResult->isUndefined()) + return scope.engine->throwTypeError(); + } + } + return trapResult->asReturnedValue(); +} + +bool ProxyObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedValue trap(scope, handler->get(scope.engine->id_set())); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->put(id, value, receiver); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 4, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.toStringOrSymbol(scope.engine); + cdata.args[2] = value; + cdata.args[3] = *receiver; + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (!trapResult->toBoolean()) + return false; + ScopedProperty targetDesc(scope); + PropertyAttributes attributes = target->getOwnProperty(id, targetDesc); + if (attributes != Attr_Invalid && !attributes.isConfigurable()) { + if (attributes.isData() && !attributes.isWritable()) { + if (!value.sameValue(targetDesc->value)) + return scope.engine->throwTypeError(); + } + if (attributes.isAccessor() && targetDesc->set.isUndefined()) + return scope.engine->throwTypeError(); + } + return true; +} + +bool ProxyObject::virtualDeleteProperty(Managed *m, PropertyKey id) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString deleteProp(scope, scope.engine->newString(QStringLiteral("deleteProperty"))); + ScopedValue trap(scope, handler->get(deleteProp)); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->deleteProperty(id); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 3, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.toStringOrSymbol(scope.engine); + cdata.args[2] = o->d(); // ### fix receiver handling + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (!trapResult->toBoolean()) + return false; + ScopedProperty targetDesc(scope); + PropertyAttributes attributes = target->getOwnProperty(id, targetDesc); + if (attributes == Attr_Invalid) + return true; + if (!attributes.isConfigurable()) + return scope.engine->throwTypeError(); + return true; +} + +bool ProxyObject::virtualHasProperty(const Managed *m, PropertyKey id) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("has"))); + ScopedValue trap(scope, handler->get(hasProp)); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->hasProperty(id); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 2, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.isArrayIndex() ? Primitive::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol(); + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (!result) { + ScopedProperty targetDesc(scope); + PropertyAttributes attributes = target->getOwnProperty(id, targetDesc); + if (attributes != Attr_Invalid) { + if (!attributes.isConfigurable() || !target->isExtensible()) + return scope.engine->throwTypeError(); + } + } + return result; +} + +PropertyAttributes ProxyObject::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString deleteProp(scope, scope.engine->newString(QStringLiteral("getOwnPropertyDescriptor"))); + ScopedValue trap(scope, handler->get(deleteProp)); + if (scope.hasException()) + return Attr_Invalid; + if (trap->isNullOrUndefined()) + return target->getOwnProperty(id, p); + if (!trap->isFunctionObject()) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + + JSCallData cdata(scope, 2, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.isArrayIndex() ? Primitive::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol(); + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (!trapResult->isObject() && !trapResult->isUndefined()) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + + ScopedProperty targetDesc(scope); + PropertyAttributes targetAttributes = target->getOwnProperty(id, targetDesc); + if (trapResult->isUndefined()) { + p->value = Encode::undefined(); + if (targetAttributes == Attr_Invalid) { + p->value = Encode::undefined(); + return Attr_Invalid; + } + if (!targetAttributes.isConfigurable() || !target->isExtensible()) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + return Attr_Invalid; + } + + //bool extensibleTarget = target->isExtensible(); + ScopedProperty resultDesc(scope); + PropertyAttributes resultAttributes; + ObjectPrototype::toPropertyDescriptor(scope.engine, trapResult, resultDesc, &resultAttributes); + resultDesc->fullyPopulated(&resultAttributes); + + // ### + //Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc). + //If valid is false, throw a TypeError exception. + + if (!resultAttributes.isConfigurable()) { + if (targetAttributes == Attr_Invalid || !targetAttributes.isConfigurable()) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + } + + p->value = resultDesc->value; + p->set = resultDesc->set; + return resultAttributes; +} + +bool ProxyObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) { + scope.engine->throwTypeError(); + return false; + } + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString prop(scope, scope.engine->newString(QStringLiteral("defineProperty"))); + ScopedValue trap(scope, handler->get(prop)); + if (scope.hasException()) + return false; + if (trap->isNullOrUndefined()) + return target->defineOwnProperty(id, p, attrs); + if (!trap->isFunctionObject()) { + scope.engine->throwTypeError(); + return false; + } + + JSCallData cdata(scope, 3, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.isArrayIndex() ? Primitive::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol(); + cdata.args[2] = ObjectPrototype::fromPropertyDescriptor(scope.engine, p, attrs); + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (!result) + return false; + + ScopedProperty targetDesc(scope); + PropertyAttributes targetAttributes = target->getOwnProperty(id, targetDesc); + bool extensibleTarget = target->isExtensible(); + bool settingConfigFalse = attrs.hasConfigurable() && !attrs.isConfigurable(); + if (targetAttributes == Attr_Invalid) { + if (!extensibleTarget || settingConfigFalse) { + scope.engine->throwTypeError(); + return false; + } + } else { + // ### + // if IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false throw a type error. + if (settingConfigFalse && targetAttributes.isConfigurable()) { + scope.engine->throwTypeError(); + return false; + } + } + + return true; +} + +bool ProxyObject::virtualIsExtensible(const Managed *m) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("isExtensible"))); + ScopedValue trap(scope, handler->get(hasProp)); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->isExtensible(); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 1, nullptr, handler); + cdata.args[0] = target; + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (result != target->isExtensible()) { + scope.engine->throwTypeError(); + return false; + } + return result; +} + +bool ProxyObject::virtualPreventExtensions(Managed *m) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("preventExtensions"))); + ScopedValue trap(scope, handler->get(hasProp)); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->preventExtensions(); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 1, nullptr, handler); + cdata.args[0] = target; + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (result && target->isExtensible()) { + scope.engine->throwTypeError(); + return false; + } + return result; +} + +Heap::Object *ProxyObject::virtualGetPrototypeOf(const Managed *m) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) { + scope.engine->throwTypeError(); + return nullptr; + } + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString name(scope, scope.engine->newString(QStringLiteral("getPrototypeOf"))); + ScopedValue trap(scope, handler->get(name)); + if (scope.hasException()) + return nullptr; + if (trap->isNullOrUndefined()) + return target->getPrototypeOf(); + if (!trap->isFunctionObject()) { + scope.engine->throwTypeError(); + return nullptr; + } + + JSCallData cdata(scope, 1, nullptr, handler); + cdata.args[0] = target; + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (!trapResult->isNull() && !trapResult->isObject()) { + scope.engine->throwTypeError(); + return nullptr; + } + Heap::Object *proto = trapResult->isNull() ? nullptr : static_cast<Heap::Object *>(trapResult->heapObject()); + if (!target->isExtensible()) { + Heap::Object *targetProto = target->getPrototypeOf(); + if (proto != targetProto) { + scope.engine->throwTypeError(); + return nullptr; + } + } + return proto; +} + +bool ProxyObject::virtualSetPrototypeOf(Managed *m, const Object *p) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) { + scope.engine->throwTypeError(); + return false; + } + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString name(scope, scope.engine->newString(QStringLiteral("setPrototypeOf"))); + ScopedValue trap(scope, handler->get(name)); + if (scope.hasException()) + return false; + if (trap->isNullOrUndefined()) + return target->setPrototypeOf(p); + if (!trap->isFunctionObject()) { + scope.engine->throwTypeError(); + return false; + } + + JSCallData cdata(scope, 2, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = p ? p->asReturnedValue() : Encode::null(); + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (!result) + return false; + if (!target->isExtensible()) { + Heap::Object *targetProto = target->getPrototypeOf(); + if (p->d() != targetProto) { + scope.engine->throwTypeError(); + return false; + } + } + return true; +} + +//ReturnedValue ProxyObject::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +//{ + +//} + +//ReturnedValue ProxyObject::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +//{ + +//} + +DEFINE_OBJECT_VTABLE(Proxy); + +void Heap::Proxy::init(QV4::ExecutionContext *ctx) +{ + Heap::FunctionObject::init(ctx, QStringLiteral("Proxy")); + + Scope scope(ctx); + Scoped<QV4::Proxy> ctor(scope, this); + ctor->defineDefaultProperty(QStringLiteral("revocable"), QV4::Proxy::method_revocable, 2); + ctor->defineReadonlyConfigurableProperty(scope.engine->id_length(), Primitive::fromInt32(2)); +} + +ReturnedValue Proxy::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +{ + Scope scope(f); + if (argc < 2 || !argv[0].isObject() || !argv[1].isObject()) + return scope.engine->throwTypeError(); + + const Object *target = static_cast<const Object *>(argv); + const Object *handler = static_cast<const Object *>(argv + 1); + if (const ProxyObject *ptarget = target->as<ProxyObject>()) + if (!ptarget->d()->handler) + return scope.engine->throwTypeError(); + if (const ProxyObject *phandler = handler->as<ProxyObject>()) + if (!phandler->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, scope.engine->memoryManager->allocate<ProxyObject>(target, handler)); + return o->asReturnedValue(); +} + +ReturnedValue Proxy::virtualCall(const FunctionObject *f, const Value *, const Value *, int) +{ + return f->engine()->throwTypeError(); +} + +ReturnedValue Proxy::method_revocable(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + ScopedObject proxy(scope, Proxy::virtualCallAsConstructor(f, argv, argc, f)); + if (scope.hasException()) + return Encode::undefined(); + + ScopedString revoke(scope, scope.engine->newString(QStringLiteral("revoke"))); + ScopedFunctionObject revoker(scope, createBuiltinFunction(scope.engine, revoke, method_revoke, 0)); + revoker->defineDefaultProperty(scope.engine->symbol_revokableProxy(), proxy); + + ScopedObject o(scope, scope.engine->newObject()); + ScopedString p(scope, scope.engine->newString(QStringLiteral("proxy"))); + o->defineDefaultProperty(p, proxy); + o->defineDefaultProperty(revoke, revoker); + return o->asReturnedValue(); +} + +ReturnedValue Proxy::method_revoke(const FunctionObject *f, const Value *, const Value *, int) +{ + Scope scope(f); + Scoped<ProxyObject> proxy(scope, f->get(scope.engine->symbol_revokableProxy())); + Q_ASSERT(proxy); + + proxy->d()->target.set(scope.engine, nullptr); + proxy->d()->handler.set(scope.engine, nullptr); + return Encode::undefined(); +} diff --git a/src/qml/jsruntime/qv4proxy_p.h b/src/qml/jsruntime/qv4proxy_p.h new file mode 100644 index 0000000000..4d631e882c --- /dev/null +++ b/src/qml/jsruntime/qv4proxy_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4PROXY_P_H +#define QV4PROXY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +#define ProxyObjectMembers(class, Member) \ + Member(class, Pointer, Object *, target) \ + Member(class, Pointer, Object *, handler) + +DECLARE_HEAP_OBJECT(ProxyObject, Object) { + DECLARE_MARKOBJECTS(ProxyObject) + + void init(const QV4::Object *target, const QV4::Object *handler); +}; + +#define ProxyMembers(class, Member) \ + Member(class, Pointer, Symbol *, revokableProxySymbol) \ + +DECLARE_HEAP_OBJECT(Proxy, FunctionObject) { + DECLARE_MARKOBJECTS(Proxy) + + void init(QV4::ExecutionContext *ctx); +}; + +} + +struct ProxyObject: Object { + V4_OBJECT2(ProxyObject, Object) + Q_MANAGED_TYPE(ProxyObject) + V4_INTERNALCLASS(ProxyObject) + + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); + static bool virtualDeleteProperty(Managed *m, PropertyKey id); + static bool virtualHasProperty(const Managed *m, PropertyKey id); + static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); + static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs); + static bool virtualIsExtensible(const Managed *m); + static bool virtualPreventExtensions(Managed *); + static Heap::Object *virtualGetPrototypeOf(const Managed *); + static bool virtualSetPrototypeOf(Managed *, const Object *); + + // those might require a second proxy object that derives from FunctionObject... +// static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); +// static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct Proxy : FunctionObject +{ + V4_OBJECT2(Proxy, FunctionObject) + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_revocable(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_revoke(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 040f060476..dc69016559 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -78,30 +78,25 @@ void Heap::QQmlContextWrapper::destroy() Object::destroy(); } -ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { Q_ASSERT(m->as<QQmlContextWrapper>()); + + if (!id.isString()) + return Object::virtualGet(m, id, receiver, hasProperty); + const QQmlContextWrapper *resource = static_cast<const QQmlContextWrapper *>(m); QV4::ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); - // In V8 the JS global object would come _before_ the QML global object, - // so simulate that here. - bool hasProp; - QV4::ScopedValue result(scope, v4->globalObject->get(name, &hasProp)); - if (hasProp) { - if (hasProperty) - *hasProperty = hasProp; - return result->asReturnedValue(); - } - if (resource->d()->isNullWrapper) - return Object::get(m, name, hasProperty); + return Object::virtualGet(m, id, receiver, hasProperty); if (v4->callingQmlContext() != *resource->d()->context) - return Object::get(m, name, hasProperty); + return Object::virtualGet(m, id, receiver, hasProperty); - result = Object::get(m, name, &hasProp); + bool hasProp = false; + ScopedValue result(scope, Object::virtualGet(m, id, receiver, &hasProp)); if (hasProp) { if (hasProperty) *hasProperty = hasProp; @@ -129,6 +124,7 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP QObject *scopeObject = resource->getScopeObject(); + ScopedString name(scope, id.asStringOrSymbol()); if (context->imports && name->startsWithUpper()) { // Search for attached properties, enums and imported scripts QQmlTypeNameCache::Result r = context->imports->query(name, QQmlImport::AllowRecursion); @@ -139,7 +135,7 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP if (r.scriptIndex != -1) { QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); if (scripts) - return scripts->getIndexed(r.scriptIndex); + return scripts->get(r.scriptIndex); return QV4::Encode::null(); } else if (r.type.isValid()) { return QQmlTypeWrapper::create(v4, scopeObject, r.type); @@ -219,14 +215,27 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP context = context->parent; } + // Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming + // true if we access properties of the global object. + result = v4->globalObject->get(name, &hasProp); + if (hasProp) { + if (hasProperty) + *hasProperty = hasProp; + return result->asReturnedValue(); + } + expressionContext->unresolvedNames = true; return Encode::undefined(); } -bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value) +bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { Q_ASSERT(m->as<QQmlContextWrapper>()); + + if (id.isSymbol() || id.isArrayIndex()) + return Object::virtualPut(m, id, value, receiver); + QQmlContextWrapper *resource = static_cast<QQmlContextWrapper *>(m); ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); @@ -234,20 +243,20 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value) return false; QV4::Scoped<QQmlContextWrapper> wrapper(scope, resource); - uint member = wrapper->internalClass()->find(name); + uint member = wrapper->internalClass()->find(id); if (member < UINT_MAX) return wrapper->putValue(member, value); if (wrapper->d()->isNullWrapper) { if (wrapper && wrapper->d()->readOnly) { - QString error = QLatin1String("Invalid write to global property \"") + name->toQString() + + QString error = QLatin1String("Invalid write to global property \"") + id.toQString() + QLatin1Char('"'); ScopedString e(scope, v4->newString(error)); v4->throwError(e); return false; } - return Object::put(m, name, value); + return Object::virtualPut(m, id, value, receiver); } // It's possible we could delay the calculation of the "actual" context (in the case @@ -261,6 +270,7 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value) // See QV8ContextWrapper::Getter for resolution order QObject *scopeObject = wrapper->getScopeObject(); + ScopedString name(scope, id.asStringOrSymbol()); while (context) { const QV4::IdentifierHash &properties = context->propertyNames(); @@ -291,7 +301,7 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value) return false; } - return Object::put(m, name, value); + return Object::virtualPut(m, id, value, receiver); } void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml) @@ -312,7 +322,7 @@ Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, cons context->isInternal = true; context->isJSContext = true; - Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QQmlContextWrapper>(context, (QObject*)nullptr)); + Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>(context, (QObject*)nullptr)); qml->d()->isNullWrapper = true; qml->setReadOnly(false); @@ -330,7 +340,7 @@ Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData * { Scope scope(parent); - Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QQmlContextWrapper>(context, scopeObject)); + Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>(context, scopeObject)); Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(parent, qml); Q_ASSERT(c->vtable() == staticVTable()); return c; diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 647bef7fc1..b9061a3f58 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -98,8 +98,8 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object void setReadOnly(bool b) { d()->readOnly = b; } - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static bool put(Managed *m, String *name, const Value &value); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index d63d42478a..a17de5d94d 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -56,7 +56,11 @@ #include <private/qv4functionobject_p.h> #include <private/qv4runtime_p.h> #include <private/qv4variantobject_p.h> + +#if QT_CONFIG(qml_sequence_object) #include <private/qv4sequenceobject_p.h> +#endif + #include <private/qv4objectproto_p.h> #include <private/qv4jsonobject_p.h> #include <private/qv4regexpobject_p.h> @@ -181,11 +185,13 @@ static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType())) return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, property.propType()); } else { +#if QT_CONFIG(qml_sequence_object) // see if it's a sequence type bool succeeded = false; - QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), &succeeded)); + QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), !property.isWritable(), &succeeded)); if (succeeded) return retn->asReturnedValue(); +#endif } if (property.propType() == QMetaType::UnknownType) { @@ -242,7 +248,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje return QV4::QObjectMethod::create(global, object, property->coreIndex()); } else if (property->isSignalHandler()) { QmlSignalHandler::initProto(engine); - return engine->memoryManager->allocObject<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue(); + return engine->memoryManager->allocate<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue(); } else { ExecutionContext *global = engine->rootContext(); return QV4::QObjectMethod::create(global, object, property->coreIndex()); @@ -308,7 +314,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String } } } - return QV4::Object::get(this, name, hasProperty); + return QV4::Object::virtualGet(this, name->propertyKey(), this, hasProperty); } QQmlData *ddata = QQmlData::get(d()->object(), false); @@ -661,7 +667,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int p return setProperty(engine, object, property, value); } -bool QObjectWrapper::isEqualTo(Managed *a, Managed *b) +bool QObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b) { Q_ASSERT(a->as<QV4::QObjectWrapper>()); QV4::QObjectWrapper *qobjectWrapper = static_cast<QV4::QObjectWrapper *>(a); @@ -684,60 +690,77 @@ ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object) return result; } } - return (engine->memoryManager->allocObject<QV4::QObjectWrapper>(object))->asReturnedValue(); + return (engine->memoryManager->allocate<QV4::QObjectWrapper>(object))->asReturnedValue(); } -QV4::ReturnedValue QObjectWrapper::get(const Managed *m, String *name, bool *hasProperty) +QV4::ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { + if (!id.isString()) + return Object::virtualGet(m, id, receiver, hasProperty); + const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); + Scope scope(that); + ScopedString n(scope, id.asStringOrSymbol()); QQmlContextData *qmlContext = that->engine()->callingQmlContext(); - return that->getQmlProperty(qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true); + return that->getQmlProperty(qmlContext, n, IgnoreRevision, hasProperty, /*includeImports*/ true); } -bool QObjectWrapper::put(Managed *m, String *name, const Value &value) +bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { + if (!id.isString()) + return Object::virtualPut(m, id, value, receiver); + + Scope scope(m); QObjectWrapper *that = static_cast<QObjectWrapper*>(m); - ExecutionEngine *v4 = that->engine(); + ScopedString name(scope, id.asStringOrSymbol()); - if (v4->hasException || QQmlData::wasDeleted(that->d()->object())) + if (scope.engine->hasException || QQmlData::wasDeleted(that->d()->object())) return false; - QQmlContextData *qmlContext = v4->callingQmlContext(); - if (!setQmlProperty(v4, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) { + QQmlContextData *qmlContext = scope.engine->callingQmlContext(); + if (!setQmlProperty(scope.engine, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) { QQmlData *ddata = QQmlData::get(that->d()->object()); // Types created by QML are not extensible at run-time, but for other QObjects we can store them // as regular JavaScript properties, like on JavaScript objects. if (ddata && ddata->context) { QString error = QLatin1String("Cannot assign to non-existent property \"") + name->toQString() + QLatin1Char('\"'); - v4->throwError(error); + scope.engine->throwError(error); return false; } else { - return QV4::Object::put(m, name, value); + return QV4::Object::virtualPut(m, id, value, receiver); } } return true; } -PropertyAttributes QObjectWrapper::query(const Managed *m, String *name) +PropertyAttributes QObjectWrapper::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p) { - const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); - const QObject *thatObject = that->d()->object(); - if (QQmlData::wasDeleted(thatObject)) - return QV4::Object::query(m, name); + if (id.isString()) { + QObjectWrapper *that = static_cast<QObjectWrapper*>(m); + const QObject *thatObject = that->d()->object(); + if (!QQmlData::wasDeleted(thatObject)) { + Scope scope(m); + ScopedString n(scope, id.asStringOrSymbol()); + QQmlContextData *qmlContext = scope.engine->callingQmlContext(); + QQmlPropertyData local; + if (that->findProperty(scope.engine, qmlContext, n, IgnoreRevision, &local) + || n->equals(scope.engine->id_destroy()) || n->equals(scope.engine->id_toString())) { + if (p) { + // ### probably not the fastest implementation + bool hasProperty; + p->value = that->getQmlProperty(qmlContext, n, IgnoreRevision, &hasProperty, /*includeImports*/ true); + } + return QV4::Attr_Data; + } + } + } - ExecutionEngine *engine = that->engine(); - QQmlContextData *qmlContext = engine->callingQmlContext(); - QQmlPropertyData local; - if (that->findProperty(engine, qmlContext, name, IgnoreRevision, &local) - || name->equals(engine->id_destroy()) || name->equals(engine->id_toString())) - return QV4::Attr_Data; - else - return QV4::Object::query(m, name); + return QV4::Object::virtualGetOwnProperty(m, id, p); } -void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) +void QObjectWrapper::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); @@ -788,7 +811,7 @@ void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name return; } } - QV4::Object::advanceIterator(m, it, name, index, p, attributes); + QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes); } namespace QV4 { @@ -1619,6 +1642,7 @@ void CallArgument::initAsType(int callType) } } +#if QT_CONFIG(qml_sequence_object) template <class T, class M> void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M CallArgument::*member, bool &queryEngine) { @@ -1631,6 +1655,7 @@ void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M } } } +#endif void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value) { @@ -1685,7 +1710,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q uint length = array->getLength(); for (uint ii = 0; ii < length; ++ii) { QObject *o = nullptr; - qobjectWrapper = array->getIndexed(ii); + qobjectWrapper = array->get(ii); if (!!qobjectWrapper) o = qobjectWrapper->object(); qlistPtr->append(o); @@ -1713,6 +1738,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q type = callType; } else if (callType == QMetaType::Void) { *qvariantPtr = QVariant(); +#if QT_CONFIG(qml_sequence_object) } else if (callType == qMetaTypeId<std::vector<int>>() || callType == qMetaTypeId<std::vector<qreal>>() || callType == qMetaTypeId<std::vector<bool>>() @@ -1740,6 +1766,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q stdVectorQModelIndexPtr = nullptr; fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine); } +#endif } else { queryEngine = true; } @@ -1833,7 +1860,7 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine) ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index) { Scope valueScope(scope); - Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope)); + Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope)); method->d()->setObject(object); if (QQmlData *ddata = QQmlData::get(object)) @@ -1846,7 +1873,7 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index) { Scope valueScope(scope); - Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope)); + Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope)); method->d()->setPropertyCache(valueType->d()->propertyCache()); method->d()->index = index; method->d()->valueTypeWrapper.set(valueScope.engine, valueType->d()); @@ -1906,7 +1933,7 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionEngine *engine, c return Encode::undefined(); } -ReturnedValue QObjectMethod::call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc) +ReturnedValue QObjectMethod::virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc) { const QObjectMethod *This = static_cast<const QObjectMethod*>(m); return This->callInternal(thisObject, argv, argc); @@ -2019,7 +2046,7 @@ void Heap::QMetaObjectWrapper::ensureConstructorsCache() { ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) { QV4::Scope scope(engine); - Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocObject<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue()); + Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue()); mo->init(engine); return mo->asReturnedValue(); } @@ -2037,7 +2064,7 @@ void QMetaObjectWrapper::init(ExecutionEngine *) { } } -ReturnedValue QMetaObjectWrapper::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue QMetaObjectWrapper::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f); return This->constructInternal(argv, argc); @@ -2068,7 +2095,7 @@ ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) } Scoped<QMetaObjectWrapper> metaObject(scope, this); object->defineDefaultProperty(v4->id_constructor(), metaObject); - object->setPrototype(const_cast<QMetaObjectWrapper*>(this)); + object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this)); return object.asReturnedValue(); } @@ -2143,7 +2170,7 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine } } -bool QMetaObjectWrapper::isEqualTo(Managed *a, Managed *b) +bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b) { Q_ASSERT(a->as<QMetaObjectWrapper>()); QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>(); diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 1455acc1b3..7843289d33 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -183,7 +183,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object void destroyObject(bool lastCall); protected: - static bool isEqualTo(Managed *that, Managed *o); + static bool virtualIsEqualTo(Managed *that, Managed *o); static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true); static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value); @@ -193,10 +193,10 @@ protected: static QQmlPropertyData *findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local); QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static bool put(Managed *m, String *name, const Value &value); - static PropertyAttributes query(const Managed *, String *name); - static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); + static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); + static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static ReturnedValue method_connect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_disconnect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); @@ -237,7 +237,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine) const; QV4::ReturnedValue method_destroy(QV4::ExecutionEngine *ctx, const Value *args, int argc) const; - static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); ReturnedValue callInternal(const Value *thisObject, const Value *argv, int argc) const; @@ -251,11 +251,12 @@ struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject V4_NEEDS_DESTROY static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject); - static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); - static bool isEqualTo(Managed *a, Managed *b); - const QMetaObject *metaObject() const { return d()->metaObject; } +protected: + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static bool virtualIsEqualTo(Managed *a, Managed *b); + private: void init(ExecutionEngine *engine); ReturnedValue constructInternal(const Value *argv, int argc) const; diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp new file mode 100644 index 0000000000..20ea39d4e5 --- /dev/null +++ b/src/qml/jsruntime/qv4reflect.cpp @@ -0,0 +1,260 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4reflect_p.h" +#include "qv4symbol_p.h" +#include "qv4runtimeapi_p.h" +#include "qv4objectproto_p.h" +#include "qv4propertykey_p.h" + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(Reflect); + +void Heap::Reflect::init() +{ + Object::init(); + Scope scope(internalClass->engine); + ScopedObject r(scope, this); + + r->defineDefaultProperty(QStringLiteral("apply"), QV4::Reflect::method_apply, 3); + r->defineDefaultProperty(QStringLiteral("construct"), QV4::Reflect::method_construct, 2); + r->defineDefaultProperty(QStringLiteral("defineProperty"), QV4::Reflect::method_defineProperty, 3); + r->defineDefaultProperty(QStringLiteral("deleteProperty"), QV4::Reflect::method_deleteProperty, 2); + r->defineDefaultProperty(QStringLiteral("get"), QV4::Reflect::method_get, 2); + r->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), QV4::Reflect::method_getOwnPropertyDescriptor, 2); + r->defineDefaultProperty(QStringLiteral("getPrototypeOf"), QV4::Reflect::method_getPrototypeOf, 1); + r->defineDefaultProperty(QStringLiteral("has"), QV4::Reflect::method_has, 2); + r->defineDefaultProperty(QStringLiteral("isExtensible"), QV4::Reflect::method_isExtensible, 1); + r->defineDefaultProperty(QStringLiteral("ownKeys"), QV4::Reflect::method_ownKeys, 1); + r->defineDefaultProperty(QStringLiteral("preventExtensions"), QV4::Reflect::method_preventExtensions, 1); + r->defineDefaultProperty(QStringLiteral("set"), QV4::Reflect::method_set, 3); + r->defineDefaultProperty(QStringLiteral("setPrototypeOf"), QV4::Reflect::method_setPrototypeOf, 2); +} + +struct CallArgs { + Value *argv; + int argc; +}; + +static CallArgs createListFromArrayLike(Scope &scope, const Object *o) +{ + int len = o->getLength(); + Value *arguments = scope.alloc(len); + + for (int i = 0; i < len; ++i) { + arguments[i] = o->get(i); + if (scope.hasException()) + return { nullptr, 0 }; + } + return { arguments, len }; +} + +ReturnedValue Reflect::method_apply(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (argc < 3 || !argv[0].isFunctionObject() || !argv[2].isObject()) + return scope.engine->throwTypeError(); + + const Object *o = static_cast<const Object *>(argv + 2); + CallArgs arguments = createListFromArrayLike(scope, o); + if (scope.hasException()) + return Encode::undefined(); + + return static_cast<const FunctionObject &>(argv[0]).call(&argv[1], arguments.argv, arguments.argc); +} + +ReturnedValue Reflect::method_construct(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (argc < 2 || !argv[0].isFunctionObject() || !argv[1].isObject()) + return scope.engine->throwTypeError(); + + const Object *o = static_cast<const Object *>(argv + 1); + CallArgs arguments = createListFromArrayLike(scope, o); + if (scope.hasException()) + return Encode::undefined(); + + return static_cast<const FunctionObject &>(argv[0]).callAsConstructor(arguments.argv, arguments.argc); +} + +ReturnedValue Reflect::method_defineProperty(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject O(scope, argv[0]); + ScopedPropertyKey name(scope, (argc > 1 ? argv[1] : Primitive::undefinedValue()).toPropertyKey(scope.engine)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); + + ScopedValue attributes(scope, argc > 2 ? argv[2] : Primitive::undefinedValue()); + ScopedProperty pd(scope); + PropertyAttributes attrs; + ObjectPrototype::toPropertyDescriptor(scope.engine, attributes, pd, &attrs); + if (scope.engine->hasException) + return QV4::Encode::undefined(); + + bool result = O->defineOwnProperty(name, pd, attrs); + + return Encode(result); +} + +ReturnedValue Reflect::method_deleteProperty(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + ExecutionEngine *e = f->engine(); + if (!argc || !argv[0].isObject()) + return e->throwTypeError(); + + bool result = Runtime::method_deleteProperty(e, argv[0], argc > 1 ? argv[1] : Primitive::undefinedValue()); + return Encode(result); +} + +ReturnedValue Reflect::method_get(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, static_cast<const Object *>(argv)); + Value undef = Primitive::undefinedValue(); + const Value *index = argc > 1 ? &argv[1] : &undef; + ScopedPropertyKey name(scope, index->toPropertyKey(scope.engine)); + if (scope.hasException()) + return Encode::undefined(); + ScopedValue receiver(scope, argc > 2 ? argv[2] : *o); + + return Encode(o->get(name, receiver)); +} + +ReturnedValue Reflect::method_getOwnPropertyDescriptor(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + if (!argc || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + return ObjectPrototype::method_getOwnPropertyDescriptor(f, thisObject, argv, argc); +} + +ReturnedValue Reflect::method_getPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + if (!argc || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + const Object *o = static_cast<const Object *>(argv); + Heap::Object *p = o->getPrototypeOf(); + return (p ? p->asReturnedValue() : Encode::null()); +} + +ReturnedValue Reflect::method_has(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, static_cast<const Object *>(argv)); + Value undef = Primitive::undefinedValue(); + const Value *index = argc > 1 ? &argv[1] : &undef; + + ScopedPropertyKey name(scope, index->toPropertyKey(scope.engine)); + if (scope.engine->hasException) + return false; + + bool hasProperty = false; + (void) o->get(name, nullptr, &hasProperty); + return Encode(hasProperty); +} + +ReturnedValue Reflect::method_isExtensible(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + if (!argc || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + const Object *o = static_cast<const Object *>(argv); + return Encode(o->isExtensible()); +} + + +ReturnedValue Reflect::method_ownKeys(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + if (!argc || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + return ObjectPrototype::method_getOwnPropertyNames(f, thisObject, argv, argc); +} + +ReturnedValue Reflect::method_preventExtensions(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, static_cast<const Object *>(argv)); + return Encode(o->preventExtensions()); +} + +ReturnedValue Reflect::method_set(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, static_cast<const Object *>(argv)); + Value undef = Primitive::undefinedValue(); + const Value *index = argc > 1 ? &argv[1] : &undef; + const Value &val = argc > 2 ? argv[2] : undef; + ScopedValue receiver(scope, argc >3 ? argv[3] : argv[0]); + + ScopedPropertyKey propertyKey(scope, index->toPropertyKey(scope.engine)); + if (scope.engine->hasException) + return false; + bool result = o->put(propertyKey, val, receiver); + return Encode(result); +} + +ReturnedValue Reflect::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + if (argc < 2 || !argv[0].isObject() || (!argv[1].isNull() && !argv[1].isObject())) + return f->engine()->throwTypeError(); + + Scope scope(f); + ScopedObject o(scope, static_cast<const Object *>(argv)); + const Object *proto = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1); + return Encode(o->setPrototypeOf(proto)); +} diff --git a/src/qml/jsruntime/qv4reflect_p.h b/src/qml/jsruntime/qv4reflect_p.h new file mode 100644 index 0000000000..d480e1d914 --- /dev/null +++ b/src/qml/jsruntime/qv4reflect_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4REFLECT_H +#define QV4REFLECT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +struct Reflect : Object { + void init(); +}; + +} + +struct Reflect : Object { + V4_OBJECT2(Reflect, Object) + + static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_construct(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_deleteProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *, const Value *argv, int argc); + static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_ownKeys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 000e2c3a7e..8429b96baa 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -45,10 +45,6 @@ #include "qv4scopedvalue_p.h" #include "qv4jscall_p.h" -#include <private/qqmljsengine_p.h> -#include <private/qqmljslexer_p.h> -#include <private/qqmljsparser_p.h> -#include <private/qqmljsast_p.h> #include "private/qlocale_tools_p.h" #include <QtCore/QDebug> @@ -214,7 +210,7 @@ void Heap::RegExpCtor::clearLastMatch() lastMatchEnd = 0; } -ReturnedValue RegExpCtor::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc) +ReturnedValue RegExpCtor::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *) { Scope scope(fo->engine()); ScopedValue r(scope, argc ? argv[0] : Primitive::undefinedValue()); @@ -263,14 +259,14 @@ ReturnedValue RegExpCtor::callAsConstructor(const FunctionObject *fo, const Valu return Encode(scope.engine->newRegExpObject(regexp)); } -ReturnedValue RegExpCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue RegExpCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) { if (argc > 0 && argv[0].as<RegExpObject>()) { if (argc == 1 || argv[1].isUndefined()) return Encode(argv[0]); } - return callAsConstructor(f, argv, argc); + return virtualCallAsConstructor(f, argv, argc, f); } void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) @@ -280,7 +276,8 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) ScopedObject ctor(scope, constructor); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(2)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(2)); + ctor->addSymbolSpecies(); // Properties deprecated in the spec but required by "the web" :( ctor->defineAccessorProperty(QStringLiteral("lastMatch"), method_get_lastMatch_n<0>, nullptr); @@ -387,7 +384,7 @@ ReturnedValue RegExpPrototype::method_exec(const FunctionObject *b, const Value } // fill in result data - ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses[EngineBase::Class_RegExpExecArray], scope.engine->arrayPrototype())); + ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses(EngineBase::Class_RegExpExecArray))); int len = r->value()->captureCount(); array->arrayReserve(len); ScopedValue v(scope); @@ -442,12 +439,12 @@ ReturnedValue RegExpPrototype::method_compile(const FunctionObject *b, const Val return Encode::undefined(); } -template <int index> +template <uint index> ReturnedValue RegExpPrototype::method_get_lastMatch_n(const FunctionObject *b, const Value *, const Value *, int) { Scope scope(b); ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch()); - ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(index) : Encode::undefined()); + ScopedValue res(scope, lastMatch ? lastMatch->get(index) : Encode::undefined()); if (res->isUndefined()) res = scope.engine->newString(); return res->asReturnedValue(); @@ -457,7 +454,7 @@ ReturnedValue RegExpPrototype::method_get_lastParen(const FunctionObject *b, con { Scope scope(b); ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch()); - ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(lastMatch->getLength() - 1) : Encode::undefined()); + ScopedValue res(scope, lastMatch ? lastMatch->get(lastMatch->getLength() - 1) : Encode::undefined()); if (res->isUndefined()) res = scope.engine->newString(); return res->asReturnedValue(); diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 181628241b..0d4fe760eb 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -129,11 +129,11 @@ struct RegExpObject: Object { void initProperties(); int lastIndex() const { - Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex())); + Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()->propertyKey())); return propertyData(Index_LastIndex)->toInt32(); } void setLastIndex(int index) { - Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex())); + Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()->propertyKey())); return setProperty(Index_LastIndex, Primitive::fromInt32(index)); } @@ -152,8 +152,8 @@ struct RegExpCtor: FunctionObject int lastMatchStart() { return d()->lastMatchStart; } int lastMatchEnd() { return d()->lastMatchEnd; } - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct RegExpPrototype: RegExpObject @@ -165,7 +165,7 @@ struct RegExpPrototype: RegExpObject static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_compile(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - template <int index> + template <uint index> static ReturnedValue method_get_lastMatch_n(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_lastParen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_input(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 9729228511..f387285e37 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -61,6 +61,8 @@ #include <private/qqmlengine_p.h> #include <private/qqmljavascriptexpression_p.h> #include "qv4qobjectwrapper_p.h" +#include "qv4symbol_p.h" +#include "qv4generatorobject_p.h" #include <private/qv8engine_p.h> #endif @@ -314,37 +316,23 @@ ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId) QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId]; Q_ASSERT(clos); ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); + if (clos->isGenerator()) + return GeneratorFunction::create(current, clos)->asReturnedValue(); return FunctionObject::createScriptFunction(current, clos)->asReturnedValue(); } -bool Runtime::method_deleteElement(ExecutionEngine *engine, const Value &base, const Value &index) +bool Runtime::method_deleteProperty(ExecutionEngine *engine, const Value &base, const Value &index) { Scope scope(engine); - ScopedObject o(scope, base); - if (o) { - uint n = index.asArrayIndex(); - if (n < UINT_MAX) - return o->deleteIndexedProperty(n); - } - - ScopedString name(scope, index.toString(engine)); - return method_deleteMemberString(engine, base, name); -} - -bool Runtime::method_deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex) -{ - Scope scope(engine); - ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - return method_deleteMemberString(engine, base, name); -} - -bool Runtime::method_deleteMemberString(ExecutionEngine *engine, const Value &base, String *name) -{ - Scope scope(engine); - ScopedObject obj(scope, base.toObject(engine)); + ScopedObject o(scope, base.toObject(engine)); if (scope.engine->hasException) return Encode::undefined(); - return obj->deleteProperty(name); + Q_ASSERT(o); + + ScopedPropertyKey key(scope, index.toPropertyKey(engine)); + if (engine->hasException) + return false; + return o->deleteProperty(key); } bool Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) @@ -361,8 +349,16 @@ QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Val if (!rhs) return engine->throwTypeError(); - // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result. - return rhs->instanceOf(lval); + Scope scope(engine); + ScopedValue hasInstance(scope, rhs->get(engine->symbol_hasInstance())); + if (hasInstance->isUndefined()) + return rhs->instanceOf(lval); + FunctionObject *f = hasInstance->as<FunctionObject>(); + if (!f) + return engine->throwTypeError(); + + ScopedValue result(scope, f->call(&rval, &lval, 1)); + return Encode(result->toBoolean()); } QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right) @@ -371,7 +367,7 @@ QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left if (!ro) return engine->throwTypeError(); Scope scope(engine); - ScopedString s(scope, left.toString(engine)); + ScopedPropertyKey s(scope, left.toPropertyKey(engine)); if (scope.hasException()) return Encode::undefined(); bool r = ro->hasProperty(s); @@ -408,27 +404,59 @@ Heap::String *RuntimeHelpers::stringFromNumber(ExecutionEngine *engine, double n ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeHint) { - if (typeHint == PREFERREDTYPE_HINT) { - if (object->as<DateObject>()) - typeHint = STRING_HINT; - else - typeHint = NUMBER_HINT; + ExecutionEngine *engine = object->internalClass()->engine; + if (engine->hasException) + return Encode::undefined(); + + String *hint; + switch (typeHint) { + case STRING_HINT: + hint = engine->id_string(); + break; + case NUMBER_HINT: + hint = engine->id_number(); + break; + default: + hint = engine->id_default(); + break; } - ExecutionEngine *engine = object->internalClass()->engine; + Scope scope(engine); + ScopedFunctionObject toPrimitive(scope, object->get(engine->symbol_toPrimitive())); if (engine->hasException) return Encode::undefined(); + if (toPrimitive) { + ScopedValue result(scope, toPrimitive->call(object, hint, 1)); + if (engine->hasException) + return Encode::undefined(); + if (!result->isPrimitive()) + return engine->throwTypeError(); + return result->asReturnedValue(); + } + + if (hint == engine->id_default()) + hint = engine->id_number(); + return ordinaryToPrimitive(engine, object, hint); +} + + +ReturnedValue RuntimeHelpers::ordinaryToPrimitive(ExecutionEngine *engine, const Object *object, String *typeHint) +{ + Q_ASSERT(!engine->hasException); String *meth1 = engine->id_toString(); String *meth2 = engine->id_valueOf(); - if (typeHint == NUMBER_HINT) + if (typeHint->propertyKey() == engine->id_number()->propertyKey()) { qSwap(meth1, meth2); + } else { + Q_ASSERT(typeHint->propertyKey() == engine->id_string()->propertyKey()); + } Scope scope(engine); ScopedValue result(scope); - ScopedValue conv(scope, object->get(meth1)); + ScopedValue conv(scope, object->get(meth1)); if (FunctionObject *o = conv->as<FunctionObject>()) { result = o->call(object, nullptr, 0); if (result->isPrimitive()) @@ -449,19 +477,22 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH } - Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Value &value) { Q_ASSERT(!value.isObject()); switch (value.type()) { case Value::Undefined_Type: + engine->throwTypeError(QLatin1String("Value is undefined and could not be converted to an object")); + return nullptr; case Value::Null_Type: - engine->throwTypeError(); + engine->throwTypeError(QLatin1String("Value is null and could not be converted to an object")); return nullptr; case Value::Boolean_Type: return engine->newBooleanObject(value.booleanValue()); case Value::Managed_Type: - Q_ASSERT(value.isString()); + Q_ASSERT(value.isStringOrSymbol()); + if (!value.isString()) + return engine->newSymbolObject(value.symbolValue()); return engine->newStringObject(value.stringValue()); case Value::Integer_Type: default: // double @@ -488,6 +519,10 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, Value val case Value::Managed_Type: { if (value.isString()) return static_cast<const String &>(value).d(); + if (value.isSymbol()) { + engine->throwTypeError(QLatin1String("Cannot convert a symbol to a string.")); + return nullptr; + } value = Primitive::fromReturnedValue(RuntimeHelpers::toPrimitive(value, hint)); Q_ASSERT(value.isPrimitive()); if (value.isString()) @@ -539,14 +574,14 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu return Encode(x + y); } -bool Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) +void Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) { Scope scope(engine); - ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + QV4::Function *v4Function = engine->currentStackFrame->v4Function; + ScopedString name(scope, v4Function->compilationUnit->runtimeStrings[nameIndex]); ScopedObject o(scope, object.toObject(engine)); - if (!o) - return false; - return o->put(name, value); + if ((!o || !o->put(name, value)) && v4Function->isStrict()) + engine->throwTypeError(); } static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engine, const Value &object, uint idx) @@ -579,12 +614,13 @@ static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engin return v->asReturnedValue(); } - return o->getIndexed(idx); + return o->get(idx); } static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, const Value &object, const Value &index) { - Q_ASSERT(index.asArrayIndex() == UINT_MAX); + Q_ASSERT(!index.isPositiveInt()); + Scope scope(engine); ScopedObject o(scope, object); @@ -598,26 +634,18 @@ static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, Q_ASSERT(!!o); // can't fail as null/undefined is covered above } - ScopedString name(scope, index.toString(engine)); + ScopedPropertyKey name(scope, index.toPropertyKey(engine)); if (scope.hasException()) return Encode::undefined(); return o->get(name); } -/* load element: - - Managed *m = object.heapObject(); - if (m) - return m->internalClass->getIndexed(m, index); - return getIndexedFallback(object, index); -*/ - ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &object, const Value &index) { - uint idx = 0; - if (index.asArrayIndex(idx)) { + if (index.isPositiveInt()) { + uint idx = static_cast<uint>(index.int_32()); if (Heap::Base *b = object.heapObject()) { - if (b->vtable()->isObject) { + if (b->internalClass->vtable->isObject) { Heap::Object *o = static_cast<Heap::Object *>(b); if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); @@ -640,8 +668,8 @@ static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Val if (engine->hasException) return false; - uint idx = 0; - if (index.asArrayIndex(idx)) { + if (index.isPositiveInt()) { + uint idx = static_cast<uint>(index.int_32()); if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (idx < s->values.size) { @@ -649,53 +677,136 @@ static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Val return true; } } - return o->putIndexed(idx, value); + return o->put(idx, value); } - ScopedString name(scope, index.toString(engine)); + ScopedPropertyKey name(scope, index.toPropertyKey(engine)); + if (engine->hasException) + return false; return o->put(name, value); } -bool Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { - uint idx = 0; - if (index.asArrayIndex(idx)) { + if (index.isPositiveInt()) { + uint idx = static_cast<uint>(index.int_32()); if (Heap::Base *b = object.heapObject()) { - if (b->vtable()->isObject) { + if (b->internalClass->vtable->isObject) { Heap::Object *o = static_cast<Heap::Object *>(b); if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); if (idx < s->values.size) { s->setData(engine, idx, value); - return true; + return; } } } } } - return setElementFallback(engine, object, index, value); + if (!setElementFallback(engine, object, index, value) && engine->currentStackFrame->v4Function->isStrict()) + engine->throwTypeError(); } -ReturnedValue Runtime::method_foreachIterator(ExecutionEngine *engine, const Value &in) +ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value &in, int iterator) { Scope scope(engine); ScopedObject o(scope, (Object *)nullptr); if (!in.isNullOrUndefined()) o = in.toObject(engine); - return engine->newForEachIteratorObject(o)->asReturnedValue(); + if (engine->hasException) + return Encode::undefined(); + if (iterator) { + if (!o) + return engine->throwTypeError(); + ScopedFunctionObject f(scope, o->get(engine->symbol_iterator())); + if (!f) + return engine->throwTypeError(); + JSCallData cData(scope, 0, nullptr, o); + ScopedObject it(scope, f->call(cData)); + if (!it) + return engine->throwTypeError(); + return it->asReturnedValue(); + } + return engine->newForInIteratorObject(o)->asReturnedValue(); } -ReturnedValue Runtime::method_foreachNextPropertyName(const Value &foreach_iterator) +ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value &iterator, Value *value) { - Q_ASSERT(foreach_iterator.isObject()); + Q_ASSERT(iterator.isObject()); - ForEachIteratorObject *it = static_cast<ForEachIteratorObject *>(foreach_iterator.objectValue()); - Q_ASSERT(it->as<ForEachIteratorObject>()); + Scope scope(engine); + ScopedFunctionObject f(scope, static_cast<const Object &>(iterator).get(engine->id_next())); + if (!f) + return engine->throwTypeError(); + JSCallData cData(scope, 0, nullptr, &iterator); + ScopedObject o(scope, f->call(cData)); + if (!o) + return engine->throwTypeError(); + ScopedValue d(scope, o->get(engine->id_done())); + bool done = d->toBoolean(); + if (done) { + *value = Encode::undefined(); + } else { + *value = o->get(engine->id_value()); + } + return Encode(done); +} - return it->nextPropertyName(); +ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value &iterator, const Value &done) +{ + Q_ASSERT(iterator.isObject()); + Q_ASSERT(done.isBoolean()); + if (done.booleanValue()) + return Encode::undefined(); + + Scope scope(engine); + bool hadException = engine->hasException; + ScopedValue e(scope); + if (hadException) { + e = *engine->exceptionValue; + engine->hasException = false; + } + ScopedFunctionObject f(scope, static_cast<const Object &>(iterator).get(engine->id_return())); + ScopedObject o(scope); + if (f) { + JSCallData cData(scope, 0, nullptr, &iterator); + o = f->call(cData); + } + if (hadException || !f) { + *engine->exceptionValue = e; + engine->hasException = hadException; + return Encode::undefined(); + } + if (engine->hasException) + return Encode::undefined(); + + if (!o) + return engine->throwTypeError(); + return Encode::undefined(); } +ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, const Value &iterator) +{ + Q_ASSERT(iterator.isObject()); + + Scope scope(engine); + ScopedArrayObject array(scope, engine->newArrayObject()); + array->arrayCreate(); + uint index = 0; + while (1) { + ScopedValue n(scope); + ScopedValue done(scope, method_iteratorNext(engine, iterator, n)); + if (engine->hasException) + return Encode::undefined(); + Q_ASSERT(done->isBoolean()); + if (done->booleanValue()) + break; + array->arraySet(index, n); + ++index; + } + return array->asReturnedValue(); +} void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, const Value &value) { @@ -745,6 +856,42 @@ ReturnedValue Runtime::method_loadName(ExecutionEngine *engine, int nameIndex) return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name); } +ReturnedValue Runtime::method_loadSuperProperty(ExecutionEngine *engine, const Value &property) +{ + Scope scope(engine); + ScopedObject base(scope, engine->currentStackFrame->thisObject()); + if (!base) + return engine->throwTypeError(); + ScopedObject proto(scope, base->getPrototypeOf()); + if (!proto) + return engine->throwTypeError(); + ScopedPropertyKey key(scope, property.toPropertyKey(engine)); + if (engine->hasException) + return Encode::undefined(); + return proto->get(key, base); +} + +void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &property, const Value &value) +{ + Scope scope(engine); + ScopedObject base(scope, engine->currentStackFrame->thisObject()); + if (!base) { + engine->throwTypeError(); + return; + } + ScopedObject proto(scope, base->getPrototypeOf()); + if (!proto) { + engine->throwTypeError(); + return; + } + ScopedPropertyKey key(scope, property.toPropertyKey(engine)); + if (engine->hasException) + return; + bool result = proto->put(key, value, base); + if (!result && engine->currentStackFrame->v4Function->isStrict()) + engine->throwTypeError(); +} + #endif // V4_BOOTSTRAP uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) @@ -1033,24 +1180,31 @@ ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, V ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc) { Scope scope(engine); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + ScopedObject lookupObject(scope, base); - if (!base->isObject()) { + if (!lookupObject) { Q_ASSERT(!base->isEmpty()); if (base->isNullOrUndefined()) { QString message = QStringLiteral("Cannot call method '%1' of %2") - .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(), - base->toQStringNoThrow()); + .arg(name->toQString(), base->toQStringNoThrow()); return engine->throwTypeError(message); } - ScopedValue thisObject(scope, RuntimeHelpers::convertToObject(engine, *base)); - if (engine->hasException) // type error - return Encode::undefined(); - base = thisObject; + if (base->isManaged()) { + Managed *m = static_cast<Managed *>(base); + lookupObject = m->internalClass()->prototype; + Q_ASSERT(m->internalClass()->prototype); + } else { + lookupObject = RuntimeHelpers::convertToObject(engine, *base); + if (engine->hasException) // type error + return Encode::undefined(); + if (!engine->currentStackFrame->v4Function->isStrict()) + base = lookupObject; + } } - ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(name)); + ScopedFunctionObject f(scope, static_cast<Object *>(lookupObject)->get(name)); if (!f) { QString error = QStringLiteral("Property '%1' of object %2 is not a function") @@ -1080,7 +1234,7 @@ ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base, ScopedValue thisObject(scope, base->toObject(engine)); base = thisObject; - ScopedString str(scope, index.toString(engine)); + ScopedPropertyKey str(scope, index.toPropertyKey(engine)); if (engine->hasException) return Encode::undefined(); @@ -1130,12 +1284,80 @@ ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engi return fo->call(qmlContextValue, argv, argc); } -ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, Value *argv, int argc) +struct CallArgs { + Value *argv; + int argc; +}; + +static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc) +{ + ScopedValue it(scope); + ScopedValue done(scope); + + int argCount = 0; + + Value *v = scope.alloc<Scope::Uninitialized>(); + Value *arguments = v; + for (int i = 0; i < argc; ++i) { + if (!argv[i].isEmpty()) { + *v = argv[i]; + ++argCount; + v = scope.alloc<Scope::Uninitialized>(); + continue; + } + // spread element + ++i; + it = Runtime::method_getIterator(scope.engine, argv[i], /* ForInIterator */ 1); + if (scope.engine->hasException) + return { nullptr, 0 }; + while (1) { + done = Runtime::method_iteratorNext(scope.engine, it, v); + if (scope.engine->hasException) + return { nullptr, 0 }; + Q_ASSERT(done->isBoolean()); + if (done->booleanValue()) + break; + ++argCount; + v = scope.alloc<Scope::Uninitialized>(); + } + } + return { arguments, argCount }; +} + +ReturnedValue Runtime::method_callWithSpread(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc) +{ + Q_ASSERT(argc >= 1); + if (!function.isFunctionObject()) + return engine->throwTypeError(); + + Scope scope(engine); + CallArgs arguments = createSpreadArguments(scope, argv, argc); + if (engine->hasException) + return Encode::undefined(); + + return static_cast<const FunctionObject &>(function).call(&thisObject, arguments.argv, arguments.argc); +} + +ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) { if (!function.isFunctionObject()) return engine->throwTypeError(); - return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc); + return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc, &newTarget); +} + +ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) +{ + Q_UNIMPLEMENTED(); + if (!function.isFunctionObject()) + return engine->throwTypeError(); + + Scope scope(engine); + CallArgs arguments = createSpreadArguments(scope, argv, argc); + if (engine->hasException) + return Encode::undefined(); + + return static_cast<const FunctionObject &>(function).callAsConstructor(arguments.argv, arguments.argc, &newTarget); } void Runtime::method_throwException(ExecutionEngine *engine, const Value &value) @@ -1161,6 +1383,8 @@ ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value & case Value::Managed_Type: if (value.isString()) res = engine->id_string(); + else if (value.isSymbol()) + res = engine->id_symbol(); else if (value.objectValue()->as<FunctionObject>()) res = engine->id_function(); else @@ -1183,25 +1407,54 @@ QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameI return method_typeofValue(engine, prop); } -/* The next three methods are a bit tricky. They can't open up a Scope, as that - * would mess up the pushing of the context. - * - * Instead the push/pop pair acts as a non local scope. - */ -ReturnedValue Runtime::method_createWithContext(ExecutionContext *parent, const Value &o) +ReturnedValue Runtime::method_createWithContext(ExecutionEngine *engine, Value *jsStackFrame) { - Q_ASSERT(o.isObject()); - const Object &obj = static_cast<const Object &>(o); - return parent->newWithContext(obj.d())->asReturnedValue(); + QV4::Value &accumulator = jsStackFrame[CallData::Accumulator]; + accumulator = accumulator.toObject(engine); + if (engine->hasException) + return Encode::undefined(); + Q_ASSERT(accumulator.isObject()); + const Object &obj = static_cast<const Object &>(accumulator); + ExecutionContext *context = static_cast<ExecutionContext *>(jsStackFrame + CallData::Context); + return context->newWithContext(obj.d())->asReturnedValue(); } -ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int exceptionVarNameIndex) +ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex) { ExecutionEngine *e = parent->engine(); - return parent->newCatchContext(e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex], - e->catchException(nullptr))->asReturnedValue(); + return parent->newCatchContext(e->currentStackFrame, blockIndex, + e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex])->asReturnedValue(); } +ReturnedValue Runtime::method_createBlockContext(ExecutionContext *parent, int index) +{ + ExecutionEngine *e = parent->engine(); + return parent->newBlockContext(e->currentStackFrame, index)->asReturnedValue(); +} + +ReturnedValue Runtime::method_cloneBlockContext(ExecutionContext *previous) +{ + return ExecutionContext::cloneBlockContext(static_cast<Heap::CallContext *>(previous->d()))->asReturnedValue(); +} + + +ReturnedValue Runtime::method_createScriptContext(ExecutionEngine *engine, int index) +{ + Q_ASSERT(engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_GlobalContext || + engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_QmlContext); + ReturnedValue c = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue(); + engine->setScriptContext(c); + return c; +} + +ReturnedValue Runtime::method_popScriptContext(ExecutionEngine *engine) +{ + ReturnedValue root = engine->rootContext()->asReturnedValue(); + engine->setScriptContext(root); + return root; +} + + void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex) { Scope scope(engine); @@ -1214,61 +1467,160 @@ ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *value return engine->newArrayObject(values, length)->asReturnedValue(); } -ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags) +ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId, int argc, const QV4::Value *args) { Scope scope(engine); - QV4::InternalClass *klass = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeClasses[classId]; - ScopedObject o(scope, engine->newObject(klass, engine->objectPrototype())); + Scoped<InternalClass> klass(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeClasses[classId]); + ScopedObject o(scope, engine->newObject(klass->d())); - { - bool needSparseArray = arrayGetterSetterCountAndFlags >> 30; - if (needSparseArray) - o->initSparseArray(); - } + Q_ASSERT(uint(argc) >= klass->d()->size); - for (uint i = 0; i < klass->size; ++i) + for (uint i = 0; i < klass->d()->size; ++i) o->setProperty(i, *args++); - if (arrayValueCount > 0) { - ScopedValue entry(scope); - for (int i = 0; i < arrayValueCount; ++i) { - uint idx = args->toUInt32(); - ++args; - entry = *args++; - o->arraySet(idx, entry); + Q_ASSERT((argc - klass->d()->size) % 3 == 0); + int additionalArgs = (argc - int(klass->d()->size))/3; + + if (!additionalArgs) + return o->asReturnedValue(); + + ScopedPropertyKey name(scope); + ScopedProperty pd(scope); + for (int i = 0; i < additionalArgs; ++i) { + Q_ASSERT(args->isInteger()); + ObjectLiteralArgument arg = ObjectLiteralArgument(args->integerValue()); + name = args[1].toPropertyKey(engine); + if (engine->hasException) + return Encode::undefined(); + Q_ASSERT(arg == ObjectLiteralArgument::Value || args[2].isFunctionObject()); + if (arg == ObjectLiteralArgument::Value || arg == ObjectLiteralArgument::Getter) { + pd->value = args[2]; + pd->set = Primitive::emptyValue(); + } else { + pd->value = Primitive::emptyValue(); + pd->set = args[2]; } + bool ok = o->defineOwnProperty(name, pd, (arg == ObjectLiteralArgument::Value ? Attr_Data : Attr_Accessor)); + if (!ok) + return engine->throwTypeError(); + + args += 3; } + return o.asReturnedValue(); +} + +ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classIndex, const Value &superClass, const Value *computedNames) +{ + const CompiledData::CompilationUnit *unit = engine->currentStackFrame->v4Function->compilationUnit; + const QV4::CompiledData::Class *cls = unit->data->classAt(classIndex); - uint arrayGetterSetterCount = arrayGetterSetterCountAndFlags & ((1 << 30) - 1); - if (arrayGetterSetterCount > 0) { - ScopedProperty pd(scope); - for (uint i = 0; i < arrayGetterSetterCount; ++i) { - uint idx = args->toUInt32(); - ++args; - pd->value = *args; - ++args; - pd->set = *args; - ++args; - o->arraySet(idx, pd, Attr_Accessor); + Scope scope(engine); + ScopedObject protoParent(scope, engine->objectPrototype()); + ScopedObject constructorParent(scope, engine->functionPrototype()); + if (!superClass.isEmpty()) { + if (superClass.isNull()) { + protoParent = Encode::null(); + } else { + // ### check that the heritage object is a constructor + if (!superClass.isFunctionObject()) + return engine->throwTypeError(QStringLiteral("The superclass is not a function object.")); + const FunctionObject *s = static_cast<const FunctionObject *>(&superClass); + ScopedValue result(scope, s->get(scope.engine->id_prototype())); + if (!result->isObject() && !result->isNull()) + return engine->throwTypeError(QStringLiteral("The value of the superclass's prototype property is not an object.")); + protoParent = *result; + constructorParent = superClass; } } - return o.asReturnedValue(); + ScopedObject proto(scope, engine->newObject()); + proto->setPrototypeUnchecked(protoParent); + ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); + + ScopedFunctionObject constructor(scope); + QV4::Function *f = cls->constructorFunction != UINT_MAX ? unit->runtimeFunctions[cls->constructorFunction] : nullptr; + constructor = FunctionObject::createConstructorFunction(current, f, !superClass.isEmpty())->asReturnedValue(); + constructor->setPrototypeUnchecked(constructorParent); + constructor->defineDefaultProperty(engine->id_prototype(), proto); + proto->defineDefaultProperty(engine->id_constructor(), constructor); + + ScopedString name(scope); + if (cls->nameIndex != UINT_MAX) { + name = unit->runtimeStrings[cls->nameIndex]; + constructor->defineReadonlyConfigurableProperty(engine->id_name(), name); + } + + ScopedObject receiver(scope, *constructor); + ScopedPropertyKey propertyName(scope); + ScopedFunctionObject function(scope); + ScopedProperty property(scope); + const CompiledData::Method *methods = cls->methodTable(); + for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) { + if (i == cls->nStaticMethods) + receiver = proto; + if (methods[i].name == UINT_MAX) { + propertyName = computedNames->toPropertyKey(engine); + if (engine->hasException) + return Encode::undefined(); + ++computedNames; + } else { + name = unit->runtimeStrings[methods[i].name]; + propertyName = name->toPropertyKey(); + } + QV4::Function *f = unit->runtimeFunctions[methods[i].function]; + Q_ASSERT(f); + if (f->isGenerator()) + function = MemberGeneratorFunction::create(current, f); + else + function = FunctionObject::createMemberFunction(current, f); + Q_ASSERT(function); + PropertyAttributes attributes; + switch (methods[i].type) { + case CompiledData::Method::Getter: + property->setGetter(function); + property->set = Primitive::emptyValue(); + attributes = Attr_Accessor|Attr_NotEnumerable; + break; + case CompiledData::Method::Setter: + property->value = Primitive::emptyValue(); + property->setSetter(function); + attributes = Attr_Accessor|Attr_NotEnumerable; + break; + default: // Regular + property->value = function; + property->set = Primitive::emptyValue(); + attributes = Attr_Data|Attr_NotEnumerable; + break; + } + receiver->defineOwnProperty(propertyName, property, attributes); + } + + return constructor->asReturnedValue(); } QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine) { Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext); - QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_ArgumentsObject]; - return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue(); + Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_ArgumentsObject); + return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue(); } QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine *engine) { - QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_StrictArgumentsObject]; - return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue(); + Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_StrictArgumentsObject); + return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue(); +} + +QV4::ReturnedValue Runtime::method_createRestParameter(ExecutionEngine *engine, int argIndex) +{ + const Value *values = engine->currentStackFrame->originalArguments + argIndex; + int nValues = engine->currentStackFrame->originalArgumentsCount - argIndex; + if (nValues <= 0) + return engine->newArrayObject(0)->asReturnedValue(); + return engine->newArrayObject(values, nValues)->asReturnedValue(); } + ReturnedValue Runtime::method_loadQmlContext(NoThrowEngine *engine) { Heap::QmlContext *ctx = engine->qmlContext(); @@ -1485,24 +1837,133 @@ ReturnedValue Runtime::method_lessEqual(const Value &left, const Value &right) return Encode(r); } +struct LazyScope +{ + ExecutionEngine *engine = nullptr; + Value *stackMark = nullptr; + ~LazyScope() { + if (engine) + engine->jsStackTop = stackMark; + } + template <typename T> + void set(Value **scopedValue, T value, ExecutionEngine *e) { + if (!engine) { + engine = e; + stackMark = engine->jsStackTop; + } + if (!*scopedValue) + *scopedValue = e->jsAlloca(1); + **scopedValue = value; + } +}; + Bool Runtime::method_compareEqual(const Value &left, const Value &right) { TRACE2(left, right); - if (left.rawValue() == right.rawValue()) - // NaN != NaN - return !left.isNaN(); + Value lhs = left; + Value rhs = right; - if (left.type() == right.type()) { - if (left.isDouble() && left.doubleValue() == 0 && right.doubleValue() == 0) - return true; // this takes care of -0 == +0 (which obviously have different raw values) - if (!left.isManaged()) - return false; - if (left.isString() == right.isString()) - return left.cast<Managed>()->isEqualTo(right.cast<Managed>()); +#ifndef V4_BOOTSTRAP + LazyScope scope; + Value *lhsGuard = nullptr; + Value *rhsGuard = nullptr; +#endif + + redo: + if (lhs.asReturnedValue() == rhs.asReturnedValue()) + return !lhs.isNaN(); + + int lt = lhs.quickType(); + int rt = rhs.quickType(); + if (rt < lt) { + qSwap(lhs, rhs); + qSwap(lt, rt); } - return RuntimeHelpers::equalHelper(left, right); + switch (lt) { + case QV4::Value::QT_ManagedOrUndefined: + if (lhs.isUndefined()) + return rhs.isNullOrUndefined(); + Q_FALLTHROUGH(); + case QV4::Value::QT_ManagedOrUndefined1: + case QV4::Value::QT_ManagedOrUndefined2: + case QV4::Value::QT_ManagedOrUndefined3: + // LHS: Managed + switch (rt) { + case QV4::Value::QT_ManagedOrUndefined: + if (rhs.isUndefined()) + return false; + Q_FALLTHROUGH(); + case QV4::Value::QT_ManagedOrUndefined1: + case QV4::Value::QT_ManagedOrUndefined2: + case QV4::Value::QT_ManagedOrUndefined3: { +#ifndef V4_BOOTSTRAP + // RHS: Managed + Heap::Base *l = lhs.m(); + Heap::Base *r = rhs.m(); + Q_ASSERT(l); + Q_ASSERT(r); + if (l->internalClass->vtable->isStringOrSymbol == r->internalClass->vtable->isStringOrSymbol) + return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs)); + if (l->internalClass->vtable->isStringOrSymbol) { + scope.set(&rhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT), r->internalClass->engine); + rhs = rhsGuard->asReturnedValue(); + break; + } else { + Q_ASSERT(r->internalClass->vtable->isStringOrSymbol); + scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), l->internalClass->engine); + lhs = lhsGuard->asReturnedValue(); + break; + } +#endif + return false; + } + case QV4::Value::QT_Empty: + Q_UNREACHABLE(); + case QV4::Value::QT_Null: + return false; + case QV4::Value::QT_Bool: + case QV4::Value::QT_Int: + rhs = Primitive::fromDouble(rhs.int_32()); + // fall through + default: // double +#ifndef V4_BOOTSTRAP + if (lhs.m()->internalClass->vtable->isStringOrSymbol) { + return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false; + } else { + scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), lhs.m()->internalClass->engine); + lhs = lhsGuard->asReturnedValue(); + } +#else + Q_UNIMPLEMENTED(); +#endif + } + goto redo; + case QV4::Value::QT_Empty: + Q_UNREACHABLE(); + case QV4::Value::QT_Null: + return rhs.isNull(); + case QV4::Value::QT_Bool: + case QV4::Value::QT_Int: + switch (rt) { + case QV4::Value::QT_ManagedOrUndefined: + case QV4::Value::QT_ManagedOrUndefined1: + case QV4::Value::QT_ManagedOrUndefined2: + case QV4::Value::QT_ManagedOrUndefined3: + case QV4::Value::QT_Empty: + case QV4::Value::QT_Null: + Q_UNREACHABLE(); + case QV4::Value::QT_Bool: + case QV4::Value::QT_Int: + return lhs.int_32() == rhs.int_32(); + default: // double + return lhs.int_32() == rhs.doubleValue(); + } + default: // double + Q_ASSERT(rhs.isDouble()); + return lhs.doubleValue() == rhs.doubleValue(); + } } ReturnedValue Runtime::method_equal(const Value &left, const Value &right) diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 3a26c23990..72af90d1dc 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -100,6 +100,7 @@ enum TypeHint { struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { static ReturnedValue objectDefaultValue(const Object *object, int typeHint); static ReturnedValue toPrimitive(const Value &value, TypeHint typeHint); + static ReturnedValue ordinaryToPrimitive(ExecutionEngine *engine, const Object *object, String *typeHint); static double stringToNumber(const QString &s); static Heap::String *stringFromNumber(ExecutionEngine *engine, double number); diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 91232256a9..3f65e6c6d6 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -99,33 +99,39 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { F(ReturnedValue, callElement, (ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc)) \ F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \ F(ReturnedValue, callPossiblyDirectEval, (ExecutionEngine *engine, Value *argv, int argc)) \ + F(ReturnedValue, callWithSpread, (ExecutionEngine *engine, const Value &func, const Value &thisObject, Value *argv, int argc)) \ \ /* construct */ \ - F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \ + F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \ + F(ReturnedValue, constructWithSpread, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \ \ /* load & store */ \ F(void, storeNameStrict, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ F(void, storeNameSloppy, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ - F(bool, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \ - F(bool, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \ + F(void, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \ + F(void, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \ F(ReturnedValue, loadProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \ F(ReturnedValue, loadName, (ExecutionEngine *engine, int nameIndex)) \ F(ReturnedValue, loadElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \ + F(ReturnedValue, loadSuperProperty, (ExecutionEngine *engine, const Value &property)) \ + F(void, storeSuperProperty, (ExecutionEngine *engine, const Value &property, const Value &value)) \ \ /* typeof */ \ F(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)) \ F(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)) \ \ /* delete */ \ - F(bool, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \ - F(bool, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \ - F(bool, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name)) \ + F(bool, deleteProperty, (ExecutionEngine *engine, const Value &base, const Value &index)) \ F(bool, deleteName, (ExecutionEngine *engine, int nameIndex)) \ \ /* exceptions & scopes */ \ F(void, throwException, (ExecutionEngine *engine, const Value &value)) \ - F(ReturnedValue, createWithContext, (ExecutionContext *parent, const Value &o)) \ - F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int exceptionVarNameIndex)) \ + F(ReturnedValue, createWithContext, (ExecutionEngine *, Value *jsStackFrame)) \ + F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex)) \ + F(ReturnedValue, createBlockContext, (ExecutionContext *parent, int index)) \ + F(ReturnedValue, createScriptContext, (ExecutionEngine *engine, int index)) \ + F(ReturnedValue, cloneBlockContext, (ExecutionContext *previous)) \ + F(ReturnedValue, popScriptContext, (ExecutionEngine *engine)) \ \ /* closures */ \ F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \ @@ -134,14 +140,18 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { F(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)) \ F(ReturnedValue, createMappedArgumentsObject, (ExecutionEngine *engine)) \ F(ReturnedValue, createUnmappedArgumentsObject, (ExecutionEngine *engine)) \ + F(ReturnedValue, createRestParameter, (ExecutionEngine *engine, int argIndex)) \ \ /* literals */ \ F(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)) \ - F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)) \ - \ - /* foreach */ \ - F(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in)) \ - F(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator)) \ + F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, int classId, int argc, const Value *args)) \ + F(ReturnedValue, createClass, (ExecutionEngine *engine, int classIndex, const Value &heritage, const Value *computedNames)) \ + \ + /* for-in, for-of and array destructuring */ \ + F(ReturnedValue, getIterator, (ExecutionEngine *engine, const Value &in, int iterator)) \ + F(ReturnedValue, iteratorNext, (ExecutionEngine *engine, const Value &iterator, Value *value)) \ + F(ReturnedValue, iteratorClose, (ExecutionEngine *engine, const Value &iterator, const Value &done)) \ + F(ReturnedValue, destructureRestElement, (ExecutionEngine *engine, const Value &iterator)) \ \ /* unary operators */ \ F(ReturnedValue, uMinus, (const Value &value)) \ diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp index fe18ddf9ed..9866966936 100644 --- a/src/qml/jsruntime/qv4runtimecodegen.cpp +++ b/src/qml/jsruntime/qv4runtimecodegen.cpp @@ -52,13 +52,16 @@ void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName, _module->finalUrl = fileName; _context = nullptr; - Compiler::ScanFunctions scan(this, sourceCode, Compiler::GlobalCode); + Compiler::ScanFunctions scan(this, sourceCode, Compiler::ContextType::Global); // fake a global environment - scan.enterEnvironment(nullptr, Compiler::FunctionCode); + scan.enterEnvironment(nullptr, Compiler::ContextType::Function, QString()); scan(ast); scan.leaveEnvironment(); - int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : nullptr); + if (hasError) + return; + + int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body); _module->rootContext = _module->functions.at(index); } diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index bb20f384b3..1f66c4a47e 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -53,6 +53,7 @@ #include "qv4engine_p.h" #include "qv4value_p.h" #include "qv4property_p.h" +#include "qv4propertykey_p.h" #ifdef V4_USE_VALGRIND #include <valgrind/memcheck.h> @@ -117,8 +118,45 @@ struct Scope { engine->jsStackTop = mark; } - QML_NEARLY_ALWAYS_INLINE Value *alloc(int nValues) const { - return engine->jsAlloca(nValues); + enum AllocMode { + Undefined, + Empty, + /* Be careful when using Uninitialized, the stack has to be fully initialized before calling into the memory manager again */ + Uninitialized + }; + template <AllocMode mode = Undefined> + QML_NEARLY_ALWAYS_INLINE Value *alloc(int nValues) const + { + Value *ptr = engine->jsAlloca(nValues); + switch (mode) { + case Undefined: + for (int i = 0; i < nValues; ++i) + ptr[i] = Primitive::undefinedValue(); + break; + case Empty: + for (int i = 0; i < nValues; ++i) + ptr[i] = Primitive::emptyValue(); + break; + case Uninitialized: + break; + } + return ptr; + } + template <AllocMode mode = Undefined> + QML_NEARLY_ALWAYS_INLINE Value *alloc() const + { + Value *ptr = engine->jsAlloca(1); + switch (mode) { + case Undefined: + *ptr = Primitive::undefinedValue(); + break; + case Empty: + *ptr = Primitive::emptyValue(); + break; + case Uninitialized: + break; + } + return ptr; } bool hasException() const { @@ -136,31 +174,31 @@ struct ScopedValue { ScopedValue(const Scope &scope) { - ptr = scope.engine->jsStackTop++; + ptr = scope.alloc<Scope::Uninitialized>(); ptr->setRawValue(0); } ScopedValue(const Scope &scope, const Value &v) { - ptr = scope.engine->jsStackTop++; + ptr = scope.alloc<Scope::Uninitialized>(); *ptr = v; } ScopedValue(const Scope &scope, Heap::Base *o) { - ptr = scope.engine->jsStackTop++; + ptr = scope.alloc<Scope::Uninitialized>(); ptr->setM(o); } ScopedValue(const Scope &scope, Managed *m) { - ptr = scope.engine->jsStackTop++; + ptr = scope.alloc<Scope::Uninitialized>(); ptr->setRawValue(m->asReturnedValue()); } ScopedValue(const Scope &scope, const ReturnedValue &v) { - ptr = scope.engine->jsStackTop++; + ptr = scope.alloc<Scope::Uninitialized>(); ptr->setRawValue(v); } @@ -203,6 +241,37 @@ struct ScopedValue Value *ptr; }; + +struct ScopedPropertyKey +{ + ScopedPropertyKey(const Scope &scope) + { + ptr = reinterpret_cast<PropertyKey *>(scope.alloc<Scope::Uninitialized>()); + *ptr = PropertyKey::invalid(); + } + + ScopedPropertyKey(const Scope &scope, const PropertyKey &v) + { + ptr = reinterpret_cast<PropertyKey *>(scope.alloc<Scope::Uninitialized>()); + *ptr = v; + } + + ScopedPropertyKey &operator=(const PropertyKey &other) { + *ptr = other; + return *this; + } + + PropertyKey *operator->() { + return ptr; + } + operator PropertyKey() const { + return *ptr; + } + + PropertyKey *ptr; +}; + + template<typename T> struct Scoped { @@ -214,66 +283,66 @@ struct Scoped QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Undefined>(); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value &v) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(v.as<T>()); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, Heap::Base *o) { Value v; v = o; - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(v.as<T>()); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ScopedValue &v) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(v.ptr->as<T>()); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value &v, ConvertType) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); ptr->setRawValue(value_convert<T>(scope.engine, v)); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value *v) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(v ? v->as<T>() : nullptr); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, T *t) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(t); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const T *t) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(t); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, typename T::Data *t) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); *ptr = t; } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ReturnedValue &v) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(QV4::Value::fromReturnedValue(v).as<T>()); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ReturnedValue &v, ConvertType) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); ptr->setRawValue(value_convert<T>(scope.engine, QV4::Value::fromReturnedValue(v))); } diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 5cd62c90f1..37c4f27ca9 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -57,10 +57,11 @@ #include <QtCore/QDebug> #include <QtCore/QString> +#include <QScopedValueRollback> using namespace QV4; -Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit) +Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit) : line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false) , compilationUnit(compilationUnit), vmFunction(nullptr), parseAsBinding(true) { @@ -128,7 +129,7 @@ void Script::parse() RuntimeCodegen cg(v4, &jsGenerator, strictMode); if (inheritContext) cg.setUseFastLookups(false); - cg.generateFromProgram(sourceFile, sourceFile, sourceCode, program, &module, compilationMode); + cg.generateFromProgram(sourceFile, sourceFile, sourceCode, program, &module, contextType); if (v4->hasException) return; @@ -154,7 +155,7 @@ ReturnedValue Script::run(const QV4::Value *thisObject) QV4::Scope valueScope(engine); if (qmlContext.isUndefined()) { - TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction); + QScopedValueRollback<Function*> savedGlobalCode(engine->globalCode, vmFunction); return vmFunction->call(thisObject ? thisObject : engine->globalObject, nullptr, 0, context); @@ -171,19 +172,16 @@ Function *Script::function() return vmFunction; } -QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator, +QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator, const QString &fileName, const QString &finalUrl, const QString &source, - QList<QQmlError> *reportedErrors, Directives *directivesCollector) + QList<QQmlError> *reportedErrors) { using namespace QV4::Compiler; using namespace QQmlJS::AST; - Engine ee; - if (directivesCollector) - ee.setDirectives(directivesCollector); - Lexer lexer(&ee); + Lexer lexer(jsEngine); lexer.setCode(source, /*line*/1, /*qml mode*/false); - Parser parser(&ee); + Parser parser(jsEngine); parser.parseProgram(); @@ -219,7 +217,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compi Codegen cg(unitGenerator, /*strict mode*/false); cg.setUseFastLookups(false); - cg.generateFromProgram(fileName, finalUrl, source, program, module, GlobalCode); + cg.generateFromProgram(fileName, finalUrl, source, program, module, ContextType::Global); errors = cg.qmlErrors(); if (!errors.isEmpty()) { if (reportedErrors) diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index b4ac150044..e7189664e2 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -62,12 +62,16 @@ QT_BEGIN_NAMESPACE class QQmlContextData; +namespace QQmlJS { +class Engine; +} + namespace QV4 { struct Q_QML_EXPORT Script { - Script(ExecutionContext *scope, QV4::Compiler::CompilationMode mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) + Script(ExecutionContext *scope, QV4::Compiler::ContextType mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) - , context(scope), strictMode(false), inheritContext(false), parsed(false), compilationMode(mode) + , context(scope), strictMode(false), inheritContext(false), parsed(false), contextType(mode) , vmFunction(nullptr), parseAsBinding(false) {} Script(ExecutionEngine *engine, QmlContext *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) @@ -76,7 +80,7 @@ struct Q_QML_EXPORT Script { if (qml) qmlContext.set(engine, *qml); } - Script(ExecutionEngine *engine, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit); + Script(ExecutionEngine *engine, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit); ~Script(); QString sourceFile; int line; @@ -86,7 +90,7 @@ struct Q_QML_EXPORT Script { bool strictMode; bool inheritContext; bool parsed; - QV4::Compiler::CompilationMode compilationMode = QV4::Compiler::EvalCode; + QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Eval; QV4::PersistentValue qmlContext; QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit; Function *vmFunction; @@ -97,10 +101,9 @@ struct Q_QML_EXPORT Script { Function *function(); - static QQmlRefPointer<CompiledData::CompilationUnit> precompile( - QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator, + static QQmlRefPointer<CompiledData::CompilationUnit> precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator, const QString &fileName, const QString &finalUrl, const QString &source, - QList<QQmlError> *reportedErrors = nullptr, QQmlJS::Directives *directivesCollector = nullptr); + QList<QQmlError> *reportedErrors = nullptr); static Script *createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlContext, const QString &fileName, const QUrl &originalUrl, QString *error); static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext); diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 7d29d0b517..1ba889ee82 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -227,7 +227,7 @@ namespace Heap { template <typename Container> struct QQmlSequence : Object { void init(const Container &container); - void init(QObject *object, int propertyIndex); + void init(QObject *object, int propertyIndex, bool readOnly); void destroy() { delete container; object.destroy(); @@ -237,7 +237,8 @@ struct QQmlSequence : Object { mutable Container *container; QQmlQPointer<QObject> object; int propertyIndex; - bool isReference; + bool isReference : 1; + bool isReadOnly : 1; }; } @@ -294,6 +295,9 @@ public: return false; } + if (d()->isReadOnly) + return false; + if (d()->isReference) { if (!d()->object) return false; @@ -345,7 +349,7 @@ public: if (d()->isReference) { if (!d()->object) { - QV4::Object::advanceIterator(this, it, name, index, p, attrs); + QV4::Object::virtualAdvanceIterator(this, it, name, index, p, attrs); return; } loadReference(); @@ -358,7 +362,7 @@ public: p->value = convertElementToValue(engine(), d()->container->at(*index)); return; } - QV4::Object::advanceIterator(this, it, name, index, p, attrs); + QV4::Object::virtualAdvanceIterator(this, it, name, index, p, attrs); } bool containerDeleteIndexedProperty(uint index) @@ -366,6 +370,8 @@ public: /* Qt containers have int (rather than uint) allowable indexes. */ if (index > INT_MAX) return false; + if (d()->isReadOnly) + return false; if (d()->isReference) { if (!d()->object) return false; @@ -432,11 +438,13 @@ public: const QV4::Value *m_compareFn; }; - void sort(const FunctionObject *f, const Value *, const Value *argv, int argc) + bool sort(const FunctionObject *f, const Value *, const Value *argv, int argc) { + if (d()->isReadOnly) + return false; if (d()->isReference) { if (!d()->object) - return; + return false; loadReference(); } @@ -450,6 +458,8 @@ public: if (d()->isReference) storeReference(); + + return true; } static QV4::ReturnedValue method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -480,6 +490,10 @@ public: generateWarning(scope.engine, QLatin1String("Index out of range during length set")); RETURN_UNDEFINED(); } + + if (This->d()->isReadOnly) + THROW_TYPE_ERROR(); + /* Read the sequence from the QObject property if we're a reference */ if (This->d()->isReference) { if (!This->d()->object) @@ -524,7 +538,7 @@ public: quint32 length = array->getLength(); QV4::ScopedValue v(scope); for (quint32 i = 0; i < length; ++i) - result.push_back(convertValueToElement<typename Container::value_type>((v = array->getIndexed(i)))); + result.push_back(convertValueToElement<typename Container::value_type>((v = array->get(i)))); return QVariant::fromValue(result); } @@ -549,17 +563,31 @@ public: QMetaObject::metacall(d()->object, QMetaObject::WriteProperty, d()->propertyIndex, a); } - static QV4::ReturnedValue getIndexed(const QV4::Managed *that, uint index, bool *hasProperty) - { return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(index, hasProperty); } - static bool putIndexed(Managed *that, uint index, const QV4::Value &value) - { return static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(index, value); } + static QV4::ReturnedValue virtualGet(const QV4::Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty) + { + if (!id.isArrayIndex()) + return Object::virtualGet(that, id, receiver, hasProperty); + return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(id.asArrayIndex(), hasProperty); + } + static bool virtualPut(Managed *that, PropertyKey id, const QV4::Value &value, Value *receiver) + { + if (id.isArrayIndex()) + return static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(id.asArrayIndex(), value); + return Object::virtualPut(that, id, value, receiver); + } static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index) { return static_cast<const QQmlSequence<Container> *>(that)->containerQueryIndexed(index); } - static bool deleteIndexedProperty(QV4::Managed *that, uint index) - { return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index); } - static bool isEqualTo(Managed *that, Managed *other) + static bool virtualDeleteProperty(QV4::Managed *that, PropertyKey id) + { + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index); + } + return Object::virtualDeleteProperty(that, id); + } + static bool virtualIsEqualTo(Managed *that, Managed *other) { return static_cast<QQmlSequence<Container> *>(that)->containerIsEqualTo(other); } - static void advanceIterator(Managed *that, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) + static void virtualAdvanceIterator(Managed *that, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) { return static_cast<QQmlSequence<Container> *>(that)->containerAdvanceIterator(it, name, index, p, attrs); } }; @@ -572,6 +600,7 @@ void Heap::QQmlSequence<Container>::init(const Container &container) this->container = new Container(container); propertyIndex = -1; isReference = false; + isReadOnly = false; object.init(); QV4::Scope scope(internalClass->engine); @@ -581,12 +610,13 @@ void Heap::QQmlSequence<Container>::init(const Container &container) } template <typename Container> -void Heap::QQmlSequence<Container>::init(QObject *object, int propertyIndex) +void Heap::QQmlSequence<Container>::init(QObject *object, int propertyIndex, bool readOnly) { Object::init(); this->container = new Container; this->propertyIndex = propertyIndex; isReference = true; + this->isReadOnly = readOnly; this->object.init(object); QV4::Scope scope(internalClass->engine); QV4::Scoped<QV4::QQmlSequence<Container> > o(scope, this); @@ -668,7 +698,8 @@ ReturnedValue SequencePrototype::method_sort(const FunctionObject *b, const Valu #define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ if (QQml##SequenceElementTypeName##List *s = o->as<QQml##SequenceElementTypeName##List>()) { \ - s->sort(b, thisObject, argv, argc); \ + if (!s->sort(b, thisObject, argv, argc)) \ + THROW_TYPE_ERROR(); \ } else FOREACH_QML_SEQUENCE_TYPE(CALL_SORT) @@ -691,11 +722,11 @@ bool SequencePrototype::isSequenceType(int sequenceTypeId) #define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ if (sequenceType == qMetaTypeId<SequenceType>()) { \ - QV4::ScopedObject obj(scope, engine->memoryManager->allocObject<QQml##ElementTypeName##List>(object, propertyIndex)); \ + QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(object, propertyIndex, readOnly)); \ return obj.asReturnedValue(); \ } else -ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool *succeeded) +ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool readOnly, bool *succeeded) { QV4::Scope scope(engine); // This function is called when the property is a QObject Q_PROPERTY of @@ -709,7 +740,7 @@ ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int s #define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ if (sequenceType == qMetaTypeId<SequenceType>()) { \ - QV4::ScopedObject obj(scope, engine->memoryManager->allocObject<QQml##ElementTypeName##List>(v.value<SequenceType >())); \ + QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(v.value<SequenceType >())); \ return obj.asReturnedValue(); \ } else diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index e9bef2f604..da71215bed 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -59,6 +59,8 @@ #include "qv4context_p.h" #include "qv4string_p.h" +QT_REQUIRE_CONFIG(qml_sequence_object); + QT_BEGIN_NAMESPACE namespace QV4 { @@ -72,7 +74,7 @@ struct SequencePrototype : public QV4::Object static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static bool isSequenceType(int sequenceTypeId); - static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded); + static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool readOnly, bool *succeeded); static ReturnedValue fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded); static int metaTypeForSequence(const Object *object); static QVariant toVariant(Object *object); diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp index 14def49d0a..e151966306 100644 --- a/src/qml/jsruntime/qv4serialize.cpp +++ b/src/qml/jsruntime/qv4serialize.cpp @@ -40,13 +40,17 @@ #include "qv4serialize_p.h" #include <private/qv8engine_p.h> +#if QT_CONFIG(qml_list_model) #include <private/qqmllistmodel_p.h> #include <private/qqmllistmodelworkeragent_p.h> +#endif #include <private/qv4value_p.h> #include <private/qv4dateobject_p.h> #include <private/qv4regexpobject_p.h> +#if QT_CONFIG(qml_sequence_object) #include <private/qv4sequenceobject_p.h> +#endif #include <private/qv4objectproto_p.h> #include <private/qv4qobjectwrapper_p.h> @@ -82,8 +86,12 @@ enum Type { WorkerNumber, WorkerDate, WorkerRegexp, +#if QT_CONFIG(qml_list_model) WorkerListModel, +#endif +#if QT_CONFIG(qml_sequence_object) WorkerSequence +#endif }; static inline quint32 valueheader(Type type, quint32 size = 0) @@ -189,7 +197,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine push(data, valueheader(WorkerArray, length)); ScopedValue val(scope); for (uint ii = 0; ii < length; ++ii) - serialize(data, (val = array->getIndexed(ii)), engine); + serialize(data, (val = array->get(ii)), engine); } else if (v.isInteger()) { reserve(data, 2 * sizeof(quint32)); push(data, valueheader(WorkerInt32)); @@ -228,6 +236,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine } else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) { // XXX TODO: Generalize passing objects between the main thread and worker scripts so // that others can trivially plug in their elements. +#if QT_CONFIG(qml_list_model) QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object()); if (lm && lm->agent()) { QQmlListModelWorkerAgent *agent = lm->agent(); @@ -236,9 +245,13 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine push(data, (void *)agent); return; } +#else + Q_UNUSED(qobjectWrapper); +#endif // No other QObject's are allowed to be sent push(data, valueheader(WorkerUndefined)); } else if (const Object *o = v.as<Object>()) { +#if QT_CONFIG(qml_sequence_object) if (o->isListType()) { // valid sequence. we generate a length (sequence length + 1 for the sequence type) uint seqLength = ScopedValue(scope, o->get(engine->id_length()))->toUInt32(); @@ -252,10 +265,11 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine serialize(data, QV4::Primitive::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type ScopedValue val(scope); for (uint ii = 0; ii < seqLength; ++ii) - serialize(data, (val = o->getIndexed(ii)), engine); // sequence elements + serialize(data, (val = o->get(ii)), engine); // sequence elements return; } +#endif // regular object QV4::ScopedValue val(scope, v); @@ -269,7 +283,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine QV4::ScopedValue s(scope); for (quint32 ii = 0; ii < length; ++ii) { - s = properties->getIndexed(ii); + s = properties->get(ii); serialize(data, s, engine); QV4::String *str = s->as<String>(); @@ -318,7 +332,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) ScopedValue v(scope); for (quint32 ii = 0; ii < size; ++ii) { v = deserialize(data, engine); - a->putIndexed(ii, v); + a->put(ii, v); } return a.asReturnedValue(); } @@ -353,6 +367,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) data += ALIGN(length * sizeof(quint16)); return Encode(engine->newRegExpObject(pattern, flags)); } +#if QT_CONFIG(qml_list_model) case WorkerListModel: { void *ptr = popPtr(data); @@ -369,6 +384,8 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) agent->setEngine(engine); return rv->asReturnedValue(); } +#endif +#if QT_CONFIG(qml_sequence_object) case WorkerSequence: { ScopedValue value(scope); @@ -387,6 +404,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded); return QV4::SequencePrototype::fromVariant(engine, seqVariant, &succeeded); } +#endif } Q_ASSERT(!"Unreachable"); return QV4::Encode::undefined(); diff --git a/src/qml/jsruntime/qv4setiterator.cpp b/src/qml/jsruntime/qv4setiterator.cpp new file mode 100644 index 0000000000..4681a49bd6 --- /dev/null +++ b/src/qml/jsruntime/qv4setiterator.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qv4iterator_p.h> +#include <private/qv4estable_p.h> +#include <private/qv4setiterator_p.h> +#include <private/qv4setobject_p.h> +#include <private/qv4symbol_p.h> + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(SetIteratorObject); + +void SetIteratorPrototype::init(ExecutionEngine *e) +{ + defineDefaultProperty(QStringLiteral("next"), method_next, 0); + + Scope scope(e); + ScopedString val(scope, e->newString(QLatin1String("Set Iterator"))); + defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val); +} + +ReturnedValue SetIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int) +{ + Scope scope(b); + const SetIteratorObject *thisObject = that->as<SetIteratorObject>(); + if (!thisObject) + return scope.engine->throwTypeError(QLatin1String("Not a Set Iterator instance")); + + Scoped<SetObject> s(scope, thisObject->d()->iteratedSet); + uint index = thisObject->d()->setNextIndex; + IteratorKind itemKind = thisObject->d()->iterationKind; + + if (!s) { + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + Value *arguments = scope.alloc(2); + + while (index < s->d()->esTable->size()) { + s->d()->esTable->iterate(index, &arguments[0], &arguments[1]); + thisObject->d()->setNextIndex = index + 1; + + if (itemKind == KeyValueIteratorKind) { + ScopedArrayObject resultArray(scope, scope.engine->newArrayObject()); + resultArray->arrayReserve(2); + resultArray->arrayPut(0, arguments[0]); + resultArray->arrayPut(1, arguments[0]); // yes, the key is repeated. + resultArray->setArrayLengthUnchecked(2); + + return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false); + } + + return IteratorPrototype::createIterResultObject(scope.engine, arguments[0], false); + } + + thisObject->d()->iteratedSet.set(scope.engine, nullptr); + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); +} + diff --git a/src/qml/jsruntime/qv4setiterator_p.h b/src/qml/jsruntime/qv4setiterator_p.h new file mode 100644 index 0000000000..78eda6d57b --- /dev/null +++ b/src/qml/jsruntime/qv4setiterator_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4SETITERATOR_P_H +#define QV4SETITERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4iterator_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +#define SetIteratorObjectMembers(class, Member) \ + Member(class, Pointer, Object *, iteratedSet) \ + Member(class, NoMark, IteratorKind, iterationKind) \ + Member(class, NoMark, quint32, setNextIndex) + +DECLARE_HEAP_OBJECT(SetIteratorObject, Object) { + DECLARE_MARKOBJECTS(SetIteratorObject); + void init(Object *obj, QV4::ExecutionEngine *engine) + { + Object::init(); + this->iteratedSet.set(engine, obj); + this->setNextIndex = 0; + } +}; + +} + +struct SetIteratorPrototype : Object +{ + V4_PROTOTYPE(iteratorPrototype) + void init(ExecutionEngine *engine); + + static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +}; + +struct SetIteratorObject : Object +{ + V4_OBJECT2(SetIteratorObject, Object) + Q_MANAGED_TYPE(SetIteratorObject) + V4_PROTOTYPE(setIteratorPrototype) + + void init(ExecutionEngine *engine); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4SETITERATOR_P_H + diff --git a/src/qml/jsruntime/qv4setobject.cpp b/src/qml/jsruntime/qv4setobject.cpp new file mode 100644 index 0000000000..30e849bfed --- /dev/null +++ b/src/qml/jsruntime/qv4setobject.cpp @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4setobject_p.h" +#include "qv4setiterator_p.h" +#include "qv4estable_p.h" +#include "qv4symbol_p.h" + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(SetCtor); +DEFINE_OBJECT_VTABLE(SetObject); + +void Heap::SetCtor::init(QV4::ExecutionContext *scope) +{ + Heap::FunctionObject::init(scope, QStringLiteral("Set")); +} + +ReturnedValue SetCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +{ + Scope scope(f); + Scoped<SetObject> a(scope, scope.engine->memoryManager->allocate<SetObject>()); + + if (argc > 0) { + ScopedValue iterable(scope, argv[0]); + if (!iterable->isUndefined() && !iterable->isNull()) { + ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("add"))))); + if (!adder) + return scope.engine->throwTypeError(); + ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true)); + CHECK_EXCEPTION(); + if (!iter) + return a.asReturnedValue(); + + Value *nextValue = scope.alloc(1); + ScopedValue done(scope); + forever { + done = Runtime::method_iteratorNext(scope.engine, iter, nextValue); + CHECK_EXCEPTION(); + if (done->toBoolean()) + return a.asReturnedValue(); + + adder->call(a, nextValue, 1); + if (scope.engine->hasException) { + ScopedValue falsey(scope, Encode(false)); + return Runtime::method_iteratorClose(scope.engine, iter, falsey); + } + } + } + } + + return a.asReturnedValue(); +} + +ReturnedValue SetCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int) +{ + Scope scope(f); + return scope.engine->throwTypeError(QString::fromLatin1("Set requires new")); +} + +void SetPrototype::init(ExecutionEngine *engine, Object *ctor) +{ + Scope scope(engine); + ScopedObject o(scope); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + ctor->addSymbolSpecies(); + defineDefaultProperty(engine->id_constructor(), (o = ctor)); + + defineDefaultProperty(QStringLiteral("add"), method_add, 1); + defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); + defineDefaultProperty(QStringLiteral("delete"), method_delete, 1); + defineDefaultProperty(QStringLiteral("entries"), method_entries, 0); + defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1); + defineDefaultProperty(QStringLiteral("has"), method_has, 1); + defineAccessorProperty(QStringLiteral("size"), method_get_size, nullptr); + + // Per the spec, the value for 'keys' is the same as 'values'. + ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("values"))); + ScopedFunctionObject valuesFn(scope, FunctionObject::createBuiltinFunction(engine, valString, SetPrototype::method_values, 0)); + defineDefaultProperty(QStringLiteral("keys"), valuesFn); + defineDefaultProperty(QStringLiteral("values"), valuesFn); + + defineDefaultProperty(engine->symbol_iterator(), valuesFn); + + ScopedString val(scope, engine->newString(QLatin1String("Set"))); + defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val); +} + +void Heap::SetObject::init() +{ + Object::init(); + esTable = new ESTable(); +} + +void Heap::SetObject::destroy() +{ + delete esTable; + esTable = 0; +} + +void Heap::SetObject::markObjects(Heap::Base *that, MarkStack *markStack) +{ + SetObject *s = static_cast<SetObject *>(that); + s->esTable->markObjects(markStack); + Object::markObjects(that, markStack); +} + +ReturnedValue SetPrototype::method_add(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + that->d()->esTable->set(argv[0], Primitive::undefinedValue()); + return that.asReturnedValue(); +} + +ReturnedValue SetPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + that->d()->esTable->clear(); + return Encode::undefined(); +} + +ReturnedValue SetPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + return Encode(that->d()->esTable->remove(argv[0])); +} + +ReturnedValue SetPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<SetIteratorObject> ao(scope, scope.engine->newSetIteratorObject(that)); + ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue SetPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + ScopedFunctionObject callbackfn(scope, argv[0]); + if (!callbackfn) + return scope.engine->throwTypeError(); + + ScopedValue thisArg(scope, Primitive::undefinedValue()); + if (argc > 1) + thisArg = ScopedValue(scope, argv[1]); + + Value *arguments = scope.alloc(3); + for (uint i = 0; i < that->d()->esTable->size(); ++i) { + that->d()->esTable->iterate(i, &arguments[0], &arguments[1]); // fill in key (0), value (1) + arguments[1] = arguments[0]; // but for set, we want to return the key twice; value is always undefined. + + arguments[2] = that; + callbackfn->call(thisArg, arguments, 3); + CHECK_EXCEPTION(); + } + return Encode::undefined(); +} + +ReturnedValue SetPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + return Encode(that->d()->esTable->has(argv[0])); +} + +ReturnedValue SetPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + return Encode(that->d()->esTable->size()); +} + +ReturnedValue SetPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<SetIteratorObject> ao(scope, scope.engine->newSetIteratorObject(that)); + ao->d()->iterationKind = IteratorKind::ValueIteratorKind; + return ao->asReturnedValue(); +} + diff --git a/src/qml/jsruntime/qv4setobject_p.h b/src/qml/jsruntime/qv4setobject_p.h new file mode 100644 index 0000000000..34649c3f01 --- /dev/null +++ b/src/qml/jsruntime/qv4setobject_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4SETOBJECT_P_H +#define QV4SETOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4objectproto_p.h" +#include "qv4functionobject_p.h" +#include "qv4string_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +class ESTable; + +namespace Heap { + +struct SetCtor : FunctionObject { + void init(QV4::ExecutionContext *scope); +}; + +struct SetObject : Object { + static void markObjects(Heap::Base *that, MarkStack *markStack); + void init(); + void destroy(); + ESTable *esTable; +}; + +} + +struct SetCtor: FunctionObject +{ + V4_OBJECT2(SetCtor, FunctionObject) + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct SetObject : Object +{ + V4_OBJECT2(SetObject, Object) + V4_PROTOTYPE(setPrototype) + V4_NEEDS_DESTROY +}; + +struct SetPrototype : Object +{ + void init(ExecutionEngine *engine, Object *ctor); + + static ReturnedValue method_add(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_clear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_size(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + + +} // namespace QV4 + + +QT_END_NAMESPACE + +#endif // QV4SETOBJECT_P_H diff --git a/src/qml/jsruntime/qv4sparsearray.cpp b/src/qml/jsruntime/qv4sparsearray.cpp index 2a3e28bf63..8930c9a94d 100644 --- a/src/qml/jsruntime/qv4sparsearray.cpp +++ b/src/qml/jsruntime/qv4sparsearray.cpp @@ -395,7 +395,7 @@ void SparseArray::freeTree(SparseArrayNode *root, int alignment) SparseArray::SparseArray() : numEntries(0) { - freeList = Primitive::emptyValue(UINT_MAX).asReturnedValue(); + freeList = Encode(-1); header.p = 0; header.left = nullptr; header.right = nullptr; diff --git a/src/qml/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h index 51869b259f..c1e50c8dcf 100644 --- a/src/qml/jsruntime/qv4sparsearray_p.h +++ b/src/qml/jsruntime/qv4sparsearray_p.h @@ -52,6 +52,7 @@ // #include "qv4global_p.h" +#include "qv4value_p.h" #include <QtCore/qlist.h> //#define Q_MAP_DEBUG @@ -151,7 +152,7 @@ struct Q_QML_EXPORT SparseArray SparseArray(const SparseArray &other); - ReturnedValue freeList; + Value freeList; private: SparseArray &operator=(const SparseArray &other); diff --git a/src/qml/jsruntime/qv4stackframe.cpp b/src/qml/jsruntime/qv4stackframe.cpp new file mode 100644 index 0000000000..a716c53aea --- /dev/null +++ b/src/qml/jsruntime/qv4stackframe.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4stackframe_p.h" +#include <QtCore/qstring.h> + +using namespace QV4; + +QString CppStackFrame::source() const +{ + return v4Function ? v4Function->sourceFile() : QString(); +} + +QString CppStackFrame::function() const +{ + return v4Function ? v4Function->name()->toQString() : QString(); +} + +int CppStackFrame::lineNumber() const +{ + if (!v4Function) + return -1; + + auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) { + return entry.codeOffset < offset; + }; + + const QV4::CompiledData::Function *cf = v4Function->compiledFunction; + uint offset = instructionPointer; + const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable(); + uint nLineNumbers = cf->nLineNumbers; + const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1; + return line->line; +} + +ReturnedValue CppStackFrame::thisObject() const { + return jsFrame->thisObject.asReturnedValue(); +} + diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h new file mode 100644 index 0000000000..aa507d61a6 --- /dev/null +++ b/src/qml/jsruntime/qv4stackframe_p.h @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STACKFRAME_H +#define QV4STACKFRAME_H + +#include <private/qv4context_p.h> +#include <private/qv4enginebase_p.h> +#ifndef V4_BOOTSTRAP +#include <private/qv4function_p.h> +#endif + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct CallData +{ + enum Offsets { + Function = 0, + Context = 1, + Accumulator = 2, + This = 3, + NewTarget = 4, + Argc = 5, + + LastOffset = Argc, + OffsetCount = LastOffset + 1 + }; + + Value function; + Value context; + Value accumulator; + Value thisObject; + Value newTarget; + Value _argc; + + int argc() const { + Q_ASSERT(_argc.isInteger()); + return _argc.int_32(); + } + + void setArgc(int argc) { + Q_ASSERT(argc >= 0); + _argc.setInt_32(argc); + } + + inline ReturnedValue argument(int i) const { + return i < argc() ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); + } + + Value args[1]; + + static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); } +}; + +Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value); +Q_STATIC_ASSERT(offsetof(CallData, function ) == CallData::Function * sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, context ) == CallData::Context * sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, accumulator) == CallData::Accumulator * sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, thisObject ) == CallData::This * sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, newTarget ) == CallData::NewTarget * sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, _argc ) == CallData::Argc * sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, args ) == 6 * sizeof(Value)); + +struct Q_QML_EXPORT CppStackFrame { + EngineBase *engine; + Value *savedStackTop; + CppStackFrame *parent; + Function *v4Function; + CallData *jsFrame; + const Value *originalArguments; + int originalArgumentsCount; + int instructionPointer; + const char *yield; + const char *unwindHandler; + const char *unwindLabel; + int unwindLevel; + + void init(EngineBase *engine, Function *v4Function, const Value *argv, int argc) { + this->engine = engine; + + this->v4Function = v4Function; + originalArguments = argv; + originalArgumentsCount = argc; + instructionPointer = 0; + yield = nullptr; + unwindHandler = nullptr; + unwindLabel = nullptr; + unwindLevel = 0; + } + + void push() { + parent = engine->currentStackFrame; + engine->currentStackFrame = this; + savedStackTop = engine->jsStackTop; + } + + void pop() { + engine->currentStackFrame = parent; + engine->jsStackTop = savedStackTop; + } + +#ifndef V4_BOOTSTRAP + static uint requiredJSStackFrameSize(uint nRegisters) { + return CallData::HeaderSize() + nRegisters; + } + static uint requiredJSStackFrameSize(Function *v4Function) { + return CallData::HeaderSize() + v4Function->compiledFunction->nRegisters; + } + uint requiredJSStackFrameSize() const { + return requiredJSStackFrameSize(v4Function); + } + void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope, + const Value &thisObject, const Value &newTarget = Primitive::undefinedValue()) { + setupJSFrame(stackSpace, function, scope, thisObject, newTarget, + v4Function->nFormals, v4Function->compiledFunction->nRegisters); + } + void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope, + const Value &thisObject, const Value &newTarget, uint nFormals, uint nRegisters) + { + jsFrame = reinterpret_cast<CallData *>(stackSpace); + jsFrame->function = function; + jsFrame->context = scope->asReturnedValue(); + jsFrame->accumulator = Encode::undefined(); + jsFrame->thisObject = thisObject; + jsFrame->newTarget = newTarget; + + uint argc = uint(originalArgumentsCount); + if (argc > nFormals) + argc = nFormals; + jsFrame->setArgc(argc); + + memcpy(jsFrame->args, originalArguments, argc*sizeof(Value)); + const Value *end = jsFrame->args + nRegisters; + for (Value *v = jsFrame->args + argc; v < end; ++v) + *v = Encode::undefined(); + } +#endif + + QString source() const; + QString function() const; + inline QV4::ExecutionContext *context() const { + return static_cast<ExecutionContext *>(&jsFrame->context); + } + int lineNumber() const; + + inline QV4::Heap::CallContext *callContext() const { + Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\ + while (ctx->type != Heap::ExecutionContext::Type_CallContext) + ctx = ctx->outer; + return static_cast<Heap::CallContext *>(ctx); + } + ReturnedValue thisObject() const; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 447992ebec..911103c05d 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -52,8 +52,17 @@ using namespace QV4; #ifndef V4_BOOTSTRAP +void Heap::StringOrSymbol::markObjects(Heap::Base *that, MarkStack *markStack) +{ + StringOrSymbol *s = static_cast<StringOrSymbol *>(that); + Heap::StringOrSymbol *id = s->identifier.asStringOrSymbol(); + if (id) + id->mark(markStack); +} + void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack) { + StringOrSymbol::markObjects(that, markStack); String *s = static_cast<String *>(that); if (s->subtype < StringType_Complex) return; @@ -68,15 +77,16 @@ void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack) } } +DEFINE_MANAGED_VTABLE(StringOrSymbol); DEFINE_MANAGED_VTABLE(String); -bool String::isEqualTo(Managed *t, Managed *o) +bool String::virtualIsEqualTo(Managed *t, Managed *o) { if (t == o) return true; - if (!o->d()->vtable()->isString) + if (!o->vtable()->isString) return false; return static_cast<String *>(t)->isEqualTo(static_cast<String *>(o)); @@ -128,7 +138,8 @@ void Heap::ComplexString::init(Heap::String *ref, int from, int len) this->len = len; } -void Heap::String::destroy() { +void Heap::StringOrSymbol::destroy() +{ if (text) { internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(-text->size) * (int)sizeof(QChar)); if (!text->ref.deref()) @@ -155,12 +166,12 @@ uint String::toUInt(bool *ok) const return UINT_MAX; } -void String::makeIdentifierImpl() const +void String::createPropertyKeyImpl() const { if (!d()->text) d()->simplifyString(); Q_ASSERT(d()->text); - engine()->identifierTable->identifier(this); + engine()->identifierTable->asPropertyKey(this); } void Heap::String::simplifyString() const @@ -174,7 +185,7 @@ void Heap::String::simplifyString() const text = result.data_ptr(); text->ref.ref(); const ComplexString *cs = static_cast<const ComplexString *>(this); - identifier = nullptr; + identifier = PropertyKey::invalid(); cs->left = cs->right = nullptr; internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(text->size) * (qptrdiff)sizeof(QChar)); @@ -227,17 +238,26 @@ void Heap::String::append(const String *data, QChar *ch) } } -void Heap::String::createHashValue() const +void Heap::StringOrSymbol::createHashValue() const { - if (!text) - simplifyString(); + if (!text) { + Q_ASSERT(internalClass->vtable->isString); + static_cast<const Heap::String *>(this)->simplifyString(); + } Q_ASSERT(text); const QChar *ch = reinterpret_cast<const QChar *>(text->data()); const QChar *end = ch + text->size; stringHash = QV4::String::calculateHashValue(ch, end, &subtype); } -uint String::getLength(const Managed *m) +PropertyKey StringOrSymbol::toPropertyKey() const { + if (d()->identifier.isValid()) + return d()->identifier; + createPropertyKey(); + return propertyKey(); +} + +qint64 String::virtualGetLength(const Managed *m) { return static_cast<const String *>(m)->d()->length(); } @@ -248,4 +268,3 @@ uint String::toArrayIndex(const QString &str) { return QV4::String::toArrayIndex(str.constData(), str.constData() + str.length()); } - diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 5466cc274d..8a4dc08693 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -60,13 +60,14 @@ QT_BEGIN_NAMESPACE namespace QV4 { struct ExecutionEngine; -struct Identifier; +struct PropertyKey; namespace Heap { -struct Q_QML_PRIVATE_EXPORT String : Base { - static void markObjects(Heap::Base *that, MarkStack *markStack); +struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base +{ enum StringType { + StringType_Symbol, StringType_Regular, StringType_ArrayIndex, StringType_Unknown, @@ -75,13 +76,20 @@ struct Q_QML_PRIVATE_EXPORT String : Base { StringType_Complex = StringType_AddedString }; -#ifndef V4_BOOTSTRAP - void init(const QString &text); + mutable QStringData *text; + mutable PropertyKey identifier; + mutable uint subtype; + mutable uint stringHash; + + static void markObjects(Heap::Base *that, MarkStack *markStack); void destroy(); - void simplifyString() const; - int length() const; - std::size_t retainedTextSize() const { - return subtype >= StringType_Complex ? 0 : (std::size_t(text->size) * sizeof(QChar)); + + inline QString toQString() const { + if (!text) + return QString(); + QStringDataPtr ptr = { text }; + text->ref.ref(); + return QString(ptr); } void createHashValue() const; inline unsigned hashValue() const { @@ -91,6 +99,22 @@ struct Q_QML_PRIVATE_EXPORT String : Base { return stringHash; } +}; + +struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol { + static void markObjects(Heap::Base *that, MarkStack *markStack); + +#ifndef V4_BOOTSTRAP + const VTable *vtable() const { + return internalClass->vtable; + } + + void init(const QString &text); + void simplifyString() const; + int length() const; + std::size_t retainedTextSize() const { + return subtype >= StringType_Complex ? 0 : (std::size_t(text->size) * sizeof(QChar)); + } inline QString toQString() const { if (subtype >= StringType_Complex) simplifyString(); @@ -104,7 +128,7 @@ struct Q_QML_PRIVATE_EXPORT String : Base { if (hashValue() != other->hashValue()) return false; Q_ASSERT(subtype < StringType_Complex); - if (identifier && identifier == other->identifier) + if (identifier.isValid() && identifier == other->identifier) return true; if (subtype == Heap::String::StringType_ArrayIndex && other->subtype == Heap::String::StringType_ArrayIndex) return true; @@ -114,10 +138,6 @@ struct Q_QML_PRIVATE_EXPORT String : Base { bool startsWithUpper() const; - mutable QStringData *text; - mutable Identifier *identifier; - mutable uint subtype; - mutable uint stringHash; private: static void append(const String *data, QChar *ch); #endif @@ -146,9 +166,29 @@ int String::length() const { } -struct Q_QML_PRIVATE_EXPORT String : public Managed { +struct Q_QML_PRIVATE_EXPORT StringOrSymbol : public Managed { #ifndef V4_BOOTSTRAP - V4_MANAGED(String, Managed) + V4_MANAGED(StringOrSymbol, Managed) + enum { + IsStringOrSymbol = true + }; + +private: + inline void createPropertyKey() const; +public: + PropertyKey propertyKey() const { Q_ASSERT(d()->identifier.isValid()); return d()->identifier; } + PropertyKey toPropertyKey() const; + + + inline QString toQString() const { + return d()->toQString(); + } +#endif +}; + +struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol { +#ifndef V4_BOOTSTRAP + V4_MANAGED(String, StringOrSymbol) Q_MANAGED_TYPE(String) V4_INTERNALCLASS(String) V4_NEEDS_DESTROY @@ -177,24 +217,10 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { inline unsigned hashValue() const { return d()->hashValue(); } - uint asArrayIndex() const { - if (subtype() >= Heap::String::StringType_Unknown) - d()->createHashValue(); - Q_ASSERT(d()->subtype < Heap::String::StringType_Complex); - if (subtype() == Heap::String::StringType_ArrayIndex) - return d()->stringHash; - return UINT_MAX; - } uint toUInt(bool *ok) const; - void makeIdentifier() const { - if (d()->identifier) - return; - makeIdentifierImpl(); - } - // slow path - Q_NEVER_INLINE void makeIdentifierImpl() const; + Q_NEVER_INLINE void createPropertyKeyImpl() const; static uint createHashValue(const QChar *ch, int length, uint *subtype) { @@ -210,11 +236,9 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { bool startsWithUpper() const { return d()->startsWithUpper(); } - Identifier *identifier() const { return d()->identifier; } - protected: - static bool isEqualTo(Managed *that, Managed *o); - static uint getLength(const Managed *m); + static bool virtualIsEqualTo(Managed *that, Managed *o); + static qint64 virtualGetLength(const Managed *m); #endif public: @@ -254,7 +278,7 @@ public: uint h = toArrayIndex(ch, end); if (h != UINT_MAX) { if (subtype) - *subtype = Heap::String::StringType_ArrayIndex; + *subtype = Heap::StringOrSymbol::StringType_ArrayIndex; return h; } @@ -264,7 +288,7 @@ public: } if (subtype) - *subtype = Heap::String::StringType_Regular; + *subtype = (toUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular; return h; } }; @@ -280,9 +304,22 @@ struct ComplexString : String { } }; +inline +void StringOrSymbol::createPropertyKey() const { + if (d()->identifier.isValid()) + return; + Q_ASSERT(isString()); + static_cast<const String *>(this)->createPropertyKeyImpl(); +} + +template<> +inline const StringOrSymbol *Value::as() const { + return isManaged() && m()->internalClass->vtable->isStringOrSymbol ? static_cast<const String *>(this) : nullptr; +} + template<> inline const String *Value::as() const { - return isManaged() && m()->vtable()->isString ? static_cast<const String *>(this) : nullptr; + return isManaged() && m()->internalClass->vtable->isString ? static_cast<const String *>(this) : nullptr; } template<> diff --git a/src/qml/jsruntime/qv4stringiterator.cpp b/src/qml/jsruntime/qv4stringiterator.cpp new file mode 100644 index 0000000000..810ed333e4 --- /dev/null +++ b/src/qml/jsruntime/qv4stringiterator.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Crimson AS <info@crimson.no> +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qv4iterator_p.h> +#include <private/qv4stringiterator_p.h> +#include <private/qv4symbol_p.h> + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(StringIteratorObject); + +void StringIteratorPrototype::init(ExecutionEngine *e) +{ + defineDefaultProperty(QStringLiteral("next"), method_next, 0); + + Scope scope(e); + ScopedString val(scope, e->newString(QLatin1String("String Iterator"))); + defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val); +} + +ReturnedValue StringIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int) +{ + Scope scope(b); + const StringIteratorObject *thisObject = that->as<StringIteratorObject>(); + if (!thisObject) + return scope.engine->throwTypeError(QLatin1String("Not an String Iterator instance")); + + ScopedString s(scope, thisObject->d()->iteratedString); + if (!s) { + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + quint32 index = thisObject->d()->nextIndex; + + QString str = s->toQString(); + quint32 len = str.length(); + + if (index >= len) { + thisObject->d()->iteratedString.set(scope.engine, nullptr); + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + QChar ch = str.at(index); + int num = 1; + if (ch.unicode() >= 0xd800 && ch.unicode() <= 0xdbff && index + 1 != len) { + ch = str.at(index + 1); + if (ch.unicode() >= 0xdc00 && ch.unicode() <= 0xdfff) + num = 2; + } + + thisObject->d()->nextIndex += num; + + ScopedString resultString(scope, scope.engine->newString(s->toQString().mid(index, num))); + return IteratorPrototype::createIterResultObject(scope.engine, resultString, false); +} + diff --git a/src/qml/jsruntime/qv4stringiterator_p.h b/src/qml/jsruntime/qv4stringiterator_p.h new file mode 100644 index 0000000000..672ccc9963 --- /dev/null +++ b/src/qml/jsruntime/qv4stringiterator_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4STRINGITERATOR_P_H +#define QV4STRINGITERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4string_p.h" + +QT_BEGIN_NAMESPACE + + +namespace QV4 { + +namespace Heap { + +#define StringIteratorObjectMembers(class, Member) \ + Member(class, Pointer, String *, iteratedString) \ + Member(class, NoMark, quint32, nextIndex) + +DECLARE_HEAP_OBJECT(StringIteratorObject, Object) { + DECLARE_MARKOBJECTS(StringIteratorObject); + void init(String *str, QV4::ExecutionEngine *engine) + { + Object::init(); + this->iteratedString.set(engine, str); + this->nextIndex = 0; + } +}; + +} + +struct StringIteratorPrototype : Object +{ + V4_PROTOTYPE(iteratorPrototype) + void init(ExecutionEngine *engine); + + static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +}; + +struct StringIteratorObject : Object +{ + V4_OBJECT2(StringIteratorObject, Object) + Q_MANAGED_TYPE(StringIteratorObject) + V4_PROTOTYPE(stringIteratorPrototype) + + void init(ExecutionEngine *engine); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4ARRAYITERATOR_P_H + diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 61176b3706..f2d0d52013 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -44,8 +44,10 @@ #include "qv4objectproto_p.h" #include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" +#include "qv4symbol_p.h" #include "qv4alloca_p.h" #include "qv4jscall_p.h" +#include "qv4stringiterator_p.h" #include <QtCore/QDateTime> #include <QtCore/QDebug> #include <QtCore/QStringList> @@ -95,19 +97,19 @@ uint Heap::StringObject::length() const return string->length(); } -bool StringObject::deleteIndexedProperty(Managed *m, uint index) +bool StringObject::virtualDeleteProperty(Managed *m, PropertyKey id) { - ExecutionEngine *v4 = static_cast<StringObject *>(m)->engine(); - Scope scope(v4); - Scoped<StringObject> o(scope, m->as<StringObject>()); - Q_ASSERT(!!o); - - if (index < static_cast<uint>(o->d()->string->toQString().length())) - return false; - return true; + Q_ASSERT(m->as<StringObject>()); + if (id.isArrayIndex()) { + StringObject *o = static_cast<StringObject *>(m); + uint index = id.asArrayIndex(); + if (index < static_cast<uint>(o->d()->string->toQString().length())) + return false; + } + return Object::virtualDeleteProperty(m, id); } -void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) +void StringObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) { name->setM(nullptr); StringObject *s = static_cast<StringObject *>(m); @@ -116,9 +118,8 @@ void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, while (it->arrayIndex < slen) { *index = it->arrayIndex; ++it->arrayIndex; - PropertyAttributes a; Property pd; - s->getOwnProperty(*index, &a, &pd); + PropertyAttributes a = s->getOwnProperty(PropertyKey::fromArrayIndex(*index), &pd); if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { *attrs = a; p->copy(&pd, a); @@ -133,7 +134,27 @@ void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, } } - return Object::advanceIterator(m, it, name, index, p, attrs); + return Object::virtualAdvanceIterator(m, it, name, index, p, attrs); +} + +PropertyAttributes StringObject::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p) +{ + PropertyAttributes attributes = Object::virtualGetOwnProperty(m, id, p); + if (attributes != Attr_Invalid) + return attributes; + + Object *o = static_cast<Object *>(m); + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + if (o->isStringObject()) { + if (index >= static_cast<const StringObject *>(m)->length()) + return Attr_Invalid; + if (p) + p->value = static_cast<StringObject *>(o)->getIndex(index); + return Attr_NotConfigurable|Attr_NotWritable; + } + } + return Attr_Invalid; } DEFINE_OBJECT_VTABLE(StringCtor); @@ -143,7 +164,7 @@ void Heap::StringCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("String")); } -ReturnedValue StringCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue StringCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { ExecutionEngine *v4 = static_cast<const Object *>(f)->engine(); Scope scope(v4); @@ -152,16 +173,58 @@ ReturnedValue StringCtor::callAsConstructor(const FunctionObject *f, const Value value = argv[0].toString(v4); else value = v4->newString(); + CHECK_EXCEPTION(); return Encode(v4->newStringObject(value)); } -ReturnedValue StringCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc) +ReturnedValue StringCtor::virtualCall(const FunctionObject *m, const Value *, const Value *argv, int argc) { ExecutionEngine *v4 = m->engine(); - if (argc) - return argv[0].toString(v4)->asReturnedValue(); - else + if (!argc) return v4->newString()->asReturnedValue(); + if (argv[0].isSymbol()) + return v4->newString(argv[0].symbolValue()->descriptiveString())->asReturnedValue(); + return argv[0].toString(v4)->asReturnedValue(); +} + +ReturnedValue StringCtor::method_fromCharCode(const FunctionObject *b, const Value *, const Value *argv, int argc) +{ + QString str(argc, Qt::Uninitialized); + QChar *ch = str.data(); + for (int i = 0, ei = argc; i < ei; ++i) { + *ch = QChar(argv[i].toUInt16()); + ++ch; + } + *ch = 0; + return Encode(b->engine()->newString(str)); +} + + + +ReturnedValue StringCtor::method_fromCodePoint(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + ExecutionEngine *e = f->engine(); + QString result(argc*2, Qt::Uninitialized); // assume worst case + QChar *ch = result.data(); + for (int i = 0; i < argc; ++i) { + double num = argv[i].toNumber(); + if (e->hasException) + return Encode::undefined(); + int cp = static_cast<int>(num); + if (cp != num || cp < 0 || cp > 0x10ffff) + return e->throwRangeError(QStringLiteral("String.fromCodePoint: argument out of range.")); + if (cp > 0xffff) { + *ch = QChar::highSurrogate(cp); + ++ch; + *ch = QChar::lowSurrogate(cp); + } else { + *ch = cp; + } + ++ch; + } + *ch = 0; + result.truncate(ch - result.constData()); + return e->newString(result)->asReturnedValue(); } void StringPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -169,15 +232,23 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) Scope scope(engine); ScopedObject o(scope); + // need to set this once again, as these were not fully defined when creating the string proto + Heap::InternalClass *ic = scope.engine->classes[ExecutionEngine::Class_StringObject]->changePrototype(scope.engine->objectPrototype()->d()); + d()->internalClass.set(scope.engine, ic); + d()->string.set(scope.engine, scope.engine->id_empty()->d()); + setProperty(scope.engine, Heap::StringObject::LengthPropertyIndex, Primitive::fromInt32(0)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); - ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), method_fromCharCode, 1); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), StringCtor::method_fromCharCode, 1); + ctor->defineDefaultProperty(QStringLiteral("fromCodePoint"), StringCtor::method_fromCodePoint, 1); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString); defineDefaultProperty(engine->id_valueOf(), method_toString); // valueOf and toString are identical defineDefaultProperty(QStringLiteral("charAt"), method_charAt, 1); defineDefaultProperty(QStringLiteral("charCodeAt"), method_charCodeAt, 1); + defineDefaultProperty(QStringLiteral("codePointAt"), method_codePointAt, 1); defineDefaultProperty(QStringLiteral("concat"), method_concat, 1); defineDefaultProperty(QStringLiteral("endsWith"), method_endsWith, 1); defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1); @@ -185,6 +256,9 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare, 1); defineDefaultProperty(QStringLiteral("match"), method_match, 1); + defineDefaultProperty(QStringLiteral("normalize"), method_normalize, 0); + defineDefaultProperty(QStringLiteral("padEnd"), method_padEnd, 1); + defineDefaultProperty(QStringLiteral("padStart"), method_padStart, 1); defineDefaultProperty(QStringLiteral("repeat"), method_repeat, 1); defineDefaultProperty(QStringLiteral("replace"), method_replace, 2); defineDefaultProperty(QStringLiteral("search"), method_search, 1); @@ -198,6 +272,7 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("toUpperCase"), method_toUpperCase); defineDefaultProperty(QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); defineDefaultProperty(QStringLiteral("trim"), method_trim); + defineDefaultProperty(engine->symbol_iterator(), method_iterator); } static Heap::String *thisAsString(ExecutionEngine *v4, const QV4::Value *thisObject) @@ -270,6 +345,29 @@ ReturnedValue StringPrototype::method_charCodeAt(const FunctionObject *b, const return Encode(qt_qnan()); } +ReturnedValue StringPrototype::method_codePointAt(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *v4 = f->engine(); + QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); + + int index = argc ? argv[0].toInteger() : 0; + if (v4->hasException) + return QV4::Encode::undefined(); + + if (index < 0 || index >= value.size()) + return Encode::undefined(); + + uint first = value.at(index).unicode(); + if (QChar::isHighSurrogate(first) && index + 1 < value.size()) { + uint second = value.at(index + 1).unicode(); + if (QChar::isLowSurrogate(second)) + return Encode(QChar::surrogateToUcs4(first, second)); + } + return Encode(first); +} + ReturnedValue StringPrototype::method_concat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { ExecutionEngine *v4 = b->engine(); @@ -298,12 +396,11 @@ ReturnedValue StringPrototype::method_endsWith(const FunctionObject *b, const Va if (v4->hasException) return QV4::Encode::undefined(); - QString searchString; - if (argc) { - if (argv[0].as<RegExpObject>()) - return v4->throwTypeError(); - searchString = argv[0].toQString(); - } + if (argc && argv[0].as<RegExpObject>()) + return v4->throwTypeError(); + QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString(); + if (v4->hasException) + return Encode::undefined(); int pos = value.length(); if (argc > 1) @@ -323,9 +420,9 @@ ReturnedValue StringPrototype::method_indexOf(const FunctionObject *b, const Val if (v4->hasException) return QV4::Encode::undefined(); - QString searchString; - if (argc) - searchString = argv[0].toQString(); + QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString(); + if (v4->hasException) + return Encode::undefined(); int pos = 0; if (argc > 1) @@ -345,12 +442,11 @@ ReturnedValue StringPrototype::method_includes(const FunctionObject *b, const Va if (v4->hasException) return QV4::Encode::undefined(); - QString searchString; - if (argc) { - if (argv[0].as<RegExpObject>()) - return v4->throwTypeError(); - searchString = argv[0].toQString(); - } + if (argc && argv[0].as<RegExpObject>()) + return v4->throwTypeError(); + QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString(); + if (v4->hasException) + return Encode::undefined(); int pos = 0; if (argc > 1) { @@ -374,9 +470,9 @@ ReturnedValue StringPrototype::method_lastIndexOf(const FunctionObject *b, const if (v4->hasException) return QV4::Encode::undefined(); - QString searchString; - if (argc) - searchString = argv[0].toQString(); + QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString(); + if (v4->hasException) + return Encode::undefined(); double position = argc > 1 ? RuntimeHelpers::toNumber(argv[1]) : +qInf(); if (std::isnan(position)) @@ -418,7 +514,7 @@ ReturnedValue StringPrototype::method_match(const FunctionObject *b, const Value Scoped<RegExpObject> that(scope, argc ? argv[0] : Primitive::undefinedValue()); if (!that) { // convert args[0] to a regexp - that = RegExpCtor::callAsConstructor(b, argv, argc); + that = RegExpCtor::virtualCallAsConstructor(b, argv, argc, b); if (v4->hasException) return Encode::undefined(); } @@ -455,6 +551,115 @@ ReturnedValue StringPrototype::method_match(const FunctionObject *b, const Value return a.asReturnedValue(); } +ReturnedValue StringPrototype::method_normalize(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *v4 = f->engine(); + const QString value = getThisString(v4, thisObject); + if (v4->hasException) + return Encode::undefined(); + + QString::NormalizationForm form = QString::NormalizationForm_C; + if (argc >= 1 && !argv[0].isUndefined()) { + QString f = argv[0].toQString(); + if (v4->hasException) + return Encode::undefined(); + if (f == QLatin1String("NFC")) + form = QString::NormalizationForm_C; + else if (f == QLatin1String("NFD")) + form = QString::NormalizationForm_D; + else if (f == QLatin1String("NFKC")) + form = QString::NormalizationForm_KC; + else if (f == QLatin1String("NFKD")) + form = QString::NormalizationForm_KD; + else + return v4->throwRangeError(QLatin1String("String.prototype.normalize: Invalid normalization form.")); + } + QString normalized = value.normalized(form); + return v4->newString(normalized)->asReturnedValue(); +} + +ReturnedValue StringPrototype::method_padEnd(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *v4 = f->engine(); + if (thisObject->isNullOrUndefined()) + return v4->throwTypeError(); + + Scope scope(v4); + ScopedString s(scope, thisAsString(v4, thisObject)); + if (v4->hasException) + return Encode::undefined(); + if (!argc) + return s->asReturnedValue(); + + int maxLen = argv[0].toInteger(); + if (maxLen <= s->d()->length()) + return s->asReturnedValue(); + QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" "); + if (v4->hasException) + return Encode::undefined(); + + if (fillString.isEmpty()) + return s->asReturnedValue(); + + QString padded = s->toQString(); + int oldLength = padded.length(); + int toFill = maxLen - oldLength; + padded.resize(maxLen); + QChar *ch = padded.data() + oldLength; + while (toFill) { + int copy = qMin(fillString.length(), toFill); + memcpy(ch, fillString.constData(), copy*sizeof(QChar)); + toFill -= copy; + ch += copy; + } + *ch = 0; + + return v4->newString(padded)->asReturnedValue(); +} + +ReturnedValue StringPrototype::method_padStart(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *v4 = f->engine(); + if (thisObject->isNullOrUndefined()) + return v4->throwTypeError(); + + Scope scope(v4); + ScopedString s(scope, thisAsString(v4, thisObject)); + if (v4->hasException) + return Encode::undefined(); + if (!argc) + return s->asReturnedValue(); + + int maxLen = argv[0].toInteger(); + if (maxLen <= s->d()->length()) + return s->asReturnedValue(); + QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" "); + if (v4->hasException) + return Encode::undefined(); + + if (fillString.isEmpty()) + return s->asReturnedValue(); + + QString original = s->toQString(); + int oldLength = original.length(); + int toFill = maxLen - oldLength; + QString padded; + padded.resize(maxLen); + QChar *ch = padded.data(); + while (toFill) { + int copy = qMin(fillString.length(), toFill); + memcpy(ch, fillString.constData(), copy*sizeof(QChar)); + toFill -= copy; + ch += copy; + } + memcpy(ch, original.constData(), oldLength*sizeof(QChar)); + ch += oldLength; + *ch = 0; + + return v4->newString(padded)->asReturnedValue(); +} + + ReturnedValue StringPrototype::method_repeat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { ExecutionEngine *v4 = b->engine(); @@ -777,12 +982,11 @@ ReturnedValue StringPrototype::method_startsWith(const FunctionObject *b, const if (v4->hasException) return QV4::Encode::undefined(); - QString searchString; - if (argc) { - if (argv[0].as<RegExpObject>()) - return v4->throwTypeError(); - searchString = argv[0].toQString(); - } + if (argc && argv[0].as<RegExpObject>()) + return v4->throwTypeError(); + QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString(); + if (v4->hasException) + return Encode::undefined(); int pos = 0; if (argc > 1) @@ -892,17 +1096,6 @@ ReturnedValue StringPrototype::method_toLocaleUpperCase(const FunctionObject *b, return method_toUpperCase(b, thisObject, argv, argc); } -ReturnedValue StringPrototype::method_fromCharCode(const FunctionObject *b, const Value *, const Value *argv, int argc) -{ - QString str(argc, Qt::Uninitialized); - QChar *ch = str.data(); - for (int i = 0, ei = argc; i < ei; ++i) { - *ch = QChar(argv[i].toUInt16()); - ++ch; - } - return Encode(b->engine()->newString(str)); -} - ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); @@ -923,3 +1116,16 @@ ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value return Encode(v4->newString(QString(chars + start, end - start + 1))); } + + + +ReturnedValue StringPrototype::method_iterator(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + ScopedString s(scope, thisObject->toString(scope.engine)); + if (!s || thisObject->isNullOrUndefined()) + return scope.engine->throwTypeError(); + + Scoped<StringIteratorObject> si(scope, scope.engine->memoryManager->allocate<StringIteratorObject>(s->d(), scope.engine)); + return si->asReturnedValue(); +} diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index 7d25678b61..2d37e36b34 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -70,6 +70,8 @@ DECLARE_HEAP_OBJECT(StringObject, Object) { LengthPropertyIndex = 0 }; + void init(bool /*don't init*/) + { Object::init(); } void init(); void init(const QV4::String *string); @@ -96,18 +98,22 @@ struct StringObject: Object { return d()->length(); } - static bool deleteIndexedProperty(Managed *m, uint index); - + using Object::getOwnProperty; protected: - static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs); + static bool virtualDeleteProperty(Managed *m, PropertyKey id); + static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs); + static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); }; struct StringCtor: FunctionObject { V4_OBJECT2(StringCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_fromCharCode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_fromCodePoint(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct StringPrototype: StringObject @@ -118,6 +124,7 @@ struct StringPrototype: StringObject static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_charAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_charCodeAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_codePointAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_concat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_endsWith(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); @@ -125,6 +132,9 @@ struct StringPrototype: StringObject static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_localeCompare(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_match(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_normalize(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_padEnd(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_padStart(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_repeat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_replace(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_search(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); @@ -137,8 +147,8 @@ struct StringPrototype: StringObject static ReturnedValue method_toLocaleLowerCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toLocaleUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_fromCharCode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_trim(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_iterator(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; } diff --git a/src/qml/jsruntime/qv4symbol.cpp b/src/qml/jsruntime/qv4symbol.cpp new file mode 100644 index 0000000000..bdefe1eb9e --- /dev/null +++ b/src/qml/jsruntime/qv4symbol.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qv4symbol_p.h> +#include <qv4functionobject_p.h> +#include <qv4identifiertable_p.h> + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(SymbolCtor); +DEFINE_MANAGED_VTABLE(Symbol); +DEFINE_OBJECT_VTABLE(SymbolObject); + +void Heap::Symbol::init(const QString &s) +{ + Q_ASSERT(s.at(0) == QLatin1Char('@')); + identifier = PropertyKey::fromStringOrSymbol(this); + QString desc(s); + text = desc.data_ptr(); + text->ref.ref(); +} + +void Heap::SymbolCtor::init(QV4::ExecutionContext *scope) +{ + Heap::FunctionObject::init(scope, QStringLiteral("Symbol")); +} + +void Heap::SymbolObject::init(const QV4::Symbol *s) +{ + Object::init(); + symbol.set(internalClass->engine, s->d()); +} + +ReturnedValue QV4::SymbolCtor::virtualCall(const QV4::FunctionObject *f, const QV4::Value *, const QV4::Value *argv, int argc) +{ + Scope scope(f); + QString desc = QChar::fromLatin1('@'); + if (argc && !argv[0].isUndefined()) { + ScopedString s(scope, argv[0].toString(scope.engine)); + if (scope.hasException()) + return Encode::undefined(); + desc += s->toQString(); + } + return Symbol::create(scope.engine, desc)->asReturnedValue(); +} + +ReturnedValue SymbolCtor::method_for(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + ScopedValue k(scope, argc ? argv[0]: Primitive::undefinedValue()); + ScopedString key(scope, k->toString(scope.engine)); + if (scope.hasException()) + return Encode::undefined(); + QString desc = QLatin1Char('@') + key->toQString(); + return scope.engine->identifierTable->insertSymbol(desc)->asReturnedValue(); +} + +ReturnedValue SymbolCtor::method_keyFor(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + ExecutionEngine *e = f->engine(); + if (!argc || !argv[0].isSymbol()) + return e->throwTypeError(QLatin1String("Symbol.keyFor: Argument is not a symbol.")); + const Symbol &arg = static_cast<const Symbol &>(argv[0]); + Heap::Symbol *s = e->identifierTable->symbolForId(arg.propertyKey()); + Q_ASSERT(!s || s == arg.d()); + if (s) + return e->newString(arg.toQString().mid((1)))->asReturnedValue(); + return Encode::undefined(); +} + +void SymbolPrototype::init(ExecutionEngine *engine, Object *ctor) +{ + Scope scope(engine); + ScopedValue v(scope); + ctor->defineReadonlyProperty(engine->id_prototype(), (v = this)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + + ctor->defineDefaultProperty(QStringLiteral("for"), SymbolCtor::method_for, 1); + ctor->defineDefaultProperty(QStringLiteral("keyFor"), SymbolCtor::method_keyFor, 1); + ctor->defineReadonlyProperty(QStringLiteral("hasInstance"), *engine->symbol_hasInstance()); + ctor->defineReadonlyProperty(QStringLiteral("isConcatSpreadable"), *engine->symbol_isConcatSpreadable()); + ctor->defineReadonlyProperty(QStringLiteral("iterator"), *engine->symbol_iterator()); + ctor->defineReadonlyProperty(QStringLiteral("match"), *engine->symbol_match()); + ctor->defineReadonlyProperty(QStringLiteral("replace"), *engine->symbol_replace()); + ctor->defineReadonlyProperty(QStringLiteral("search"), *engine->symbol_search()); + ctor->defineReadonlyProperty(QStringLiteral("species"), *engine->symbol_species()); + ctor->defineReadonlyProperty(QStringLiteral("split"), *engine->symbol_split()); + ctor->defineReadonlyProperty(QStringLiteral("toPrimitive"), *engine->symbol_toPrimitive()); + ctor->defineReadonlyProperty(QStringLiteral("toStringTag"), *engine->symbol_toStringTag()); + ctor->defineReadonlyProperty(QStringLiteral("unscopables"), *engine->symbol_unscopables()); + + defineDefaultProperty(QStringLiteral("constructor"), (v = ctor)); + defineDefaultProperty(QStringLiteral("toString"), method_toString); + defineDefaultProperty(QStringLiteral("valueOf"), method_valueOf); + defineDefaultProperty(engine->symbol_toPrimitive(), method_symbolToPrimitive, 1, Attr_ReadOnly_ButConfigurable); + + v = engine->newString(QStringLiteral("Symbol")); + defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), v); + +} + +ReturnedValue SymbolPrototype::method_toString(const FunctionObject *f, const Value *thisObject, const Value *, int) +{ + Scope scope(f); + Scoped<Symbol> s(scope, thisObject->as<Symbol>()); + if (!s) { + if (const SymbolObject *o = thisObject->as<SymbolObject>()) + s = o->d()->symbol; + else + return scope.engine->throwTypeError(); + } + return scope.engine->newString(s->descriptiveString())->asReturnedValue(); +} + +ReturnedValue SymbolPrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *, int) +{ + Scope scope(f); + Scoped<Symbol> s(scope, thisObject->as<Symbol>()); + if (!s) { + if (const SymbolObject *o = thisObject->as<SymbolObject>()) + s = o->d()->symbol; + else + return scope.engine->throwTypeError(); + } + return s->asReturnedValue(); +} + +ReturnedValue SymbolPrototype::method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *, int) +{ + if (thisObject->isSymbol()) + return thisObject->asReturnedValue(); + if (const SymbolObject *o = thisObject->as<SymbolObject>()) + return o->d()->symbol->asReturnedValue(); + return f->engine()->throwTypeError(); +} + +Heap::Symbol *Symbol::create(ExecutionEngine *e, const QString &s) +{ + Q_ASSERT(s.at(0) == QLatin1Char('@')); + return e->memoryManager->alloc<Symbol>(s); +} + +QString Symbol::descriptiveString() const +{ + return QLatin1String("Symbol(") + toQString().midRef(1) + QLatin1String(")"); +} diff --git a/src/qml/jsruntime/qv4symbol_p.h b/src/qml/jsruntime/qv4symbol_p.h new file mode 100644 index 0000000000..46fa2979f8 --- /dev/null +++ b/src/qml/jsruntime/qv4symbol_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4_SYMBOL_H +#define QV4_SYMBOL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4string_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + + +namespace QV4 { + +namespace Heap { + +struct SymbolCtor : FunctionObject { + void init(QV4::ExecutionContext *scope); +}; + +struct Symbol : StringOrSymbol { + void init(const QString &s); +}; + +#define SymbolObjectMembers(class, Member) \ + Member(class, Pointer, Symbol *, symbol) + +DECLARE_HEAP_OBJECT(SymbolObject, Object) { + DECLARE_MARKOBJECTS(SymbolObject); + void init(const QV4::Symbol *s); +}; + +} + +struct SymbolCtor : FunctionObject +{ + V4_OBJECT2(SymbolCtor, FunctionObject) + + static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_for(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_keyFor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + +struct SymbolPrototype : Object +{ + V4_PROTOTYPE(objectPrototype) + void init(ExecutionEngine *engine, Object *ctor); + + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_symbolToPrimitive(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + +struct Symbol : StringOrSymbol +{ + V4_MANAGED(Symbol, StringOrSymbol) + Q_MANAGED_TYPE(Symbol) + V4_INTERNALCLASS(Symbol) + V4_NEEDS_DESTROY + + static Heap::Symbol *create(ExecutionEngine *e, const QString &s); + + QString descriptiveString() const; +}; + +struct SymbolObject : Object +{ + V4_OBJECT2(SymbolObject, Object) + Q_MANAGED_TYPE(SymbolObject) + V4_INTERNALCLASS(SymbolObject) + V4_PROTOTYPE(symbolPrototype) + + static bool virtualPut(Managed *, PropertyKey, const Value &, Value *) { return false; } +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index ea1532b8ce..cb9cdd8df5 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -36,15 +36,20 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + #include "qv4typedarray_p.h" +#include "qv4arrayiterator_p.h" #include "qv4arraybuffer_p.h" #include "qv4string_p.h" #include "qv4jscall_p.h" +#include "qv4symbol_p.h" #include <cmath> using namespace QV4; +DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayCtor); +DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayPrototype); DEFINE_OBJECT_VTABLE(TypedArrayCtor); DEFINE_OBJECT_VTABLE(TypedArrayPrototype); DEFINE_OBJECT_VTABLE(TypedArray); @@ -209,16 +214,19 @@ void Heap::TypedArrayCtor::init(QV4::ExecutionContext *scope, TypedArray::Type t type = t; } -ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Scope scope(f->engine()); const TypedArrayCtor *that = static_cast<const TypedArrayCtor *>(f); if (!argc || !argv[0].isObject()) { // ECMA 6 22.2.1.1 - double l = argc ? argv[0].toNumber() : 0; + qint64 l = argc ? argv[0].toIndex() : 0; if (scope.engine->hasException) return Encode::undefined(); + // ### lift UINT_MAX restriction + if (l < 0 || l > UINT_MAX) + return scope.engine->throwRangeError(QLatin1String("Index out of range.")); uint len = (uint)l; if (l != len) scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array.")); @@ -312,7 +320,10 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V return scope.engine->throwTypeError(); uint elementSize = operations[that->d()->type].bytesPerElement; - Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(l * elementSize)); + size_t bufferSize; + if (mul_overflow(size_t(l), size_t(elementSize), &bufferSize)) + return scope.engine->throwRangeError(QLatin1String("new TypedArray: invalid length")); + Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(bufferSize)); if (scope.engine->hasException) return Encode::undefined(); @@ -325,7 +336,7 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V char *b = newBuffer->d()->data->data(); ScopedValue val(scope); while (idx < l) { - val = o->getIndexed(idx); + val = o->get(idx); array->d()->type->write(scope.engine, b, 0, val); if (scope.engine->hasException) return Encode::undefined(); @@ -337,9 +348,9 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V return array.asReturnedValue(); } -ReturnedValue TypedArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue TypedArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int) { - return callAsConstructor(f, argv, argc); + return f->engine()->throwTypeError(QStringLiteral("calling a TypedArray constructor without new is invalid")); } void Heap::TypedArray::init(Type t) @@ -351,13 +362,17 @@ void Heap::TypedArray::init(Type t) Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t) { - QV4::InternalClass *ic = e->internalClasses[EngineBase::Class_Empty]->changeVTable(staticVTable()); - ic = ic->changePrototype(e->typedArrayPrototype[t].d()); - return e->memoryManager->allocObject<TypedArray>(ic, e->typedArrayPrototype + t, t); + Scope scope(e); + Scoped<InternalClass> ic(scope, e->newInternalClass(staticVTable(), e->typedArrayPrototype + t)); + return e->memoryManager->allocObject<TypedArray>(ic->d(), t); } -ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProperty) +ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { + if (!id.isArrayIndex()) + return Object::virtualGet(m, id, receiver, hasProperty); + + uint index = id.asArrayIndex(); Scope scope(static_cast<const Object *>(m)->engine()); Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m)); @@ -373,8 +388,12 @@ ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProp return a->d()->type->read(a->d()->buffer->data->data(), byteOffset); } -bool TypedArray::putIndexed(Managed *m, uint index, const Value &value) +bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { + if (!id.isArrayIndex()) + return Object::virtualPut(m, id, value, receiver); + + uint index = id.asArrayIndex(); ExecutionEngine *v4 = static_cast<Object *>(m)->engine(); if (v4->hasException) return false; @@ -395,21 +414,18 @@ void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3)); - ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(3)); + ctor->defineReadonlyProperty(engine->id_prototype(), *this); ctor->defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement)); + ctor->setPrototypeOf(engine->intrinsicTypedArrayCtor()); + + setPrototypeOf(engine->intrinsicTypedArrayPrototype()); defineDefaultProperty(engine->id_constructor(), (o = ctor)); - defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr); - defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr); - defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr); - defineAccessorProperty(QStringLiteral("length"), method_get_length, nullptr); defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement)); - - defineDefaultProperty(QStringLiteral("set"), method_set, 1); - defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0); } -ReturnedValue TypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int) +ReturnedValue IntrinsicTypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); const TypedArray *v = thisObject->as<TypedArray>(); @@ -419,7 +435,7 @@ ReturnedValue TypedArrayPrototype::method_get_buffer(const FunctionObject *b, co return v->d()->buffer->asReturnedValue(); } -ReturnedValue TypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int) +ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); const TypedArray *v = thisObject->as<TypedArray>(); @@ -429,7 +445,7 @@ ReturnedValue TypedArrayPrototype::method_get_byteLength(const FunctionObject *b return Encode(v->d()->byteLength); } -ReturnedValue TypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int) +ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); const TypedArray *v = thisObject->as<TypedArray>(); @@ -439,7 +455,7 @@ ReturnedValue TypedArrayPrototype::method_get_byteOffset(const FunctionObject *b return Encode(v->d()->byteOffset); } -ReturnedValue TypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) +ReturnedValue IntrinsicTypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); const TypedArray *v = thisObject->as<TypedArray>(); @@ -449,7 +465,43 @@ ReturnedValue TypedArrayPrototype::method_get_length(const FunctionObject *b, co return Encode(v->d()->byteLength/v->d()->type->bytesPerElement); } -ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +ReturnedValue IntrinsicTypedArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<TypedArray> O(scope, thisObject); + if (!O) + THROW_TYPE_ERROR(); + + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<TypedArray> O(scope, thisObject); + if (!O) + THROW_TYPE_ERROR(); + + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + ao->d()->iterationKind = IteratorKind::KeyIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<TypedArray> O(scope, thisObject); + if (!O) + THROW_TYPE_ERROR(); + + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + ao->d()->iterationKind = IteratorKind::ValueIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); Scoped<TypedArray> a(scope, *thisObject); @@ -487,7 +539,7 @@ ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Val char *b = buffer->d()->data->data() + a->d()->byteOffset + offset*elementSize; ScopedValue val(scope); while (idx < l) { - val = o->getIndexed(idx); + val = o->get(idx); a->d()->type->write(scope.engine, b, 0, val); if (scope.engine->hasException) RETURN_UNDEFINED(); @@ -538,7 +590,7 @@ ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Val RETURN_UNDEFINED(); } -ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) +ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) { Scope scope(builtin); Scoped<TypedArray> a(scope, *thisObject); @@ -578,3 +630,47 @@ ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin arguments[2] = Encode(newLen); return constructor->callAsConstructor(arguments, 3); } + +ReturnedValue IntrinsicTypedArrayPrototype::method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *, int) +{ + const TypedArray *a = thisObject->as<TypedArray>(); + if (!a) + return Encode::undefined(); + + return a->engine()->newString(QString::fromLatin1(a->d()->type->name))->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) +{ + return f->engine()->throwTypeError(); +} + +ReturnedValue IntrinsicTypedArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int) +{ + return f->engine()->throwTypeError(); +} + +void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor) +{ + ctor->defineReadonlyProperty(engine->id_prototype(), *this); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + ctor->addSymbolSpecies(); + + defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr); + defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr); + defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr); + defineAccessorProperty(QStringLiteral("length"), method_get_length, nullptr); + + defineDefaultProperty(QStringLiteral("entries"), method_entries, 0); + defineDefaultProperty(QStringLiteral("keys"), method_keys, 0); + defineDefaultProperty(QStringLiteral("set"), method_set, 1); + defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0); + + Scope scope(engine); + ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values"))); + ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0)); + defineDefaultProperty(QStringLiteral("values"), values); + defineDefaultProperty(engine->symbol_iterator(), values); + + defineAccessorProperty(engine->symbol_toStringTag(), method_get_toStringTag, nullptr); +} diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index 129c662c97..967d3235b2 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -97,12 +97,18 @@ DECLARE_HEAP_OBJECT(TypedArray, Object) { void init(Type t); }; +struct IntrinsicTypedArrayCtor : FunctionObject { +}; + struct TypedArrayCtor : FunctionObject { void init(QV4::ExecutionContext *scope, TypedArray::Type t); TypedArray::Type type; }; +struct IntrinsicTypedArrayPrototype : Object { +}; + struct TypedArrayPrototype : Object { inline void init(TypedArray::Type t); TypedArray::Type type; @@ -132,34 +138,56 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object Heap::TypedArray::Type arrayType() const { return static_cast<Heap::TypedArray::Type>(d()->arrayType); } + using Object::get; - static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static bool putIndexed(Managed *m, uint index, const Value &value); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); +}; + +struct IntrinsicTypedArrayCtor: FunctionObject +{ + V4_OBJECT2(IntrinsicTypedArrayCtor, FunctionObject) + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct TypedArrayCtor: FunctionObject { V4_OBJECT2(TypedArrayCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; - -struct TypedArrayPrototype : Object +struct IntrinsicTypedArrayPrototype : Object { - V4_OBJECT2(TypedArrayPrototype, Object) + V4_OBJECT2(IntrinsicTypedArrayPrototype, Object) V4_PROTOTYPE(objectPrototype) - void init(ExecutionEngine *engine, TypedArrayCtor *ctor); + void init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor); static ReturnedValue method_get_buffer(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_byteLength(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_byteOffset(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_length(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_subarray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + +}; + +struct TypedArrayPrototype : Object +{ + V4_OBJECT2(TypedArrayPrototype, Object) + V4_PROTOTYPE(objectPrototype) + + void init(ExecutionEngine *engine, TypedArrayCtor *ctor); }; inline void diff --git a/src/qml/jsruntime/qv4util_p.h b/src/qml/jsruntime/qv4util_p.h index 2669a3e4bf..073832937d 100644 --- a/src/qml/jsruntime/qv4util_p.h +++ b/src/qml/jsruntime/qv4util_p.h @@ -59,26 +59,6 @@ QT_BEGIN_NAMESPACE namespace QV4 { -template <typename T> -struct TemporaryAssignment -{ - TemporaryAssignment(T &var, const T& temporaryValue) - : variable(var) - , savedValue(var) - { - variable = temporaryValue; - } - ~TemporaryAssignment() - { - variable = savedValue; - } - T &variable; - T savedValue; -private: - TemporaryAssignment(const TemporaryAssignment<T>&); - TemporaryAssignment operator=(const TemporaryAssignment<T>&); -}; - #if !defined(BROKEN_STD_VECTOR_BOOL_OR_BROKEN_STD_FIND) // Sanity: class BitVector diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index 0d4711df3c..cbc153bb86 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -39,7 +39,9 @@ #include <qv4engine_p.h> #include <qv4runtime_p.h> #include <qv4string_p.h> +#include <qv4propertykey_p.h> #ifndef V4_BOOTSTRAP +#include <qv4symbol_p.h> #include <qv4object_p.h> #include <qv4objectproto_p.h> #include <private/qv4mm_p.h> @@ -84,7 +86,7 @@ bool Value::toBooleanImpl(Value val) #ifdef V4_BOOTSTRAP Q_UNIMPLEMENTED(); #else - if (b->vtable()->isString) + if (b->internalClass->vtable->isString) return static_cast<Heap::String *>(b)->length() > 0; #endif return true; @@ -107,6 +109,11 @@ double Value::toNumberImpl(Value val) #else if (String *s = val.stringValue()) return RuntimeHelpers::stringToNumber(s->toQString()); + if (val.isSymbol()) { + Managed &m = static_cast<Managed &>(val); + m.engine()->throwTypeError(); + return 0; + } { Q_ASSERT(val.isObject()); Scope scope(val.objectValue()->engine()); @@ -145,6 +152,8 @@ QString Value::toQStringNoThrow() const case Value::Managed_Type: if (String *s = stringValue()) return s->toQString(); + if (Symbol *s = symbolValue()) + return s->descriptiveString(); { Q_ASSERT(isObject()); Scope scope(objectValue()->engine()); @@ -197,9 +206,12 @@ QString Value::toQString() const else return QStringLiteral("false"); case Value::Managed_Type: - if (String *s = stringValue()) + if (String *s = stringValue()) { return s->toQString(); - { + } else if (isSymbol()) { + static_cast<const Managed *>(this)->engine()->throwTypeError(); + return QString(); + } else { Q_ASSERT(isObject()); Scope scope(objectValue()->engine()); ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT)); @@ -217,6 +229,25 @@ QString Value::toQString() const } } // switch } + +QV4::PropertyKey Value::toPropertyKey(ExecutionEngine *e) const +{ + if (isInteger() && int_32() >= 0) + return PropertyKey::fromArrayIndex(static_cast<uint>(int_32())); + if (isStringOrSymbol()) { + Scope scope(e); + ScopedStringOrSymbol s(scope, this); + return s->toPropertyKey(); + } + Scope scope(e); + ScopedValue v(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT)); + if (!v->isStringOrSymbol()) + v = v->toString(e); + if (e->hasException) + return PropertyKey::invalid(); + ScopedStringOrSymbol s(scope, v); + return s->toPropertyKey(); +} #endif // V4_BOOTSTRAP bool Value::sameValue(Value other) const { @@ -235,6 +266,25 @@ bool Value::sameValue(Value other) const { return false; } +bool Value::sameValueZero(Value other) const { + if (_val == other._val) + return true; + String *s = stringValue(); + String *os = other.stringValue(); + if (s && os) + return s->isEqualTo(os); + if (isInteger() && other.isDouble()) + return double(int_32()) == other.doubleValue(); + if (isDouble() && other.isInteger()) + return other.int_32() == doubleValue(); + if (isDouble() && other.isDouble()) { + if (doubleValue() == 0 && other.doubleValue() == 0) { + return true; + } + } + return false; +} + #ifndef V4_BOOTSTRAP Heap::String *Value::toString(ExecutionEngine *e, Value val) { diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index a5ee6b5373..6a6df3eb6d 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -56,6 +56,7 @@ #include <QtCore/QString> #include "qv4global_p.h" #include <private/qv4heap_p.h> +#include <private/qv4internalclass_p.h> #include <private/qnumeric_p.h> @@ -184,23 +185,7 @@ public: QML_NEARLY_ALWAYS_INLINE void setEmpty() { - setTagValue(quint32(ValueTypeInternal::Empty), value()); - } - - QML_NEARLY_ALWAYS_INLINE void setEmpty(int i) - { - setTagValue(quint32(ValueTypeInternal::Empty), quint32(i)); - } - - QML_NEARLY_ALWAYS_INLINE void setEmpty(quint32 i) - { - setTagValue(quint32(ValueTypeInternal::Empty), i); - } - - QML_NEARLY_ALWAYS_INLINE quint32 emptyValue() - { - Q_ASSERT(isEmpty()); - return quint32(value()); + setTagValue(quint32(ValueTypeInternal::Empty), 0); } // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible @@ -248,7 +233,8 @@ public: IsManagedOrUndefined_Shift = 64-15, IsIntegerConvertible_Shift = 64-15, IsIntegerOrBool_Shift = 64-16, - QuickType_Shift = 64 - 17 + QuickType_Shift = 64 - 17, + IsPositiveIntShift = 31 }; static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 @@ -315,6 +301,14 @@ public: } inline bool isNaN() const { return (tag() & 0x7ffc0000 ) == 0x00040000; } + inline bool isPositiveInt() const { +#if QT_POINTER_SIZE == 4 + return isInteger() && int_32() >= 0; +#else + return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1); +#endif + } + QML_NEARLY_ALWAYS_INLINE double doubleValue() const { Q_ASSERT(isDouble()); double d; @@ -331,6 +325,8 @@ public: Q_ASSERT(isDouble()); } inline bool isString() const; + inline bool isStringOrSymbol() const; + inline bool isSymbol() const; inline bool isObject() const; inline bool isFunctionObject() const; inline bool isInt32() { @@ -338,14 +334,17 @@ public: return true; if (isDouble()) { double d = doubleValue(); - int i = (int)d; - if (i == d && !(d == 0 && std::signbit(d))) { - setInt_32(i); + if (isInt32(d)) { + setInt_32(int(d)); return true; } } return false; } + QML_NEARLY_ALWAYS_INLINE static bool isInt32(double d) { + int i = int(d); + return (i == d && !(d == 0 && std::signbit(d))); + } double asDouble() const { if (tag() == quint32(ValueTypeInternal::Integer)) return int_32(); @@ -362,7 +361,17 @@ public: QML_NEARLY_ALWAYS_INLINE String *stringValue() const { if (!isString()) return nullptr; - return reinterpret_cast<String*>(const_cast<Value *>(this)); + return reinterpret_cast<String *>(const_cast<Value *>(this)); + } + QML_NEARLY_ALWAYS_INLINE StringOrSymbol *stringOrSymbolValue() const { + if (!isStringOrSymbol()) + return nullptr; + return reinterpret_cast<StringOrSymbol *>(const_cast<Value *>(this)); + } + QML_NEARLY_ALWAYS_INLINE Symbol *symbolValue() const { + if (!isSymbol()) + return nullptr; + return reinterpret_cast<Symbol *>(const_cast<Value *>(this)); } QML_NEARLY_ALWAYS_INLINE Object *objectValue() const { if (!isObject()) @@ -388,6 +397,8 @@ public: int toUInt16() const; inline int toInt32() const; inline unsigned int toUInt32() const; + qint64 toLength() const; + inline qint64 toIndex() const; bool toBoolean() const { if (integerCompatible()) @@ -407,6 +418,8 @@ public: return reinterpret_cast<Heap::String *>(m()); return toString(e, *this); } + QV4::PropertyKey toPropertyKey(ExecutionEngine *e) const; + static Heap::String *toString(ExecutionEngine *e, Value val); Heap::Object *toObject(ExecutionEngine *e) const { if (isObject()) @@ -428,11 +441,11 @@ public: if (!isManaged()) return nullptr; - Q_ASSERT(m()->vtable()); + Q_ASSERT(m()->internalClass->vtable); #if !defined(QT_NO_QOBJECT_CHECK) static_cast<const T *>(this)->qt_check_for_QMANAGED_macro(static_cast<const T *>(this)); #endif - const VTable *vt = m()->vtable(); + const VTable *vt = m()->internalClass->vtable; while (vt) { if (vt == T::staticVTable()) return static_cast<const T *>(this); @@ -455,8 +468,6 @@ public: return static_cast<const T *>(managed()); } - inline uint asArrayIndex() const; - inline bool asArrayIndex(uint &idx) const; #ifndef V4_BOOTSTRAP uint asArrayLength(bool *ok) const; #endif @@ -465,8 +476,9 @@ public: ReturnedValue asReturnedValue() const { return _val; } static Value fromReturnedValue(ReturnedValue val) { Value v; v._val = val; return v; } - // Section 9.12 + // As per ES specs bool sameValue(Value other) const; + bool sameValueZero(Value other) const; inline void mark(MarkStack *markStack); @@ -500,18 +512,32 @@ inline void Value::mark(MarkStack *markStack) inline bool Value::isString() const { Heap::Base *b = heapObject(); - return b && b->vtable()->isString; + return b && b->internalClass->vtable->isString; +} + +bool Value::isStringOrSymbol() const +{ + Heap::Base *b = heapObject(); + return b && b->internalClass->vtable->isStringOrSymbol; +} + +bool Value::isSymbol() const +{ + Heap::Base *b = heapObject(); + return b && b->internalClass->vtable->isStringOrSymbol && !b->internalClass->vtable->isString; } + inline bool Value::isObject() const + { Heap::Base *b = heapObject(); - return b && b->vtable()->isObject; + return b && b->internalClass->vtable->isObject; } inline bool Value::isFunctionObject() const { Heap::Base *b = heapObject(); - return b && b->vtable()->isFunctionObject; + return b && b->internalClass->vtable->isFunctionObject; } inline bool Value::isPrimitive() const @@ -528,43 +554,6 @@ inline double Value::toNumber() const return toNumberImpl(); } - -#ifndef V4_BOOTSTRAP -inline uint Value::asArrayIndex() const -{ -#if QT_POINTER_SIZE == 8 - if (!isNumber()) - return UINT_MAX; - if (isInteger()) - return int_32() >= 0 ? (uint)int_32() : UINT_MAX; -#else - if (isInteger() && int_32() >= 0) - return (uint)int_32(); - if (!isDouble()) - return UINT_MAX; -#endif - double d = doubleValue(); - uint idx = (uint)d; - if (idx != d) - return UINT_MAX; - return idx; -} - -inline bool Value::asArrayIndex(uint &idx) const -{ - if (Q_LIKELY(!isDouble())) { - if (Q_LIKELY(isInteger() && int_32() >= 0)) { - idx = (uint)int_32(); - return true; - } - return false; - } - double d = doubleValue(); - idx = (uint)d; - return (idx == d && idx != UINT_MAX); -} -#endif - inline ReturnedValue Heap::Base::asReturnedValue() const { @@ -576,7 +565,6 @@ ReturnedValue Heap::Base::asReturnedValue() const struct Q_QML_PRIVATE_EXPORT Primitive : public Value { inline static Primitive emptyValue(); - inline static Primitive emptyValue(uint v); static inline Primitive fromBoolean(bool b); static inline Primitive fromInt32(int i); inline static Primitive undefinedValue(); @@ -602,14 +590,7 @@ inline Primitive Primitive::undefinedValue() inline Primitive Primitive::emptyValue() { Primitive v; - v.setEmpty(0); - return v; -} - -inline Primitive Primitive::emptyValue(uint e) -{ - Primitive v; - v.setEmpty(e); + v.setEmpty(); return v; } @@ -755,7 +736,7 @@ struct Encode { } static ReturnedValue smallestNumber(double d) { - if (static_cast<int>(d) == d && !(d == 0. && std::signbit(d))) + if (Value::isInt32(d)) return Encode(static_cast<int>(d)); else return Encode(d); @@ -788,6 +769,31 @@ inline unsigned int Value::toUInt32() const return static_cast<unsigned int>(toInt32()); } +inline qint64 Value::toLength() const +{ + if (Q_LIKELY(integerCompatible())) + return int_32() < 0 ? 0 : int_32(); + double i = Primitive::toInteger(isDouble() ? doubleValue() : toNumberImpl()); + if (i <= 0) + return 0; + if (i > (static_cast<qint64>(1) << 53) - 1) + return (static_cast<qint64>(1) << 53) - 1; + return static_cast<qint64>(i); +} + +inline qint64 Value::toIndex() const +{ + qint64 idx; + if (Q_LIKELY(integerCompatible())) { + idx = int_32(); + } else { + idx = static_cast<qint64>(Primitive::toInteger(isDouble() ? doubleValue() : toNumberImpl())); + } + if (idx > (static_cast<qint64>(1) << 53) - 1) + idx = -1; + return idx; +} + inline double Value::toInteger() const { if (integerCompatible()) @@ -831,7 +837,7 @@ struct ValueArray { WriteBarrier::write(e, base(), values[index].data_ptr(), v.asReturnedValue()); } void set(EngineBase *e, uint index, Heap::Base *b) { - WriteBarrier::write(e, base(), values[index].data_ptr(), b->asReturnedValue()); + WriteBarrier::write(e, base(), values[index].data_ptr(), Value::fromHeapObject(b).asReturnedValue()); } inline const Value &operator[] (uint index) const { Q_ASSERT(index < alloc); @@ -884,7 +890,6 @@ struct ValueArray { // have wrong offsets between host and target. Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8); - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index bee17e0390..ef0877dbd0 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -70,7 +70,7 @@ bool VariantObject::Data::isScarce() const return t == QVariant::Pixmap || t == QVariant::Image; } -bool VariantObject::isEqualTo(Managed *m, Managed *other) +bool VariantObject::virtualIsEqualTo(Managed *m, Managed *other) { Q_ASSERT(m->as<QV4::VariantObject>()); QV4::VariantObject *lv = static_cast<QV4::VariantObject *>(m); diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h index 62fa7ff9a8..78e0a5373a 100644 --- a/src/qml/jsruntime/qv4variantobject_p.h +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -99,7 +99,8 @@ struct VariantObject : Object void addVmePropertyReference() const; void removeVmePropertyReference() const; - static bool isEqualTo(Managed *m, Managed *other); +protected: + static bool virtualIsEqualTo(Managed *m, Managed *other); }; struct VariantPrototype : VariantObject diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index a1f5b01fa9..53e5632eff 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -55,12 +55,13 @@ #include <private/qv4string_p.h> #include <private/qv4profiling_p.h> #include <private/qv4jscall_p.h> +#include <private/qv4generatorobject_p.h> #include <private/qqmljavascriptexpression_p.h> #include <iostream> #include "qv4alloca_p.h" -#include <private/qv4jit_p.h> +#include <private/qv4baselinejit_p.h> #undef COUNT_INSTRUCTIONS @@ -327,7 +328,7 @@ static struct InstrCount { #ifdef MOTH_COMPUTED_GOTO #define MOTH_END_INSTR(instr) \ - MOTH_DISPATCH() \ + MOTH_DISPATCH_SINGLE() \ } #else // !MOTH_COMPUTED_GOTO #define MOTH_END_INSTR(instr) \ @@ -343,7 +344,7 @@ static struct InstrCount { #endif #define CHECK_EXCEPTION \ if (engine->hasException) \ - goto catchException + goto handleUnwind static inline Heap::CallContext *getScope(QV4::Value *stack, int level) { @@ -361,95 +362,6 @@ static inline const QV4::Value &constant(Function *function, int index) return function->compilationUnit->constants[index]; } - -static bool compareEqual(QV4::Value lhs, QV4::Value rhs) -{ - redo: - if (lhs.asReturnedValue() == rhs.asReturnedValue()) - return !lhs.isNaN(); - - int lt = lhs.quickType(); - int rt = rhs.quickType(); - if (rt < lt) { - qSwap(lhs, rhs); - qSwap(lt, rt); - } - - switch (lt) { - case QV4::Value::QT_ManagedOrUndefined: - if (lhs.isUndefined()) - return rhs.isNullOrUndefined(); - Q_FALLTHROUGH(); - case QV4::Value::QT_ManagedOrUndefined1: - case QV4::Value::QT_ManagedOrUndefined2: - case QV4::Value::QT_ManagedOrUndefined3: - // LHS: Managed - switch (rt) { - case QV4::Value::QT_ManagedOrUndefined: - if (rhs.isUndefined()) - return false; - Q_FALLTHROUGH(); - case QV4::Value::QT_ManagedOrUndefined1: - case QV4::Value::QT_ManagedOrUndefined2: - case QV4::Value::QT_ManagedOrUndefined3: { - // RHS: Managed - Heap::Base *l = lhs.m(); - Heap::Base *r = rhs.m(); - Q_ASSERT(l); - Q_ASSERT(r); - if (l->vtable()->isString == r->vtable()->isString) - return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs)); - if (l->vtable()->isString) { - rhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT)); - break; - } else { - Q_ASSERT(r->vtable()->isString); - lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT)); - break; - } - return false; - } - case QV4::Value::QT_Empty: - Q_UNREACHABLE(); - case QV4::Value::QT_Null: - return false; - case QV4::Value::QT_Bool: - case QV4::Value::QT_Int: - rhs = Primitive::fromDouble(rhs.int_32()); - // fall through - default: // double - if (lhs.m()->vtable()->isString) - return RuntimeHelpers::toNumber(lhs) == rhs.doubleValue(); - else - lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT)); - } - goto redo; - case QV4::Value::QT_Empty: - Q_UNREACHABLE(); - case QV4::Value::QT_Null: - return rhs.isNull(); - case QV4::Value::QT_Bool: - case QV4::Value::QT_Int: - switch (rt) { - case QV4::Value::QT_ManagedOrUndefined: - case QV4::Value::QT_ManagedOrUndefined1: - case QV4::Value::QT_ManagedOrUndefined2: - case QV4::Value::QT_ManagedOrUndefined3: - case QV4::Value::QT_Empty: - case QV4::Value::QT_Null: - Q_UNREACHABLE(); - case QV4::Value::QT_Bool: - case QV4::Value::QT_Int: - return lhs.int_32() == rhs.int_32(); - default: // double - return lhs.int_32() == rhs.doubleValue(); - } - default: // double - Q_ASSERT(rhs.isDouble()); - return lhs.doubleValue() == rhs.doubleValue(); - } -} - static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) { redo: @@ -462,7 +374,7 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) case QV4::Value::QT_ManagedOrUndefined2: case QV4::Value::QT_ManagedOrUndefined3: // LHS: Managed - if (lhs.m()->vtable()->isString) + if (lhs.m()->internalClass->vtable->isString) return RuntimeHelpers::stringToNumber(static_cast<String &>(lhs).toQString()) == rhs; accumulator = lhs; lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(accumulator), PREFERREDTYPE_HINT)); @@ -479,7 +391,7 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) } } -#define STORE_IP() frame.instructionPointer = int(code - codeStart); +#define STORE_IP() frame->instructionPointer = int(code - function->codeData); #define STORE_ACC() accumulator = acc; #define ACC Primitive::fromReturnedValue(acc) #define VALUE_TO_INT(i, val) \ @@ -500,82 +412,46 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) } \ } while (false) -QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObject, const QV4::Value *argv, int argc) +ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) { qt_v4ResolvePendingBreakpointsHook(); - ExecutionEngine *engine; - QV4::Value *stack; - CppStackFrame frame; - frame.originalArguments = argv; - frame.originalArgumentsCount = argc; - Function *function; - - { - Heap::ExecutionContext *scope; - - quintptr d = reinterpret_cast<quintptr>(fo); - if (d & 0x1) { - // we don't have a FunctionObject, but a ExecData - ExecData *data = reinterpret_cast<ExecData *>(d - 1); - function = data->function; - scope = data->scope->d(); - fo = nullptr; - } else { - function = fo->function(); - scope = fo->scope(); - } - - engine = function->internalClass->engine; - - stack = engine->jsStackTop; - CallData *callData = reinterpret_cast<CallData *>(stack); - callData->function = fo ? fo->asReturnedValue() : Encode::undefined(); - callData->context = scope; - callData->accumulator = Encode::undefined(); - callData->thisObject = thisObject ? *thisObject : Primitive::undefinedValue(); - if (argc > int(function->nFormals)) - argc = int(function->nFormals); - callData->setArgc(argc); - - int jsStackFrameSize = offsetof(CallData, args)/sizeof(Value) + function->compiledFunction->nRegisters; - engine->jsStackTop += jsStackFrameSize; - memcpy(callData->args, argv, argc*sizeof(Value)); - for (Value *v = callData->args + argc; v < engine->jsStackTop; ++v) - *v = Encode::undefined(); - - frame.parent = engine->currentStackFrame; - frame.v4Function = function; - frame.instructionPointer = 0; - frame.jsFrame = callData; - engine->currentStackFrame = &frame; - } CHECK_STACK_LIMITS(engine); + Function *function = frame->v4Function; Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling QV4::Debugging::Debugger *debugger = engine->debugger(); - const uchar *exceptionHandler = nullptr; - - QV4::Value &accumulator = frame.jsFrame->accumulator; - QV4::ReturnedValue acc = Encode::undefined(); #ifdef V4_ENABLE_JIT - if (function->jittedCode == nullptr && debugger == nullptr) { - if (engine->canJIT(function)) - QV4::JIT::BaselineJIT(function).generate(); - else - ++function->interpreterCallCount; + if (debugger == nullptr) { + if (function->jittedCode == nullptr) { + if (engine->canJIT(function)) + QV4::JIT::BaselineJIT(function).generate(); + else + ++function->interpreterCallCount; + } + if (function->jittedCode != nullptr) + return function->jittedCode(frame, engine); } #endif // V4_ENABLE_JIT + // interpreter if (debugger) debugger->enteringFunction(); - if (function->jittedCode != nullptr && debugger == nullptr) { - acc = function->jittedCode(&frame, engine); - } else { - // interpreter - const uchar *code = function->codeData; - const uchar *codeStart = code; + ReturnedValue result = interpret(frame, engine, function->codeData); + + if (debugger) + debugger->leavingFunction(result); + + return result; +} + +QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *code) +{ + QV4::Function *function = frame->v4Function; + QV4::Value &accumulator = frame->jsFrame->accumulator; + QV4::ReturnedValue acc = accumulator.asReturnedValue(); + Value *stack = reinterpret_cast<Value *>(frame->jsFrame); MOTH_JUMP_TABLE; @@ -629,12 +505,14 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_BEGIN_INSTR(LoadLocal) auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); + Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext); acc = cc->locals[index].asReturnedValue(); MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) CHECK_EXCEPTION; auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); + Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext); QV4::WriteBarrier::write(engine, cc, cc->locals.values[index].data_ptr(), acc); MOTH_END_INSTR(StoreLocal) @@ -689,58 +567,37 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_BEGIN_INSTR(LoadElement) STORE_IP(); - acc = Runtime::method_loadElement(engine, STACK_VALUE(base), STACK_VALUE(index)); - CHECK_EXCEPTION; - MOTH_END_INSTR(LoadElement) - - MOTH_BEGIN_INSTR(LoadElementA) - STORE_IP(); STORE_ACC(); acc = Runtime::method_loadElement(engine, STACK_VALUE(base), accumulator); CHECK_EXCEPTION; - MOTH_END_INSTR(LoadElementA) + MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) STORE_IP(); STORE_ACC(); - if (!Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator) && function->isStrict()) - engine->throwTypeError(); + Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) STORE_IP(); - acc = Runtime::method_loadProperty(engine, STACK_VALUE(base), name); - CHECK_EXCEPTION; - MOTH_END_INSTR(LoadProperty) - - MOTH_BEGIN_INSTR(LoadPropertyA) - STORE_IP(); STORE_ACC(); acc = Runtime::method_loadProperty(engine, accumulator, name); CHECK_EXCEPTION; - MOTH_END_INSTR(LoadPropertyA) + MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) STORE_IP(); - QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; - acc = l->getter(l, engine, STACK_VALUE(base)); - CHECK_EXCEPTION; - MOTH_END_INSTR(GetLookup) - - MOTH_BEGIN_INSTR(GetLookupA) - STORE_IP(); STORE_ACC(); QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; acc = l->getter(l, engine, accumulator); CHECK_EXCEPTION; - MOTH_END_INSTR(GetLookupA) + MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) STORE_IP(); STORE_ACC(); - if (!Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator) && function->isStrict()) - engine->throwTypeError(); + Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreProperty) @@ -753,6 +610,20 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj CHECK_EXCEPTION; MOTH_END_INSTR(SetLookup) + MOTH_BEGIN_INSTR(LoadSuperProperty) + STORE_IP(); + STORE_ACC(); + acc = Runtime::method_loadSuperProperty(engine, STACK_VALUE(property)); + CHECK_EXCEPTION; + MOTH_END_INSTR(LoadSuperProperty) + + MOTH_BEGIN_INSTR(StoreSuperProperty) + STORE_IP(); + STORE_ACC(); + Runtime::method_storeSuperProperty(engine, STACK_VALUE(property), accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(StoreSuperProperty) + MOTH_BEGIN_INSTR(StoreScopeObjectProperty) STORE_ACC(); Runtime::method_storeQmlScopeObjectProperty(engine, STACK_VALUE(base), propertyIndex, accumulator); @@ -784,14 +655,33 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj CHECK_EXCEPTION; MOTH_END_INSTR(LoadIdObject) + MOTH_BEGIN_INSTR(Yield) + frame->yield = code; + return acc; + MOTH_END_INSTR(Yield) + + MOTH_BEGIN_INSTR(Resume) + // check exception, in case the generator was called with throw() or return() + if (engine->hasException) { + // an empty value indicates that the generator was called with return() + if (engine->exceptionValue->asReturnedValue() != Primitive::emptyValue().asReturnedValue()) + goto handleUnwind; + engine->hasException = false; + *engine->exceptionValue = Primitive::undefinedValue(); + } else { + code += offset; + } + MOTH_END_INSTR(Resume) + MOTH_BEGIN_INSTR(CallValue) STORE_IP(); Value func = STACK_VALUE(name); if (Q_UNLIKELY(!func.isFunctionObject())) { acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); - goto catchException; + goto handleUnwind; } - acc = static_cast<const FunctionObject &>(func).call(nullptr, stack + argv, argc); + Value undef = Primitive::undefinedValue(); + acc = static_cast<const FunctionObject &>(func).call(&undef, stack + argv, argc); CHECK_EXCEPTION; MOTH_END_INSTR(CallValue) @@ -809,7 +699,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj if (Q_UNLIKELY(!f.isFunctionObject())) { acc = engine->throwTypeError(); - goto catchException; + goto handleUnwind; } acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc); @@ -852,15 +742,49 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj CHECK_EXCEPTION; MOTH_END_INSTR(CallContextObjectProperty) - MOTH_BEGIN_INSTR(SetExceptionHandler) - exceptionHandler = offset ? code + offset : nullptr; - MOTH_END_INSTR(SetExceptionHandler) + MOTH_BEGIN_INSTR(CallWithSpread) + STORE_IP(); + acc = Runtime::method_callWithSpread(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(CallWithSpread) + + MOTH_BEGIN_INSTR(Construct) + STORE_IP(); + acc = Runtime::method_construct(engine, STACK_VALUE(func), ACC, stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(Construct) + + MOTH_BEGIN_INSTR(ConstructWithSpread) + STORE_IP(); + acc = Runtime::method_constructWithSpread(engine, STACK_VALUE(func), ACC, stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(ConstructWithSpread) + + MOTH_BEGIN_INSTR(SetUnwindHandler) + frame->unwindHandler = offset ? code + offset : nullptr; + MOTH_END_INSTR(SetUnwindHandler) + + MOTH_BEGIN_INSTR(UnwindDispatch) + CHECK_EXCEPTION; + if (frame->unwindLevel) { + --frame->unwindLevel; + if (frame->unwindLevel) + goto handleUnwind; + code = frame->unwindLabel; + } + MOTH_END_INSTR(UnwindDispatch) + + MOTH_BEGIN_INSTR(UnwindToLabel) + frame->unwindLevel = level; + frame->unwindLabel = code + offset; + goto handleUnwind; + MOTH_END_INSTR(UnwindToLabel) MOTH_BEGIN_INSTR(ThrowException) STORE_IP(); STORE_ACC(); Runtime::method_throwException(engine, accumulator); - goto catchException; + goto handleUnwind; MOTH_END_INSTR(ThrowException) MOTH_BEGIN_INSTR(GetException) @@ -870,71 +794,90 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_END_INSTR(HasException) MOTH_BEGIN_INSTR(SetException) - *engine->exceptionValue = acc; - engine->hasException = true; + if (acc != Primitive::emptyValue().asReturnedValue()) { + *engine->exceptionValue = acc; + engine->hasException = true; + } MOTH_END_INSTR(SetException) MOTH_BEGIN_INSTR(PushCatchContext) - STACK_VALUE(reg) = STACK_VALUE(CallData::Context); ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); - STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, name); + STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, index, name); MOTH_END_INSTR(PushCatchContext) MOTH_BEGIN_INSTR(CreateCallContext) - stack[CallData::Context] = ExecutionContext::newCallContext(&frame); + stack[CallData::Context] = ExecutionContext::newCallContext(frame); MOTH_END_INSTR(CreateCallContext) MOTH_BEGIN_INSTR(PushWithContext) STORE_IP(); STORE_ACC(); - accumulator = accumulator.toObject(engine); + auto ctx = Runtime::method_createWithContext(engine, stack); CHECK_EXCEPTION; - STACK_VALUE(reg) = STACK_VALUE(CallData::Context); - ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); - STACK_VALUE(CallData::Context) = Runtime::method_createWithContext(c, accumulator); + STACK_VALUE(CallData::Context) = ctx; MOTH_END_INSTR(PushWithContext) + MOTH_BEGIN_INSTR(PushBlockContext) + STORE_ACC(); + ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); + STACK_VALUE(CallData::Context) = Runtime::method_createBlockContext(c, index); + MOTH_END_INSTR(PushBlockContext) + + MOTH_BEGIN_INSTR(CloneBlockContext) + STORE_ACC(); + ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); + STACK_VALUE(CallData::Context) = Runtime::method_cloneBlockContext(c); + MOTH_END_INSTR(CloneBlockContext) + + MOTH_BEGIN_INSTR(PushScriptContext) + STACK_VALUE(CallData::Context) = Runtime::method_createScriptContext(engine, index); + MOTH_END_INSTR(PushScriptContext) + + MOTH_BEGIN_INSTR(PopScriptContext) + STACK_VALUE(CallData::Context) = Runtime::method_popScriptContext(engine); + MOTH_END_INSTR(PopScriptContext) + MOTH_BEGIN_INSTR(PopContext) - STACK_VALUE(CallData::Context) = STACK_VALUE(reg); + ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); + STACK_VALUE(CallData::Context) = c->d()->outer; MOTH_END_INSTR(PopContext) - MOTH_BEGIN_INSTR(ForeachIteratorObject) + MOTH_BEGIN_INSTR(GetIterator) STORE_ACC(); - acc = Runtime::method_foreachIterator(engine, accumulator); + acc = Runtime::method_getIterator(engine, accumulator, iterator); CHECK_EXCEPTION; - MOTH_END_INSTR(ForeachIteratorObject) + MOTH_END_INSTR(GetIterator) - MOTH_BEGIN_INSTR(ForeachNextPropertyName) + MOTH_BEGIN_INSTR(IteratorNext) STORE_ACC(); - acc = Runtime::method_foreachNextPropertyName(accumulator); + acc = Runtime::method_iteratorNext(engine, accumulator, &STACK_VALUE(value)); CHECK_EXCEPTION; - MOTH_END_INSTR(ForeachNextPropertyName) + MOTH_END_INSTR(IteratorNext) - MOTH_BEGIN_INSTR(DeleteMember) - if (!Runtime::method_deleteMember(engine, STACK_VALUE(base), member)) { - if (function->isStrict()) { - STORE_IP(); - engine->throwTypeError(); - goto catchException; - } - acc = Encode(false); - } else { - acc = Encode(true); - } - MOTH_END_INSTR(DeleteMember) + MOTH_BEGIN_INSTR(IteratorClose) + STORE_ACC(); + acc = Runtime::method_iteratorClose(engine, accumulator, STACK_VALUE(done)); + CHECK_EXCEPTION; + MOTH_END_INSTR(IteratorClose) + + MOTH_BEGIN_INSTR(DestructureRestElement) + STORE_ACC(); + acc = Runtime::method_destructureRestElement(engine, ACC); + CHECK_EXCEPTION; + MOTH_END_INSTR(DestructureRestElement) - MOTH_BEGIN_INSTR(DeleteSubscript) - if (!Runtime::method_deleteElement(engine, STACK_VALUE(base), STACK_VALUE(index))) { + MOTH_BEGIN_INSTR(DeleteProperty) + if (!Runtime::method_deleteProperty(engine, STACK_VALUE(base), STACK_VALUE(index))) { if (function->isStrict()) { STORE_IP(); engine->throwTypeError(); - goto catchException; + goto handleUnwind; } acc = Encode(false); } else { acc = Encode(true); } - MOTH_END_INSTR(DeleteSubscript) + MOTH_END_INSTR(DeleteProperty) MOTH_BEGIN_INSTR(DeleteName) if (!Runtime::method_deleteName(engine, name)) { @@ -942,7 +885,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj STORE_IP(); QString n = function->compilationUnit->runtimeStrings[name]->toQString(); engine->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(n)); - goto catchException; + goto handleUnwind; } acc = Encode(false); } else { @@ -970,9 +913,13 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_BEGIN_INSTR(DefineObjectLiteral) QV4::Value *arguments = stack + args; - acc = Runtime::method_objectLiteral(engine, arguments, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags); + acc = Runtime::method_objectLiteral(engine, internalClassId, argc, arguments); MOTH_END_INSTR(DefineObjectLiteral) + MOTH_BEGIN_INSTR(CreateClass) + acc = Runtime::method_createClass(engine, classIndex, STACK_VALUE(heritage), stack + computedNames); + MOTH_END_INSTR(CreateClass) + MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) acc = Runtime::method_createMappedArgumentsObject(engine); MOTH_END_INSTR(CreateMappedArgumentsObject) @@ -981,6 +928,10 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj acc = Runtime::method_createUnmappedArgumentsObject(engine); MOTH_END_INSTR(CreateUnmappedArgumentsObject) + MOTH_BEGIN_INSTR(CreateRestParameter) + acc = Runtime::method_createRestParameter(engine, argIndex); + MOTH_END_INSTR(CreateRestParameter) + MOTH_BEGIN_INSTR(ConvertThisToObject) Value *t = &stack[CallData::This]; if (!t->isObject()) { @@ -993,11 +944,20 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj } MOTH_END_INSTR(ConvertThisToObject) - MOTH_BEGIN_INSTR(Construct) - STORE_IP(); - acc = Runtime::method_construct(engine, STACK_VALUE(func), stack + argv, argc); + MOTH_BEGIN_INSTR(LoadSuperConstructor) + const Value *f = &stack[CallData::Function]; + if (!f->isFunctionObject()) { + engine->throwTypeError(); + } else { + acc = static_cast<const Object *>(f)->getPrototypeOf()->asReturnedValue(); + } CHECK_EXCEPTION; - MOTH_END_INSTR(Construct) + MOTH_END_INSTR(LoadSuperConstructor) + + MOTH_BEGIN_INSTR(ToObject) + acc = ACC.toObject(engine)->asReturnedValue(); + CHECK_EXCEPTION; + MOTH_END_INSTR(ToObject) MOTH_BEGIN_INSTR(Jump) code += offset; @@ -1023,6 +983,16 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj } MOTH_END_INSTR(JumpFalse) + MOTH_BEGIN_INSTR(JumpNoException) + if (!engine->hasException) + code += offset; + MOTH_END_INSTR(JumpNoException) + + MOTH_BEGIN_INSTR(JumpNotUndefined) + if (Q_LIKELY(acc != QV4::Encode::undefined())) + code += offset; + MOTH_END_INSTR(JumpNotUndefined) + MOTH_BEGIN_INSTR(CmpEqNull) acc = Encode(ACC.isNullOrUndefined()); MOTH_END_INSTR(CmpEqNull) @@ -1059,7 +1029,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj acc = Encode(left.int_32() == ACC.int_32()); } else { STORE_ACC(); - acc = Encode(compareEqual(left, accumulator)); + acc = Encode(bool(Runtime::method_compareEqual(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpEq) @@ -1070,7 +1040,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj acc = Encode(bool(left.int_32() != ACC.int_32())); } else { STORE_ACC(); - acc = Encode(!compareEqual(left, accumulator)); + acc = Encode(bool(!Runtime::method_compareEqual(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpNe) @@ -1154,27 +1124,11 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_END_INSTR(CmpIn) MOTH_BEGIN_INSTR(CmpInstanceOf) - // 11.8.6, 5: rval must be an Object - if (Q_UNLIKELY(!Primitive::fromReturnedValue(acc).isObject())) { - acc = engine->throwTypeError(); - goto catchException; - } - - // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result. - acc = Primitive::fromReturnedValue(acc).objectValue()->instanceOf(STACK_VALUE(lhs)); + STORE_ACC(); + acc = Runtime::method_instanceof(engine, STACK_VALUE(lhs), ACC); CHECK_EXCEPTION; MOTH_END_INSTR(CmpInstanceOf) - MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt) - if (STACK_VALUE(lhs).int_32() != rhs || STACK_VALUE(lhs).isUndefined()) - code += offset; - MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) - - MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt) - if (STACK_VALUE(lhs).int_32() == rhs && !STACK_VALUE(lhs).isUndefined()) - code += offset; - MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) - MOTH_BEGIN_INSTR(UNot) if (ACC.integerCompatible()) { acc = Encode(!static_cast<bool>(ACC.int_32())); @@ -1259,6 +1213,16 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj } MOTH_END_INSTR(Sub) + MOTH_BEGIN_INSTR(Exp) + const Value left = STACK_VALUE(lhs); + double base = left.toNumber(); + double exp = ACC.toNumber(); + if (qIsInf(exp) && (base == 1 || base == -1)) + acc = Encode(qSNaN()); + else + acc = Encode(pow(base,exp)); + MOTH_END_INSTR(Exp) + MOTH_BEGIN_INSTR(Mul) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { @@ -1351,7 +1315,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_END_INSTR(ShlConst) MOTH_BEGIN_INSTR(Ret) - goto functionExit; + return acc; MOTH_END_INSTR(Ret) MOTH_BEGIN_INSTR(Debug) @@ -1369,21 +1333,12 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj STACK_VALUE(result) = Runtime::method_loadQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine)); MOTH_END_INSTR(LoadQmlImportedScripts) - catchException: - Q_ASSERT(engine->hasException); - if (!exceptionHandler) { + handleUnwind: + Q_ASSERT(engine->hasException || frame->unwindLevel); + if (!frame->unwindHandler) { acc = Encode::undefined(); - goto functionExit; + return acc; } - code = exceptionHandler; - } + code = frame->unwindHandler; } - -functionExit: - if (QV4::Debugging::Debugger *debugger = engine->debugger()) - debugger->leavingFunction(ACC.asReturnedValue()); - engine->currentStackFrame = frame.parent; - engine->jsStackTop = stack; - - return acc; } diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index 3b7723ca7e..8a76e60f20 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -65,13 +65,8 @@ public: QV4::Function *function; const QV4::ExecutionContext *scope; }; - static inline - QV4::ReturnedValue exec(Function *v4Function, const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) { - ExecData data{v4Function, context}; - quintptr d = reinterpret_cast<quintptr>(&data) | 0x1; - return exec(reinterpret_cast<const FunctionObject *>(d), thisObject, argv, argc); - } - static QV4::ReturnedValue exec(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc); + static QV4::ReturnedValue exec(CppStackFrame *frame, ExecutionEngine *engine); + static QV4::ReturnedValue interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *codeEntry); }; } // namespace Moth diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h new file mode 100644 index 0000000000..b4e868d45d --- /dev/null +++ b/src/qml/jsruntime/qv4vtable_p.h @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4VTABLE_P_H +#define QV4VTABLE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4global_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct VTable +{ + typedef void (*Destroy)(Heap::Base *); + typedef void (*MarkObjects)(Heap::Base *, MarkStack *markStack); + typedef bool (*IsEqualTo)(Managed *m, Managed *other); + + typedef ReturnedValue (*Get)(const Managed *, PropertyKey id, const Value *receiver, bool *hasProperty); + typedef bool (*Put)(Managed *, PropertyKey id, const Value &value, Value *receiver); + typedef bool (*DeleteProperty)(Managed *m, PropertyKey id); + typedef bool (*HasProperty)(const Managed *m, PropertyKey id); + typedef PropertyAttributes (*GetOwnProperty)(Managed *m, PropertyKey id, Property *p); + typedef bool (*DefineOwnProperty)(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs); + typedef bool (*IsExtensible)(const Managed *); + typedef bool (*PreventExtensions)(Managed *); + typedef Heap::Object *(*GetPrototypeOf)(const Managed *); + typedef bool (*SetPrototypeOf)(Managed *, const Object *); + typedef qint64 (*GetLength)(const Managed *m); + typedef void (*AdvanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); + typedef ReturnedValue (*InstanceOf)(const Object *typeObject, const Value &var); + + typedef ReturnedValue (*Call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + typedef ReturnedValue (*CallAsConstructor)(const FunctionObject *, const Value *argv, int argc, const Value *newTarget); + + const VTable * const parent; + quint32 inlinePropertyOffset : 16; + quint32 nInlineProperties : 16; + quint8 isExecutionContext; + quint8 isString; + quint8 isObject; + quint8 isFunctionObject; + quint8 isErrorObject; + quint8 isArrayData; + quint8 isStringOrSymbol; + quint8 type; + quint8 unused[4]; + const char *className; + + Destroy destroy; + MarkObjects markObjects; + IsEqualTo isEqualTo; + + Get get; + Put put; + DeleteProperty deleteProperty; + HasProperty hasProperty; + GetOwnProperty getOwnProperty; + DefineOwnProperty defineOwnProperty; + IsExtensible isExtensible; + PreventExtensions preventExtensions; + GetPrototypeOf getPrototypeOf; + SetPrototypeOf setPrototypeOf; + GetLength getLength; + AdvanceIterator advanceIterator; + InstanceOf instanceOf; + + Call call; + CallAsConstructor callAsConstructor; +}; + + +struct VTableBase { +protected: + static constexpr VTable::Destroy virtualDestroy = nullptr; + static constexpr VTable::IsEqualTo virtualIsEqualTo = nullptr; + + static constexpr VTable::Get virtualGet = nullptr; + static constexpr VTable::Put virtualPut = nullptr; + static constexpr VTable::DeleteProperty virtualDeleteProperty = nullptr; + static constexpr VTable::HasProperty virtualHasProperty = nullptr; + static constexpr VTable::GetOwnProperty virtualGetOwnProperty = nullptr; + static constexpr VTable::DefineOwnProperty virtualDefineOwnProperty = nullptr; + static constexpr VTable::IsExtensible virtualIsExtensible = nullptr; + static constexpr VTable::PreventExtensions virtualPreventExtensions = nullptr; + static constexpr VTable::GetPrototypeOf virtualGetPrototypeOf = nullptr; + static constexpr VTable::SetPrototypeOf virtualSetPrototypeOf = nullptr; + static constexpr VTable::GetLength virtualGetLength = nullptr; + static constexpr VTable::AdvanceIterator virtualAdvanceIterator = nullptr; + static constexpr VTable::InstanceOf virtualInstanceOf = nullptr; + + static constexpr VTable::Call virtualCall = nullptr; + static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr; +}; + +#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ +{ \ + parentVTable, \ + (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ + (sizeof(classname::Data) + (classname::NInlineProperties*sizeof(QV4::Value)) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \ + - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ + classname::IsExecutionContext, \ + classname::IsString, \ + classname::IsObject, \ + classname::IsFunctionObject, \ + classname::IsErrorObject, \ + classname::IsArrayData, \ + classname::IsStringOrSymbol, \ + classname::MyType, \ + { 0, 0, 0, 0 }, \ + #classname, \ + \ + classname::virtualDestroy, \ + classname::Data::markObjects, \ + classname::virtualIsEqualTo, \ + \ + classname::virtualGet, \ + classname::virtualPut, \ + classname::virtualDeleteProperty, \ + classname::virtualHasProperty, \ + classname::virtualGetOwnProperty, \ + classname::virtualDefineOwnProperty, \ + classname::virtualIsExtensible, \ + classname::virtualPreventExtensions, \ + classname::virtualGetPrototypeOf, \ + classname::virtualSetPrototypeOf, \ + classname::virtualGetLength, \ + classname::virtualAdvanceIterator, \ + classname::virtualInstanceOf, \ + \ + classname::virtualCall, \ + classname::virtualCallAsConstructor, \ +} + +#define DEFINE_MANAGED_VTABLE(classname) \ +const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0) + +#define V4_OBJECT2(DataClass, superClass) \ + private: \ + DataClass() Q_DECL_EQ_DELETE; \ + Q_DISABLE_COPY(DataClass) \ + public: \ + Q_MANAGED_CHECK \ + typedef QV4::Heap::DataClass Data; \ + typedef superClass SuperClass; \ + static const QV4::VTable static_vtbl; \ + static inline const QV4::VTable *staticVTable() { return &static_vtbl; } \ + V4_MANAGED_SIZE_TEST \ + QV4::Heap::DataClass *d_unchecked() const { return static_cast<QV4::Heap::DataClass *>(m()); } \ + QV4::Heap::DataClass *d() const { \ + QV4::Heap::DataClass *dptr = d_unchecked(); \ + dptr->_checkIsInitialized(); \ + return dptr; \ + } \ + Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value); + +#define V4_PROTOTYPE(p) \ + static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \ + { return e->p(); } + + +#define DEFINE_OBJECT_VTABLE_BASE(classname) \ + const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, (std::is_same<classname::SuperClass, Object>::value) ? nullptr : &classname::SuperClass::static_vtbl) + +#define DEFINE_OBJECT_VTABLE(classname) \ +DEFINE_OBJECT_VTABLE_BASE(classname) + +#define DEFINE_OBJECT_TEMPLATE_VTABLE(classname) \ +template<> DEFINE_OBJECT_VTABLE_BASE(classname) + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h index 53933cd090..07af5a6f7a 100644 --- a/src/qml/memory/qv4heap_p.h +++ b/src/qml/memory/qv4heap_p.h @@ -54,7 +54,7 @@ #include <private/qv4global_p.h> #include <private/qv4mmdefs_p.h> #include <private/qv4writebarrier_p.h> -#include <private/qv4internalclass_p.h> +#include <private/qv4vtable_p.h> #include <QSharedPointer> // To check if Heap::Base::init is called (meaning, all subclasses did their init and called their @@ -65,40 +65,43 @@ QT_BEGIN_NAMESPACE namespace QV4 { -struct InternalClass; +namespace Heap { -struct VTable -{ - const VTable * const parent; - uint inlinePropertyOffset : 16; - uint nInlineProperties : 16; - uint isExecutionContext : 1; - uint isString : 1; - uint isObject : 1; - uint isFunctionObject : 1; - uint isErrorObject : 1; - uint isArrayData : 1; - uint unused : 18; - uint type : 8; - const char *className; - void (*destroy)(Heap::Base *); - void (*markObjects)(Heap::Base *, MarkStack *markStack); - bool (*isEqualTo)(Managed *m, Managed *other); -}; +template <typename T, size_t o> +struct Pointer { + static Q_CONSTEXPR size_t offset = o; + T operator->() const { return get(); } + operator T () const { return get(); } -namespace Heap { + Base *base(); + + void set(EngineBase *e, T newVal) { + WriteBarrier::write(e, base(), &ptr, reinterpret_cast<Base *>(newVal)); + } + + T get() const { return reinterpret_cast<T>(ptr); } + + template <typename Type> + Type *cast() { return static_cast<Type *>(ptr); } + + Base *heapObject() const { return ptr; } + +private: + Base *ptr; +}; +typedef Pointer<char *, 0> V4PointerCheck; +Q_STATIC_ASSERT(std::is_trivial< V4PointerCheck >::value); struct Q_QML_EXPORT Base { void *operator new(size_t) = delete; - static void markObjects(Heap::Base *, MarkStack *) {} + static void markObjects(Base *, MarkStack *); - InternalClass *internalClass; + Pointer<InternalClass *, 0> internalClass; inline ReturnedValue asReturnedValue() const; inline void mark(QV4::MarkStack *markStack); - const VTable *vtable() const { return internalClass->vtable; } inline bool isMarked() const { const HeapItem *h = reinterpret_cast<const HeapItem *>(this); Chunk *c = h->chunk(); @@ -125,13 +128,9 @@ struct Q_QML_EXPORT Base { return Chunk::testBit(c->objectBitmap, h - c->realBase()); } - inline void markChildren(MarkStack *markStack) { - vtable()->markObjects(this, markStack); - } - void *operator new(size_t, Managed *m) { return m; } - void *operator new(size_t, Heap::Base *m) { return m; } - void operator delete(void *, Heap::Base *) {} + void *operator new(size_t, Base *m) { return m; } + void operator delete(void *, Base *) {} void init() { _setInitialized(); } void destroy() { _setDestroyed(); } @@ -178,10 +177,8 @@ Q_STATIC_ASSERT(std::is_standard_layout<Base>::value); Q_STATIC_ASSERT(offsetof(Base, internalClass) == 0); Q_STATIC_ASSERT(sizeof(Base) == QT_POINTER_SIZE); -} - inline -void Heap::Base::mark(QV4::MarkStack *markStack) +void Base::mark(QV4::MarkStack *markStack) { Q_ASSERT(inUse()); const HeapItem *h = reinterpret_cast<const HeapItem *>(this); @@ -196,36 +193,12 @@ void Heap::Base::mark(QV4::MarkStack *markStack) } } -namespace Heap { - -template <typename T, size_t o> -struct Pointer { - static Q_CONSTEXPR size_t offset = o; - T operator->() const { return get(); } - operator T () const { return get(); } - - Heap::Base *base() { - Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base)); - Q_ASSERT(base->inUse()); - return base; - } - - void set(EngineBase *e, T newVal) { - WriteBarrier::write(e, base(), &ptr, reinterpret_cast<Heap::Base *>(newVal)); - } - - T get() const { return reinterpret_cast<T>(ptr); } - - template <typename Type> - Type *cast() { return static_cast<Type *>(ptr); } - - Heap::Base *heapObject() const { return ptr; } - -private: - Heap::Base *ptr; -}; -typedef Pointer<char *, 0> V4PointerCheck; -Q_STATIC_ASSERT(std::is_trivial< V4PointerCheck >::value); +template<typename T, size_t o> +Base *Pointer<T, o>::base() { + Base *base = reinterpret_cast<Base *>(this) - (offset/sizeof(Base *)); + Q_ASSERT(base->inUse()); + return base; +} } diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 4e83abebdf..d9772e608e 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -42,6 +42,7 @@ #include "qv4objectproto_p.h" #include "qv4mm_p.h" #include "qv4qobjectwrapper_p.h" +#include "qv4identifiertable_p.h" #include <QtCore/qalgorithms.h> #include <QtCore/private/qnumeric_p.h> #include <QtCore/qloggingcategory.h> @@ -335,7 +336,7 @@ bool Chunk::sweep(ExecutionEngine *engine) HeapItem *itemToFree = o + index; Heap::Base *b = *itemToFree; - const VTable *v = b->vtable(); + const VTable *v = b->internalClass->vtable; // if (Q_UNLIKELY(classCountPtr)) // classCountPtr(v->className); if (v->destroy) { @@ -389,8 +390,8 @@ void Chunk::freeAll(ExecutionEngine *engine) HeapItem *itemToFree = o + index; Heap::Base *b = *itemToFree; - if (b->vtable()->destroy) { - b->vtable()->destroy(b); + if (b->internalClass->vtable->destroy) { + b->internalClass->vtable->destroy(b); b->_checkIsDestroyed(); } #ifdef V4_USE_HEAPTRACK @@ -412,10 +413,6 @@ void Chunk::resetBlackBits() memset(blackBitmap, 0, sizeof(blackBitmap)); } -#ifdef MM_STATS -static uint nGrayItems = 0; -#endif - void Chunk::collectGrayItems(MarkStack *markStack) { // DEBUG << "sweeping chunk" << this << (*freeList); @@ -438,10 +435,6 @@ void Chunk::collectGrayItems(MarkStack *markStack) Heap::Base *b = *itemToFree; Q_ASSERT(b->inUse()); markStack->push(b); -#ifdef MM_STATS - ++nGrayItems; -// qDebug() << "adding gray item" << b << "to mark stack"; -#endif } grayBitmap[i] = 0; o += Chunk::Bits; @@ -459,7 +452,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins) #else const int start = 1; #endif -#ifdef MM_STATS +#ifndef QT_NO_DEBUG uint freeSlots = 0; uint allocatedSlots = 0; #endif @@ -469,7 +462,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins) if (!i) usedSlots |= (static_cast<quintptr>(1) << (HeaderSize/SlotSize)) - 1; #endif -#ifdef MM_STATS +#ifndef QT_NO_DEBUG allocatedSlots += qPopulationCount(usedSlots); // qDebug() << hex << " i=" << i << "used=" << usedSlots; #endif @@ -486,7 +479,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins) break; } usedSlots = (objectBitmap[i]|extendsBitmap[i]); -#ifdef MM_STATS +#ifndef QT_NO_DEBUG allocatedSlots += qPopulationCount(usedSlots); // qDebug() << hex << " i=" << i << "used=" << usedSlots; #endif @@ -497,7 +490,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins) usedSlots |= (quintptr(1) << index) - 1; uint freeEnd = i*Bits + index; uint nSlots = freeEnd - freeStart; -#ifdef MM_STATS +#ifndef QT_NO_DEBUG // qDebug() << hex << " got free slots from" << freeStart << "to" << freeEnd << "n=" << nSlots << "usedSlots=" << usedSlots; freeSlots += nSlots; #endif @@ -508,7 +501,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins) bins[bin] = freeItem; } } -#ifdef MM_STATS +#ifndef QT_NO_DEBUG Q_ASSERT(freeSlots + allocatedSlots == (EntriesInBitmap - start) * 8 * sizeof(quintptr)); #endif } @@ -643,8 +636,9 @@ void BlockAllocator::sweep() void BlockAllocator::freeAll() { - for (auto c : chunks) { + for (auto c : chunks) c->freeAll(engine); + for (auto c : chunks) { Q_V4_PROFILE_DEALLOC(engine, Chunk::DataSize, Profiling::HeapPage); chunkAllocator->free(c); } @@ -691,7 +685,7 @@ static void freeHugeChunk(ChunkAllocator *chunkAllocator, const HugeItemAllocato { HeapItem *itemToFree = c.chunk->first(); Heap::Base *b = *itemToFree; - const VTable *v = b->vtable(); + const VTable *v = b->internalClass->vtable; if (Q_UNLIKELY(classCountPtr)) classCountPtr(v->className); @@ -758,6 +752,7 @@ MemoryManager::MemoryManager(ExecutionEngine *engine) : engine(engine) , chunkAllocator(new ChunkAllocator) , blockAllocator(chunkAllocator, engine) + , icAllocator(chunkAllocator, engine) , hugeItemAllocator(chunkAllocator, engine) , m_persistentValues(new PersistentValueStorage(engine)) , m_weakValues(new PersistentValueStorage(engine)) @@ -774,11 +769,6 @@ MemoryManager::MemoryManager(ExecutionEngine *engine) blockAllocator.allocationStats = statistics.allocations; } -#ifdef MM_STATS -static int allocationCount = 0; -static size_t lastAllocRequestedSlots = 0; -#endif - Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize) { const size_t stringSize = align(sizeof(Heap::String)); @@ -884,7 +874,7 @@ Heap::Object *MemoryManager::allocObjectWithMemberData(const QV4::VTable *vtable Chunk::clearBit(c->extendsBitmap, index); } o->memberData.set(engine, m); - m->internalClass = engine->internalClasses[EngineBase::Class_MemberData]; + m->internalClass.set(engine, engine->internalClasses(EngineBase::Class_MemberData)); Q_ASSERT(o->memberData->internalClass); m->values.alloc = static_cast<uint>((memberSize - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value)); m->values.size = o->memberData->values.alloc; @@ -911,7 +901,7 @@ void MarkStack::drain() Heap::Base *h = pop(); ++markStackSize; Q_ASSERT(h); // at this point we should only have Heap::Base objects in this area on the stack. If not, weird things might happen. - h->markChildren(this); + h->internalClass->vtable->markObjects(h, this); } } @@ -1017,8 +1007,13 @@ void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPt } } - blockAllocator.sweep(); - hugeItemAllocator.sweep(classCountPtr); + + if (!lastSweep) { + engine->identifierTable->sweep(); + blockAllocator.sweep(/*classCountPtr*/); + hugeItemAllocator.sweep(classCountPtr); + icAllocator.sweep(/*classCountPtr*/); + } } bool MemoryManager::shouldRunGC() const @@ -1096,10 +1091,6 @@ void MemoryManager::runGC() qDebug(stats) << "Fragmented memory before GC" << (totalMem - usedBefore); dumpBins(&blockAllocator); -#ifdef MM_STATS - nGrayItems = 0; -#endif - QElapsedTimer t; t.start(); mark(); @@ -1159,7 +1150,8 @@ void MemoryManager::runGC() if (aggressiveGC) { // ensure we don't 'loose' any memory - Q_ASSERT(blockAllocator.allocatedMem() == getUsedMem() + dumpBins(&blockAllocator, false)); + Q_ASSERT(blockAllocator.allocatedMem() + == blockAllocator.usedMem() + dumpBins(&blockAllocator, false)); } usedSlotsAfterLastFullSweep = blockAllocator.usedSlotsAfterLastSweep; @@ -1167,16 +1159,17 @@ void MemoryManager::runGC() // reset all black bits blockAllocator.resetBlackBits(); hugeItemAllocator.resetBlackBits(); + icAllocator.resetBlackBits(); } size_t MemoryManager::getUsedMem() const { - return blockAllocator.usedMem(); + return blockAllocator.usedMem() + icAllocator.usedMem(); } size_t MemoryManager::getAllocatedMem() const { - return blockAllocator.allocatedMem() + hugeItemAllocator.usedMem(); + return blockAllocator.allocatedMem() + icAllocator.allocatedMem() + hugeItemAllocator.usedMem(); } size_t MemoryManager::getLargeItemsMem() const @@ -1193,6 +1186,7 @@ MemoryManager::~MemoryManager() sweep(/*lastSweep*/true); blockAllocator.freeAll(); hugeItemAllocator.freeAll(); + icAllocator.freeAll(); delete m_weakValues; #ifdef V4_USE_VALGRIND diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 40670bcdc7..e3a011caf9 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -160,224 +160,98 @@ public: { return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); } template<typename ManagedType> - inline typename ManagedType::Data *allocManaged(std::size_t size) + inline typename ManagedType::Data *allocManaged(std::size_t size, Heap::InternalClass *ic) { Q_STATIC_ASSERT(std::is_trivial< typename ManagedType::Data >::value); size = align(size); - Heap::Base *o = allocData(size); - InternalClass *ic = ManagedType::defaultInternalClass(engine); - ic = ic->changeVTable(ManagedType::staticVTable()); - o->internalClass = ic; - Q_ASSERT(o->internalClass && o->internalClass->vtable); - return static_cast<typename ManagedType::Data *>(o); + typename ManagedType::Data *d = static_cast<typename ManagedType::Data *>(allocData(size)); + d->internalClass.set(engine, ic); + Q_ASSERT(d->internalClass && d->internalClass->vtable); + Q_ASSERT(ic->vtable == ManagedType::staticVTable()); + return d; } template<typename ManagedType> inline typename ManagedType::Data *allocManaged(std::size_t size, InternalClass *ic) { - Q_STATIC_ASSERT(std::is_trivial< typename ManagedType::Data >::value); - size = align(size); - Heap::Base *o = allocData(size); - o->internalClass = ic; - Q_ASSERT(o->internalClass && o->internalClass->vtable); - Q_ASSERT(ic->vtable == ManagedType::staticVTable()); - return static_cast<typename ManagedType::Data *>(o); + return allocManaged<ManagedType>(size, ic->d()); + } + + template<typename ManagedType> + inline typename ManagedType::Data *allocManaged(std::size_t size) + { + Scope scope(engine); + Scoped<InternalClass> ic(scope, ManagedType::defaultInternalClass(engine)); + return allocManaged<ManagedType>(size, ic); } template <typename ObjectType> - typename ObjectType::Data *allocateObject(InternalClass *ic) + typename ObjectType::Data *allocateObject(Heap::InternalClass *ic) { Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size); - o->internalClass = ic; - Q_ASSERT(o->internalClass && o->internalClass->vtable); - Q_ASSERT(ic->vtable == ObjectType::staticVTable()); + o->internalClass.set(engine, ic); + Q_ASSERT(o->internalClass.get() && o->vtable()); + Q_ASSERT(o->vtable() == ObjectType::staticVTable()); return static_cast<typename ObjectType::Data *>(o); } template <typename ObjectType> + typename ObjectType::Data *allocateObject(InternalClass *ic) + { + return allocateObject<ObjectType>(ic->d()); + } + + template <typename ObjectType> typename ObjectType::Data *allocateObject() { - InternalClass *ic = ObjectType::defaultInternalClass(engine); + Scope scope(engine); + Scoped<InternalClass> ic(scope, ObjectType::defaultInternalClass(engine)); ic = ic->changeVTable(ObjectType::staticVTable()); ic = ic->changePrototype(ObjectType::defaultPrototype(engine)->d()); - Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size); - o->internalClass = ic; - Q_ASSERT(o->internalClass && o->internalClass->vtable); - Q_ASSERT(o->internalClass->prototype == ObjectType::defaultPrototype(engine)->d()); - return static_cast<typename ObjectType::Data *>(o); + return allocateObject<ObjectType>(ic); } template <typename ManagedType, typename Arg1> typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 arg1) { typename ManagedType::Data *o = reinterpret_cast<typename ManagedType::Data *>(allocString(unmanagedSize)); - o->internalClass = ManagedType::defaultInternalClass(engine); + o->internalClass.set(engine, ManagedType::defaultInternalClass(engine)); Q_ASSERT(o->internalClass && o->internalClass->vtable); o->init(arg1); return o; } - template <typename ObjectType> - typename ObjectType::Data *allocObject(InternalClass *ic) - { - Scope scope(engine); - Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - t->d_unchecked()->init(); - return t->d(); - } - - template <typename ObjectType> - typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype) - { - Scope scope(engine); - Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr)); - Q_UNUSED(prototype); - t->d_unchecked()->init(); - return t->d(); - } - - template <typename ObjectType, typename Arg1> - typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1) - { - Scope scope(engine); - Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr)); - Q_UNUSED(prototype); - t->d_unchecked()->init(arg1); - return t->d(); - } - - template <typename ObjectType, typename Arg1, typename Arg2> - typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1, Arg2 arg2) - { - Scope scope(engine); - Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr)); - Q_UNUSED(prototype); - t->d_unchecked()->init(arg1, arg2); - return t->d(); - } - - template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3> - typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1, Arg2 arg2, Arg3 arg3) - { - Scope scope(engine); - Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr)); - Q_UNUSED(prototype); - t->d_unchecked()->init(arg1, arg2, arg3); - return t->d(); - } - - template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3, typename Arg4> - typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) - { - Scope scope(engine); - Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr)); - Q_UNUSED(prototype); - t->d_unchecked()->init(arg1, arg2, arg3, arg4); - return t->d(); - } - - template <typename ObjectType> - typename ObjectType::Data *allocObject() - { - Scope scope(engine); - Scoped<ObjectType> t(scope, allocateObject<ObjectType>()); - t->d_unchecked()->init(); - return t->d(); - } - - template <typename ObjectType, typename Arg1> - typename ObjectType::Data *allocObject(Arg1 arg1) + template <typename ObjectType, typename... Args> + typename ObjectType::Data *allocObject(Heap::InternalClass *ic, Args... args) { - Scope scope(engine); - Scoped<ObjectType> t(scope, allocateObject<ObjectType>()); - t->d_unchecked()->init(arg1); - return t->d(); - } - - template <typename ObjectType, typename Arg1, typename Arg2> - typename ObjectType::Data *allocObject(Arg1 arg1, Arg2 arg2) - { - Scope scope(engine); - Scoped<ObjectType> t(scope, allocateObject<ObjectType>()); - t->d_unchecked()->init(arg1, arg2); - return t->d(); + typename ObjectType::Data *d = allocateObject<ObjectType>(ic); + d->init(args...); + return d; } - template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3> - typename ObjectType::Data *allocObject(Arg1 arg1, Arg2 arg2, Arg3 arg3) + template <typename ObjectType, typename... Args> + typename ObjectType::Data *allocObject(InternalClass *ic, Args... args) { - Scope scope(engine); - Scoped<ObjectType> t(scope, allocateObject<ObjectType>()); - t->d_unchecked()->init(arg1, arg2, arg3); - return t->d(); + typename ObjectType::Data *d = allocateObject<ObjectType>(ic); + d->init(args...); + return d; } - template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3, typename Arg4> - typename ObjectType::Data *allocObject(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + template <typename ObjectType, typename... Args> + typename ObjectType::Data *allocate(Args... args) { Scope scope(engine); Scoped<ObjectType> t(scope, allocateObject<ObjectType>()); - t->d_unchecked()->init(arg1, arg2, arg3, arg4); - return t->d(); - } - - - template <typename ManagedType> - typename ManagedType::Data *alloc() - { - Scope scope(engine); - Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data))); - t->d_unchecked()->init(); - return t->d(); - } - - template <typename ManagedType, typename Arg1> - typename ManagedType::Data *alloc(Arg1 arg1) - { - Scope scope(engine); - Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data))); - t->d_unchecked()->init(arg1); - return t->d(); - } - - template <typename ManagedType, typename Arg1, typename Arg2> - typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2) - { - Scope scope(engine); - Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data))); - t->d_unchecked()->init(arg1, arg2); - return t->d(); - } - - template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3> - typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3) - { - Scope scope(engine); - Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data))); - t->d_unchecked()->init(arg1, arg2, arg3); - return t->d(); - } - - template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3, typename Arg4> - typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) - { - Scope scope(engine); - Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data))); - t->d_unchecked()->init(arg1, arg2, arg3, arg4); + t->d_unchecked()->init(args...); return t->d(); } - template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> - typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + template <typename ManagedType, typename... Args> + typename ManagedType::Data *alloc(Args... args) { Scope scope(engine); Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data))); - t->d_unchecked()->init(arg1, arg2, arg3, arg4, arg5); + t->d_unchecked()->init(args...); return t->d(); } @@ -392,6 +266,14 @@ public: // called when a JS object grows itself. Specifically: Heap::String::append void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; } + template<typename ManagedType> + typename ManagedType::Data *allocIC() + { + size_t size = align(sizeof(typename ManagedType::Data)); + Heap::Base *b = *icAllocator.allocate(size, true); + return static_cast<typename ManagedType::Data *>(b); + } + protected: /// expects size to be aligned Heap::Base *allocString(std::size_t unmanagedSize); @@ -409,6 +291,7 @@ public: QV4::ExecutionEngine *engine; ChunkAllocator *chunkAllocator; BlockAllocator blockAllocator; + BlockAllocator icAllocator; HugeItemAllocator hugeItemAllocator; PersistentValueStorage *m_persistentValues; PersistentValueStorage *m_weakValues; @@ -423,6 +306,9 @@ public: bool gcStats = false; bool gcCollectorStats = false; + int allocationCount = 0; + size_t lastAllocRequestedSlots = 0; + struct { size_t maxReservedMem = 0; size_t maxAllocatedMem = 0; diff --git a/src/qml/parser/parser.pri b/src/qml/parser/parser.pri index e5b8ae2749..adab4ef9a2 100644 --- a/src/qml/parser/parser.pri +++ b/src/qml/parser/parser.pri @@ -3,20 +3,24 @@ HEADERS += \ $$PWD/qqmljsastfwd_p.h \ $$PWD/qqmljsastvisitor_p.h \ $$PWD/qqmljsengine_p.h \ - $$PWD/qqmljsgrammar_p.h \ $$PWD/qqmljslexer_p.h \ $$PWD/qqmljsmemorypool_p.h \ - $$PWD/qqmljsparser_p.h \ $$PWD/qqmljsglobal_p.h \ $$PWD/qqmljskeywords_p.h \ + $$PWD/qqmljsengine_p.h \ + $$PWD/qqmljsglobal_p.h SOURCES += \ $$PWD/qqmljsast.cpp \ $$PWD/qqmljsastvisitor.cpp \ $$PWD/qqmljsengine_p.cpp \ - $$PWD/qqmljsgrammar.cpp \ $$PWD/qqmljslexer.cpp \ - $$PWD/qqmljsparser.cpp \ -OTHER_FILES += \ - $$PWD/qqmljs.g +CONFIG += qlalr +QLALRSOURCES = $$PWD/qqmljs.g +QMAKE_QLALRFLAGS = --no-debug --qt + +OTHER_FILES += $$QLALRSOURCES + +# make sure we install the headers generated by qlalr +private_headers.CONFIG += no_check_exist diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index d2d947e55c..cd40a77299 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -40,8 +40,7 @@ %parser QQmlJSGrammar %decl qqmljsparser_p.h %impl qqmljsparser.cpp -%expect 5 -%expect-rr 2 +%expect 1 %token T_AND "&" T_AND_AND "&&" T_AND_EQ "&=" %token T_BREAK "break" T_CASE "case" T_CATCH "catch" @@ -64,7 +63,8 @@ %token T_RBRACE "}" T_RBRACKET "]" T_REMAINDER "%" %token T_REMAINDER_EQ "%=" T_RETURN "return" T_RPAREN ")" %token T_SEMICOLON ";" T_AUTOMATIC_SEMICOLON T_STAR "*" -%token T_STAR_EQ "*=" T_STRING_LITERAL "string literal" +%token T_STAR_STAR "**" T_STAR_STAR_EQ "**=" T_STAR_EQ "*=" +%token T_STRING_LITERAL "string literal" %token T_PROPERTY "property" T_SIGNAL "signal" T_READONLY "readonly" %token T_SWITCH "switch" T_THIS "this" T_THROW "throw" %token T_TILDE "~" T_TRY "try" T_TYPEOF "typeof" @@ -77,14 +77,29 @@ %token T_MULTILINE_STRING_LITERAL "multiline string literal" %token T_COMMENT "comment" %token T_COMPATIBILITY_SEMICOLON +%token T_ARROW "=>" %token T_ENUM "enum" +%token T_ELLIPSIS "..." +%token T_YIELD "yield" +%token T_SUPER "super" +%token T_CLASS "class" +%token T_EXTENDS "extends" +%token T_STATIC "static" +%token T_EXPORT "export" +%token T_FROM "from" + +--- template strings +%token T_NO_SUBSTITUTION_TEMPLATE"(no subst template)" +%token T_TEMPLATE_HEAD "(template head)" +%token T_TEMPLATE_MIDDLE "(template middle)" +%token T_TEMPLATE_TAIL "(template tail)" --- context keywords. %token T_PUBLIC "public" %token T_IMPORT "import" %token T_PRAGMA "pragma" %token T_AS "as" -%token T_ON "on" +%token T_OF "of" %token T_GET "get" %token T_SET "set" @@ -95,11 +110,16 @@ %token T_FEED_UI_OBJECT_MEMBER %token T_FEED_JS_STATEMENT %token T_FEED_JS_EXPRESSION -%token T_FEED_JS_SOURCE_ELEMENT -%token T_FEED_JS_PROGRAM +%token T_FEED_JS_SCRIPT +%token T_FEED_JS_MODULE -%nonassoc SHIFT_THERE -%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY T_ON T_SET T_GET +--- Lookahead handling +%token T_FORCE_DECLARATION "(force decl)" +%token T_FORCE_BLOCK "(force block)" +%token T_FOR_LOOKAHEAD_OK "(for lookahead ok)" + +--%left T_PLUS T_MINUS +%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY T_ON T_SET T_GET T_OF T_STATIC T_FROM %nonassoc REDUCE_HERE %start TopLevel @@ -143,10 +163,10 @@ ** ****************************************************************************/ -#include "qqmljsengine_p.h" -#include "qqmljslexer_p.h" -#include "qqmljsast_p.h" -#include "qqmljsmemorypool_p.h" +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsast_p.h> +#include <private/qqmljsmemorypool_p.h> #include <QtCore/qdebug.h> #include <QtCore/qcoreapplication.h> @@ -221,10 +241,10 @@ #ifndef QQMLJSPARSER_P_H #define QQMLJSPARSER_P_H -#include "qqmljsglobal_p.h" -#include "qqmljsgrammar_p.h" -#include "qqmljsast_p.h" -#include "qqmljsengine_p.h" +#include <private/qqmljsglobal_p.h> +#include <private/qqmljsgrammar_p.h> +#include <private/qqmljsast_p.h> +#include <private/qqmljsengine_p.h> #include <QtCore/qlist.h> #include <QtCore/qstring.h> @@ -241,30 +261,32 @@ public: union Value { int ival; double dval; + AST::VariableScope scope; + AST::ForEachType forEachType; AST::ArgumentList *ArgumentList; AST::CaseBlock *CaseBlock; AST::CaseClause *CaseClause; AST::CaseClauses *CaseClauses; AST::Catch *Catch; AST::DefaultClause *DefaultClause; - AST::ElementList *ElementList; AST::Elision *Elision; AST::ExpressionNode *Expression; + AST::TemplateLiteral *Template; AST::Finally *Finally; AST::FormalParameterList *FormalParameterList; - AST::FunctionBody *FunctionBody; AST::FunctionDeclaration *FunctionDeclaration; AST::Node *Node; AST::PropertyName *PropertyName; - AST::PropertyAssignment *PropertyAssignment; - AST::PropertyAssignmentList *PropertyAssignmentList; - AST::SourceElement *SourceElement; - AST::SourceElements *SourceElements; AST::Statement *Statement; AST::StatementList *StatementList; AST::Block *Block; - AST::VariableDeclaration *VariableDeclaration; AST::VariableDeclarationList *VariableDeclarationList; + AST::Pattern *Pattern; + AST::PatternElement *PatternElement; + AST::PatternElementList *PatternElementList; + AST::PatternProperty *PatternProperty; + AST::PatternPropertyList *PatternPropertyList; + AST::ClassElementList *ClassElementList; AST::UiProgram *UiProgram; AST::UiHeaderItemList *UiHeaderItemList; @@ -290,12 +312,13 @@ public: ~Parser(); // parse a UI program - bool parse() { return parse(T_FEED_UI_PROGRAM); } + bool parse() { ++functionNestingLevel; bool r = parse(T_FEED_UI_PROGRAM); --functionNestingLevel; return r; } bool parseStatement() { return parse(T_FEED_JS_STATEMENT); } bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); } - bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); } - bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); } - bool parseProgram() { return parse(T_FEED_JS_PROGRAM); } + bool parseUiObjectMember() { ++functionNestingLevel; bool r = parse(T_FEED_UI_OBJECT_MEMBER); --functionNestingLevel; return r; } + bool parseProgram() { return parse(T_FEED_JS_SCRIPT); } + bool parseScript() { return parse(T_FEED_JS_SCRIPT); } + bool parseModule() { return parse(T_FEED_JS_MODULE); } AST::UiProgram *ast() const { return AST::cast<AST::UiProgram *>(program); } @@ -366,20 +389,30 @@ protected: AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr); AST::UiQualifiedPragmaId *reparseAsQualifiedPragmaId(AST::ExpressionNode *expr); + void pushToken(int token); + int lookaheadToken(Lexer *lexer); + + void syntaxError(const AST::SourceLocation &location, const char *message) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location, QLatin1String(message))); + } + void syntaxError(const AST::SourceLocation &location, const QString &message) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location, message)); + } + protected: Engine *driver; MemoryPool *pool; - int tos; - int stack_size; - Value *sym_stack; - int *state_stack; - AST::SourceLocation *location_stack; - QStringRef *string_stack; + int tos = 0; + int stack_size = 0; + Value *sym_stack = nullptr; + int *state_stack = nullptr; + AST::SourceLocation *location_stack = nullptr; + QVector<QStringRef> string_stack; - AST::Node *program; + AST::Node *program = nullptr; - // error recovery - enum { TOKEN_BUFFER_SIZE = 3 }; + // error recovery and lookahead handling + enum { TOKEN_BUFFER_SIZE = 5 }; struct SavedToken { int token; @@ -388,14 +421,25 @@ protected: QStringRef spell; }; - double yylval; + int yytoken = -1; + double yylval = 0.; QStringRef yytokenspell; AST::SourceLocation yylloc; AST::SourceLocation yyprevlloc; SavedToken token_buffer[TOKEN_BUFFER_SIZE]; - SavedToken *first_token; - SavedToken *last_token; + SavedToken *first_token = nullptr; + SavedToken *last_token = nullptr; + + int functionNestingLevel = 0; + + enum CoverExpressionType { + CE_Invalid, + CE_ParenthesizedExpression, + CE_FormalParameterList + }; + AST::SourceLocation coverExpressionErrorLocation; + CoverExpressionType coverExpressionType = CE_Invalid; QList<DiagnosticMessage> diagnostic_messages; }; @@ -424,6 +468,8 @@ protected: // qlalr --no-debug --no-lines --qt qqmljs.g // +#define UNIMPLEMENTED syntaxError(loc(1), "Unimplemented"); return false + using namespace QQmlJS; QT_QML_BEGIN_NAMESPACE @@ -438,22 +484,12 @@ void Parser::reallocateStack() sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value))); state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int))); location_stack = reinterpret_cast<AST::SourceLocation*> (realloc(location_stack, stack_size * sizeof(AST::SourceLocation))); - string_stack = reinterpret_cast<QStringRef*> (realloc(string_stack, stack_size * sizeof(QStringRef))); + string_stack.resize(stack_size); } Parser::Parser(Engine *engine): driver(engine), - pool(engine->pool()), - tos(0), - stack_size(0), - sym_stack(0), - state_stack(0), - location_stack(0), - string_stack(0), - program(0), - yylval(0), - first_token(0), - last_token(0) + pool(engine->pool()) { } @@ -463,7 +499,6 @@ Parser::~Parser() free(sym_stack); free(state_stack); free(location_stack); - free(string_stack); } } @@ -517,17 +552,39 @@ AST::UiQualifiedPragmaId *Parser::reparseAsQualifiedPragmaId(AST::ExpressionNode return 0; } +void Parser::pushToken(int token) +{ + last_token->token = yytoken; + last_token->dval = yylval; + last_token->spell = yytokenspell; + last_token->loc = yylloc; + ++last_token; + yytoken = token; +} + +int Parser::lookaheadToken(Lexer *lexer) +{ + if (yytoken < 0) { + yytoken = lexer->lex(); + yylval = lexer->tokenValue(); + yytokenspell = lexer->tokenSpell(); + yylloc = location(lexer); + } + return yytoken; +} + +//#define PARSER_DEBUG bool Parser::parse(int startToken) { Lexer *lexer = driver->lexer(); bool hadErrors = false; - int yytoken = -1; + yytoken = -1; int action = 0; token_buffer[0].token = startToken; first_token = &token_buffer[0]; - if (startToken == T_FEED_JS_PROGRAM && !lexer->qmlMode()) { + if (startToken == T_FEED_JS_SCRIPT && !lexer->qmlMode()) { Directives ignoreDirectives; Directives *directives = driver->directives(); if (!directives) @@ -570,10 +627,19 @@ bool Parser::parse(int startToken) yytokenspell = first_token->spell; yylloc = first_token->loc; ++first_token; + if (first_token == last_token) + first_token = last_token = &token_buffer[0]; } } +#ifdef PARSER_DEBUG + qDebug() << " in state" << action; +#endif + action = t_action(action, yytoken); +#ifdef PARSER_DEBUG + qDebug() << " current token" << yytoken << (yytoken >= 0 ? spell[yytoken] : "(null)") << "new state" << action; +#endif if (action > 0) { if (action != ACCEPT_STATE) { yytoken = -1; @@ -588,6 +654,10 @@ bool Parser::parse(int startToken) const int r = -action - 1; tos -= rhs[r]; +#ifdef PARSER_DEBUG + qDebug() << " reducing through rule " << -action; +#endif + switch (r) { ./ @@ -595,2582 +665,3463 @@ bool Parser::parse(int startToken) -- Declarative UI -------------------------------------------------------------------------------------------------------- -TopLevel: T_FEED_UI_PROGRAM UiProgram ; +TopLevel: T_FEED_UI_PROGRAM UiProgram; /. -case $rule_number: { - sym(1).Node = sym(2).Node; - program = sym(1).Node; -} break; + case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; + } break; ./ -TopLevel: T_FEED_JS_STATEMENT Statement ; +TopLevel: T_FEED_JS_STATEMENT Statement; /. -case $rule_number: { - sym(1).Node = sym(2).Node; - program = sym(1).Node; -} break; + case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; + } break; ./ -TopLevel: T_FEED_JS_EXPRESSION Expression ; +TopLevel: T_FEED_JS_EXPRESSION Expression; /. -case $rule_number: { - sym(1).Node = sym(2).Node; - program = sym(1).Node; -} break; + case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; + } break; ./ -TopLevel: T_FEED_JS_SOURCE_ELEMENT SourceElement ; +TopLevel: T_FEED_UI_OBJECT_MEMBER UiObjectMember; /. -case $rule_number: { - sym(1).Node = sym(2).Node; - program = sym(1).Node; -} break; + case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; + } break; ./ -TopLevel: T_FEED_UI_OBJECT_MEMBER UiObjectMember ; +TopLevel: T_FEED_JS_SCRIPT Script; /. -case $rule_number: { - sym(1).Node = sym(2).Node; - program = sym(1).Node; -} break; + case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; + } break; ./ -TopLevel: T_FEED_JS_PROGRAM Program ; +TopLevel: T_FEED_JS_MODULE Module; /. -case $rule_number: { - sym(1).Node = sym(2).Node; - program = sym(1).Node; -} break; + case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; + } break; ./ + UiProgram: UiHeaderItemListOpt UiRootMember; /. -case $rule_number: { - sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiHeaderItemList, - sym(2).UiObjectMemberList->finish()); -} break; + case $rule_number: { + sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiHeaderItemList, sym(2).UiObjectMemberList->finish()); + } break; ./ -UiHeaderItemListOpt: Empty ; -UiHeaderItemListOpt: UiHeaderItemList ; +UiHeaderItemListOpt: Empty; +UiHeaderItemListOpt: UiHeaderItemList; /. -case $rule_number: { - sym(1).Node = sym(1).UiHeaderItemList->finish(); -} break; + case $rule_number: { + sym(1).Node = sym(1).UiHeaderItemList->finish(); + } break; ./ -UiHeaderItemList: UiPragma ; +UiHeaderItemList: UiPragma; /. -case $rule_number: { - sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiPragma); -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiPragma); + } break; ./ -UiHeaderItemList: UiImport ; +UiHeaderItemList: UiImport; /. -case $rule_number: { - sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiImport); -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiImport); + } break; ./ -UiHeaderItemList: UiHeaderItemList UiPragma ; +UiHeaderItemList: UiHeaderItemList UiPragma; /. -case $rule_number: { - sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiPragma); -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiPragma); + } break; ./ -UiHeaderItemList: UiHeaderItemList UiImport ; +UiHeaderItemList: UiHeaderItemList UiImport; /. -case $rule_number: { - sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiImport); -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiImport); + } break; ./ -PragmaId: MemberExpression ; +PragmaId: MemberExpression; -ImportId: MemberExpression ; +ImportId: MemberExpression; -UiPragma: UiPragmaHead T_AUTOMATIC_SEMICOLON ; -UiPragma: UiPragmaHead T_SEMICOLON ; +UiPragma: UiPragmaHead T_AUTOMATIC_SEMICOLON; +UiPragma: UiPragmaHead T_SEMICOLON; /. -case $rule_number: { - sym(1).UiPragma->semicolonToken = loc(2); -} break; + case $rule_number: { + sym(1).UiPragma->semicolonToken = loc(2); + } break; ./ -UiImport: UiImportHead T_AUTOMATIC_SEMICOLON ; -UiImport: UiImportHead T_SEMICOLON ; +UiImport: UiImportHead T_AUTOMATIC_SEMICOLON; +UiImport: UiImportHead T_SEMICOLON; /. -case $rule_number: { - sym(1).UiImport->semicolonToken = loc(2); -} break; + case $rule_number: { + sym(1).UiImport->semicolonToken = loc(2); + } break; ./ -UiImport: UiImportHead T_NUMERIC_LITERAL T_AUTOMATIC_SEMICOLON ; -UiImport: UiImportHead T_NUMERIC_LITERAL T_SEMICOLON ; +UiImport: UiImportHead T_NUMERIC_LITERAL T_AUTOMATIC_SEMICOLON; +UiImport: UiImportHead T_NUMERIC_LITERAL T_SEMICOLON; /. -case $rule_number: { - sym(1).UiImport->versionToken = loc(2); - sym(1).UiImport->semicolonToken = loc(3); -} break; + case $rule_number: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->semicolonToken = loc(3); + } break; ./ -UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ; -UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_SEMICOLON ; +UiImport: UiImportHead T_NUMERIC_LITERAL T_AS QmlIdentifier T_AUTOMATIC_SEMICOLON; +UiImport: UiImportHead T_NUMERIC_LITERAL T_AS QmlIdentifier T_SEMICOLON; /. -case $rule_number: { - sym(1).UiImport->versionToken = loc(2); - sym(1).UiImport->asToken = loc(3); - sym(1).UiImport->importIdToken = loc(4); - sym(1).UiImport->importId = stringRef(4); - sym(1).UiImport->semicolonToken = loc(5); -} break; + case $rule_number: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->asToken = loc(3); + sym(1).UiImport->importIdToken = loc(4); + sym(1).UiImport->importId = stringRef(4); + sym(1).UiImport->semicolonToken = loc(5); + } break; ./ -UiImport: UiImportHead T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ; -UiImport: UiImportHead T_AS JsIdentifier T_SEMICOLON ; +UiImport: UiImportHead T_AS QmlIdentifier T_AUTOMATIC_SEMICOLON; +UiImport: UiImportHead T_AS QmlIdentifier T_SEMICOLON; /. -case $rule_number: { - sym(1).UiImport->asToken = loc(2); - sym(1).UiImport->importIdToken = loc(3); - sym(1).UiImport->importId = stringRef(3); - sym(1).UiImport->semicolonToken = loc(4); -} break; + case $rule_number: { + sym(1).UiImport->asToken = loc(2); + sym(1).UiImport->importIdToken = loc(3); + sym(1).UiImport->importId = stringRef(3); + sym(1).UiImport->semicolonToken = loc(4); + } break; ./ -UiPragmaHead: T_PRAGMA PragmaId ; +UiPragmaHead: T_PRAGMA PragmaId; /. -case $rule_number: { - AST::UiPragma *node = 0; + case $rule_number: { + AST::UiPragma *node = 0; - if (AST::UiQualifiedPragmaId *qualifiedId = reparseAsQualifiedPragmaId(sym(2).Expression)) { - node = new (pool) AST::UiPragma(qualifiedId); - } + if (AST::UiQualifiedPragmaId *qualifiedId = reparseAsQualifiedPragmaId(sym(2).Expression)) + node = new (pool) AST::UiPragma(qualifiedId); - sym(1).Node = node; + sym(1).Node = node; - if (node) { - node->pragmaToken = loc(1); - } else { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), - QLatin1String("Expected a qualified name id"))); + if (node) { + node->pragmaToken = loc(1); + } else { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id"))); - return false; // ### remove me - } -} break; + return false; // ### remove me + } + } break; ./ -UiImportHead: T_IMPORT ImportId ; +UiImportHead: T_IMPORT ImportId; /. -case $rule_number: { - AST::UiImport *node = 0; - - if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) { - node = new (pool) AST::UiImport(importIdLiteral->value); - node->fileNameToken = loc(2); - } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) { - node = new (pool) AST::UiImport(qualifiedId); - node->fileNameToken = loc(2); - } + case $rule_number: { + AST::UiImport *node = 0; - sym(1).Node = node; + if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) { + node = new (pool) AST::UiImport(importIdLiteral->value); + node->fileNameToken = loc(2); + } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) { + node = new (pool) AST::UiImport(qualifiedId); + node->fileNameToken = loc(2); + } - if (node) { - node->importToken = loc(1); - } else { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), - QLatin1String("Expected a qualified name id or a string literal"))); + sym(1).Node = node; - return false; // ### remove me - } -} break; + if (node) { + node->importToken = loc(1); + } else { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id or a string literal"))); + + return false; // ### remove me + } + } break; ./ Empty: ; /. -case $rule_number: { - sym(1).Node = 0; -} break; + case $rule_number: { + sym(1).Node = nullptr; + } break; ./ -UiRootMember: UiObjectDefinition ; +UiRootMember: UiObjectDefinition; /. -case $rule_number: { - sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); + } break; ./ -UiObjectMemberList: UiObjectMember ; +UiObjectMemberList: UiObjectMember; /. -case $rule_number: { - sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); + } break; ./ -UiObjectMemberList: UiObjectMemberList UiObjectMember ; +UiObjectMemberList: UiObjectMemberList UiObjectMember; /. -case $rule_number: { - AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList( - sym(1).UiObjectMemberList, sym(2).UiObjectMember); - sym(1).Node = node; -} break; + case $rule_number: { + AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList(sym(1).UiObjectMemberList, sym(2).UiObjectMember); + sym(1).Node = node; + } break; ./ -UiArrayMemberList: UiObjectDefinition ; +UiArrayMemberList: UiObjectDefinition; /. -case $rule_number: { - sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember); -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember); + } break; ./ -UiArrayMemberList: UiArrayMemberList T_COMMA UiObjectDefinition ; +UiArrayMemberList: UiArrayMemberList T_COMMA UiObjectDefinition; /. -case $rule_number: { - AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList( - sym(1).UiArrayMemberList, sym(3).UiObjectMember); - node->commaToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList(sym(1).UiArrayMemberList, sym(3).UiObjectMember); + node->commaToken = loc(2); + sym(1).Node = node; + } break; ./ -UiObjectInitializer: T_LBRACE T_RBRACE ; +UiObjectInitializer: T_LBRACE T_RBRACE; /. -case $rule_number: { - AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)0); - node->lbraceToken = loc(1); - node->rbraceToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)0); + node->lbraceToken = loc(1); + node->rbraceToken = loc(2); + sym(1).Node = node; + } break; ./ -UiObjectInitializer: T_LBRACE UiObjectMemberList T_RBRACE ; +UiObjectInitializer: T_LBRACE UiObjectMemberList T_RBRACE; /. -case $rule_number: { - AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish()); - node->lbraceToken = loc(1); - node->rbraceToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; + } break; ./ -UiObjectDefinition: UiQualifiedId UiObjectInitializer ; +UiObjectDefinition: UiQualifiedId UiObjectInitializer; /. -case $rule_number: { - AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId, - sym(2).UiObjectInitializer); - sym(1).Node = node; -} break; + case $rule_number: { + AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId, sym(2).UiObjectInitializer); + sym(1).Node = node; + } break; ./ -UiObjectMember: UiObjectDefinition ; +UiObjectMember: UiObjectDefinition; -UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ; +UiObjectMember: UiQualifiedId T_COLON ExpressionStatementLookahead T_LBRACKET UiArrayMemberList T_RBRACKET; /. -case $rule_number: { - AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding( - sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish()); - node->colonToken = loc(2); - node->lbracketToken = loc(3); - node->rbracketToken = loc(5); - sym(1).Node = node; -} break; + case $rule_number: { + AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding(sym(1).UiQualifiedId, sym(5).UiArrayMemberList->finish()); + node->colonToken = loc(2); + node->lbracketToken = loc(4); + node->rbracketToken = loc(6); + sym(1).Node = node; + } break; ./ -UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ; +UiObjectMember: UiQualifiedId T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer; /. -case $rule_number: { - AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( - sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer); - node->colonToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( + sym(1).UiQualifiedId, sym(4).UiQualifiedId, sym(5).UiObjectInitializer); + node->colonToken = loc(2); + sym(1).Node = node; + } break; ./ -UiObjectMember: UiQualifiedId T_ON UiQualifiedId UiObjectInitializer ; +UiObjectMember: UiQualifiedId T_ON UiQualifiedId UiObjectInitializer; /. -case $rule_number: { - AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( - sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer); - node->colonToken = loc(2); - node->hasOnToken = true; - sym(1).Node = node; -} break; + case $rule_number: { + AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( + sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + node->hasOnToken = true; + sym(1).Node = node; + } break; ./ -UiScriptStatement: Block ; -UiScriptStatement: EmptyStatement ; -UiScriptStatement: ExpressionStatement ; -UiScriptStatement: IfStatement ; -UiScriptStatement: WithStatement ; -UiScriptStatement: SwitchStatement ; -UiScriptStatement: TryStatement ; -UiObjectMember: UiQualifiedId T_COLON UiScriptStatement ; +UiObjectLiteral: T_LBRACE ExpressionStatementLookahead UiPropertyDefinitionList T_RBRACE; +/. case $rule_number: Q_FALLTHROUGH(); ./ +UiObjectLiteral: T_LBRACE ExpressionStatementLookahead UiPropertyDefinitionList T_COMMA T_RBRACE; /. -case $rule_number: -{ - AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding( - sym(1).UiQualifiedId, sym(3).Statement); - node->colonToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ObjectPattern *l = new (pool) AST::ObjectPattern(sym(3).PatternPropertyList->finish()); + l->lbraceToken = loc(1); + l->rbraceToken = loc(4); + AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(l); + sym(1).Node = node; + } break; ./ -UiPropertyType: T_VAR ; -/. -case $rule_number: { - AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1)); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; -./ -UiPropertyType: T_RESERVED_WORD ; +UiScriptStatement: ExpressionStatementLookahead T_FORCE_DECLARATION ExpressionStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +UiScriptStatement: ExpressionStatementLookahead T_FORCE_BLOCK Block; +/. case $rule_number: Q_FALLTHROUGH(); ./ +UiScriptStatement: ExpressionStatementLookahead T_FORCE_BLOCK UiObjectLiteral; /. -case $rule_number: { - AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1)); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = sym(3).Node; + } break; ./ -UiPropertyType: T_IDENTIFIER ; -/. -case $rule_number: { - AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1)); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; -./ -UiPropertyType: UiPropertyType T_DOT T_IDENTIFIER ; +UiScriptStatement: ExpressionStatementLookahead EmptyStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +UiScriptStatement: ExpressionStatementLookahead ExpressionStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +UiScriptStatement: ExpressionStatementLookahead IfStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +UiScriptStatement: ExpressionStatementLookahead WithStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +UiScriptStatement: ExpressionStatementLookahead SwitchStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +UiScriptStatement: ExpressionStatementLookahead TryStatement; /. -case $rule_number: { - AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(sym(1).UiQualifiedId, stringRef(3)); - node->identifierToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = sym(2).Node; + } break; ./ -UiParameterListOpt: ; +UiObjectMember: UiQualifiedId T_COLON UiScriptStatement; /. -case $rule_number: { - sym(1).Node = 0; -} break; +case $rule_number: +{ + AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding(sym(1).UiQualifiedId, sym(3).Statement); + node->colonToken = loc(2); + sym(1).Node = node; + } break; ./ -UiParameterListOpt: UiParameterList ; +UiPropertyType: T_VAR; /. -case $rule_number: { - sym(1).Node = sym(1).UiParameterList->finish (); -} break; + case $rule_number: { + AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1)); + node->identifierToken = loc(1); + sym(1).Node = node; + } break; ./ -UiParameterList: UiPropertyType JsIdentifier ; +UiPropertyType: T_RESERVED_WORD; /. -case $rule_number: { - AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiQualifiedId->finish(), stringRef(2)); - node->propertyTypeToken = loc(1); - node->identifierToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1)); + node->identifierToken = loc(1); + sym(1).Node = node; + } break; ./ -UiParameterList: UiParameterList T_COMMA UiPropertyType JsIdentifier ; +UiPropertyType: T_IDENTIFIER; /. -case $rule_number: { - AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(3).UiQualifiedId->finish(), stringRef(4)); - node->propertyTypeToken = loc(3); - node->commaToken = loc(2); - node->identifierToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1)); + node->identifierToken = loc(1); + sym(1).Node = node; + } break; ./ -UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_AUTOMATIC_SEMICOLON ; -UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEMICOLON ; +UiPropertyType: UiPropertyType T_DOT T_IDENTIFIER; /. -case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2)); - node->type = AST::UiPublicMember::Signal; - node->propertyToken = loc(1); - node->typeToken = loc(2); - node->identifierToken = loc(2); - node->parameters = sym(4).UiParameterList; - node->semicolonToken = loc(6); - sym(1).Node = node; -} break; + case $rule_number: { + AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(sym(1).UiQualifiedId, stringRef(3)); + node->identifierToken = loc(3); + sym(1).Node = node; + } break; ./ -UiObjectMember: T_SIGNAL T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; -UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON ; +UiParameterListOpt: ; /. -case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2)); - node->type = AST::UiPublicMember::Signal; - node->propertyToken = loc(1); - node->typeToken = loc(2); - node->identifierToken = loc(2); - node->semicolonToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = nullptr; + } break; ./ -UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_AUTOMATIC_SEMICOLON ; -UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_SEMICOLON ; +UiParameterListOpt: UiParameterList; /. -case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6)); - node->typeModifier = stringRef(2); - node->propertyToken = loc(1); - node->typeModifierToken = loc(2); - node->typeToken = loc(4); - node->identifierToken = loc(6); - node->semicolonToken = loc(7); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = sym(1).UiParameterList->finish(); + } break; ./ -UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ; -UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ; +UiParameterList: UiPropertyType QmlIdentifier; /. -case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3)); - node->propertyToken = loc(1); - node->typeToken = loc(2); - node->identifierToken = loc(3); - node->semicolonToken = loc(4); - sym(1).Node = node; -} break; -./ + case $rule_number: { + AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiQualifiedId->finish(), stringRef(2)); + node->propertyTypeToken = loc(1); + node->identifierToken = loc(2); + sym(1).Node = node; + } break; +./ -UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ; -UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ; +UiParameterList: UiParameterList T_COMMA UiPropertyType QmlIdentifier; /. -case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4)); - node->isDefaultMember = true; - node->defaultToken = loc(1); - node->propertyToken = loc(2); - node->typeToken = loc(3); - node->identifierToken = loc(4); - node->semicolonToken = loc(5); - sym(1).Node = node; -} break; -./ - -UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_AUTOMATIC_SEMICOLON ; -UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_SEMICOLON ; + case $rule_number: { + AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(3).UiQualifiedId->finish(), stringRef(4)); + node->propertyTypeToken = loc(3); + node->commaToken = loc(2); + node->identifierToken = loc(4); + sym(1).Node = node; + } break; +./ + +UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_AUTOMATIC_SEMICOLON; +UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEMICOLON; /. -case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7)); - node->isDefaultMember = true; - node->defaultToken = loc(1); - node->typeModifier = stringRef(3); - node->propertyToken = loc(2); - node->typeModifierToken = loc(2); - node->typeToken = loc(4); - node->identifierToken = loc(7); - node->semicolonToken = loc(8); - sym(1).Node = node; -} break; -./ + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2)); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->parameters = sym(4).UiParameterList; + node->semicolonToken = loc(6); + sym(1).Node = node; + } break; +./ + +UiObjectMember: T_SIGNAL T_IDENTIFIER T_AUTOMATIC_SEMICOLON; +UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON; +/. + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2)); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; + } break; +./ + +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_AUTOMATIC_SEMICOLON; +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_SEMICOLON; +/. + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6)); + node->typeModifier = stringRef(2); + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; + } break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_AUTOMATIC_SEMICOLON; +UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON; +/. + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3)); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); + sym(1).Node = node; + } break; +./ -UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ; -/. -case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3), - sym(5).Statement); - node->propertyToken = loc(1); - node->typeToken = loc(2); - node->identifierToken = loc(3); - node->colonToken = loc(4); - sym(1).Node = node; -} break; +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_AUTOMATIC_SEMICOLON; +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON; +/. + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4)); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->semicolonToken = loc(5); + sym(1).Node = node; + } break; +./ + +UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_AUTOMATIC_SEMICOLON; +UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_SEMICOLON; +/. + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7)); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->typeModifier = stringRef(3); + node->propertyToken = loc(2); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(7); + node->semicolonToken = loc(8); + sym(1).Node = node; + } break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement; +/. + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3), sym(5).Statement); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->colonToken = loc(4); + sym(1).Node = node; + } break; +./ + +UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement; +/. + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), sym(6).Statement); + node->isReadonlyMember = true; + node->readonlyToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; + } break; ./ -UiObjectMember: T_READONLY T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ; +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement; /. -case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), - sym(6).Statement); - node->isReadonlyMember = true; - node->readonlyToken = loc(1); - node->propertyToken = loc(2); - node->typeToken = loc(3); - node->identifierToken = loc(4); - node->colonToken = loc(5); - sym(1).Node = node; -} break; + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), sym(6).Statement); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; + } break; ./ -UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ; +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET; /. -case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), - sym(6).Statement); - node->isDefaultMember = true; - node->defaultToken = loc(1); - node->propertyToken = loc(2); - node->typeToken = loc(3); - node->identifierToken = loc(4); - node->colonToken = loc(5); - sym(1).Node = node; -} break; -./ + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6)); + node->typeModifier = stringRef(2); + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); // insert a fake ';' before ':' -UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ; -/. -case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6)); - node->typeModifier = stringRef(2); - node->propertyToken = loc(1); - node->typeModifierToken = loc(2); - node->typeToken = loc(4); - node->identifierToken = loc(6); - node->semicolonToken = loc(7); // insert a fake ';' before ':' - - AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6)); - propertyName->identifierToken = loc(6); - propertyName->next = 0; - - AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding( - propertyName, sym(9).UiArrayMemberList->finish()); - binding->colonToken = loc(7); - binding->lbracketToken = loc(8); - binding->rbracketToken = loc(10); - - node->binding = binding; + AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6)); + propertyName->identifierToken = loc(6); + propertyName->next = 0; - sym(1).Node = node; -} break; + AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding(propertyName, sym(9).UiArrayMemberList->finish()); + binding->colonToken = loc(7); + binding->lbracketToken = loc(8); + binding->rbracketToken = loc(10); + + node->binding = binding; + + sym(1).Node = node; + } break; ./ -UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiQualifiedId UiObjectInitializer ; +UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer; /. -case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3)); - node->propertyToken = loc(1); - node->typeToken = loc(2); - node->identifierToken = loc(3); - node->semicolonToken = loc(4); // insert a fake ';' before ':' + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3)); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); // insert a fake ';' before ':' - AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3)); - propertyName->identifierToken = loc(3); - propertyName->next = 0; + AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3)); + propertyName->identifierToken = loc(3); + propertyName->next = 0; - AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding( - propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer); - binding->colonToken = loc(4); + AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding( + propertyName, sym(6).UiQualifiedId, sym(7).UiObjectInitializer); + binding->colonToken = loc(4); - node->binding = binding; + node->binding = binding; - sym(1).Node = node; -} break; + sym(1).Node = node; + } break; ./ -UiObjectMember: T_READONLY T_PROPERTY UiPropertyType JsIdentifier T_COLON UiQualifiedId UiObjectInitializer ; +UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer; /. -case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4)); - node->isReadonlyMember = true; - node->readonlyToken = loc(1); - node->propertyToken = loc(2); - node->typeToken = loc(3); - node->identifierToken = loc(4); - node->semicolonToken = loc(5); // insert a fake ';' before ':' + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4)); + node->isReadonlyMember = true; + node->readonlyToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->semicolonToken = loc(5); // insert a fake ';' before ':' - AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(4)); - propertyName->identifierToken = loc(4); - propertyName->next = 0; + AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(4)); + propertyName->identifierToken = loc(4); + propertyName->next = 0; - AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding( - propertyName, sym(6).UiQualifiedId, sym(7).UiObjectInitializer); - binding->colonToken = loc(5); + AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding( + propertyName, sym(7).UiQualifiedId, sym(8).UiObjectInitializer); + binding->colonToken = loc(5); - node->binding = binding; + node->binding = binding; - sym(1).Node = node; -} break; + sym(1).Node = node; + } break; ./ -UiObjectMember: FunctionDeclaration ; +UiObjectMember: FunctionDeclaration; /. -case $rule_number: { - sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); + } break; ./ -UiObjectMember: VariableStatement ; +UiObjectMember: VariableStatement; /. -case $rule_number: { - sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); + } break; +./ + +UiQualifiedId: MemberExpression; +/. + case $rule_number: { + if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, + QLatin1String("Ignored annotation"))); + + sym(1).Expression = mem->base; + } + + if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) { + sym(1).UiQualifiedId = qualifiedId; + } else { + sym(1).UiQualifiedId = 0; + + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id"))); + + return false; // ### recover + } + } break; ./ UiObjectMember: T_ENUM T_IDENTIFIER T_LBRACE EnumMemberList T_RBRACE; /. -case $rule_number: { - AST::UiEnumDeclaration *enumDeclaration = new (pool) AST::UiEnumDeclaration(stringRef(2), sym(4).UiEnumMemberList->finish()); - enumDeclaration->enumToken = loc(1); - enumDeclaration->rbraceToken = loc(5); - sym(1).Node = enumDeclaration; - break; -} + case $rule_number: { + AST::UiEnumDeclaration *enumDeclaration = new (pool) AST::UiEnumDeclaration(stringRef(2), sym(4).UiEnumMemberList->finish()); + enumDeclaration->enumToken = loc(1); + enumDeclaration->rbraceToken = loc(5); + sym(1).Node = enumDeclaration; + break; + } ./ EnumMemberList: T_IDENTIFIER; /. -case $rule_number: { - AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1)); - node->memberToken = loc(1); - sym(1).Node = node; - break; -} + case $rule_number: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1)); + node->memberToken = loc(1); + sym(1).Node = node; + break; + } ./ EnumMemberList: T_IDENTIFIER T_EQ T_NUMERIC_LITERAL; /. -case $rule_number: { - AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1), sym(3).dval); - node->memberToken = loc(1); - node->valueToken = loc(3); - sym(1).Node = node; - break; -} + case $rule_number: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1), sym(3).dval); + node->memberToken = loc(1); + node->valueToken = loc(3); + sym(1).Node = node; + break; + } ./ EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER; /. -case $rule_number: { - AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3)); - node->memberToken = loc(3); - sym(1).Node = node; - break; -} + case $rule_number: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3)); + node->memberToken = loc(3); + sym(1).Node = node; + break; + } ./ EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER T_EQ T_NUMERIC_LITERAL; /. -case $rule_number: { - AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3), sym(5).dval); - node->memberToken = loc(3); - node->valueToken = loc(5); - sym(1).Node = node; - break; -} + case $rule_number: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3), sym(5).dval); + node->memberToken = loc(3); + node->valueToken = loc(5); + sym(1).Node = node; + break; + } ./ -JsIdentifier: T_IDENTIFIER; +QmlIdentifier: T_IDENTIFIER; +QmlIdentifier: T_PROPERTY; +QmlIdentifier: T_SIGNAL; +QmlIdentifier: T_READONLY; +QmlIdentifier: T_ON; +QmlIdentifier: T_GET; +QmlIdentifier: T_SET; +QmlIdentifier: T_FROM; +QmlIdentifier: T_OF; -JsIdentifier: T_PROPERTY ; -JsIdentifier: T_SIGNAL ; -JsIdentifier: T_READONLY ; -JsIdentifier: T_ON ; -JsIdentifier: T_GET ; -JsIdentifier: T_SET ; +JsIdentifier: T_IDENTIFIER; +JsIdentifier: T_PROPERTY; +JsIdentifier: T_SIGNAL; +JsIdentifier: T_READONLY; +JsIdentifier: T_ON; +JsIdentifier: T_GET; +JsIdentifier: T_SET; +JsIdentifier: T_FROM; +JsIdentifier: T_STATIC; +JsIdentifier: T_OF; + +IdentifierReference: JsIdentifier; +BindingIdentifier: IdentifierReference; -------------------------------------------------------------------------------------------------------- -- Expressions -------------------------------------------------------------------------------------------------------- -PrimaryExpression: T_THIS ; +PrimaryExpression: T_THIS; /. -case $rule_number: { - AST::ThisExpression *node = new (pool) AST::ThisExpression(); - node->thisToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ThisExpression *node = new (pool) AST::ThisExpression(); + node->thisToken = loc(1); + sym(1).Node = node; + } break; ./ -PrimaryExpression: JsIdentifier ; +PrimaryExpression: IdentifierReference; /. -case $rule_number: { - AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1)); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1)); + node->identifierToken = loc(1); + sym(1).Node = node; + } break; ./ -PrimaryExpression: T_NULL ; +PrimaryExpression: Literal; +PrimaryExpression: ArrayLiteral; +PrimaryExpression: ObjectLiteral; +PrimaryExpression: FunctionExpression; +PrimaryExpression: ClassExpression; +PrimaryExpression: GeneratorExpression; +PrimaryExpression: RegularExpressionLiteral; +PrimaryExpression: TemplateLiteral; + +PrimaryExpression: CoverParenthesizedExpressionAndArrowParameterList; /. -case $rule_number: { - AST::NullExpression *node = new (pool) AST::NullExpression(); - node->nullToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + if (coverExpressionType != CE_ParenthesizedExpression) { + syntaxError(coverExpressionErrorLocation, "Expected token ')'."); + return false; + } + } break; ./ -PrimaryExpression: T_TRUE ; +-- Parsing of the CoverParenthesizedExpressionAndArrowParameterList is restricted to the one rule below when this is parsed as a primary expression +CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN Expression_In T_RPAREN; /. -case $rule_number: { - AST::TrueLiteral *node = new (pool) AST::TrueLiteral(); - node->trueToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression); + node->lparenToken = loc(1); + node->rparenToken = loc(3); + sym(1).Node = node; + coverExpressionType = CE_ParenthesizedExpression; + } break; ./ -PrimaryExpression: T_FALSE ; +CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN T_RPAREN; /. -case $rule_number: { - AST::FalseLiteral *node = new (pool) AST::FalseLiteral(); - node->falseToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = nullptr; + coverExpressionErrorLocation = loc(2); + coverExpressionType = CE_FormalParameterList; + } break; ./ -PrimaryExpression: T_NUMERIC_LITERAL ; +CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN BindingRestElement T_RPAREN; /. -case $rule_number: { - AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval); - node->literalToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FormalParameterList *node = (new (pool) AST::FormalParameterList(nullptr, sym(2).PatternElement))->finish(pool); + sym(1).Node = node; + coverExpressionErrorLocation = loc(2); + coverExpressionType = CE_FormalParameterList; + } break; ./ -PrimaryExpression: T_MULTILINE_STRING_LITERAL ; -/.case $rule_number:./ +CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN Expression_In T_COMMA BindingRestElementOpt T_RPAREN; +/. + case $rule_number: { + AST::FormalParameterList *list = sym(2).Expression->reparseAsFormalParameterList(pool); + if (!list) { + syntaxError(loc(1), "Invalid Arrow parameter list."); + return false; + } + if (sym(4).Node) { + list = new (pool) AST::FormalParameterList(list, sym(4).PatternElement); + } + coverExpressionErrorLocation = loc(4); + coverExpressionType = CE_FormalParameterList; + sym(1).Node = list->finish(pool); + } break; +./ -PrimaryExpression: T_STRING_LITERAL ; +Literal: T_NULL; /. -case $rule_number: { - AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1)); - node->literalToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::NullExpression *node = new (pool) AST::NullExpression(); + node->nullToken = loc(1); + sym(1).Node = node; + } break; +./ + +Literal: T_TRUE; +/. + case $rule_number: { + AST::TrueLiteral *node = new (pool) AST::TrueLiteral(); + node->trueToken = loc(1); + sym(1).Node = node; + } break; +./ + +Literal: T_FALSE; +/. + case $rule_number: { + AST::FalseLiteral *node = new (pool) AST::FalseLiteral(); + node->falseToken = loc(1); + sym(1).Node = node; + } break; +./ + +Literal: T_NUMERIC_LITERAL; +/. + case $rule_number: { + AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval); + node->literalToken = loc(1); + sym(1).Node = node; + } break; ./ -PrimaryExpression: T_DIVIDE_ ; +Literal: T_MULTILINE_STRING_LITERAL; +/. case $rule_number: Q_FALLTHROUGH(); ./ + +Literal: T_STRING_LITERAL; +/. + case $rule_number: { + AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1)); + node->literalToken = loc(1); + sym(1).Node = node; + } break; +./ + +RegularExpressionLiteral: T_DIVIDE_; /: #define J_SCRIPT_REGEXPLITERAL_RULE1 $rule_number :/ /. -case $rule_number: { - bool rx = lexer->scanRegExp(Lexer::NoPrefix); - if (!rx) { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); - return false; // ### remove me - } - - loc(1).length = lexer->tokenLength(); - yylloc = loc(1); // adjust the location of the current token - - AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral( - driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags()); - node->literalToken = loc(1); - sym(1).Node = node; -} break; +{ + Lexer::RegExpBodyPrefix prefix; + case $rule_number: + prefix = Lexer::NoPrefix; + goto scan_regexp; ./ -PrimaryExpression: T_DIVIDE_EQ ; +RegularExpressionLiteral: T_DIVIDE_EQ; /: #define J_SCRIPT_REGEXPLITERAL_RULE2 $rule_number :/ /. -case $rule_number: { - bool rx = lexer->scanRegExp(Lexer::EqualPrefix); - if (!rx) { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); - return false; - } + case $rule_number: + prefix = Lexer::EqualPrefix; + goto scan_regexp; + + scan_regexp: { + bool rx = lexer->scanRegExp(prefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; + } - loc(1).length = lexer->tokenLength(); - yylloc = loc(1); // adjust the location of the current token + loc(1).length = lexer->tokenLength(); + yylloc = loc(1); // adjust the location of the current token - AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral( - driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags()); - node->literalToken = loc(1); - sym(1).Node = node; -} break; + AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags()); + node->literalToken = loc(1); + sym(1).Node = node; + } break; +} ./ -PrimaryExpression: T_LBRACKET T_RBRACKET ; + +ArrayLiteral: T_LBRACKET ElisionOpt T_RBRACKET; /. -case $rule_number: { - AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) 0); - node->lbracketToken = loc(1); - node->rbracketToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::PatternElementList *list = nullptr; + if (sym(2).Elision) + list = (new (pool) AST::PatternElementList(sym(2).Elision, nullptr))->finish(); + AST::ArrayPattern *node = new (pool) AST::ArrayPattern(list); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; + } break; ./ -PrimaryExpression: T_LBRACKET Elision T_RBRACKET ; +ArrayLiteral: T_LBRACKET ElementList T_RBRACKET; /. -case $rule_number: { - AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish()); - node->lbracketToken = loc(1); - node->rbracketToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ArrayPattern *node = new (pool) AST::ArrayPattern(sym(2).PatternElementList->finish()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; + } break; ./ -PrimaryExpression: T_LBRACKET ElementList T_RBRACKET ; +ArrayLiteral: T_LBRACKET ElementList T_COMMA ElisionOpt T_RBRACKET; /. -case $rule_number: { - AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ()); - node->lbracketToken = loc(1); - node->rbracketToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + auto *list = sym(2).PatternElementList; + if (sym(4).Elision) { + AST::PatternElementList *l = new (pool) AST::PatternElementList(sym(4).Elision, nullptr); + list = list->append(l); + } + AST::ArrayPattern *node = new (pool) AST::ArrayPattern(list->finish()); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; + Q_ASSERT(node->isValidArrayLiteral()); + } break; ./ -PrimaryExpression: T_LBRACKET ElementList T_COMMA T_RBRACKET ; +ElementList: AssignmentExpression_In; /. -case $rule_number: { - AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), - (AST::Elision *) 0); - node->lbracketToken = loc(1); - node->commaToken = loc(3); - node->rbracketToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + AST::PatternElement *e = new (pool) AST::PatternElement(sym(1).Expression); + sym(1).Node = new (pool) AST::PatternElementList(nullptr, e); + } break; ./ -PrimaryExpression: T_LBRACKET ElementList T_COMMA Elision T_RBRACKET ; +ElementList: Elision AssignmentExpression_In; /. -case $rule_number: { - AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), - sym(4).Elision->finish()); - node->lbracketToken = loc(1); - node->commaToken = loc(3); - node->rbracketToken = loc(5); - sym(1).Node = node; -} break; + case $rule_number: { + AST::PatternElement *e = new (pool) AST::PatternElement(sym(2).Expression); + sym(1).Node = new (pool) AST::PatternElementList(sym(1).Elision->finish(), e); + } break; ./ --- PrimaryExpression: T_LBRACE T_RBRACE ; --- /. --- case $rule_number: { --- sym(1).Node = new (pool) AST::ObjectLiteral(); --- } break; --- ./ +ElementList: ElisionOpt SpreadElement; +/. + case $rule_number: { + AST::PatternElementList *node = new (pool) AST::PatternElementList(sym(1).Elision, sym(2).PatternElement); + sym(1).Node = node; + } break; +./ -PrimaryExpression: T_LBRACE PropertyAssignmentListOpt T_RBRACE ; +ElementList: ElementList T_COMMA ElisionOpt AssignmentExpression_In; /. -case $rule_number: { - AST::ObjectLiteral *node = 0; - if (sym(2).Node) - node = new (pool) AST::ObjectLiteral( - sym(2).PropertyAssignmentList->finish ()); - else - node = new (pool) AST::ObjectLiteral(); - node->lbraceToken = loc(1); - node->rbraceToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + AST::PatternElement *e = new (pool) AST::PatternElement(sym(4).Expression); + AST::PatternElementList *node = new (pool) AST::PatternElementList(sym(3).Elision, e); + sym(1).Node = sym(1).PatternElementList->append(node); + } break; ./ -PrimaryExpression: T_LBRACE PropertyAssignmentList T_COMMA T_RBRACE ; +ElementList: ElementList T_COMMA ElisionOpt SpreadElement; /. -case $rule_number: { - AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral( - sym(2).PropertyAssignmentList->finish ()); - node->lbraceToken = loc(1); - node->rbraceToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + AST::PatternElementList *node = new (pool) AST::PatternElementList(sym(3).Elision, sym(4).PatternElement); + sym(1).Node = sym(1).PatternElementList->append(node); + } break; ./ -PrimaryExpression: T_LPAREN Expression T_RPAREN ; +Elision: T_COMMA; /. -case $rule_number: { - AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression); - node->lparenToken = loc(1); - node->rparenToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + AST::Elision *node = new (pool) AST::Elision(); + node->commaToken = loc(1); + sym(1).Node = node; + } break; ./ +Elision: Elision T_COMMA; +/. + case $rule_number: { + AST::Elision *node = new (pool) AST::Elision(sym(1).Elision); + node->commaToken = loc(2); + sym(1).Node = node; + } break; +./ -UiQualifiedId: MemberExpression ; +ElisionOpt: ; /. -case $rule_number: { - if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, - QLatin1String("Ignored annotation"))); + case $rule_number: { + sym(1).Node = nullptr; + } break; +./ - sym(1).Expression = mem->base; - } +ElisionOpt: Elision; +/. + case $rule_number: { + sym(1).Node = sym(1).Elision->finish(); + } break; +./ - if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) { - sym(1).UiQualifiedId = qualifiedId; - } else { - sym(1).UiQualifiedId = 0; +SpreadElement: T_ELLIPSIS AssignmentExpression; +/. + case $rule_number: { + AST::PatternElement *node = new (pool) AST::PatternElement(sym(2).Expression, AST::PatternElement::SpreadElement); + sym(1).Node = node; + } break; +./ - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), - QLatin1String("Expected a qualified name id"))); +ObjectLiteral: T_LBRACE T_RBRACE; +/. + case $rule_number: { + AST::ObjectPattern *node = new (pool) AST::ObjectPattern(); + node->lbraceToken = loc(1); + node->rbraceToken = loc(2); + sym(1).Node = node; + } break; +./ - return false; // ### recover - } -} break; +ObjectLiteral: T_LBRACE PropertyDefinitionList T_RBRACE; +/. + case $rule_number: { + AST::ObjectPattern *node = new (pool) AST::ObjectPattern(sym(2).PatternPropertyList->finish()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; + } break; ./ -ElementList: AssignmentExpression ; +ObjectLiteral: T_LBRACE PropertyDefinitionList T_COMMA T_RBRACE; /. -case $rule_number: { - sym(1).Node = new (pool) AST::ElementList((AST::Elision *) 0, sym(1).Expression); -} break; + case $rule_number: { + AST::ObjectPattern *node = new (pool) AST::ObjectPattern(sym(2).PatternPropertyList->finish()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(4); + sym(1).Node = node; + } break; ./ -ElementList: Elision AssignmentExpression ; + +UiPropertyDefinitionList: UiPropertyDefinition; +/. case $rule_number: Q_FALLTHROUGH(); ./ +PropertyDefinitionList: PropertyDefinition; /. -case $rule_number: { - sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression); -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::PatternPropertyList(sym(1).PatternProperty); + } break; ./ -ElementList: ElementList T_COMMA AssignmentExpression ; +UiPropertyDefinitionList: UiPropertyDefinitionList T_COMMA UiPropertyDefinition; +/. case $rule_number: Q_FALLTHROUGH(); ./ +PropertyDefinitionList: PropertyDefinitionList T_COMMA PropertyDefinition; /. -case $rule_number: { - AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, - (AST::Elision *) 0, sym(3).Expression); - node->commaToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::PatternPropertyList *node = new (pool) AST::PatternPropertyList(sym(1).PatternPropertyList, sym(3).PatternProperty); + sym(1).Node = node; + } break; ./ -ElementList: ElementList T_COMMA Elision AssignmentExpression ; +PropertyDefinition: IdentifierReference; /. -case $rule_number: { - AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(), - sym(4).Expression); - node->commaToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::IdentifierPropertyName *name = new (pool) AST::IdentifierPropertyName(stringRef(1)); + name->propertyNameToken = loc(1); + AST::IdentifierExpression *expr = new (pool) AST::IdentifierExpression(stringRef(1)); + expr->identifierToken = loc(1); + AST::PatternProperty *node = new (pool) AST::PatternProperty(name, expr); + node->colonToken = loc(2); + sym(1).Node = node; + } break; ./ -Elision: T_COMMA ; +-- Using this production should result in a syntax error when used in an ObjectLiteral +PropertyDefinition: CoverInitializedName; + +CoverInitializedName: IdentifierReference Initializer_In; /. -case $rule_number: { - AST::Elision *node = new (pool) AST::Elision(); - node->commaToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::IdentifierPropertyName *name = new (pool) AST::IdentifierPropertyName(stringRef(1)); + name->propertyNameToken = loc(1); + AST::IdentifierExpression *left = new (pool) AST::IdentifierExpression(stringRef(1)); + left->identifierToken = loc(1); + // if initializer is an anonymous function expression, we need to assign identifierref as it's name + if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression)) + f->name = stringRef(1); + if (auto *c = asAnonymousClassDefinition(sym(2).Expression)) + c->name = stringRef(1); + AST::BinaryExpression *assignment = new (pool) AST::BinaryExpression(left, QSOperator::Assign, sym(2).Expression); + AST::PatternProperty *node = new (pool) AST::PatternProperty(name, assignment); + node->colonToken = loc(1); + sym(1).Node = node; + + } break; ./ -Elision: Elision T_COMMA ; +UiPropertyDefinition: UiPropertyName T_COLON AssignmentExpression_In; +/. case $rule_number: Q_FALLTHROUGH(); ./ +PropertyDefinition: PropertyName T_COLON AssignmentExpression_In; /. -case $rule_number: { - AST::Elision *node = new (pool) AST::Elision(sym(1).Elision); - node->commaToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, sym(3).Expression); + if (auto *f = asAnonymousFunctionDefinition(sym(3).Expression)) { + if (!AST::cast<AST::ComputedPropertyName *>(sym(1).PropertyName)) + f->name = driver->newStringRef(sym(1).PropertyName->asString()); + } + if (auto *c = asAnonymousClassDefinition(sym(3).Expression)) { + if (!AST::cast<AST::ComputedPropertyName *>(sym(1).PropertyName)) + c->name = driver->newStringRef(sym(1).PropertyName->asString()); + } + node->colonToken = loc(2); + sym(1).Node = node; + } break; +./ + +PropertyDefinition: MethodDefinition; + +PropertyName: LiteralPropertyName; +PropertyName: ComputedPropertyName; + +LiteralPropertyName: IdentifierName; +/. + case $rule_number: { + AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; + } break; ./ -PropertyAssignment: PropertyName T_COLON AssignmentExpression ; +UiPropertyName: T_STRING_LITERAL; +/. case $rule_number: Q_FALLTHROUGH(); ./ +LiteralPropertyName: T_STRING_LITERAL; /. -case $rule_number: { - AST::PropertyNameAndValue *node = new (pool) AST::PropertyNameAndValue( - sym(1).PropertyName, sym(3).Expression); - node->colonToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; + } break; ./ -PropertyAssignment: T_GET PropertyName T_LPAREN T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +UiPropertyName: T_NUMERIC_LITERAL; +/. case $rule_number: Q_FALLTHROUGH(); ./ +LiteralPropertyName: T_NUMERIC_LITERAL; /. -case $rule_number: { - AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter( - sym(2).PropertyName, sym(6).FunctionBody); - node->getSetToken = loc(1); - node->lparenToken = loc(3); - node->rparenToken = loc(4); - node->lbraceToken = loc(5); - node->rbraceToken = loc(7); - sym(1).Node = node; -} break; + case $rule_number: { + AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval); + node->propertyNameToken = loc(1); + sym(1).Node = node; + } break; ./ -PropertyAssignment: T_SET PropertyName T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +IdentifierName: IdentifierReference; +IdentifierName: ReservedIdentifier; + +ReservedIdentifier: T_BREAK; +ReservedIdentifier: T_CASE; +ReservedIdentifier: T_CATCH; +ReservedIdentifier: T_CONTINUE; +ReservedIdentifier: T_DEFAULT; +ReservedIdentifier: T_DELETE; +ReservedIdentifier: T_DO; +ReservedIdentifier: T_ELSE; +ReservedIdentifier: T_ENUM; +ReservedIdentifier: T_FALSE; +ReservedIdentifier: T_FINALLY; +ReservedIdentifier: T_FOR; +ReservedIdentifier: T_FUNCTION; +ReservedIdentifier: T_IF; +ReservedIdentifier: T_IN; +ReservedIdentifier: T_INSTANCEOF; +ReservedIdentifier: T_NEW; +ReservedIdentifier: T_NULL; +ReservedIdentifier: T_RETURN; +ReservedIdentifier: T_SWITCH; +ReservedIdentifier: T_THIS; +ReservedIdentifier: T_THROW; +ReservedIdentifier: T_TRUE; +ReservedIdentifier: T_TRY; +ReservedIdentifier: T_TYPEOF; +ReservedIdentifier: T_VAR; +ReservedIdentifier: T_VOID; +ReservedIdentifier: T_WHILE; +ReservedIdentifier: T_CONST; +ReservedIdentifier: T_LET; +ReservedIdentifier: T_DEBUGGER; +ReservedIdentifier: T_RESERVED_WORD; +ReservedIdentifier: T_SUPER; +ReservedIdentifier: T_WITH; +ReservedIdentifier: T_CLASS; +ReservedIdentifier: T_EXTENDS; +ReservedIdentifier: T_EXPORT; + +ComputedPropertyName: T_LBRACKET AssignmentExpression_In T_RBRACKET; /. -case $rule_number: { - AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter( - sym(2).PropertyName, sym(4).FormalParameterList, sym(7).FunctionBody); - node->getSetToken = loc(1); - node->lparenToken = loc(3); - node->rparenToken = loc(5); - node->lbraceToken = loc(6); - node->rbraceToken = loc(8); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ComputedPropertyName *node = new (pool) AST::ComputedPropertyName(sym(2).Expression); + node->propertyNameToken = loc(1); + sym(1).Node = node; + } break; ./ -PropertyAssignmentList: PropertyAssignment ; +Initializer: T_EQ AssignmentExpression; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Initializer_In: T_EQ AssignmentExpression_In; /. case $rule_number: { - sym(1).Node = new (pool) AST::PropertyAssignmentList(sym(1).PropertyAssignment); + sym(1) = sym(2); } break; ./ -PropertyAssignmentList: PropertyAssignmentList T_COMMA PropertyAssignment ; + +InitializerOpt: ; +/. case $rule_number: Q_FALLTHROUGH(); ./ +InitializerOpt_In: ; /. -case $rule_number: { - AST::PropertyAssignmentList *node = new (pool) AST::PropertyAssignmentList( - sym(1).PropertyAssignmentList, sym(3).PropertyAssignment); - node->commaToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = nullptr; + } break; ./ -PropertyName: JsIdentifier %prec SHIFT_THERE ; +InitializerOpt: Initializer; +InitializerOpt_In: Initializer_In; + +TemplateLiteral: T_NO_SUBSTITUTION_TEMPLATE; +/. case $rule_number: Q_FALLTHROUGH(); ./ + +TemplateSpans: T_TEMPLATE_TAIL; /. -case $rule_number: { - AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); - node->propertyNameToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::TemplateLiteral *node = new (pool) AST::TemplateLiteral(stringRef(1), nullptr); + node->literalToken = loc(1); + sym(1).Node = node; + } break; ./ -PropertyName: T_STRING_LITERAL ; +TemplateSpans: T_TEMPLATE_MIDDLE Expression TemplateSpans; +/. case $rule_number: Q_FALLTHROUGH(); ./ + +TemplateLiteral: T_TEMPLATE_HEAD Expression TemplateSpans; /. -case $rule_number: { - AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1)); - node->propertyNameToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::TemplateLiteral *node = new (pool) AST::TemplateLiteral(stringRef(1), sym(2).Expression); + node->next = sym(3).Template; + node->literalToken = loc(1); + sym(1).Node = node; + } break; ./ -PropertyName: T_NUMERIC_LITERAL ; + +MemberExpression: PrimaryExpression; + +Super: T_SUPER; /. -case $rule_number: { - AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval); - node->propertyNameToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::SuperLiteral *node = new (pool) AST::SuperLiteral(); + node->superToken = loc(1); + sym(1).Node = node; + } break; ./ -PropertyName: ReservedIdentifier ; + +MemberExpression: Super T_LBRACKET Expression_In T_RBRACKET; +/. case $rule_number: Q_FALLTHROUGH(); ./ +MemberExpression: MemberExpression T_LBRACKET Expression_In T_RBRACKET; /. -case $rule_number: { - AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); - node->propertyNameToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; + } break; ./ -ReservedIdentifier: T_BREAK ; -ReservedIdentifier: T_CASE ; -ReservedIdentifier: T_CATCH ; -ReservedIdentifier: T_CONTINUE ; -ReservedIdentifier: T_DEFAULT ; -ReservedIdentifier: T_DELETE ; -ReservedIdentifier: T_DO ; -ReservedIdentifier: T_ELSE ; -ReservedIdentifier: T_ENUM ; -ReservedIdentifier: T_FALSE ; -ReservedIdentifier: T_FINALLY ; -ReservedIdentifier: T_FOR ; -ReservedIdentifier: T_FUNCTION ; -ReservedIdentifier: T_IF ; -ReservedIdentifier: T_IN ; -ReservedIdentifier: T_INSTANCEOF ; -ReservedIdentifier: T_NEW ; -ReservedIdentifier: T_NULL ; -ReservedIdentifier: T_RETURN ; -ReservedIdentifier: T_SWITCH ; -ReservedIdentifier: T_THIS ; -ReservedIdentifier: T_THROW ; -ReservedIdentifier: T_TRUE ; -ReservedIdentifier: T_TRY ; -ReservedIdentifier: T_TYPEOF ; -ReservedIdentifier: T_VAR ; -ReservedIdentifier: T_VOID ; -ReservedIdentifier: T_WHILE ; -ReservedIdentifier: T_CONST ; -ReservedIdentifier: T_LET ; -ReservedIdentifier: T_DEBUGGER ; -ReservedIdentifier: T_RESERVED_WORD ; -ReservedIdentifier: T_WITH ; - -PropertyIdentifier: JsIdentifier ; -PropertyIdentifier: ReservedIdentifier ; - -MemberExpression: PrimaryExpression ; -MemberExpression: FunctionExpression ; - -MemberExpression: MemberExpression T_LBRACKET Expression T_RBRACKET ; + +-- the identifier has to be "target", catched at codegen time +NewTarget: T_NEW T_DOT T_IDENTIFIER; +/. case $rule_number: + { + AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1)); + node->identifierToken= loc(1); + sym(1).Node = node; + } Q_FALLTHROUGH(); +./ +MemberExpression: Super T_DOT IdentifierName; +/. case $rule_number: Q_FALLTHROUGH(); ./ +MemberExpression: MemberExpression T_DOT IdentifierName; /. -case $rule_number: { - AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); - node->lbracketToken = loc(2); - node->rbracketToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; + } break; ./ -MemberExpression: MemberExpression T_DOT PropertyIdentifier ; +MemberExpression: MetaProperty; + +MemberExpression: T_NEW MemberExpression T_LPAREN Arguments T_RPAREN; /. -case $rule_number: { - AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); - node->dotToken = loc(2); - node->identifierToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList); + node->newToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + sym(1).Node = node; + } break; ./ -MemberExpression: T_NEW MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ; +MetaProperty: NewTarget; + + +NewExpression: MemberExpression; + +NewExpression: T_NEW NewExpression; /. -case $rule_number: { - AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList); - node->newToken = loc(1); - node->lparenToken = loc(3); - node->rparenToken = loc(5); - sym(1).Node = node; -} break; + case $rule_number: { + AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression); + node->newToken = loc(1); + sym(1).Node = node; + } break; ./ -NewExpression: MemberExpression ; -NewExpression: T_NEW NewExpression ; +CallExpression: CallExpression TemplateLiteral; +/. case $rule_number: Q_FALLTHROUGH(); ./ +MemberExpression: MemberExpression TemplateLiteral; /. -case $rule_number: { - AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression); - node->newToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::TaggedTemplate *node = new (pool) AST::TaggedTemplate(sym(1).Expression, sym(2).Template); + sym(1).Node = node; + } break; ./ -CallExpression: MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ; +CallExpression: MemberExpression T_LPAREN Arguments T_RPAREN; /. -case $rule_number: { - AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; + } break; ./ -CallExpression: CallExpression T_LPAREN ArgumentListOpt T_RPAREN ; +CallExpression: Super T_LPAREN Arguments T_RPAREN; +/. case $rule_number: Q_FALLTHROUGH(); ./ +CallExpression: CallExpression T_LPAREN Arguments T_RPAREN; /. -case $rule_number: { - AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; + } break; ./ -CallExpression: CallExpression T_LBRACKET Expression T_RBRACKET ; +CallExpression: CallExpression T_LBRACKET Expression_In T_RBRACKET; /. -case $rule_number: { - AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); - node->lbracketToken = loc(2); - node->rbracketToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; + } break; ./ -CallExpression: CallExpression T_DOT PropertyIdentifier ; +CallExpression: CallExpression T_DOT IdentifierName; /. -case $rule_number: { - AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); - node->dotToken = loc(2); - node->identifierToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; + } break; ./ -ArgumentListOpt: ; +Arguments: ; /. -case $rule_number: { - sym(1).Node = 0; -} break; + case $rule_number: { + sym(1).Node = nullptr; + } break; ./ -ArgumentListOpt: ArgumentList ; +Arguments: ArgumentList; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Arguments: ArgumentList T_COMMA; /. -case $rule_number: { - sym(1).Node = sym(1).ArgumentList->finish(); -} break; + case $rule_number: { + sym(1).Node = sym(1).ArgumentList->finish(); + } break; ./ -ArgumentList: AssignmentExpression ; +ArgumentList: AssignmentExpression_In; /. -case $rule_number: { - sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression); -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression); + } break; ./ -ArgumentList: ArgumentList T_COMMA AssignmentExpression ; +ArgumentList: T_ELLIPSIS AssignmentExpression_In; /. -case $rule_number: { - AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression); - node->commaToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(2).Expression); + node->isSpreadElement = true; + sym(1).Node = node; + } break; ./ -LeftHandSideExpression: NewExpression ; -LeftHandSideExpression: CallExpression ; -PostfixExpression: LeftHandSideExpression ; +ArgumentList: ArgumentList T_COMMA AssignmentExpression_In; +/. + case $rule_number: { + AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; + } break; +./ -PostfixExpression: LeftHandSideExpression T_PLUS_PLUS ; +ArgumentList: ArgumentList T_COMMA T_ELLIPSIS AssignmentExpression_In; /. -case $rule_number: { - AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression); - node->incrementToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(4).Expression); + node->commaToken = loc(2); + node->isSpreadElement = true; + sym(1).Node = node; + } break; ./ -PostfixExpression: LeftHandSideExpression T_MINUS_MINUS ; +LeftHandSideExpression: NewExpression; +LeftHandSideExpression: CallExpression; + +UpdateExpression: LeftHandSideExpression; + +UpdateExpression: LeftHandSideExpression T_PLUS_PLUS; /. -case $rule_number: { - AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression); - node->decrementToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression); + node->incrementToken = loc(2); + sym(1).Node = node; + } break; ./ -UnaryExpression: PostfixExpression ; +UpdateExpression: LeftHandSideExpression T_MINUS_MINUS; +/. + case $rule_number: { + AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression); + node->decrementToken = loc(2); + sym(1).Node = node; + } break; +./ -UnaryExpression: T_DELETE UnaryExpression ; +UpdateExpression: T_PLUS_PLUS UnaryExpression; /. -case $rule_number: { - AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression); - node->deleteToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression); + node->incrementToken = loc(1); + sym(1).Node = node; + } break; ./ -UnaryExpression: T_VOID UnaryExpression ; +UpdateExpression: T_MINUS_MINUS UnaryExpression; /. -case $rule_number: { - AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression); - node->voidToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression); + node->decrementToken = loc(1); + sym(1).Node = node; + } break; ./ -UnaryExpression: T_TYPEOF UnaryExpression ; +UnaryExpression: UpdateExpression; + +UnaryExpression: T_DELETE UnaryExpression; /. -case $rule_number: { - AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression); - node->typeofToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression); + node->deleteToken = loc(1); + sym(1).Node = node; + } break; ./ -UnaryExpression: T_PLUS_PLUS UnaryExpression ; +UnaryExpression: T_VOID UnaryExpression; /. -case $rule_number: { - AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression); - node->incrementToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression); + node->voidToken = loc(1); + sym(1).Node = node; + } break; ./ -UnaryExpression: T_MINUS_MINUS UnaryExpression ; +UnaryExpression: T_TYPEOF UnaryExpression; /. -case $rule_number: { - AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression); - node->decrementToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression); + node->typeofToken = loc(1); + sym(1).Node = node; + } break; ./ -UnaryExpression: T_PLUS UnaryExpression ; +UnaryExpression: T_PLUS UnaryExpression; /. -case $rule_number: { - AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression); - node->plusToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression); + node->plusToken = loc(1); + sym(1).Node = node; + } break; ./ -UnaryExpression: T_MINUS UnaryExpression ; +UnaryExpression: T_MINUS UnaryExpression; /. -case $rule_number: { - AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression); - node->minusToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression); + node->minusToken = loc(1); + sym(1).Node = node; + } break; ./ -UnaryExpression: T_TILDE UnaryExpression ; +UnaryExpression: T_TILDE UnaryExpression; /. -case $rule_number: { - AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression); - node->tildeToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression); + node->tildeToken = loc(1); + sym(1).Node = node; + } break; ./ -UnaryExpression: T_NOT UnaryExpression ; +UnaryExpression: T_NOT UnaryExpression; /. -case $rule_number: { - AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression); - node->notToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression); + node->notToken = loc(1); + sym(1).Node = node; + } break; ./ -MultiplicativeExpression: UnaryExpression ; +ExponentiationExpression: UnaryExpression; -MultiplicativeExpression: MultiplicativeExpression T_STAR UnaryExpression ; +ExponentiationExpression: UpdateExpression T_STAR_STAR ExponentiationExpression; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Mul, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Exp, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -MultiplicativeExpression: MultiplicativeExpression T_DIVIDE_ UnaryExpression ; +MultiplicativeExpression: ExponentiationExpression; + +MultiplicativeExpression: MultiplicativeExpression MultiplicativeOperator ExponentiationExpression; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Div, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -MultiplicativeExpression: MultiplicativeExpression T_REMAINDER UnaryExpression ; +MultiplicativeOperator: T_STAR; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Mod, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).ival = QSOperator::Mul; + } break; ./ -AdditiveExpression: MultiplicativeExpression ; +MultiplicativeOperator: T_DIVIDE_; +/. + case $rule_number: { + sym(1).ival = QSOperator::Div; + } break; +./ -AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression ; +MultiplicativeOperator: T_REMAINDER; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Add, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).ival = QSOperator::Mod; + } break; ./ -AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression ; +AdditiveExpression: MultiplicativeExpression; + +AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Sub, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Add, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; +./ + +AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression; +/. + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Sub, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -ShiftExpression: AdditiveExpression ; +ShiftExpression: AdditiveExpression; -ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression ; +ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::LShift, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::LShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression ; +ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::RShift, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::RShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression ; +ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::URShift, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::URShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -RelationalExpression: ShiftExpression ; +RelationalExpression_In: ShiftExpression; +RelationalExpression: ShiftExpression; -RelationalExpression: RelationalExpression T_LT ShiftExpression ; +RelationalExpression_In: RelationalExpression_In RelationalOperator ShiftExpression; +/. case $rule_number: Q_FALLTHROUGH(); ./ +RelationalExpression: RelationalExpression RelationalOperator ShiftExpression; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Lt, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -RelationalExpression: RelationalExpression T_GT ShiftExpression ; +RelationalOperator: T_LT; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Gt, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).ival = QSOperator::Lt; + } break; +./ +RelationalOperator: T_GT; +/. + case $rule_number: { + sym(1).ival = QSOperator::Gt; + } break; +./ +RelationalOperator: T_LE; +/. + case $rule_number: { + sym(1).ival = QSOperator::Le; + } break; +./ +RelationalOperator: T_GE; +/. + case $rule_number: { + sym(1).ival = QSOperator::Ge; + } break; +./ +RelationalOperator: T_INSTANCEOF; +/. + case $rule_number: { + sym(1).ival = QSOperator::InstanceOf; + } break; ./ -RelationalExpression: RelationalExpression T_LE ShiftExpression ; +RelationalExpression_In: RelationalExpression_In T_IN ShiftExpression; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Le, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::In, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -RelationalExpression: RelationalExpression T_GE ShiftExpression ; +EqualityExpression_In: RelationalExpression_In; +EqualityExpression: RelationalExpression; + +EqualityExpression_In: EqualityExpression_In EqualityOperator RelationalExpression_In; +/. case $rule_number: Q_FALLTHROUGH(); ./ +EqualityExpression: EqualityExpression EqualityOperator RelationalExpression; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Ge, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -RelationalExpression: RelationalExpression T_INSTANCEOF ShiftExpression ; +EqualityOperator: T_EQ_EQ; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::InstanceOf, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).ival = QSOperator::Equal; + } break; +./ +EqualityOperator: T_NOT_EQ; +/. + case $rule_number: { + sym(1).ival = QSOperator::NotEqual; + } break; ./ +EqualityOperator: T_EQ_EQ_EQ; +/. + case $rule_number: { + sym(1).ival = QSOperator::StrictEqual; + } break; +./ +EqualityOperator: T_NOT_EQ_EQ; +/. + case $rule_number: { + sym(1).ival = QSOperator::StrictNotEqual; + } break; +./ + + +BitwiseANDExpression: EqualityExpression; +XitwiseANDExpression_In: EqualityExpression_In; -RelationalExpression: RelationalExpression T_IN ShiftExpression ; +BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression; +/. case $rule_number: Q_FALLTHROUGH(); ./ +XitwiseANDExpression_In: XitwiseANDExpression_In T_AND EqualityExpression_In; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::In, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -RelationalExpressionNotIn: ShiftExpression ; -RelationalExpressionNotIn: RelationalExpressionNotIn T_LT ShiftExpression ; +BitwiseXORExpression: BitwiseANDExpression; +BitwiseXORExpression_In: XitwiseANDExpression_In; + +BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression; +/. case $rule_number: Q_FALLTHROUGH(); ./ +BitwiseXORExpression_In: BitwiseXORExpression_In T_XOR XitwiseANDExpression_In; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Lt, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -RelationalExpressionNotIn: RelationalExpressionNotIn T_GT ShiftExpression ; +BitwiseORExpression: BitwiseXORExpression; +BitwiseORExpression_In: BitwiseXORExpression_In; + +BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression; +/. case $rule_number: Q_FALLTHROUGH(); ./ +BitwiseORExpression_In: BitwiseORExpression_In T_OR BitwiseXORExpression_In; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Gt, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -RelationalExpressionNotIn: RelationalExpressionNotIn T_LE ShiftExpression ; +LogicalANDExpression: BitwiseORExpression; +LogicalANDExpression_In: BitwiseORExpression_In; + +LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression; +/. case $rule_number: Q_FALLTHROUGH(); ./ +LogicalANDExpression_In: LogicalANDExpression_In T_AND_AND BitwiseORExpression_In; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Le, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -RelationalExpressionNotIn: RelationalExpressionNotIn T_GE ShiftExpression ; +LogicalORExpression: LogicalANDExpression; +LogicalORExpression_In: LogicalANDExpression_In; + +LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression; +/. case $rule_number: Q_FALLTHROUGH(); ./ +LogicalORExpression_In: LogicalORExpression_In T_OR_OR LogicalANDExpression_In; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Ge, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -RelationalExpressionNotIn: RelationalExpressionNotIn T_INSTANCEOF ShiftExpression ; + +ConditionalExpression: LogicalORExpression; +ConditionalExpression_In: LogicalORExpression_In; + +ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression_In T_COLON AssignmentExpression; +/. case $rule_number: Q_FALLTHROUGH(); ./ +ConditionalExpression_In: LogicalORExpression_In T_QUESTION AssignmentExpression_In T_COLON AssignmentExpression_In; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::InstanceOf, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; + } break; ./ -EqualityExpression: RelationalExpression ; +AssignmentExpression: ConditionalExpression; +AssignmentExpression_In: ConditionalExpression_In; + +AssignmentExpression: YieldExpression; +AssignmentExpression_In: YieldExpression_In; -EqualityExpression: EqualityExpression T_EQ_EQ RelationalExpression ; +AssignmentExpression: ArrowFunction; +AssignmentExpression_In: ArrowFunction_In; + +AssignmentExpression: LeftHandSideExpression T_EQ AssignmentExpression; +/. case $rule_number: Q_FALLTHROUGH(); ./ +AssignmentExpression_In: LeftHandSideExpression T_EQ AssignmentExpression_In; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Equal, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + // need to convert the LHS to an AssignmentPattern if it was an Array/ObjectLiteral + if (AST::Pattern *p = sym(1).Expression->patternCast()) { + AST::SourceLocation errorLoc; + QString errorMsg; + if (!p->convertLiteralToAssignmentPattern(pool, &errorLoc, &errorMsg)) { + syntaxError(errorLoc, errorMsg); + return false; + } + } + // if lhs is an identifier expression and rhs is an anonymous function expression, we need to assign the name of lhs to the function + if (auto *f = asAnonymousFunctionDefinition(sym(3).Expression)) { + if (auto *id = AST::cast<AST::IdentifierExpression *>(sym(1).Expression)) + f->name = id->name; + } + if (auto *c = asAnonymousClassDefinition(sym(3).Expression)) { + if (auto *id = AST::cast<AST::IdentifierExpression *>(sym(1).Expression)) + c->name = id->name; + } + + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Assign, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -EqualityExpression: EqualityExpression T_NOT_EQ RelationalExpression ; +AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression; +/. case $rule_number: Q_FALLTHROUGH(); ./ +AssignmentExpression_In: LeftHandSideExpression AssignmentOperator AssignmentExpression_In; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::NotEqual, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; + } break; ./ -EqualityExpression: EqualityExpression T_EQ_EQ_EQ RelationalExpression ; +AssignmentOperator: T_STAR_EQ; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::StrictEqual, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).ival = QSOperator::InplaceMul; + } break; ./ -EqualityExpression: EqualityExpression T_NOT_EQ_EQ RelationalExpression ; +AssignmentOperator: T_STAR_STAR_EQ; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::StrictNotEqual, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).ival = QSOperator::InplaceExp; + } break; ./ -EqualityExpressionNotIn: RelationalExpressionNotIn ; +AssignmentOperator: T_DIVIDE_EQ; +/. + case $rule_number: { + sym(1).ival = QSOperator::InplaceDiv; + } break; +./ -EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ RelationalExpressionNotIn ; +AssignmentOperator: T_REMAINDER_EQ; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Equal, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).ival = QSOperator::InplaceMod; + } break; ./ -EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ RelationalExpressionNotIn; +AssignmentOperator: T_PLUS_EQ; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::NotEqual, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).ival = QSOperator::InplaceAdd; + } break; ./ -EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ_EQ RelationalExpressionNotIn ; +AssignmentOperator: T_MINUS_EQ; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::StrictEqual, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).ival = QSOperator::InplaceSub; + } break; ./ -EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ_EQ RelationalExpressionNotIn ; +AssignmentOperator: T_LT_LT_EQ; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::StrictNotEqual, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).ival = QSOperator::InplaceLeftShift; + } break; ./ -BitwiseANDExpression: EqualityExpression ; +AssignmentOperator: T_GT_GT_EQ; +/. + case $rule_number: { + sym(1).ival = QSOperator::InplaceRightShift; + } break; +./ -BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression ; +AssignmentOperator: T_GT_GT_GT_EQ; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitAnd, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).ival = QSOperator::InplaceURightShift; + } break; ./ -BitwiseANDExpressionNotIn: EqualityExpressionNotIn ; +AssignmentOperator: T_AND_EQ; +/. + case $rule_number: { + sym(1).ival = QSOperator::InplaceAnd; + } break; +./ -BitwiseANDExpressionNotIn: BitwiseANDExpressionNotIn T_AND EqualityExpressionNotIn ; +AssignmentOperator: T_XOR_EQ; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitAnd, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).ival = QSOperator::InplaceXor; + } break; +./ + +AssignmentOperator: T_OR_EQ; +/. + case $rule_number: { + sym(1).ival = QSOperator::InplaceOr; + } break; ./ -BitwiseXORExpression: BitwiseANDExpression ; +Expression: AssignmentExpression; +Expression_In: AssignmentExpression_In; -BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression ; +Expression: Expression T_COMMA AssignmentExpression; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Expression_In: Expression_In T_COMMA AssignmentExpression_In; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitXor, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; + } break; ./ -BitwiseXORExpressionNotIn: BitwiseANDExpressionNotIn ; +ExpressionOpt: ; +/. case $rule_number: Q_FALLTHROUGH(); ./ +ExpressionOpt_In: ; +/. + case $rule_number: { + sym(1).Node = nullptr; + } break; +./ + +ExpressionOpt: Expression; +ExpressionOpt_In: Expression_In; -BitwiseXORExpressionNotIn: BitwiseXORExpressionNotIn T_XOR BitwiseANDExpressionNotIn ; +-- Statements + +Statement: ExpressionStatementLookahead T_FORCE_BLOCK BlockStatement; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitXor, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = sym(3).Node; + } break; ./ -BitwiseORExpression: BitwiseXORExpression ; +Statement: ExpressionStatementLookahead VariableStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Statement: ExpressionStatementLookahead EmptyStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Statement: ExpressionStatementLookahead ExpressionStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Statement: ExpressionStatementLookahead IfStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Statement: ExpressionStatementLookahead BreakableStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Statement: ExpressionStatementLookahead ContinueStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Statement: ExpressionStatementLookahead BreakStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Statement: ExpressionStatementLookahead ReturnStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Statement: ExpressionStatementLookahead WithStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Statement: ExpressionStatementLookahead LabelledStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Statement: ExpressionStatementLookahead ThrowStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Statement: ExpressionStatementLookahead TryStatement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +Statement: ExpressionStatementLookahead DebuggerStatement; +/. + case $rule_number: { + sym(1).Node = sym(2).Node; + } break; +./ + +Declaration: HoistableDeclaration; +Declaration: ClassDeclaration; +Declaration: LexicalDeclaration_In; + +HoistableDeclaration: FunctionDeclaration; +HoistableDeclaration: GeneratorDeclaration; + +HoistableDeclaration_Default: FunctionDeclaration_Default; +HoistableDeclaration_Default: GeneratorDeclaration_Default; + +BreakableStatement: IterationStatement; +BreakableStatement: SwitchStatement; -BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression ; +BlockStatement: Block; + +Block: T_LBRACE StatementListOpt T_RBRACE; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitOr, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::Block *node = new (pool) AST::Block(sym(2).StatementList); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; + } break; ./ -BitwiseORExpressionNotIn: BitwiseXORExpressionNotIn ; +StatementList: StatementListItem; -BitwiseORExpressionNotIn: BitwiseORExpressionNotIn T_OR BitwiseXORExpressionNotIn ; +StatementList: StatementList StatementListItem; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitOr, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).StatementList = sym(1).StatementList->append(sym(2).StatementList); + } break; ./ -LogicalANDExpression: BitwiseORExpression ; +StatementListItem: Statement; +/. + case $rule_number: { + sym(1).StatementList = new (pool) AST::StatementList(sym(1).Statement); + } break; +./ -LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression ; +StatementListItem: ExpressionStatementLookahead T_FORCE_DECLARATION Declaration T_AUTOMATIC_SEMICOLON; +StatementListItem: ExpressionStatementLookahead T_FORCE_DECLARATION Declaration T_SEMICOLON; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::And, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::StatementList(sym(3).FunctionDeclaration); + } break; ./ -LogicalANDExpressionNotIn: BitwiseORExpressionNotIn ; +StatementListOpt: ExpressionStatementLookahead; +/. + case $rule_number: { + sym(1).Node = nullptr; + } break; +./ -LogicalANDExpressionNotIn: LogicalANDExpressionNotIn T_AND_AND BitwiseORExpressionNotIn ; +StatementListOpt: StatementList; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::And, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = sym(1).StatementList->finish(); + } break; ./ -LogicalORExpression: LogicalANDExpression ; +LetOrConst: T_LET; +/. + case $rule_number: { + sym(1).scope = AST::VariableScope::Let; + } break; +./ +LetOrConst: T_CONST; +/. + case $rule_number: { + sym(1).scope = AST::VariableScope::Const; + } break; +./ -LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression ; +Var: T_VAR; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Or, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).scope = AST::VariableScope::Var; + } break; ./ -LogicalORExpressionNotIn: LogicalANDExpressionNotIn ; +LexicalDeclaration: LetOrConst BindingList; +/. case $rule_number: Q_FALLTHROUGH(); ./ +LexicalDeclaration_In: LetOrConst BindingList_In; +/. case $rule_number: Q_FALLTHROUGH(); ./ +VarDeclaration: Var VariableDeclarationList; +/. case $rule_number: Q_FALLTHROUGH(); ./ +VarDeclaration_In: Var VariableDeclarationList_In; +/. + case $rule_number: { + AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(sym(1).scope)); + node->declarationKindToken = loc(1); + sym(1).Node = node; + } break; +./ + +VariableStatement: VarDeclaration_In T_AUTOMATIC_SEMICOLON; +VariableStatement: VarDeclaration_In T_SEMICOLON; -LogicalORExpressionNotIn: LogicalORExpressionNotIn T_OR_OR LogicalANDExpressionNotIn ; +BindingList: LexicalBinding_In; +/. case $rule_number: Q_FALLTHROUGH(); ./ +BindingList_In: LexicalBinding_In; +/. case $rule_number: Q_FALLTHROUGH(); ./ +VariableDeclarationList: VariableDeclaration; +/. case $rule_number: Q_FALLTHROUGH(); ./ +VariableDeclarationList_In: VariableDeclaration_In; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Or, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).PatternElement); + } break; ./ -ConditionalExpression: LogicalORExpression ; +BindingList: BindingList T_COMMA LexicalBinding; +/. case $rule_number: Q_FALLTHROUGH(); ./ +BindingList_In: BindingList_In T_COMMA LexicalBinding_In; +/. case $rule_number: Q_FALLTHROUGH(); ./ +VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration; +/. case $rule_number: Q_FALLTHROUGH(); ./ +VariableDeclarationList_In: VariableDeclarationList_In T_COMMA VariableDeclaration_In; +/. + case $rule_number: { + AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).PatternElement); + node->commaToken = loc(2); + sym(1).Node = node; + } break; +./ -ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression T_COLON AssignmentExpression ; +LexicalBinding: BindingIdentifier InitializerOpt; +/. case $rule_number: Q_FALLTHROUGH(); ./ +LexicalBinding_In: BindingIdentifier InitializerOpt_In; +/. case $rule_number: Q_FALLTHROUGH(); ./ +VariableDeclaration: BindingIdentifier InitializerOpt; +/. case $rule_number: Q_FALLTHROUGH(); ./ +VariableDeclaration_In: BindingIdentifier InitializerOpt_In; /. -case $rule_number: { - AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, - sym(3).Expression, sym(5).Expression); - node->questionToken = loc(2); - node->colonToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + auto *node = new (pool) AST::PatternElement(stringRef(1), sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; + // if initializer is an anonymous function expression, we need to assign identifierref as it's name + if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression)) + f->name = stringRef(1); + if (auto *c = asAnonymousClassDefinition(sym(2).Expression)) + c->name = stringRef(1); + } break; ./ -ConditionalExpressionNotIn: LogicalORExpressionNotIn ; +LexicalBinding: BindingPattern Initializer; +/. case $rule_number: Q_FALLTHROUGH(); ./ +LexicalBinding_In: BindingPattern Initializer_In; +/. case $rule_number: Q_FALLTHROUGH(); ./ +VariableDeclaration: BindingPattern Initializer; +/. case $rule_number: Q_FALLTHROUGH(); ./ +VariableDeclaration_In: BindingPattern Initializer_In; +/. + case $rule_number: { + auto *node = new (pool) AST::PatternElement(sym(1).Pattern, sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; + } break; +./ -ConditionalExpressionNotIn: LogicalORExpressionNotIn T_QUESTION AssignmentExpressionNotIn T_COLON AssignmentExpressionNotIn ; +BindingPattern: T_LBRACE ObjectBindingPattern T_RBRACE; /. -case $rule_number: { - AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, - sym(3).Expression, sym(5).Expression); - node->questionToken = loc(2); - node->colonToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + auto *node = new (pool) AST::ObjectPattern(sym(2).PatternPropertyList); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + node->parseMode = AST::Pattern::Binding; + sym(1).Node = node; + } break; ./ -AssignmentExpression: ConditionalExpression ; +BindingPattern: T_LBRACKET ArrayBindingPattern T_RBRACKET; +/. + case $rule_number: { + auto *node = new (pool) AST::ArrayPattern(sym(2).PatternElementList); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + node->parseMode = AST::Pattern::Binding; + sym(1).Node = node; + } break; +./ + +ObjectBindingPattern: ; +/. + case $rule_number: { + sym(1).Node = nullptr; + } break; +./ + +ObjectBindingPattern: BindingPropertyList; +/. case $rule_number: ./ +ObjectBindingPattern: BindingPropertyList T_COMMA; +/. + case $rule_number: { + sym(1).Node = sym(1).PatternPropertyList->finish(); + } break; +./ + +ArrayBindingPattern: ElisionOpt BindingRestElementOpt; +/. + case $rule_number: { + if (sym(1).Elision || sym(2).Node) { + auto *l = new (pool) AST::PatternElementList(sym(1).Elision, sym(2).PatternElement); + sym(1).Node = l->finish(); + } else { + sym(1).Node = nullptr; + } + } break; +./ -AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression ; +ArrayBindingPattern: BindingElementList; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - sym(2).ival, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = sym(1).PatternElementList->finish(); + } break; ./ -AssignmentExpressionNotIn: ConditionalExpressionNotIn ; +ArrayBindingPattern: BindingElementList T_COMMA ElisionOpt BindingRestElementOpt; +/. + case $rule_number: { + if (sym(3).Elision || sym(4).Node) { + auto *l = new (pool) AST::PatternElementList(sym(3).Elision, sym(4).PatternElement); + l = sym(1).PatternElementList->append(l); + sym(1).Node = l; + } + sym(1).Node = sym(1).PatternElementList->finish(); + } break; +./ -AssignmentExpressionNotIn: LeftHandSideExpression AssignmentOperator AssignmentExpressionNotIn ; +BindingPropertyList: BindingProperty; /. -case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - sym(2).ival, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::PatternPropertyList(sym(1).PatternProperty); + } break; ./ -AssignmentOperator: T_EQ ; +BindingPropertyList: BindingPropertyList T_COMMA BindingProperty; /. -case $rule_number: { - sym(1).ival = QSOperator::Assign; -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::PatternPropertyList(sym(1).PatternPropertyList, sym(3).PatternProperty); + } break; ./ -AssignmentOperator: T_STAR_EQ ; +BindingElementList: BindingElisionElement; + +BindingElementList: BindingElementList T_COMMA BindingElisionElement; /. -case $rule_number: { - sym(1).ival = QSOperator::InplaceMul; -} break; + case $rule_number: { + sym(1).PatternElementList = sym(1).PatternElementList->append(sym(3).PatternElementList); + } break; ./ -AssignmentOperator: T_DIVIDE_EQ ; +BindingElisionElement: ElisionOpt BindingElement; /. -case $rule_number: { - sym(1).ival = QSOperator::InplaceDiv; -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::PatternElementList(sym(1).Elision, sym(2).PatternElement); + } break; ./ -AssignmentOperator: T_REMAINDER_EQ ; + +BindingProperty: BindingIdentifier InitializerOpt_In; /. -case $rule_number: { - sym(1).ival = QSOperator::InplaceMod; -} break; + case $rule_number: { + AST::StringLiteralPropertyName *name = new (pool) AST::StringLiteralPropertyName(stringRef(1)); + name->propertyNameToken = loc(1); + // if initializer is an anonymous function expression, we need to assign identifierref as it's name + if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression)) + f->name = stringRef(1); + if (auto *c = asAnonymousClassDefinition(sym(2).Expression)) + c->name = stringRef(1); + sym(1).Node = new (pool) AST::PatternProperty(name, stringRef(1), sym(2).Expression); + } break; ./ -AssignmentOperator: T_PLUS_EQ ; +BindingProperty: PropertyName T_COLON BindingIdentifier InitializerOpt_In; /. -case $rule_number: { - sym(1).ival = QSOperator::InplaceAdd; -} break; + case $rule_number: { + AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, stringRef(3), sym(4).Expression); + sym(1).Node = node; + } break; ./ -AssignmentOperator: T_MINUS_EQ ; +BindingProperty: PropertyName T_COLON BindingPattern InitializerOpt_In; /. -case $rule_number: { - sym(1).ival = QSOperator::InplaceSub; -} break; + case $rule_number: { + AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, sym(3).Pattern, sym(4).Expression); + sym(1).Node = node; + } break; ./ -AssignmentOperator: T_LT_LT_EQ ; +BindingElement: BindingIdentifier InitializerOpt_In; /. -case $rule_number: { - sym(1).ival = QSOperator::InplaceLeftShift; -} break; + case $rule_number: { + AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(1), sym(2).Expression); + node->identifierToken = loc(1); + // if initializer is an anonymous function expression, we need to assign identifierref as it's name + if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression)) + f->name = stringRef(1); + if (auto *c = asAnonymousClassDefinition(sym(2).Expression)) + c->name = stringRef(1); + sym(1).Node = node; + } break; ./ -AssignmentOperator: T_GT_GT_EQ ; +BindingElement: BindingPattern InitializerOpt_In; /. -case $rule_number: { - sym(1).ival = QSOperator::InplaceRightShift; -} break; + case $rule_number: { + AST::PatternElement *node = new (pool) AST::PatternElement(sym(1).Pattern, sym(2).Expression); + sym(1).Node = node; + } break; ./ -AssignmentOperator: T_GT_GT_GT_EQ ; +BindingRestElement: T_ELLIPSIS BindingIdentifier; /. -case $rule_number: { - sym(1).ival = QSOperator::InplaceURightShift; -} break; + case $rule_number: { + AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(2), nullptr, AST::PatternElement::RestElement); + node->identifierToken = loc(2); + sym(1).Node = node; + } break; ./ -AssignmentOperator: T_AND_EQ ; +BindingRestElement: T_ELLIPSIS BindingPattern; /. -case $rule_number: { - sym(1).ival = QSOperator::InplaceAnd; -} break; + case $rule_number: { + AST::PatternElement *node = new (pool) AST::PatternElement(sym(2).Pattern, nullptr, AST::PatternElement::RestElement); + sym(1).Node = node; + } break; ./ -AssignmentOperator: T_XOR_EQ ; +BindingRestElementOpt: ; /. -case $rule_number: { - sym(1).ival = QSOperator::InplaceXor; -} break; + case $rule_number: { + sym(1).Node = nullptr; + } break; ./ -AssignmentOperator: T_OR_EQ ; +BindingRestElementOpt: BindingRestElement; + + +EmptyStatement: T_SEMICOLON; /. -case $rule_number: { - sym(1).ival = QSOperator::InplaceOr; -} break; + case $rule_number: { + AST::EmptyStatement *node = new (pool) AST::EmptyStatement(); + node->semicolonToken = loc(1); + sym(1).Node = node; + } break; ./ -Expression: AssignmentExpression ; +-- Spec says it should have a "[lookahead ∉ { {, function, class, let [ }]" before the Expression statement. +-- This is implemented with the rule below that is run before any statement and inserts a T_EXPRESSION_STATEMENT_OK +-- token if it's ok to parse as an expression statement. +ExpressionStatementLookahead: ; +/: +#define J_SCRIPT_EXPRESSIONSTATEMENTLOOKAHEAD_RULE $rule_number +:/ +/. + case $rule_number: { + int token = lookaheadToken(lexer); + if (token == T_LBRACE) + pushToken(T_FORCE_BLOCK); + else if (token == T_FUNCTION || token == T_CLASS || token == T_LET || token == T_CONST) + pushToken(T_FORCE_DECLARATION); + } break; +./ -Expression: Expression T_COMMA AssignmentExpression ; +ExpressionStatement: Expression_In T_AUTOMATIC_SEMICOLON; +ExpressionStatement: Expression_In T_SEMICOLON; /. -case $rule_number: { - AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); - node->commaToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression); + node->semicolonToken = loc(2); + sym(1).Node = node; + } break; ./ -ExpressionOpt: ; +IfStatement: T_IF T_LPAREN Expression_In T_RPAREN Statement T_ELSE Statement; /. -case $rule_number: { - sym(1).Node = 0; -} break; + case $rule_number: { + AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + node->elseToken = loc(6); + sym(1).Node = node; + } break; ./ -ExpressionOpt: Expression ; +IfStatement: T_IF T_LPAREN Expression_In T_RPAREN Statement; +/. + case $rule_number: { + AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; + } break; +./ -ExpressionNotIn: AssignmentExpressionNotIn ; -ExpressionNotIn: ExpressionNotIn T_COMMA AssignmentExpressionNotIn ; +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_AUTOMATIC_SEMICOLON; +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_COMPATIBILITY_SEMICOLON; -- for JSC/V8 compatibility +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_SEMICOLON; /. -case $rule_number: { - AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); - node->commaToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression); + node->doToken = loc(1); + node->whileToken = loc(3); + node->lparenToken = loc(4); + node->rparenToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; + } break; ./ -ExpressionNotInOpt: ; +IterationStatement: T_WHILE T_LPAREN Expression_In T_RPAREN Statement; /. -case $rule_number: { - sym(1).Node = 0; -} break; + case $rule_number: { + AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement); + node->whileToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; + } break; ./ -ExpressionNotInOpt: ExpressionNotIn ; +IterationStatement: T_FOR T_LPAREN ExpressionOpt T_SEMICOLON ExpressionOpt_In T_SEMICOLON ExpressionOpt_In T_RPAREN Statement; -- [lookahead != { let [ }] +/. + case $rule_number: { + AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, sym(5).Expression, sym(7).Expression, sym(9).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->firstSemicolonToken = loc(4); + node->secondSemicolonToken = loc(6); + node->rparenToken = loc(8); + sym(1).Node = node; + } break; +./ -Statement: Block ; -Statement: VariableStatement ; -Statement: EmptyStatement ; -Statement: ExpressionStatement ; -Statement: IfStatement ; -Statement: IterationStatement ; -Statement: ContinueStatement ; -Statement: BreakStatement ; -Statement: ReturnStatement ; -Statement: WithStatement ; -Statement: LabelledStatement ; -Statement: SwitchStatement ; -Statement: ThrowStatement ; -Statement: TryStatement ; -Statement: DebuggerStatement ; +IterationStatement: T_FOR T_LPAREN VarDeclaration T_SEMICOLON ExpressionOpt_In T_SEMICOLON ExpressionOpt_In T_RPAREN Statement; +/. case $rule_number: Q_FALLTHROUGH(); ./ +IterationStatement: T_FOR T_LPAREN LexicalDeclaration T_SEMICOLON ExpressionOpt_In T_SEMICOLON ExpressionOpt_In T_RPAREN Statement; +/. + case $rule_number: { + // ### get rid of the static_cast! + AST::ForStatement *node = new (pool) AST::ForStatement( + static_cast<AST::VariableStatement *>(sym(3).Node)->declarations, sym(5).Expression, + sym(7).Expression, sym(9).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->firstSemicolonToken = loc(4); + node->secondSemicolonToken = loc(6); + node->rparenToken = loc(8); + sym(1).Node = node; + } break; +./ +InOrOf: T_IN; +/. + case $rule_number: { + sym(1).forEachType = AST::ForEachType::In; + } break; +./ -Block: T_LBRACE StatementListOpt T_RBRACE ; +InOrOf: T_OF; /. -case $rule_number: { - AST::Block *node = new (pool) AST::Block(sym(2).StatementList); - node->lbraceToken = loc(1); - node->rbraceToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).forEachType = AST::ForEachType::Of; + } break; ./ -StatementList: Statement ; +IterationStatement: T_FOR T_LPAREN LeftHandSideExpression InOrOf Expression_In T_RPAREN Statement; /. -case $rule_number: { - sym(1).Node = new (pool) AST::StatementList(sym(1).Statement); -} break; + case $rule_number: { + // need to convert the LHS to an AssignmentPattern if it was an Array/ObjectLiteral + if (AST::Pattern *p = sym(3).Expression->patternCast()) { + AST::SourceLocation errorLoc; + QString errorMsg; + if (!p->convertLiteralToAssignmentPattern(pool, &errorLoc, &errorMsg)) { + syntaxError(errorLoc, errorMsg); + return false; + } + } + AST::ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, sym(5).Expression, sym(7).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->inOfToken = loc(4); + node->rparenToken = loc(6); + node->type = sym(4).forEachType; + sym(1).Node = node; + } break; ./ -StatementList: StatementList Statement ; +IterationStatement: T_FOR T_LPAREN ForDeclaration InOrOf Expression_In T_RPAREN Statement; /. -case $rule_number: { - sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement); -} break; + case $rule_number: { + AST::ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).PatternElement, sym(5).Expression, sym(7).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->inOfToken = loc(4); + node->rparenToken = loc(6); + node->type = sym(4).forEachType; + sym(1).Node = node; + } break; ./ -StatementListOpt: ; +ForDeclaration: LetOrConst BindingIdentifier; +/. case $rule_number: Q_FALLTHROUGH(); ./ +ForDeclaration: Var BindingIdentifier; /. -case $rule_number: { - sym(1).Node = 0; -} break; + case $rule_number: { + auto *node = new (pool) AST::PatternElement(stringRef(2), nullptr); + node->identifierToken = loc(2); + node->scope = sym(1).scope; + sym(1).Node = node; + } break; ./ -StatementListOpt: StatementList ; +ForDeclaration: LetOrConst BindingPattern; +/. case $rule_number: Q_FALLTHROUGH(); ./ +ForDeclaration: Var BindingPattern; /. -case $rule_number: { - sym(1).Node = sym(1).StatementList->finish (); -} break; + case $rule_number: { + auto *node = new (pool) AST::PatternElement(sym(2).Pattern, nullptr); + node->scope = sym(1).scope; + sym(1).Node = node; + } break; ./ -VariableStatement: VariableDeclarationKind VariableDeclarationList T_AUTOMATIC_SEMICOLON ; -- automatic semicolon -VariableStatement: VariableDeclarationKind VariableDeclarationList T_SEMICOLON ; +ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON; +ContinueStatement: T_CONTINUE T_SEMICOLON; /. -case $rule_number: { - AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; - if (sym(1).ival == T_LET) - s = AST::VariableDeclaration::BlockScope; - else if (sym(1).ival == T_CONST) - s = AST::VariableDeclaration::ReadOnlyBlockScope; - - AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(s)); - node->declarationKindToken = loc(1); - node->semicolonToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ContinueStatement *node = new (pool) AST::ContinueStatement(); + node->continueToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; + } break; ./ -VariableDeclarationKind: T_LET ; +ContinueStatement: T_CONTINUE IdentifierReference T_AUTOMATIC_SEMICOLON; +ContinueStatement: T_CONTINUE IdentifierReference T_SEMICOLON; /. -case $rule_number: { - sym(1).ival = T_LET; -} break; + case $rule_number: { + AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2)); + node->continueToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; + } break; ./ -VariableDeclarationKind: T_CONST ; +BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON; +BreakStatement: T_BREAK T_SEMICOLON; /. -case $rule_number: { - sym(1).ival = T_CONST; -} break; + case $rule_number: { + AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef()); + node->breakToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; + } break; ./ -VariableDeclarationKind: T_VAR ; +BreakStatement: T_BREAK IdentifierReference T_AUTOMATIC_SEMICOLON; +BreakStatement: T_BREAK IdentifierReference T_SEMICOLON; /. -case $rule_number: { - sym(1).ival = T_VAR; -} break; + case $rule_number: { + AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2)); + node->breakToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; + } break; ./ -VariableDeclarationList: VariableDeclaration ; +ReturnStatement: T_RETURN ExpressionOpt_In T_AUTOMATIC_SEMICOLON; +ReturnStatement: T_RETURN ExpressionOpt_In T_SEMICOLON; /. -case $rule_number: { - sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); -} break; + case $rule_number: { + if (!functionNestingLevel) { + syntaxError(loc(1), "Return statement not allowed outside of Function declaration."); + return false; + } + AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression); + node->returnToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; + } break; ./ -VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration ; +WithStatement: T_WITH T_LPAREN Expression_In T_RPAREN Statement; /. -case $rule_number: { - AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList( - sym(1).VariableDeclarationList, sym(3).VariableDeclaration); - node->commaToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement); + node->withToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; + } break; ./ -VariableDeclarationListNotIn: VariableDeclarationNotIn ; +SwitchStatement: T_SWITCH T_LPAREN Expression_In T_RPAREN CaseBlock; /. -case $rule_number: { - sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); -} break; + case $rule_number: { + AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock); + node->switchToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; + } break; ./ -VariableDeclarationListNotIn: VariableDeclarationListNotIn T_COMMA VariableDeclarationNotIn ; +CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE; /. -case $rule_number: { - sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration); -} break; + case $rule_number: { + AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; + } break; ./ -VariableDeclaration: JsIdentifier InitialiserOpt ; +CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE; /. -case $rule_number: { - AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; - AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(5); + sym(1).Node = node; + } break; ./ -VariableDeclarationNotIn: JsIdentifier InitialiserNotInOpt ; +CaseClauses: CaseClause; /. -case $rule_number: { - AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; - AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause); + } break; ./ -Initialiser: T_EQ AssignmentExpression ; +CaseClauses: CaseClauses CaseClause; /. -case $rule_number: { - // ### TODO: AST for initializer - sym(1) = sym(2); -} break; + case $rule_number: { + sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause); + } break; ./ -InitialiserOpt: ; +CaseClausesOpt: ; /. -case $rule_number: { - sym(1).Node = 0; -} break; + case $rule_number: { + sym(1).Node = nullptr; + } break; ./ -InitialiserOpt: Initialiser ; +CaseClausesOpt: CaseClauses; +/. + case $rule_number: { + sym(1).Node = sym(1).CaseClauses->finish(); + } break; +./ -InitialiserNotIn: T_EQ AssignmentExpressionNotIn ; +CaseClause: T_CASE Expression_In T_COLON StatementListOpt; /. -case $rule_number: { - // ### TODO: AST for initializer - sym(1) = sym(2); -} break; + case $rule_number: { + AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList); + node->caseToken = loc(1); + node->colonToken = loc(3); + sym(1).Node = node; + } break; ./ -InitialiserNotInOpt: ; +DefaultClause: T_DEFAULT T_COLON StatementListOpt; /. -case $rule_number: { - sym(1).Node = 0; -} break; + case $rule_number: { + AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList); + node->defaultToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; + } break; ./ -InitialiserNotInOpt: InitialiserNotIn ; +LabelledStatement: IdentifierReference T_COLON LabelledItem; +/. + case $rule_number: { + AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; + } break; +./ + +LabelledItem: Statement; -EmptyStatement: T_SEMICOLON ; +LabelledItem: ExpressionStatementLookahead T_FORCE_DECLARATION FunctionDeclaration; /. -case $rule_number: { - AST::EmptyStatement *node = new (pool) AST::EmptyStatement(); - node->semicolonToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + syntaxError(loc(3), "FunctionDeclarations are not allowed after a label."); + return false; + } break; ./ -ExpressionStatement: Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon -ExpressionStatement: Expression T_SEMICOLON ; +ThrowStatement: T_THROW Expression_In T_AUTOMATIC_SEMICOLON; +ThrowStatement: T_THROW Expression_In T_SEMICOLON; /. -case $rule_number: { - AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression); - node->semicolonToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression); + node->throwToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; + } break; ./ -IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement T_ELSE Statement ; +TryStatement: T_TRY Block Catch; /. -case $rule_number: { - AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement); - node->ifToken = loc(1); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - node->elseToken = loc(6); - sym(1).Node = node; -} break; + case $rule_number: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch); + node->tryToken = loc(1); + sym(1).Node = node; + } break; ./ -IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement ; +TryStatement: T_TRY Block Finally; /. -case $rule_number: { - AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement); - node->ifToken = loc(1); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally); + node->tryToken = loc(1); + sym(1).Node = node; + } break; ./ +TryStatement: T_TRY Block Catch Finally; +/. + case $rule_number: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally); + node->tryToken = loc(1); + sym(1).Node = node; + } break; +./ -IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_AUTOMATIC_SEMICOLON ; -- automatic semicolon -IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_COMPATIBILITY_SEMICOLON ; -- for JSC/V8 compatibility -IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_SEMICOLON ; +Catch: T_CATCH T_LPAREN CatchParameter T_RPAREN Block; /. -case $rule_number: { - AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression); - node->doToken = loc(1); - node->whileToken = loc(3); - node->lparenToken = loc(4); - node->rparenToken = loc(6); - node->semicolonToken = loc(7); - sym(1).Node = node; -} break; + case $rule_number: { + AST::Catch *node = new (pool) AST::Catch(sym(3).PatternElement, sym(5).Block); + node->catchToken = loc(1); + node->lparenToken = loc(2); + node->identifierToken = loc(3); + node->rparenToken = loc(4); + sym(1).Node = node; + } break; ./ -IterationStatement: T_WHILE T_LPAREN Expression T_RPAREN Statement ; +Finally: T_FINALLY Block; /. -case $rule_number: { - AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement); - node->whileToken = loc(1); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + AST::Finally *node = new (pool) AST::Finally(sym(2).Block); + node->finallyToken = loc(1); + sym(1).Node = node; + } break; ./ -IterationStatement: T_FOR T_LPAREN ExpressionNotInOpt T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; +CatchParameter: BindingIdentifier; /. -case $rule_number: { - AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, - sym(5).Expression, sym(7).Expression, sym(9).Statement); - node->forToken = loc(1); - node->lparenToken = loc(2); - node->firstSemicolonToken = loc(4); - node->secondSemicolonToken = loc(6); - node->rparenToken = loc(8); - sym(1).Node = node; -} break; + case $rule_number: { + AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(1)); + node->identifierToken = loc(1); + node->scope = AST::VariableScope::Let; + sym(1).Node = node; + } break; ./ -IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationListNotIn T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; +CatchParameter: BindingPattern; /. -case $rule_number: { - AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; - AST::LocalForStatement *node = new (pool) AST::LocalForStatement( - sym(4).VariableDeclarationList->finish(s), sym(6).Expression, - sym(8).Expression, sym(10).Statement); - node->forToken = loc(1); - node->lparenToken = loc(2); - node->varToken = loc(3); - node->firstSemicolonToken = loc(5); - node->secondSemicolonToken = loc(7); - node->rparenToken = loc(9); - sym(1).Node = node; -} break; + case $rule_number: { + AST::PatternElement *node = new (pool) AST::PatternElement(sym(1).Pattern); + node->scope = AST::VariableScope::Let; + sym(1).Node = node; + } break; ./ -IterationStatement: T_FOR T_LPAREN LeftHandSideExpression T_IN Expression T_RPAREN Statement ; +DebuggerStatement: T_DEBUGGER T_AUTOMATIC_SEMICOLON; -- automatic semicolon +DebuggerStatement: T_DEBUGGER T_SEMICOLON; /. -case $rule_number: { - AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, - sym(5).Expression, sym(7).Statement); - node->forToken = loc(1); - node->lparenToken = loc(2); - node->inToken = loc(4); - node->rparenToken = loc(6); - sym(1).Node = node; -} break; + case $rule_number: { + AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement(); + node->debuggerToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; + } break; ./ -IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationNotIn T_IN Expression T_RPAREN Statement ; +-- tell the parser to prefer function declarations to function expressions. +-- That is, the `Function' symbol is used to mark the start of a function +-- declaration. +-- This is still required for parsing QML, where MemberExpression and FunctionDeclaration would +-- otherwise conflict. +Function: T_FUNCTION %prec REDUCE_HERE; + +FunctionDeclaration: Function BindingIdentifier T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; /. -case $rule_number: { - AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement( - sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); - node->forToken = loc(1); - node->lparenToken = loc(2); - node->varToken = loc(3); - node->inToken = loc(5); - node->rparenToken = loc(7); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList); + node->functionToken = loc(1); + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; + } break; ./ -ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON ; -- automatic semicolon -ContinueStatement: T_CONTINUE T_SEMICOLON ; + +FunctionDeclaration_Default: FunctionDeclaration; +FunctionDeclaration_Default: Function T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; /. -case $rule_number: { - AST::ContinueStatement *node = new (pool) AST::ContinueStatement(); - node->continueToken = loc(1); - node->semicolonToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(1), sym(3).FormalParameterList, sym(6).StatementList); + node->functionToken = loc(1); + node->identifierToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + node->lbraceToken = loc(5); + node->rbraceToken = loc(7); + sym(1).Node = node; + } break; ./ -ContinueStatement: T_CONTINUE JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon -ContinueStatement: T_CONTINUE JsIdentifier T_SEMICOLON ; +FunctionExpression: T_FUNCTION BindingIdentifier T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; /. -case $rule_number: { - AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2)); - node->continueToken = loc(1); - node->identifierToken = loc(2); - node->semicolonToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList); + node->functionToken = loc(1); + if (! stringRef(2).isNull()) + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; + } break; ./ -BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON ; -- automatic semicolon -BreakStatement: T_BREAK T_SEMICOLON ; +FunctionExpression: T_FUNCTION T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; /. -case $rule_number: { - AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef()); - node->breakToken = loc(1); - node->semicolonToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).StatementList); + node->functionToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + node->lbraceToken = loc(5); + node->rbraceToken = loc(7); + sym(1).Node = node; + } break; ./ -BreakStatement: T_BREAK JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon -BreakStatement: T_BREAK JsIdentifier T_SEMICOLON ; +StrictFormalParameters: FormalParameters; + +FormalParameters: ; /. -case $rule_number: { - AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2)); - node->breakToken = loc(1); - node->identifierToken = loc(2); - node->semicolonToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = nullptr; + } break; ./ -ReturnStatement: T_RETURN ExpressionOpt T_AUTOMATIC_SEMICOLON ; -- automatic semicolon -ReturnStatement: T_RETURN ExpressionOpt T_SEMICOLON ; +FormalParameters: BindingRestElement; /. -case $rule_number: { - AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression); - node->returnToken = loc(1); - node->semicolonToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FormalParameterList *node = (new (pool) AST::FormalParameterList(nullptr, sym(1).PatternElement))->finish(pool); + sym(1).Node = node; + } break; ./ -WithStatement: T_WITH T_LPAREN Expression T_RPAREN Statement ; +FormalParameters: FormalParameterList; +/. case $rule_number: ./ +FormalParameters: FormalParameterList T_COMMA; /. -case $rule_number: { - AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement); - node->withToken = loc(1); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + sym(1).Node = sym(1).FormalParameterList->finish(pool); + } break; ./ -SwitchStatement: T_SWITCH T_LPAREN Expression T_RPAREN CaseBlock ; +FormalParameters: FormalParameterList T_COMMA BindingRestElement; /. -case $rule_number: { - AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock); - node->switchToken = loc(1); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FormalParameterList *node = (new (pool) AST::FormalParameterList(sym(1).FormalParameterList, sym(3).PatternElement))->finish(pool); + sym(1).Node = node; + } break; ./ -CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE ; +FormalParameterList: BindingElement; /. -case $rule_number: { - AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses); - node->lbraceToken = loc(1); - node->rbraceToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FormalParameterList *node = new (pool) AST::FormalParameterList(nullptr, sym(1).PatternElement); + sym(1).Node = node; + } break; ./ -CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE ; + +FormalParameterList: FormalParameterList T_COMMA BindingElement; /. -case $rule_number: { - AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); - node->lbraceToken = loc(1); - node->rbraceToken = loc(5); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, sym(3).PatternElement); + sym(1).Node = node; + } break; ./ -CaseClauses: CaseClause ; +FormalParameter: BindingElement; + +FunctionLBrace: T_LBRACE; /. -case $rule_number: { - sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause); -} break; + case $rule_number: { + ++functionNestingLevel; + } break; ./ -CaseClauses: CaseClauses CaseClause ; +FunctionRBrace: T_RBRACE; /. -case $rule_number: { - sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause); -} break; + case $rule_number: { + --functionNestingLevel; + } break; ./ -CaseClausesOpt: ; + +FunctionBody: StatementListOpt; + +ArrowFunction: ArrowParameters T_ARROW ConciseBodyLookahead AssignmentExpression; -- [lookahead ≠{] +/. case $rule_number: Q_FALLTHROUGH(); ./ +ArrowFunction_In: ArrowParameters T_ARROW ConciseBodyLookahead AssignmentExpression_In; -- [lookahead ≠{] /. -case $rule_number: { - sym(1).Node = 0; -} break; + case $rule_number: { + AST::ReturnStatement *ret = new (pool) AST::ReturnStatement(sym(4).Expression); + ret->returnToken = sym(4).Node->firstSourceLocation(); + ret->semicolonToken = sym(4).Node->lastSourceLocation(); + AST::StatementList *statements = (new (pool) AST::StatementList(ret))->finish(); + AST::FunctionExpression *f = new (pool) AST::FunctionExpression(QStringRef(), sym(1).FormalParameterList, statements); + f->isArrowFunction = true; + f->functionToken = sym(1).Node ? sym(1).Node->firstSourceLocation() : loc(1); + f->lbraceToken = sym(4).Node->firstSourceLocation(); + f->rbraceToken = sym(4).Node->lastSourceLocation(); + sym(1).Node = f; + } break; ./ -CaseClausesOpt: CaseClauses ; +ArrowFunction: ArrowParameters T_ARROW ConciseBodyLookahead T_FORCE_BLOCK FunctionLBrace FunctionBody FunctionRBrace; +/. case $rule_number: Q_FALLTHROUGH(); ./ +ArrowFunction_In: ArrowParameters T_ARROW ConciseBodyLookahead T_FORCE_BLOCK FunctionLBrace FunctionBody FunctionRBrace; /. -case $rule_number: { - sym(1).Node = sym(1).CaseClauses->finish (); -} break; + case $rule_number: { + AST::FunctionExpression *f = new (pool) AST::FunctionExpression(QStringRef(), sym(1).FormalParameterList, sym(6).StatementList); + f->isArrowFunction = true; + f->functionToken = sym(1).Node ? sym(1).Node->firstSourceLocation() : loc(1); + f->lbraceToken = loc(6); + f->rbraceToken = loc(7); + sym(1).Node = f; + } break; ./ -CaseClause: T_CASE Expression T_COLON StatementListOpt ; +ArrowParameters: BindingIdentifier; /. -case $rule_number: { - AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList); - node->caseToken = loc(1); - node->colonToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + AST::PatternElement *e = new (pool) AST::PatternElement(stringRef(1), nullptr, AST::PatternElement::Binding); + e->identifierToken = loc(1); + sym(1).FormalParameterList = (new (pool) AST::FormalParameterList(nullptr, e))->finish(pool); + } break; ./ -DefaultClause: T_DEFAULT T_COLON StatementListOpt ; +-- CoverParenthesizedExpressionAndArrowParameterList for ArrowParameters i being refined to: +-- ArrowFormalParameters: T_LPAREN StrictFormalParameters T_RPAREN +ArrowParameters: CoverParenthesizedExpressionAndArrowParameterList; /. -case $rule_number: { - AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList); - node->defaultToken = loc(1); - node->colonToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + if (coverExpressionType != CE_FormalParameterList) { + AST::NestedExpression *ne = static_cast<AST::NestedExpression *>(sym(1).Node); + AST::FormalParameterList *list = ne->expression->reparseAsFormalParameterList(pool); + if (!list) { + syntaxError(loc(1), "Invalid Arrow parameter list."); + return false; + } + sym(1).Node = list->finish(pool); + } + } break; ./ -LabelledStatement: JsIdentifier T_COLON Statement ; +ConciseBodyLookahead: ; +/: +#define J_SCRIPT_CONCISEBODYLOOKAHEAD_RULE $rule_number +:/ /. -case $rule_number: { - AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); - node->identifierToken = loc(1); - node->colonToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + if (lookaheadToken(lexer) == T_LBRACE) + pushToken(T_FORCE_BLOCK); + } break; ./ -ThrowStatement: T_THROW Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon -ThrowStatement: T_THROW Expression T_SEMICOLON ; +MethodDefinition: PropertyName T_LPAREN StrictFormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; /. -case $rule_number: { - AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression); - node->throwToken = loc(1); - node->semicolonToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(1), sym(3).FormalParameterList, sym(6).StatementList); + f->functionToken = sym(1).PropertyName->firstSourceLocation(); + f->lparenToken = loc(2); + f->rparenToken = loc(4); + f->lbraceToken = loc(5); + f->rbraceToken = loc(7); + AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, f); + node->colonToken = loc(2); + sym(1).Node = node; + } break; ./ -TryStatement: T_TRY Block Catch ; +MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace; /. -case $rule_number: { - AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch); - node->tryToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList); + f->functionToken = sym(2).PropertyName->firstSourceLocation(); + f->lparenToken = loc(3); + f->rparenToken = loc(5); + f->lbraceToken = loc(6); + f->rbraceToken = loc(8); + f->isGenerator = true; + AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f); + node->colonToken = loc(2); + sym(1).Node = node; + } break; +./ + + +MethodDefinition: T_GET PropertyName T_LPAREN T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; +/. + case $rule_number: { + AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), nullptr, sym(6).StatementList); + f->functionToken = sym(2).PropertyName->firstSourceLocation(); + f->lparenToken = loc(3); + f->rparenToken = loc(4); + f->lbraceToken = loc(5); + f->rbraceToken = loc(7); + AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Getter); + node->colonToken = loc(2); + sym(1).Node = node; + } break; +./ + +MethodDefinition: T_SET PropertyName T_LPAREN PropertySetParameterList T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; +/. + case $rule_number: { + AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList); + f->functionToken = sym(2).PropertyName->firstSourceLocation(); + f->lparenToken = loc(3); + f->rparenToken = loc(5); + f->lbraceToken = loc(6); + f->rbraceToken = loc(8); + AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Setter); + node->colonToken = loc(2); + sym(1).Node = node; + } break; ./ -TryStatement: T_TRY Block Finally ; + +PropertySetParameterList: FormalParameter; /. -case $rule_number: { - AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally); - node->tryToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FormalParameterList *node = (new (pool) AST::FormalParameterList(nullptr, sym(1).PatternElement))->finish(pool); + sym(1).Node = node; + } break; +./ + +GeneratorLParen: T_LPAREN; +/. + case $rule_number: { + lexer->enterGeneratorBody(); + } break; ./ -TryStatement: T_TRY Block Catch Finally ; +GeneratorRBrace: T_RBRACE; /. -case $rule_number: { - AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally); - node->tryToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + --functionNestingLevel; + lexer->leaveGeneratorBody(); + } break; ./ -Catch: T_CATCH T_LPAREN JsIdentifier T_RPAREN Block ; +GeneratorDeclaration: Function T_STAR BindingIdentifier GeneratorLParen FormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace; /. -case $rule_number: { - AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block); - node->catchToken = loc(1); - node->lparenToken = loc(2); - node->identifierToken = loc(3); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(3), sym(5).FormalParameterList, sym(8).StatementList); + node->functionToken = loc(1); + node->identifierToken = loc(3); + node->lparenToken = loc(4); + node->rparenToken = loc(6); + node->lbraceToken = loc(7); + node->rbraceToken = loc(9); + node->isGenerator = true; + sym(1).Node = node; + } break; +./ + +GeneratorDeclaration_Default: GeneratorDeclaration; +GeneratorDeclaration_Default: Function T_STAR GeneratorLParen FormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace; +/. + case $rule_number: { + AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(1), sym(4).FormalParameterList, sym(7).StatementList); + node->functionToken = loc(1); + node->identifierToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + node->isGenerator = true; + sym(1).Node = node; + } break; ./ -Finally: T_FINALLY Block ; +GeneratorExpression: T_FUNCTION T_STAR BindingIdentifier GeneratorLParen FormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace; /. -case $rule_number: { - AST::Finally *node = new (pool) AST::Finally(sym(2).Block); - node->finallyToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(3), sym(5).FormalParameterList, sym(8).StatementList); + node->functionToken = loc(1); + if (!stringRef(3).isNull()) + node->identifierToken = loc(3); + node->lparenToken = loc(4); + node->rparenToken = loc(6); + node->lbraceToken = loc(7); + node->rbraceToken = loc(9); + node->isGenerator = true; + sym(1).Node = node; + } break; ./ -DebuggerStatement: T_DEBUGGER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon -DebuggerStatement: T_DEBUGGER T_SEMICOLON ; +GeneratorExpression: T_FUNCTION T_STAR GeneratorLParen FormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace; /. -case $rule_number: { - AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement(); - node->debuggerToken = loc(1); - node->semicolonToken = loc(2); - sym(1).Node = node; -} break; + case $rule_number: { + AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(4).FormalParameterList, sym(7).StatementList); + node->functionToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + node->isGenerator = true; + sym(1).Node = node; + } break; ./ --- tell the parser to prefer function declarations to function expressions. --- That is, the `Function' symbol is used to mark the start of a function --- declaration. -Function: T_FUNCTION %prec REDUCE_HERE ; +GeneratorBody: FunctionBody; -FunctionDeclaration: Function JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +YieldExpression: T_YIELD; +/. case $rule_number: Q_FALLTHROUGH(); ./ +YieldExpression_In: T_YIELD; /. -case $rule_number: { - AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); - node->functionToken = loc(1); - node->identifierToken = loc(2); - node->lparenToken = loc(3); - node->rparenToken = loc(5); - node->lbraceToken = loc(6); - node->rbraceToken = loc(8); - sym(1).Node = node; -} break; + case $rule_number: { + AST::YieldExpression *node = new (pool) AST::YieldExpression(); + node->yieldToken = loc(1); + sym(1).Node = node; + } break; ./ -FunctionExpression: T_FUNCTION JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +YieldExpression: T_YIELD T_STAR AssignmentExpression; +/. case $rule_number: Q_FALLTHROUGH(); ./ +YieldExpression_In: T_YIELD T_STAR AssignmentExpression_In; /. -case $rule_number: { - AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); - node->functionToken = loc(1); - if (! stringRef(2).isNull()) - node->identifierToken = loc(2); - node->lparenToken = loc(3); - node->rparenToken = loc(5); - node->lbraceToken = loc(6); - node->rbraceToken = loc(8); - sym(1).Node = node; -} break; + case $rule_number: { + AST::YieldExpression *node = new (pool) AST::YieldExpression(sym(3).Expression); + node->yieldToken = loc(1); + node->isYieldStar = true; + sym(1).Node = node; + } break; ./ -FunctionExpression: T_FUNCTION T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +YieldExpression: T_YIELD AssignmentExpression; +/. case $rule_number: Q_FALLTHROUGH(); ./ +YieldExpression_In: T_YIELD AssignmentExpression_In; /. -case $rule_number: { - AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).FunctionBody); - node->functionToken = loc(1); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - node->lbraceToken = loc(5); - node->rbraceToken = loc(7); - sym(1).Node = node; -} break; + case $rule_number: { + AST::YieldExpression *node = new (pool) AST::YieldExpression(sym(2).Expression); + node->yieldToken = loc(1); + sym(1).Node = node; + } break; ./ -FormalParameterList: JsIdentifier ; + +ClassDeclaration: T_CLASS BindingIdentifier ClassHeritageOpt ClassLBrace ClassBodyOpt ClassRBrace; /. -case $rule_number: { - AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1)); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ClassDeclaration *node = new (pool) AST::ClassDeclaration(stringRef(2), sym(3).Expression, sym(5).ClassElementList); + node->classToken = loc(1); + node->identifierToken = loc(2); + node->lbraceToken = loc(4); + node->rbraceToken = loc(6); + sym(1).Node = node; + } break; ./ -FormalParameterList: FormalParameterList T_COMMA JsIdentifier ; +ClassExpression: T_CLASS BindingIdentifier ClassHeritageOpt ClassLBrace ClassBodyOpt ClassRBrace; /. -case $rule_number: { - AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3)); - node->commaToken = loc(2); - node->identifierToken = loc(3); - sym(1).Node = node; -} break; + case $rule_number: { + AST::ClassExpression *node = new (pool) AST::ClassExpression(stringRef(2), sym(3).Expression, sym(5).ClassElementList); + node->classToken = loc(1); + node->identifierToken = loc(2); + node->lbraceToken = loc(4); + node->rbraceToken = loc(6); + sym(1).Node = node; + } break; ./ -FormalParameterListOpt: ; +ClassDeclaration_Default: T_CLASS ClassHeritageOpt ClassLBrace ClassBodyOpt ClassRBrace; /. -case $rule_number: { - sym(1).Node = 0; -} break; + case $rule_number: { + AST::ClassDeclaration *node = new (pool) AST::ClassDeclaration(QStringRef(), sym(2).Expression, sym(4).ClassElementList); + node->classToken = loc(1); + node->lbraceToken = loc(3); + node->rbraceToken = loc(5); + sym(1).Node = node; + } break; ./ -FormalParameterListOpt: FormalParameterList ; +ClassExpression: T_CLASS ClassHeritageOpt ClassLBrace ClassBodyOpt ClassRBrace; /. -case $rule_number: { - sym(1).Node = sym(1).FormalParameterList->finish (); -} break; + case $rule_number: { + AST::ClassExpression *node = new (pool) AST::ClassExpression(QStringRef(), sym(2).Expression, sym(4).ClassElementList); + node->classToken = loc(1); + node->lbraceToken = loc(3); + node->rbraceToken = loc(5); + sym(1).Node = node; + } break; ./ -FunctionBodyOpt: ; +ClassDeclaration_Default: ClassDeclaration; + +ClassLBrace: T_LBRACE; /. -case $rule_number: { - sym(1).Node = 0; -} break; + case $rule_number: { + lexer->setStaticIsKeyword(true); + } break; ./ -FunctionBodyOpt: FunctionBody ; +ClassRBrace: T_RBRACE; +/. case $rule_number: ./ +ClassStaticQualifier: T_STATIC; +/. + case $rule_number: { + lexer->setStaticIsKeyword(false); + } break; +./ -FunctionBody: SourceElements ; +ClassHeritageOpt: ; /. -case $rule_number: { - sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ()); -} break; + case $rule_number: { + sym(1).Node = nullptr; + } break; ./ -Program: Empty ; +ClassHeritageOpt: T_EXTENDS LeftHandSideExpression; +/. + case $rule_number: { + sym(1).Node = sym(2).Node; + } break; +./ -Program: SourceElements ; +ClassBodyOpt: ; /. -case $rule_number: { - sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ()); -} break; + case $rule_number: { + sym(1).Node = nullptr; + } break; ./ -SourceElements: SourceElement ; +ClassBodyOpt: ClassElementList; /. -case $rule_number: { - sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement); -} break; + case $rule_number: { + if (sym(1).Node) + sym(1).Node = sym(1).ClassElementList->finish(); + } break; ./ -SourceElements: SourceElements SourceElement ; +ClassElementList: ClassElement; + +ClassElementList: ClassElementList ClassElement; /. -case $rule_number: { - sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement); -} break; + case $rule_number: { + if (sym(2).Node) + sym(1).ClassElementList = sym(1).ClassElementList->append(sym(2).ClassElementList); + } break; ./ -SourceElement: Statement ; +ClassElement: MethodDefinition; /. -case $rule_number: { - sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement); -} break; + case $rule_number: { + AST::ClassElementList *node = new (pool) AST::ClassElementList(sym(1).PatternProperty, false); + sym(1).Node = node; + } break; ./ -SourceElement: FunctionDeclaration ; +ClassElement: ClassStaticQualifier MethodDefinition; /. -case $rule_number: { - sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration); -} break; + case $rule_number: { + lexer->setStaticIsKeyword(true); + AST::ClassElementList *node = new (pool) AST::ClassElementList(sym(2).PatternProperty, true); + sym(1).Node = node; + } break; ./ -PropertyAssignmentListOpt: ; +ClassElement: T_SEMICOLON; /. -case $rule_number: { - sym(1).Node = 0; -} break; + case $rule_number: { + sym(1).Node = nullptr; + } break; +./ + +-- Scripts and Modules + +Script: ; +/. + case $rule_number: { + sym(1).Node = nullptr; + } break; ./ -PropertyAssignmentListOpt: PropertyAssignmentList ; +Script: ScriptBody; +ScriptBody: StatementList; /. + case $rule_number: { + sym(1).Node = new (pool) AST::Program(sym(1).StatementList->finish()); + } break; +./ + +Module: ModuleBodyOpt; +/. case $rule_number: { UNIMPLEMENTED; } ./ + +ModuleBody: ModuleItemList; + +ModuleBodyOpt: ; +ModuleBodyOpt: ModuleBody; + +ModuleItemList: ModuleItem; +ModuleItemList: ModuleItemList ModuleItem; + +ModuleItem: ImportDeclaration T_AUTOMATIC_SEMICOLON; +ModuleItem: ImportDeclaration T_SEMICOLON; +ModuleItem: ExportDeclaration T_AUTOMATIC_SEMICOLON; +ModuleItem: ExportDeclaration T_SEMICOLON; +ModuleItem: StatementListItem; + +ImportDeclaration: T_IMPORT ImportClause FromClause; +ImportDeclaration: T_IMPORT ModuleSpecifier; + +ImportClause: ImportedDefaultBinding; +ImportClause: NameSpaceImport; +ImportClause: NamedImports; +ImportClause: ImportedDefaultBinding T_COMMA NameSpaceImport; +ImportClause: ImportedDefaultBinding T_COMMA NamedImports; + +ImportedDefaultBinding: ImportedBinding; + +NameSpaceImport: T_STAR T_AS ImportedBinding; + +NamedImports: T_LBRACE T_RBRACE; +NamedImports: T_LBRACE ImportsList T_RBRACE; +NamedImports: T_LBRACE ImportsList T_COMMA T_RBRACE; + +FromClause: T_FROM ModuleSpecifier; + +ImportsList: ImportSpecifier; +ImportsList: ImportsList T_COMMA ImportSpecifier; + +ImportSpecifier: ImportedBinding; +ImportSpecifier: IdentifierName T_AS ImportedBinding; + +ModuleSpecifier: T_STRING_LITERAL; + +ImportedBinding: BindingIdentifier; + +ExportDeclarationLookahead: ; +/: +#define J_SCRIPT_EXPORTDECLARATIONLOOKAHEAD_RULE $rule_number +:/ +/. + case $rule_number: { + int token = lookaheadToken(lexer); + if (token == T_FUNCTION || token == T_CLASS) + pushToken(T_FORCE_DECLARATION); + } break; +./ + +ExportDeclaration: T_EXPORT T_STAR FromClause; +ExportDeclaration: T_EXPORT ExportClause FromClause; +ExportDeclaration: T_EXPORT ExportClause; +ExportDeclaration: T_EXPORT VariableStatement; +ExportDeclaration: T_EXPORT Declaration; +ExportDeclaration: T_EXPORT T_DEFAULT ExportDeclarationLookahead T_FORCE_DECLARATION HoistableDeclaration_Default; +ExportDeclaration: T_EXPORT T_DEFAULT ExportDeclarationLookahead T_FORCE_DECLARATION ClassDeclaration_Default; +ExportDeclaration: T_EXPORT T_DEFAULT ExportDeclarationLookahead AssignmentExpression_In; -- [lookahead ∉ { function, class }] + +ExportClause: T_LBRACE T_RBRACE; +ExportClause: T_LBRACE ExportsList T_RBRACE; +ExportClause: T_LBRACE ExportsList T_COMMA T_RBRACE; + +ExportsList: ExportSpecifier; +ExportsList: ExportsList T_COMMA ExportSpecifier; + +ExportSpecifier: IdentifierName; +ExportSpecifier: IdentifierName T_AS IdentifierName; + +-- Old top level code + +/. + // ------------ end of switch statement } // switch action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT); } // if } while (action != 0); +#ifdef PARSER_DEBUG + qDebug() << "Done or error."; +#endif + if (first_token == last_token) { const int errorState = state_stack[tos]; // automatic insertion of `;' if (yytoken != -1 && ((t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken)) || t_action(errorState, T_COMPATIBILITY_SEMICOLON))) { +#ifdef PARSER_DEBUG + qDebug() << "Inserting automatic semicolon."; +#endif SavedToken &tk = token_buffer[0]; tk.token = yytoken; tk.dval = yylval; @@ -3260,8 +4211,7 @@ PropertyAssignmentListOpt: PropertyAssignmentList ; for (int tk = 1; tk < TERMINAL_COUNT; ++tk) { if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM || - tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION || - tk == T_FEED_JS_SOURCE_ELEMENT) + tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION) continue; int a = t_action(errorState, tk); diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp index 0d1b512c92..d254279cbf 100644 --- a/src/qml/parser/qqmljsast.cpp +++ b/src/qml/parser/qqmljsast.cpp @@ -45,6 +45,27 @@ QT_QML_BEGIN_NAMESPACE namespace QQmlJS { namespace AST { +FunctionExpression *asAnonymousFunctionDefinition(Node *n) +{ + if (!n) + return nullptr; + FunctionExpression *f = n->asFunctionDefinition(); + if (!f || !f->name.isNull()) + return nullptr; + return f; +} + +ClassExpression *asAnonymousClassDefinition(Node *n) +{ + if (!n) + return nullptr; + ClassExpression *c = n->asClassDefinition(); + if (!c || !c->name.isNull()) + return nullptr; + return c; +} + + void Node::accept(Visitor *visitor) { if (visitor->preVisit(this)) { @@ -79,11 +100,67 @@ UiObjectMember *Node::uiObjectMemberCast() return nullptr; } +LeftHandSideExpression *Node::leftHandSideExpressionCast() +{ + return nullptr; +} + +Pattern *Node::patternCast() +{ + return nullptr; +} + +FunctionExpression *Node::asFunctionDefinition() +{ + return nullptr; +} + +ClassExpression *Node::asClassDefinition() +{ + return nullptr; +} + ExpressionNode *ExpressionNode::expressionCast() { return this; } +FormalParameterList *ExpressionNode::reparseAsFormalParameterList(MemoryPool *pool) +{ + AST::ExpressionNode *expr = this; + AST::FormalParameterList *f = nullptr; + if (AST::Expression *commaExpr = AST::cast<AST::Expression *>(expr)) { + f = commaExpr->left->reparseAsFormalParameterList(pool); + if (!f) + return nullptr; + + expr = commaExpr->right; + } + + AST::ExpressionNode *rhs = nullptr; + if (AST::BinaryExpression *assign = AST::cast<AST::BinaryExpression *>(expr)) { + if (assign->op != QSOperator::Assign) + return nullptr; + expr = assign->left; + rhs = assign->right; + } + AST::PatternElement *binding = nullptr; + if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(expr)) { + binding = new (pool) AST::PatternElement(idExpr->name, rhs); + binding->identifierToken = idExpr->identifierToken; + } else if (AST::Pattern *p = expr->patternCast()) { + SourceLocation loc; + QString s; + if (!p->convertLiteralToAssignmentPattern(pool, &loc, &s)) + return nullptr; + binding = new (pool) AST::PatternElement(p, rhs); + binding->identifierToken = p->firstSourceLocation(); + } + if (!binding) + return nullptr; + return new (pool) AST::FormalParameterList(f, binding); +} + BinaryExpression *BinaryExpression::binaryExpressionCast() { return this; @@ -107,6 +184,16 @@ void NestedExpression::accept0(Visitor *visitor) visitor->endVisit(this); } +FunctionExpression *NestedExpression::asFunctionDefinition() +{ + return expression->asFunctionDefinition(); +} + +ClassExpression *NestedExpression::asClassDefinition() +{ + return expression->asClassDefinition(); +} + void ThisExpression::accept0(Visitor *visitor) { if (visitor->visit(this)) { @@ -147,7 +234,7 @@ void FalseLiteral::accept0(Visitor *visitor) visitor->endVisit(this); } -void StringLiteral::accept0(Visitor *visitor) +void SuperLiteral::accept0(Visitor *visitor) { if (visitor->visit(this)) { } @@ -155,7 +242,8 @@ void StringLiteral::accept0(Visitor *visitor) visitor->endVisit(this); } -void NumericLiteral::accept0(Visitor *visitor) + +void StringLiteral::accept0(Visitor *visitor) { if (visitor->visit(this)) { } @@ -163,81 +251,229 @@ void NumericLiteral::accept0(Visitor *visitor) visitor->endVisit(this); } -void RegExpLiteral::accept0(Visitor *visitor) +void TemplateLiteral::accept0(Visitor *visitor) { if (visitor->visit(this)) { + if (next) + accept(next, visitor); } visitor->endVisit(this); } -void ArrayLiteral::accept0(Visitor *visitor) +void NumericLiteral::accept0(Visitor *visitor) { if (visitor->visit(this)) { - accept(elements, visitor); - accept(elision, visitor); } visitor->endVisit(this); } -void ObjectLiteral::accept0(Visitor *visitor) +void RegExpLiteral::accept0(Visitor *visitor) { if (visitor->visit(this)) { - accept(properties, visitor); } visitor->endVisit(this); } -void ElementList::accept0(Visitor *visitor) +void ArrayPattern::accept0(Visitor *visitor) { - if (visitor->visit(this)) { - for (ElementList *it = this; it; it = it->next) { - accept(it->elision, visitor); - accept(it->expression, visitor); - } - } + if (visitor->visit(this)) + accept(elements, visitor); visitor->endVisit(this); } -void Elision::accept0(Visitor *visitor) +bool ArrayPattern::isValidArrayLiteral(SourceLocation *errorLocation) const { + for (PatternElementList *it = elements; it != nullptr; it = it->next) { + PatternElement *e = it->element; + if (e && e->bindingTarget != nullptr) { + if (errorLocation) + *errorLocation = e->firstSourceLocation(); + return false; + } + } + return true; +} + +void ObjectPattern::accept0(Visitor *visitor) { if (visitor->visit(this)) { - // ### + accept(properties, visitor); } visitor->endVisit(this); } -void PropertyNameAndValue::accept0(Visitor *visitor) -{ - if (visitor->visit(this)) { - accept(name, visitor); - accept(value, visitor); +/* + This is the grammar for AssignmentPattern that we need to convert the literal to: + + AssignmentPattern: + ObjectAssignmentPattern + ArrayAssignmentPattern + ArrayAssignmentPattern: + [ ElisionOpt AssignmentRestElementOpt ] + [ AssignmentElementList ] + [ AssignmentElementList , ElisionOpt AssignmentRestElementOpt ] + AssignmentElementList: + AssignmentElisionElement + AssignmentElementList , AssignmentElisionElement + AssignmentElisionElement: + ElisionOpt AssignmentElement + AssignmentRestElement: + ... DestructuringAssignmentTarget + + ObjectAssignmentPattern: + {} + { AssignmentPropertyList } + { AssignmentPropertyList, } + AssignmentPropertyList: + AssignmentProperty + AssignmentPropertyList , AssignmentProperty + AssignmentProperty: + IdentifierReference InitializerOpt_In + PropertyName: + AssignmentElement + + AssignmentElement: + DestructuringAssignmentTarget InitializerOpt_In + DestructuringAssignmentTarget: + LeftHandSideExpression + + It was originally parsed with the following grammar: + +ArrayLiteral: + [ ElisionOpt ] + [ ElementList ] + [ ElementList , ElisionOpt ] +ElementList: + ElisionOpt AssignmentExpression_In + ElisionOpt SpreadElement + ElementList , ElisionOpt AssignmentExpression_In + ElementList , Elisionopt SpreadElement +SpreadElement: + ... AssignmentExpression_In +ObjectLiteral: + {} + { PropertyDefinitionList } + { PropertyDefinitionList , } +PropertyDefinitionList: + PropertyDefinition + PropertyDefinitionList , PropertyDefinition +PropertyDefinition: + IdentifierReference + CoverInitializedName + PropertyName : AssignmentExpression_In + MethodDefinition +PropertyName: + LiteralPropertyName + ComputedPropertyName + +*/ +bool ArrayPattern::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) +{ + if (parseMode == Binding) + return true; + for (auto *it = elements; it; it = it->next) { + if (!it->element) + continue; + if (it->element->type == PatternElement::SpreadElement && it->next) { + *errorLocation = it->element->firstSourceLocation(); + *errorMessage = QString::fromLatin1("'...' can only appear as last element in a destructuring list."); + return false; + } + if (!it->element->convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage)) + return false; } + parseMode = Binding; + return true; +} - visitor->endVisit(this); +bool ObjectPattern::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) +{ + if (parseMode == Binding) + return true; + for (auto *it = properties; it; it = it->next) { + if (!it->property->convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage)) + return false; + } + parseMode = Binding; + return true; } -void PropertyGetterSetter::accept0(Visitor *visitor) +bool PatternElement::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) { - if (visitor->visit(this)) { - accept(name, visitor); - accept(formals, visitor); - accept(functionBody, visitor); + Q_ASSERT(type == Literal || type == SpreadElement); + Q_ASSERT(bindingIdentifier.isNull()); + Q_ASSERT(bindingTarget == nullptr); + Q_ASSERT(bindingTarget == nullptr); + Q_ASSERT(initializer); + ExpressionNode *init = initializer; + + initializer = nullptr; + LeftHandSideExpression *lhs = init->leftHandSideExpressionCast(); + if (type == SpreadElement) { + if (!lhs) { + *errorLocation = init->firstSourceLocation(); + *errorMessage = QString::fromLatin1("Invalid lhs expression after '...' in destructuring expression."); + return false; + } + } else { + type = PatternElement::Binding; + + if (BinaryExpression *b = init->binaryExpressionCast()) { + if (b->op != QSOperator::Assign) { + *errorLocation = b->operatorToken; + *errorMessage = QString::fromLatin1("Invalid assignment operation in destructuring expression"); + return false; + } + lhs = b->left->leftHandSideExpressionCast(); + initializer = b->right; + Q_ASSERT(lhs); + } else { + lhs = init->leftHandSideExpressionCast(); + } + if (!lhs) { + *errorLocation = init->firstSourceLocation(); + *errorMessage = QString::fromLatin1("Destructuring target is not a left hand side expression."); + return false; + } } - visitor->endVisit(this); + if (auto *i = cast<IdentifierExpression *>(lhs)) { + bindingIdentifier = i->name; + identifierToken = i->identifierToken; + return true; + } + + bindingTarget = lhs; + if (auto *p = lhs->patternCast()) { + if (!p->convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage)) + return false; + } + return true; +} + +bool PatternProperty::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) +{ + Q_ASSERT(type != SpreadElement); + if (type == Binding) + return true; + if (type == Getter || type == Setter) { + *errorLocation = firstSourceLocation(); + *errorMessage = QString::fromLatin1("Invalid getter/setter in destructuring expression."); + return false; + } + Q_ASSERT(type == Literal); + return PatternElement::convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage); } -void PropertyAssignmentList::accept0(Visitor *visitor) + +void Elision::accept0(Visitor *visitor) { if (visitor->visit(this)) { - for (PropertyAssignmentList *it = this; it; it = it->next) { - accept(it->assignment, visitor); - } + // ### } visitor->endVisit(this); @@ -518,15 +754,6 @@ void VariableDeclarationList::accept0(Visitor *visitor) visitor->endVisit(this); } -void VariableDeclaration::accept0(Visitor *visitor) -{ - if (visitor->visit(this)) { - accept(expression, visitor); - } - - visitor->endVisit(this); -} - void EmptyStatement::accept0(Visitor *visitor) { if (visitor->visit(this)) { @@ -579,17 +806,6 @@ void ForStatement::accept0(Visitor *visitor) { if (visitor->visit(this)) { accept(initialiser, visitor); - accept(condition, visitor); - accept(expression, visitor); - accept(statement, visitor); - } - - visitor->endVisit(this); -} - -void LocalForStatement::accept0(Visitor *visitor) -{ - if (visitor->visit(this)) { accept(declarations, visitor); accept(condition, visitor); accept(expression, visitor); @@ -602,7 +818,7 @@ void LocalForStatement::accept0(Visitor *visitor) void ForEachStatement::accept0(Visitor *visitor) { if (visitor->visit(this)) { - accept(initialiser, visitor); + accept(lhs, visitor); accept(expression, visitor); accept(statement, visitor); } @@ -610,18 +826,15 @@ void ForEachStatement::accept0(Visitor *visitor) visitor->endVisit(this); } -void LocalForEachStatement::accept0(Visitor *visitor) +void ContinueStatement::accept0(Visitor *visitor) { if (visitor->visit(this)) { - accept(declaration, visitor); - accept(expression, visitor); - accept(statement, visitor); } visitor->endVisit(this); } -void ContinueStatement::accept0(Visitor *visitor) +void BreakStatement::accept0(Visitor *visitor) { if (visitor->visit(this)) { } @@ -629,15 +842,16 @@ void ContinueStatement::accept0(Visitor *visitor) visitor->endVisit(this); } -void BreakStatement::accept0(Visitor *visitor) +void ReturnStatement::accept0(Visitor *visitor) { if (visitor->visit(this)) { + accept(expression, visitor); } visitor->endVisit(this); } -void ReturnStatement::accept0(Visitor *visitor) +void YieldExpression::accept0(Visitor *visitor) { if (visitor->visit(this)) { accept(expression, visitor); @@ -646,6 +860,7 @@ void ReturnStatement::accept0(Visitor *visitor) visitor->endVisit(this); } + void WithStatement::accept0(Visitor *visitor) { if (visitor->visit(this)) { @@ -739,6 +954,7 @@ void TryStatement::accept0(Visitor *visitor) void Catch::accept0(Visitor *visitor) { if (visitor->visit(this)) { + accept(patternElement, visitor); accept(statement, visitor); } @@ -774,57 +990,69 @@ void FunctionExpression::accept0(Visitor *visitor) visitor->endVisit(this); } -void FormalParameterList::accept0(Visitor *visitor) +FunctionExpression *FunctionExpression::asFunctionDefinition() { - if (visitor->visit(this)) { - // ### - } - - visitor->endVisit(this); + return this; } -void FunctionBody::accept0(Visitor *visitor) +QStringList FormalParameterList::formals() const { - if (visitor->visit(this)) { - accept(elements, visitor); + QStringList formals; + int i = 0; + for (const FormalParameterList *it = this; it; it = it->next) { + if (it->element) { + QString name = it->element->bindingIdentifier.toString(); + int duplicateIndex = formals.indexOf(name); + if (duplicateIndex >= 0) { + // change the name of the earlier argument to enforce the lookup semantics from the spec + formals[duplicateIndex] += QLatin1String("#") + QString::number(i); + } + formals += name; + } + ++i; } - - visitor->endVisit(this); + return formals; } -void Program::accept0(Visitor *visitor) +QStringList FormalParameterList::boundNames() const { - if (visitor->visit(this)) { - accept(elements, visitor); + QStringList names; + for (const FormalParameterList *it = this; it; it = it->next) { + if (it->element) + it->element->boundNames(&names); } - - visitor->endVisit(this); + return names; } -void SourceElements::accept0(Visitor *visitor) +void FormalParameterList::accept0(Visitor *visitor) { if (visitor->visit(this)) { - for (SourceElements *it = this; it; it = it->next) { - accept(it->element, visitor); - } + accept(element, visitor); + if (next) + accept(next, visitor); } visitor->endVisit(this); } -void FunctionSourceElement::accept0(Visitor *visitor) +FormalParameterList *FormalParameterList::finish(QQmlJS::MemoryPool *pool) { - if (visitor->visit(this)) { - accept(declaration, visitor); - } + FormalParameterList *front = next; + next = nullptr; - visitor->endVisit(this); + int i = 0; + for (const FormalParameterList *it = this; it; it = it->next) { + if (it->element && it->element->bindingIdentifier.isEmpty()) + it->element->bindingIdentifier = pool->newString(QLatin1String("arg#") + QString::number(i)); + ++i; + } + return front; } -void StatementSourceElement::accept0(Visitor *visitor) +void Program::accept0(Visitor *visitor) { if (visitor->visit(this)) { - accept(statement, visitor); + accept(statements, visitor); } visitor->endVisit(this); @@ -1006,6 +1234,153 @@ void UiEnumMemberList::accept0(Visitor *visitor) visitor->endVisit(this); } +void TaggedTemplate::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + accept(templateLiteral, visitor); + } + + visitor->endVisit(this); +} + +void PatternElement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(bindingTarget, visitor); + accept(initializer, visitor); + } + + visitor->endVisit(this); +} + +void PatternElement::boundNames(QStringList *names) +{ + if (bindingTarget) { + if (PatternElementList *e = elementList()) + e->boundNames(names); + else if (PatternPropertyList *p = propertyList()) + p->boundNames(names); + } else { + names->append(bindingIdentifier.toString()); + } +} + +void PatternElementList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(elision, visitor); + accept(element, visitor); + if (next) + accept(next, visitor); + } + + visitor->endVisit(this); +} + +void PatternElementList::boundNames(QStringList *names) +{ + for (PatternElementList *it = this; it; it = it->next) { + if (it->element) + it->element->boundNames(names); + } +} + +void PatternProperty::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(name, visitor); + accept(bindingTarget, visitor); + accept(initializer, visitor); + } + + visitor->endVisit(this); +} + +void PatternProperty::boundNames(QStringList *names) +{ + PatternElement::boundNames(names); +} + +void PatternPropertyList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(property, visitor); + if (next) + accept(next, visitor); + } + + visitor->endVisit(this); +} + +void PatternPropertyList::boundNames(QStringList *names) +{ + for (PatternPropertyList *it = this; it; it = it->next) + it->property->boundNames(names); +} + +void ComputedPropertyName::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void ClassExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(heritage, visitor); + accept(elements, visitor); + } + + visitor->endVisit(this); +} + +ClassExpression *ClassExpression::asClassDefinition() +{ + return this; +} + +void ClassDeclaration::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(heritage, visitor); + accept(elements, visitor); + } + + visitor->endVisit(this); +} + +void ClassElementList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(property, visitor); + if (next) + accept(next, visitor); + } + + visitor->endVisit(this); +} + +ClassElementList *ClassElementList::finish() +{ + ClassElementList *front = next; + next = nullptr; + return front; +} + +Pattern *Pattern::patternCast() +{ + return this; +} + +LeftHandSideExpression *LeftHandSideExpression::leftHandSideExpressionCast() +{ + return this; +} + } } // namespace QQmlJS::AST QT_QML_END_NAMESPACE diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index 9c5fd5adf6..07999404b4 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -77,6 +77,8 @@ enum Op { Div, InplaceDiv, Equal, + Exp, + InplaceExp, Ge, Gt, In, @@ -110,6 +112,13 @@ namespace QQmlJS { namespace AST { +enum class VariableScope { + NoScope, + Var, + Let, + Const +}; + template <typename T1, typename T2> T1 cast(T2 *ast) { @@ -119,6 +128,9 @@ T1 cast(T2 *ast) return 0; } +FunctionExpression *asAnonymousFunctionDefinition(AST::Node *n); +ClassExpression *asAnonymousClassDefinition(AST::Node *n); + class QML_PARSER_EXPORT Node: public Managed { public: @@ -126,7 +138,7 @@ public: Kind_Undefined, Kind_ArgumentList, - Kind_ArrayLiteral, + Kind_ArrayPattern, Kind_ArrayMemberExpression, Kind_BinaryExpression, Kind_Block, @@ -148,6 +160,7 @@ public: Kind_Expression, Kind_ExpressionStatement, Kind_FalseLiteral, + Kind_SuperLiteral, Kind_FieldMemberExpression, Kind_Finally, Kind_ForEachStatement, @@ -156,38 +169,38 @@ public: Kind_FunctionBody, Kind_FunctionDeclaration, Kind_FunctionExpression, - Kind_FunctionSourceElement, + Kind_ClassExpression, + Kind_ClassDeclaration, Kind_IdentifierExpression, Kind_IdentifierPropertyName, + Kind_ComputedPropertyName, Kind_IfStatement, Kind_LabelledStatement, - Kind_LocalForEachStatement, - Kind_LocalForStatement, Kind_NewExpression, Kind_NewMemberExpression, Kind_NotExpression, Kind_NullExpression, + Kind_YieldExpression, Kind_NumericLiteral, Kind_NumericLiteralPropertyName, - Kind_ObjectLiteral, + Kind_ObjectPattern, Kind_PostDecrementExpression, Kind_PostIncrementExpression, Kind_PreDecrementExpression, Kind_PreIncrementExpression, Kind_Program, - Kind_PropertyAssignmentList, + Kind_PropertyDefinitionList, Kind_PropertyGetterSetter, Kind_PropertyName, Kind_PropertyNameAndValue, Kind_RegExpLiteral, Kind_ReturnStatement, - Kind_SourceElement, - Kind_SourceElements, Kind_StatementList, - Kind_StatementSourceElement, Kind_StringLiteral, Kind_StringLiteralPropertyName, Kind_SwitchStatement, + Kind_TemplateLiteral, + Kind_TaggedTemplate, Kind_ThisExpression, Kind_ThrowStatement, Kind_TildeExpression, @@ -203,6 +216,12 @@ public: Kind_WhileStatement, Kind_WithStatement, Kind_NestedExpression, + Kind_ClassElementList, + Kind_PatternElement, + Kind_PatternElementList, + Kind_PatternProperty, + Kind_PatternPropertyList, + Kind_UiArrayBinding, Kind_UiImport, @@ -235,6 +254,11 @@ public: virtual BinaryExpression *binaryExpressionCast(); virtual Statement *statementCast(); virtual UiObjectMember *uiObjectMemberCast(); + virtual LeftHandSideExpression *leftHandSideExpressionCast(); + virtual Pattern *patternCast(); + // implements the IsFunctionDefinition rules in the spec + virtual FunctionExpression *asFunctionDefinition(); + virtual ClassExpression *asClassDefinition(); void accept(Visitor *visitor); static void accept(Node *node, Visitor *visitor); @@ -256,6 +280,14 @@ public: ExpressionNode() {} ExpressionNode *expressionCast() override; + + AST::FormalParameterList *reparseAsFormalParameterList(MemoryPool *pool); + +}; + +class QML_PARSER_EXPORT LeftHandSideExpression : public ExpressionNode +{ + LeftHandSideExpression *leftHandSideExpressionCast() override; }; class QML_PARSER_EXPORT Statement: public Node @@ -266,7 +298,7 @@ public: Statement *statementCast() override; }; -class QML_PARSER_EXPORT NestedExpression: public ExpressionNode +class QML_PARSER_EXPORT NestedExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(NestedExpression) @@ -283,13 +315,17 @@ public: SourceLocation lastSourceLocation() const override { return rparenToken; } + FunctionExpression *asFunctionDefinition() override; + ClassExpression *asClassDefinition() override; + + // attributes ExpressionNode *expression; SourceLocation lparenToken; SourceLocation rparenToken; }; -class QML_PARSER_EXPORT ThisExpression: public ExpressionNode +class QML_PARSER_EXPORT ThisExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(ThisExpression) @@ -308,7 +344,7 @@ public: SourceLocation thisToken; }; -class QML_PARSER_EXPORT IdentifierExpression: public ExpressionNode +class QML_PARSER_EXPORT IdentifierExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(IdentifierExpression) @@ -329,7 +365,7 @@ public: SourceLocation identifierToken; }; -class QML_PARSER_EXPORT NullExpression: public ExpressionNode +class QML_PARSER_EXPORT NullExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(NullExpression) @@ -348,7 +384,7 @@ public: SourceLocation nullToken; }; -class QML_PARSER_EXPORT TrueLiteral: public ExpressionNode +class QML_PARSER_EXPORT TrueLiteral: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(TrueLiteral) @@ -367,7 +403,7 @@ public: SourceLocation trueToken; }; -class QML_PARSER_EXPORT FalseLiteral: public ExpressionNode +class QML_PARSER_EXPORT FalseLiteral: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(FalseLiteral) @@ -386,7 +422,27 @@ public: SourceLocation falseToken; }; -class QML_PARSER_EXPORT NumericLiteral: public ExpressionNode +class QML_PARSER_EXPORT SuperLiteral : public LeftHandSideExpression +{ +public: + QQMLJS_DECLARE_AST_NODE(SuperLiteral) + + SuperLiteral() { kind = K; } + + void accept0(Visitor *visitor) override; + + SourceLocation firstSourceLocation() const override + { return superToken; } + + SourceLocation lastSourceLocation() const override + { return superToken; } + +// attributes + SourceLocation superToken; +}; + + +class QML_PARSER_EXPORT NumericLiteral: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(NumericLiteral) @@ -407,7 +463,7 @@ public: SourceLocation literalToken; }; -class QML_PARSER_EXPORT StringLiteral: public ExpressionNode +class QML_PARSER_EXPORT StringLiteral: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(StringLiteral) @@ -428,7 +484,30 @@ public: SourceLocation literalToken; }; -class QML_PARSER_EXPORT RegExpLiteral: public ExpressionNode +class QML_PARSER_EXPORT TemplateLiteral : public LeftHandSideExpression +{ +public: + QQMLJS_DECLARE_AST_NODE(TemplateLiteral) + + TemplateLiteral(const QStringRef &str, ExpressionNode *e) + : value(str), expression(e), next(nullptr) + { kind = K; } + + SourceLocation firstSourceLocation() const override + { return literalToken; } + + SourceLocation lastSourceLocation() const override + { return next ? next->lastSourceLocation() : (expression ? expression->lastSourceLocation() : literalToken); } + + void accept0(Visitor *visitor) override; + + QStringRef value; + ExpressionNode *expression; + TemplateLiteral *next; + SourceLocation literalToken; +}; + +class QML_PARSER_EXPORT RegExpLiteral: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(RegExpLiteral) @@ -450,22 +529,26 @@ public: SourceLocation literalToken; }; -class QML_PARSER_EXPORT ArrayLiteral: public ExpressionNode +class QML_PARSER_EXPORT Pattern : public LeftHandSideExpression { public: - QQMLJS_DECLARE_AST_NODE(ArrayLiteral) - - ArrayLiteral(Elision *e): - elements (nullptr), elision (e) - { kind = K; } + enum ParseMode { + Literal, + Binding + }; + Pattern *patternCast() override; + virtual bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) = 0; + ParseMode parseMode = Literal; +}; - ArrayLiteral(ElementList *elts): - elements (elts), elision (nullptr) - { kind = K; } +class QML_PARSER_EXPORT ArrayPattern : public Pattern +{ +public: + QQMLJS_DECLARE_AST_NODE(ArrayPattern) - ArrayLiteral(ElementList *elts, Elision *e): - elements (elts), elision (e) - { kind = K; } + ArrayPattern(PatternElementList *elts) + : elements(elts) + { kind = K; } void accept0(Visitor *visitor) override; @@ -475,24 +558,28 @@ public: SourceLocation lastSourceLocation() const override { return rbracketToken; } + bool isValidArrayLiteral(SourceLocation *errorLocation = nullptr) const; + + bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override; + // attributes - ElementList *elements; - Elision *elision; + PatternElementList *elements = nullptr; SourceLocation lbracketToken; SourceLocation commaToken; SourceLocation rbracketToken; }; -class QML_PARSER_EXPORT ObjectLiteral: public ExpressionNode +class QML_PARSER_EXPORT ObjectPattern : public Pattern { public: - QQMLJS_DECLARE_AST_NODE(ObjectLiteral) + QQMLJS_DECLARE_AST_NODE(ObjectPattern) - ObjectLiteral() + ObjectPattern() { kind = K; } - ObjectLiteral(PropertyAssignmentList *plist): - properties (plist) { kind = K; } + ObjectPattern(PatternPropertyList *plist) + : properties(plist) + { kind = K; } void accept0(Visitor *visitor) override; @@ -502,8 +589,10 @@ public: SourceLocation lastSourceLocation() const override { return rbraceToken; } + bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override; + // attributes - PropertyAssignmentList *properties = nullptr; + PatternPropertyList *properties = nullptr; SourceLocation lbraceToken; SourceLocation rbraceToken; }; @@ -543,53 +632,6 @@ public: SourceLocation commaToken; }; -class QML_PARSER_EXPORT ElementList: public Node -{ -public: - QQMLJS_DECLARE_AST_NODE(ElementList) - - ElementList(Elision *e, ExpressionNode *expr): - elision (e), expression (expr), next (this) - { kind = K; } - - ElementList(ElementList *previous, Elision *e, ExpressionNode *expr): - elision (e), expression (expr) - { - kind = K; - next = previous->next; - previous->next = this; - } - - inline ElementList *finish () - { - ElementList *front = next; - next = nullptr; - return front; - } - - void accept0(Visitor *visitor) override; - - SourceLocation firstSourceLocation() const override - { - if (elision) - return elision->firstSourceLocation(); - return expression->firstSourceLocation(); - } - - SourceLocation lastSourceLocation() const override - { - if (next) - return next->lastSourceLocation(); - return expression->lastSourceLocation(); - } - -// attributes - Elision *elision; - ExpressionNode *expression; - ElementList *next; - SourceLocation commaToken; -}; - class QML_PARSER_EXPORT PropertyName: public Node { public: @@ -609,116 +651,184 @@ public: SourceLocation propertyNameToken; }; -class QML_PARSER_EXPORT PropertyAssignment: public Node +class QML_PARSER_EXPORT PatternElement : public Node { public: - PropertyAssignment(PropertyName *n) - : name(n) - {} + QQMLJS_DECLARE_AST_NODE(PatternElement) + + enum Type { + // object literal types + Literal, + Getter, + Setter, + + // used by both bindings and literals + SpreadElement, + RestElement = SpreadElement, + + // binding types + Binding, + }; + + PatternElement(ExpressionNode *i = nullptr, Type t = Literal) + : initializer(i), type(t) + { kind = K; } + + PatternElement(const QStringRef &n, ExpressionNode *i = nullptr, Type t = Binding) + : bindingIdentifier(n), initializer(i), type(t) + { + Q_ASSERT(t >= RestElement); + kind = K; + } + + PatternElement(Pattern *pattern, ExpressionNode *i = nullptr, Type t = Binding) + : bindingTarget(pattern), initializer(i), type(t) + { + Q_ASSERT(t >= RestElement); + kind = K; + } + + void accept0(Visitor *visitor) override; + virtual bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage); + + SourceLocation firstSourceLocation() const override + { return identifierToken.isValid() ? identifierToken : (bindingTarget ? bindingTarget->firstSourceLocation() : initializer->firstSourceLocation()); } + + SourceLocation lastSourceLocation() const override + { return initializer ? initializer->lastSourceLocation() : (bindingTarget ? bindingTarget->lastSourceLocation() : identifierToken); } + + ExpressionNode *destructuringTarget() const { return bindingTarget; } + Pattern *destructuringPattern() const { return bindingTarget ? bindingTarget->patternCast() : nullptr; } + PatternElementList *elementList() const { ArrayPattern *a = cast<ArrayPattern *>(bindingTarget); return a ? a->elements : nullptr; } + PatternPropertyList *propertyList() const { ObjectPattern *o = cast<ObjectPattern *>(bindingTarget); return o ? o->properties : nullptr; } + + bool isVariableDeclaration() const { return scope != VariableScope::NoScope; } + bool isLexicallyScoped() const { return scope == VariableScope::Let || scope == VariableScope::Const; } + + virtual void boundNames(QStringList *names); + // attributes - PropertyName *name; + SourceLocation identifierToken; + QStringRef bindingIdentifier; + ExpressionNode *bindingTarget = nullptr; + ExpressionNode *initializer = nullptr; + Type type = Literal; + // when used in a VariableDeclarationList + VariableScope scope = VariableScope::NoScope; }; -class QML_PARSER_EXPORT PropertyAssignmentList: public Node +class QML_PARSER_EXPORT PatternElementList : public Node { public: - QQMLJS_DECLARE_AST_NODE(PropertyAssignmentList) + QQMLJS_DECLARE_AST_NODE(PatternElementList) - PropertyAssignmentList(PropertyAssignment *assignment) - : assignment(assignment) - , next(this) + PatternElementList(Elision *elision, PatternElement *element) + : elision(elision), element(element), next(this) { kind = K; } - PropertyAssignmentList(PropertyAssignmentList *previous, PropertyAssignment *assignment) - : assignment(assignment) - { - kind = K; - next = previous->next; - previous->next = this; + PatternElementList *append(PatternElementList *n) { + n->next = next; + next = n; + return n; } - inline PropertyAssignmentList *finish () + inline PatternElementList *finish () { - PropertyAssignmentList *front = next; - next = nullptr; + PatternElementList *front = next; + next = 0; return front; } void accept0(Visitor *visitor) override; + void boundNames(QStringList *names); + SourceLocation firstSourceLocation() const override - { return assignment->firstSourceLocation(); } + { return elision ? elision->firstSourceLocation() : element->firstSourceLocation(); } SourceLocation lastSourceLocation() const override - { return next ? next->lastSourceLocation() : assignment->lastSourceLocation(); } + { return next ? next->lastSourceLocation() : (element ? element->lastSourceLocation() : elision->lastSourceLocation()); } -// attributes - PropertyAssignment *assignment; - PropertyAssignmentList *next; - SourceLocation commaToken; + Elision *elision = nullptr; + PatternElement *element = nullptr; + PatternElementList *next; }; -class QML_PARSER_EXPORT PropertyNameAndValue: public PropertyAssignment +class QML_PARSER_EXPORT PatternProperty : public PatternElement { public: - QQMLJS_DECLARE_AST_NODE(PropertyNameAndValue) + QQMLJS_DECLARE_AST_NODE(PatternProperty) + + PatternProperty(PropertyName *name, ExpressionNode *i = nullptr, Type t = Literal) + : PatternElement(i, t), name(name) + { kind = K; } - PropertyNameAndValue(PropertyName *n, ExpressionNode *v) - : PropertyAssignment(n), value(v) + PatternProperty(PropertyName *name, const QStringRef &n, ExpressionNode *i = nullptr) + : PatternElement(n, i), name(name) + { kind = K; } + + PatternProperty(PropertyName *name, Pattern *pattern, ExpressionNode *i = nullptr) + : PatternElement(pattern, i), name(name) { kind = K; } void accept0(Visitor *visitor) override; SourceLocation firstSourceLocation() const override { return name->firstSourceLocation(); } - SourceLocation lastSourceLocation() const override - { return value->lastSourceLocation(); } + { + SourceLocation loc = PatternElement::lastSourceLocation(); + return loc.isValid() ? loc : name->lastSourceLocation(); + } + + void boundNames(QStringList *names) override; + bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override; // attributes + PropertyName *name; SourceLocation colonToken; - ExpressionNode *value; - SourceLocation commaToken; }; -class QML_PARSER_EXPORT PropertyGetterSetter: public PropertyAssignment + +class QML_PARSER_EXPORT PatternPropertyList : public Node { public: - QQMLJS_DECLARE_AST_NODE(PropertyGetterSetter) - - enum Type { - Getter, - Setter - }; + QQMLJS_DECLARE_AST_NODE(PatternPropertyList) - PropertyGetterSetter(PropertyName *n, FunctionBody *b) - : PropertyAssignment(n), type(Getter), formals(nullptr), functionBody (b) + PatternPropertyList(PatternProperty *property) + : property(property), next(this) { kind = K; } - PropertyGetterSetter(PropertyName *n, FormalParameterList *f, FunctionBody *b) - : PropertyAssignment(n), type(Setter), formals(f), functionBody (b) - { kind = K; } + PatternPropertyList(PatternPropertyList *previous, PatternProperty *property) + : property(property), next(this) + { + kind = K; + next = previous->next; + previous->next = this; + } void accept0(Visitor *visitor) override; + void boundNames(QStringList *names); + + inline PatternPropertyList *finish () + { + PatternPropertyList *front = next; + next = 0; + return front; + } + SourceLocation firstSourceLocation() const override - { return getSetToken; } + { return property->firstSourceLocation(); } SourceLocation lastSourceLocation() const override - { return rbraceToken; } + { return next ? next->lastSourceLocation() : property->lastSourceLocation(); } -// attributes - Type type; - SourceLocation getSetToken; - SourceLocation lparenToken; - FormalParameterList *formals; - SourceLocation rparenToken; - SourceLocation lbraceToken; - FunctionBody *functionBody; - SourceLocation rbraceToken; + PatternProperty *property; + PatternPropertyList *next; }; -class QML_PARSER_EXPORT IdentifierPropertyName: public PropertyName +class QML_PARSER_EXPORT IdentifierPropertyName : public PropertyName { public: QQMLJS_DECLARE_AST_NODE(IdentifierPropertyName) @@ -766,7 +876,31 @@ public: double id; }; -class QML_PARSER_EXPORT ArrayMemberExpression: public ExpressionNode +class QML_PARSER_EXPORT ComputedPropertyName : public PropertyName +{ +public: + QQMLJS_DECLARE_AST_NODE(ComputedPropertyName) + + ComputedPropertyName(ExpressionNode *expression) + : expression(expression) + { kind = K; } + + void accept0(Visitor *visitor) override; + + QString asString() const override { return QString(); } + + SourceLocation firstSourceLocation() const override + { return expression->firstSourceLocation(); } + + SourceLocation lastSourceLocation() const override + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; +}; + + +class QML_PARSER_EXPORT ArrayMemberExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(ArrayMemberExpression) @@ -790,7 +924,7 @@ public: SourceLocation rbracketToken; }; -class QML_PARSER_EXPORT FieldMemberExpression: public ExpressionNode +class QML_PARSER_EXPORT FieldMemberExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(FieldMemberExpression) @@ -814,7 +948,29 @@ public: SourceLocation identifierToken; }; -class QML_PARSER_EXPORT NewMemberExpression: public ExpressionNode +class QML_PARSER_EXPORT TaggedTemplate : public LeftHandSideExpression +{ +public: + QQMLJS_DECLARE_AST_NODE(TaggedTemplate) + + TaggedTemplate(ExpressionNode *b, TemplateLiteral *t) + : base (b), templateLiteral(t) + { kind = K; } + + void accept0(Visitor *visitor) override; + + SourceLocation firstSourceLocation() const override + { return base->firstSourceLocation(); } + + SourceLocation lastSourceLocation() const override + { return templateLiteral->lastSourceLocation(); } + + // attributes + ExpressionNode *base; + TemplateLiteral *templateLiteral; +}; + +class QML_PARSER_EXPORT NewMemberExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(NewMemberExpression) @@ -839,7 +995,7 @@ public: SourceLocation rparenToken; }; -class QML_PARSER_EXPORT NewExpression: public ExpressionNode +class QML_PARSER_EXPORT NewExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(NewExpression) @@ -860,7 +1016,7 @@ public: SourceLocation newToken; }; -class QML_PARSER_EXPORT CallExpression: public ExpressionNode +class QML_PARSER_EXPORT CallExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(CallExpression) @@ -924,6 +1080,7 @@ public: ExpressionNode *expression; ArgumentList *next; SourceLocation commaToken; + bool isSpreadElement = false; }; class QML_PARSER_EXPORT PostIncrementExpression: public ExpressionNode @@ -1257,16 +1414,15 @@ class QML_PARSER_EXPORT StatementList: public Node public: QQMLJS_DECLARE_AST_NODE(StatementList) - StatementList(Statement *stmt): - statement (stmt), next (this) - { kind = K; } + // ### This should be a Statement, but FunctionDeclaration currently doesn't inherit it. + StatementList(Node *stmt) + : statement(stmt), next (this) + { kind = K; } - StatementList(StatementList *previous, Statement *stmt): - statement (stmt) - { - kind = K; - next = previous->next; - previous->next = this; + StatementList *append(StatementList *n) { + n->next = next; + next = n; + return n; } void accept0(Visitor *visitor) override; @@ -1285,76 +1441,21 @@ public: } // attributes - Statement *statement; + Node *statement = nullptr; StatementList *next; }; -class QML_PARSER_EXPORT VariableStatement: public Statement -{ -public: - QQMLJS_DECLARE_AST_NODE(VariableStatement) - - VariableStatement(VariableDeclarationList *vlist): - declarations (vlist) - { kind = K; } - - void accept0(Visitor *visitor) override; - - SourceLocation firstSourceLocation() const override - { return declarationKindToken; } - - SourceLocation lastSourceLocation() const override - { return semicolonToken; } - -// attributes - VariableDeclarationList *declarations; - SourceLocation declarationKindToken; - SourceLocation semicolonToken; -}; - -class QML_PARSER_EXPORT VariableDeclaration: public Node -{ -public: - QQMLJS_DECLARE_AST_NODE(VariableDeclaration) - - enum VariableScope { - FunctionScope, - BlockScope, // let - ReadOnlyBlockScope // const - }; - - VariableDeclaration(const QStringRef &n, ExpressionNode *e, VariableScope s): - name (n), expression (e), scope(s) - { kind = K; } - - bool isLexicallyScoped() const { return scope != FunctionScope; } - - void accept0(Visitor *visitor) override; - - SourceLocation firstSourceLocation() const override - { return identifierToken; } - - SourceLocation lastSourceLocation() const override - { return expression ? expression->lastSourceLocation() : identifierToken; } - -// attributes - QStringRef name; - ExpressionNode *expression; - SourceLocation identifierToken; - VariableScope scope; -}; - class QML_PARSER_EXPORT VariableDeclarationList: public Node { public: QQMLJS_DECLARE_AST_NODE(VariableDeclarationList) - VariableDeclarationList(VariableDeclaration *decl): - declaration (decl), next (this) - { kind = K; } + VariableDeclarationList(PatternElement *decl) + : declaration(decl), next(this) + { kind = K; } - VariableDeclarationList(VariableDeclarationList *previous, VariableDeclaration *decl): - declaration (decl) + VariableDeclarationList(VariableDeclarationList *previous, PatternElement *decl) + : declaration(decl) { kind = K; next = previous->next; @@ -1373,7 +1474,7 @@ public: return declaration->lastSourceLocation(); } - inline VariableDeclarationList *finish(VariableDeclaration::VariableScope s) + inline VariableDeclarationList *finish(VariableScope s) { VariableDeclarationList *front = next; next = nullptr; @@ -1385,11 +1486,33 @@ public: } // attributes - VariableDeclaration *declaration; + PatternElement *declaration; VariableDeclarationList *next; SourceLocation commaToken; }; +class QML_PARSER_EXPORT VariableStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(VariableStatement) + + VariableStatement(VariableDeclarationList *vlist): + declarations (vlist) + { kind = K; } + + void accept0(Visitor *visitor) override; + + SourceLocation firstSourceLocation() const override + { return declarationKindToken; } + + SourceLocation lastSourceLocation() const override + { return declarations->lastSourceLocation(); } + +// attributes + VariableDeclarationList *declarations; + SourceLocation declarationKindToken; +}; + class QML_PARSER_EXPORT EmptyStatement: public Statement { public: @@ -1423,7 +1546,7 @@ public: { return expression->firstSourceLocation(); } SourceLocation lastSourceLocation() const override - { return semicolonToken; } + { return expression->lastSourceLocation(); } // attributes ExpressionNode *expression; @@ -1523,35 +1646,11 @@ public: initialiser (i), condition (c), expression (e), statement (stmt) { kind = K; } - void accept0(Visitor *visitor) override; - - SourceLocation firstSourceLocation() const override - { return forToken; } - - SourceLocation lastSourceLocation() const override - { return statement->lastSourceLocation(); } - -// attributes - ExpressionNode *initialiser; - ExpressionNode *condition; - ExpressionNode *expression; - Statement *statement; - SourceLocation forToken; - SourceLocation lparenToken; - SourceLocation firstSemicolonToken; - SourceLocation secondSemicolonToken; - SourceLocation rparenToken; -}; - -class QML_PARSER_EXPORT LocalForStatement: public Statement -{ -public: - QQMLJS_DECLARE_AST_NODE(LocalForStatement) - - LocalForStatement(VariableDeclarationList *vlist, ExpressionNode *c, ExpressionNode *e, Statement *stmt): + ForStatement(VariableDeclarationList *vlist, ExpressionNode *c, ExpressionNode *e, Statement *stmt): declarations (vlist), condition (c), expression (e), statement (stmt) { kind = K; } + void accept0(Visitor *visitor) override; SourceLocation firstSourceLocation() const override @@ -1561,26 +1660,34 @@ public: { return statement->lastSourceLocation(); } // attributes - VariableDeclarationList *declarations; + ExpressionNode *initialiser = nullptr; + VariableDeclarationList *declarations = nullptr; ExpressionNode *condition; ExpressionNode *expression; Statement *statement; SourceLocation forToken; SourceLocation lparenToken; - SourceLocation varToken; SourceLocation firstSemicolonToken; SourceLocation secondSemicolonToken; SourceLocation rparenToken; }; +enum class ForEachType { + In, + Of +}; + class QML_PARSER_EXPORT ForEachStatement: public Statement { public: QQMLJS_DECLARE_AST_NODE(ForEachStatement) - ForEachStatement(ExpressionNode *i, ExpressionNode *e, Statement *stmt): - initialiser (i), expression (e), statement (stmt) - { kind = K; } + ForEachStatement(ExpressionNode *i, ExpressionNode *e, Statement *stmt) + : lhs(i), expression(e), statement(stmt) + { kind = K; } + ForEachStatement(PatternElement *v, ExpressionNode *e, Statement *stmt) + : lhs(v), expression(e), statement(stmt) + { kind = K; } void accept0(Visitor *visitor) override; @@ -1590,42 +1697,19 @@ public: SourceLocation lastSourceLocation() const override { return statement->lastSourceLocation(); } -// attributes - ExpressionNode *initialiser; - ExpressionNode *expression; - Statement *statement; - SourceLocation forToken; - SourceLocation lparenToken; - SourceLocation inToken; - SourceLocation rparenToken; -}; - -class QML_PARSER_EXPORT LocalForEachStatement: public Statement -{ -public: - QQMLJS_DECLARE_AST_NODE(LocalForEachStatement) - - LocalForEachStatement(VariableDeclaration *v, ExpressionNode *e, Statement *stmt): - declaration (v), expression (e), statement (stmt) - { kind = K; } - - void accept0(Visitor *visitor) override; - - SourceLocation firstSourceLocation() const override - { return forToken; } - - SourceLocation lastSourceLocation() const override - { return statement->lastSourceLocation(); } + PatternElement *declaration() const { + return AST::cast<PatternElement *>(lhs); + } // attributes - VariableDeclaration *declaration; + Node *lhs; ExpressionNode *expression; Statement *statement; SourceLocation forToken; SourceLocation lparenToken; - SourceLocation varToken; - SourceLocation inToken; + SourceLocation inOfToken; SourceLocation rparenToken; + ForEachType type; }; class QML_PARSER_EXPORT ContinueStatement: public Statement @@ -1696,6 +1780,28 @@ public: SourceLocation semicolonToken; }; +class QML_PARSER_EXPORT YieldExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(YieldExpression) + + YieldExpression(ExpressionNode *e = nullptr): + expression (e) { kind = K; } + + void accept0(Visitor *visitor) override; + + SourceLocation firstSourceLocation() const override + { return yieldToken; } + + SourceLocation lastSourceLocation() const override + { return expression ? expression->lastSourceLocation() : yieldToken; } + +// attributes + ExpressionNode *expression; + bool isYieldStar = false; + SourceLocation yieldToken; +}; + class QML_PARSER_EXPORT WithStatement: public Statement { public: @@ -1906,9 +2012,9 @@ class QML_PARSER_EXPORT Catch: public Node public: QQMLJS_DECLARE_AST_NODE(Catch) - Catch(const QStringRef &n, Block *stmt): - name (n), statement (stmt) - { kind = K; } + Catch(PatternElement *p, Block *stmt) + : patternElement(p), statement(stmt) + { kind = K; } void accept0(Visitor *visitor) override; @@ -1919,7 +2025,7 @@ public: { return statement->lastSourceLocation(); } // attributes - QStringRef name; + PatternElement *patternElement; Block *statement; SourceLocation catchToken; SourceLocation lparenToken; @@ -1993,7 +2099,7 @@ class QML_PARSER_EXPORT FunctionExpression: public ExpressionNode public: QQMLJS_DECLARE_AST_NODE(FunctionExpression) - FunctionExpression(const QStringRef &n, FormalParameterList *f, FunctionBody *b): + FunctionExpression(const QStringRef &n, FormalParameterList *f, StatementList *b): name (n), formals (f), body (b) { kind = K; } @@ -2005,10 +2111,14 @@ public: SourceLocation lastSourceLocation() const override { return rbraceToken; } + FunctionExpression *asFunctionDefinition() override; + // attributes QStringRef name; + bool isArrowFunction = false; + bool isGenerator = false; FormalParameterList *formals; - FunctionBody *body; + StatementList *body; SourceLocation functionToken; SourceLocation identifierToken; SourceLocation lparenToken; @@ -2022,7 +2132,7 @@ class QML_PARSER_EXPORT FunctionDeclaration: public FunctionExpression public: QQMLJS_DECLARE_AST_NODE(FunctionDeclaration) - FunctionDeclaration(const QStringRef &n, FormalParameterList *f, FunctionBody *b): + FunctionDeclaration(const QStringRef &n, FormalParameterList *f, StatementList *b): FunctionExpression(n, f, b) { kind = K; } @@ -2034,65 +2144,68 @@ class QML_PARSER_EXPORT FormalParameterList: public Node public: QQMLJS_DECLARE_AST_NODE(FormalParameterList) - FormalParameterList(const QStringRef &n): - name (n), next (this) - { kind = K; } - - FormalParameterList(FormalParameterList *previous, const QStringRef &n): - name (n) + FormalParameterList(FormalParameterList *previous, PatternElement *e) + : element(e) { kind = K; - next = previous->next; - previous->next = this; + if (previous) { + next = previous->next; + previous->next = this; + } else { + next = this; + } } - void accept0(Visitor *visitor) override; - - SourceLocation firstSourceLocation() const override - { return identifierToken; } - - SourceLocation lastSourceLocation() const override - { return next ? next->lastSourceLocation() : identifierToken; } + FormalParameterList *append(FormalParameterList *n) { + n->next = next; + next = n; + return n; + } - inline FormalParameterList *finish () + bool isSimpleParameterList() { - FormalParameterList *front = next; - next = nullptr; - return front; + AST::FormalParameterList *formals = this; + while (formals) { + PatternElement *e = formals->element; + if (e && e->type == PatternElement::RestElement) + return false; + if (e && (e->initializer || e->bindingTarget)) + return false; + formals = formals->next; + } + return true; } -// attributes - QStringRef name; - FormalParameterList *next; - SourceLocation commaToken; - SourceLocation identifierToken; -}; - -class QML_PARSER_EXPORT SourceElement: public Node -{ -public: - QQMLJS_DECLARE_AST_NODE(SourceElement) - - inline SourceElement() - { kind = K; } -}; + int length() + { + // the length property of Function objects + int l = 0; + AST::FormalParameterList *formals = this; + while (formals) { + PatternElement *e = formals->element; + if (!e || e->initializer) + break; + if (e->type == PatternElement::RestElement) + break; + ++l; + formals = formals->next; + } + return l; + } -class QML_PARSER_EXPORT SourceElements: public Node -{ -public: - QQMLJS_DECLARE_AST_NODE(SourceElements) + bool containsName(const QString &name) const { + for (const FormalParameterList *it = this; it; it = it->next) { + PatternElement *b = it->element; + // ### handle binding patterns + if (b && b->bindingIdentifier == name) + return true; + } + return false; + } - SourceElements(SourceElement *elt): - element (elt), next (this) - { kind = K; } + QStringList formals() const; - SourceElements(SourceElements *previous, SourceElement *elt): - element (elt) - { - kind = K; - next = previous->next; - previous->next = this; - } + QStringList boundNames() const; void accept0(Visitor *visitor) override; @@ -2102,100 +2215,111 @@ public: SourceLocation lastSourceLocation() const override { return next ? next->lastSourceLocation() : element->lastSourceLocation(); } - inline SourceElements *finish () - { - SourceElements *front = next; - next = nullptr; - return front; - } + FormalParameterList *finish(MemoryPool *pool); // attributes - SourceElement *element; - SourceElements *next; + PatternElement *element = nullptr; + FormalParameterList *next; }; -class QML_PARSER_EXPORT FunctionBody: public Node +class QML_PARSER_EXPORT ClassExpression : public ExpressionNode { public: - QQMLJS_DECLARE_AST_NODE(FunctionBody) + QQMLJS_DECLARE_AST_NODE(ClassExpression) - FunctionBody(SourceElements *elts): - elements (elts) + ClassExpression(const QStringRef &n, ExpressionNode *heritage, ClassElementList *elements) + : name(n), heritage(heritage), elements(elements) { kind = K; } void accept0(Visitor *visitor) override; SourceLocation firstSourceLocation() const override - { return elements ? elements->firstSourceLocation() : SourceLocation(); } + { return classToken; } SourceLocation lastSourceLocation() const override - { return elements ? elements->lastSourceLocation() : SourceLocation(); } + { return rbraceToken; } + + ClassExpression *asClassDefinition() override; // attributes - SourceElements *elements; + QStringRef name; + ExpressionNode *heritage; + ClassElementList *elements; + SourceLocation classToken; + SourceLocation identifierToken; + SourceLocation lbraceToken; + SourceLocation rbraceToken; }; -class QML_PARSER_EXPORT Program: public Node +class QML_PARSER_EXPORT ClassDeclaration: public ClassExpression { public: - QQMLJS_DECLARE_AST_NODE(Program) + QQMLJS_DECLARE_AST_NODE(ClassDeclaration) - Program(SourceElements *elts): - elements (elts) + ClassDeclaration(const QStringRef &n, ExpressionNode *heritage, ClassElementList *elements) + : ClassExpression(n, heritage, elements) { kind = K; } void accept0(Visitor *visitor) override; - - SourceLocation firstSourceLocation() const override - { return elements ? elements->firstSourceLocation() : SourceLocation(); } - - SourceLocation lastSourceLocation() const override - { return elements ? elements->lastSourceLocation() : SourceLocation(); } - -// attributes - SourceElements *elements; }; -class QML_PARSER_EXPORT FunctionSourceElement: public SourceElement + +class QML_PARSER_EXPORT ClassElementList : public Node { public: - QQMLJS_DECLARE_AST_NODE(FunctionSourceElement) + QQMLJS_DECLARE_AST_NODE(ClassElementList) - FunctionSourceElement(FunctionDeclaration *f): - declaration (f) - { kind = K; } + ClassElementList(PatternProperty *property, bool isStatic) + : isStatic(isStatic), property(property) + { + kind = K; + next = this; + } + + ClassElementList *append(ClassElementList *n) { + n->next = next; + next = n; + return n; + } void accept0(Visitor *visitor) override; SourceLocation firstSourceLocation() const override - { return declaration->firstSourceLocation(); } + { return property->firstSourceLocation(); } SourceLocation lastSourceLocation() const override - { return declaration->lastSourceLocation(); } + { + if (next) + return next->lastSourceLocation(); + return property->lastSourceLocation(); + } -// attributes - FunctionDeclaration *declaration; + ClassElementList *finish(); + + bool isStatic; + ClassElementList *next; + PatternProperty *property; }; -class QML_PARSER_EXPORT StatementSourceElement: public SourceElement +class QML_PARSER_EXPORT Program: public Node { public: - QQMLJS_DECLARE_AST_NODE(StatementSourceElement) + QQMLJS_DECLARE_AST_NODE(Program) - StatementSourceElement(Statement *stmt): - statement (stmt) - { kind = K; } + Program(StatementList *statements) + : statements(statements) + { kind = K; } void accept0(Visitor *visitor) override; SourceLocation firstSourceLocation() const override - { return statement->firstSourceLocation(); } + { return statements ? statements->firstSourceLocation() : SourceLocation(); } SourceLocation lastSourceLocation() const override - { return statement->lastSourceLocation(); } + { return statements ? statements->lastSourceLocation() : SourceLocation(); } // attributes - Statement *statement; + StatementList *statements; }; class QML_PARSER_EXPORT DebuggerStatement: public Statement diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h index 140a757e51..406a06c6db 100644 --- a/src/qml/parser/qqmljsastfwd_p.h +++ b/src/qml/parser/qqmljsastfwd_p.h @@ -89,22 +89,27 @@ class IdentifierExpression; class NullExpression; class TrueLiteral; class FalseLiteral; +class SuperLiteral; class NumericLiteral; class StringLiteral; +class TemplateLiteral; class RegExpLiteral; -class ArrayLiteral; -class ObjectLiteral; -class ElementList; +class Pattern; +class ArrayPattern; +class ObjectPattern; +class PatternElement; +class PatternElementList; +class PatternProperty; +class PatternPropertyList; class Elision; -class PropertyAssignmentList; -class PropertyGetterSetter; -class PropertyNameAndValue; class PropertyName; class IdentifierPropertyName; class StringLiteralPropertyName; class NumericLiteralPropertyName; +class ComputedPropertyName; class ArrayMemberExpression; class FieldMemberExpression; +class TaggedTemplate; class NewMemberExpression; class NewExpression; class CallExpression; @@ -123,20 +128,19 @@ class NotExpression; class BinaryExpression; class ConditionalExpression; class Expression; // ### rename +class YieldExpression; class Block; +class LeftHandSideExpression; class StatementList; class VariableStatement; class VariableDeclarationList; -class VariableDeclaration; class EmptyStatement; class ExpressionStatement; class IfStatement; class DoWhileStatement; class WhileStatement; class ForStatement; -class LocalForStatement; class ForEachStatement; -class LocalForEachStatement; class ContinueStatement; class BreakStatement; class ReturnStatement; @@ -154,14 +158,12 @@ class Finally; class FunctionDeclaration; class FunctionExpression; class FormalParameterList; -class FunctionBody; class Program; -class SourceElements; -class SourceElement; -class FunctionSourceElement; -class StatementSourceElement; class DebuggerStatement; class NestedExpression; +class ClassExpression; +class ClassDeclaration; +class ClassElementList; // ui elements class UiProgram; diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h index 13218f0e98..6e1fed9dbb 100644 --- a/src/qml/parser/qqmljsastvisitor_p.h +++ b/src/qml/parser/qqmljsastvisitor_p.h @@ -122,35 +122,41 @@ public: virtual bool visit(FalseLiteral *) { return true; } virtual void endVisit(FalseLiteral *) {} + virtual bool visit(SuperLiteral *) { return true; } + virtual void endVisit(SuperLiteral *) {} + virtual bool visit(StringLiteral *) { return true; } virtual void endVisit(StringLiteral *) {} + virtual bool visit(TemplateLiteral *) { return true; } + virtual void endVisit(TemplateLiteral *) {} + virtual bool visit(NumericLiteral *) { return true; } virtual void endVisit(NumericLiteral *) {} virtual bool visit(RegExpLiteral *) { return true; } virtual void endVisit(RegExpLiteral *) {} - virtual bool visit(ArrayLiteral *) { return true; } - virtual void endVisit(ArrayLiteral *) {} + virtual bool visit(ArrayPattern *) { return true; } + virtual void endVisit(ArrayPattern *) {} - virtual bool visit(ObjectLiteral *) { return true; } - virtual void endVisit(ObjectLiteral *) {} + virtual bool visit(ObjectPattern *) { return true; } + virtual void endVisit(ObjectPattern *) {} - virtual bool visit(ElementList *) { return true; } - virtual void endVisit(ElementList *) {} + virtual bool visit(PatternElementList *) { return true; } + virtual void endVisit(PatternElementList *) {} - virtual bool visit(Elision *) { return true; } - virtual void endVisit(Elision *) {} + virtual bool visit(PatternPropertyList *) { return true; } + virtual void endVisit(PatternPropertyList *) {} - virtual bool visit(PropertyAssignmentList *) { return true; } - virtual void endVisit(PropertyAssignmentList *) {} + virtual bool visit(PatternElement *) { return true; } + virtual void endVisit(PatternElement *) {} - virtual bool visit(PropertyNameAndValue *) { return true; } - virtual void endVisit(PropertyNameAndValue *) {} + virtual bool visit(PatternProperty *) { return true; } + virtual void endVisit(PatternProperty *) {} - virtual bool visit(PropertyGetterSetter *) { return true; } - virtual void endVisit(PropertyGetterSetter *) {} + virtual bool visit(Elision *) { return true; } + virtual void endVisit(Elision *) {} virtual bool visit(NestedExpression *) { return true; } virtual void endVisit(NestedExpression *) {} @@ -164,12 +170,18 @@ public: virtual bool visit(NumericLiteralPropertyName *) { return true; } virtual void endVisit(NumericLiteralPropertyName *) {} + virtual bool visit(ComputedPropertyName *) { return true; } + virtual void endVisit(ComputedPropertyName *) {} + virtual bool visit(ArrayMemberExpression *) { return true; } virtual void endVisit(ArrayMemberExpression *) {} virtual bool visit(FieldMemberExpression *) { return true; } virtual void endVisit(FieldMemberExpression *) {} + virtual bool visit(TaggedTemplate *) { return true; } + virtual void endVisit(TaggedTemplate *) {} + virtual bool visit(NewMemberExpression *) { return true; } virtual void endVisit(NewMemberExpression *) {} @@ -236,9 +248,6 @@ public: virtual bool visit(VariableDeclarationList *) { return true; } virtual void endVisit(VariableDeclarationList *) {} - virtual bool visit(VariableDeclaration *) { return true; } - virtual void endVisit(VariableDeclaration *) {} - virtual bool visit(EmptyStatement *) { return true; } virtual void endVisit(EmptyStatement *) {} @@ -257,15 +266,9 @@ public: virtual bool visit(ForStatement *) { return true; } virtual void endVisit(ForStatement *) {} - virtual bool visit(LocalForStatement *) { return true; } - virtual void endVisit(LocalForStatement *) {} - virtual bool visit(ForEachStatement *) { return true; } virtual void endVisit(ForEachStatement *) {} - virtual bool visit(LocalForEachStatement *) { return true; } - virtual void endVisit(LocalForEachStatement *) {} - virtual bool visit(ContinueStatement *) { return true; } virtual void endVisit(ContinueStatement *) {} @@ -275,6 +278,9 @@ public: virtual bool visit(ReturnStatement *) { return true; } virtual void endVisit(ReturnStatement *) {} + virtual bool visit(YieldExpression *) { return true; } + virtual void endVisit(YieldExpression *) {} + virtual bool visit(WithStatement *) { return true; } virtual void endVisit(WithStatement *) {} @@ -317,20 +323,17 @@ public: virtual bool visit(FormalParameterList *) { return true; } virtual void endVisit(FormalParameterList *) {} - virtual bool visit(FunctionBody *) { return true; } - virtual void endVisit(FunctionBody *) {} - - virtual bool visit(Program *) { return true; } - virtual void endVisit(Program *) {} + virtual bool visit(ClassExpression *) { return true; } + virtual void endVisit(ClassExpression *) {} - virtual bool visit(SourceElements *) { return true; } - virtual void endVisit(SourceElements *) {} + virtual bool visit(ClassDeclaration *) { return true; } + virtual void endVisit(ClassDeclaration *) {} - virtual bool visit(FunctionSourceElement *) { return true; } - virtual void endVisit(FunctionSourceElement *) {} + virtual bool visit(ClassElementList *) { return true; } + virtual void endVisit(ClassElementList *) {} - virtual bool visit(StatementSourceElement *) { return true; } - virtual void endVisit(StatementSourceElement *) {} + virtual bool visit(Program *) { return true; } + virtual void endVisit(Program *) {} virtual bool visit(DebuggerStatement *) { return true; } virtual void endVisit(DebuggerStatement *) {} diff --git a/src/qml/parser/qqmljsengine_p.cpp b/src/qml/parser/qqmljsengine_p.cpp index b4f0debf85..97ce6ebea3 100644 --- a/src/qml/parser/qqmljsengine_p.cpp +++ b/src/qml/parser/qqmljsengine_p.cpp @@ -112,13 +112,6 @@ double integerFromString(const char *buf, int size, int radix) return result; } -double integerFromString(const QString &str, int radix) -{ - QByteArray ba = QStringRef(&str).trimmed().toLatin1(); - return integerFromString(ba.constData(), ba.size(), radix); -} - - Engine::Engine() : _lexer(nullptr), _directives(nullptr) { } diff --git a/src/qml/parser/qqmljsengine_p.h b/src/qml/parser/qqmljsengine_p.h index af26bac0ff..1de907d296 100644 --- a/src/qml/parser/qqmljsengine_p.h +++ b/src/qml/parser/qqmljsengine_p.h @@ -63,9 +63,35 @@ QT_QML_BEGIN_NAMESPACE namespace QQmlJS { class Lexer; -class Directives; class MemoryPool; +class QML_PARSER_EXPORT Directives { +public: + virtual ~Directives() {} + + virtual void pragmaLibrary() + { + } + + virtual void importFile(const QString &jsfile, const QString &module, int line, int column) + { + Q_UNUSED(jsfile); + Q_UNUSED(module); + Q_UNUSED(line); + Q_UNUSED(column); + } + + virtual void importModule(const QString &uri, const QString &version, const QString &module, int line, int column) + { + Q_UNUSED(uri); + Q_UNUSED(version); + Q_UNUSED(module); + Q_UNUSED(line); + Q_UNUSED(column); + } +}; + + class QML_PARSER_EXPORT DiagnosticMessage { public: diff --git a/src/qml/parser/qqmljsgrammar.cpp b/src/qml/parser/qqmljsgrammar.cpp deleted file mode 100644 index 2aaeb385e3..0000000000 --- a/src/qml/parser/qqmljsgrammar.cpp +++ /dev/null @@ -1,1167 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// This file was generated by qlalr - DO NOT EDIT! -#include "qqmljsgrammar_p.h" - -QT_BEGIN_NAMESPACE - -const char *const QQmlJSGrammar::spell [] = { - "end of file", "&", "&&", "&=", "break", "case", "catch", ":", ",", "continue", - "default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===", - "finally", "for", "function", ">=", ">", ">>", ">>=", ">>>", ">>>=", "identifier", - "if", "in", "instanceof", "{", "[", "<=", "(", "<", "<<", "<<=", - "-", "-=", "--", "new", "!", "!=", "!==", "numeric literal", "|", "|=", - "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return", - ")", ";", nullptr, "*", "*=", "string literal", "property", "signal", "readonly", "switch", - "this", "throw", "~", "try", "typeof", "var", "void", "while", "with", "^", - "^=", "null", "true", "false", "const", "let", "debugger", "reserved word", "multiline string literal", "comment", - nullptr, "enum", "public", "import", "pragma", "as", "on", "get", "set", nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr -}; - -const short QQmlJSGrammar::lhs [] = { - 108, 108, 108, 108, 108, 108, 109, 115, 115, 118, - 118, 118, 118, 121, 123, 119, 119, 120, 120, 120, - 120, 120, 120, 120, 120, 124, 125, 117, 116, 128, - 128, 129, 129, 130, 130, 127, 113, 113, 113, 113, - 132, 132, 132, 132, 132, 132, 132, 113, 140, 140, - 140, 140, 141, 141, 142, 142, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 145, 145, 145, - 145, 126, 126, 126, 126, 126, 126, 126, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 131, 148, 148, 148, - 148, 147, 147, 152, 152, 152, 150, 150, 153, 153, - 153, 153, 156, 156, 156, 156, 156, 156, 156, 156, - 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, - 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, - 156, 156, 156, 156, 156, 157, 157, 122, 122, 122, - 122, 122, 160, 160, 161, 161, 161, 161, 159, 159, - 162, 162, 163, 163, 164, 164, 164, 165, 165, 165, - 165, 165, 165, 165, 165, 165, 165, 166, 166, 166, - 166, 167, 167, 167, 168, 168, 168, 168, 169, 169, - 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, - 170, 171, 171, 171, 171, 171, 172, 172, 172, 172, - 172, 173, 173, 174, 174, 175, 175, 176, 176, 177, - 177, 178, 178, 179, 179, 180, 180, 181, 181, 182, - 182, 183, 183, 184, 184, 151, 151, 185, 185, 186, - 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, - 186, 111, 111, 187, 187, 188, 188, 189, 189, 110, - 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, - 110, 110, 110, 110, 133, 198, 198, 197, 197, 144, - 144, 199, 199, 199, 200, 200, 202, 202, 201, 203, - 206, 204, 204, 207, 205, 205, 134, 135, 135, 136, - 136, 190, 190, 190, 190, 190, 190, 190, 190, 191, - 191, 191, 191, 192, 192, 192, 192, 193, 193, 137, - 138, 208, 208, 211, 211, 209, 209, 212, 210, 194, - 195, 195, 139, 139, 139, 213, 214, 196, 196, 215, - 143, 158, 158, 216, 216, 155, 155, 154, 154, 217, - 114, 114, 218, 218, 112, 112, 149, 149, 219 -}; - -const short QQmlJSGrammar::rhs [] = { - 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, - 1, 2, 2, 1, 1, 2, 2, 2, 2, 3, - 3, 5, 5, 4, 4, 2, 2, 0, 1, 1, - 2, 1, 3, 2, 3, 2, 1, 5, 4, 4, - 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, - 1, 3, 0, 1, 2, 4, 6, 6, 3, 3, - 7, 7, 4, 4, 5, 5, 8, 8, 5, 6, - 6, 10, 6, 7, 1, 1, 5, 1, 3, 3, - 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, - 3, 4, 5, 3, 4, 3, 1, 1, 2, 3, - 4, 1, 2, 3, 7, 8, 1, 3, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, - 3, 5, 1, 2, 4, 4, 4, 3, 0, 1, - 1, 3, 1, 1, 1, 2, 2, 1, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 1, 3, 3, - 3, 1, 3, 3, 1, 3, 3, 3, 1, 3, - 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, - 3, 1, 3, 3, 3, 3, 1, 3, 3, 3, - 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, - 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, - 3, 1, 5, 1, 5, 1, 3, 1, 3, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 3, 0, 1, 1, 3, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 3, 1, 2, 0, 1, 3, - 3, 1, 1, 1, 1, 3, 1, 3, 2, 2, - 2, 0, 1, 2, 0, 1, 1, 2, 2, 7, - 5, 7, 7, 7, 5, 9, 10, 7, 8, 2, - 2, 3, 3, 2, 2, 3, 3, 3, 3, 5, - 5, 3, 5, 1, 2, 0, 1, 4, 3, 3, - 3, 3, 3, 3, 4, 5, 2, 2, 2, 1, - 8, 8, 7, 1, 3, 0, 1, 0, 1, 1, - 1, 1, 1, 2, 1, 1, 0, 1, 2 -}; - -const short QQmlJSGrammar::action_default [] = { - 0, 0, 28, 0, 0, 0, 28, 0, 195, 262, - 226, 234, 230, 174, 246, 222, 3, 159, 90, 175, - 238, 242, 163, 192, 173, 178, 158, 212, 199, 0, - 97, 98, 93, 0, 87, 82, 367, 0, 0, 0, - 0, 95, 0, 0, 91, 94, 86, 0, 0, 83, - 85, 88, 84, 96, 89, 0, 92, 0, 0, 188, - 0, 0, 175, 194, 177, 176, 0, 0, 0, 190, - 191, 189, 193, 0, 223, 0, 0, 0, 0, 213, - 0, 0, 0, 0, 0, 0, 203, 0, 0, 0, - 197, 198, 196, 201, 205, 204, 202, 200, 215, 214, - 216, 0, 231, 0, 227, 0, 0, 169, 156, 168, - 157, 123, 124, 125, 151, 126, 153, 127, 128, 129, - 130, 131, 132, 133, 134, 135, 136, 137, 138, 152, - 139, 140, 154, 141, 142, 143, 144, 145, 146, 147, - 148, 149, 150, 155, 0, 0, 167, 263, 170, 0, - 171, 0, 172, 166, 0, 259, 252, 250, 257, 258, - 256, 255, 261, 254, 253, 251, 260, 247, 0, 235, - 0, 0, 239, 0, 0, 243, 0, 0, 169, 161, - 0, 160, 0, 165, 179, 0, 356, 356, 357, 0, - 354, 0, 355, 0, 358, 270, 277, 276, 284, 272, - 0, 273, 0, 359, 0, 366, 274, 275, 90, 280, - 278, 363, 360, 365, 281, 0, 293, 0, 0, 0, - 0, 350, 0, 367, 292, 264, 307, 0, 0, 0, - 294, 0, 0, 282, 283, 0, 271, 279, 308, 309, - 0, 356, 0, 0, 358, 0, 351, 352, 0, 340, - 364, 0, 324, 325, 326, 327, 0, 320, 321, 322, - 323, 348, 349, 0, 0, 0, 0, 0, 312, 313, - 314, 268, 266, 228, 236, 232, 248, 224, 269, 0, - 175, 240, 244, 217, 206, 0, 0, 225, 0, 0, - 0, 0, 218, 0, 0, 0, 0, 0, 210, 208, - 211, 209, 207, 220, 219, 221, 0, 233, 0, 229, - 0, 267, 175, 0, 249, 264, 265, 0, 264, 0, - 0, 316, 0, 0, 0, 318, 0, 237, 0, 0, - 241, 0, 0, 245, 305, 0, 297, 306, 300, 0, - 304, 0, 264, 298, 0, 264, 0, 0, 317, 0, - 0, 0, 319, 0, 0, 0, 311, 0, 310, 90, - 117, 368, 0, 0, 122, 286, 289, 0, 123, 293, - 126, 153, 128, 129, 93, 134, 135, 87, 136, 292, - 139, 91, 94, 264, 88, 96, 142, 89, 144, 92, - 146, 147, 294, 149, 150, 155, 0, 119, 118, 121, - 105, 120, 104, 0, 114, 287, 285, 0, 0, 0, - 358, 0, 115, 163, 164, 169, 0, 162, 0, 328, - 329, 0, 356, 0, 0, 358, 0, 116, 0, 0, - 0, 331, 336, 334, 337, 0, 0, 335, 336, 0, - 332, 0, 333, 288, 339, 0, 288, 338, 0, 341, - 342, 0, 288, 343, 344, 0, 0, 345, 0, 0, - 0, 346, 347, 181, 180, 0, 0, 0, 315, 0, - 0, 0, 330, 302, 295, 0, 303, 299, 0, 301, - 290, 0, 291, 296, 0, 0, 358, 0, 353, 108, - 0, 0, 112, 99, 0, 101, 110, 0, 102, 111, - 113, 103, 109, 100, 0, 106, 185, 183, 187, 184, - 182, 186, 361, 6, 362, 4, 2, 75, 107, 0, - 0, 0, 83, 85, 84, 37, 5, 0, 76, 0, - 51, 50, 49, 0, 0, 51, 0, 0, 0, 52, - 0, 67, 68, 0, 65, 0, 66, 41, 42, 43, - 44, 46, 47, 71, 45, 0, 0, 0, 78, 0, - 77, 80, 0, 81, 0, 79, 0, 51, 0, 0, - 0, 0, 0, 61, 0, 62, 0, 0, 32, 0, - 0, 72, 33, 0, 36, 34, 30, 0, 35, 31, - 0, 63, 0, 64, 163, 0, 69, 73, 0, 0, - 0, 0, 163, 288, 0, 70, 90, 123, 293, 126, - 153, 128, 129, 93, 134, 135, 136, 292, 139, 91, - 94, 264, 96, 142, 89, 144, 92, 146, 147, 294, - 149, 150, 155, 74, 0, 59, 53, 60, 54, 0, - 0, 0, 0, 56, 0, 57, 58, 55, 0, 0, - 0, 0, 48, 0, 38, 39, 0, 40, 8, 0, - 0, 9, 0, 11, 0, 10, 0, 1, 27, 15, - 14, 26, 13, 12, 29, 7, 0, 18, 0, 19, - 0, 24, 25, 0, 20, 21, 0, 22, 23, 16, - 17, 369 -}; - -const short QQmlJSGrammar::goto_default [] = { - 7, 667, 213, 200, 211, 526, 513, 662, 675, 512, - 661, 665, 663, 671, 22, 668, 666, 664, 18, 525, - 587, 577, 584, 579, 553, 195, 199, 201, 206, 237, - 214, 234, 568, 639, 638, 205, 236, 557, 26, 491, - 490, 362, 361, 9, 360, 363, 204, 484, 364, 109, - 17, 149, 24, 13, 148, 19, 25, 59, 23, 8, - 28, 27, 283, 15, 277, 10, 273, 12, 275, 11, - 274, 20, 281, 21, 282, 14, 276, 272, 313, 418, - 278, 279, 207, 197, 196, 210, 209, 233, 198, 367, - 366, 235, 475, 474, 335, 336, 477, 338, 476, 337, - 431, 435, 438, 434, 433, 453, 454, 202, 188, 203, - 212, 0 -}; - -const short QQmlJSGrammar::action_index [] = { - 350, 1528, 3041, 3041, 2937, 1235, 115, 105, 239, -108, - 102, 93, 95, 205, -108, 422, 110, -108, -108, 727, - 92, 118, 265, 256, -108, -108, -108, 507, 247, 1528, - -108, -108, -108, 665, -108, -108, 2729, 1826, 1528, 1528, - 1528, -108, 1041, 1528, -108, -108, -108, 1528, 1528, -108, - -108, -108, -108, -108, -108, 1528, -108, 1528, 1528, -108, - 1528, 1528, 174, 221, -108, -108, 1528, 1528, 1528, -108, - -108, -108, 177, 1528, 422, 1528, 1528, 1528, 1528, 411, - 1528, 1528, 1528, 1528, 1528, 1528, 211, 1528, 1528, 1528, - 142, 148, 154, 217, 223, 226, 227, 231, 507, 385, - 395, 1528, 57, 1528, 83, 2521, 1528, 1528, -108, -108, - -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, - -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, - -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, - -108, -108, -108, -108, 179, 1528, -108, -108, 77, 36, - -108, 1528, -108, -108, 1528, -108, -108, -108, -108, -108, - -108, -108, -108, -108, -108, -108, -108, -108, 1528, 56, - 1528, 1528, 80, 74, 1528, -108, 2521, 1528, 1528, -108, - 125, -108, 55, -108, -108, 53, 410, 418, 72, 52, - -108, 392, -108, 46, 3041, -108, -108, -108, -108, -108, - 273, -108, 396, -108, 44, -108, -108, -108, 76, -108, - -108, -108, 3041, -108, -108, 744, -108, 589, 98, 2937, - 91, 90, 88, 3249, -108, 1528, -108, 86, 1528, 81, - -108, 75, 73, -108, -108, 586, -108, -108, -108, -108, - 71, 491, 69, 65, 3041, 64, -108, -108, 2937, -108, - -108, 139, -108, -108, -108, -108, 134, -108, -108, -108, - -108, -108, -108, 63, 66, 1528, 147, 264, -108, -108, - -108, 1726, -108, 87, 68, 70, -108, 334, 82, 78, - 796, 89, 121, 349, 318, 469, 1528, 330, 1528, 1528, - 1528, 1528, 359, 1528, 1528, 1528, 1528, 1528, 284, 289, - 303, 306, 313, 365, 369, 375, 1528, 58, 1528, 85, - 1528, -108, 849, 1528, -108, 1528, 79, 59, 1528, 61, - 2937, -108, 1528, 146, 2937, -108, 1528, 62, 1528, 1528, - 106, 99, 1528, -108, 96, 176, 171, -108, -108, 1528, - -108, 407, 1528, -108, 97, 1528, -52, 2937, -108, 1528, - 120, 2937, -108, 1528, 123, 2937, 101, 2937, -108, 116, - -108, 84, 45, 0, -108, -108, 2937, -39, 641, -3, - 652, 156, 1528, 2937, -7, -11, 567, 2625, -21, 5, - 945, 2, 94, 1629, 2625, -1, -26, 6, 1528, 10, - -15, 1528, 14, 1528, -25, -12, 2833, -108, -108, -108, - -108, -108, -108, 1528, -108, -108, -108, -14, -58, -13, - 3041, -36, -108, 287, -108, 1528, -46, -108, 153, -108, - -108, -31, 586, -57, -32, 3041, 11, -108, 1528, 168, - 29, -108, 47, -108, 48, 169, 1528, -108, 49, 50, - -108, 9, -108, 2937, -108, 136, 2937, -108, 275, -108, - -108, 126, 2937, 35, -108, 33, 37, -108, 466, -4, - 38, -108, -108, -108, -108, 1528, 130, 2937, -108, 1528, - 117, 2937, -108, 34, -108, 296, -108, -108, 1528, -108, - -108, 404, -108, -108, 12, 40, 3041, 13, -108, -108, - 155, 1926, -108, -108, 2026, -108, -108, 2126, -108, -108, - -108, -108, -108, -108, 144, -108, -108, -108, -108, -108, - -108, -108, -108, -108, 3041, -108, -108, -108, 132, -27, - 15, 1137, 218, -23, 17, -108, -108, 196, -108, 242, - 8, -108, -108, 579, 237, -108, 127, 18, 415, -108, - 103, -108, -108, 236, -108, 2223, -108, -108, -108, -108, - -108, -108, -108, -108, -108, 27, 21, 137, 31, 20, - -108, 23, -5, -108, -9, -108, 332, -10, 571, 201, - 195, 586, 225, -108, 7, -108, 1137, 165, -108, 4, - 1137, -108, -108, 1333, -108, -108, -108, 1431, -108, -108, - 184, -108, 2223, -108, 331, 3, -108, -108, 202, 531, - 26, 2417, 327, 3145, 1, -108, 25, 629, 24, 649, - 124, 1528, 2937, 22, -6, 514, -8, 19, 1041, 16, - 94, 1629, 28, 42, 67, 1528, 60, 43, 1528, 54, - 1528, 41, 39, -108, 234, -108, 228, -108, 51, -2, - 564, 233, 575, -108, 100, -108, -108, -108, 2320, 1137, - 1826, 32, -108, 122, -108, -108, 30, -108, -108, 1137, - 1137, 104, 903, -108, 308, -108, 108, -108, -108, 133, - 119, -108, -108, -108, -108, -108, 451, -108, 164, -108, - 161, -108, -108, 458, -108, -108, 151, -108, -108, -108, - -108, -108, - - -112, 18, 86, 97, 69, 316, 7, -112, -112, -112, - -112, -112, -112, -112, -112, -112, -112, -112, -112, -64, - -112, -112, -112, -112, -112, -112, -112, -112, -112, 66, - -112, -112, -112, -17, -112, -112, -10, -36, 3, 90, - 95, -112, 149, 74, -112, -112, -112, 67, 13, -112, - -112, -112, -112, -112, -112, 178, -112, 181, 185, -112, - 189, 190, -112, -112, -112, -112, 198, 208, 212, -112, - -112, -112, -112, 209, -112, 201, 164, 111, 113, -112, - 116, 130, 131, 132, 134, 142, -112, 118, 124, 144, - -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, 154, -112, 155, -112, 268, 28, -8, -112, -112, - -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -112, 42, -112, -112, -112, -112, - -112, 47, -112, -112, 50, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -112, -112, -112, -112, 159, -112, - 158, 56, -112, -112, 57, -112, 362, 60, 157, -112, - -112, -112, -112, -112, -112, -112, 20, 151, -112, -112, - -112, 25, -112, -112, 30, -112, -112, -112, -112, -112, - -112, -112, 31, -112, -112, -112, -112, -112, -112, -112, - -112, -112, 233, -112, -112, 34, -112, 35, -112, 225, - -112, 36, -112, 216, -112, 55, -112, -112, 53, 39, - -112, -112, -112, -112, -112, 19, -112, -112, -112, -112, - -112, 94, -112, -112, 92, -112, -112, -112, 117, -112, - -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -112, 33, -112, -112, -112, -112, - -112, 88, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -112, 51, 220, -112, 227, 235, - 236, 244, -112, 21, 17, 102, 91, 89, -112, -112, - -112, -112, -112, -112, -112, -112, 211, -112, 247, -112, - 257, -112, -112, 264, -112, 75, -112, -112, 103, -112, - 112, -112, 29, -112, 73, -112, 263, -112, 255, 254, - -112, -112, 245, -112, -112, -112, -112, -112, -112, 248, - -112, 65, 105, -112, -112, 99, -112, 162, -112, 37, - -112, 115, -112, 44, -112, 135, -112, 137, -112, -112, - -112, -112, -112, -112, -112, -112, 138, -112, 24, -112, - 26, -112, 104, 140, -112, -112, 32, 64, -112, -112, - 174, -112, -112, 48, 87, -112, -112, -112, 54, -112, - 40, 71, -112, 150, -112, -112, 197, -112, -112, -112, - -112, -112, -112, 12, -112, -112, -112, -112, -112, -112, - 206, -112, -112, -112, -112, 207, -112, -112, -112, -112, - -112, -112, 231, -112, -112, 239, -112, -112, 43, -112, - -112, -112, -112, -112, -59, -112, 38, -112, -62, -112, - -112, -112, -112, 258, -112, -112, 259, -112, -112, -112, - -112, -112, 163, -72, -112, -112, 41, -112, 62, -112, - 61, -112, -112, -112, -112, 59, -112, 193, -112, 58, - -112, 204, -112, -112, -112, -112, -112, -112, 52, -112, - -112, 175, -112, -112, -112, -112, 186, -112, -112, -112, - -112, 49, -112, -112, 173, -112, -112, 45, -112, -112, - -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, 213, -112, -112, -112, -112, -112, - -112, 46, -112, -112, -112, -112, -112, -112, -112, 27, - -112, -112, -112, -18, -9, -112, -112, -112, 15, -112, - -112, -112, -112, -112, -112, 331, -112, -112, -112, -112, - -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -112, -112, -112, -112, 11, -6, - -112, 10, -112, -112, -112, -112, 156, -112, -112, -112, - 240, -112, -112, 330, -112, -112, -112, 332, -112, -112, - -112, -112, 376, -112, -112, 8, -112, -112, -7, 76, - -112, 358, -112, 228, 5, -112, -112, 6, -112, 4, - -112, 79, 221, -112, -112, 2, -112, -112, 174, -112, - -112, 16, -112, -112, -112, 14, -112, -16, 70, -112, - 63, -112, -112, -112, -112, -112, -30, -112, -112, -112, - -15, -28, -13, -112, -112, -112, -112, -112, 460, 93, - 307, -12, -112, -112, -112, -112, -11, -112, -112, -2, - -1, 85, 84, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -112, -112, -3, -112, -112, -112, - -112, -112, -112, 0, -112, -112, -112, -112, -112, -112, - -112, -112 -}; - -const short QQmlJSGrammar::action_info [] = { - -132, 425, 409, 424, -151, 422, -120, 403, 347, -140, - 428, 465, -152, -143, 417, 353, 406, -145, 452, 412, - 410, -148, 408, -140, 469, 271, -152, 569, 353, -132, - 271, -151, 248, 601, 583, -120, 583, 583, 565, 529, - 562, 576, 563, 598, 555, 534, 634, 539, 564, 561, - 558, 478, 436, 436, 436, 456, 460, 443, 644, 641, - 556, -148, 432, 583, 442, 583, 427, -145, 488, 458, - 452, 452, 485, 486, -143, 469, 452, 465, 428, 194, - 191, 174, 168, 248, 73, 151, 286, 145, 286, 187, - 310, 326, 396, 0, 168, 0, 153, 0, 244, 247, - 402, -121, 265, 73, 101, 691, 332, 241, 326, 469, - 306, 465, 193, 339, 452, 183, 306, 357, 145, 246, - 318, 320, 428, 248, 353, 145, 186, 271, 145, 243, - 580, 145, 455, 145, 176, 0, 103, 308, 145, 315, - 264, 101, 537, 446, 145, 559, 456, 176, 176, 308, - 0, 538, 145, 177, 145, 145, 0, 0, 345, 262, - 261, 646, 645, 494, 542, 541, 177, 177, 170, 690, - 689, 328, 171, 580, 103, 329, 145, 471, 654, 439, - 351, 181, 60, 355, 341, 262, 261, 145, 60, 66, - 467, 592, 560, 61, 60, 260, 259, 659, 660, 61, - 255, 254, 349, 648, 505, 61, 324, 267, 659, 660, - 537, 495, 688, 687, 420, 419, 64, 262, 261, 571, - 105, 581, 682, 681, 440, 685, 684, 65, 430, 583, - 535, 535, 574, 66, 67, 146, 87, 342, 88, 106, - 68, 107, 87, 545, 88, 593, 591, 567, 87, 89, - 88, 87, 87, 88, 88, 89, 87, 535, 88, 683, - 0, 89, 535, 0, 89, 89, 535, 0, 66, 89, - 636, 530, 87, 0, 88, 0, 532, 532, 67, 60, - 176, 145, 0, 145, 68, 89, 575, 573, 531, 531, - 61, 0, 649, 532, 0, 637, 635, 546, 544, 177, - 0, 178, 176, 532, 481, 531, 0, 0, 532, 87, - 0, 88, 532, 67, 87, 531, 88, 532, 0, 68, - 531, 177, 89, 415, 531, 270, 268, 89, 87, 531, - 88, 87, 0, 88, 239, 238, 450, 449, 87, 0, - 88, 89, 176, 87, 89, 88, 176, 176, 288, 289, - 0, 89, 288, 289, 269, 678, 89, 482, 480, 0, - -107, 177, 0, 178, -107, 177, 177, 178, 415, 679, - 677, 0, 293, 294, 0, 290, 291, 0, 0, 290, - 291, 295, 293, 294, 296, 0, 297, 0, 293, 294, - 0, 295, 293, 294, 296, 0, 297, 295, 293, 294, - 296, 295, 297, 676, 296, 0, 297, 295, 80, 81, - 296, 0, 297, 0, 0, 0, 82, 83, 80, 81, - 84, 35, 85, 0, 0, 35, 82, 83, 0, 0, - 84, 0, 85, 35, 80, 81, 35, 0, 0, 35, - 75, 76, 82, 83, 35, 0, 84, 35, 85, 0, - 6, 5, 4, 1, 3, 2, 0, 0, 49, 52, - 50, 0, 49, 52, 50, 0, 0, 77, 78, 0, - 49, 52, 50, 49, 52, 50, 49, 52, 50, 0, - 35, 49, 52, 50, 49, 52, 50, 35, 46, 34, - 51, 0, 46, 34, 51, 35, 0, 0, 35, 0, - 46, 34, 51, 46, 34, 51, 46, 34, 51, 0, - 0, 46, 34, 51, 46, 34, 51, 49, 52, 50, - 35, 0, 0, 0, 49, 52, 50, 0, 0, 0, - 80, 81, 49, 52, 50, 49, 52, 50, 82, 83, - 0, 0, 84, 35, 85, 0, 537, 46, 34, 51, - 186, 0, 0, 0, 46, 34, 51, 49, 52, 50, - 35, 0, 46, 34, 51, 46, 34, 51, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 537, - 49, 52, 50, 0, 0, 0, 537, 46, 34, 51, - 537, 0, 0, 35, 537, 0, 35, 49, 52, 50, - 35, 0, 0, 186, 35, 0, 0, 0, 35, 0, - 46, 34, 51, 0, 0, 35, 0, 0, 35, 0, - 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, - 49, 52, 50, 49, 52, 50, 0, 49, 52, 50, - 0, 49, 52, 50, 0, 49, 52, 50, 0, 0, - 258, 257, 49, 52, 50, 49, 52, 50, 35, 0, - 46, 34, 51, 46, 34, 51, 0, 46, 34, 51, - 35, 46, 34, 51, 0, 46, 34, 51, 35, 0, - 0, 35, 46, 34, 51, 46, 34, 51, 0, 0, - 253, 252, 0, 0, 35, 49, 52, 50, 0, 0, - 0, 186, 253, 252, 0, 0, 0, 49, 52, 50, - 258, 257, 0, 258, 257, 49, 52, 50, 49, 52, - 50, 0, 0, 0, 0, 46, 34, 51, 0, 0, - 155, 49, 52, 50, 0, 0, 0, 46, 34, 51, - 156, 0, 0, 0, 157, 46, 34, 51, 46, 34, - 51, 0, 0, 158, 0, 159, 0, 0, 0, 0, - 0, 46, 34, 51, 0, 0, 160, 0, 161, 64, - 0, 0, 0, 35, 0, 0, 162, 0, 0, 163, - 65, 0, 0, 0, 0, 164, 0, 0, 0, 0, - 0, 165, 0, 0, 0, 0, 0, 0, 0, 155, - 0, 0, 0, 0, 0, 253, 252, 166, 0, 156, - 49, 52, 50, 157, 0, 0, 0, 0, 0, 0, - 0, 0, 158, 0, 159, 0, 0, 322, 0, 0, - 0, 0, 0, 0, 0, 160, 0, 161, 64, 0, - 46, 34, 51, 0, 0, 162, 0, 0, 163, 65, - 0, 0, 155, 0, 164, 0, 0, 0, 0, 0, - 165, 0, 156, 0, 0, 0, 157, 0, 0, 0, - 0, 0, 0, 0, 0, 158, 166, 159, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 160, 0, - 161, 64, 0, 0, 0, 0, 0, 0, 162, 0, - 0, 163, 65, 0, 0, 0, 0, 164, 0, 0, - 0, 0, 0, 165, 0, 30, 31, 0, 0, 0, - 0, 0, 0, 0, 0, 33, 0, 0, 0, 166, - 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, - 0, 0, 0, 0, 0, 0, 521, 0, 0, 0, - 45, 0, 0, 0, 0, 0, 0, 30, 31, 0, - 0, 0, 0, 0, 0, 0, 0, 33, 53, 49, - 52, 50, 0, 54, 35, 0, 0, 0, 36, 37, - 0, 38, 0, 0, 44, 56, 32, 0, 42, 0, - 0, 41, 45, 0, 0, 0, 0, 0, 0, 46, - 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, - 53, 49, 52, 50, 0, 54, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 44, 56, 32, 0, - 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, - 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 30, 31, 0, 0, 0, 0, 0, - 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, - 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, - 0, 0, 0, 0, 42, 0, 0, 0, 45, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, - 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 44, 56, 32, 0, 0, 0, 0, 41, - 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, - 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, - 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, - 36, 37, 0, 38, 0, 0, 0, 0, 0, 0, - 521, 0, 0, 0, 45, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 53, 49, 52, 50, 0, 54, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 44, 56, - 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, - 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 519, 0, 30, 31, 0, - 0, 0, 0, 0, 0, 0, 0, 221, 0, 0, - 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, - 0, 38, 0, 0, 0, 0, 0, 0, 521, 0, - 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 53, 522, 524, 523, 0, 54, 0, 0, 0, 0, - 230, 0, 0, 0, 0, 0, 44, 56, 32, 216, - 224, 0, 0, 41, 0, 0, 520, 0, 0, 0, - 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 519, 0, 30, 31, 0, 0, 0, - 0, 0, 0, 0, 0, 221, 0, 0, 0, 0, - 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, - 0, 0, 0, 0, 0, 0, 521, 0, 0, 0, - 45, 0, 0, 0, 0, 0, 0, 0, 585, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 53, 522, - 524, 523, 0, 54, 0, 0, 0, 0, 230, 0, - 0, 0, 0, 0, 44, 56, 32, 216, 224, 0, - 0, 41, 0, 0, 520, 0, 0, 0, 0, 46, - 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 519, 0, 30, 31, 0, 0, 0, 0, 0, - 0, 0, 0, 221, 0, 0, 0, 0, 0, 0, - 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, - 0, 0, 0, 0, 521, 0, 0, 0, 45, 0, - 0, 0, 0, 0, 0, 0, 588, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 53, 522, 524, 523, - 0, 54, 0, 0, 0, 0, 230, 0, 0, 0, - 0, 0, 44, 56, 32, 216, 224, 0, 0, 41, - 0, 0, 520, 0, 0, 0, 0, 46, 34, 51, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, - 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, - 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, - 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, - 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, - 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, - 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, - 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, - 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, - 0, 0, 0, 0, 0, 0, -141, 0, 0, 0, - 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, - 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, - 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, - 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, - 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, - 0, 55, 0, 57, 0, 58, 0, 0, 0, 0, - 44, 56, 32, 0, 0, 0, 0, 41, 0, 0, - 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, - 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, - 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, - 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, - 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, - 57, 285, 58, 0, 0, 0, 0, 44, 56, 32, - 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 492, 0, 0, 29, 30, 31, - 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, - 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, - 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, - 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 493, 0, 0, 0, 0, 0, 0, 0, - 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, - 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, - 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 500, 0, 0, 29, 30, 31, - 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, - 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, - 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, - 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 503, 0, 0, 0, 0, 0, 0, 0, - 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, - 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, - 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 492, 0, 0, 29, 30, 31, - 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, - 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, - 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, - 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 498, 0, 0, 0, 0, 0, 0, 0, - 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, - 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, - 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 500, 0, 0, 29, 30, 31, - 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, - 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, - 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, - 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 501, 0, 0, 0, 0, 0, 0, 0, - 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, - 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, - 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 29, 30, 31, 0, 0, 0, - 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, - 0, 0, 35, 222, 0, 0, 223, 37, 0, 38, - 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, - 45, 0, 0, 0, 47, 0, 48, 0, 0, 0, - 0, 0, 0, 0, 226, 0, 0, 0, 53, 49, - 52, 50, 227, 54, 0, 55, 229, 57, 0, 58, - 0, 232, 0, 0, 44, 56, 32, 0, 0, 0, - 0, 41, 0, 0, 0, 0, 0, 0, 0, 46, - 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, - 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, - 222, 0, 0, 603, 650, 0, 38, 0, 0, 0, - 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, - 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, - 0, 226, 0, 0, 0, 53, 49, 52, 50, 227, - 54, 0, 55, 229, 57, 0, 58, 0, 232, 0, - 0, 44, 56, 32, 0, 0, 0, 0, 41, 0, - 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, - 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, - 0, 0, 0, 0, 0, 0, 35, 222, 0, 0, - 603, 37, 0, 38, 0, 0, 0, 39, 0, 40, - 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, - 48, 0, 0, 0, 0, 0, 0, 0, 226, 0, - 0, 0, 53, 49, 52, 50, 227, 54, 0, 55, - 229, 57, 0, 58, 0, 232, 0, 0, 44, 56, - 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, - 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 112, 113, 0, 0, - 115, 117, 118, 0, 0, 119, 0, 120, 0, 0, - 0, 123, 124, 125, 0, 0, 0, 0, 0, 0, - 35, 126, 127, 128, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 130, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 133, 0, 0, 0, 0, 0, 0, 49, 52, 50, - 134, 135, 136, 0, 138, 139, 140, 141, 142, 143, - 0, 0, 131, 137, 122, 114, 129, 116, 132, 0, - 0, 0, 121, 0, 0, 0, 0, 46, 34, 51, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, - 112, 113, 0, 0, 115, 117, 118, 0, 0, 119, - 0, 120, 0, 0, 0, 123, 124, 125, 0, 0, - 0, 0, 0, 0, 35, 126, 127, 128, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 130, 0, - 0, 0, 399, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 133, 0, 0, 0, 0, 0, - 401, 49, 52, 50, 134, 135, 136, 0, 138, 139, - 140, 141, 142, 143, 0, 0, 131, 137, 122, 114, - 129, 116, 132, 0, 0, 0, 121, 0, 0, 0, - 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 111, 112, 113, 0, 0, 115, 117, - 118, 0, 0, 119, 0, 120, 0, 0, 0, 123, - 124, 125, 0, 0, 0, 0, 0, 0, 35, 126, - 127, 128, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 130, 0, 0, 0, 399, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 133, 0, - 0, 0, 0, 0, 401, 49, 52, 50, 134, 135, - 136, 0, 138, 139, 140, 141, 142, 143, 0, 0, - 131, 137, 122, 114, 129, 116, 132, 0, 0, 0, - 121, 0, 0, 0, 0, 46, 377, 384, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 111, 112, 113, - 0, 0, 115, 117, 118, 0, 0, 119, 0, 120, - 0, 0, 0, 123, 124, 125, 0, 0, 0, 0, - 0, 0, 35, 126, 127, 128, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 130, 0, 0, 0, - 399, 0, 0, 0, 0, 0, 0, 0, 400, 0, - 0, 0, 133, 0, 0, 0, 0, 0, 401, 49, - 52, 50, 134, 135, 136, 0, 138, 139, 140, 141, - 142, 143, 0, 0, 131, 137, 122, 114, 129, 116, - 132, 0, 0, 0, 121, 0, 0, 0, 0, 46, - 377, 384, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 215, 0, 0, 0, 0, 217, 0, 29, 30, - 31, 219, 0, 0, 0, 0, 0, 0, 220, 33, - 0, 0, 0, 0, 0, 0, 35, 222, 0, 0, - 223, 37, 0, 38, 0, 0, 0, 39, 0, 40, - 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, - 48, 0, 0, 0, 0, 0, 225, 0, 226, 0, - 0, 0, 53, 49, 52, 50, 227, 54, 228, 55, - 229, 57, 230, 58, 231, 232, 0, 0, 44, 56, - 32, 216, 224, 218, 0, 41, 0, 0, 0, 0, - 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 215, 0, 0, 0, 0, - 217, 0, 29, 30, 31, 219, 0, 0, 0, 0, - 0, 0, 220, 221, 0, 0, 0, 0, 0, 0, - 35, 222, 0, 0, 223, 37, 0, 38, 0, 0, - 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, - 0, 0, 47, 0, 48, 0, 0, 0, 0, 0, - 225, 0, 226, 0, 0, 0, 53, 49, 52, 50, - 227, 54, 228, 55, 229, 57, 230, 58, 231, 232, - 0, 0, 44, 56, 32, 216, 224, 218, 0, 41, - 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 607, - 112, 113, 0, 0, 609, 117, 611, 30, 31, 612, - 0, 120, 0, 0, 0, 123, 614, 615, 0, 0, - 0, 0, 0, 0, 35, 616, 127, 128, 223, 37, - 0, 38, 0, 0, 0, 39, 0, 40, 618, 43, - 0, 0, 620, 0, 0, 0, 47, 0, 48, 0, - 0, 0, 0, 0, 621, 0, 226, 0, 0, 0, - 622, 49, 52, 50, 623, 624, 625, 55, 627, 628, - 629, 630, 631, 632, 0, 0, 619, 626, 613, 608, - 617, 610, 132, 41, 0, 0, 121, 0, 0, 0, - 0, 46, 377, 384, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 368, 112, 113, 0, 0, 370, 117, - 372, 30, 31, 373, 0, 120, 0, 0, 0, 123, - 375, 376, 0, 0, 0, 0, 0, 0, 35, 378, - 127, 128, 223, 37, 0, 38, 0, 0, 0, 39, - 0, 40, 380, 43, 0, 0, 382, 0, 0, 0, - 47, 0, 48, 0, -288, 0, 0, 0, 383, 0, - 226, 0, 0, 0, 385, 49, 52, 50, 386, 387, - 388, 55, 390, 391, 392, 393, 394, 395, 0, 0, - 381, 389, 374, 369, 379, 371, 132, 41, 0, 0, - 121, 0, 0, 0, 0, 46, 377, 384, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - - 543, 185, 640, 647, 642, 643, 504, 489, 397, 451, - 655, 657, 669, 670, 154, 680, 658, 448, 686, 316, - 185, 16, 256, 536, 251, 599, 570, 633, 572, 590, - 597, 144, 323, 540, 457, 150, 266, 473, 190, 441, - 350, 445, 251, 192, 256, 437, 429, 354, 208, 240, - 185, 316, 251, 256, 185, 404, 448, 448, 316, 533, - 566, 470, 466, 180, 451, 451, 462, 0, 62, 334, - 510, 516, 62, 0, 0, 325, 62, 299, 316, 0, - 459, 298, 397, 334, 0, 147, 461, 208, 499, 0, - 152, 208, 502, 167, 600, 479, 673, 672, 518, 173, - 175, 515, 316, 674, 208, 397, 316, 518, 316, 407, - 208, 0, 190, 0, 321, 208, 656, 352, 62, 249, - 464, 62, 62, 184, 509, 62, 62, 463, 463, 62, - 208, 508, 421, 208, 62, 208, 184, 356, 245, 358, - 405, 242, 263, 280, 62, 62, 62, 506, 284, 302, - 62, 301, 507, 208, 317, 208, 208, 62, 208, 62, - 343, 184, 300, 413, 348, 365, 62, 0, 62, 190, - 518, 62, 99, 62, 100, 578, 86, 90, 346, 62, - 208, 208, 319, 91, 344, 62, 62, 62, 413, 62, - 93, 94, 95, 473, 96, 468, 514, 62, 189, 62, - 150, 414, 97, 92, 208, 62, 472, 464, 182, 62, - 62, 208, 497, 62, 62, 397, 496, 250, 365, 62, - 104, 102, 208, 263, 208, 98, 414, 263, 169, 172, - 365, 208, 487, 62, 359, 511, 62, 250, 463, 208, - 62, 398, 464, 208, 62, 62, 606, 63, 72, 190, - 150, 208, 411, 62, 518, 69, 62, 208, 416, 582, - 365, 365, 79, 62, 62, 70, 62, 62, 483, 71, - 0, 284, 74, 0, 0, 62, 208, 208, 423, 307, - 284, 0, 62, 0, 287, 426, 108, 284, 0, 292, - 62, 62, 0, 0, 0, 284, 284, 303, 304, 62, - 312, 0, 62, 312, 284, 284, 305, 284, 284, 312, - 62, 0, 312, 309, 284, 284, 110, 284, 62, 312, - 0, 594, 333, 284, 284, 340, 578, 330, 653, 0, - 518, 331, 0, 327, 311, 586, 0, 589, 0, 527, - 0, 314, 0, 0, 518, 0, 518, 444, 447, 0, - 489, 517, 528, 527, 0, 527, 547, 548, 549, 550, - 554, 551, 552, 0, 0, 517, 528, 517, 528, 0, - 0, 0, 602, 0, 0, 0, 0, 0, 0, 0, - 108, 604, 605, 547, 548, 549, 550, 554, 551, 552, - 594, 0, 0, 0, 0, 0, 0, 0, 0, 595, - 596, 547, 548, 549, 550, 554, 551, 552, 0, 0, - 110, 179, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 602, 0, 0, 0, 0, 0, - 0, 0, 0, 651, 652, 547, 548, 549, 550, 554, - 551, 552, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 -}; - -const short QQmlJSGrammar::action_check [] = { - 7, 33, 60, 60, 7, 36, 7, 7, 60, 7, - 36, 36, 7, 7, 60, 36, 55, 7, 33, 55, - 33, 7, 36, 7, 36, 36, 7, 37, 36, 7, - 36, 7, 7, 7, 33, 7, 33, 33, 47, 66, - 17, 34, 47, 66, 29, 37, 29, 29, 17, 29, - 29, 17, 5, 5, 5, 20, 60, 7, 60, 8, - 33, 7, 33, 33, 55, 33, 55, 7, 55, 36, - 33, 33, 60, 33, 7, 36, 33, 36, 36, 33, - 8, 7, 2, 7, 1, 8, 1, 8, 1, 36, - 8, 2, 8, -1, 2, -1, 60, -1, 33, 55, - 55, 7, 36, 1, 48, 0, 7, 36, 2, 36, - 48, 36, 60, 17, 33, 60, 48, 16, 8, 55, - 61, 60, 36, 7, 36, 8, 36, 36, 8, 60, - 8, 8, 6, 8, 15, -1, 79, 79, 8, 61, - 77, 48, 15, 7, 8, 8, 20, 15, 15, 79, - -1, 24, 8, 34, 8, 8, -1, -1, 61, 61, - 62, 61, 62, 8, 61, 62, 34, 34, 50, 61, - 62, 50, 54, 8, 79, 54, 8, 60, 56, 10, - 60, 56, 40, 60, 8, 61, 62, 8, 40, 12, - 60, 7, 55, 51, 40, 61, 62, 93, 94, 51, - 61, 62, 31, 7, 60, 51, 60, 60, 93, 94, - 15, 56, 61, 62, 61, 62, 42, 61, 62, 24, - 15, 56, 61, 62, 55, 61, 62, 53, 60, 33, - 29, 29, 7, 12, 57, 56, 25, 61, 27, 34, - 63, 36, 25, 7, 27, 61, 62, 29, 25, 38, - 27, 25, 25, 27, 27, 38, 25, 29, 27, 95, - -1, 38, 29, -1, 38, 38, 29, -1, 12, 38, - 36, 29, 25, -1, 27, -1, 75, 75, 57, 40, - 15, 8, -1, 8, 63, 38, 61, 62, 87, 87, - 51, -1, 96, 75, -1, 61, 62, 61, 62, 34, - -1, 36, 15, 75, 8, 87, -1, -1, 75, 25, - -1, 27, 75, 57, 25, 87, 27, 75, -1, 63, - 87, 34, 38, 36, 87, 61, 62, 38, 25, 87, - 27, 25, -1, 27, 61, 62, 61, 62, 25, -1, - 27, 38, 15, 25, 38, 27, 15, 15, 18, 19, - -1, 38, 18, 19, 90, 47, 38, 61, 62, -1, - 33, 34, -1, 36, 33, 34, 34, 36, 36, 61, - 62, -1, 23, 24, -1, 45, 46, -1, -1, 45, - 46, 32, 23, 24, 35, -1, 37, -1, 23, 24, - -1, 32, 23, 24, 35, -1, 37, 32, 23, 24, - 35, 32, 37, 95, 35, -1, 37, 32, 23, 24, - 35, -1, 37, -1, -1, -1, 31, 32, 23, 24, - 35, 29, 37, -1, -1, 29, 31, 32, -1, -1, - 35, -1, 37, 29, 23, 24, 29, -1, -1, 29, - 18, 19, 31, 32, 29, -1, 35, 29, 37, -1, - 100, 101, 102, 103, 104, 105, -1, -1, 66, 67, - 68, -1, 66, 67, 68, -1, -1, 45, 46, -1, - 66, 67, 68, 66, 67, 68, 66, 67, 68, -1, - 29, 66, 67, 68, 66, 67, 68, 29, 96, 97, - 98, -1, 96, 97, 98, 29, -1, -1, 29, -1, - 96, 97, 98, 96, 97, 98, 96, 97, 98, -1, - -1, 96, 97, 98, 96, 97, 98, 66, 67, 68, - 29, -1, -1, -1, 66, 67, 68, -1, -1, -1, - 23, 24, 66, 67, 68, 66, 67, 68, 31, 32, - -1, -1, 35, 29, 37, -1, 15, 96, 97, 98, - 36, -1, -1, -1, 96, 97, 98, 66, 67, 68, - 29, -1, 96, 97, 98, 96, 97, 98, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, - 66, 67, 68, -1, -1, -1, 15, 96, 97, 98, - 15, -1, -1, 29, 15, -1, 29, 66, 67, 68, - 29, -1, -1, 36, 29, -1, -1, -1, 29, -1, - 96, 97, 98, -1, -1, 29, -1, -1, 29, -1, - -1, -1, -1, -1, -1, -1, -1, 96, 97, 98, - 66, 67, 68, 66, 67, 68, -1, 66, 67, 68, - -1, 66, 67, 68, -1, 66, 67, 68, -1, -1, - 61, 62, 66, 67, 68, 66, 67, 68, 29, -1, - 96, 97, 98, 96, 97, 98, -1, 96, 97, 98, - 29, 96, 97, 98, -1, 96, 97, 98, 29, -1, - -1, 29, 96, 97, 98, 96, 97, 98, -1, -1, - 61, 62, -1, -1, 29, 66, 67, 68, -1, -1, - -1, 36, 61, 62, -1, -1, -1, 66, 67, 68, - 61, 62, -1, 61, 62, 66, 67, 68, 66, 67, - 68, -1, -1, -1, -1, 96, 97, 98, -1, -1, - 3, 66, 67, 68, -1, -1, -1, 96, 97, 98, - 13, -1, -1, -1, 17, 96, 97, 98, 96, 97, - 98, -1, -1, 26, -1, 28, -1, -1, -1, -1, - -1, 96, 97, 98, -1, -1, 39, -1, 41, 42, - -1, -1, -1, 29, -1, -1, 49, -1, -1, 52, - 53, -1, -1, -1, -1, 58, -1, -1, -1, -1, - -1, 64, -1, -1, -1, -1, -1, -1, -1, 3, - -1, -1, -1, -1, -1, 61, 62, 80, -1, 13, - 66, 67, 68, 17, -1, -1, -1, -1, -1, -1, - -1, -1, 26, -1, 28, -1, -1, 31, -1, -1, - -1, -1, -1, -1, -1, 39, -1, 41, 42, -1, - 96, 97, 98, -1, -1, 49, -1, -1, 52, 53, - -1, -1, 3, -1, 58, -1, -1, -1, -1, -1, - 64, -1, 13, -1, -1, -1, 17, -1, -1, -1, - -1, -1, -1, -1, -1, 26, 80, 28, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 39, -1, - 41, 42, -1, -1, -1, -1, -1, -1, 49, -1, - -1, 52, 53, -1, -1, -1, -1, 58, -1, -1, - -1, -1, -1, 64, -1, 12, 13, -1, -1, -1, - -1, -1, -1, -1, -1, 22, -1, -1, -1, 80, - -1, -1, 29, -1, -1, -1, 33, 34, -1, 36, - -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, - 47, -1, -1, -1, -1, -1, -1, 12, 13, -1, - -1, -1, -1, -1, -1, -1, -1, 22, 65, 66, - 67, 68, -1, 70, 29, -1, -1, -1, 33, 34, - -1, 36, -1, -1, 81, 82, 83, -1, 43, -1, - -1, 88, 47, -1, -1, -1, -1, -1, -1, 96, - 97, 98, -1, -1, -1, -1, -1, -1, -1, -1, - 65, 66, 67, 68, -1, 70, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 81, 82, 83, -1, - -1, -1, -1, 88, -1, -1, -1, -1, -1, -1, - -1, 96, 97, 98, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 12, 13, -1, -1, -1, -1, -1, - -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, - 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, - -1, -1, -1, -1, 43, -1, -1, -1, 47, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, - -1, 70, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 81, 82, 83, -1, -1, -1, -1, 88, - -1, -1, -1, -1, -1, -1, -1, 96, 97, 98, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, - 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, - -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, - 33, 34, -1, 36, -1, -1, -1, -1, -1, -1, - 43, -1, -1, -1, 47, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 65, 66, 67, 68, -1, 70, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 81, 82, - 83, -1, -1, -1, -1, 88, -1, -1, -1, -1, - -1, -1, -1, 96, 97, 98, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 10, -1, 12, 13, -1, - -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, - -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, - -1, 36, -1, -1, -1, -1, -1, -1, 43, -1, - -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 65, 66, 67, 68, -1, 70, -1, -1, -1, -1, - 75, -1, -1, -1, -1, -1, 81, 82, 83, 84, - 85, -1, -1, 88, -1, -1, 91, -1, -1, -1, - -1, 96, 97, 98, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 10, -1, 12, 13, -1, -1, -1, - -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, - -1, -1, 29, -1, -1, -1, 33, 34, -1, 36, - -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, - 47, -1, -1, -1, -1, -1, -1, -1, 55, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, - 67, 68, -1, 70, -1, -1, -1, -1, 75, -1, - -1, -1, -1, -1, 81, 82, 83, 84, 85, -1, - -1, 88, -1, -1, 91, -1, -1, -1, -1, 96, - 97, 98, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 10, -1, 12, 13, -1, -1, -1, -1, -1, - -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, - 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, - -1, -1, -1, -1, 43, -1, -1, -1, 47, -1, - -1, -1, -1, -1, -1, -1, 55, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, - -1, 70, -1, -1, -1, -1, 75, -1, -1, -1, - -1, -1, 81, 82, 83, 84, 85, -1, -1, 88, - -1, -1, 91, -1, -1, -1, -1, 96, 97, 98, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 11, - 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, - 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, - -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, - 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, - -1, 53, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, - 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, - 82, 83, -1, -1, -1, -1, 88, -1, -1, -1, - -1, -1, -1, -1, 96, 97, 98, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, - 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, - -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, - -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, - -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, - 51, -1, 53, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, - -1, 72, -1, 74, -1, 76, -1, -1, -1, -1, - 81, 82, 83, -1, -1, -1, -1, 88, -1, -1, - -1, -1, -1, -1, -1, 96, 97, 98, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 11, 12, 13, - -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, - -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, - 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, - 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, - 74, 75, 76, -1, -1, -1, -1, 81, 82, 83, - -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, - -1, -1, 96, 97, 98, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, - -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, - -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, - 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, - 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, - -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, - -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, - 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, - -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, - -1, -1, 96, 97, 98, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, - -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, - -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, - 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, - 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, - -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, - -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, - 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, - -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, - -1, -1, 96, 97, 98, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, - -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, - -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, - 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, - 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, - -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, - -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, - 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, - -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, - -1, -1, 96, 97, 98, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, - -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, - -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, - 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, - 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, - -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, - -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, - 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, - -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, - -1, -1, 96, 97, 98, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 11, 12, 13, -1, -1, -1, - -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, - -1, -1, 29, 30, -1, -1, 33, 34, -1, 36, - -1, -1, -1, 40, -1, 42, 43, 44, -1, -1, - 47, -1, -1, -1, 51, -1, 53, -1, -1, -1, - -1, -1, -1, -1, 61, -1, -1, -1, 65, 66, - 67, 68, 69, 70, -1, 72, 73, 74, -1, 76, - -1, 78, -1, -1, 81, 82, 83, -1, -1, -1, - -1, 88, -1, -1, -1, -1, -1, -1, -1, 96, - 97, 98, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, - -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, - 30, -1, -1, 33, 34, -1, 36, -1, -1, -1, - 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, - -1, 51, -1, 53, -1, -1, -1, -1, -1, -1, - -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, - 70, -1, 72, 73, 74, -1, 76, -1, 78, -1, - -1, 81, 82, 83, -1, -1, -1, -1, 88, -1, - -1, -1, -1, -1, -1, -1, 96, 97, 98, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 11, 12, - 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, - -1, -1, -1, -1, -1, -1, 29, 30, -1, -1, - 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, - 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, - 53, -1, -1, -1, -1, -1, -1, -1, 61, -1, - -1, -1, 65, 66, 67, 68, 69, 70, -1, 72, - 73, 74, -1, 76, -1, 78, -1, -1, 81, 82, - 83, -1, -1, -1, -1, 88, -1, -1, -1, -1, - -1, -1, -1, 96, 97, 98, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 4, 5, 6, -1, -1, - 9, 10, 11, -1, -1, 14, -1, 16, -1, -1, - -1, 20, 21, 22, -1, -1, -1, -1, -1, -1, - 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 43, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 59, -1, -1, -1, -1, -1, -1, 66, 67, 68, - 69, 70, 71, -1, 73, 74, 75, 76, 77, 78, - -1, -1, 81, 82, 83, 84, 85, 86, 87, -1, - -1, -1, 91, -1, -1, -1, -1, 96, 97, 98, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, - 5, 6, -1, -1, 9, 10, 11, -1, -1, 14, - -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, - -1, -1, -1, -1, 29, 30, 31, 32, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 43, -1, - -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, - 65, 66, 67, 68, 69, 70, 71, -1, 73, 74, - 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, - 85, 86, 87, -1, -1, -1, 91, -1, -1, -1, - -1, 96, 97, 98, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 4, 5, 6, -1, -1, 9, 10, - 11, -1, -1, 14, -1, 16, -1, -1, -1, 20, - 21, 22, -1, -1, -1, -1, -1, -1, 29, 30, - 31, 32, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 59, -1, - -1, -1, -1, -1, 65, 66, 67, 68, 69, 70, - 71, -1, 73, 74, 75, 76, 77, 78, -1, -1, - 81, 82, 83, 84, 85, 86, 87, -1, -1, -1, - 91, -1, -1, -1, -1, 96, 97, 98, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, - -1, -1, 9, 10, 11, -1, -1, 14, -1, 16, - -1, -1, -1, 20, 21, 22, -1, -1, -1, -1, - -1, -1, 29, 30, 31, 32, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, - 47, -1, -1, -1, -1, -1, -1, -1, 55, -1, - -1, -1, 59, -1, -1, -1, -1, -1, 65, 66, - 67, 68, 69, 70, 71, -1, 73, 74, 75, 76, - 77, 78, -1, -1, 81, 82, 83, 84, 85, 86, - 87, -1, -1, -1, 91, -1, -1, -1, -1, 96, - 97, 98, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 4, -1, -1, -1, -1, 9, -1, 11, 12, - 13, 14, -1, -1, -1, -1, -1, -1, 21, 22, - -1, -1, -1, -1, -1, -1, 29, 30, -1, -1, - 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, - 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, - 53, -1, -1, -1, -1, -1, 59, -1, 61, -1, - -1, -1, 65, 66, 67, 68, 69, 70, 71, 72, - 73, 74, 75, 76, 77, 78, -1, -1, 81, 82, - 83, 84, 85, 86, -1, 88, -1, -1, -1, -1, - -1, -1, -1, 96, 97, 98, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, - 9, -1, 11, 12, 13, 14, -1, -1, -1, -1, - -1, -1, 21, 22, -1, -1, -1, -1, -1, -1, - 29, 30, -1, -1, 33, 34, -1, 36, -1, -1, - -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, - -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, - 59, -1, 61, -1, -1, -1, 65, 66, 67, 68, - 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, - -1, -1, 81, 82, 83, 84, 85, 86, -1, 88, - -1, -1, -1, -1, -1, -1, -1, 96, 97, 98, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, - 5, 6, -1, -1, 9, 10, 11, 12, 13, 14, - -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, - -1, -1, -1, -1, 29, 30, 31, 32, 33, 34, - -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, - -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, - -1, -1, -1, -1, 59, -1, 61, -1, -1, -1, - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, - 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, - 85, 86, 87, 88, -1, -1, 91, -1, -1, -1, - -1, 96, 97, 98, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 4, 5, 6, -1, -1, 9, 10, - 11, 12, 13, 14, -1, 16, -1, -1, -1, 20, - 21, 22, -1, -1, -1, -1, -1, -1, 29, 30, - 31, 32, 33, 34, -1, 36, -1, -1, -1, 40, - -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, - 51, -1, 53, -1, 55, -1, -1, -1, 59, -1, - 61, -1, -1, -1, 65, 66, 67, 68, 69, 70, - 71, 72, 73, 74, 75, 76, 77, 78, -1, -1, - 81, 82, 83, 84, 85, 86, 87, 88, -1, -1, - 91, -1, -1, -1, -1, 96, 97, 98, -1, -1, - -1, -1, -1, -1, -1, -1, -1, - - 18, 18, 32, 18, 32, 18, 3, 43, 18, 25, - 22, 22, 14, 14, 78, 18, 9, 3, 18, 3, - 18, 3, 18, 32, 18, 32, 32, 22, 18, 18, - 22, 3, 3, 18, 106, 43, 3, 18, 18, 101, - 3, 3, 18, 18, 18, 104, 3, 3, 18, 18, - 18, 3, 18, 18, 18, 43, 3, 3, 3, 32, - 14, 3, 3, 3, 25, 25, 25, -1, 55, 18, - 57, 2, 55, -1, -1, 2, 55, 60, 3, -1, - 18, 60, 18, 18, -1, 43, 25, 18, 43, -1, - 43, 18, 43, 43, 18, 43, 11, 12, 14, 43, - 43, 4, 3, 19, 18, 18, 3, 14, 3, 45, - 18, -1, 18, -1, 2, 18, 23, 2, 55, 2, - 57, 55, 55, 57, 57, 55, 55, 57, 57, 55, - 18, 57, 45, 18, 55, 18, 57, 2, 46, 2, - 2, 47, 2, 55, 55, 55, 55, 57, 60, 60, - 55, 60, 57, 18, 79, 18, 18, 55, 18, 55, - 95, 57, 60, 14, 2, 2, 55, -1, 55, 18, - 14, 55, 61, 55, 61, 19, 60, 59, 79, 55, - 18, 18, 79, 59, 79, 55, 55, 55, 14, 55, - 60, 60, 60, 18, 60, 2, 110, 55, 47, 55, - 43, 52, 60, 59, 18, 55, 2, 57, 51, 55, - 55, 18, 39, 55, 55, 18, 43, 4, 2, 55, - 65, 67, 18, 2, 18, 61, 52, 2, 69, 71, - 2, 18, 46, 55, 18, 57, 55, 4, 57, 18, - 55, 44, 57, 18, 55, 55, 18, 58, 58, 18, - 43, 18, 46, 55, 14, 57, 55, 18, 51, 19, - 2, 2, 61, 55, 55, 57, 55, 55, 93, 57, - -1, 60, 63, -1, -1, 55, 18, 18, 47, 68, - 60, -1, 55, -1, 64, 46, 18, 60, -1, 62, - 55, 55, -1, -1, -1, 60, 60, 62, 62, 55, - 55, -1, 55, 55, 60, 60, 62, 60, 60, 55, - 55, -1, 55, 66, 60, 60, 48, 60, 55, 55, - -1, 14, 77, 60, 60, 77, 19, 72, 21, -1, - 14, 77, -1, 70, 77, 5, -1, 5, -1, 23, - -1, 77, -1, -1, 14, -1, 14, 89, 89, -1, - 43, 35, 36, 23, -1, 23, 25, 26, 27, 28, - 29, 30, 31, -1, -1, 35, 36, 35, 36, -1, - -1, -1, 14, -1, -1, -1, -1, -1, -1, -1, - 18, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 14, -1, -1, -1, -1, -1, -1, -1, -1, 23, - 24, 25, 26, 27, 28, 29, 30, 31, -1, -1, - 48, 49, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 14, -1, -1, -1, -1, -1, - -1, -1, -1, 23, 24, 25, 26, 27, 28, 29, - 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1 -}; - -QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsgrammar_p.h b/src/qml/parser/qqmljsgrammar_p.h deleted file mode 100644 index 9d028f2191..0000000000 --- a/src/qml/parser/qqmljsgrammar_p.h +++ /dev/null @@ -1,215 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -// This file was generated by qlalr - DO NOT EDIT! -#ifndef QQMLJSGRAMMAR_P_H -#define QQMLJSGRAMMAR_P_H - -#include <QtCore/qglobal.h> - -QT_BEGIN_NAMESPACE - -class QQmlJSGrammar -{ -public: - enum VariousConstants { - EOF_SYMBOL = 0, - REDUCE_HERE = 107, - SHIFT_THERE = 106, - T_AND = 1, - T_AND_AND = 2, - T_AND_EQ = 3, - T_AS = 95, - T_AUTOMATIC_SEMICOLON = 62, - T_BREAK = 4, - T_CASE = 5, - T_CATCH = 6, - T_COLON = 7, - T_COMMA = 8, - T_COMMENT = 89, - T_COMPATIBILITY_SEMICOLON = 90, - T_CONST = 84, - T_CONTINUE = 9, - T_DEBUGGER = 86, - T_DEFAULT = 10, - T_DELETE = 11, - T_DIVIDE_ = 12, - T_DIVIDE_EQ = 13, - T_DO = 14, - T_DOT = 15, - T_ELSE = 16, - T_ENUM = 91, - T_EQ = 17, - T_EQ_EQ = 18, - T_EQ_EQ_EQ = 19, - T_ERROR = 99, - T_FALSE = 83, - T_FEED_JS_EXPRESSION = 103, - T_FEED_JS_PROGRAM = 105, - T_FEED_JS_SOURCE_ELEMENT = 104, - T_FEED_JS_STATEMENT = 102, - T_FEED_UI_OBJECT_MEMBER = 101, - T_FEED_UI_PROGRAM = 100, - T_FINALLY = 20, - T_FOR = 21, - T_FUNCTION = 22, - T_GE = 23, - T_GET = 97, - T_GT = 24, - T_GT_GT = 25, - T_GT_GT_EQ = 26, - T_GT_GT_GT = 27, - T_GT_GT_GT_EQ = 28, - T_IDENTIFIER = 29, - T_IF = 30, - T_IMPORT = 93, - T_IN = 31, - T_INSTANCEOF = 32, - T_LBRACE = 33, - T_LBRACKET = 34, - T_LE = 35, - T_LET = 85, - T_LPAREN = 36, - T_LT = 37, - T_LT_LT = 38, - T_LT_LT_EQ = 39, - T_MINUS = 40, - T_MINUS_EQ = 41, - T_MINUS_MINUS = 42, - T_MULTILINE_STRING_LITERAL = 88, - T_NEW = 43, - T_NOT = 44, - T_NOT_EQ = 45, - T_NOT_EQ_EQ = 46, - T_NULL = 81, - T_NUMERIC_LITERAL = 47, - T_ON = 96, - T_OR = 48, - T_OR_EQ = 49, - T_OR_OR = 50, - T_PLUS = 51, - T_PLUS_EQ = 52, - T_PLUS_PLUS = 53, - T_PRAGMA = 94, - T_PROPERTY = 66, - T_PUBLIC = 92, - T_QUESTION = 54, - T_RBRACE = 55, - T_RBRACKET = 56, - T_READONLY = 68, - T_REMAINDER = 57, - T_REMAINDER_EQ = 58, - T_RESERVED_WORD = 87, - T_RETURN = 59, - T_RPAREN = 60, - T_SEMICOLON = 61, - T_SET = 98, - T_SIGNAL = 67, - T_STAR = 63, - T_STAR_EQ = 64, - T_STRING_LITERAL = 65, - T_SWITCH = 69, - T_THIS = 70, - T_THROW = 71, - T_TILDE = 72, - T_TRUE = 82, - T_TRY = 73, - T_TYPEOF = 74, - T_VAR = 75, - T_VOID = 76, - T_WHILE = 77, - T_WITH = 78, - T_XOR = 79, - T_XOR_EQ = 80, - - ACCEPT_STATE = 691, - RULE_COUNT = 369, - STATE_COUNT = 692, - TERMINAL_COUNT = 108, - NON_TERMINAL_COUNT = 112, - - GOTO_INDEX_OFFSET = 692, - GOTO_INFO_OFFSET = 3357, - GOTO_CHECK_OFFSET = 3357 - }; - - static const char *const spell[]; - static const short lhs[]; - static const short rhs[]; - static const short goto_default[]; - static const short action_default[]; - static const short action_index[]; - static const short action_info[]; - static const short action_check[]; - - static inline int nt_action (int state, int nt) - { - const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; - if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) - return goto_default [nt]; - - return action_info [GOTO_INFO_OFFSET + yyn]; - } - - static inline int t_action (int state, int token) - { - const int yyn = action_index [state] + token; - - if (yyn < 0 || action_check [yyn] != token) - return - action_default [state]; - - return action_info [yyn]; - } -}; - - -QT_END_NAMESPACE -#endif // QQMLJSGRAMMAR_P_H - diff --git a/src/qml/parser/qqmljskeywords_p.h b/src/qml/parser/qqmljskeywords_p.h index 20daf545a9..40094ecdf8 100644 --- a/src/qml/parser/qqmljskeywords_p.h +++ b/src/qml/parser/qqmljskeywords_p.h @@ -57,10 +57,10 @@ QT_QML_BEGIN_NAMESPACE namespace QQmlJS { -static inline int classify2(const QChar *s, bool qmlMode) { +static inline int classify2(const QChar *s, int parseModeFlags) { if (s[0].unicode() == 'a') { if (s[1].unicode() == 's') { - return qmlMode ? Lexer::T_AS : Lexer::T_IDENTIFIER; + return (parseModeFlags & Lexer::QmlMode) ? Lexer::T_AS : Lexer::T_IDENTIFIER; } } else if (s[0].unicode() == 'd') { @@ -76,15 +76,18 @@ static inline int classify2(const QChar *s, bool qmlMode) { return Lexer::T_IN; } } - else if (qmlMode && s[0].unicode() == 'o') { + else if (s[0].unicode() == 'o') { if (s[1].unicode() == 'n') { - return qmlMode ? Lexer::T_ON : Lexer::T_IDENTIFIER; + return (parseModeFlags & Lexer::QmlMode) ? Lexer::T_ON : Lexer::T_IDENTIFIER; + } + else if (s[1].unicode() == 'f') { + return Lexer::T_OF; } } return Lexer::T_IDENTIFIER; } -static inline int classify3(const QChar *s, bool qmlMode) { +static inline int classify3(const QChar *s, int parseModeFlags) { if (s[0].unicode() == 'f') { if (s[1].unicode() == 'o') { if (s[2].unicode() == 'r') { @@ -102,7 +105,7 @@ static inline int classify3(const QChar *s, bool qmlMode) { else if (s[0].unicode() == 'i') { if (s[1].unicode() == 'n') { if (s[2].unicode() == 't') { - return qmlMode ? int(Lexer::T_INT) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_INT) : int(Lexer::T_IDENTIFIER); } } } @@ -144,12 +147,12 @@ static inline int classify3(const QChar *s, bool qmlMode) { return Lexer::T_IDENTIFIER; } -static inline int classify4(const QChar *s, bool qmlMode) { +static inline int classify4(const QChar *s, int parseModeFlags) { if (s[0].unicode() == 'b') { if (s[1].unicode() == 'y') { if (s[2].unicode() == 't') { if (s[3].unicode() == 'e') { - return qmlMode ? int(Lexer::T_BYTE) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_BYTE) : int(Lexer::T_IDENTIFIER); } } } @@ -165,7 +168,7 @@ static inline int classify4(const QChar *s, bool qmlMode) { else if (s[1].unicode() == 'h') { if (s[2].unicode() == 'a') { if (s[3].unicode() == 'r') { - return qmlMode ? int(Lexer::T_CHAR) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_CHAR) : int(Lexer::T_IDENTIFIER); } } } @@ -181,7 +184,16 @@ static inline int classify4(const QChar *s, bool qmlMode) { else if (s[1].unicode() == 'n') { if (s[2].unicode() == 'u') { if (s[3].unicode() == 'm') { - return qmlMode ? int(Lexer::T_ENUM) : int(Lexer::T_RESERVED_WORD); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_ENUM) : int(Lexer::T_RESERVED_WORD); + } + } + } + } + else if (s[0].unicode() == 'f') { + if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'o') { + if (s[3].unicode() == 'm') { + return int(Lexer::T_FROM); } } } @@ -190,7 +202,7 @@ static inline int classify4(const QChar *s, bool qmlMode) { if (s[1].unicode() == 'o') { if (s[2].unicode() == 't') { if (s[3].unicode() == 'o') { - return qmlMode ? int(Lexer::T_GOTO) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_GOTO) : int(Lexer::T_IDENTIFIER); } } } @@ -199,7 +211,7 @@ static inline int classify4(const QChar *s, bool qmlMode) { if (s[1].unicode() == 'o') { if (s[2].unicode() == 'n') { if (s[3].unicode() == 'g') { - return qmlMode ? int(Lexer::T_LONG) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_LONG) : int(Lexer::T_IDENTIFIER); } } } @@ -250,7 +262,7 @@ static inline int classify4(const QChar *s, bool qmlMode) { return Lexer::T_IDENTIFIER; } -static inline int classify5(const QChar *s, bool qmlMode) { +static inline int classify5(const QChar *s, int parseModeFlags) { if (s[0].unicode() == 'b') { if (s[1].unicode() == 'r') { if (s[2].unicode() == 'e') { @@ -305,7 +317,7 @@ static inline int classify5(const QChar *s, bool qmlMode) { if (s[2].unicode() == 'n') { if (s[3].unicode() == 'a') { if (s[4].unicode() == 'l') { - return qmlMode ? int(Lexer::T_FINAL) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_FINAL) : int(Lexer::T_IDENTIFIER); } } } @@ -314,7 +326,7 @@ static inline int classify5(const QChar *s, bool qmlMode) { if (s[2].unicode() == 'o') { if (s[3].unicode() == 'a') { if (s[4].unicode() == 't') { - return qmlMode ? int(Lexer::T_FLOAT) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_FLOAT) : int(Lexer::T_IDENTIFIER); } } } @@ -325,7 +337,7 @@ static inline int classify5(const QChar *s, bool qmlMode) { if (s[2].unicode() == 'o') { if (s[3].unicode() == 'r') { if (s[4].unicode() == 't') { - return qmlMode ? int(Lexer::T_SHORT) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_SHORT) : int(Lexer::T_IDENTIFIER); } } } @@ -334,7 +346,7 @@ static inline int classify5(const QChar *s, bool qmlMode) { if (s[2].unicode() == 'p') { if (s[3].unicode() == 'e') { if (s[4].unicode() == 'r') { - return qmlMode ? int(Lexer::T_SUPER) : int(Lexer::T_RESERVED_WORD); + return int(Lexer::T_SUPER); } } } @@ -362,10 +374,21 @@ static inline int classify5(const QChar *s, bool qmlMode) { } } } + else if (s[0].unicode() == 'y') { + if (s[1].unicode() == 'i') { + if (s[2].unicode() == 'e') { + if (s[3].unicode() == 'l') { + if (s[4].unicode() == 'd') { + return (parseModeFlags & Lexer::YieldIsKeyword) ? Lexer::T_YIELD : Lexer::T_IDENTIFIER; + } + } + } + } + } return Lexer::T_IDENTIFIER; } -static inline int classify6(const QChar *s, bool qmlMode) { +static inline int classify6(const QChar *s, int parseModeFlags) { if (s[0].unicode() == 'd') { if (s[1].unicode() == 'e') { if (s[2].unicode() == 'l') { @@ -383,7 +406,7 @@ static inline int classify6(const QChar *s, bool qmlMode) { if (s[3].unicode() == 'b') { if (s[4].unicode() == 'l') { if (s[5].unicode() == 'e') { - return qmlMode ? int(Lexer::T_DOUBLE) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_DOUBLE) : int(Lexer::T_IDENTIFIER); } } } @@ -409,7 +432,7 @@ static inline int classify6(const QChar *s, bool qmlMode) { if (s[3].unicode() == 'o') { if (s[4].unicode() == 'r') { if (s[5].unicode() == 't') { - return qmlMode ? int(Lexer::T_IMPORT) : int(Lexer::T_RESERVED_WORD); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_IMPORT) : int(Lexer::T_RESERVED_WORD); } } } @@ -422,7 +445,7 @@ static inline int classify6(const QChar *s, bool qmlMode) { if (s[3].unicode() == 'i') { if (s[4].unicode() == 'v') { if (s[5].unicode() == 'e') { - return qmlMode ? int(Lexer::T_NATIVE) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_NATIVE) : int(Lexer::T_IDENTIFIER); } } } @@ -435,7 +458,7 @@ static inline int classify6(const QChar *s, bool qmlMode) { if (s[3].unicode() == 'l') { if (s[4].unicode() == 'i') { if (s[5].unicode() == 'c') { - return qmlMode ? Lexer::T_PUBLIC : Lexer::T_IDENTIFIER; + return (parseModeFlags & Lexer::QmlMode) ? Lexer::T_PUBLIC : Lexer::T_IDENTIFIER; } } } @@ -446,7 +469,7 @@ static inline int classify6(const QChar *s, bool qmlMode) { if (s[3].unicode() == 'g') { if (s[4].unicode() == 'm') { if (s[5].unicode() == 'a') { - return qmlMode ? Lexer::T_PRAGMA : Lexer::T_IDENTIFIER; + return (parseModeFlags & Lexer::QmlMode) ? Lexer::T_PRAGMA : Lexer::T_IDENTIFIER; } } } @@ -467,7 +490,7 @@ static inline int classify6(const QChar *s, bool qmlMode) { } } else if (s[0].unicode() == 's') { - if (qmlMode && s[1].unicode() == 'i') { + if ((parseModeFlags & Lexer::QmlMode) && s[1].unicode() == 'i') { if (s[2].unicode() == 'g') { if (s[3].unicode() == 'n') { if (s[4].unicode() == 'a') { @@ -483,7 +506,7 @@ static inline int classify6(const QChar *s, bool qmlMode) { if (s[3].unicode() == 't') { if (s[4].unicode() == 'i') { if (s[5].unicode() == 'c') { - return qmlMode ? int(Lexer::T_STATIC) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::StaticIsKeyword) ? int(Lexer::T_STATIC) : int(Lexer::T_IDENTIFIER); } } } @@ -507,7 +530,7 @@ static inline int classify6(const QChar *s, bool qmlMode) { if (s[3].unicode() == 'o') { if (s[4].unicode() == 'w') { if (s[5].unicode() == 's') { - return qmlMode ? int(Lexer::T_THROWS) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_THROWS) : int(Lexer::T_IDENTIFIER); } } } @@ -528,7 +551,7 @@ static inline int classify6(const QChar *s, bool qmlMode) { return Lexer::T_IDENTIFIER; } -static inline int classify7(const QChar *s, bool qmlMode) { +static inline int classify7(const QChar *s, int parseModeFlags) { if (s[0].unicode() == 'b') { if (s[1].unicode() == 'o') { if (s[2].unicode() == 'o') { @@ -536,7 +559,7 @@ static inline int classify7(const QChar *s, bool qmlMode) { if (s[4].unicode() == 'e') { if (s[5].unicode() == 'a') { if (s[6].unicode() == 'n') { - return qmlMode ? int(Lexer::T_BOOLEAN) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_BOOLEAN) : int(Lexer::T_IDENTIFIER); } } } @@ -596,7 +619,7 @@ static inline int classify7(const QChar *s, bool qmlMode) { if (s[4].unicode() == 'a') { if (s[5].unicode() == 'g') { if (s[6].unicode() == 'e') { - return qmlMode ? int(Lexer::T_PACKAGE) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_PACKAGE) : int(Lexer::T_IDENTIFIER); } } } @@ -609,7 +632,7 @@ static inline int classify7(const QChar *s, bool qmlMode) { if (s[4].unicode() == 'a') { if (s[5].unicode() == 't') { if (s[6].unicode() == 'e') { - return qmlMode ? int(Lexer::T_PRIVATE) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_PRIVATE) : int(Lexer::T_IDENTIFIER); } } } @@ -620,7 +643,7 @@ static inline int classify7(const QChar *s, bool qmlMode) { return Lexer::T_IDENTIFIER; } -static inline int classify8(const QChar *s, bool qmlMode) { +static inline int classify8(const QChar *s, int parseModeFlags) { if (s[0].unicode() == 'a') { if (s[1].unicode() == 'b') { if (s[2].unicode() == 's') { @@ -629,7 +652,7 @@ static inline int classify8(const QChar *s, bool qmlMode) { if (s[5].unicode() == 'a') { if (s[6].unicode() == 'c') { if (s[7].unicode() == 't') { - return qmlMode ? int(Lexer::T_ABSTRACT) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_ABSTRACT) : int(Lexer::T_IDENTIFIER); } } } @@ -689,7 +712,7 @@ static inline int classify8(const QChar *s, bool qmlMode) { } } } - else if (qmlMode && s[0].unicode() == 'p') { + else if ((parseModeFlags & Lexer::QmlMode) && s[0].unicode() == 'p') { if (s[1].unicode() == 'r') { if (s[2].unicode() == 'o') { if (s[3].unicode() == 'p') { @@ -706,7 +729,7 @@ static inline int classify8(const QChar *s, bool qmlMode) { } } } - else if (qmlMode && s[0].unicode() == 'r') { + else if ((parseModeFlags & Lexer::QmlMode) && s[0].unicode() == 'r') { if (s[1].unicode() == 'e') { if (s[2].unicode() == 'a') { if (s[3].unicode() == 'd') { @@ -731,7 +754,7 @@ static inline int classify8(const QChar *s, bool qmlMode) { if (s[5].unicode() == 'i') { if (s[6].unicode() == 'l') { if (s[7].unicode() == 'e') { - return qmlMode ? int(Lexer::T_VOLATILE) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_VOLATILE) : int(Lexer::T_IDENTIFIER); } } } @@ -743,7 +766,7 @@ static inline int classify8(const QChar *s, bool qmlMode) { return Lexer::T_IDENTIFIER; } -static inline int classify9(const QChar *s, bool qmlMode) { +static inline int classify9(const QChar *s, int parseModeFlags) { if (s[0].unicode() == 'i') { if (s[1].unicode() == 'n') { if (s[2].unicode() == 't') { @@ -753,7 +776,7 @@ static inline int classify9(const QChar *s, bool qmlMode) { if (s[6].unicode() == 'a') { if (s[7].unicode() == 'c') { if (s[8].unicode() == 'e') { - return qmlMode ? int(Lexer::T_INTERFACE) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_INTERFACE) : int(Lexer::T_IDENTIFIER); } } } @@ -772,7 +795,7 @@ static inline int classify9(const QChar *s, bool qmlMode) { if (s[6].unicode() == 't') { if (s[7].unicode() == 'e') { if (s[8].unicode() == 'd') { - return qmlMode ? int(Lexer::T_PROTECTED) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_PROTECTED) : int(Lexer::T_IDENTIFIER); } } } @@ -791,7 +814,7 @@ static inline int classify9(const QChar *s, bool qmlMode) { if (s[6].unicode() == 'e') { if (s[7].unicode() == 'n') { if (s[8].unicode() == 't') { - return qmlMode ? int(Lexer::T_TRANSIENT) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_TRANSIENT) : int(Lexer::T_IDENTIFIER); } } } @@ -804,7 +827,7 @@ static inline int classify9(const QChar *s, bool qmlMode) { return Lexer::T_IDENTIFIER; } -static inline int classify10(const QChar *s, bool qmlMode) { +static inline int classify10(const QChar *s, int parseModeFlags) { if (s[0].unicode() == 'i') { if (s[1].unicode() == 'm') { if (s[2].unicode() == 'p') { @@ -815,7 +838,7 @@ static inline int classify10(const QChar *s, bool qmlMode) { if (s[7].unicode() == 'n') { if (s[8].unicode() == 't') { if (s[9].unicode() == 's') { - return qmlMode ? int(Lexer::T_IMPLEMENTS) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_IMPLEMENTS) : int(Lexer::T_IDENTIFIER); } } } @@ -848,7 +871,7 @@ static inline int classify10(const QChar *s, bool qmlMode) { return Lexer::T_IDENTIFIER; } -static inline int classify12(const QChar *s, bool qmlMode) { +static inline int classify12(const QChar *s, int parseModeFlags) { if (s[0].unicode() == 's') { if (s[1].unicode() == 'y') { if (s[2].unicode() == 'n') { @@ -861,7 +884,7 @@ static inline int classify12(const QChar *s, bool qmlMode) { if (s[9].unicode() == 'z') { if (s[10].unicode() == 'e') { if (s[11].unicode() == 'd') { - return qmlMode ? int(Lexer::T_SYNCHRONIZED) : int(Lexer::T_IDENTIFIER); + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_SYNCHRONIZED) : int(Lexer::T_IDENTIFIER); } } } @@ -877,18 +900,18 @@ static inline int classify12(const QChar *s, bool qmlMode) { return Lexer::T_IDENTIFIER; } -int Lexer::classify(const QChar *s, int n, bool qmlMode) { +int Lexer::classify(const QChar *s, int n, int parseModeFlags) { switch (n) { - case 2: return classify2(s, qmlMode); - case 3: return classify3(s, qmlMode); - case 4: return classify4(s, qmlMode); - case 5: return classify5(s, qmlMode); - case 6: return classify6(s, qmlMode); - case 7: return classify7(s, qmlMode); - case 8: return classify8(s, qmlMode); - case 9: return classify9(s, qmlMode); - case 10: return classify10(s, qmlMode); - case 12: return classify12(s, qmlMode); + case 2: return classify2(s, parseModeFlags); + case 3: return classify3(s, parseModeFlags); + case 4: return classify4(s, parseModeFlags); + case 5: return classify5(s, parseModeFlags); + case 6: return classify6(s, parseModeFlags); + case 7: return classify7(s, parseModeFlags); + case 8: return classify8(s, parseModeFlags); + case 9: return classify9(s, parseModeFlags); + case 10: return classify10(s, parseModeFlags); + case 12: return classify12(s, parseModeFlags); default: return Lexer::T_IDENTIFIER; } // switch } diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp index aab9025a08..ae016076b8 100644 --- a/src/qml/parser/qqmljslexer.cpp +++ b/src/qml/parser/qqmljslexer.cpp @@ -77,22 +77,15 @@ static inline QChar convertHex(QChar c1, QChar c2) return QChar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode())); } -static inline QChar convertUnicode(QChar c1, QChar c2, QChar c3, QChar c4) -{ - return QChar((convertHex(c3.unicode()) << 4) + convertHex(c4.unicode()), - (convertHex(c1.unicode()) << 4) + convertHex(c2.unicode())); -} - Lexer::Lexer(Engine *engine) : _engine(engine) , _codePtr(nullptr) , _endPtr(nullptr) - , _lastLinePtr(nullptr) - , _tokenLinePtr(nullptr) , _tokenStartPtr(nullptr) , _char(QLatin1Char('\n')) , _errorCode(NoError) , _currentLineNumber(0) + , _currentColumnNumber(0) , _tokenValue(0) , _parenthesesState(IgnoreParentheses) , _parenthesesCount(0) @@ -101,6 +94,7 @@ Lexer::Lexer(Engine *engine) , _tokenKind(0) , _tokenLength(0) , _tokenLine(0) + , _tokenColumn(0) , _validTokenText(false) , _prohibitAutomaticSemicolon(false) , _restrictedKeyword(false) @@ -137,14 +131,13 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode) _codePtr = code.unicode(); _endPtr = _codePtr + code.length(); - _lastLinePtr = _codePtr; - _tokenLinePtr = _codePtr; _tokenStartPtr = _codePtr; _char = QLatin1Char('\n'); _errorCode = NoError; _currentLineNumber = lineno; + _currentColumnNumber = 0; _tokenValue = 0; // parentheses state @@ -156,6 +149,7 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode) _patternFlags = 0; _tokenLength = 0; _tokenLine = lineno; + _tokenColumn = 0; _validTokenText = false; _prohibitAutomaticSemicolon = false; @@ -172,9 +166,10 @@ void Lexer::scanChar() if (sequenceLength == 2) _char = *_codePtr++; - if (unsigned sequenceLength = isLineTerminatorSequence()) { - _lastLinePtr = _codePtr + sequenceLength - 1; // points to the first character after the newline + ++_currentColumnNumber; + if (isLineTerminator()) { ++_currentLineNumber; + _currentColumnNumber = 0; } } @@ -222,12 +217,32 @@ inline bool isBinop(int tok) return false; } } + +int hexDigit(QChar c) +{ + if (c >= QLatin1Char('0') && c <= QLatin1Char('9')) + return c.unicode() - '0'; + if (c >= QLatin1Char('a') && c <= QLatin1Char('f')) + return c.unicode() - 'a' + 10; + if (c >= QLatin1Char('A') && c <= QLatin1Char('F')) + return c.unicode() - 'A' + 10; + return -1; +} + +int octalDigit(QChar c) +{ + if (c >= QLatin1Char('0') && c <= QLatin1Char('7')) + return c.unicode() - '0'; + return -1; +} + } // anonymous namespace int Lexer::lex() { const int previousTokenKind = _tokenKind; + again: _tokenSpell = QStringRef(); _tokenKind = scanToken(); _tokenLength = _codePtr - _tokenStartPtr - 1; @@ -239,6 +254,9 @@ int Lexer::lex() // update the flags switch (_tokenKind) { case T_LBRACE: + if (_bracesCount > 0) + ++_bracesCount; + Q_FALLTHROUGH(); case T_SEMICOLON: case T_QUESTION: case T_COLON: @@ -266,9 +284,15 @@ int Lexer::lex() case T_CONTINUE: case T_BREAK: case T_RETURN: + case T_YIELD: case T_THROW: _restrictedKeyword = true; break; + case T_RBRACE: + if (_bracesCount > 0) + --_bracesCount; + if (_bracesCount == 0) + goto again; } // switch // update the parentheses state @@ -295,39 +319,57 @@ int Lexer::lex() return _tokenKind; } -bool Lexer::isUnicodeEscapeSequence(const QChar *chars) -{ - if (isHexDigit(chars[0]) && isHexDigit(chars[1]) && isHexDigit(chars[2]) && isHexDigit(chars[3])) - return true; - - return false; -} - -QChar Lexer::decodeUnicodeEscapeCharacter(bool *ok) +uint Lexer::decodeUnicodeEscapeCharacter(bool *ok) { - if (_char == QLatin1Char('u') && isUnicodeEscapeSequence(&_codePtr[0])) { - scanChar(); // skip u + Q_ASSERT(_char == QLatin1Char('u')); + scanChar(); // skip u + if (_codePtr + 4 <= _endPtr && isHexDigit(_char)) { + uint codePoint = 0; + for (int i = 0; i < 4; ++i) { + int digit = hexDigit(_char); + if (digit < 0) + goto error; + codePoint *= 16; + codePoint += digit; + scanChar(); + } - const QChar c1 = _char; - scanChar(); + *ok = true; + return codePoint; + } else if (_codePtr < _endPtr && _char == QLatin1Char('{')) { + scanChar(); // skip '{' + uint codePoint = 0; + if (!isHexDigit(_char)) + // need at least one hex digit + goto error; - const QChar c2 = _char; - scanChar(); + while (_codePtr <= _endPtr) { + int digit = hexDigit(_char); + if (digit < 0) + break; + codePoint *= 16; + codePoint += digit; + if (codePoint > 0x10ffff) + goto error; + scanChar(); + } - const QChar c3 = _char; - scanChar(); + if (_char != QLatin1Char('}')) + goto error; - const QChar c4 = _char; - scanChar(); + scanChar(); // skip '}' - if (ok) - *ok = true; - return convertUnicode(c1, c2, c3, c4); + *ok = true; + return codePoint; } + error: + _errorCode = IllegalUnicodeEscapeSequence; + _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); + *ok = false; - return QChar(); + return 0; } QChar Lexer::decodeHexEscapeCharacter(bool *ok) @@ -351,15 +393,15 @@ QChar Lexer::decodeHexEscapeCharacter(bool *ok) return QChar(); } -static inline bool isIdentifierStart(QChar ch) +static inline bool isIdentifierStart(uint ch) { // fast path for ascii - if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || - (ch.unicode() >= 'A' && ch.unicode() <= 'Z') || + if ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || ch == '$' || ch == '_') return true; - switch (ch.category()) { + switch (QChar::category(ch)) { case QChar::Number_Letter: case QChar::Letter_Uppercase: case QChar::Letter_Lowercase: @@ -373,17 +415,17 @@ static inline bool isIdentifierStart(QChar ch) return false; } -static bool isIdentifierPart(QChar ch) +static bool isIdentifierPart(uint ch) { // fast path for ascii - if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || - (ch.unicode() >= 'A' && ch.unicode() <= 'Z') || - (ch.unicode() >= '0' && ch.unicode() <= '9') || + if ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || ch == '$' || ch == '_' || - ch.unicode() == 0x200c /* ZWNJ */ || ch.unicode() == 0x200d /* ZWJ */) + ch == 0x200c /* ZWNJ */ || ch == 0x200d /* ZWJ */) return true; - switch (ch.category()) { + switch (QChar::category(ch)) { case QChar::Mark_NonSpacing: case QChar::Mark_SpacingCombining: @@ -412,19 +454,23 @@ int Lexer::scanToken() return tk; } + if (_bracesCount == 0) { + // we're inside a Template string + return scanString(TemplateContinuation); + } + + _terminator = false; again: _validTokenText = false; - _tokenLinePtr = _lastLinePtr; while (_char.isSpace()) { - if (unsigned sequenceLength = isLineTerminatorSequence()) { - _tokenLinePtr = _codePtr + sequenceLength - 1; - + if (isLineTerminator()) { if (_restrictedKeyword) { // automatic semicolon insertion _tokenLine = _currentLineNumber; + _tokenColumn = _currentColumnNumber; _tokenStartPtr = _codePtr - 1; return T_SEMICOLON; } else { @@ -438,6 +484,7 @@ again: _tokenStartPtr = _codePtr - 1; _tokenLine = _currentLineNumber; + _tokenColumn = _currentColumnNumber; if (_codePtr > _endPtr) return EOF_SYMBOL; @@ -501,6 +548,9 @@ again: return T_EQ_EQ_EQ; } return T_EQ_EQ; + } else if (_char == QLatin1Char('>')) { + scanChar(); + return T_ARROW; } return T_EQ; @@ -557,50 +607,18 @@ again: return T_DIVIDE_; case '.': - if (_char.isDigit()) { - QVarLengthArray<char,32> chars; - - chars.append(ch.unicode()); // append the `.' - - while (_char.isDigit()) { - chars.append(_char.unicode()); + if (isDecimalDigit(_char.unicode())) + return scanNumber(ch); + if (_char == QLatin1Char('.')) { + scanChar(); + if (_char == QLatin1Char('.')) { scanChar(); - } - - if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { - if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) && - _codePtr[1].isDigit())) { - - chars.append(_char.unicode()); - scanChar(); // consume `e' - - if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) { - chars.append(_char.unicode()); - scanChar(); // consume the sign - } - - while (_char.isDigit()) { - chars.append(_char.unicode()); - scanChar(); - } - } - } - - chars.append('\0'); - - const char *begin = chars.constData(); - const char *end = nullptr; - bool ok = false; - - _tokenValue = qstrtod(begin, &end, &ok); - - if (end - begin != chars.size() - 1) { - _errorCode = IllegalExponentIndicator; - _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal syntax for exponential number"); + return T_ELLIPSIS; + } else { + _errorCode = IllegalCharacter; + _errorMessage = QCoreApplication::translate("QQmlParser", "Unexpected token '.'"); return T_ERROR; } - - return T_NUMERIC_LITERAL; } return T_DOT; @@ -611,7 +629,7 @@ again: } else if (_char == QLatin1Char('-')) { scanChar(); - if (_terminator && !_delimited && !_prohibitAutomaticSemicolon) { + if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) { _stackToken = T_MINUS_MINUS; return T_SEMICOLON; } @@ -629,7 +647,7 @@ again: } else if (_char == QLatin1Char('+')) { scanChar(); - if (_terminator && !_delimited && !_prohibitAutomaticSemicolon) { + if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) { _stackToken = T_PLUS_PLUS; return T_SEMICOLON; } @@ -642,6 +660,13 @@ again: if (_char == QLatin1Char('=')) { scanChar(); return T_STAR_EQ; + } else if (_char == QLatin1Char('*')) { + scanChar(); + if (_char == QLatin1Char('=')) { + scanChar(); + return T_STAR_STAR_EQ; + } + return T_STAR_STAR; } return T_STAR; @@ -676,141 +701,12 @@ again: } return T_NOT; + case '`': + _outerTemplateBraceCount.push(_bracesCount); + Q_FALLTHROUGH(); case '\'': - case '"': { - const QChar quote = ch; - bool multilineStringLiteral = false; - - const QChar *startCode = _codePtr; - - if (_engine) { - while (_codePtr <= _endPtr) { - if (isLineTerminator()) { - if (qmlMode()) - break; - _errorCode = IllegalCharacter; - _errorMessage = QCoreApplication::translate("QQmlParser", "Stray newline in string literal"); - return T_ERROR; - } else if (_char == QLatin1Char('\\')) { - break; - } else if (_char == quote) { - _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode); - scanChar(); - - return T_STRING_LITERAL; - } - scanChar(); - } - } - - _validTokenText = true; - _tokenText.resize(0); - startCode--; - while (startCode != _codePtr - 1) - _tokenText += *startCode++; - - while (_codePtr <= _endPtr) { - if (unsigned sequenceLength = isLineTerminatorSequence()) { - multilineStringLiteral = true; - _tokenText += _char; - if (sequenceLength == 2) - _tokenText += *_codePtr; - scanChar(); - } else if (_char == quote) { - scanChar(); - - if (_engine) - _tokenSpell = _engine->newStringRef(_tokenText); - - return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL; - } else if (_char == QLatin1Char('\\')) { - scanChar(); - if (_codePtr > _endPtr) { - _errorCode = IllegalEscapeSequence; - _errorMessage = QCoreApplication::translate("QQmlParser", "End of file reached at escape sequence"); - return T_ERROR; - } - - QChar u; - - switch (_char.unicode()) { - // unicode escape sequence - case 'u': { - bool ok = false; - u = decodeUnicodeEscapeCharacter(&ok); - if (! ok) { - _errorCode = IllegalUnicodeEscapeSequence; - _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); - return T_ERROR; - } - } break; - - // hex escape sequence - case 'x': { - bool ok = false; - u = decodeHexEscapeCharacter(&ok); - if (!ok) { - _errorCode = IllegalHexadecimalEscapeSequence; - _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal hexadecimal escape sequence"); - return T_ERROR; - } - } break; - - // single character escape sequence - case '\\': u = QLatin1Char('\\'); scanChar(); break; - case '\'': u = QLatin1Char('\''); scanChar(); break; - case '\"': u = QLatin1Char('\"'); scanChar(); break; - case 'b': u = QLatin1Char('\b'); scanChar(); break; - case 'f': u = QLatin1Char('\f'); scanChar(); break; - case 'n': u = QLatin1Char('\n'); scanChar(); break; - case 'r': u = QLatin1Char('\r'); scanChar(); break; - case 't': u = QLatin1Char('\t'); scanChar(); break; - case 'v': u = QLatin1Char('\v'); scanChar(); break; - - case '0': - if (! _codePtr->isDigit()) { - scanChar(); - u = QLatin1Char('\0'); - break; - } - Q_FALLTHROUGH(); - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - _errorCode = IllegalEscapeSequence; - _errorMessage = QCoreApplication::translate("QQmlParser", "Octal escape sequences are not allowed"); - return T_ERROR; - - case '\r': - case '\n': - case 0x2028u: - case 0x2029u: - scanChar(); - continue; - - default: - // non escape character - u = _char; - scanChar(); - } - - _tokenText += u; - } else { - _tokenText += _char; - scanChar(); - } - } - - _errorCode = UnclosedStringLiteral; - _errorMessage = QCoreApplication::translate("QQmlParser", "Unclosed string at end of line"); - return T_ERROR; - } + case '"': + return scanString(ScanStringMode(ch.unicode())); case '0': case '1': case '2': @@ -824,28 +720,36 @@ again: return scanNumber(ch); default: { - QChar c = ch; + uint c = ch.unicode(); bool identifierWithEscapeChars = false; - if (c == QLatin1Char('\\') && _char == QLatin1Char('u')) { + if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_char.unicode())) { + c = QChar::surrogateToUcs4(ushort(c), _char.unicode()); + scanChar(); + } else if (c == '\\' && _char == QLatin1Char('u')) { identifierWithEscapeChars = true; bool ok = false; c = decodeUnicodeEscapeCharacter(&ok); - if (! ok) { - _errorCode = IllegalUnicodeEscapeSequence; - _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); + if (!ok) return T_ERROR; - } } if (isIdentifierStart(c)) { if (identifierWithEscapeChars) { _tokenText.resize(0); - _tokenText += c; + if (QChar::requiresSurrogates(c)) { + _tokenText += QChar(QChar::highSurrogate(c)); + _tokenText += QChar(QChar::lowSurrogate(c)); + } else { + _tokenText += QChar(c); + } _validTokenText = true; } - while (true) { - c = _char; - if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) { - if (! identifierWithEscapeChars) { + while (_codePtr <= _endPtr) { + c = _char.unicode(); + if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_codePtr->unicode())) { + scanChar(); + c = QChar::surrogateToUcs4(ushort(c), _char.unicode()); + } else if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) { + if (!identifierWithEscapeChars) { identifierWithEscapeChars = true; _tokenText.resize(0); _tokenText.insert(0, _tokenStartPtr, _codePtr - _tokenStartPtr - 1); @@ -855,38 +759,52 @@ again: scanChar(); // skip '\\' bool ok = false; c = decodeUnicodeEscapeCharacter(&ok); - if (! ok) { - _errorCode = IllegalUnicodeEscapeSequence; - _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); + if (!ok) return T_ERROR; - } - if (isIdentifierPart(c)) - _tokenText += c; - continue; - } else if (isIdentifierPart(c)) { - if (identifierWithEscapeChars) - _tokenText += c; - scanChar(); + if (!isIdentifierPart(c)) + break; + + if (identifierWithEscapeChars) { + if (QChar::requiresSurrogates(c)) { + _tokenText += QChar(QChar::highSurrogate(c)); + _tokenText += QChar(QChar::lowSurrogate(c)); + } else { + _tokenText += QChar(c); + } + } continue; } - _tokenLength = _codePtr - _tokenStartPtr - 1; + if (!isIdentifierPart(c)) + break; - int kind = T_IDENTIFIER; + if (identifierWithEscapeChars) { + if (QChar::requiresSurrogates(c)) { + _tokenText += QChar(QChar::highSurrogate(c)); + _tokenText += QChar(QChar::lowSurrogate(c)); + } else { + _tokenText += QChar(c); + } + } + scanChar(); + } + + _tokenLength = _codePtr - _tokenStartPtr - 1; - if (! identifierWithEscapeChars) - kind = classify(_tokenStartPtr, _tokenLength, _qmlMode); + int kind = T_IDENTIFIER; - if (_engine) { - if (kind == T_IDENTIFIER && identifierWithEscapeChars) - _tokenSpell = _engine->newStringRef(_tokenText); - else - _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength); - } + if (!identifierWithEscapeChars) + kind = classify(_tokenStartPtr, _tokenLength, parseModeFlags()); - return kind; + if (_engine) { + if (kind == T_IDENTIFIER && identifierWithEscapeChars) + _tokenSpell = _engine->newStringRef(_tokenText); + else + _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength); } + + return kind; } } @@ -896,93 +814,278 @@ again: return T_ERROR; } -int Lexer::scanNumber(QChar ch) +int Lexer::scanString(ScanStringMode mode) { - if (ch != QLatin1Char('0')) { - QVarLengthArray<char, 64> buf; - buf += ch.toLatin1(); - - QChar n = _char; - const QChar *code = _codePtr; - while (n.isDigit()) { - buf += n.toLatin1(); - n = *code++; - } + QChar quote = (mode == TemplateContinuation) ? QChar(TemplateHead) : QChar(mode); + bool multilineStringLiteral = false; - if (n != QLatin1Char('.') && n != QLatin1Char('e') && n != QLatin1Char('E')) { - if (code != _codePtr) { - _codePtr = code - 1; + const QChar *startCode = _codePtr; + + if (_engine) { + while (_codePtr <= _endPtr) { + if (isLineTerminator() && quote != QLatin1Char('`')) { + if (qmlMode()) + break; + _errorCode = IllegalCharacter; + _errorMessage = QCoreApplication::translate("QQmlParser", "Stray newline in string literal"); + return T_ERROR; + } else if (_char == QLatin1Char('\\')) { + break; + } else if (_char == '$' && quote == QLatin1Char('`')) { + break; + } else if (_char == quote) { + _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode); scanChar(); + + if (quote == QLatin1Char('`')) + _bracesCount = _outerTemplateBraceCount.pop(); + + if (mode == TemplateHead) + return T_NO_SUBSTITUTION_TEMPLATE; + else if (mode == TemplateContinuation) + return T_TEMPLATE_TAIL; + else + return T_STRING_LITERAL; } - buf.append('\0'); - _tokenValue = strtod(buf.constData(), nullptr); - return T_NUMERIC_LITERAL; + scanChar(); } - } else if (_char.isDigit() && !qmlMode()) { - _errorCode = IllegalCharacter; - _errorMessage = QCoreApplication::translate("QQmlParser", "Decimal numbers can't start with '0'"); - return T_ERROR; } - QVarLengthArray<char,32> chars; - chars.append(ch.unicode()); + _validTokenText = true; + _tokenText.resize(0); + startCode--; + while (startCode != _codePtr - 1) + _tokenText += *startCode++; + + while (_codePtr <= _endPtr) { + if (unsigned sequenceLength = isLineTerminatorSequence()) { + multilineStringLiteral = true; + _tokenText += _char; + if (sequenceLength == 2) + _tokenText += *_codePtr; + scanChar(); + } else if (_char == mode) { + scanChar(); - if (ch == QLatin1Char('0') && (_char == QLatin1Char('x') || _char == QLatin1Char('X'))) { - ch = _char; // remember the x or X to use it in the error message below. + if (_engine) + _tokenSpell = _engine->newStringRef(_tokenText); - // parse hex integer literal - chars.append(_char.unicode()); - scanChar(); // consume `x' + if (quote == QLatin1Char('`')) + _bracesCount = _outerTemplateBraceCount.pop(); - while (isHexDigit(_char)) { - chars.append(_char.unicode()); + if (mode == TemplateContinuation) + return T_TEMPLATE_TAIL; + else if (mode == TemplateHead) + return T_NO_SUBSTITUTION_TEMPLATE; + + return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL; + } else if (quote == QLatin1Char('`') && _char == QLatin1Char('$') && *_codePtr == '{') { + scanChar(); + scanChar(); + _bracesCount = 1; + if (_engine) + _tokenSpell = _engine->newStringRef(_tokenText); + + return (mode == TemplateHead ? T_TEMPLATE_HEAD : T_TEMPLATE_MIDDLE); + } else if (_char == QLatin1Char('\\')) { + scanChar(); + if (_codePtr > _endPtr) { + _errorCode = IllegalEscapeSequence; + _errorMessage = QCoreApplication::translate("QQmlParser", "End of file reached at escape sequence"); + return T_ERROR; + } + + QChar u; + + switch (_char.unicode()) { + // unicode escape sequence + case 'u': { + bool ok = false; + uint codePoint = decodeUnicodeEscapeCharacter(&ok); + if (!ok) + return T_ERROR; + if (QChar::requiresSurrogates(codePoint)) { + // need to use a surrogate pair + _tokenText += QChar(QChar::highSurrogate(codePoint)); + u = QChar::lowSurrogate(codePoint); + } else { + u = codePoint; + } + } break; + + // hex escape sequence + case 'x': { + bool ok = false; + u = decodeHexEscapeCharacter(&ok); + if (!ok) { + _errorCode = IllegalHexadecimalEscapeSequence; + _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal hexadecimal escape sequence"); + return T_ERROR; + } + } break; + + // single character escape sequence + case '\\': u = QLatin1Char('\\'); scanChar(); break; + case '\'': u = QLatin1Char('\''); scanChar(); break; + case '\"': u = QLatin1Char('\"'); scanChar(); break; + case 'b': u = QLatin1Char('\b'); scanChar(); break; + case 'f': u = QLatin1Char('\f'); scanChar(); break; + case 'n': u = QLatin1Char('\n'); scanChar(); break; + case 'r': u = QLatin1Char('\r'); scanChar(); break; + case 't': u = QLatin1Char('\t'); scanChar(); break; + case 'v': u = QLatin1Char('\v'); scanChar(); break; + + case '0': + if (! _codePtr->isDigit()) { + scanChar(); + u = QLatin1Char('\0'); + break; + } + Q_FALLTHROUGH(); + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + _errorCode = IllegalEscapeSequence; + _errorMessage = QCoreApplication::translate("QQmlParser", "Octal escape sequences are not allowed"); + return T_ERROR; + + case '\r': + case '\n': + case 0x2028u: + case 0x2029u: + scanChar(); + continue; + + default: + // non escape character + u = _char; + scanChar(); + } + + _tokenText += u; + } else { + _tokenText += _char; scanChar(); } + } - if (chars.size() < 3) { - _errorCode = IllegalHexNumber; - _errorMessage = QCoreApplication::translate("QQmlParser", "At least one hexadecimal digit is required after '0%1'").arg(ch); + _errorCode = UnclosedStringLiteral; + _errorMessage = QCoreApplication::translate("QQmlParser", "Unclosed string at end of line"); + return T_ERROR; +} + +int Lexer::scanNumber(QChar ch) +{ + if (ch == QLatin1Char('0')) { + if (_char == QLatin1Char('x') || _char == QLatin1Char('X')) { + ch = _char; // remember the x or X to use it in the error message below. + + // parse hex integer literal + scanChar(); // consume 'x' + + if (!isHexDigit(_char)) { + _errorCode = IllegalNumber; + _errorMessage = QCoreApplication::translate("QQmlParser", "At least one hexadecimal digit is required after '0%1'").arg(ch); + return T_ERROR; + } + + double d = 0.; + while (1) { + int digit = ::hexDigit(_char); + if (digit < 0) + break; + d *= 16; + d += digit; + scanChar(); + } + + _tokenValue = d; + return T_NUMERIC_LITERAL; + } else if (_char == QLatin1Char('o') || _char == QLatin1Char('O')) { + ch = _char; // remember the o or O to use it in the error message below. + + // parse octal integer literal + scanChar(); // consume 'o' + + if (!isOctalDigit(_char.unicode())) { + _errorCode = IllegalNumber; + _errorMessage = QCoreApplication::translate("QQmlParser", "At least one octal digit is required after '0%1'").arg(ch); + return T_ERROR; + } + + double d = 0.; + while (1) { + int digit = ::octalDigit(_char); + if (digit < 0) + break; + d *= 8; + d += digit; + scanChar(); + } + + _tokenValue = d; + return T_NUMERIC_LITERAL; + } else if (_char == QLatin1Char('b') || _char == QLatin1Char('B')) { + ch = _char; // remember the b or B to use it in the error message below. + + // parse binary integer literal + scanChar(); // consume 'b' + + if (_char.unicode() != '0' && _char.unicode() != '1') { + _errorCode = IllegalNumber; + _errorMessage = QCoreApplication::translate("QQmlParser", "At least one binary digit is required after '0%1'").arg(ch); + return T_ERROR; + } + + double d = 0.; + while (1) { + int digit = 0; + if (_char.unicode() == '1') + digit = 1; + else if (_char.unicode() != '0') + break; + d *= 2; + d += digit; + scanChar(); + } + + _tokenValue = d; + return T_NUMERIC_LITERAL; + } else if (_char.isDigit() && !qmlMode()) { + _errorCode = IllegalCharacter; + _errorMessage = QCoreApplication::translate("QQmlParser", "Decimal numbers can't start with '0'"); return T_ERROR; } - - _tokenValue = integerFromString(chars.constData(), chars.size(), 16); - return T_NUMERIC_LITERAL; } // decimal integer literal - while (_char.isDigit()) { - chars.append(_char.unicode()); - scanChar(); // consume the digit - } - - if (_char == QLatin1Char('.')) { - chars.append(_char.unicode()); - scanChar(); // consume `.' + QVarLengthArray<char,32> chars; + chars.append(ch.unicode()); + if (ch != QLatin1Char('.')) { while (_char.isDigit()) { chars.append(_char.unicode()); - scanChar(); + scanChar(); // consume the digit } - if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { - if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) && - _codePtr[1].isDigit())) { - - chars.append(_char.unicode()); - scanChar(); // consume `e' + if (_char == QLatin1Char('.')) { + chars.append(_char.unicode()); + scanChar(); // consume `.' + } + } - if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) { - chars.append(_char.unicode()); - scanChar(); // consume the sign - } + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); + } - while (_char.isDigit()) { - chars.append(_char.unicode()); - scanChar(); - } - } - } - } else if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { + if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) && _codePtr[1].isDigit())) { @@ -1001,12 +1104,6 @@ int Lexer::scanNumber(QChar ch) } } - if (chars.length() == 1) { - // if we ended up with a single digit, then it was a '0' - _tokenValue = 0; - return T_NUMERIC_LITERAL; - } - chars.append('\0'); const char *begin = chars.constData(); @@ -1174,16 +1271,6 @@ bool Lexer::isOctalDigit(ushort c) return (c >= '0' && c <= '7'); } -int Lexer::tokenEndLine() const -{ - return _currentLineNumber; -} - -int Lexer::tokenEndColumn() const -{ - return _codePtr - _lastLinePtr; -} - QString Lexer::tokenText() const { if (_validTokenText) @@ -1256,6 +1343,7 @@ static const int uriTokens[] = { QQmlJSGrammar::T_FUNCTION, QQmlJSGrammar::T_IF, QQmlJSGrammar::T_IN, + QQmlJSGrammar::T_OF, QQmlJSGrammar::T_INSTANCEOF, QQmlJSGrammar::T_NEW, QQmlJSGrammar::T_NULL, diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h index 11d8081713..a6ac8cb354 100644 --- a/src/qml/parser/qqmljslexer_p.h +++ b/src/qml/parser/qqmljslexer_p.h @@ -51,10 +51,11 @@ // We mean it. // -#include "qqmljsglobal_p.h" -#include "qqmljsgrammar_p.h" +#include <private/qqmljsglobal_p.h> +#include <private/qqmljsgrammar_p.h> #include <QtCore/qstring.h> +#include <QtCore/qstack.h> QT_QML_BEGIN_NAMESPACE @@ -62,32 +63,7 @@ namespace QQmlJS { class Engine; class DiagnosticMessage; - -class QML_PARSER_EXPORT Directives { -public: - virtual ~Directives() {} - - virtual void pragmaLibrary() - { - } - - virtual void importFile(const QString &jsfile, const QString &module, int line, int column) - { - Q_UNUSED(jsfile); - Q_UNUSED(module); - Q_UNUSED(line); - Q_UNUSED(column); - } - - virtual void importModule(const QString &uri, const QString &version, const QString &module, int line, int column) - { - Q_UNUSED(uri); - Q_UNUSED(version); - Q_UNUSED(module); - Q_UNUSED(line); - Q_UNUSED(column); - } -}; +class Directives; class QML_PARSER_EXPORT Lexer: public QQmlJSGrammar { @@ -97,10 +73,7 @@ public: T_BOOLEAN = T_RESERVED_WORD, T_BYTE = T_RESERVED_WORD, T_CHAR = T_RESERVED_WORD, - T_CLASS = T_RESERVED_WORD, T_DOUBLE = T_RESERVED_WORD, - T_EXPORT = T_RESERVED_WORD, - T_EXTENDS = T_RESERVED_WORD, T_FINAL = T_RESERVED_WORD, T_FLOAT = T_RESERVED_WORD, T_GOTO = T_RESERVED_WORD, @@ -113,8 +86,6 @@ public: T_PRIVATE = T_RESERVED_WORD, T_PROTECTED = T_RESERVED_WORD, T_SHORT = T_RESERVED_WORD, - T_STATIC = T_RESERVED_WORD, - T_SUPER = T_RESERVED_WORD, T_SYNCHRONIZED = T_RESERVED_WORD, T_THROWS = T_RESERVED_WORD, T_TRANSIENT = T_RESERVED_WORD, @@ -124,7 +95,7 @@ public: enum Error { NoError, IllegalCharacter, - IllegalHexNumber, + IllegalNumber, UnclosedStringLiteral, IllegalEscapeSequence, IllegalUnicodeEscapeSequence, @@ -145,10 +116,29 @@ public: RegExp_Multiline = 0x04 }; + enum ParseModeFlags { + QmlMode = 0x1, + YieldIsKeyword = 0x2, + StaticIsKeyword = 0x4 + }; + public: Lexer(Engine *engine); + int parseModeFlags() const { + int flags = 0; + if (qmlMode()) + flags |= QmlMode|StaticIsKeyword; + if (yieldIsKeyWord()) + flags |= YieldIsKeyword; + if (_staticIsKeyword) + flags |= StaticIsKeyword; + return flags; + } + bool qmlMode() const; + bool yieldIsKeyWord() const { return _generatorLevel != 0; } + void setStaticIsKeyword(bool b) { _staticIsKeyword = b; } QString code() const; void setCode(const QString &code, int lineno, bool qmlMode = true); @@ -166,10 +156,7 @@ public: int tokenLength() const { return _tokenLength; } int tokenStartLine() const { return _tokenLine; } - int tokenStartColumn() const { return _tokenStartPtr - _tokenLinePtr + 1; } - - int tokenEndLine() const; - int tokenEndColumn() const; + int tokenStartColumn() const { return _tokenColumn; } inline QStringRef tokenSpell() const { return _tokenSpell; } double tokenValue() const { return _tokenValue; } @@ -188,13 +175,23 @@ public: BalancedParentheses }; + void enterGeneratorBody() { ++_generatorLevel; } + void leaveGeneratorBody() { --_generatorLevel; } + protected: - int classify(const QChar *s, int n, bool qmlMode); + static int classify(const QChar *s, int n, int parseModeFlags); private: inline void scanChar(); int scanToken(); int scanNumber(QChar ch); + enum ScanStringMode { + SingleQuote = '\'', + DoubleQuote = '"', + TemplateHead = '`', + TemplateContinuation = 0 + }; + int scanString(ScanStringMode mode); bool isLineTerminator() const; unsigned isLineTerminatorSequence() const; @@ -202,10 +199,9 @@ private: static bool isDecimalDigit(ushort c); static bool isHexDigit(QChar c); static bool isOctalDigit(ushort c); - static bool isUnicodeEscapeSequence(const QChar *chars); void syncProhibitAutomaticSemicolon(); - QChar decodeUnicodeEscapeCharacter(bool *ok); + uint decodeUnicodeEscapeCharacter(bool *ok); QChar decodeHexEscapeCharacter(bool *ok); private: @@ -218,26 +214,30 @@ private: const QChar *_codePtr; const QChar *_endPtr; - const QChar *_lastLinePtr; - const QChar *_tokenLinePtr; const QChar *_tokenStartPtr; QChar _char; Error _errorCode; int _currentLineNumber; + int _currentColumnNumber; double _tokenValue; // parentheses state ParenthesesState _parenthesesState; int _parenthesesCount; + // template string stack + QStack<int> _outerTemplateBraceCount; + int _bracesCount = -1; + int _stackToken; int _patternFlags; int _tokenKind; int _tokenLength; int _tokenLine; + int _tokenColumn; bool _validTokenText; bool _prohibitAutomaticSemicolon; @@ -246,6 +246,8 @@ private: bool _followsClosingBrace; bool _delimited; bool _qmlMode; + int _generatorLevel = 0; + bool _staticIsKeyword = false; }; } // end of namespace QQmlJS diff --git a/src/qml/parser/qqmljsmemorypool_p.h b/src/qml/parser/qqmljsmemorypool_p.h index ef443d96bc..9a480f1224 100644 --- a/src/qml/parser/qqmljsmemorypool_p.h +++ b/src/qml/parser/qqmljsmemorypool_p.h @@ -83,6 +83,7 @@ public: free(_blocks); } + qDeleteAll(strings); } inline void *allocate(size_t size) @@ -104,6 +105,11 @@ public: template <typename Tp> Tp *New() { return new (this->allocate(sizeof(Tp))) Tp(); } + QStringRef newString(const QString &string) { + strings.append(new QString(string)); + return QStringRef(strings.last()); + } + private: Q_NEVER_INLINE void *allocate_helper(size_t size) { @@ -143,6 +149,7 @@ private: int _blockCount = -1; char *_ptr = nullptr; char *_end = nullptr; + QVector<QString*> strings; enum { @@ -153,12 +160,10 @@ private: class QML_PARSER_EXPORT Managed { - Managed(const Managed &other); - void operator = (const Managed &other); - + Q_DISABLE_COPY(Managed) public: - Managed() {} - ~Managed() {} + Managed() = default; + ~Managed() = default; void *operator new(size_t size, MemoryPool *pool) { return pool->allocate(size); } void operator delete(void *) {} diff --git a/src/qml/parser/qqmljsparser.cpp b/src/qml/parser/qqmljsparser.cpp deleted file mode 100644 index 24b04b02f9..0000000000 --- a/src/qml/parser/qqmljsparser.cpp +++ /dev/null @@ -1,2010 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmljsengine_p.h" -#include "qqmljslexer_p.h" -#include "qqmljsast_p.h" -#include "qqmljsmemorypool_p.h" - -#include <QtCore/qdebug.h> -#include <QtCore/qcoreapplication.h> - -#include <string.h> - - - -#include "qqmljsparser_p.h" - -#include <QtCore/qvarlengtharray.h> - -// -// W A R N I N G -// ------------- -// -// This file is automatically generated from qqmljs.g. -// Changes should be made to that file, not here. Any change to this file will -// be lost! -// -// To regenerate this file, run: -// qlalr --no-debug --no-lines --qt qqmljs.g -// - -using namespace QQmlJS; - -QT_QML_BEGIN_NAMESPACE - -void Parser::reallocateStack() -{ - if (! stack_size) - stack_size = 128; - else - stack_size <<= 1; - - sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value))); - state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int))); - location_stack = reinterpret_cast<AST::SourceLocation*> (realloc(location_stack, stack_size * sizeof(AST::SourceLocation))); - string_stack = reinterpret_cast<QStringRef*> (realloc(static_cast<void *>(string_stack), stack_size * sizeof(QStringRef))); -} - -Parser::Parser(Engine *engine): - driver(engine), - pool(engine->pool()), - tos(0), - stack_size(0), - sym_stack(nullptr), - state_stack(nullptr), - location_stack(nullptr), - string_stack(nullptr), - program(nullptr), - yylval(0), - first_token(nullptr), - last_token(nullptr) -{ -} - -Parser::~Parser() -{ - if (stack_size) { - free(sym_stack); - free(state_stack); - free(location_stack); - free(string_stack); - } -} - -static inline AST::SourceLocation location(Lexer *lexer) -{ - AST::SourceLocation loc; - loc.offset = lexer->tokenOffset(); - loc.length = lexer->tokenLength(); - loc.startLine = lexer->tokenStartLine(); - loc.startColumn = lexer->tokenStartColumn(); - return loc; -} - -AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr) -{ - QVarLengthArray<QStringRef, 4> nameIds; - QVarLengthArray<AST::SourceLocation, 4> locations; - - AST::ExpressionNode *it = expr; - while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) { - nameIds.append(m->name); - locations.append(m->identifierToken); - it = m->base; - } - - if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(it)) { - AST::UiQualifiedId *q = new (pool) AST::UiQualifiedId(idExpr->name); - q->identifierToken = idExpr->identifierToken; - - AST::UiQualifiedId *currentId = q; - for (int i = nameIds.size() - 1; i != -1; --i) { - currentId = new (pool) AST::UiQualifiedId(currentId, nameIds[i]); - currentId->identifierToken = locations[i]; - } - - return currentId->finish(); - } - - return nullptr; -} - -AST::UiQualifiedPragmaId *Parser::reparseAsQualifiedPragmaId(AST::ExpressionNode *expr) -{ - if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(expr)) { - AST::UiQualifiedPragmaId *q = new (pool) AST::UiQualifiedPragmaId(idExpr->name); - q->identifierToken = idExpr->identifierToken; - - return q->finish(); - } - - return nullptr; -} - - -bool Parser::parse(int startToken) -{ - Lexer *lexer = driver->lexer(); - bool hadErrors = false; - int yytoken = -1; - int action = 0; - - token_buffer[0].token = startToken; - first_token = &token_buffer[0]; - if (startToken == T_FEED_JS_PROGRAM && !lexer->qmlMode()) { - Directives ignoreDirectives; - Directives *directives = driver->directives(); - if (!directives) - directives = &ignoreDirectives; - DiagnosticMessage error; - if (!lexer->scanDirectives(directives, &error)) { - diagnostic_messages.append(error); - return false; - } - token_buffer[1].token = lexer->tokenKind(); - token_buffer[1].dval = lexer->tokenValue(); - token_buffer[1].loc = location(lexer); - token_buffer[1].spell = lexer->tokenSpell(); - last_token = &token_buffer[2]; - } else { - last_token = &token_buffer[1]; - } - - tos = -1; - program = nullptr; - - do { - if (++tos == stack_size) - reallocateStack(); - - state_stack[tos] = action; - - _Lcheck_token: - if (yytoken == -1 && -TERMINAL_COUNT != action_index[action]) { - yyprevlloc = yylloc; - - if (first_token == last_token) { - yytoken = lexer->lex(); - yylval = lexer->tokenValue(); - yytokenspell = lexer->tokenSpell(); - yylloc = location(lexer); - } else { - yytoken = first_token->token; - yylval = first_token->dval; - yytokenspell = first_token->spell; - yylloc = first_token->loc; - ++first_token; - } - } - - action = t_action(action, yytoken); - if (action > 0) { - if (action != ACCEPT_STATE) { - yytoken = -1; - sym(1).dval = yylval; - stringRef(1) = yytokenspell; - loc(1) = yylloc; - } else { - --tos; - return ! hadErrors; - } - } else if (action < 0) { - const int r = -action - 1; - tos -= rhs[r]; - - switch (r) { - -case 0: { - sym(1).Node = sym(2).Node; - program = sym(1).Node; -} break; - -case 1: { - sym(1).Node = sym(2).Node; - program = sym(1).Node; -} break; - -case 2: { - sym(1).Node = sym(2).Node; - program = sym(1).Node; -} break; - -case 3: { - sym(1).Node = sym(2).Node; - program = sym(1).Node; -} break; - -case 4: { - sym(1).Node = sym(2).Node; - program = sym(1).Node; -} break; - -case 5: { - sym(1).Node = sym(2).Node; - program = sym(1).Node; -} break; - -case 6: { - sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiHeaderItemList, - sym(2).UiObjectMemberList->finish()); -} break; - -case 8: { - sym(1).Node = sym(1).UiHeaderItemList->finish(); -} break; - -case 9: { - sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiPragma); -} break; - -case 10: { - sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiImport); -} break; - -case 11: { - sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiPragma); -} break; - -case 12: { - sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiImport); -} break; - -case 16: { - sym(1).UiPragma->semicolonToken = loc(2); -} break; - -case 18: { - sym(1).UiImport->semicolonToken = loc(2); -} break; - -case 20: { - sym(1).UiImport->versionToken = loc(2); - sym(1).UiImport->semicolonToken = loc(3); -} break; - -case 22: { - sym(1).UiImport->versionToken = loc(2); - sym(1).UiImport->asToken = loc(3); - sym(1).UiImport->importIdToken = loc(4); - sym(1).UiImport->importId = stringRef(4); - sym(1).UiImport->semicolonToken = loc(5); -} break; - -case 24: { - sym(1).UiImport->asToken = loc(2); - sym(1).UiImport->importIdToken = loc(3); - sym(1).UiImport->importId = stringRef(3); - sym(1).UiImport->semicolonToken = loc(4); -} break; - -case 25: { - AST::UiPragma *node = nullptr; - - if (AST::UiQualifiedPragmaId *qualifiedId = reparseAsQualifiedPragmaId(sym(2).Expression)) { - node = new (pool) AST::UiPragma(qualifiedId); - } - - sym(1).Node = node; - - if (node) { - node->pragmaToken = loc(1); - } else { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), - QLatin1String("Expected a qualified name id"))); - - return false; // ### remove me - } -} break; - -case 26: { - AST::UiImport *node = nullptr; - - if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) { - node = new (pool) AST::UiImport(importIdLiteral->value); - node->fileNameToken = loc(2); - } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) { - node = new (pool) AST::UiImport(qualifiedId); - node->fileNameToken = loc(2); - } - - sym(1).Node = node; - - if (node) { - node->importToken = loc(1); - } else { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), - QLatin1String("Expected a qualified name id or a string literal"))); - - return false; // ### remove me - } -} break; - -case 27: { - sym(1).Node = nullptr; -} break; - -case 28: { - sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); -} break; - -case 29: { - sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); -} break; - -case 30: { - AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList( - sym(1).UiObjectMemberList, sym(2).UiObjectMember); - sym(1).Node = node; -} break; - -case 31: { - sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember); -} break; - -case 32: { - AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList( - sym(1).UiArrayMemberList, sym(3).UiObjectMember); - node->commaToken = loc(2); - sym(1).Node = node; -} break; - -case 33: { - AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)nullptr); - node->lbraceToken = loc(1); - node->rbraceToken = loc(2); - sym(1).Node = node; -} break; - -case 34: { - AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish()); - node->lbraceToken = loc(1); - node->rbraceToken = loc(3); - sym(1).Node = node; -} break; - -case 35: { - AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId, - sym(2).UiObjectInitializer); - sym(1).Node = node; -} break; - -case 37: { - AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding( - sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish()); - node->colonToken = loc(2); - node->lbracketToken = loc(3); - node->rbracketToken = loc(5); - sym(1).Node = node; -} break; - -case 38: { - AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( - sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer); - node->colonToken = loc(2); - sym(1).Node = node; -} break; - -case 39: { - AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( - sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer); - node->colonToken = loc(2); - node->hasOnToken = true; - sym(1).Node = node; -} break; - -case 47: -{ - AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding( - sym(1).UiQualifiedId, sym(3).Statement); - node->colonToken = loc(2); - sym(1).Node = node; -} break; - -case 48: { - AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1)); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; - -case 49: { - AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1)); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; - -case 50: { - AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1)); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; - -case 51: { - AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(sym(1).UiQualifiedId, stringRef(3)); - node->identifierToken = loc(3); - sym(1).Node = node; -} break; - -case 52: { - sym(1).Node = nullptr; -} break; - -case 53: { - sym(1).Node = sym(1).UiParameterList->finish (); -} break; - -case 54: { - AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiQualifiedId->finish(), stringRef(2)); - node->propertyTypeToken = loc(1); - node->identifierToken = loc(2); - sym(1).Node = node; -} break; - -case 55: { - AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(3).UiQualifiedId->finish(), stringRef(4)); - node->propertyTypeToken = loc(3); - node->commaToken = loc(2); - node->identifierToken = loc(4); - sym(1).Node = node; -} break; - -case 57: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2)); - node->type = AST::UiPublicMember::Signal; - node->propertyToken = loc(1); - node->typeToken = loc(2); - node->identifierToken = loc(2); - node->parameters = sym(4).UiParameterList; - node->semicolonToken = loc(6); - sym(1).Node = node; -} break; - -case 59: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2)); - node->type = AST::UiPublicMember::Signal; - node->propertyToken = loc(1); - node->typeToken = loc(2); - node->identifierToken = loc(2); - node->semicolonToken = loc(3); - sym(1).Node = node; -} break; - -case 61: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6)); - node->typeModifier = stringRef(2); - node->propertyToken = loc(1); - node->typeModifierToken = loc(2); - node->typeToken = loc(4); - node->identifierToken = loc(6); - node->semicolonToken = loc(7); - sym(1).Node = node; -} break; - -case 63: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3)); - node->propertyToken = loc(1); - node->typeToken = loc(2); - node->identifierToken = loc(3); - node->semicolonToken = loc(4); - sym(1).Node = node; -} break; - -case 65: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4)); - node->isDefaultMember = true; - node->defaultToken = loc(1); - node->propertyToken = loc(2); - node->typeToken = loc(3); - node->identifierToken = loc(4); - node->semicolonToken = loc(5); - sym(1).Node = node; -} break; - -case 67: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7)); - node->isDefaultMember = true; - node->defaultToken = loc(1); - node->typeModifier = stringRef(3); - node->propertyToken = loc(2); - node->typeModifierToken = loc(2); - node->typeToken = loc(4); - node->identifierToken = loc(7); - node->semicolonToken = loc(8); - sym(1).Node = node; -} break; - -case 68: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3), - sym(5).Statement); - node->propertyToken = loc(1); - node->typeToken = loc(2); - node->identifierToken = loc(3); - node->colonToken = loc(4); - sym(1).Node = node; -} break; - -case 69: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), - sym(6).Statement); - node->isReadonlyMember = true; - node->readonlyToken = loc(1); - node->propertyToken = loc(2); - node->typeToken = loc(3); - node->identifierToken = loc(4); - node->colonToken = loc(5); - sym(1).Node = node; -} break; - -case 70: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), - sym(6).Statement); - node->isDefaultMember = true; - node->defaultToken = loc(1); - node->propertyToken = loc(2); - node->typeToken = loc(3); - node->identifierToken = loc(4); - node->colonToken = loc(5); - sym(1).Node = node; -} break; - -case 71: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6)); - node->typeModifier = stringRef(2); - node->propertyToken = loc(1); - node->typeModifierToken = loc(2); - node->typeToken = loc(4); - node->identifierToken = loc(6); - node->semicolonToken = loc(7); // insert a fake ';' before ':' - - AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6)); - propertyName->identifierToken = loc(6); - propertyName->next = nullptr; - - AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding( - propertyName, sym(9).UiArrayMemberList->finish()); - binding->colonToken = loc(7); - binding->lbracketToken = loc(8); - binding->rbracketToken = loc(10); - - node->binding = binding; - - sym(1).Node = node; -} break; - -case 72: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3)); - node->propertyToken = loc(1); - node->typeToken = loc(2); - node->identifierToken = loc(3); - node->semicolonToken = loc(4); // insert a fake ';' before ':' - - AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3)); - propertyName->identifierToken = loc(3); - propertyName->next = nullptr; - - AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding( - propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer); - binding->colonToken = loc(4); - - node->binding = binding; - - sym(1).Node = node; -} break; - -case 73: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4)); - node->isReadonlyMember = true; - node->readonlyToken = loc(1); - node->propertyToken = loc(2); - node->typeToken = loc(3); - node->identifierToken = loc(4); - node->semicolonToken = loc(5); // insert a fake ';' before ':' - - AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(4)); - propertyName->identifierToken = loc(4); - propertyName->next = nullptr; - - AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding( - propertyName, sym(6).UiQualifiedId, sym(7).UiObjectInitializer); - binding->colonToken = loc(5); - - node->binding = binding; - - sym(1).Node = node; -} break; - -case 74: { - sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); -} break; - -case 75: { - sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); -} break; - -case 76: { - AST::UiEnumDeclaration *enumDeclaration = new (pool) AST::UiEnumDeclaration(stringRef(2), sym(4).UiEnumMemberList->finish()); - enumDeclaration->enumToken = loc(1); - enumDeclaration->rbraceToken = loc(5); - sym(1).Node = enumDeclaration; - break; -} - -case 77: { - AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1)); - node->memberToken = loc(1); - sym(1).Node = node; - break; -} - -case 78: { - AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1), sym(3).dval); - node->memberToken = loc(1); - node->valueToken = loc(3); - sym(1).Node = node; - break; -} - -case 79: { - AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3)); - node->memberToken = loc(3); - sym(1).Node = node; - break; -} - -case 80: { - AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3), sym(5).dval); - node->memberToken = loc(3); - node->valueToken = loc(5); - sym(1).Node = node; - break; -} - -case 88: { - AST::ThisExpression *node = new (pool) AST::ThisExpression(); - node->thisToken = loc(1); - sym(1).Node = node; -} break; - -case 89: { - AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1)); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; - -case 90: { - AST::NullExpression *node = new (pool) AST::NullExpression(); - node->nullToken = loc(1); - sym(1).Node = node; -} break; - -case 91: { - AST::TrueLiteral *node = new (pool) AST::TrueLiteral(); - node->trueToken = loc(1); - sym(1).Node = node; -} break; - -case 92: { - AST::FalseLiteral *node = new (pool) AST::FalseLiteral(); - node->falseToken = loc(1); - sym(1).Node = node; -} break; - -case 93: { - AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval); - node->literalToken = loc(1); - sym(1).Node = node; -} break; -case 94: -case 95: { - AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1)); - node->literalToken = loc(1); - sym(1).Node = node; -} break; - -case 96: { - bool rx = lexer->scanRegExp(Lexer::NoPrefix); - if (!rx) { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); - return false; // ### remove me - } - - loc(1).length = lexer->tokenLength(); - yylloc = loc(1); // adjust the location of the current token - - AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral( - driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags()); - node->literalToken = loc(1); - sym(1).Node = node; -} break; - -case 97: { - bool rx = lexer->scanRegExp(Lexer::EqualPrefix); - if (!rx) { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); - return false; - } - - loc(1).length = lexer->tokenLength(); - yylloc = loc(1); // adjust the location of the current token - - AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral( - driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags()); - node->literalToken = loc(1); - sym(1).Node = node; -} break; - -case 98: { - AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) nullptr); - node->lbracketToken = loc(1); - node->rbracketToken = loc(2); - sym(1).Node = node; -} break; - -case 99: { - AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish()); - node->lbracketToken = loc(1); - node->rbracketToken = loc(3); - sym(1).Node = node; -} break; - -case 100: { - AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ()); - node->lbracketToken = loc(1); - node->rbracketToken = loc(3); - sym(1).Node = node; -} break; - -case 101: { - AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), - (AST::Elision *) nullptr); - node->lbracketToken = loc(1); - node->commaToken = loc(3); - node->rbracketToken = loc(4); - sym(1).Node = node; -} break; - -case 102: { - AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), - sym(4).Elision->finish()); - node->lbracketToken = loc(1); - node->commaToken = loc(3); - node->rbracketToken = loc(5); - sym(1).Node = node; -} break; - -case 103: { - AST::ObjectLiteral *node = nullptr; - if (sym(2).Node) - node = new (pool) AST::ObjectLiteral( - sym(2).PropertyAssignmentList->finish ()); - else - node = new (pool) AST::ObjectLiteral(); - node->lbraceToken = loc(1); - node->rbraceToken = loc(3); - sym(1).Node = node; -} break; - -case 104: { - AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral( - sym(2).PropertyAssignmentList->finish ()); - node->lbraceToken = loc(1); - node->rbraceToken = loc(4); - sym(1).Node = node; -} break; - -case 105: { - AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression); - node->lparenToken = loc(1); - node->rparenToken = loc(3); - sym(1).Node = node; -} break; - -case 106: { - if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, - QLatin1String("Ignored annotation"))); - - sym(1).Expression = mem->base; - } - - if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) { - sym(1).UiQualifiedId = qualifiedId; - } else { - sym(1).UiQualifiedId = nullptr; - - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), - QLatin1String("Expected a qualified name id"))); - - return false; // ### recover - } -} break; - -case 107: { - sym(1).Node = new (pool) AST::ElementList((AST::Elision *) nullptr, sym(1).Expression); -} break; - -case 108: { - sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression); -} break; - -case 109: { - AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, - (AST::Elision *) nullptr, sym(3).Expression); - node->commaToken = loc(2); - sym(1).Node = node; -} break; - -case 110: { - AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(), - sym(4).Expression); - node->commaToken = loc(2); - sym(1).Node = node; -} break; - -case 111: { - AST::Elision *node = new (pool) AST::Elision(); - node->commaToken = loc(1); - sym(1).Node = node; -} break; - -case 112: { - AST::Elision *node = new (pool) AST::Elision(sym(1).Elision); - node->commaToken = loc(2); - sym(1).Node = node; -} break; - -case 113: { - AST::PropertyNameAndValue *node = new (pool) AST::PropertyNameAndValue( - sym(1).PropertyName, sym(3).Expression); - node->colonToken = loc(2); - sym(1).Node = node; -} break; - -case 114: { - AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter( - sym(2).PropertyName, sym(6).FunctionBody); - node->getSetToken = loc(1); - node->lparenToken = loc(3); - node->rparenToken = loc(4); - node->lbraceToken = loc(5); - node->rbraceToken = loc(7); - sym(1).Node = node; -} break; - -case 115: { - AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter( - sym(2).PropertyName, sym(4).FormalParameterList, sym(7).FunctionBody); - node->getSetToken = loc(1); - node->lparenToken = loc(3); - node->rparenToken = loc(5); - node->lbraceToken = loc(6); - node->rbraceToken = loc(8); - sym(1).Node = node; -} break; - -case 116: { - sym(1).Node = new (pool) AST::PropertyAssignmentList(sym(1).PropertyAssignment); -} break; - -case 117: { - AST::PropertyAssignmentList *node = new (pool) AST::PropertyAssignmentList( - sym(1).PropertyAssignmentList, sym(3).PropertyAssignment); - node->commaToken = loc(2); - sym(1).Node = node; -} break; - -case 118: { - AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); - node->propertyNameToken = loc(1); - sym(1).Node = node; -} break; - -case 119: { - AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1)); - node->propertyNameToken = loc(1); - sym(1).Node = node; -} break; - -case 120: { - AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval); - node->propertyNameToken = loc(1); - sym(1).Node = node; -} break; - -case 121: { - AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); - node->propertyNameToken = loc(1); - sym(1).Node = node; -} break; - -case 159: { - AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); - node->lbracketToken = loc(2); - node->rbracketToken = loc(4); - sym(1).Node = node; -} break; - -case 160: { - AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); - node->dotToken = loc(2); - node->identifierToken = loc(3); - sym(1).Node = node; -} break; - -case 161: { - AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList); - node->newToken = loc(1); - node->lparenToken = loc(3); - node->rparenToken = loc(5); - sym(1).Node = node; -} break; - -case 163: { - AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression); - node->newToken = loc(1); - sym(1).Node = node; -} break; - -case 164: { - AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; - -case 165: { - AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; - -case 166: { - AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); - node->lbracketToken = loc(2); - node->rbracketToken = loc(4); - sym(1).Node = node; -} break; - -case 167: { - AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); - node->dotToken = loc(2); - node->identifierToken = loc(3); - sym(1).Node = node; -} break; - -case 168: { - sym(1).Node = nullptr; -} break; - -case 169: { - sym(1).Node = sym(1).ArgumentList->finish(); -} break; - -case 170: { - sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression); -} break; - -case 171: { - AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression); - node->commaToken = loc(2); - sym(1).Node = node; -} break; - -case 175: { - AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression); - node->incrementToken = loc(2); - sym(1).Node = node; -} break; - -case 176: { - AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression); - node->decrementToken = loc(2); - sym(1).Node = node; -} break; - -case 178: { - AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression); - node->deleteToken = loc(1); - sym(1).Node = node; -} break; - -case 179: { - AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression); - node->voidToken = loc(1); - sym(1).Node = node; -} break; - -case 180: { - AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression); - node->typeofToken = loc(1); - sym(1).Node = node; -} break; - -case 181: { - AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression); - node->incrementToken = loc(1); - sym(1).Node = node; -} break; - -case 182: { - AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression); - node->decrementToken = loc(1); - sym(1).Node = node; -} break; - -case 183: { - AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression); - node->plusToken = loc(1); - sym(1).Node = node; -} break; - -case 184: { - AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression); - node->minusToken = loc(1); - sym(1).Node = node; -} break; - -case 185: { - AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression); - node->tildeToken = loc(1); - sym(1).Node = node; -} break; - -case 186: { - AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression); - node->notToken = loc(1); - sym(1).Node = node; -} break; - -case 188: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Mul, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 189: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Div, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 190: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Mod, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 192: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Add, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 193: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Sub, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 195: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::LShift, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 196: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::RShift, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 197: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::URShift, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 199: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Lt, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 200: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Gt, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 201: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Le, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 202: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Ge, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 203: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::InstanceOf, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 204: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::In, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 206: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Lt, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 207: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Gt, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 208: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Le, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 209: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Ge, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 210: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::InstanceOf, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 212: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Equal, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 213: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::NotEqual, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 214: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::StrictEqual, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 215: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::StrictNotEqual, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 217: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Equal, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 218: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::NotEqual, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 219: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::StrictEqual, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 220: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::StrictNotEqual, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 222: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitAnd, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 224: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitAnd, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 226: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitXor, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 228: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitXor, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 230: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitOr, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 232: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitOr, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 234: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::And, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 236: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::And, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 238: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Or, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 240: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Or, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 242: { - AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, - sym(3).Expression, sym(5).Expression); - node->questionToken = loc(2); - node->colonToken = loc(4); - sym(1).Node = node; -} break; - -case 244: { - AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, - sym(3).Expression, sym(5).Expression); - node->questionToken = loc(2); - node->colonToken = loc(4); - sym(1).Node = node; -} break; - -case 246: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - sym(2).ival, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 248: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - sym(2).ival, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - -case 249: { - sym(1).ival = QSOperator::Assign; -} break; - -case 250: { - sym(1).ival = QSOperator::InplaceMul; -} break; - -case 251: { - sym(1).ival = QSOperator::InplaceDiv; -} break; - -case 252: { - sym(1).ival = QSOperator::InplaceMod; -} break; - -case 253: { - sym(1).ival = QSOperator::InplaceAdd; -} break; - -case 254: { - sym(1).ival = QSOperator::InplaceSub; -} break; - -case 255: { - sym(1).ival = QSOperator::InplaceLeftShift; -} break; - -case 256: { - sym(1).ival = QSOperator::InplaceRightShift; -} break; - -case 257: { - sym(1).ival = QSOperator::InplaceURightShift; -} break; - -case 258: { - sym(1).ival = QSOperator::InplaceAnd; -} break; - -case 259: { - sym(1).ival = QSOperator::InplaceXor; -} break; - -case 260: { - sym(1).ival = QSOperator::InplaceOr; -} break; - -case 262: { - AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); - node->commaToken = loc(2); - sym(1).Node = node; -} break; - -case 263: { - sym(1).Node = nullptr; -} break; - -case 266: { - AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); - node->commaToken = loc(2); - sym(1).Node = node; -} break; - -case 267: { - sym(1).Node = nullptr; -} break; - -case 284: { - AST::Block *node = new (pool) AST::Block(sym(2).StatementList); - node->lbraceToken = loc(1); - node->rbraceToken = loc(3); - sym(1).Node = node; -} break; - -case 285: { - sym(1).Node = new (pool) AST::StatementList(sym(1).Statement); -} break; - -case 286: { - sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement); -} break; - -case 287: { - sym(1).Node = nullptr; -} break; - -case 288: { - sym(1).Node = sym(1).StatementList->finish (); -} break; - -case 290: { - AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; - if (sym(1).ival == T_LET) - s = AST::VariableDeclaration::BlockScope; - else if (sym(1).ival == T_CONST) - s = AST::VariableDeclaration::ReadOnlyBlockScope; - - AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(s)); - node->declarationKindToken = loc(1); - node->semicolonToken = loc(3); - sym(1).Node = node; -} break; - -case 291: { - sym(1).ival = T_LET; -} break; - -case 292: { - sym(1).ival = T_CONST; -} break; - -case 293: { - sym(1).ival = T_VAR; -} break; - -case 294: { - sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); -} break; - -case 295: { - AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList( - sym(1).VariableDeclarationList, sym(3).VariableDeclaration); - node->commaToken = loc(2); - sym(1).Node = node; -} break; - -case 296: { - sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); -} break; - -case 297: { - sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration); -} break; - -case 298: { - AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; - AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; - -case 299: { - AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; - AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; - -case 300: { - // ### TODO: AST for initializer - sym(1) = sym(2); -} break; - -case 301: { - sym(1).Node = nullptr; -} break; - -case 303: { - // ### TODO: AST for initializer - sym(1) = sym(2); -} break; - -case 304: { - sym(1).Node = nullptr; -} break; - -case 306: { - AST::EmptyStatement *node = new (pool) AST::EmptyStatement(); - node->semicolonToken = loc(1); - sym(1).Node = node; -} break; - -case 308: { - AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression); - node->semicolonToken = loc(2); - sym(1).Node = node; -} break; - -case 309: { - AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement); - node->ifToken = loc(1); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - node->elseToken = loc(6); - sym(1).Node = node; -} break; - -case 310: { - AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement); - node->ifToken = loc(1); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; - -case 313: { - AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression); - node->doToken = loc(1); - node->whileToken = loc(3); - node->lparenToken = loc(4); - node->rparenToken = loc(6); - node->semicolonToken = loc(7); - sym(1).Node = node; -} break; - -case 314: { - AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement); - node->whileToken = loc(1); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; - -case 315: { - AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, - sym(5).Expression, sym(7).Expression, sym(9).Statement); - node->forToken = loc(1); - node->lparenToken = loc(2); - node->firstSemicolonToken = loc(4); - node->secondSemicolonToken = loc(6); - node->rparenToken = loc(8); - sym(1).Node = node; -} break; - -case 316: { - AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; - AST::LocalForStatement *node = new (pool) AST::LocalForStatement( - sym(4).VariableDeclarationList->finish(s), sym(6).Expression, - sym(8).Expression, sym(10).Statement); - node->forToken = loc(1); - node->lparenToken = loc(2); - node->varToken = loc(3); - node->firstSemicolonToken = loc(5); - node->secondSemicolonToken = loc(7); - node->rparenToken = loc(9); - sym(1).Node = node; -} break; - -case 317: { - AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, - sym(5).Expression, sym(7).Statement); - node->forToken = loc(1); - node->lparenToken = loc(2); - node->inToken = loc(4); - node->rparenToken = loc(6); - sym(1).Node = node; -} break; - -case 318: { - AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement( - sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); - node->forToken = loc(1); - node->lparenToken = loc(2); - node->varToken = loc(3); - node->inToken = loc(5); - node->rparenToken = loc(7); - sym(1).Node = node; -} break; - -case 320: { - AST::ContinueStatement *node = new (pool) AST::ContinueStatement(); - node->continueToken = loc(1); - node->semicolonToken = loc(2); - sym(1).Node = node; -} break; - -case 322: { - AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2)); - node->continueToken = loc(1); - node->identifierToken = loc(2); - node->semicolonToken = loc(3); - sym(1).Node = node; -} break; - -case 324: { - AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef()); - node->breakToken = loc(1); - node->semicolonToken = loc(2); - sym(1).Node = node; -} break; - -case 326: { - AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2)); - node->breakToken = loc(1); - node->identifierToken = loc(2); - node->semicolonToken = loc(3); - sym(1).Node = node; -} break; - -case 328: { - AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression); - node->returnToken = loc(1); - node->semicolonToken = loc(3); - sym(1).Node = node; -} break; - -case 329: { - AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement); - node->withToken = loc(1); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; - -case 330: { - AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock); - node->switchToken = loc(1); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; - -case 331: { - AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses); - node->lbraceToken = loc(1); - node->rbraceToken = loc(3); - sym(1).Node = node; -} break; - -case 332: { - AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); - node->lbraceToken = loc(1); - node->rbraceToken = loc(5); - sym(1).Node = node; -} break; - -case 333: { - sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause); -} break; - -case 334: { - sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause); -} break; - -case 335: { - sym(1).Node = nullptr; -} break; - -case 336: { - sym(1).Node = sym(1).CaseClauses->finish (); -} break; - -case 337: { - AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList); - node->caseToken = loc(1); - node->colonToken = loc(3); - sym(1).Node = node; -} break; - -case 338: { - AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList); - node->defaultToken = loc(1); - node->colonToken = loc(2); - sym(1).Node = node; -} break; - -case 339: { - AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); - node->identifierToken = loc(1); - node->colonToken = loc(2); - sym(1).Node = node; -} break; - -case 341: { - AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression); - node->throwToken = loc(1); - node->semicolonToken = loc(3); - sym(1).Node = node; -} break; - -case 342: { - AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch); - node->tryToken = loc(1); - sym(1).Node = node; -} break; - -case 343: { - AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally); - node->tryToken = loc(1); - sym(1).Node = node; -} break; - -case 344: { - AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally); - node->tryToken = loc(1); - sym(1).Node = node; -} break; - -case 345: { - AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block); - node->catchToken = loc(1); - node->lparenToken = loc(2); - node->identifierToken = loc(3); - node->rparenToken = loc(4); - sym(1).Node = node; -} break; - -case 346: { - AST::Finally *node = new (pool) AST::Finally(sym(2).Block); - node->finallyToken = loc(1); - sym(1).Node = node; -} break; - -case 348: { - AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement(); - node->debuggerToken = loc(1); - node->semicolonToken = loc(2); - sym(1).Node = node; -} break; - -case 350: { - AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); - node->functionToken = loc(1); - node->identifierToken = loc(2); - node->lparenToken = loc(3); - node->rparenToken = loc(5); - node->lbraceToken = loc(6); - node->rbraceToken = loc(8); - sym(1).Node = node; -} break; - -case 351: { - AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); - node->functionToken = loc(1); - if (! stringRef(2).isNull()) - node->identifierToken = loc(2); - node->lparenToken = loc(3); - node->rparenToken = loc(5); - node->lbraceToken = loc(6); - node->rbraceToken = loc(8); - sym(1).Node = node; -} break; - -case 352: { - AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).FunctionBody); - node->functionToken = loc(1); - node->lparenToken = loc(2); - node->rparenToken = loc(4); - node->lbraceToken = loc(5); - node->rbraceToken = loc(7); - sym(1).Node = node; -} break; - -case 353: { - AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1)); - node->identifierToken = loc(1); - sym(1).Node = node; -} break; - -case 354: { - AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3)); - node->commaToken = loc(2); - node->identifierToken = loc(3); - sym(1).Node = node; -} break; - -case 355: { - sym(1).Node = nullptr; -} break; - -case 356: { - sym(1).Node = sym(1).FormalParameterList->finish (); -} break; - -case 357: { - sym(1).Node = nullptr; -} break; - -case 359: { - sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ()); -} break; - -case 361: { - sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ()); -} break; - -case 362: { - sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement); -} break; - -case 363: { - sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement); -} break; - -case 364: { - sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement); -} break; - -case 365: { - sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration); -} break; - -case 366: { - sym(1).Node = nullptr; -} break; - - } // switch - action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT); - } // if - } while (action != 0); - - if (first_token == last_token) { - const int errorState = state_stack[tos]; - - // automatic insertion of `;' - if (yytoken != -1 && ((t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken)) - || t_action(errorState, T_COMPATIBILITY_SEMICOLON))) { - SavedToken &tk = token_buffer[0]; - tk.token = yytoken; - tk.dval = yylval; - tk.spell = yytokenspell; - tk.loc = yylloc; - - yylloc = yyprevlloc; - yylloc.offset += yylloc.length; - yylloc.startColumn += yylloc.length; - yylloc.length = 0; - - //const QString msg = QCoreApplication::translate("QQmlParser", "Missing `;'"); - //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg)); - - first_token = &token_buffer[0]; - last_token = &token_buffer[1]; - - yytoken = T_SEMICOLON; - yylval = 0; - - action = errorState; - - goto _Lcheck_token; - } - - hadErrors = true; - - token_buffer[0].token = yytoken; - token_buffer[0].dval = yylval; - token_buffer[0].spell = yytokenspell; - token_buffer[0].loc = yylloc; - - token_buffer[1].token = yytoken = lexer->lex(); - token_buffer[1].dval = yylval = lexer->tokenValue(); - token_buffer[1].spell = yytokenspell = lexer->tokenSpell(); - token_buffer[1].loc = yylloc = location(lexer); - - if (t_action(errorState, yytoken)) { - QString msg; - int token = token_buffer[0].token; - if (token < 0 || token >= TERMINAL_COUNT) - msg = QCoreApplication::translate("QQmlParser", "Syntax error"); - else - msg = QCoreApplication::translate("QQmlParser", "Unexpected token `%1'").arg(QLatin1String(spell[token])); - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); - - action = errorState; - goto _Lcheck_token; - } - - static int tokens[] = { - T_PLUS, - T_EQ, - - T_COMMA, - T_COLON, - T_SEMICOLON, - - T_RPAREN, T_RBRACKET, T_RBRACE, - - T_NUMERIC_LITERAL, - T_IDENTIFIER, - - T_LPAREN, T_LBRACKET, T_LBRACE, - - EOF_SYMBOL - }; - - for (int *tk = tokens; *tk != EOF_SYMBOL; ++tk) { - int a = t_action(errorState, *tk); - if (a > 0 && t_action(a, yytoken)) { - const QString msg = QCoreApplication::translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[*tk])); - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); - - yytoken = *tk; - yylval = 0; - yylloc = token_buffer[0].loc; - yylloc.length = 0; - - first_token = &token_buffer[0]; - last_token = &token_buffer[2]; - - action = errorState; - goto _Lcheck_token; - } - } - - for (int tk = 1; tk < TERMINAL_COUNT; ++tk) { - if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM || - tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION || - tk == T_FEED_JS_SOURCE_ELEMENT) - continue; - - int a = t_action(errorState, tk); - if (a > 0 && t_action(a, yytoken)) { - const QString msg = QCoreApplication::translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[tk])); - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); - - yytoken = tk; - yylval = 0; - yylloc = token_buffer[0].loc; - yylloc.length = 0; - - action = errorState; - goto _Lcheck_token; - } - } - - const QString msg = QCoreApplication::translate("QQmlParser", "Syntax error"); - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); - } - - return false; -} - -QT_QML_END_NAMESPACE - - diff --git a/src/qml/parser/qqmljsparser_p.h b/src/qml/parser/qqmljsparser_p.h deleted file mode 100644 index b4aecd2f08..0000000000 --- a/src/qml/parser/qqmljsparser_p.h +++ /dev/null @@ -1,258 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -// -// W A R N I N G -// ------------- -// -// This file is automatically generated from qqmljs.g. -// Changes should be made to that file, not here. Any change to this file will -// be lost! -// -// To regenerate this file, run: -// qlalr --no-debug --no-lines --qt qqmljs.g -// - -#ifndef QQMLJSPARSER_P_H -#define QQMLJSPARSER_P_H - -#include "qqmljsglobal_p.h" -#include "qqmljsgrammar_p.h" -#include "qqmljsast_p.h" -#include "qqmljsengine_p.h" - -#include <QtCore/qlist.h> -#include <QtCore/qstring.h> - -QT_QML_BEGIN_NAMESPACE - -namespace QQmlJS { - -class Engine; - -class QML_PARSER_EXPORT Parser: protected QQmlJSGrammar -{ -public: - union Value { - int ival; - double dval; - AST::ArgumentList *ArgumentList; - AST::CaseBlock *CaseBlock; - AST::CaseClause *CaseClause; - AST::CaseClauses *CaseClauses; - AST::Catch *Catch; - AST::DefaultClause *DefaultClause; - AST::ElementList *ElementList; - AST::Elision *Elision; - AST::ExpressionNode *Expression; - AST::Finally *Finally; - AST::FormalParameterList *FormalParameterList; - AST::FunctionBody *FunctionBody; - AST::FunctionDeclaration *FunctionDeclaration; - AST::Node *Node; - AST::PropertyName *PropertyName; - AST::PropertyAssignment *PropertyAssignment; - AST::PropertyAssignmentList *PropertyAssignmentList; - AST::SourceElement *SourceElement; - AST::SourceElements *SourceElements; - AST::Statement *Statement; - AST::StatementList *StatementList; - AST::Block *Block; - AST::VariableDeclaration *VariableDeclaration; - AST::VariableDeclarationList *VariableDeclarationList; - - AST::UiProgram *UiProgram; - AST::UiHeaderItemList *UiHeaderItemList; - AST::UiPragma *UiPragma; - AST::UiImport *UiImport; - AST::UiParameterList *UiParameterList; - AST::UiPublicMember *UiPublicMember; - AST::UiObjectDefinition *UiObjectDefinition; - AST::UiObjectInitializer *UiObjectInitializer; - AST::UiObjectBinding *UiObjectBinding; - AST::UiScriptBinding *UiScriptBinding; - AST::UiArrayBinding *UiArrayBinding; - AST::UiObjectMember *UiObjectMember; - AST::UiObjectMemberList *UiObjectMemberList; - AST::UiArrayMemberList *UiArrayMemberList; - AST::UiQualifiedId *UiQualifiedId; - AST::UiQualifiedPragmaId *UiQualifiedPragmaId; - AST::UiEnumMemberList *UiEnumMemberList; - }; - -public: - Parser(Engine *engine); - ~Parser(); - - // parse a UI program - bool parse() { return parse(T_FEED_UI_PROGRAM); } - bool parseStatement() { return parse(T_FEED_JS_STATEMENT); } - bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); } - bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); } - bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); } - bool parseProgram() { return parse(T_FEED_JS_PROGRAM); } - - AST::UiProgram *ast() const - { return AST::cast<AST::UiProgram *>(program); } - - AST::Statement *statement() const - { - if (! program) - return nullptr; - - return program->statementCast(); - } - - AST::ExpressionNode *expression() const - { - if (! program) - return nullptr; - - return program->expressionCast(); - } - - AST::UiObjectMember *uiObjectMember() const - { - if (! program) - return nullptr; - - return program->uiObjectMemberCast(); - } - - AST::Node *rootNode() const - { return program; } - - QList<DiagnosticMessage> diagnosticMessages() const - { return diagnostic_messages; } - - inline DiagnosticMessage diagnosticMessage() const - { - for (const DiagnosticMessage &d : diagnostic_messages) { - if (d.kind != DiagnosticMessage::Warning) - return d; - } - - return DiagnosticMessage(); - } - - inline QString errorMessage() const - { return diagnosticMessage().message; } - - inline int errorLineNumber() const - { return diagnosticMessage().loc.startLine; } - - inline int errorColumnNumber() const - { return diagnosticMessage().loc.startColumn; } - -protected: - bool parse(int startToken); - - void reallocateStack(); - - inline Value &sym(int index) - { return sym_stack [tos + index - 1]; } - - inline QStringRef &stringRef(int index) - { return string_stack [tos + index - 1]; } - - inline AST::SourceLocation &loc(int index) - { return location_stack [tos + index - 1]; } - - AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr); - AST::UiQualifiedPragmaId *reparseAsQualifiedPragmaId(AST::ExpressionNode *expr); - -protected: - Engine *driver; - MemoryPool *pool; - int tos; - int stack_size; - Value *sym_stack; - int *state_stack; - AST::SourceLocation *location_stack; - QStringRef *string_stack; - - AST::Node *program; - - // error recovery - enum { TOKEN_BUFFER_SIZE = 3 }; - - struct SavedToken { - int token; - double dval; - AST::SourceLocation loc; - QStringRef spell; - }; - - double yylval; - QStringRef yytokenspell; - AST::SourceLocation yylloc; - AST::SourceLocation yyprevlloc; - - SavedToken token_buffer[TOKEN_BUFFER_SIZE]; - SavedToken *first_token; - SavedToken *last_token; - - QList<DiagnosticMessage> diagnostic_messages; -}; - -} // end of namespace QQmlJS - - - -#define J_SCRIPT_REGEXPLITERAL_RULE1 96 - -#define J_SCRIPT_REGEXPLITERAL_RULE2 97 - -QT_QML_END_NAMESPACE - - - -#endif // QQMLJSPARSER_P_H diff --git a/src/qml/qml.pro b/src/qml/qml.pro index 2137877427..a76a87b153 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -31,7 +31,7 @@ DEFINES += QT_NO_FOREACH !equals(tag, "$${LITERAL_DOLLAR}Format:%H$${LITERAL_DOLLAR}") { QML_COMPILE_HASH = $$tag } else:exists($$PWD/../../.git) { - commit = $$system(git describe --tags --always --long --dirty) + commit = $$system(git rev-parse HEAD) QML_COMPILE_HASH = $$commit } compile_hash_contents = \ @@ -73,7 +73,7 @@ include(jsruntime/jsruntime.pri) include(jit/jit.pri) include(qml/qml.pri) include(debugger/debugger.pri) -qtConfig(animation) { +qtConfig(qml-animation) { include(animations/animations.pri) } include(types/types.pri) diff --git a/src/qml/qml/ftw/qflagpointer_p.h b/src/qml/qml/ftw/qflagpointer_p.h index 91ce74bec9..71b41cd30b 100644 --- a/src/qml/qml/ftw/qflagpointer_p.h +++ b/src/qml/qml/ftw/qflagpointer_p.h @@ -82,6 +82,8 @@ public: inline T *data() const; + inline explicit operator bool() const; + private: quintptr ptr_value = 0; @@ -230,6 +232,12 @@ T *QFlagPointer<T>::data() const return (T *)(ptr_value & ~FlagsMask); } +template<typename T> +QFlagPointer<T>::operator bool() const +{ + return data() != nullptr; +} + template<typename T, typename T2> QBiPointer<T, T2>::QBiPointer() { diff --git a/src/qml/qml/ftw/qqmlrefcount_p.h b/src/qml/qml/ftw/qqmlrefcount_p.h index 3cfb345b30..d32a08e0f5 100644 --- a/src/qml/qml/ftw/qqmlrefcount_p.h +++ b/src/qml/qml/ftw/qqmlrefcount_p.h @@ -85,19 +85,23 @@ public: inline QQmlRefPointer(); inline QQmlRefPointer(T *, Mode m = AddRef); inline QQmlRefPointer(const QQmlRefPointer<T> &); + inline QQmlRefPointer(QQmlRefPointer<T> &&); inline ~QQmlRefPointer(); inline QQmlRefPointer<T> &operator=(const QQmlRefPointer<T> &o); + inline QQmlRefPointer<T> &operator=(QQmlRefPointer<T> &&o); inline bool isNull() const { return !o; } inline T* operator->() const { return o; } inline T& operator*() const { return *o; } - inline operator T*() const { return o; } + explicit inline operator bool() const { return o != nullptr; } inline T* data() const { return o; } inline QQmlRefPointer<T> &adopt(T *); + inline T* take() { T *res = o; o = nullptr; return res; } + private: T *o; }; @@ -156,6 +160,12 @@ QQmlRefPointer<T>::QQmlRefPointer(const QQmlRefPointer<T> &other) if (o) o->addref(); } +template <class T> +QQmlRefPointer<T>::QQmlRefPointer(QQmlRefPointer<T> &&other) + : o(other.take()) +{ +} + template<class T> QQmlRefPointer<T>::~QQmlRefPointer() { @@ -171,6 +181,14 @@ QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(const QQmlRefPointer<T> &other) return *this; } +template <class T> +QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(QQmlRefPointer<T> &&other) +{ + QQmlRefPointer<T> m(std::move(other)); + qSwap(o, m.o); + return *this; +} + /*! Takes ownership of \a other. take() does *not* add a reference, as it assumes ownership of the callers reference of other. diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 412dc6cba2..6d69294c17 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -20,7 +20,6 @@ SOURCES += \ $$PWD/qqmlinfo.cpp \ $$PWD/qqmlerror.cpp \ $$PWD/qqmlvaluetype.cpp \ - $$PWD/qqmlxmlhttprequest.cpp \ $$PWD/qqmlcleanup.cpp \ $$PWD/qqmlpropertycache.cpp \ $$PWD/qqmlnotifier.cpp \ @@ -31,7 +30,6 @@ SOURCES += \ $$PWD/qqmlextensionplugin.cpp \ $$PWD/qqmlimport.cpp \ $$PWD/qqmllist.cpp \ - $$PWD/qqmllocale.cpp \ $$PWD/qqmljavascriptexpression.cpp \ $$PWD/qqmlabstractbinding.cpp \ $$PWD/qqmlvaluetypeproxybinding.cpp \ @@ -85,7 +83,6 @@ HEADERS += \ $$PWD/qqmldata_p.h \ $$PWD/qqmlerror.h \ $$PWD/qqmlvaluetype_p.h \ - $$PWD/qqmlxmlhttprequest_p.h \ $$PWD/qqmlcleanup_p.h \ $$PWD/qqmlpropertycache_p.h \ $$PWD/qqmlpropertyindex_p.h \ @@ -99,7 +96,6 @@ HEADERS += \ $$PWD/qqmlimport_p.h \ $$PWD/qqmlextensionplugin.h \ $$PWD/qqmlscriptstring_p.h \ - $$PWD/qqmllocale_p.h \ $$PWD/qqmlcomponentattached_p.h \ $$PWD/qqmljavascriptexpression_p.h \ $$PWD/qqmlabstractbinding_p.h \ @@ -120,5 +116,22 @@ HEADERS += \ $$PWD/qqmldelayedcallqueue_p.h \ $$PWD/qqmlloggingcategory_p.h +qtConfig(qml-xml-http-request) { + HEADERS += \ + $$PWD/qqmlxmlhttprequest_p.h + + SOURCES += \ + $$PWD/qqmlxmlhttprequest.cpp + +} + +qtConfig(qml-locale) { + HEADERS += \ + $$PWD/qqmllocale_p.h + + SOURCES += \ + $$PWD/qqmllocale.cpp +} + include(ftw/ftw.pri) include(v8/v8.pri) diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index 213f23cd98..2a8e236905 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -645,6 +645,8 @@ inline int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, i return QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, &type); } +int Q_QML_EXPORT qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName); + QT_END_NAMESPACE QML_DECLARE_TYPE(QObject) diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 30a18440a8..a4b3f1f4e4 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -337,7 +337,7 @@ protected: class QQmlTranslationBinding : public GenericBinding<QMetaType::QString> { public: - QQmlTranslationBinding(QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Binding *binding) + QQmlTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding) { setCompilationUnit(compilationUnit); m_binding = binding; @@ -374,11 +374,13 @@ public: } } + bool hasDependencies() const override final { return true; } + private: const QV4::CompiledData::Binding *m_binding; }; -QQmlBinding *QQmlBinding::createTranslationBinding(QV4::CompiledData::CompilationUnit *unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt) +QQmlBinding *QQmlBinding::createTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt) { QQmlTranslationBinding *b = new QQmlTranslationBinding(unit, binding); @@ -663,6 +665,11 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const return dependencies; } +bool QQmlBinding::hasDependencies() const +{ + return !permanentGuards.isEmpty() || !activeGuards.isEmpty() || translationsCaptured(); +} + class QObjectPointerBinding: public QQmlNonbindingBinding { QQmlMetaObject targetMetaObject; diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index a1295bd0ac..f192de4342 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -79,7 +79,7 @@ public: const QString &url = QString(), quint16 lineNumber = 0); static QQmlBinding *create(const QQmlPropertyData *property, QV4::Function *function, QObject *obj, QQmlContextData *ctxt, QV4::ExecutionContext *scope); - static QQmlBinding *createTranslationBinding(QV4::CompiledData::CompilationUnit *unit, const QV4::CompiledData::Binding *binding, + static QQmlBinding *createTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt); ~QQmlBinding() override; @@ -118,6 +118,7 @@ public: * Call this method from the UI thread. */ QVector<QQmlProperty> dependencies() const; + virtual bool hasDependencies() const; protected: virtual void doUpdate(const DeleteWatcher &watcher, diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 8885e69161..9b43ea0531 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -334,7 +334,7 @@ void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p) emit q->progressChanged(p); } -void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data) +void QQmlComponentPrivate::fromTypeData(const QQmlRefPointer<QQmlTypeData> &data) { url = data->finalUrl(); compilationUnit = data->compilationUnit(); @@ -343,15 +343,12 @@ void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data) Q_ASSERT(data->isError()); state.errors = data->errors(); } - - data->release(); } void QQmlComponentPrivate::clear() { if (typeData) { typeData->unregisterCallback(this); - typeData->release(); typeData = nullptr; } @@ -387,7 +384,7 @@ QQmlComponent::~QQmlComponent() if (d->typeData) { d->typeData->unregisterCallback(d); - d->typeData->release(); + d->typeData = nullptr; } } @@ -580,7 +577,7 @@ void QQmlComponent::setData(const QByteArray &data, const QUrl &url) d->url = url; - QQmlTypeData *typeData = QQmlEnginePrivate::get(d->engine)->typeLoader.getType(data, url); + QQmlRefPointer<QQmlTypeData> typeData = QQmlEnginePrivate::get(d->engine)->typeLoader.getType(data, url); if (typeData->isCompleteOrError()) { d->fromTypeData(typeData); @@ -667,7 +664,7 @@ void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::Compilatio ? QQmlTypeLoader::Asynchronous : QQmlTypeLoader::PreferSynchronous; - QQmlTypeData *data = QQmlEnginePrivate::get(engine)->typeLoader.getType(url, loaderMode); + QQmlRefPointer<QQmlTypeData> data = QQmlEnginePrivate::get(engine)->typeLoader.getType(url, loaderMode); if (data->isCompleteOrError()) { fromTypeData(data); @@ -848,13 +845,10 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) // Do not create infinite recursion in object creation static const int maxCreationDepth = 10; - if (++creationDepth.localData() >= maxCreationDepth) { + if (creationDepth.localData() >= maxCreationDepth) { qWarning("QQmlComponent: Component creation is recursing - aborting"); - --creationDepth.localData(); return nullptr; } - Q_ASSERT(creationDepth.localData() >= 1); - depthIncreased = true; QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); @@ -878,10 +872,6 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) ddata->indestructible = true; ddata->explicitIndestructibleSet = true; ddata->rootObjectInCreation = false; - } else { - Q_ASSERT(creationDepth.localData() >= 1); - --creationDepth.localData(); - depthIncreased = false; } return rv; @@ -955,14 +945,10 @@ void QQmlComponent::completeCreate() void QQmlComponentPrivate::completeCreate() { if (state.completePending) { + ++creationDepth.localData(); QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); complete(ep, &state); - } - - if (depthIncreased) { - Q_ASSERT(creationDepth.localData() >= 1); --creationDepth.localData(); - depthIncreased = false; } } @@ -1421,9 +1407,9 @@ void QQmlComponent::incubateObject(QQmlV4Function *args) QQmlComponentExtension *e = componentExtension(args->v4engine()); - QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocObject<QV4::QmlIncubatorObject>(mode)); + QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocate<QV4::QmlIncubatorObject>(mode)); QV4::ScopedObject p(scope, e->incubationProto.value()); - r->setPrototype(p); + r->setPrototypeOf(p); if (!valuemap->isUndefined()) r->d()->valuemap.set(scope.engine, valuemap); diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index 2a8d36f317..9b2db4bccf 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -79,7 +79,7 @@ class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public public: QQmlComponentPrivate() - : typeData(nullptr), progress(0.), start(-1), engine(nullptr), creationContext(nullptr), depthIncreased(false) {} + : progress(0.), start(-1), engine(nullptr), creationContext(nullptr) {} void loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous); @@ -95,11 +95,11 @@ public: QQmlContextData *context, QQmlContextData *forContext); - QQmlTypeData *typeData; + QQmlRefPointer<QQmlTypeData> typeData; void typeDataReady(QQmlTypeData *) override; void typeDataProgress(QQmlTypeData *, qreal) override; - void fromTypeData(QQmlTypeData *data); + void fromTypeData(const QQmlRefPointer<QQmlTypeData> &data); QUrl url; qreal progress; @@ -136,7 +136,6 @@ public: QQmlEngine *engine; QQmlGuardedContextData creationContext; - bool depthIncreased; void clear(); diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 59fefde893..2468de6857 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -232,7 +232,7 @@ public: QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; QVector<DeferredData *> deferredData; - void deferData(int objectIndex, QV4::CompiledData::CompilationUnit *, QQmlContextData *); + void deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, QQmlContextData *); void releaseDeferredData(); QV4::WeakValue jsWrapper; diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp index 5bcf5cd586..61cb0a9065 100644 --- a/src/qml/qml/qqmldelayedcallqueue.cpp +++ b/src/qml/qml/qqmldelayedcallqueue.cpp @@ -71,7 +71,7 @@ void QQmlDelayedCallQueue::DelayedFunctionCall::execute(QV4::ExecutionEngine *en *jsCallData->thisObject = QV4::Encode::undefined(); for (int i = 0; i < argCount; i++) { - jsCallData->args[i] = array->getIndexed(i); + jsCallData->args[i] = array->get(i); } callback->call(jsCallData); @@ -186,7 +186,7 @@ void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, const QV4 QV4::ScopedArrayObject array(scope, engine->newArrayObject(length)); uint i = 0; for (int j = offset, ej = argc; j < ej; ++i, ++j) - array->putIndexed(i, argv[j]); + array->put(i, argv[j]); dfc.m_args.set(engine, array); } diff --git a/src/qml/qml/qqmldirparser.cpp b/src/qml/qml/qqmldirparser.cpp index 4cca8a4d58..8c89cf0e61 100644 --- a/src/qml/qml/qqmldirparser.cpp +++ b/src/qml/qml/qqmldirparser.cpp @@ -107,6 +107,7 @@ bool QQmlDirParser::parse(const QString &source) _components.clear(); _scripts.clear(); _designerSupported = false; + _className.clear(); quint16 lineNumber = 0; bool firstLine = true; @@ -196,7 +197,8 @@ bool QQmlDirParser::parse(const QString &source) continue; } - // Ignore these. qmlimportscanner uses them. + _className = sections[1]; + } else if (sections[0] == QLatin1String("internal")) { if (sectionCount != 3) { reportError(lineNumber, 0, @@ -377,6 +379,11 @@ bool QQmlDirParser::designerSupported() const return _designerSupported; } +QString QQmlDirParser::className() const +{ + return _className; +} + QDebug &operator<< (QDebug &debug, const QQmlDirParser::Component &component) { const QString output = QStringLiteral("{%1 %2.%3}"). diff --git a/src/qml/qml/qqmldirparser_p.h b/src/qml/qml/qqmldirparser_p.h index 820c40238d..d7e29813d1 100644 --- a/src/qml/qml/qqmldirparser_p.h +++ b/src/qml/qml/qqmldirparser_p.h @@ -135,6 +135,8 @@ public: QList<TypeInfo> typeInfos() const; #endif + QString className() const; + private: bool maybeAddComponent(const QString &typeName, const QString &fileName, const QString &version, QHash<QString,Component> &hash, int lineNumber = -1, bool multi = true); void reportError(quint16 line, quint16 column, const QString &message); @@ -150,6 +152,7 @@ private: #ifdef QT_CREATOR QList<TypeInfo> _typeInfos; #endif + QString _className; }; typedef QHash<QString,QQmlDirParser::Component> QQmlDirComponents; diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index c6b39581a7..119120572c 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -48,7 +48,6 @@ #include "qqmlcomponent.h" #include "qqmlvme_p.h" #include "qqmlstringconverters_p.h" -#include "qqmlxmlhttprequest_p.h" #include "qqmlscriptstring.h" #include "qqmlglobal_p.h" #include "qqmlcomponent_p.h" @@ -79,13 +78,17 @@ #include <private/qobject_p.h> #include <private/qmetaobject_p.h> +#if QT_CONFIG(qml_locale) #include <private/qqmllocale_p.h> +#endif #include <private/qqmlbind_p.h> #include <private/qqmlconnections_p.h> -#if QT_CONFIG(animation) +#if QT_CONFIG(qml_animation) #include <private/qqmltimer_p.h> #endif +#if QT_CONFIG(qml_list_model) #include <private/qqmllistmodel_p.h> +#endif #include <private/qqmlplatform_p.h> #include <private/qquickpackage_p.h> #if QT_CONFIG(qml_delegate_model) @@ -219,21 +222,25 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int qmlRegisterType<QQmlBind,8>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "Binding"); //Only available in >=2.8 qmlRegisterType<QQmlConnections,1>(uri, versionMajor, (versionMinor < 3 ? 3 : versionMinor), "Connections"); //Only available in >=2.3 qmlRegisterType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections"); -#if QT_CONFIG(animation) +#if QT_CONFIG(qml_animation) qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer"); #endif qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1 qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections", new QQmlConnectionsParser); qmlRegisterType<QQmlInstanceModel>(); - qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "LoggingCategory"); //Only available in >=2.8 + + qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, 8, "LoggingCategory"); //Only available in >=2.8 + qmlRegisterType<QQmlLoggingCategory,1>(uri, versionMajor, 12, "LoggingCategory"); //Only available in >=2.12 } // These QtQuick types' implementation resides in the QtQml module void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor) { +#if QT_CONFIG(qml_list_model) qmlRegisterType<QQmlListElement>(uri, versionMajor, versionMinor, "ListElement"); // Now in QtQml.Models, here for compatibility qmlRegisterCustomType<QQmlListModel>(uri, versionMajor, versionMinor, "ListModel", new QQmlListModelParser); // Now in QtQml.Models, here for compatibility +#endif qmlRegisterType<QQuickWorkerScript>(uri, versionMajor, versionMinor, "WorkerScript"); qmlRegisterType<QQuickPackage>(uri, versionMajor, versionMinor, "Package"); #if QT_CONFIG(qml_delegate_model) @@ -250,7 +257,9 @@ void QQmlEnginePrivate::defineQtQuick2Module() // register the QtQuick2 types which are implemented in the QtQml module. registerQtQuick2Types("QtQuick",2,0); +#if QT_CONFIG(qml_locale) qmlRegisterUncreatableType<QQmlLocale>("QtQuick", 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); +#endif // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward qmlRegisterModule("QtQuick", 2, QT_VERSION_MINOR); @@ -951,7 +960,9 @@ void QQmlEnginePrivate::init() if (baseModulesUninitialized) { qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component"); // required for the Compiler. registerBaseTypes("QtQml", 2, 0); // import which provides language building blocks. +#if QT_CONFIG(qml_locale) qmlRegisterUncreatableType<QQmlLocale>("QtQml", 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); +#endif // Auto-increment the import to stay in sync with ALL future QtQml minor versions from 5.11 onward qmlRegisterModule("QtQml", 2, QT_VERSION_MINOR); @@ -1365,6 +1376,71 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled) } /*! + \fn template<typename T> T QQmlEngine::singletonInstance(int qmlTypeId) + + Returns the instance of a singleton type that was registered under \a qmlTypeId. + + The template argument \e T may be either QJSValue or a pointer to a QObject-derived + type and depends on how the singleton was registered. If no instance of \e T has been + created yet, it is created now. If \a qmlTypeId does not represent a valid singleton + type, either a default constructed QJSValue or a \c nullptr is returned. + + QObject* example: + \code + class MySingleton : public QObject { + Q_OBJECT + static int typeId; + // ... + }; + + // Register with QObject* callback + MySingleton::typeId = qmlRegisterSingletonType<MySingleton>(...); + + // Retrieve as QObject* + QQmlEngine engine; + MySingleton* instance = engine.singletonInstance<MySingleton*>(MySingleton::typeId); + \endcode + + QJSValue example: + \code + // Register with QJSValue callback + int typeId = qmlRegisterSingletonType(...); + + // Retrieve as QJSValue + QQmlEngine engine; + QJSValue instance = engine.singletonInstance<QJSValue>(typeId); + \endcode + + It is recommended to store the QML type id during registration, e.g. as a static member + in the singleton class. Otherwise, a costly lookup via qmlTypeId() has to be performed + at run-time. + + \sa qmlRegisterSingletonType(), qmlTypeId() + \since 5.12 +*/ +template<> +QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId) +{ + QQmlType type = QQmlMetaType::qmlType(qmlTypeId, QQmlMetaType::TypeIdCategory::QmlType); + + if (!type.isValid() || !type.isSingleton()) + return QJSValue(); + + QQmlType::SingletonInstanceInfo* info = type.singletonInstanceInfo(); + info->init(this); + + if (QObject* o = info->qobjectApi(this)) + return this->newQObject(o); + else { + QJSValue value = info->scriptApi(this); + if (!value.isUndefined()) + return value; + } + + return QJSValue(); +} + +/*! Refreshes all binding expressions that use strings marked for translation. Call this function after you have installed a new translator with @@ -1699,7 +1775,7 @@ void QQmlData::NotifyList::layout() todo = nullptr; } -void QQmlData::deferData(int objectIndex, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *context) +void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *context) { QQmlData::DeferredData *deferData = new QQmlData::DeferredData; deferData->deferredIdx = objectIndex; @@ -2266,7 +2342,7 @@ QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const Locker locker(this); auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return QQmlMetaObject((*iter)->rootPropertyCache()); + return QQmlMetaObject((*iter)->rootPropertyCache().data()); } else { QQmlType type = QQmlMetaType::qmlType(t); return QQmlMetaObject(type.baseMetaObject()); @@ -2278,7 +2354,7 @@ QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const Locker locker(this); auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return QQmlMetaObject((*iter)->rootPropertyCache()); + return QQmlMetaObject((*iter)->rootPropertyCache().data()); } else { QQmlType type = QQmlMetaType::qmlType(t); return QQmlMetaObject(type.metaObject()); @@ -2290,7 +2366,7 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t) Locker locker(this); auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return (*iter)->rootPropertyCache(); + return (*iter)->rootPropertyCache().data(); } else { QQmlType type = QQmlMetaType::qmlType(t); locker.unlock(); @@ -2303,7 +2379,7 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t, int minorVe Locker locker(this); auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return (*iter)->rootPropertyCache(); + return (*iter)->rootPropertyCache().data(); } else { QQmlType type = QQmlMetaType::qmlType(t); locker.unlock(); diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 73ad2754c8..871e6bd9b4 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -143,6 +143,9 @@ public: bool outputWarningsToStandardError() const; void setOutputWarningsToStandardError(bool); + template<typename T> + T singletonInstance(int qmlTypeId); + public Q_SLOTS: void retranslate(); @@ -167,6 +170,19 @@ private: Q_DECLARE_PRIVATE(QQmlEngine) }; +template<> +Q_QML_EXPORT QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId); + +template<typename T> +T QQmlEngine::singletonInstance(int qmlTypeId) { + QJSValue instance = singletonInstance<QJSValue>(qmlTypeId); + if (!instance.isQObject()) + return nullptr; + + QObject *object = instance.toQObject(); + return qobject_cast<T>(object); +} + QT_END_NAMESPACE #endif // QQMLENGINE_H diff --git a/src/qml/qml/qqmlguard_p.h b/src/qml/qml/qqmlguard_p.h index 808bf4c709..3ac63926a0 100644 --- a/src/qml/qml/qqmlguard_p.h +++ b/src/qml/qml/qqmlguard_p.h @@ -106,6 +106,34 @@ protected: virtual void objectDestroyed(T *) {} }; +template <typename T> +class QQmlStrongJSQObjectReference : public QQmlGuard<T> +{ +public: + void setObject(T *o, QObject *parent) { + T *old = this->object(); + if (o == old) + return; + + if (m_jsOwnership && old && old->parent() == parent) + QQml_setParent_noEvent(old, nullptr); + + this->QQmlGuard<T>::operator=(o); + + if (o && !o->parent() && !QQmlData::keepAliveDuringGarbageCollection(o)) { + m_jsOwnership = true; + QQml_setParent_noEvent(o, parent); + } else { + m_jsOwnership = false; + } + } + +private: + using QQmlGuard<T>::setObject; + using QQmlGuard<T>::operator=; + bool m_jsOwnership = false; +}; + QT_END_NAMESPACE Q_DECLARE_METATYPE(QQmlGuard<QObject>) diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 80ebab5ca3..1c37894751 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -737,7 +737,8 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt int *vmajor, int *vminor, QQmlType *type_return, QString *base, bool *typeRecursionDetected, QQmlType::RegistrationType registrationType, - QQmlImport::RecursionRestriction recursionRestriction) const + QQmlImport::RecursionRestriction recursionRestriction, + QList<QQmlError> *errors) const { if (majversion >= 0 && minversion >= 0) { QQmlType t = QQmlMetaType::qmlType(type, uri, majversion, minversion); @@ -820,8 +821,19 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt }; for (uint i = 0; i < sizeof(urlsToTry) / sizeof(urlsToTry[0]); ++i) { const QString url = urlsToTry[i]; - exists = !typeLoader->absoluteFilePath(QQmlFile::urlToLocalFileOrQrc(url)).isEmpty(); + const QString localPath = QQmlFile::urlToLocalFileOrQrc(url); + exists = !typeLoader->absoluteFilePath(localPath).isEmpty(); if (exists) { + // don't let function.qml confuse the use of "new Function(...)" for example. + if (!QQml_isFileCaseCorrect(localPath)) { + exists = false; + if (errors) { + QQmlError caseError; + caseError.setDescription(QLatin1String("File name case mismatch")); + errors->append(caseError); + } + break; + } qmlUrl = url; break; } @@ -908,7 +920,7 @@ bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedS for (int i=0; i<imports.count(); ++i) { const QQmlImportInstance *import = imports.at(i); if (import->resolveType(typeLoader, type, vmajor, vminor, type_return, base, - &typeRecursionDetected, registrationType, recursionRestriction)) { + &typeRecursionDetected, registrationType, recursionRestriction, errors)) { if (qmlCheckTypes()) { // check for type clashes for (int j = i+1; j<imports.count(); ++j) { @@ -2050,7 +2062,7 @@ bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &b if (QQmlExtensionPlugin *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) { // basepath should point to the directory of the module, not the plugin file itself: - QQmlExtensionPluginPrivate::get(plugin)->baseUrl = QUrl::fromLocalFile(basePath); + QQmlExtensionPluginPrivate::get(plugin)->baseUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath); } iface->registerTypes(moduleId); diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index 2437979ef8..283bd40660 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -94,7 +94,8 @@ struct QQmlImportInstance int *vmajor, int *vminor, QQmlType* type_return, QString *base = nullptr, bool *typeRecursionDetected = nullptr, QQmlType::RegistrationType = QQmlType::AnyRegistrationType, - QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion) const; + QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion, + QList<QQmlError> *errors = nullptr) const; }; class QQmlImportNamespace diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 93ec9421ed..9f2a96d5d9 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -93,8 +93,7 @@ void QQmlDelayedError::catchJavaScriptException(QV4::ExecutionEngine *engine) QQmlJavaScriptExpression::QQmlJavaScriptExpression() - : m_error(nullptr), - m_context(nullptr), + : m_context(nullptr), m_prevExpression(nullptr), m_nextExpression(nullptr), m_v4Function(nullptr) @@ -247,6 +246,9 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b while (QQmlJavaScriptExpressionGuard *g = capture.guards.takeFirst()) g->Delete(); + if (!watcher.wasDeleted()) + setTranslationsCaptured(capture.translationCaptured); + ep->propertyCapture = lastPropertyCapture; return result->asReturnedValue(); @@ -392,7 +394,7 @@ QQmlDelayedError *QQmlJavaScriptExpression::delayedError() { if (!m_error) m_error = new QQmlDelayedError; - return m_error; + return m_error.data(); } QV4::ReturnedValue @@ -458,7 +460,7 @@ void QQmlJavaScriptExpression::setupFunction(QV4::ExecutionContext *qmlContext, setCompilationUnit(m_v4Function->compilationUnit); } -void QQmlJavaScriptExpression::setCompilationUnit(QV4::CompiledData::CompilationUnit *compilationUnit) +void QQmlJavaScriptExpression::setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) { m_compilationUnit = compilationUnit; } diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 01af3b89ca..de3fba0774 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -162,7 +162,7 @@ protected: } void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f); - void setCompilationUnit(QV4::CompiledData::CompilationUnit *compilationUnit); + void setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit); // We store some flag bits in the following flag pointers. // activeGuards:flag1 - notifyOnValueChanged @@ -171,13 +171,17 @@ protected: QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> activeGuards; QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> permanentGuards; + void setTranslationsCaptured(bool captured) { m_error.setFlagValue(captured); } + bool translationsCaptured() const { return m_error.flag(); } + private: friend class QQmlContextData; friend class QQmlPropertyCapture; friend void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *, void **); friend class QQmlTranslationBinding; - QQmlDelayedError *m_error; + // m_error:flag1 translationsCapturedDuringEvaluation + QFlagPointer<QQmlDelayedError> m_error; QQmlContextData *m_context; QQmlJavaScriptExpression **m_prevExpression; @@ -208,12 +212,14 @@ public: static void registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction); void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce); void captureProperty(QObject *, int, int, Duration duration = OnlyOnce, bool doNotify = true); + void captureTranslation() { translationCaptured = true; } QQmlEngine *engine; QQmlJavaScriptExpression *expression; QQmlJavaScriptExpression::DeleteWatcher *watcher; QFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> guards; QStringList *errorString; + bool translationCaptured = false; }; QQmlJavaScriptExpression::DeleteWatcher::DeleteWatcher(QQmlJavaScriptExpression *e) @@ -260,18 +266,17 @@ void QQmlJavaScriptExpression::setScopeObject(QObject *v) bool QQmlJavaScriptExpression::hasError() const { - return m_error && m_error->isValid(); + return !m_error.isNull() && m_error->isValid(); } bool QQmlJavaScriptExpression::hasDelayedError() const { - return m_error; + return !m_error.isNull(); } inline void QQmlJavaScriptExpression::clearError() { - if (m_error) - delete m_error; + delete m_error.data(); m_error = nullptr; } diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index 3fbe3df2ab..7c35f73e6c 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -74,7 +74,7 @@ ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QObject *object, i Scope scope(engine); - Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocObject<QmlListWrapper>()); + Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>()); r->d()->object = object; r->d()->propertyType = propType; void *args[] = { &r->d()->property(), nullptr }; @@ -86,7 +86,7 @@ ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, const QQmlListProp { Scope scope(engine); - Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocObject<QmlListWrapper>()); + Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>()); r->d()->object = prop.object; r->d()->property() = prop; r->d()->propertyType = propType; @@ -102,54 +102,45 @@ QVariant QmlListWrapper::toVariant() const } -ReturnedValue QmlListWrapper::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { Q_ASSERT(m->as<QmlListWrapper>()); const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m); QV4::ExecutionEngine *v4 = w->engine(); - if (name->equals(v4->id_length()) && !w->d()->object.isNull()) { + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0; - return Primitive::fromUInt32(count).asReturnedValue(); - } - - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return getIndexed(m, idx, hasProperty); - - return Object::get(m, name, hasProperty); -} - -ReturnedValue QmlListWrapper::getIndexed(const Managed *m, uint index, bool *hasProperty) -{ - Q_UNUSED(hasProperty); + if (index < count && w->d()->property().at) { + if (hasProperty) + *hasProperty = true; + return QV4::QObjectWrapper::wrap(v4, w->d()->property().at(&w->d()->property(), index)); + } - Q_ASSERT(m->as<QmlListWrapper>()); - const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m); - QV4::ExecutionEngine *v4 = w->engine(); - - quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0; - if (index < count && w->d()->property().at) { if (hasProperty) - *hasProperty = true; - return QV4::QObjectWrapper::wrap(v4, w->d()->property().at(&w->d()->property(), index)); + *hasProperty = false; + return Primitive::undefinedValue().asReturnedValue(); + } else if (id.isString()) { + if (id == v4->id_length()->propertyKey() && !w->d()->object.isNull()) { + quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0; + return Primitive::fromUInt32(count).asReturnedValue(); + } } - if (hasProperty) - *hasProperty = false; - return Primitive::undefinedValue().asReturnedValue(); + return Object::virtualGet(m, id, receiver, hasProperty); } -bool QmlListWrapper::put(Managed *m, String *name, const Value &value) +bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { // doesn't do anything. Should we throw? Q_UNUSED(m); - Q_UNUSED(name); + Q_UNUSED(id); Q_UNUSED(value); + Q_UNUSED(receiver); return false; } -void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) +void QmlListWrapper::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) { name->setM(nullptr); *index = UINT_MAX; @@ -163,7 +154,7 @@ void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name p->value = QV4::QObjectWrapper::wrap(w->engine(), w->d()->property().at(&w->d()->property(), *index)); return; } - return QV4::Object::advanceIterator(m, it, name, index, p, attrs); + return QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attrs); } void PropertyListPrototype::init(ExecutionEngine *) diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h index e02831c8d1..2d6a0880e3 100644 --- a/src/qml/qml/qqmllistwrapper_p.h +++ b/src/qml/qml/qqmllistwrapper_p.h @@ -93,10 +93,9 @@ struct Q_QML_EXPORT QmlListWrapper : Object QVariant toVariant() const; - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static bool put(Managed *m, String *name, const Value &value); - static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); + static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); }; struct PropertyListPrototype : Object diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index b4f7092a22..42c72e0447 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -350,7 +350,7 @@ ReturnedValue QQmlDateExtension::method_timeZoneUpdated(const QV4::FunctionObjec if (argc != 0) THROW_ERROR("Locale: Date.timeZoneUpdated(): Invalid arguments"); - QV4::DatePrototype::timezoneUpdated(); + QV4::DatePrototype::timezoneUpdated(scope.engine); RETURN_UNDEFINED(); } @@ -825,10 +825,10 @@ QV4::ReturnedValue QQmlLocale::wrap(ExecutionEngine *v4, const QLocale &locale) { QV4::Scope scope(v4); QV4LocaleDataDeletable *d = localeV4Data(scope.engine); - QV4::Scoped<QQmlLocaleData> wrapper(scope, v4->memoryManager->allocObject<QQmlLocaleData>()); + QV4::Scoped<QQmlLocaleData> wrapper(scope, v4->memoryManager->allocate<QQmlLocaleData>()); *wrapper->d()->locale = locale; QV4::ScopedObject p(scope, d->prototype.value()); - wrapper->setPrototype(p); + wrapper->setPrototypeOf(p); return wrapper.asReturnedValue(); } diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h index 8341b1f555..859c36e11b 100644 --- a/src/qml/qml/qqmllocale_p.h +++ b/src/qml/qml/qqmllocale_p.h @@ -58,6 +58,8 @@ #include <private/qqmlglobal_p.h> #include <private/qv4object_p.h> +QT_REQUIRE_CONFIG(qml_locale); + QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlloggingcategory.cpp b/src/qml/qml/qqmlloggingcategory.cpp index 597fe458fa..b59a26e17e 100644 --- a/src/qml/qml/qqmlloggingcategory.cpp +++ b/src/qml/qml/qqmlloggingcategory.cpp @@ -59,6 +59,7 @@ LoggingCategory { id: category name: "com.qt.category" + defaultLogLevel: LoggingCategory.Warning } Component.onCompleted: { @@ -84,6 +85,17 @@ \sa QLoggingCategory::categoryName() */ +/*! + \qmlproperty enumeration QtQml::LoggingCategory::defaultLogLevel + \since 5.12 + + Holds the default log level of the logging category. By default it is + created with the LoggingCategory.Debug log level. + + \note This property needs to be set when declaring the LoggingCategory + and cannot be changed later. +*/ + QQmlLoggingCategory::QQmlLoggingCategory(QObject *parent) : QObject(parent) , m_initialized(false) @@ -99,6 +111,11 @@ QString QQmlLoggingCategory::name() const return QString::fromUtf8(m_name); } +QQmlLoggingCategory::DefaultLogLevel QQmlLoggingCategory::defaultLogLevel() const +{ + return m_defaultLogLevel; +} + QLoggingCategory *QQmlLoggingCategory::category() const { return m_category.data(); @@ -111,10 +128,25 @@ void QQmlLoggingCategory::classBegin() void QQmlLoggingCategory::componentComplete() { m_initialized = true; - if (m_name.isNull()) + if (m_name.isNull()) { qmlWarning(this) << QLatin1String("Declaring the name of the LoggingCategory is mandatory and cannot be changed later !"); + } else { + QScopedPointer<QLoggingCategory> category(new QLoggingCategory(m_name.constData(), QtMsgType(m_defaultLogLevel))); + m_category.swap(category); + } } +void QQmlLoggingCategory::setDefaultLogLevel(DefaultLogLevel defaultLogLevel) +{ + if (m_initialized) { + qmlWarning(this) << QLatin1String("The defaultLogLevel of a LoggingCategory cannot be changed after the Item is created"); + return; + } + + m_defaultLogLevel = defaultLogLevel; +} + + void QQmlLoggingCategory::setName(const QString &name) { if (m_initialized) { @@ -123,8 +155,6 @@ void QQmlLoggingCategory::setName(const QString &name) } m_name = name.toUtf8(); - QScopedPointer<QLoggingCategory> category(new QLoggingCategory(m_name.constData())); - m_category.swap(category); } #include "moc_qqmlloggingcategory_p.cpp" diff --git a/src/qml/qml/qqmlloggingcategory_p.h b/src/qml/qml/qqmlloggingcategory_p.h index 544db1fe33..ece06e04b4 100644 --- a/src/qml/qml/qqmlloggingcategory_p.h +++ b/src/qml/qml/qqmlloggingcategory_p.h @@ -65,11 +65,23 @@ class QQmlLoggingCategory : public QObject, public QQmlParserStatus Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(DefaultLogLevel defaultLogLevel READ defaultLogLevel WRITE setDefaultLogLevel REVISION 1) public: + enum DefaultLogLevel { + Debug = QtDebugMsg, + Info = QtInfoMsg, + Warning = QtWarningMsg, + Critical = QtCriticalMsg, + Fatal = QtFatalMsg + }; + Q_ENUM(DefaultLogLevel); + QQmlLoggingCategory(QObject *parent = nullptr); virtual ~QQmlLoggingCategory(); + DefaultLogLevel defaultLogLevel() const; + void setDefaultLogLevel(DefaultLogLevel defaultLogLevel); QString name() const; void setName(const QString &name); @@ -81,6 +93,7 @@ public: private: QByteArray m_name; QScopedPointer<QLoggingCategory> m_category; + DefaultLogLevel m_defaultLogLevel = Debug; bool m_initialized; }; diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 2705321e77..b31058ece5 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -288,7 +288,14 @@ void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv) void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) { if (scriptCallback && scriptApi(e).isUndefined()) { - setScriptApi(e, scriptCallback(e, e)); + QJSValue value = scriptCallback(e, e); + if (value.isQObject()) { + QObject *o = value.toQObject(); + // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) + // should behave identically to QML singleton types. + e->setContextForObject(o, new QQmlContext(e->rootContext(), e)); + } + setScriptApi(e, value); } else if (qobjectCallback && !qobjectApi(e)) { QObject *o = qobjectCallback(e, e); setQObjectApi(e, o); @@ -297,6 +304,9 @@ void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) } // if this object can use a property cache, create it now QQmlData::ensurePropertyCache(e, o); + // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) + // should behave identically to QML singleton types. + e->setContextForObject(o, new QQmlContext(e->rootContext(), e)); } else if (!url.isEmpty() && !qobjectApi(e)) { QQmlComponent component(e, url, QQmlComponent::PreferSynchronous); QObject *o = component.beginCreate(e->rootContext()); @@ -600,7 +610,7 @@ QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const Q_ASSERT(isComposite()); if (!engine || !d) return QQmlType(); - QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()), QQmlRefPointer<QQmlTypeData>::Adopt); + QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl())); if (td.isNull() || !td->isComplete()) return QQmlType(); QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit(); @@ -614,11 +624,11 @@ QQmlPropertyCache *QQmlType::compositePropertyCache(QQmlEnginePrivate *engine) c Q_ASSERT(isComposite()); if (!engine) return nullptr; - QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()), QQmlRefPointer<QQmlTypeData>::Adopt); + QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl())); if (td.isNull() || !td->isComplete()) return nullptr; QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit(); - return compilationUnit->rootPropertyCache(); + return compilationUnit->rootPropertyCache().data(); } static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, @@ -865,7 +875,7 @@ QQmlPropertyCache *QQmlTypePrivate::propertyCacheForMinorVersion(int minorVersio { for (int i = 0; i < propertyCaches.count(); ++i) if (propertyCaches.at(i).minorVersion == minorVersion) - return propertyCaches.at(i).cache; + return propertyCaches.at(i).cache.data(); return nullptr; } @@ -1877,6 +1887,23 @@ void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor) p->maxMinorVersion = qMax(p->maxMinorVersion, versionMinor); } +//From qqml.h +int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName) +{ + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), versionMajor, data); + if (!module) + return -1; + + QQmlType type = module->type(QHashedStringRef(QString::fromUtf8(qmlName)), versionMinor); + if (!type.isValid()) + return -1; + + return type.index(); +} + bool QQmlMetaType::namespaceContainsRegistrations(const QString &uri, int majorVersion) { const QQmlMetaTypeData *data = metaTypeData(); @@ -2248,19 +2275,25 @@ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStrin } /*! - Returns the type (if any) that corresponds to the QVariant::Type \a userType. - Returns null if no type is registered. + Returns the type (if any) that corresponds to \a typeId. Depending on \a category, the + \a typeId is interpreted either as QVariant::Type or as QML type id returned by one of the + qml type registration functions. Returns null if no type is registered. */ -QQmlType QQmlMetaType::qmlType(int userType) +QQmlType QQmlMetaType::qmlType(int typeId, TypeIdCategory category) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - QQmlTypePrivate *type = data->idToType.value(userType); - if (type && type->typeId == userType) - return QQmlType(type); - else - return QQmlType(); + if (category == TypeIdCategory::MetaType) { + QQmlTypePrivate *type = data->idToType.value(typeId); + if (type && type->typeId == typeId) + return QQmlType(type); + } else if (category == TypeIdCategory::QmlType) { + QQmlType type = data->types.value(typeId); + if (type.isValid()) + return type; + } + return QQmlType(); } /*! diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index bcaf62d6ba..4a5e4ba266 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -91,11 +91,16 @@ public: static QList<QQmlType> qmlSingletonTypes(); static QList<QQmlType> qmlAllTypes(); + enum class TypeIdCategory { + MetaType, + QmlType + }; + static QQmlType qmlType(const QString &qualifiedName, int, int); static QQmlType qmlType(const QHashedStringRef &name, const QHashedStringRef &module, int, int); static QQmlType qmlType(const QMetaObject *); static QQmlType qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor); - static QQmlType qmlType(int); + static QQmlType qmlType(int typeId, TypeIdCategory category = TypeIdCategory::MetaType); static QQmlType qmlType(const QUrl &unNormalizedUrl, bool includeNonFileImports = false); static QQmlPropertyCache *propertyCache(const QMetaObject *metaObject); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 5aaf79c9e5..fb05201010 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -71,7 +71,7 @@ struct ActiveOCRestorer }; } -QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *creationContext, +QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator) : phase(Startup) , compilationUnit(compilationUnit) @@ -99,7 +99,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::Compil } } -QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState) +QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState) : phase(Startup) , compilationUnit(compilationUnit) , resolvedTypes(compilationUnit->resolvedTypes) @@ -193,8 +193,8 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI context->importedScripts.set(v4, scripts); QV4::ScopedValue v(scope); for (int i = 0; i < compilationUnit->dependentScripts.count(); ++i) { - QQmlScriptData *s = compilationUnit->dependentScripts.at(i); - scripts->putIndexed(i, (v = s->scriptValueForContext(context))); + QQmlRefPointer<QQmlScriptData> s = compilationUnit->dependentScripts.at(i); + scripts->put(i, (v = s->scriptValueForContext(context))); } } else if (sharedState->creationContext) { context->importedScripts = sharedState->creationContext->importedScripts; @@ -252,7 +252,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance, QQmlData:: Q_ASSERT(!sharedState->allJavaScriptObjects); sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount); - QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1)); + QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc()); qSwap(_qmlContext, qmlContext); @@ -312,7 +312,7 @@ bool QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty, if (!sharedState->allJavaScriptObjects) sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount); - QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1)); + QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc()); qSwap(_qmlContext, qmlContext); @@ -876,7 +876,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper && !_valueTypeProperty) QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex())); - if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) { + if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) { if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex()); @@ -898,7 +898,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper targetProperty = _valueTypeProperty; subprop = bindingProperty; } - if (binding->containsTranslations()) { + if (binding->isTranslationBinding()) { qmlBinding = QQmlBinding::createTranslationBinding(compilationUnit, binding, _scopeObject, context); } else { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; @@ -1144,9 +1144,9 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (obj->flags & QV4::CompiledData::Object::IsComponent) { isComponent = true; - QQmlComponent *component = new QQmlComponent(engine, compilationUnit, index, parent); + QQmlComponent *component = new QQmlComponent(engine, compilationUnit.data(), index, parent); Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( - compilationUnit, obj, QStringLiteral("<component>"), context->url())); + compilationUnit.data(), obj, QStringLiteral("<component>"), context->url())); QQmlComponentPrivate::get(component)->creationContext = context; instance = component; ddata = QQmlData::get(instance, /*create*/true); @@ -1157,7 +1157,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo QQmlType type = typeRef->type; if (type.isValid()) { Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( - compilationUnit, obj, type.qmlTypeName(), context->url())); + compilationUnit.data(), obj, type.qmlTypeName(), context->url())); void *ddataMemory = nullptr; type.create(&instance, &ddataMemory, sizeof(QQmlData)); @@ -1190,8 +1190,8 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo } else { Q_ASSERT(typeRef->compilationUnit); Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( - compilationUnit, obj, typeRef->compilationUnit->fileName(), - context->url())); + compilationUnit.data(), obj, typeRef->compilationUnit->fileName(), + context->url())); if (typeRef->compilationUnit->data->isSingleton()) { recordError(obj->location, tr("Composite Singleton Type %1 is not creatable").arg(stringAt(obj->inheritedTypeNameIndex))); @@ -1254,7 +1254,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) { customParser->engine = QQmlEnginePrivate::get(engine); - customParser->imports = compilationUnit->typeNameCache; + customParser->imports = compilationUnit->typeNameCache.data(); QList<const QV4::CompiledData::Binding *> bindings; const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); @@ -1264,7 +1264,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo bindings << binding; } } - customParser->applyBindings(instance, compilationUnit, bindings); + customParser->applyBindings(instance, compilationUnit.data(), bindings); customParser->engine = nullptr; customParser->imports = (QQmlTypeNameCache*)nullptr; @@ -1280,7 +1280,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (installPropertyCache) { if (ddata->propertyCache) ddata->propertyCache->release();; - ddata->propertyCache = cache; + ddata->propertyCache = cache.data(); ddata->propertyCache->addref(); } @@ -1292,7 +1292,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo ++sharedState->allJavaScriptObjects; QV4::Scope valueScope(v4); - QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1)); + QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc()); qSwap(_qmlContext, qmlContext); @@ -1344,6 +1344,11 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru data->clearPendingBindingBit(b->targetPropertyIndex().coreIndex()); b->setEnabled(true, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); + if (!b->isValueTypeProxy()) { + QQmlBinding *binding = static_cast<QQmlBinding*>(b.data()); + if (!binding->hasError() && !binding->hasDependencies()) + b->removeFromObject(); + } if (watcher.hasRecursed() || interrupt.shouldInterrupt()) return nullptr; @@ -1436,7 +1441,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * vmeMetaObject = new QQmlVMEMetaObject(v4, _qobject, cache, compilationUnit, _compiledObjectIndex); if (_ddata->propertyCache) _ddata->propertyCache->release(); - _ddata->propertyCache = cache; + _ddata->propertyCache = cache.data(); _ddata->propertyCache->addref(); scopeObjectProtector = _ddata->jsWrapper.value(); } else { diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 67a5bdd827..435b213341 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -85,7 +85,7 @@ class Q_QML_PRIVATE_EXPORT QQmlObjectCreator { Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator) public: - QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr); + QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr); ~QQmlObjectCreator(); QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, QQmlInstantiationInterrupt *interrupt = nullptr); @@ -104,7 +104,7 @@ public: QFiniteStack<QPointer<QObject> > &allCreatedObjects() const { return sharedState->allCreatedObjects; } private: - QQmlObjectCreator(QQmlContextData *contextData, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState); + QQmlObjectCreator(QQmlContextData *contextData, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState); void init(QQmlContextData *parentContext); diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp index 1b44bbdda3..fc798a2c23 100644 --- a/src/qml/qml/qqmlopenmetaobject.cpp +++ b/src/qml/qml/qqmlopenmetaobject.cpp @@ -181,46 +181,75 @@ void QQmlOpenMetaObjectTypePrivate::init(const QMetaObject *metaObj) class QQmlOpenMetaObjectPrivate { public: - QQmlOpenMetaObjectPrivate(QQmlOpenMetaObject *_q) - : q(_q), parent(nullptr), type(nullptr), cacheProperties(false) {} + QQmlOpenMetaObjectPrivate(QQmlOpenMetaObject *_q, bool _autoCreate, QObject *obj) + : q(_q), object(obj), autoCreate(_autoCreate) {} + + struct Property { + private: + QVariant m_value; + QPointer<QObject> qobjectTracker; + public: + bool valueSet = false; + + QVariant value() const { + if (QMetaType::typeFlags(m_value.userType()) & QMetaType::PointerToQObject + && qobjectTracker.isNull()) + return QVariant::fromValue<QObject*>(nullptr); + return m_value; + } + QVariant &valueRef() { return m_value; } + void setValue(const QVariant &v) { + m_value = v; + valueSet = true; + if (QMetaType::typeFlags(v.userType()) & QMetaType::PointerToQObject) + qobjectTracker = m_value.value<QObject*>(); + } + }; - inline QPair<QVariant, bool> &getDataRef(int idx) { - while (data.count() <= idx) - data << QPair<QVariant, bool>(QVariant(), false); - return data[idx]; + inline void setPropertyValue(int idx, const QVariant &value) { + if (data.count() <= idx) + data.resize(idx + 1); + data[idx].setValue(value); } - inline QVariant &getData(int idx) { - QPair<QVariant, bool> &prop = getDataRef(idx); - if (!prop.second) { - prop.first = q->initialValue(idx); - prop.second = true; - } - return prop.first; + inline Property &propertyRef(int idx) { + if (data.count() <= idx) + data.resize(idx + 1); + Property &prop = data[idx]; + if (!prop.valueSet) + prop.setValue(q->initialValue(idx)); + return prop; + } + + inline QVariant propertyValue(int idx) { + auto &prop = propertyRef(idx); + return prop.value(); + } + + inline QVariant &propertyValueRef(int idx) { + auto &prop = propertyRef(idx); + return prop.valueRef(); } - inline bool hasData(int idx) const { + inline bool hasProperty(int idx) const { if (idx >= data.count()) return false; - return data[idx].second; + return data[idx].valueSet; } - bool autoCreate; QQmlOpenMetaObject *q; - QAbstractDynamicMetaObject *parent; - QList<QPair<QVariant, bool> > data; + QAbstractDynamicMetaObject *parent = nullptr; + QVector<Property> data; QObject *object; - QQmlOpenMetaObjectType *type; - bool cacheProperties; + QQmlRefPointer<QQmlOpenMetaObjectType> type; + bool autoCreate; + bool cacheProperties = false; }; QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, const QMetaObject *base, bool automatic) -: d(new QQmlOpenMetaObjectPrivate(this)) +: d(new QQmlOpenMetaObjectPrivate(this, automatic, obj)) { - d->autoCreate = automatic; - d->object = obj; - - d->type = new QQmlOpenMetaObjectType(base ? base : obj->metaObject(), nullptr); + d->type.adopt(new QQmlOpenMetaObjectType(base ? base : obj->metaObject(), nullptr)); d->type->d->referers.insert(this); QObjectPrivate *op = QObjectPrivate::get(obj); @@ -230,13 +259,9 @@ QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, const QMetaObject *base, bo } QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, QQmlOpenMetaObjectType *type, bool automatic) -: d(new QQmlOpenMetaObjectPrivate(this)) +: d(new QQmlOpenMetaObjectPrivate(this, automatic, obj)) { - d->autoCreate = automatic; - d->object = obj; - d->type = type; - d->type->addref(); d->type->d->referers.insert(this); QObjectPrivate *op = QObjectPrivate::get(obj); @@ -250,13 +275,12 @@ QQmlOpenMetaObject::~QQmlOpenMetaObject() if (d->parent) delete d->parent; d->type->d->referers.remove(this); - d->type->release(); delete d; } QQmlOpenMetaObjectType *QQmlOpenMetaObject::type() const { - return d->type; + return d->type.data(); } void QQmlOpenMetaObject::emitPropertyNotification(const QByteArray &propertyName) @@ -276,13 +300,11 @@ int QQmlOpenMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void * int propId = id - d->type->d->propertyOffset; if (c == QMetaObject::ReadProperty) { propertyRead(propId); - *reinterpret_cast<QVariant *>(a[0]) = d->getData(propId); + *reinterpret_cast<QVariant *>(a[0]) = d->propertyValue(propId); } else if (c == QMetaObject::WriteProperty) { - if (propId >= d->data.count() || d->data.at(propId).first != *reinterpret_cast<QVariant *>(a[0])) { + if (propId >= d->data.count() || d->data.at(propId).value() != *reinterpret_cast<QVariant *>(a[0])) { propertyWrite(propId); - QPair<QVariant, bool> &prop = d->getDataRef(propId); - prop.first = propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0])); - prop.second = true; + d->setPropertyValue(propId, propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0]))); propertyWritten(propId); activate(o, d->type->d->signalOffset + propId, nullptr); } @@ -303,14 +325,12 @@ QAbstractDynamicMetaObject *QQmlOpenMetaObject::parent() const QVariant QQmlOpenMetaObject::value(int id) const { - return d->getData(id); + return d->propertyValue(id); } void QQmlOpenMetaObject::setValue(int id, const QVariant &value) { - QPair<QVariant, bool> &prop = d->getDataRef(id); - prop.first = propertyWriteValue(id, value); - prop.second = true; + d->setPropertyValue(id, propertyWriteValue(id, value)); activate(d->object, id + d->type->d->signalOffset, nullptr); } @@ -320,23 +340,18 @@ QVariant QQmlOpenMetaObject::value(const QByteArray &name) const if (iter == d->type->d->names.cend()) return QVariant(); - return d->getData(*iter); + return d->propertyValue(*iter); } -QVariant &QQmlOpenMetaObject::operator[](const QByteArray &name) +QVariant &QQmlOpenMetaObject::valueRef(const QByteArray &name) { QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name); Q_ASSERT(iter != d->type->d->names.cend()); - return d->getData(*iter); -} - -QVariant &QQmlOpenMetaObject::operator[](int id) -{ - return d->getData(id); + return d->propertyValueRef(*iter); } -bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val) +bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val, bool force) { QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name); @@ -348,11 +363,10 @@ bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val) } if (id >= 0) { - QVariant &dataVal = d->getData(id); - if (dataVal == val) + if (!force && d->propertyValue(id) == val) return false; - dataVal = val; + d->setPropertyValue(id, val); activate(d->object, id + d->type->d->signalOffset, nullptr); return true; } @@ -363,7 +377,7 @@ bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val) // returns true if this value has been initialized by a call to either value() or setValue() bool QQmlOpenMetaObject::hasValue(int id) const { - return d->hasData(id); + return d->hasProperty(id); } void QQmlOpenMetaObject::setCached(bool c) diff --git a/src/qml/qml/qqmlopenmetaobject_p.h b/src/qml/qml/qqmlopenmetaobject_p.h index 4905190b75..168a2a6f7f 100644 --- a/src/qml/qml/qqmlopenmetaobject_p.h +++ b/src/qml/qml/qqmlopenmetaobject_p.h @@ -100,11 +100,10 @@ public: ~QQmlOpenMetaObject() override; QVariant value(const QByteArray &) const; - bool setValue(const QByteArray &, const QVariant &); + bool setValue(const QByteArray &, const QVariant &, bool force = false); QVariant value(int) const; void setValue(int, const QVariant &); - QVariant &operator[](const QByteArray &); - QVariant &operator[](int); + QVariant &valueRef(const QByteArray &); bool hasValue(int) const; int count() const; diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index c4487f91a3..bc0124eeab 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -255,7 +255,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) { if (!obj) return; - QQmlTypeNameCache *typeNameCache = context?context->imports:nullptr; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache = context?context->imports:nullptr; QObject *currentObject = obj; QVector<QStringRef> path; diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index b78a2ddd20..0785910cec 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -979,13 +979,13 @@ public: void append(QQmlPropertyCache *cache) { cache->addref(); data.append(cache); } QQmlPropertyCache *at(int index) const { return data.at(index).data(); } - void set(int index, QQmlPropertyCache *replacement) { + void set(int index, const QQmlRefPointer<QQmlPropertyCache> &replacement) { if (QQmlPropertyCache *oldCache = data.at(index).data()) { - if (replacement == oldCache) + if (replacement.data() == oldCache) return; oldCache->release(); } - data[index] = replacement; + data[index] = replacement.data(); replacement->addref(); } diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index c656fac4ff..85167848fb 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -504,11 +504,12 @@ void QQmlDataBlob::addDependency(QQmlDataBlob *blob) if (!blob || blob->status() == Error || blob->status() == Complete || - status() == Error || status() == Complete || m_isDone || - m_waitingFor.contains(blob)) + status() == Error || status() == Complete || m_isDone) return; - blob->addref(); + for (auto existingDep: qAsConst(m_waitingFor)) + if (existingDep.data() == blob) + return; m_data.setStatus(WaitingForDependencies); @@ -691,13 +692,11 @@ void QQmlDataBlob::tryDone() void QQmlDataBlob::cancelAllWaitingFor() { while (m_waitingFor.count()) { - QQmlDataBlob *blob = m_waitingFor.takeLast(); + QQmlRefPointer<QQmlDataBlob> blob = m_waitingFor.takeLast(); Q_ASSERT(blob->m_waitingOnMe.contains(this)); blob->m_waitingOnMe.removeOne(this); - - blob->release(); } } @@ -706,7 +705,8 @@ void QQmlDataBlob::notifyAllWaitingOnMe() while (m_waitingOnMe.count()) { QQmlDataBlob *blob = m_waitingOnMe.takeLast(); - Q_ASSERT(blob->m_waitingFor.contains(this)); + Q_ASSERT(std::any_of(blob->m_waitingFor.constBegin(), blob->m_waitingFor.constEnd(), + [this](const QQmlRefPointer<QQmlDataBlob> &waiting) { return waiting.data() == this; })); blob->notifyComplete(this); } @@ -714,13 +714,19 @@ void QQmlDataBlob::notifyAllWaitingOnMe() void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob) { - Q_ASSERT(m_waitingFor.contains(blob)); Q_ASSERT(blob->status() == Error || blob->status() == Complete); QQmlCompilingProfiler prof(typeLoader()->profiler(), blob); m_inCallback = true; - m_waitingFor.removeOne(blob); + QQmlRefPointer<QQmlDataBlob> blobRef; + for (int i = 0; i < m_waitingFor.count(); ++i) { + if (m_waitingFor.at(i).data() == blob) { + blobRef = m_waitingFor.takeAt(i); + break; + } + } + Q_ASSERT(blobRef); if (blob->status() == Error) { dependencyError(blob); @@ -728,8 +734,6 @@ void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob) dependencyComplete(blob); } - blob->release(); - if (!isError() && m_waitingFor.isEmpty()) allDependenciesDone(); @@ -1327,20 +1331,17 @@ QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoa QQmlTypeLoader::Blob::~Blob() { - for (int ii = 0; ii < m_qmldirs.count(); ++ii) - m_qmldirs.at(ii)->release(); } bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors) { - QQmlQmldirData *data = typeLoader()->getQmldir(url); + QQmlRefPointer<QQmlQmldirData> data = typeLoader()->getQmldir(url); data->setImport(this, import); data->setPriority(this, priority); if (data->status() == Error) { // This qmldir must not exist - which is not an error - data->release(); return true; } else if (data->status() == Complete) { // This data is already available @@ -1348,11 +1349,11 @@ bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData: } // Wait for this data to become available - addDependency(data); + addDependency(data.data()); return true; } -bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors) +bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors) { QString qmldirIdentifier = data->urlString(); QString qmldirUrl = qmldirIdentifier.left(qmldirIdentifier.lastIndexOf(QLatin1Char('/')) + 1); @@ -1378,8 +1379,8 @@ bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::Compile const auto qmldirScripts = qmldir.scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); - QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl); - addDependency(blob); + QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl); + addDependency(blob.data()); scriptImported(blob, import->location, script.nameSpace, importQualifier); } @@ -1398,8 +1399,8 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL const QString &importQualifier = stringAt(import->qualifierIndex); if (import->type == QV4::CompiledData::Import::ImportScript) { QUrl scriptUrl = finalUrl().resolved(QUrl(importUri)); - QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl); - addDependency(blob); + QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl); + addDependency(blob.data()); scriptImported(blob, import->location, importQualifier, QString()); } else if (import->type == QV4::CompiledData::Import::ImportLibrary) { @@ -1426,8 +1427,8 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL const auto qmldirScripts = qmldir.scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); - QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl); - addDependency(blob); + QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl); + addDependency(blob.data()); scriptImported(blob, import->location, script.nameSpace, importQualifier); } @@ -1499,14 +1500,6 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL return true; } -void QQmlTypeLoader::Blob::dependencyError(QQmlDataBlob *blob) -{ - if (blob->type() == QQmlDataBlob::QmldirFile) { - QQmlQmldirData *data = static_cast<QQmlQmldirData *>(blob); - data->release(); - } -} - void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob) { if (blob->type() == QQmlDataBlob::QmldirFile) { @@ -1532,7 +1525,7 @@ bool QQmlTypeLoader::Blob::isDebugging() const return typeLoader()->engine()->handle()->debugger() != nullptr; } -bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlError> *errors) +bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors) { bool resolve = true; @@ -1552,7 +1545,6 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlE if (resolve) { // This is the (current) best resolution for this import if (!updateQmldir(data, import, errors)) { - data->release(); return false; } @@ -1562,7 +1554,6 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlE } } - data->release(); return true; } @@ -1664,7 +1655,7 @@ QUrl QQmlTypeLoader::normalize(const QUrl &unNormalizedUrl) /*! Returns a QQmlTypeData for the specified \a url. The QQmlTypeData may be cached. */ -QQmlTypeData *QQmlTypeLoader::getType(const QUrl &unNormalizedUrl, Mode mode) +QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QUrl &unNormalizedUrl, Mode mode) { Q_ASSERT(!unNormalizedUrl.isRelative() && (QQmlFile::urlToLocalFileOrQrc(unNormalizedUrl).isEmpty() || @@ -1707,8 +1698,6 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &unNormalizedUrl, Mode mode) } } - typeData->addref(); - return typeData; } @@ -1716,20 +1705,20 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &unNormalizedUrl, Mode mode) Returns a QQmlTypeData for the given \a data with the provided base \a url. The QQmlTypeData will not be cached. */ -QQmlTypeData *QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url, Mode mode) +QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url, Mode mode) { LockHolder<QQmlTypeLoader> holder(this); QQmlTypeData *typeData = new QQmlTypeData(url, this); QQmlTypeLoader::loadWithStaticData(typeData, data, mode); - return typeData; + return QQmlRefPointer<QQmlTypeData>(typeData, QQmlRefPointer<QQmlTypeData>::Adopt); } /*! Return a QQmlScriptBlob for \a url. The QQmlScriptData may be cached. */ -QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &unNormalizedUrl) +QQmlRefPointer<QQmlScriptBlob> QQmlTypeLoader::getScript(const QUrl &unNormalizedUrl) { Q_ASSERT(!unNormalizedUrl.isRelative() && (QQmlFile::urlToLocalFileOrQrc(unNormalizedUrl).isEmpty() || @@ -1754,15 +1743,13 @@ QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &unNormalizedUrl) } } - scriptBlob->addref(); - return scriptBlob; } /*! Returns a QQmlQmldirData for \a url. The QQmlQmldirData may be cached. */ -QQmlQmldirData *QQmlTypeLoader::getQmldir(const QUrl &url) +QQmlRefPointer<QQmlQmldirData> QQmlTypeLoader::getQmldir(const QUrl &url) { Q_ASSERT(!url.isRelative() && (QQmlFile::urlToLocalFileOrQrc(url).isEmpty() || @@ -1778,8 +1765,6 @@ QQmlQmldirData *QQmlTypeLoader::getQmldir(const QUrl &url) QQmlTypeLoader::load(qmldirData); } - qmldirData->addref(); - return qmldirData; } @@ -2073,17 +2058,9 @@ QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager) QQmlTypeData::~QQmlTypeData() { - for (int ii = 0; ii < m_scripts.count(); ++ii) - m_scripts.at(ii).script->release(); - for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) { - if (QQmlTypeData *tdata = m_compositeSingletons.at(ii).typeData) - tdata->release(); - } - for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); - it != end; ++it) { - if (QQmlTypeData *tdata = it->typeData) - tdata->release(); - } + m_scripts.clear(); + m_compositeSingletons.clear(); + m_resolvedTypes.clear(); } const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const @@ -2201,7 +2178,7 @@ void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeName { QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(&m_compiledData->propertyCaches, &pendingGroupPropertyBindings, - engine, m_compiledData, &m_importCache); + engine, m_compiledData.data(), &m_importCache); QQmlCompileError error = propertyCacheCreator.buildMetaObjects(); if (error.isSet()) { setError(error); @@ -2209,7 +2186,7 @@ void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeName } } - QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> aliasCreator(&m_compiledData->propertyCaches, m_compiledData); + QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> aliasCreator(&m_compiledData->propertyCaches, m_compiledData.data()); aliasCreator.appendAliasPropertiesToMetaObjects(); pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches); @@ -2393,8 +2370,7 @@ void QQmlTypeData::done() } m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); - QQmlScriptData *scriptData = script.script->scriptData(); - scriptData->addref(); + QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData(); m_compiledData->dependentScripts << scriptData; } } @@ -2637,8 +2613,8 @@ void QQmlTypeData::resolveTypes() // Add any imported scripts to our resolved set const auto resolvedScripts = m_importCache.resolvedScripts(); for (const QQmlImports::ScriptReference &script : resolvedScripts) { - QQmlScriptBlob *blob = typeLoader()->getScript(script.location); - addDependency(blob); + QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(script.location); + addDependency(blob.data()); ScriptReference ref; //ref.location = ... @@ -2681,7 +2657,7 @@ void QQmlTypeData::resolveTypes() // TODO: give an error message? If so, we should record and show the path of the cycle. continue; } - addDependency(ref.typeData); + addDependency(ref.typeData.data()); ref.prefix = csRef.prefix; m_compositeSingletons << ref; @@ -2711,7 +2687,7 @@ void QQmlTypeData::resolveTypes() if (ref.type.isComposite()) { ref.typeData = typeLoader()->getType(ref.type.sourceUrl()); - addDependency(ref.typeData); + addDependency(ref.typeData.data()); } ref.majorVersion = majorVersion; ref.minorVersion = minorVersion; @@ -2743,7 +2719,7 @@ QQmlCompileError QQmlTypeData::buildTypeResolutionCaches( for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons) (*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix); - m_importCache.populateCache(*typeNameCache); + m_importCache.populateCache(typeNameCache->data()); QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); @@ -2833,7 +2809,7 @@ bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int & return true; } -void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/) +void QQmlTypeData::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/) { ScriptReference ref; ref.script = blob; @@ -2921,7 +2897,7 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent } QV4::ScopedValue v(scope); for (int ii = 0; ii < scripts.count(); ++ii) - scriptsArray->putIndexed(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(ctxt))); + scriptsArray->put(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(ctxt))); if (!hasEngine()) initialize(parentCtxt->engine); @@ -2959,8 +2935,6 @@ void QQmlScriptData::clear() typeNameCache = nullptr; } - for (int ii = 0; ii < scripts.count(); ++ii) - scripts.at(ii)->release(); scripts.clear(); // An addref() was made when the QQmlCleanup was added to the engine. @@ -2968,19 +2942,15 @@ void QQmlScriptData::clear() } QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader) -: QQmlTypeLoader::Blob(url, JavaScriptFile, loader), m_scriptData(nullptr) +: QQmlTypeLoader::Blob(url, JavaScriptFile, loader) { } QQmlScriptBlob::~QQmlScriptBlob() { - if (m_scriptData) { - m_scriptData->release(); - m_scriptData = nullptr; - } } -QQmlScriptData *QQmlScriptBlob::scriptData() const +QQmlRefPointer<QQmlScriptData> QQmlScriptBlob::scriptData() const { return m_scriptData; } @@ -3017,11 +2987,12 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data) } QmlIR::ScriptDirectivesCollector collector(&irUnit); + irUnit.jsParserEngine.setDirectives(&collector); QList<QQmlError> errors; QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Script::precompile( - &irUnit.jsModule, &irUnit.jsGenerator, urlString(), finalUrlString(), - source, &errors, &collector); + &irUnit.jsModule, &irUnit.jsParserEngine, &irUnit.jsGenerator, urlString(), finalUrlString(), + source, &errors); // No need to addref on unit, it's initial refcount is 1 source.clear(); if (!errors.isEmpty()) { @@ -3095,6 +3066,7 @@ void QQmlScriptBlob::done() } m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace); } + m_scripts.clear(); m_importCache.populateCache(m_scriptData->typeNameCache); } @@ -3104,7 +3076,7 @@ QString QQmlScriptBlob::stringAt(int index) const return m_scriptData->m_precompiledScript->data->stringAt(index); } -void QQmlScriptBlob::scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) +void QQmlScriptBlob::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) { ScriptReference ref; ref.script = blob; @@ -3118,7 +3090,7 @@ void QQmlScriptBlob::scriptImported(QQmlScriptBlob *blob, const QV4::CompiledDat void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit) { Q_ASSERT(!m_scriptData); - m_scriptData = new QQmlScriptData(); + m_scriptData.adopt(new QQmlScriptData()); m_scriptData->url = finalUrl(); m_scriptData->urlString = finalUrlString(); m_scriptData->m_precompiledScript = unit; diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index e75719866d..98ac4b1bc1 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -217,7 +217,7 @@ private: QList<QQmlDataBlob *> m_waitingOnMe; // List of QQmlDataBlob's that I am waiting for to complete. - QList<QQmlDataBlob *> m_waitingFor; + QVector<QQmlRefPointer<QQmlDataBlob>> m_waitingFor; int m_redirectCount:30; bool m_inCallback:1; @@ -279,14 +279,13 @@ public: bool addImport(const QV4::CompiledData::Import *import, QList<QQmlError> *errors); bool fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors); - bool updateQmldir(QQmlQmldirData *data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors); + bool updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors); private: - virtual bool qmldirDataAvailable(QQmlQmldirData *, QList<QQmlError> *); + virtual bool qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &, QList<QQmlError> *); - virtual void scriptImported(QQmlScriptBlob *, const QV4::CompiledData::Location &, const QString &, const QString &) {} + virtual void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &, const QV4::CompiledData::Location &, const QString &, const QString &) {} - void dependencyError(QQmlDataBlob *) override; void dependencyComplete(QQmlDataBlob *) override; protected: @@ -296,7 +295,7 @@ public: QQmlImports m_importCache; QHash<const QV4::CompiledData::Import*, int> m_unresolvedImports; - QList<QQmlQmldirData *> m_qmldirs; + QVector<QQmlRefPointer<QQmlQmldirData>> m_qmldirs; QQmlMetaType::CachedUnitLookupError m_cachedUnitStatus = QQmlMetaType::CachedUnitLookupError::NoError; }; @@ -307,11 +306,11 @@ public: static QUrl normalize(const QUrl &unNormalizedUrl); - QQmlTypeData *getType(const QUrl &unNormalizedUrl, Mode mode = PreferSynchronous); - QQmlTypeData *getType(const QByteArray &, const QUrl &url, Mode mode = PreferSynchronous); + QQmlRefPointer<QQmlTypeData> getType(const QUrl &unNormalizedUrl, Mode mode = PreferSynchronous); + QQmlRefPointer<QQmlTypeData> getType(const QByteArray &, const QUrl &url, Mode mode = PreferSynchronous); - QQmlScriptBlob *getScript(const QUrl &unNormalizedUrl); - QQmlQmldirData *getQmldir(const QUrl &); + QQmlRefPointer<QQmlScriptBlob> getScript(const QUrl &unNormalizedUrl); + QQmlRefPointer<QQmlQmldirData> getQmldir(const QUrl &); QString absoluteFilePath(const QString &path); bool directoryExists(const QString &path); @@ -424,13 +423,13 @@ class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob public: struct TypeReference { - TypeReference() : majorVersion(0), minorVersion(0), typeData(nullptr), needsCreation(true) {} + TypeReference() : majorVersion(0), minorVersion(0), needsCreation(true) {} QV4::CompiledData::Location location; QQmlType type; int majorVersion; int minorVersion; - QQmlTypeData *typeData; + QQmlRefPointer<QQmlTypeData> typeData; QString prefix; // used by CompositeSingleton types QString qualifiedName() const; bool needsCreation; @@ -438,11 +437,9 @@ public: struct ScriptReference { - ScriptReference() : script(nullptr) {} - QV4::CompiledData::Location location; QString qualifier; - QQmlScriptBlob *script; + QQmlRefPointer<QQmlScriptBlob> script; }; private: @@ -495,7 +492,7 @@ private: bool reportErrors = true, QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType); - void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override; + void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override; SourceCodeData m_backupSourceCode; // used when cache verification fails. @@ -541,7 +538,7 @@ public: QUrl url; QString urlString; QQmlTypeNameCache *typeNameCache; - QList<QQmlScriptBlob *> scripts; + QVector<QQmlRefPointer<QQmlScriptBlob>> scripts; QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt); @@ -571,15 +568,13 @@ public: struct ScriptReference { - ScriptReference() : script(nullptr) {} - QV4::CompiledData::Location location; QString qualifier; QString nameSpace; - QQmlScriptBlob *script; + QQmlRefPointer<QQmlScriptBlob> script; }; - QQmlScriptData *scriptData() const; + QQmlRefPointer<QQmlScriptData> scriptData() const; protected: void dataReceived(const SourceCodeData &) override; @@ -589,11 +584,11 @@ protected: QString stringAt(int index) const override; private: - void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override; + void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override; void initializeFromCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit); QList<ScriptReference> m_scripts; - QQmlScriptData *m_scriptData; + QQmlRefPointer<QQmlScriptData> m_scriptData; }; class Q_AUTOTEST_EXPORT QQmlQmldirData : public QQmlTypeLoader::Blob diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index ce35e966aa..c6affcb79c 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -95,12 +95,17 @@ QObject* QQmlTypeWrapper::singletonObject() const QVariant QQmlTypeWrapper::toVariant() const { - QObject *qobjectSingleton = singletonObject(); - if (qobjectSingleton) + // Only Singleton type wrappers can be converted to a variant. + if (!isSingleton()) + return QVariant(); + + QQmlEngine *e = engine()->qmlEngine(); + QQmlType::SingletonInstanceInfo *siinfo = d()->type().singletonInstanceInfo(); + siinfo->init(e); + if (QObject *qobjectSingleton = siinfo->qobjectApi(e)) return QVariant::fromValue<QObject*>(qobjectSingleton); - // only QObject Singleton Type can be converted to a variant. - return QVariant(); + return QVariant::fromValue<QJSValue>(siinfo->scriptApi(e)); } @@ -111,7 +116,7 @@ ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, Q_ASSERT(t.isValid()); Scope scope(engine); - Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocObject<QQmlTypeWrapper>()); + Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>()); w->d()->mode = mode; w->d()->object = o; w->d()->typePrivate = t.priv(); QQmlType::refHandle(w->d()->typePrivate); @@ -120,15 +125,15 @@ ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, // Returns a type wrapper for importNamespace (of t) on o. This allows nested resolution of a type in a // namespace. -ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, QQmlTypeNameCache *t, const QQmlImportRef *importNamespace, +ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlTypeNameCache> &t, const QQmlImportRef *importNamespace, Heap::QQmlTypeWrapper::TypeNameMode mode) { Q_ASSERT(t); Q_ASSERT(importNamespace); Scope scope(engine); - Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocObject<QQmlTypeWrapper>()); - w->d()->mode = mode; w->d()->object = o; w->d()->typeNamespace = t; w->d()->importNamespace = importNamespace; + Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>()); + w->d()->mode = mode; w->d()->object = o; w->d()->typeNamespace = t.data(); w->d()->importNamespace = importNamespace; t->addref(); return w.asReturnedValue(); } @@ -162,12 +167,16 @@ static ReturnedValue throwLowercaseEnumError(QV4::ExecutionEngine *v4, String *n return v4->throwTypeError(message); } -ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { Q_ASSERT(m->as<QQmlTypeWrapper>()); + if (!id.isString()) + return Object::virtualGet(m, id, receiver, hasProperty); + QV4::ExecutionEngine *v4 = static_cast<const QQmlTypeWrapper *>(m)->engine(); QV4::Scope scope(v4); + ScopedString name(scope, id.asStringOrSymbol()); Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(m)); @@ -232,7 +241,7 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); if (ok) { - Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocObject<QQmlScopedEnumWrapper>()); + Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>()); enumWrapper->d()->typePrivate = type.priv(); QQmlType::refHandle(enumWrapper->d()->typePrivate); enumWrapper->d()->scopeEnumIndex = value; @@ -263,7 +272,7 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp return create(scope.engine, object, r.type, w->d()->mode); } else if (r.scriptIndex != -1) { QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); - return scripts->getIndexed(r.scriptIndex); + return scripts->get(r.scriptIndex); } else if (r.importNamespace) { return create(scope.engine, object, context->imports, r.importNamespace); } @@ -279,7 +288,7 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp } bool ok = false; - const ReturnedValue result = Object::get(m, name, &ok); + const ReturnedValue result = Object::virtualGet(m, id, receiver, &ok); if (hasProperty) *hasProperty = ok; @@ -295,16 +304,20 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp } -bool QQmlTypeWrapper::put(Managed *m, String *name, const Value &value) +bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { + if (!id.isString()) + return Object::virtualPut(m, id, value, receiver); + + Q_ASSERT(m->as<QQmlTypeWrapper>()); QQmlTypeWrapper *w = static_cast<QQmlTypeWrapper *>(m); - QV4::ExecutionEngine *v4 = w->engine(); - if (v4->hasException) + QV4::Scope scope(w); + if (scope.engine->hasException) return false; - QV4::Scope scope(v4); - QQmlContextData *context = v4->callingQmlContext(); + ScopedString name(scope, id.asStringOrSymbol()); + QQmlContextData *context = scope.engine->callingQmlContext(); QQmlType type = w->d()->type(); if (type.isValid() && !type.isSingleton() && w->d()->object) { @@ -312,7 +325,7 @@ bool QQmlTypeWrapper::put(Managed *m, String *name, const Value &value) QQmlEngine *e = scope.engine->qmlEngine(); QObject *ao = qmlAttachedPropertiesObjectById(type.attachedPropertiesId(QQmlEnginePrivate::get(e)), object); if (ao) - return QV4::QObjectWrapper::setQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); + return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); return false; } else if (type.isSingleton()) { QQmlEngine *e = scope.engine->qmlEngine(); @@ -321,12 +334,12 @@ bool QQmlTypeWrapper::put(Managed *m, String *name, const Value &value) QObject *qobjectSingleton = siinfo->qobjectApi(e); if (qobjectSingleton) { - return QV4::QObjectWrapper::setQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); + return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); } else if (!siinfo->scriptApi(e).isUndefined()) { - QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e))); + QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(scope.engine, siinfo->scriptApi(e))); if (!apiprivate) { QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); - v4->throwError(error); + scope.engine->throwError(error); return false; } else { return apiprivate->put(name, value); @@ -337,15 +350,21 @@ bool QQmlTypeWrapper::put(Managed *m, String *name, const Value &value) return false; } -PropertyAttributes QQmlTypeWrapper::query(const Managed *m, String *name) +PropertyAttributes QQmlTypeWrapper::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p) { - // ### Implement more efficiently. - bool hasProperty = false; - static_cast<Object *>(const_cast<Managed*>(m))->get(name, &hasProperty); - return hasProperty ? Attr_Data : Attr_Invalid; + if (id.isString()) { + Scope scope(m); + ScopedString n(scope, id.asStringOrSymbol()); + // ### Implement more efficiently. + bool hasProperty = false; + static_cast<Object *>(m)->get(n, &hasProperty); + return hasProperty ? Attr_Data : Attr_Invalid; + } + + return QV4::Object::virtualGetOwnProperty(m, id, p); } -bool QQmlTypeWrapper::isEqualTo(Managed *a, Managed *b) +bool QQmlTypeWrapper::virtualIsEqualTo(Managed *a, Managed *b) { Q_ASSERT(a->as<QV4::QQmlTypeWrapper>()); QV4::QQmlTypeWrapper *qmlTypeWrapperA = static_cast<QV4::QQmlTypeWrapper *>(a); @@ -357,7 +376,7 @@ bool QQmlTypeWrapper::isEqualTo(Managed *a, Managed *b) return false; } -ReturnedValue QQmlTypeWrapper::instanceOf(const Object *typeObject, const Value &var) +ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const Value &var) { Q_ASSERT(typeObject->as<QV4::QQmlTypeWrapper>()); const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject); @@ -385,10 +404,9 @@ ReturnedValue QQmlTypeWrapper::instanceOf(const Object *typeObject, const Value if (!theirDData->compilationUnit) return Encode(false); - QQmlTypeData *td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl()); + QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl()); CompiledData::CompilationUnit *cu = td->compilationUnit(); myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); - td->release(); } else { myQmlType = qenginepriv->metaObjectForType(myTypeId); } @@ -410,12 +428,16 @@ QQmlType Heap::QQmlScopedEnumWrapper::type() const return QQmlType(typePrivate); } -ReturnedValue QQmlScopedEnumWrapper::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue QQmlScopedEnumWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { Q_ASSERT(m->as<QQmlScopedEnumWrapper>()); + if (!id.isString()) + return Object::virtualGet(m, id, receiver, hasProperty); + const QQmlScopedEnumWrapper *resource = static_cast<const QQmlScopedEnumWrapper *>(m); QV4::ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); + ScopedString name(scope, id.asStringOrSymbol()); QQmlType type = resource->d()->type(); int index = resource->d()->scopeEnumIndex; diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index 25ff7ba7c8..c0eb534d36 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -108,15 +108,15 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlType &, Heap::QQmlTypeWrapper::TypeNameMode = Heap::QQmlTypeWrapper::IncludeEnums); - static ReturnedValue create(ExecutionEngine *, QObject *, QQmlTypeNameCache *, const QQmlImportRef *, + static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlRefPointer<QQmlTypeNameCache> &, const QQmlImportRef *, Heap::QQmlTypeWrapper::TypeNameMode = Heap::QQmlTypeWrapper::IncludeEnums); - - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static bool put(Managed *m, String *name, const Value &value); - static PropertyAttributes query(const Managed *, String *name); - static bool isEqualTo(Managed *that, Managed *o); - static ReturnedValue instanceOf(const Object *typeObject, const Value &var); +protected: + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); + static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); + static bool virtualIsEqualTo(Managed *that, Managed *o); + static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var); }; struct Q_QML_EXPORT QQmlScopedEnumWrapper : Object @@ -124,7 +124,7 @@ struct Q_QML_EXPORT QQmlScopedEnumWrapper : Object V4_OBJECT2(QQmlScopedEnumWrapper, Object) V4_NEEDS_DESTROY - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); }; } diff --git a/src/qml/qml/qqmlvaluetypeproxybinding_p.h b/src/qml/qml/qqmlvaluetypeproxybinding_p.h index ba0d305bd9..35b54c339b 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding_p.h +++ b/src/qml/qml/qqmlvaluetypeproxybinding_p.h @@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE -class QQmlValueTypeProxyBinding : public QQmlAbstractBinding +class Q_AUTOTEST_EXPORT QQmlValueTypeProxyBinding : public QQmlAbstractBinding { public: QQmlValueTypeProxyBinding(QObject *o, QQmlPropertyIndex coreIndex); diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 6196a09d94..1fde5945ba 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -39,7 +39,6 @@ #include "qqmlvaluetypewrapper_p.h" #include <private/qv8engine_p.h> - #include <private/qqmlvaluetype_p.h> #include <private/qqmlbinding_p.h> #include <private/qqmlglobal_p.h> @@ -49,6 +48,7 @@ #include <private/qv4functionobject_p.h> #include <private/qv4variantobject_p.h> #include <private/qv4alloca_p.h> +#include <private/qv4stackframe_p.h> #include <private/qv4objectiterator_p.h> #include <private/qv4qobjectwrapper_p.h> #include <QtCore/qloggingcategory.h> @@ -186,7 +186,7 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *obj Scope scope(engine); initProto(engine); - Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->allocObject<QQmlValueTypeReference>()); + Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->allocate<QQmlValueTypeReference>()); r->d()->object = object; r->d()->property = property; r->d()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject)); @@ -200,7 +200,7 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, const QVaria Scope scope(engine); initProto(engine); - Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocObject<QQmlValueTypeWrapper>()); + Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>()); r->d()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject)); r->d()->valueType = QQmlValueTypeFactory::valueType(typeId); r->d()->gadgetPtr = nullptr; @@ -227,7 +227,7 @@ bool QQmlValueTypeWrapper::toGadget(void *data) const return true; } -bool QQmlValueTypeWrapper::isEqualTo(Managed *m, Managed *other) +bool QQmlValueTypeWrapper::virtualIsEqualTo(Managed *m, Managed *other) { Q_ASSERT(m && m->as<QQmlValueTypeWrapper>() && other); QV4::QQmlValueTypeWrapper *lv = static_cast<QQmlValueTypeWrapper *>(m); @@ -241,16 +241,20 @@ bool QQmlValueTypeWrapper::isEqualTo(Managed *m, Managed *other) return false; } -PropertyAttributes QQmlValueTypeWrapper::query(const Managed *m, String *name) +PropertyAttributes QQmlValueTypeWrapper::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p) { - Q_ASSERT(m->as<const QQmlValueTypeWrapper>()); - const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); + if (id.isString()) { + Scope scope(m); + ScopedString n(scope, id.asStringOrSymbol()); + const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); + QQmlPropertyData *result = r->d()->propertyCache()->property(n.getPointer(), nullptr, nullptr); + return result ? Attr_Data : Attr_Invalid; + } - QQmlPropertyData *result = r->d()->propertyCache()->property(name, nullptr, nullptr); - return result ? Attr_Data : Attr_Invalid; + return QV4::Object::virtualGetOwnProperty(m, id, p); } -void QQmlValueTypeWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) +void QQmlValueTypeWrapper::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { name->setM(nullptr); *index = UINT_MAX; @@ -275,7 +279,7 @@ void QQmlValueTypeWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value return; } } - QV4::Object::advanceIterator(m, it, name, index, p, attributes); + QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes); } bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const @@ -355,11 +359,17 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con return Encode(b->engine()->newString(result)); } -ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { Q_ASSERT(m->as<QQmlValueTypeWrapper>()); + + if (!id.isString()) + return Object::virtualGet(m, id, receiver, hasProperty); + const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); QV4::ExecutionEngine *v4 = r->engine(); + Scope scope(v4); + ScopedString name(scope, id.asStringOrSymbol()); // Note: readReferenceValue() can change the reference->type. if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) { @@ -367,9 +377,9 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha return Primitive::undefinedValue().asReturnedValue(); } - QQmlPropertyData *result = r->d()->propertyCache()->property(name, nullptr, nullptr); + QQmlPropertyData *result = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr); if (!result) - return Object::get(m, name, hasProperty); + return Object::virtualGet(m, id, receiver, hasProperty); if (hasProperty) *hasProperty = true; @@ -413,8 +423,11 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha #undef VALUE_TYPE_ACCESSOR } -bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) +bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { + if (!id.isString()) + return Object::virtualPut(m, id, value, receiver); + Q_ASSERT(m->as<QQmlValueTypeWrapper>()); ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine(); Scope scope(v4); @@ -435,8 +448,10 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) writeBackPropertyType = writebackProperty.userType(); } + ScopedString name(scope, id.asStringOrSymbol()); + const QMetaObject *metaObject = r->d()->propertyCache()->metaObject(); - const QQmlPropertyData *pd = r->d()->propertyCache()->property(name, nullptr, nullptr); + const QQmlPropertyData *pd = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr); if (!pd) return false; diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index f99d207d68..ff8ab98bb6 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -106,11 +106,11 @@ public: int typeId() const; bool write(QObject *target, int propertyIndex) const; - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static bool put(Managed *m, String *name, const Value &value); - static bool isEqualTo(Managed *m, Managed *other); - static PropertyAttributes query(const Managed *, String *name); - static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); + static bool virtualIsEqualTo(Managed *m, Managed *other); + static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); + static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static ReturnedValue method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h index 9a94ac6258..13c5524d96 100644 --- a/src/qml/qml/qqmlvme_p.h +++ b/src/qml/qml/qqmlvme_p.h @@ -124,6 +124,10 @@ private: // Used to check that a QQmlVME that is interrupted mid-execution // is still valid. Checks all the objects and contexts have not been // deleted. +// +// VME stands for Virtual Machine Execution. QML files used to +// be compiled to a byte code data structure that a virtual machine executed +// (for constructing the tree of QObjects and setting properties). class QQmlVMEGuard { public: diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index c1d3980b58..6bc0f0aa1e 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -106,7 +106,7 @@ void QQmlVMEVariantQObjectPtr::objectDestroyed(QObject *) QV4::Scope scope(v4); QV4::Scoped<QV4::MemberData> sp(scope, m_target->propertyAndMethodStorage.value()); if (sp) { - QV4::MemberData::Index index{ sp->d(), sp->d()->values.values + m_index }; + QV4::PropertyIndex index{ sp->d(), sp->d()->values.values + m_index }; index.set(v4, QV4::Primitive::nullValue()); } } @@ -176,7 +176,7 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() } -QQmlInterceptorMetaObject::QQmlInterceptorMetaObject(QObject *obj, QQmlPropertyCache *cache) +QQmlInterceptorMetaObject::QQmlInterceptorMetaObject(QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache) : object(obj), cache(cache), interceptors(nullptr), @@ -316,7 +316,7 @@ QAbstractDynamicMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObje QQmlVMEMetaObject::QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, - QQmlPropertyCache *cache, QV4::CompiledData::CompilationUnit *qmlCompilationUnit, int qmlObjectId) + const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlCompilationUnit, int qmlObjectId) : QQmlInterceptorMetaObject(obj, cache), engine(engine), ctxt(QQmlData::get(obj, true)->outerContext), diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index 0c82686d47..dbcc9d2884 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -94,7 +94,7 @@ public: class Q_QML_PRIVATE_EXPORT QQmlInterceptorMetaObject : public QAbstractDynamicMetaObject { public: - QQmlInterceptorMetaObject(QObject *obj, QQmlPropertyCache *cache); + QQmlInterceptorMetaObject(QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache); ~QQmlInterceptorMetaObject() override; void registerInterceptor(QQmlPropertyIndex index, QQmlPropertyValueInterceptor *interceptor); @@ -104,7 +104,7 @@ public: QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *o) override; // Used by auto-tests for inspection - QQmlPropertyCache *propertyCache() const { return cache; } + QQmlPropertyCache *propertyCache() const { return cache.data(); } bool intercepts(QQmlPropertyIndex propertyIndex) const { @@ -146,7 +146,7 @@ class QQmlVMEMetaObjectEndpoint; class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject { public: - QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, QQmlPropertyCache *cache, QV4::CompiledData::CompilationUnit *qmlCompilationUnit, int qmlObjectId); + QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlCompilationUnit, int qmlObjectId); ~QQmlVMEMetaObject() override; bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const; diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 567d83f3ee..51e5be4b5b 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -228,8 +228,7 @@ public: static ReturnedValue create(ExecutionEngine *, NodeImpl *, const QList<NodeImpl *> &); // JS API - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); }; void Heap::NamedNodeMap::init(NodeImpl *data, const QList<NodeImpl *> &list) @@ -250,8 +249,7 @@ public: V4_NEEDS_DESTROY // JS API - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); // C++ API static ReturnedValue create(ExecutionEngine *, NodeImpl *); @@ -595,7 +593,7 @@ ReturnedValue NodePrototype::getProto(ExecutionEngine *v4) Scope scope(v4); QQmlXMLHttpRequestData *d = xhrdata(v4); if (d->nodePrototype.isUndefined()) { - ScopedObject p(scope, v4->memoryManager->allocObject<NodePrototype>()); + ScopedObject p(scope, v4->memoryManager->allocate<NodePrototype>()); d->nodePrototype.set(v4, p); v4->v8Engine->freezeObject(p); } @@ -606,12 +604,12 @@ ReturnedValue Node::create(ExecutionEngine *v4, NodeImpl *data) { Scope scope(v4); - Scoped<Node> instance(scope, v4->memoryManager->allocObject<Node>(data)); + Scoped<Node> instance(scope, v4->memoryManager->allocate<Node>(data)); ScopedObject p(scope); switch (data->type) { case NodeImpl::Attr: - instance->setPrototype((p = Attr::prototype(v4))); + instance->setPrototypeUnchecked((p = Attr::prototype(v4))); break; case NodeImpl::Comment: case NodeImpl::Document: @@ -623,13 +621,13 @@ ReturnedValue Node::create(ExecutionEngine *v4, NodeImpl *data) case NodeImpl::ProcessingInstruction: return Encode::undefined(); case NodeImpl::CDATA: - instance->setPrototype((p = CDATA::prototype(v4))); + instance->setPrototypeUnchecked((p = CDATA::prototype(v4))); break; case NodeImpl::Text: - instance->setPrototype((p = Text::prototype(v4))); + instance->setPrototypeUnchecked((p = Text::prototype(v4))); break; case NodeImpl::Element: - instance->setPrototype((p = Element::prototype(v4))); + instance->setPrototypeUnchecked((p = Element::prototype(v4))); break; } @@ -643,7 +641,7 @@ ReturnedValue Element::prototype(ExecutionEngine *engine) Scope scope(engine); ScopedObject p(scope, engine->newObject()); ScopedObject pp(scope); - p->setPrototype((pp = NodePrototype::getProto(engine))); + p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine))); p->defineAccessorProperty(QStringLiteral("tagName"), NodePrototype::method_get_nodeName, nullptr); d->elementPrototype.set(engine, p); engine->v8Engine->freezeObject(p); @@ -658,7 +656,7 @@ ReturnedValue Attr::prototype(ExecutionEngine *engine) Scope scope(engine); ScopedObject p(scope, engine->newObject()); ScopedObject pp(scope); - p->setPrototype((pp = NodePrototype::getProto(engine))); + p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine))); p->defineAccessorProperty(QStringLiteral("name"), method_name, nullptr); p->defineAccessorProperty(QStringLiteral("value"), method_value, nullptr); p->defineAccessorProperty(QStringLiteral("ownerElement"), method_ownerElement, nullptr); @@ -715,7 +713,7 @@ ReturnedValue CharacterData::prototype(ExecutionEngine *v4) Scope scope(v4); ScopedObject p(scope, v4->newObject()); ScopedObject pp(scope); - p->setPrototype((pp = NodePrototype::getProto(v4))); + p->setPrototypeUnchecked((pp = NodePrototype::getProto(v4))); p->defineAccessorProperty(QStringLiteral("data"), NodePrototype::method_get_nodeValue, nullptr); p->defineAccessorProperty(QStringLiteral("length"), method_length, nullptr); d->characterDataPrototype.set(v4, p); @@ -751,7 +749,7 @@ ReturnedValue Text::prototype(ExecutionEngine *v4) Scope scope(v4); ScopedObject p(scope, v4->newObject()); ScopedObject pp(scope); - p->setPrototype((pp = CharacterData::prototype(v4))); + p->setPrototypeUnchecked((pp = CharacterData::prototype(v4))); p->defineAccessorProperty(QStringLiteral("isElementContentWhitespace"), method_isElementContentWhitespace, nullptr); p->defineAccessorProperty(QStringLiteral("wholeText"), method_wholeText, nullptr); d->textPrototype.set(v4, p); @@ -768,7 +766,7 @@ ReturnedValue CDATA::prototype(ExecutionEngine *v4) Scope scope(v4); ScopedObject p(scope, v4->newObject()); ScopedObject pp(scope); - p->setPrototype((pp = Text::prototype(v4))); + p->setPrototypeUnchecked((pp = Text::prototype(v4))); d->cdataPrototype.set(v4, p); v4->v8Engine->freezeObject(p); } @@ -782,7 +780,7 @@ ReturnedValue Document::prototype(ExecutionEngine *v4) Scope scope(v4); ScopedObject p(scope, v4->newObject()); ScopedObject pp(scope); - p->setPrototype((pp = NodePrototype::getProto(v4))); + p->setPrototypeUnchecked((pp = NodePrototype::getProto(v4))); p->defineAccessorProperty(QStringLiteral("xmlVersion"), method_xmlVersion, nullptr); p->defineAccessorProperty(QStringLiteral("xmlEncoding"), method_xmlEncoding, nullptr); p->defineAccessorProperty(QStringLiteral("xmlStandalone"), method_xmlStandalone, nullptr); @@ -876,10 +874,10 @@ ReturnedValue Document::load(ExecutionEngine *v4, const QByteArray &data) return Encode::null(); } - ScopedObject instance(scope, v4->memoryManager->allocObject<Node>(document)); + ScopedObject instance(scope, v4->memoryManager->allocate<Node>(document)); document->release(); // the GC should own the NodeImpl via Node now ScopedObject p(scope); - instance->setPrototype((p = Document::prototype(v4))); + instance->setPrototypeUnchecked((p = Document::prototype(v4))); return instance.asReturnedValue(); } @@ -888,33 +886,33 @@ bool Node::isNull() const return d()->d == nullptr; } -ReturnedValue NamedNodeMap::getIndexed(const Managed *m, uint index, bool *hasProperty) +ReturnedValue NamedNodeMap::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { Q_ASSERT(m->as<NamedNodeMap>()); + const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m); QV4::ExecutionEngine *v4 = r->engine(); - if ((int)index < r->d()->list().count()) { + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + + if ((int)index < r->d()->list().count()) { + if (hasProperty) + *hasProperty = true; + return Node::create(v4, r->d()->list().at(index)); + } if (hasProperty) - *hasProperty = true; - return Node::create(v4, r->d()->list().at(index)); + *hasProperty = false; + return Encode::undefined(); } - if (hasProperty) - *hasProperty = false; - return Encode::undefined(); -} -ReturnedValue NamedNodeMap::get(const Managed *m, String *name, bool *hasProperty) -{ - Q_ASSERT(m->as<NamedNodeMap>()); - const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m); - QV4::ExecutionEngine *v4 = r->engine(); + if (id.isSymbol()) + return Object::virtualGet(m, id, receiver, hasProperty); - name->makeIdentifier(); - if (name->equals(v4->id_length())) + if (id == v4->id_length()->propertyKey()) return Primitive::fromInt32(r->d()->list().count()).asReturnedValue(); - QString str = name->toQString(); + QString str = id.toQString(); for (int ii = 0; ii < r->d()->list().count(); ++ii) { if (r->d()->list().at(ii)->name == str) { if (hasProperty) @@ -930,41 +928,35 @@ ReturnedValue NamedNodeMap::get(const Managed *m, String *name, bool *hasPropert ReturnedValue NamedNodeMap::create(ExecutionEngine *v4, NodeImpl *data, const QList<NodeImpl *> &list) { - return (v4->memoryManager->allocObject<NamedNodeMap>(data, list))->asReturnedValue(); + return (v4->memoryManager->allocate<NamedNodeMap>(data, list))->asReturnedValue(); } -ReturnedValue NodeList::getIndexed(const Managed *m, uint index, bool *hasProperty) +ReturnedValue NodeList::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { Q_ASSERT(m->as<NodeList>()); const NodeList *r = static_cast<const NodeList *>(m); QV4::ExecutionEngine *v4 = r->engine(); - if ((int)index < r->d()->d->children.count()) { + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + if ((int)index < r->d()->d->children.count()) { + if (hasProperty) + *hasProperty = true; + return Node::create(v4, r->d()->d->children.at(index)); + } if (hasProperty) - *hasProperty = true; - return Node::create(v4, r->d()->d->children.at(index)); + *hasProperty = false; + return Encode::undefined(); } - if (hasProperty) - *hasProperty = false; - return Encode::undefined(); -} - -ReturnedValue NodeList::get(const Managed *m, String *name, bool *hasProperty) -{ - Q_ASSERT(m->as<NodeList>()); - const NodeList *r = static_cast<const NodeList *>(m); - QV4::ExecutionEngine *v4 = r->engine(); - - name->makeIdentifier(); - if (name->equals(v4->id_length())) + if (id == v4->id_length()->propertyKey()) return Primitive::fromInt32(r->d()->d->children.count()).asReturnedValue(); - return Object::get(m, name, hasProperty); + return Object::virtualGet(m, id, receiver, hasProperty); } ReturnedValue NodeList::create(ExecutionEngine *v4, NodeImpl *data) { - return (v4->memoryManager->allocObject<NodeList>(data))->asReturnedValue(); + return (v4->memoryManager->allocate<NodeList>(data))->asReturnedValue(); } ReturnedValue Document::method_documentElement(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -1637,19 +1629,19 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject { V4_OBJECT2(QQmlXMLHttpRequestCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *, int) + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) { Scope scope(f->engine()); const QQmlXMLHttpRequestCtor *ctor = static_cast<const QQmlXMLHttpRequestCtor *>(f); QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager(), scope.engine); - Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocObject<QQmlXMLHttpRequestWrapper>(r)); + Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocate<QQmlXMLHttpRequestWrapper>(r)); ScopedObject proto(scope, ctor->d()->proto); - w->setPrototype(proto); + w->setPrototypeUnchecked(proto); return w.asReturnedValue(); } - static ReturnedValue call(const FunctionObject *, const Value *, const Value *, int) { + static ReturnedValue virtualCall(const FunctionObject *, const Value *, const Value *, int) { return Encode::undefined(); } @@ -2049,7 +2041,7 @@ void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4) { Scope scope(v4); - Scoped<QQmlXMLHttpRequestCtor> ctor(scope, v4->memoryManager->allocObject<QQmlXMLHttpRequestCtor>(v4)); + Scoped<QQmlXMLHttpRequestCtor> ctor(scope, v4->memoryManager->allocate<QQmlXMLHttpRequestCtor>(v4)); ScopedString s(scope, v4->newString(QStringLiteral("XMLHttpRequest"))); v4->globalObject->defineReadonlyProperty(s, ctor); diff --git a/src/qml/qml/qqmlxmlhttprequest_p.h b/src/qml/qml/qqmlxmlhttprequest_p.h index f2836d8301..7515ef8dcc 100644 --- a/src/qml/qml/qqmlxmlhttprequest_p.h +++ b/src/qml/qml/qqmlxmlhttprequest_p.h @@ -55,7 +55,7 @@ #include <QtCore/qglobal.h> #include <private/qqmlglobal_p.h> -#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network) +QT_REQUIRE_CONFIG(qml_xml_http_request); QT_BEGIN_NAMESPACE @@ -64,7 +64,5 @@ void qt_rem_qmlxmlhttprequest(QV4::ExecutionEngine *engine, void *); QT_END_NAMESPACE -#endif // xmlstreamreader && qml_network - #endif // QQMLXMLHTTPREQUEST_P_H diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index f04bee7f19..e6f68a30b3 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -44,7 +44,9 @@ #include <private/qqmlcomponent_p.h> #include <private/qqmlloggingcategory_p.h> #include <private/qqmlstringconverters_p.h> +#if QT_CONFIG(qml_locale) #include <private/qqmllocale_p.h> +#endif #include <private/qv8engine_p.h> #include <private/qqmldelayedcallqueue_p.h> #include <QFileInfo> @@ -65,6 +67,7 @@ #include <private/qv4jsonobject_p.h> #include <private/qv4objectproto_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qv4stackframe_p.h> #include <QtCore/qstring.h> #include <QtCore/qdatetime.h> @@ -138,7 +141,9 @@ void Heap::QtObject::init(QQmlEngine *qmlEngine) o->defineDefaultProperty(QStringLiteral("btoa"), QV4::QtObject::method_btoa); o->defineDefaultProperty(QStringLiteral("atob"), QV4::QtObject::method_atob); o->defineDefaultProperty(QStringLiteral("resolvedUrl"), QV4::QtObject::method_resolvedUrl); +#if QT_CONFIG(qml_locale) o->defineDefaultProperty(QStringLiteral("locale"), QV4::QtObject::method_locale); +#endif o->defineDefaultProperty(QStringLiteral("binding"), QV4::QtObject::method_binding); if (qmlEngine) { @@ -193,35 +198,35 @@ ReturnedValue QtObject::findAndAdd(const QString *name, bool &foundProperty) con return Encode::undefined(); } -ReturnedValue QtObject::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue QtObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { bool hasProp = false; if (hasProperty == nullptr) { hasProperty = &hasProp; } - ReturnedValue ret = QV4::Object::get(m, name, hasProperty); + ReturnedValue ret = QV4::Object::virtualGet(m, id, receiver, hasProperty); if (*hasProperty) { return ret; } auto that = static_cast<const QtObject*>(m); if (!that->d()->isComplete()) { - const QString key = name->toQString(); + const QString key = id.toQString(); ret = that->findAndAdd(&key, *hasProperty); } return ret; } -void QtObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) +void QtObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { auto that = static_cast<QtObject*>(m); if (!that->d()->isComplete()) { that->addAll(); } - QV4::Object::advanceIterator(m, it, name, index, p, attributes); + QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes); } /*! @@ -1106,7 +1111,7 @@ ReturnedValue QtObject::method_createQmlObject(const FunctionObject *b, const Va qmlerror->put((s = v4->newString(QStringLiteral("columnNumber"))), (v = QV4::Primitive::fromInt32(error.column()))); qmlerror->put((s = v4->newString(QStringLiteral("fileName"))), (v = v4->newString(error.url().toString()))); qmlerror->put((s = v4->newString(QStringLiteral("message"))), (v = v4->newString(error.description()))); - qmlerrors->putIndexed(ii, qmlerror); + qmlerrors->put(ii, qmlerror); } v = v4->newString(errorstr); @@ -1152,7 +1157,7 @@ ReturnedValue QtObject::method_createQmlObject(const FunctionObject *b, const Va if (!parentArg) THROW_GENERIC_ERROR("Qt.createQmlObject(): Missing parent object"); - QQmlTypeData *typeData = QQmlEnginePrivate::get(engine)->typeLoader.getType( + QQmlRefPointer<QQmlTypeData> typeData = QQmlEnginePrivate::get(engine)->typeLoader.getType( qml.toUtf8(), url, QQmlTypeLoader::Synchronous); Q_ASSERT(typeData->isCompleteOrError()); QQmlComponent component(engine); @@ -1309,6 +1314,7 @@ ReturnedValue QtObject::method_createComponent(const FunctionObject *b, const Va return QV4::QObjectWrapper::wrap(scope.engine, c); } +#if QT_CONFIG(qml_locale) /*! \qmlmethod Qt::locale(name) @@ -1343,6 +1349,7 @@ ReturnedValue QtObject::method_locale(const FunctionObject *b, const Value *, co return QQmlLocale::locale(scope.engine, code); } +#endif void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *bindingFunction) { @@ -1416,7 +1423,7 @@ ReturnedValue QtObject::method_binding(const FunctionObject *b, const Value *, c if (!f) THROW_TYPE_ERROR_WITH_MESSAGE("binding(): argument (binding expression) must be a function"); - return Encode(scope.engine->memoryManager->allocObject<QQmlBindingFunction>(f)); + return Encode(scope.engine->memoryManager->allocate<QQmlBindingFunction>(f)); } @@ -1551,7 +1558,7 @@ static ReturnedValue writeToConsole(const FunctionObject *b, const Value *, cons if (i != start) result.append(QLatin1Char(' ')); - if (argv[i].as<ArrayObject>()) + if (argv[i].isManaged() && argv[i].managed()->isArrayLike()) result += QLatin1Char('[') + argv[i].toQStringNoThrow() + QLatin1Char(']'); else result.append(argv[i].toQStringNoThrow()); @@ -1795,7 +1802,7 @@ void QV4::GlobalExtensions::init(Object *globalObject, QJSEngine::Extensions ext globalObject->defineDefaultProperty(QStringLiteral("print"), QV4::ConsoleObject::method_log); - QV4::ScopedObject console(scope, globalObject->engine()->memoryManager->allocObject<QV4::ConsoleObject>()); + QV4::ScopedObject console(scope, globalObject->engine()->memoryManager->allocate<QV4::ConsoleObject>()); globalObject->defineDefaultProperty(QStringLiteral("console"), console); } @@ -1850,6 +1857,10 @@ ReturnedValue GlobalExtensions::method_qsTranslate(const FunctionObject *b, cons if (argc > i) n = argv[i].toInt32(); + if (QQmlEnginePrivate *ep = (scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr)) + if (ep->propertyCapture) + ep->propertyCapture->captureTranslation(); + QString result = QCoreApplication::translate(context.toUtf8().constData(), text.toUtf8().constData(), comment.toUtf8().constData(), @@ -1953,6 +1964,10 @@ ReturnedValue GlobalExtensions::method_qsTr(const FunctionObject *b, const Value if (argc > 2) n = argv[2].toInt32(); + if (QQmlEnginePrivate *ep = (scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr)) + if (ep->propertyCapture) + ep->propertyCapture->captureTranslation(); + QString result = QCoreApplication::translate(context.toUtf8().constData(), text.toUtf8().constData(), comment.toUtf8().constData(), n); @@ -2033,6 +2048,10 @@ ReturnedValue GlobalExtensions::method_qsTrId(const FunctionObject *b, const Val if (argc > 1) n = argv[1].toInt32(); + if (QQmlEnginePrivate *ep = (scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr)) + if (ep->propertyCapture) + ep->propertyCapture->captureTranslation(); + return Encode(scope.engine->newString(qtTrId(argv[0].toQStringNoThrow().toUtf8().constData(), n))); } diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index ee3b5f7d6e..4b3814c8b8 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -93,8 +93,8 @@ struct QtObject : Object { V4_OBJECT2(QtObject, Object) - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static ReturnedValue method_isQtObject(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_rgba(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); @@ -126,7 +126,9 @@ struct QtObject : Object static ReturnedValue method_resolvedUrl(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_createQmlObject(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_createComponent(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +#if QT_CONFIG(qml_locale) static ReturnedValue method_locale(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +#endif static ReturnedValue method_binding(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_platform(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 038a75d50a..78c9fe822b 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -39,14 +39,21 @@ #include "qv8engine_p.h" +#if QT_CONFIG(qml_sequence_object) #include "qv4sequenceobject_p.h" +#endif + #include "private/qjsengine_p.h" #include <private/qqmlbuiltinfunctions_p.h> #include <private/qqmllist_p.h> #include <private/qqmlengine_p.h> +#if QT_CONFIG(qml_xml_http_request) #include <private/qqmlxmlhttprequest_p.h> +#endif +#if QT_CONFIG(qml_locale) #include <private/qqmllocale_p.h> +#endif #include <private/qqmlglobal_p.h> #include <private/qqmlmemoryprofiler_p.h> #include <private/qqmlplatform_p.h> @@ -78,6 +85,7 @@ #include <private/qv4script_p.h> #include <private/qv4include_p.h> #include <private/qv4jsonobject_p.h> +#include <private/qv4identifiertable_p.h> Q_DECLARE_METATYPE(QList<int>) @@ -123,11 +131,12 @@ static void restoreJSValue(QDataStream &stream, void *data) } } -QV8Engine::QV8Engine(QJSEngine *qq, QV4::ExecutionEngine *v4) - : q(qq) - , m_engine(nullptr) +QV8Engine::QV8Engine(QV4::ExecutionEngine *v4) + : m_engine(nullptr) , m_v4Engine(v4) +#if QT_CONFIG(qml_xml_http_request) , m_xmlHttpRequestData(nullptr) +#endif { #ifdef Q_PROCESSOR_X86_32 if (!qCpuHasFeature(SSE2)) { @@ -157,7 +166,7 @@ QV8Engine::~QV8Engine() qDeleteAll(m_extensionData); m_extensionData.clear(); -#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network) +#if QT_CONFIG(qml_xml_http_request) qt_rem_qmlxmlhttprequest(m_v4Engine, m_xmlHttpRequestData); m_xmlHttpRequestData = nullptr; #endif @@ -180,14 +189,16 @@ void QV8Engine::initializeGlobal() QV4::Scope scope(m_v4Engine); QV4::GlobalExtensions::init(m_v4Engine->globalObject, QJSEngine::AllExtensions); - QV4::ScopedObject qt(scope, m_v4Engine->memoryManager->allocObject<QV4::QtObject>(m_engine)); + QV4::ScopedObject qt(scope, m_v4Engine->memoryManager->allocate<QV4::QtObject>(m_engine)); m_v4Engine->globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt); +#if QT_CONFIG(qml_locale) QQmlLocale::registerStringLocaleCompare(m_v4Engine); QQmlDateExtension::registerExtension(m_v4Engine); QQmlNumberExtension::registerExtension(m_v4Engine); +#endif -#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network) +#if QT_CONFIG(qml_xml_http_request) qt_add_domexceptions(m_v4Engine); m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(m_v4Engine); #endif @@ -196,8 +207,10 @@ void QV8Engine::initializeGlobal() { for (uint i = 0; i < m_v4Engine->globalObject->internalClass()->size; ++i) { - if (m_v4Engine->globalObject->internalClass()->nameMap.at(i)) - m_illegalNames.insert(m_v4Engine->globalObject->internalClass()->nameMap.at(i)->string); + if (m_v4Engine->globalObject->internalClass()->nameMap.at(i).isString()) { + QV4::PropertyKey id = m_v4Engine->globalObject->internalClass()->nameMap.at(i); + m_illegalNames.insert(id.toQString()); + } } } } @@ -210,25 +223,25 @@ static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object) QV4::Scope scope(v4); bool instanceOfObject = false; - QV4::ScopedObject p(scope, object->prototype()); + QV4::ScopedObject p(scope, object->getPrototypeOf()); while (p) { if (p->d() == v4->objectPrototype()->d()) { instanceOfObject = true; break; } - p = p->prototype(); + p = p->getPrototypeOf(); } if (!instanceOfObject) return; - QV4::InternalClass *frozen = object->internalClass()->propertiesFrozen(); + QV4::Heap::InternalClass *frozen = object->internalClass()->propertiesFrozen(); if (object->internalClass() == frozen) return; object->setInternalClass(frozen); QV4::ScopedObject o(scope); for (uint i = 0; i < frozen->size; ++i) { - if (!frozen->nameMap.at(i)) + if (!frozen->nameMap.at(i).isStringOrSymbol()) continue; o = *object->propertyData(i); if (o) diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index b6a378667e..23559618ef 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -67,6 +67,7 @@ #include <private/qv4value_p.h> #include <private/qv4identifier_p.h> #include <private/qv4context_p.h> +#include <private/qv4stackframe_p.h> #include <private/qqmldelayedcallqueue_p.h> QT_BEGIN_NAMESPACE @@ -154,10 +155,9 @@ class Q_QML_PRIVATE_EXPORT QV8Engine friend class QJSEngine; public: // static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; } - static QV4::ExecutionEngine *getV4(QJSEngine *q) { return q->handle(); } static QV4::ExecutionEngine *getV4(QV8Engine *d) { return d->m_v4Engine; } - QV8Engine(QJSEngine* qq, QV4::ExecutionEngine *v4); + QV8Engine(QV4::ExecutionEngine *v4); virtual ~QV8Engine(); // This enum should be in sync with QQmlEngine::ObjectOwnership @@ -170,10 +170,11 @@ public: void initQmlGlobalObject(); void setEngine(QQmlEngine *engine); QQmlEngine *engine() { return m_engine; } - QJSEngine *publicEngine() { return q; } QQmlDelayedCallQueue *delayedCallQueue() { return &m_delayedCallQueue; } +#if QT_CONFIG(qml_xml_http_request) void *xmlHttpRequestData() const { return m_xmlHttpRequestData; } +#endif void freezeObject(const QV4::Value &value); @@ -202,13 +203,14 @@ public: int consoleCountHelper(const QString &file, quint16 line, quint16 column); protected: - QJSEngine* q; QQmlEngine *m_engine; QQmlDelayedCallQueue m_delayedCallQueue; QV4::ExecutionEngine *m_v4Engine; +#if QT_CONFIG(qml_xml_http_request) void *m_xmlHttpRequestData; +#endif QVector<Deletable *> m_extensionData; diff --git a/src/qml/qtqmlglobal.h b/src/qml/qtqmlglobal.h index 6e92867cf5..e02dfa5ed4 100644 --- a/src/qml/qtqmlglobal.h +++ b/src/qml/qtqmlglobal.h @@ -52,6 +52,7 @@ # endif #else # define QT_FEATURE_qml_debug -1 +# define QT_FEATURE_qml_sequence_object 1 #endif QT_BEGIN_NAMESPACE diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h index 4c0ba338d8..bb880451a9 100644 --- a/src/qml/qtqmlglobal_p.h +++ b/src/qml/qtqmlglobal_p.h @@ -57,6 +57,8 @@ # include <QtQml/private/qtqml-config_p.h> #endif +#define Q_QML_PRIVATE_API_VERSION 2 + #define Q_QML_PRIVATE_EXPORT Q_QML_EXPORT #if !defined(QT_QMLDEVTOOLS_LIB) && !defined(QT_BUILD_QMLDEVTOOLS_LIB) diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 7a12813f0c..7aba0689d1 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -93,10 +93,10 @@ struct DelegateModelGroupFunction : QV4::FunctionObject static Heap::DelegateModelGroupFunction *create(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)) { - return scope->engine()->memoryManager->allocObject<DelegateModelGroupFunction>(scope, flag, code); + return scope->engine()->memoryManager->allocate<DelegateModelGroupFunction>(scope, flag, code); } - static ReturnedValue call(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc) + static ReturnedValue virtualCall(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc) { QV4::Scope scope(that->engine()); QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<const DelegateModelGroupFunction *>(that)); @@ -200,8 +200,7 @@ QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent) */ QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt) - : m_delegate(nullptr) - , m_cacheMetaType(nullptr) + : m_cacheMetaType(nullptr) , m_context(ctxt) , m_parts(nullptr) , m_filterGroup(QStringLiteral("items")) @@ -214,6 +213,7 @@ QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt) , m_transaction(false) , m_incubatorCleanupScheduled(false) , m_waitingToFetchMore(false) + , m_useFirstColumnOnly(true) , m_cacheItems(nullptr) , m_items(nullptr) , m_persistedItems(nullptr) @@ -228,6 +228,11 @@ QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate() m_cacheMetaType->release(); } +int QQmlDelegateModelPrivate::adaptorModelCount() const +{ + return m_useFirstColumnOnly ? m_adaptorModel.rowCount() : m_adaptorModel.count(); +} + void QQmlDelegateModelPrivate::requestMoreIfNecessary() { Q_Q(QQmlDelegateModel); @@ -263,6 +268,8 @@ QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent) QQmlDelegateModel::~QQmlDelegateModel() { Q_D(QQmlDelegateModel); + d->disconnectFromAbstractItemModel(); + d->m_adaptorModel.setObject(nullptr, this); for (QQmlDelegateModelItem *cacheItem : qAsConst(d->m_cache)) { if (cacheItem->object) { @@ -336,7 +343,7 @@ void QQmlDelegateModel::componentComplete() static_cast<QQmlPartsModel *>(d->m_pendingParts.first())->updateFilterGroup(); QVector<Compositor::Insert> inserts; - d->m_count = d->m_adaptorModel.count(); + d->m_count = d->adaptorModelCount(); d->m_compositor.append( &d->m_adaptorModel, 0, @@ -368,6 +375,54 @@ QVariant QQmlDelegateModel::model() const return d->m_adaptorModel.model(); } +void QQmlDelegateModelPrivate::connectToAbstractItemModel() +{ + Q_Q(QQmlDelegateModel); + if (!m_adaptorModel.adaptsAim()) + return; + + auto aim = m_adaptorModel.aim(); + + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + q, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), + q, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + q, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), + q, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + q, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(modelReset()), + q, QQmlDelegateModel, SLOT(_q_modelReset())); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), + q, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); +} + +void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel() +{ + Q_Q(QQmlDelegateModel); + if (!m_adaptorModel.adaptsAim()) + return; + + auto aim = m_adaptorModel.aim(); + + QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)), + q, SLOT(_q_rowsInserted(QModelIndex,int,int))); + QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); + QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)), + q, SLOT(_q_rowsRemoved(QModelIndex,int,int))); + QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), + q, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); + QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + q, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); + QObject::disconnect(aim, SIGNAL(modelReset()), + q, SLOT(_q_modelReset())); + QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), + q, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); +} + void QQmlDelegateModel::setModel(const QVariant &model) { Q_D(QQmlDelegateModel); @@ -375,7 +430,10 @@ void QQmlDelegateModel::setModel(const QVariant &model) if (d->m_complete) _q_itemsRemoved(0, d->m_count); + d->disconnectFromAbstractItemModel(); d->m_adaptorModel.setModel(model, this, d->m_context->engine()); + d->connectToAbstractItemModel(); + d->m_adaptorModel.replaceWatchedRoles(QList<QByteArray>(), d->m_watchedRoles); for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) { d->m_adaptorModel.replaceWatchedRoles( @@ -383,7 +441,7 @@ void QQmlDelegateModel::setModel(const QVariant &model) } if (d->m_complete) { - _q_itemsInserted(0, d->m_adaptorModel.count()); + _q_itemsInserted(0, d->adaptorModelCount()); d->requestMoreIfNecessary(); } } @@ -409,7 +467,7 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) return; } bool wasValid = d->m_delegate != nullptr; - d->m_delegate = delegate; + d->m_delegate.setObject(delegate, this); d->m_delegateValidated = false; if (wasValid && d->m_complete) { for (int i = 1; i < d->m_groupCount; ++i) { @@ -470,12 +528,16 @@ void QQmlDelegateModel::setRootIndex(const QVariant &root) if (changed || !d->m_adaptorModel.isValid()) { const int oldCount = d->m_count; d->m_adaptorModel.rootIndex = modelIndex; - if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) // The previous root index was invalidated, so we need to reconnect the model. + if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) { + // The previous root index was invalidated, so we need to reconnect the model. + d->disconnectFromAbstractItemModel(); d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine()); + d->connectToAbstractItemModel(); + } if (d->m_adaptorModel.canFetchMore()) d->m_adaptorModel.fetchMore(); if (d->m_complete) { - const int newCount = d->m_adaptorModel.count(); + const int newCount = d->adaptorModelCount(); if (oldCount) _q_itemsRemoved(0, oldCount); if (newCount) @@ -487,6 +549,35 @@ void QQmlDelegateModel::setRootIndex(const QVariant &root) } /*! + \qmlproperty int QtQml.Models::DelegateModel::rows + + Contains the number of rows in the model. If the model + is a list of items, it will be equal to the number of items + in the list. + + \since QtQml.Models 2.12 +*/ +int QQmlDelegateModel::rows() const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.rowCount(); +} + +/*! + \qmlproperty int QtQml.Models::DelegateModel::columns + + Contains the number of columns in the model. If the model + is a list of items, it will be equal to \c 1. + + \since QtQml.Models 2.12 +*/ +int QQmlDelegateModel::columns() const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.columnCount(); +} + +/*! \qmlmethod QModelIndex QtQml.Models::DelegateModel::modelIndex(int index) QAbstractItemModel provides a hierarchical tree of data, whereas @@ -536,25 +627,24 @@ int QQmlDelegateModel::count() const QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object) { - QQmlDelegateModel::ReleaseFlags stat = nullptr; if (!object) - return stat; - - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object)) { - if (cacheItem->releaseObject()) { - cacheItem->destroyObject(); - emitDestroyingItem(object); - if (cacheItem->incubationTask) { - releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = nullptr; - } - cacheItem->Dispose(); - stat |= QQmlInstanceModel::Destroyed; - } else { - stat |= QQmlDelegateModel::Referenced; - } + return QQmlDelegateModel::ReleaseFlags(0); + + QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object); + if (!cacheItem) + return QQmlDelegateModel::ReleaseFlags(0); + + if (!cacheItem->releaseObject()) + return QQmlDelegateModel::Referenced; + + cacheItem->destroyObject(); + emitDestroyingItem(object); + if (cacheItem->incubationTask) { + releaseIncubator(cacheItem->incubationTask); + cacheItem->incubationTask = nullptr; } - return stat; + cacheItem->Dispose(); + return QQmlInstanceModel::Destroyed; } /* @@ -808,6 +898,12 @@ QObject *QQmlDelegateModel::parts() return d->m_parts; } +const QAbstractItemModel *QQmlDelegateModel::abstractItemModel() const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.adaptsAim() ? d->m_adaptorModel.aim() : nullptr; +} + void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) { for (int i = 1; i < m_groupCount; ++i) @@ -862,6 +958,13 @@ void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTa } } +void QQmlDelegateModelPrivate::addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it) +{ + m_cache.insert(it.cacheIndex, item); + m_compositor.setFlags(it, 1, Compositor::CacheFlag); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); +} + void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem) { int cidx = m_cache.lastIndexOf(cacheItem); @@ -951,10 +1054,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ return nullptr; cacheItem->groups = it->flags; - - m_cache.insert(it.cacheIndex, cacheItem); - m_compositor.setFlags(it, 1, Compositor::CacheFlag); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + addCacheItem(cacheItem, it); } // Bump the reference counts temporarily so neither the content data or the delegate object @@ -1046,7 +1146,10 @@ QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index) if (!it->inCache()) return QQmlIncubator::Null; - return d->m_cache.at(it.cacheIndex)->incubationTask->status(); + if (auto incubationTask = d->m_cache.at(it.cacheIndex)->incubationTask) + return incubationTask->status(); + + return QQmlIncubator::Ready; } QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) @@ -1266,8 +1369,12 @@ void QQmlDelegateModel::_q_itemsInserted(int index, int count) const QList<QQmlDelegateModelItem *> cache = d->m_cache; for (int i = 0, c = cache.count(); i < c; ++i) { QQmlDelegateModelItem *item = cache.at(i); - if (item->modelIndex() >= index) - item->setModelIndex(item->modelIndex() + count); + if (item->modelIndex() >= index) { + const int newIndex = item->modelIndex() + count; + const int row = newIndex; + const int column = 0; + item->setModelIndex(newIndex, row, column); + } } QVector<Compositor::Insert> inserts; @@ -1408,10 +1515,14 @@ void QQmlDelegateModel::_q_itemsRemoved(int index, int count) if (!d->m_cache.contains(item)) continue; - if (item->modelIndex() >= index + count) - item->setModelIndex(item->modelIndex() - count); - else if (item->modelIndex() >= index) - item->setModelIndex(-1); + if (item->modelIndex() >= index + count) { + const int newIndex = item->modelIndex() - count; + const int row = newIndex; + const int column = 0; + item->setModelIndex(newIndex, row, column); + } else if (item->modelIndex() >= index) { + item->setModelIndex(-1, -1, -1); + } } QVector<Compositor::Remove> removes; @@ -1456,10 +1567,17 @@ void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count) const QList<QQmlDelegateModelItem *> cache = d->m_cache; for (int i = 0, c = cache.count(); i < c; ++i) { QQmlDelegateModelItem *item = cache.at(i); - if (item->modelIndex() >= from && item->modelIndex() < from + count) - item->setModelIndex(item->modelIndex() - from + to); - else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) - item->setModelIndex(item->modelIndex() + difference); + if (item->modelIndex() >= from && item->modelIndex() < from + count) { + const int newIndex = item->modelIndex() - from + to; + const int row = newIndex; + const int column = 0; + item->setModelIndex(newIndex, row, column); + } else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) { + const int newIndex = item->modelIndex() + difference; + const int row = newIndex; + const int column = 0; + item->setModelIndex(newIndex, row, column); + } } QVector<Compositor::Remove> removes; @@ -1510,13 +1628,13 @@ void QQmlDelegateModel::_q_modelReset() d->m_adaptorModel.rootIndex = QModelIndex(); if (d->m_complete) { - d->m_count = d->m_adaptorModel.count(); + d->m_count = d->adaptorModelCount(); const QList<QQmlDelegateModelItem *> cache = d->m_cache; for (int i = 0, c = cache.count(); i < c; ++i) { QQmlDelegateModelItem *item = cache.at(i); if (item->modelIndex() != -1) - item->setModelIndex(-1); + item->setModelIndex(-1, -1, -1); } QVector<Compositor::Remove> removes; @@ -1552,7 +1670,8 @@ void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int b if (index.parent() == parent && index.row() >= begin && index.row() <= end) { const int oldCount = d->m_count; d->m_count = 0; - d->m_adaptorModel.invalidateModel(this); + d->disconnectFromAbstractItemModel(); + d->m_adaptorModel.invalidateModel(); if (d->m_complete && oldCount > 0) { QVector<Compositor::Remove> removes; @@ -1588,8 +1707,15 @@ void QQmlDelegateModel::_q_rowsMoved( void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles) { Q_D(QQmlDelegateModel); - if (begin.parent() == d->m_adaptorModel.rootIndex) - _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles); + if (begin.parent() != d->m_adaptorModel.rootIndex) + return; + + int rowCount = end.row() - begin.row() + 1; + + for (int col = begin.column(); col <= end.column(); ++col) { + int startIndex = d->m_adaptorModel.indexAt(begin.row(), col); + _q_itemsChanged(startIndex, rowCount, roles); + } } bool QQmlDelegateModel::isDescendantOf(const QPersistentModelIndex& desc, const QList< QPersistentModelIndex >& parents) const @@ -1804,7 +1930,7 @@ int QQmlDelegateModelItemMetaType::parseGroups(const QV4::Value &groups) const QV4::ScopedValue v(scope); uint arrayLength = array->getLength(); for (uint i = 0; i < arrayLength; ++i) { - v = array->getIndexed(i); + v = array->get(i); const QString groupName = v->toQString(); int index = groupNames.indexOf(groupName); if (index != -1) @@ -1906,8 +2032,7 @@ void QV4::Heap::QQmlDelegateModelItemObject::destroy() } -QQmlDelegateModelItem::QQmlDelegateModelItem( - QQmlDelegateModelItemMetaType *metaType, int modelIndex) +QQmlDelegateModelItem::QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex, int row, int column) : v4(metaType->v4Engine) , metaType(metaType) , contextData(nullptr) @@ -1918,6 +2043,8 @@ QQmlDelegateModelItem::QQmlDelegateModelItem( , scriptRef(0) , groups(0) , index(modelIndex) + , row(row) + , column(column) { metaType->addref(); } @@ -1952,6 +2079,26 @@ void QQmlDelegateModelItem::Dispose() delete this; } +void QQmlDelegateModelItem::setModelIndex(int idx, int newRow, int newColumn) +{ + if (idx == index) + return; + + const int prevRow = row; + const int prevColumn = column; + + index = idx; + row = newRow; + column = newColumn; + + Q_EMIT modelIndexChanged(); + + if (row != prevRow) + emit rowChanged(); + if (column != prevColumn) + emit columnChanged(); +} + void QQmlDelegateModelItem::destroyObject() { Q_ASSERT(object); @@ -2080,22 +2227,29 @@ QQmlDelegateModelAttached::QQmlDelegateModelAttached( , m_previousGroups(cacheItem->groups) { QQml_setParent_noEvent(this, parent); + resetCurrentIndex(); + // Let m_previousIndex be equal to m_currentIndex + std::copy(std::begin(m_currentIndex), std::end(m_currentIndex), std::begin(m_previousIndex)); + + if (!cacheItem->metaType->metaObject) + cacheItem->metaType->initializeMetaObject(); + + QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject; + cacheItem->metaType->metaObject->addref(); +} + +void QQmlDelegateModelAttached::resetCurrentIndex() +{ if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) { for (int i = 1; i < qMin<int>(m_cacheItem->metaType->groupCount, Compositor::MaximumGroupCount); ++i) - m_currentIndex[i] = m_previousIndex[i] = incubationTask->index[i]; + m_currentIndex[i] = incubationTask->index[i]; } else { QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); Compositor::iterator it = model->m_compositor.find( Compositor::Cache, model->m_cache.indexOf(m_cacheItem)); for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) - m_currentIndex[i] = m_previousIndex[i] = it.index[i]; + m_currentIndex[i] = it.index[i]; } - - if (!cacheItem->metaType->metaObject) - cacheItem->metaType->initializeMetaObject(); - - QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject; - cacheItem->metaType->metaObject->addref(); } /*! @@ -2483,9 +2637,9 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index) model->m_cacheMetaType->initializePrototype(); QV4::ExecutionEngine *v4 = model->m_cacheMetaType->v4Engine; QV4::Scope scope(v4); - QV4::ScopedObject o(scope, v4->memoryManager->allocObject<QQmlDelegateModelItemObject>(cacheItem)); + QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(cacheItem)); QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value()); - o->setPrototype(p); + o->setPrototypeOf(p); ++cacheItem->scriptRef; return QQmlV4Handle(o); @@ -3194,7 +3348,10 @@ QQmlIncubator::Status QQmlPartsModel::incubationStatus(int index) if (!it->inCache()) return QQmlIncubator::Null; - return model->m_cache.at(it.cacheIndex)->incubationTask->status(); + if (auto incubationTask = model->m_cache.at(it.cacheIndex)->incubationTask) + return incubationTask->status(); + + return QQmlIncubator::Ready; } int QQmlPartsModel::indexOf(QObject *item, QObject *) const @@ -3214,7 +3371,10 @@ void QQmlPartsModel::createdPackage(int index, QQuickPackage *package) void QQmlPartsModel::initPackage(int index, QQuickPackage *package) { - emit initItem(index, package->part(m_part)); + if (m_modelUpdatePending) + m_pendingPackageInitializations << index; + else + emit initItem(index, package->part(m_part)); } void QQmlPartsModel::destroyingPackage(QQuickPackage *package) @@ -3226,9 +3386,22 @@ void QQmlPartsModel::destroyingPackage(QQuickPackage *package) void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) { + m_modelUpdatePending = false; emit modelUpdated(changeSet, reset); if (changeSet.difference() != 0) emit countChanged(); + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + QVector<int> pendingPackageInitializations; + qSwap(pendingPackageInitializations, m_pendingPackageInitializations); + for (int index : pendingPackageInitializations) { + if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) + continue; + QObject *object = model->object(m_compositorGroup, index, QQmlIncubator::Asynchronous); + if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) + emit initItem(index, package->part(m_part)); + model->release(object); + } } //============================================================================ @@ -3238,7 +3411,7 @@ struct QQmlDelegateModelGroupChange : QV4::Object V4_OBJECT2(QQmlDelegateModelGroupChange, QV4::Object) static QV4::Heap::QQmlDelegateModelGroupChange *create(QV4::ExecutionEngine *e) { - return e->memoryManager->allocObject<QQmlDelegateModelGroupChange>(); + return e->memoryManager->allocate<QQmlDelegateModelGroupChange>(); } static QV4::ReturnedValue method_get_index(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { @@ -3275,49 +3448,49 @@ struct QQmlDelegateModelGroupChangeArray : public QV4::Object public: static QV4::Heap::QQmlDelegateModelGroupChangeArray *create(QV4::ExecutionEngine *engine, const QVector<QQmlChangeSet::Change> &changes) { - return engine->memoryManager->allocObject<QQmlDelegateModelGroupChangeArray>(changes); + return engine->memoryManager->allocate<QQmlDelegateModelGroupChangeArray>(changes); } quint32 count() const { return d()->changes->count(); } const QQmlChangeSet::Change &at(int index) const { return d()->changes->at(index); } - static QV4::ReturnedValue getIndexed(const QV4::Managed *m, uint index, bool *hasProperty) + static QV4::ReturnedValue virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty) { - Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); - QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine(); - QV4::Scope scope(v4); - QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<const QQmlDelegateModelGroupChangeArray *>(m)); - - if (index >= array->count()) { - if (hasProperty) - *hasProperty = false; - return QV4::Primitive::undefinedValue().asReturnedValue(); - } + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); + QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine(); + QV4::Scope scope(v4); + QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<const QQmlDelegateModelGroupChangeArray *>(m)); + + if (index >= array->count()) { + if (hasProperty) + *hasProperty = false; + return QV4::Primitive::undefinedValue().asReturnedValue(); + } - const QQmlChangeSet::Change &change = array->at(index); + const QQmlChangeSet::Change &change = array->at(index); - QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value()); - QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4)); - object->setPrototype(changeProto); - object->d()->change = change; + QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value()); + QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4)); + object->setPrototypeOf(changeProto); + object->d()->change = change; - if (hasProperty) - *hasProperty = true; - return object.asReturnedValue(); - } + if (hasProperty) + *hasProperty = true; + return object.asReturnedValue(); + } - static QV4::ReturnedValue get(const QV4::Managed *m, QV4::String *name, bool *hasProperty) - { Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); const QQmlDelegateModelGroupChangeArray *array = static_cast<const QQmlDelegateModelGroupChangeArray *>(m); - if (name->equals(array->engine()->id_length())) { + if (id == array->engine()->id_length()->propertyKey()) { if (hasProperty) *hasProperty = true; return QV4::Encode(array->count()); } - return Object::get(m, name, hasProperty); + return Object::virtualGet(m, id, receiver, hasProperty); } }; diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h index b0786cd088..65b1b82d70 100644 --- a/src/qml/types/qqmldelegatemodel_p.h +++ b/src/qml/types/qqmldelegatemodel_p.h @@ -88,6 +88,8 @@ class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public Q_PROPERTY(QQmlListProperty<QQmlDelegateModelGroup> groups READ groups CONSTANT) Q_PROPERTY(QObject *parts READ parts CONSTANT) Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) + Q_PROPERTY(int rows READ rows NOTIFY rowsChanged REVISION 12) + Q_PROPERTY(int columns READ columns NOTIFY columnsChanged REVISION 12) Q_CLASSINFO("DefaultProperty", "delegate") Q_INTERFACES(QQmlParserStatus) public: @@ -107,6 +109,9 @@ public: QVariant rootIndex() const; void setRootIndex(const QVariant &root); + int rows() const; + int columns() const; + Q_INVOKABLE QVariant modelIndex(int idx) const; Q_INVOKABLE QVariant parentModelIndex() const; @@ -130,6 +135,8 @@ public: QQmlListProperty<QQmlDelegateModelGroup> groups(); QObject *parts(); + const QAbstractItemModel *abstractItemModel() const override; + bool event(QEvent *) override; static QQmlDelegateModelAttached *qmlAttachedProperties(QObject *obj); @@ -138,6 +145,8 @@ Q_SIGNALS: void filterGroupChanged(); void defaultGroupsChanged(); void rootIndexChanged(); + Q_REVISION(12) void rowsChanged(); + Q_REVISION(12) void columnsChanged(); private Q_SLOTS: void _q_itemsChanged(int index, int count, const QVector<int> &roles); @@ -212,6 +221,7 @@ public: QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent); ~QQmlDelegateModelAttached() {} + void resetCurrentIndex(); void setCacheItem(QQmlDelegateModelItem *item); QQmlDelegateModel *model() const; diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index 18980cfd7c..c4e253800a 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -68,7 +68,7 @@ typedef QQmlListCompositor Compositor; class QQmlDelegateModelAttachedMetaObject; -class QQmlDelegateModelItemMetaType : public QQmlRefCount +class Q_QML_PRIVATE_EXPORT QQmlDelegateModelItemMetaType : public QQmlRefCount { public: QQmlDelegateModelItemMetaType(QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames); @@ -95,9 +95,11 @@ class QQmlDelegateModelItem : public QObject { Q_OBJECT Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged) + Q_PROPERTY(int row MEMBER row NOTIFY rowChanged) + Q_PROPERTY(int column MEMBER column NOTIFY columnChanged) Q_PROPERTY(QObject *model READ modelObject CONSTANT) public: - QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex); + QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex, int row, int column); ~QQmlDelegateModelItem(); void referenceObject() { ++objectRef; } @@ -121,7 +123,7 @@ public: int groupIndex(Compositor::Group group); int modelIndex() const { return index; } - void setModelIndex(int idx) { index = idx; Q_EMIT modelIndexChanged(); } + virtual void setModelIndex(int idx, int newRow, int newColumn); virtual QV4::ReturnedValue get() { return QV4::QObjectWrapper::wrap(v4, this); } @@ -148,9 +150,13 @@ public: Q_SIGNALS: void modelIndexChanged(); + void rowChanged(); + void columnChanged(); protected: void objectDestroyed(QObject *); + int row; + int column; }; namespace QV4 { @@ -190,8 +196,8 @@ public: void statusChanged(Status) override; void setInitialState(QObject *) override; - QQmlDelegateModelItem *incubating; - QQmlDelegateModelPrivate *vdm; + QQmlDelegateModelItem *incubating = nullptr; + QQmlDelegateModelPrivate *vdm = nullptr; int index[QQmlListCompositor::MaximumGroupCount]; }; @@ -256,6 +262,8 @@ public: void init(); void connectModel(QQmlAdaptorModel *model); + void connectToAbstractItemModel(); + void disconnectFromAbstractItemModel(); void requestMoreIfNecessary(); QObject *object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode); @@ -269,6 +277,7 @@ public: Q_EMIT q_func()->initItem(incubationTask->index[m_compositorGroup], item); } void emitDestroyingPackage(QQuickPackage *package); void emitDestroyingItem(QObject *item) { Q_EMIT q_func()->destroyingItem(item); } + void addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it); void removeCacheItem(QQmlDelegateModelItem *cacheItem); void updateFilterGroup(); @@ -295,6 +304,8 @@ public: bool insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups); + int adaptorModelCount() const; + static void group_append(QQmlListProperty<QQmlDelegateModelGroup> *property, QQmlDelegateModelGroup *group); static int group_count(QQmlListProperty<QQmlDelegateModelGroup> *property); static QQmlDelegateModelGroup *group_at(QQmlListProperty<QQmlDelegateModelGroup> *property, int index); @@ -305,7 +316,7 @@ public: QQmlAdaptorModel m_adaptorModel; QQmlListCompositor m_compositor; - QQmlComponent *m_delegate; + QQmlStrongJSQObjectReference<QQmlComponent> m_delegate; QQmlDelegateModelItemMetaType *m_cacheMetaType; QPointer<QQmlContext> m_context; QQmlDelegateModelParts *m_parts; @@ -327,6 +338,7 @@ public: bool m_transaction : 1; bool m_incubatorCleanupScheduled : 1; bool m_waitingToFetchMore : 1; + bool m_useFirstColumnOnly : 1; union { struct { @@ -378,8 +390,10 @@ private: QString m_part; QString m_filterGroup; QList<QByteArray> m_watchedRoles; + QVector<int> m_pendingPackageInitializations; // vector holds model indices Compositor::Group m_compositorGroup; bool m_inheritGroup; + bool m_modelUpdatePending = true; }; class QMetaPropertyBuilder; diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 830c2bef5a..2325eca469 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -62,6 +62,8 @@ #include <QtCore/qdatetime.h> #include <QScopedValueRollback> +Q_DECLARE_METATYPE(const QV4::CompiledData::Binding*); + QT_BEGIN_NAMESPACE // Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models. @@ -124,8 +126,8 @@ const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::Data const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type) { - const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; - const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; + const int dataSizes[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; + const int dataAlignments[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; Role *r = new Role; r->name = key; @@ -227,6 +229,10 @@ const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QV data.value<QJSValue>().isCallable()) { type = Role::Function; break; + } else if (data.userType() == qMetaTypeId<const QV4::CompiledData::Binding*>() + && data.value<const QV4::CompiledData::Binding*>()->isTranslationBinding()) { + type = Role::String; + break; } else { type = Role::List; break; @@ -261,6 +267,75 @@ const ListLayout::Role *ListLayout::getExistingRole(QV4::String *key) const return r; } +StringOrTranslation::StringOrTranslation(const QString &s) +{ + d.setFlag(); + setString(s); +} + +StringOrTranslation::StringOrTranslation(const QV4::CompiledData::Binding *binding) +{ + d.setFlag(); + clear(); + d = binding; +} + +StringOrTranslation::~StringOrTranslation() +{ + clear(); +} + +void StringOrTranslation::setString(const QString &s) +{ + d.setFlag(); + clear(); + QStringData *stringData = const_cast<QString &>(s).data_ptr(); + d = stringData; + if (stringData) + stringData->ref.ref(); +} + +void StringOrTranslation::setTranslation(const QV4::CompiledData::Binding *binding) +{ + d.setFlag(); + clear(); + d = binding; +} + +QString StringOrTranslation::toString(const QQmlListModel *owner) const +{ + if (d.isNull()) + return QString(); + if (d.isT1()) { + QStringDataPtr holder = { d.asT1() }; + holder.ptr->ref.ref(); + return QString(holder); + } + if (!owner) + return QString(); + return d.asT2()->valueAsString(owner->m_compilationUnit->data); +} + +QString StringOrTranslation::asString() const +{ + if (d.isNull()) + return QString(); + if (!d.isT1()) + return QString(); + QStringDataPtr holder = { d.asT1() }; + holder.ptr->ref.ref(); + return QString(holder); +} + +void StringOrTranslation::clear() +{ + if (QStringData *strData = d.isT1() ? d.asT1() : nullptr) { + if (!strData->ref.deref()) + QStringData::deallocate(strData); + } + d = static_cast<QStringData *>(nullptr); +} + QObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex) { ListElement *e = elements[elementIndex]; @@ -512,7 +587,7 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { - o = a->getIndexed(j); + o = a->get(j); subModel->append(o); } @@ -593,7 +668,7 @@ void ListModel::set(int elementIndex, QV4::Object *object) int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { - o = a->getIndexed(j); + o = a->get(j); subModel->append(o); } @@ -717,11 +792,11 @@ ModelNodeMetaObject *ListElement::objectCache() return ModelNodeMetaObject::get(m_objectCache); } -QString *ListElement::getStringProperty(const ListLayout::Role &role) +StringOrTranslation *ListElement::getStringProperty(const ListLayout::Role &role) { char *mem = getPropertyMemory(role); - QString *s = reinterpret_cast<QString *>(mem); - return s->data_ptr() ? s : nullptr; + StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem); + return s; } QObject *ListElement::getQObjectProperty(const ListLayout::Role &role) @@ -806,9 +881,9 @@ QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListMo break; case ListLayout::Role::String: { - QString *value = reinterpret_cast<QString *>(mem); - if (value->data_ptr() != nullptr) - data = *value; + StringOrTranslation *value = reinterpret_cast<StringOrTranslation *>(mem); + if (value->isSet()) + data = value->toString(owner); } break; case ListLayout::Role::Bool: @@ -878,15 +953,13 @@ int ListElement::setStringProperty(const ListLayout::Role &role, const QString & if (role.type == ListLayout::Role::String) { char *mem = getPropertyMemory(role); - QString *c = reinterpret_cast<QString *>(mem); + StringOrTranslation *c = reinterpret_cast<StringOrTranslation *>(mem); bool changed; - if (c->data_ptr() == nullptr) { - new (mem) QString(s); + if (!c->isSet() || c->isTranslation()) changed = true; - } else { - changed = c->compare(s) != 0; - *c = s; - } + else + changed = c->asString().compare(s) != 0; + c->setString(s); if (changed) roleIndex = role.index; } @@ -1048,11 +1121,25 @@ int ListElement::setFunctionProperty(const ListLayout::Role &role, const QJSValu return roleIndex; } +int ListElement::setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::String) { + char *mem = getPropertyMemory(role); + StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem); + s->setTranslation(b); + roleIndex = role.index; + } + + return roleIndex; +} + void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s) { char *mem = getPropertyMemory(role); - new (mem) QString(s); + new (mem) StringOrTranslation(s); } void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d) @@ -1219,9 +1306,9 @@ void ListElement::destroy(ListLayout *layout) switch (r.type) { case ListLayout::Role::String: { - QString *string = getStringProperty(r); + StringOrTranslation *string = getStringProperty(r); if (string) - string->~QString(); + string->~StringOrTranslation(); } break; case ListLayout::Role::List: @@ -1284,7 +1371,10 @@ int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant roleIndex = setDoubleProperty(role, d.toDouble()); break; case ListLayout::Role::String: - roleIndex = setStringProperty(role, d.toString()); + if (d.userType() == qMetaTypeId<const QV4::CompiledData::Binding *>()) + roleIndex = setTranslationProperty(role, d.value<const QV4::CompiledData::Binding*>()); + else + roleIndex = setStringProperty(role, d.toString()); break; case ListLayout::Role::Bool: roleIndex = setBoolProperty(role, d.toBool()); @@ -1332,7 +1422,7 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d ListModel *subModel = new ListModel(role.subLayout, nullptr); int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { - o = a->getIndexed(j); + o = a->get(j); subModel->append(o); } roleIndex = setListProperty(role, subModel); @@ -1446,7 +1536,7 @@ void ModelNodeMetaObject::propertyWritten(int index) return; QString propName = QString::fromUtf8(name(index)); - QVariant value = operator[](index); + const QVariant value = this->value(index); QV4::Scope scope(m_model->engine()); QV4::ScopedValue v(scope, scope.engine->fromVariant(value)); @@ -1474,29 +1564,37 @@ void ModelNodeMetaObject::emitDirectNotifies(const int *changedRoles, int roleCo namespace QV4 { -bool ModelObject::put(Managed *m, String *name, const Value &value) +bool ModelObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { + if (!id.isString()) + return Object::virtualPut(m, id, value, receiver); + QString propName = id.toQString(); + ModelObject *that = static_cast<ModelObject*>(m); ExecutionEngine *eng = that->engine(); const int elementIndex = that->d()->elementIndex(); - const QString propName = name->toQString(); int roleIndex = that->d()->m_model->m_listModel->setExistingProperty(elementIndex, propName, value, eng); if (roleIndex != -1) that->d()->m_model->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex)); ModelNodeMetaObject *mo = ModelNodeMetaObject::get(that->object()); if (mo->initialized()) - mo->emitPropertyNotification(name->toQString().toUtf8()); + mo->emitPropertyNotification(propName.toUtf8()); return true; } -ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { + if (!id.isString()) + return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); + const ModelObject *that = static_cast<const ModelObject*>(m); + Scope scope(that); + ScopedString name(scope, id.asStringOrSymbol()); const ListLayout::Role *role = that->d()->m_model->m_listModel->getExistingRole(name); if (!role) - return QObjectWrapper::get(m, name, hasProperty); + return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); if (hasProperty) *hasProperty = true; @@ -1512,7 +1610,7 @@ ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty return that->engine()->fromVariant(value); } -void ModelObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) +void ModelObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { ModelObject *that = static_cast<ModelObject*>(m); ExecutionEngine *v4 = that->engine(); @@ -1532,7 +1630,7 @@ void ModelObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, u // Fall back to QV4::Object as opposed to QV4::QObjectWrapper otherwise it will add // unnecessary entries that relate to the roles used. These just create extra work // later on as they will just be ignored. - QV4::Object::advanceIterator(m, it, name, index, p, attributes); + QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes); } DEFINE_OBJECT_VTABLE(ModelObject); @@ -1809,6 +1907,7 @@ QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV4::E m_listModel = data; m_engine = engine; + m_compilationUnit = owner->m_compilationUnit; } QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent) @@ -1828,6 +1927,7 @@ QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agen ListModel::sync(orig->m_listModel, m_listModel); m_engine = nullptr; + m_compilationUnit = orig->m_compilationUnit; } QQmlListModel::~QQmlListModel() @@ -2244,7 +2344,7 @@ void QQmlListModel::insert(QQmlV4Function *args) int objectArrayLength = objectArray->getLength(); emitItemsAboutToBeInserted(index, objectArrayLength); for (int i=0 ; i < objectArrayLength ; ++i) { - argObject = objectArray->getIndexed(i); + argObject = objectArray->get(i); if (m_dynamicRoles) { m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); @@ -2356,7 +2456,7 @@ void QQmlListModel::append(QQmlV4Function *args) emitItemsAboutToBeInserted(index, objectArrayLength); for (int i=0 ; i < objectArrayLength ; ++i) { - argObject = objectArray->getIndexed(i); + argObject = objectArray->get(i); if (m_dynamicRoles) { m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); @@ -2434,7 +2534,7 @@ QQmlV4Handle QQmlListModel::get(int index) const QObject *object = m_listModel->getOrCreateModelObject(const_cast<QQmlListModel *>(this), index); QQmlData *ddata = QQmlData::get(object); if (ddata->jsWrapper.isNullOrUndefined()) { - result = scope.engine->memoryManager->allocObject<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this)); + result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this)); // Keep track of the QObjectWrapper in persistent value storage ddata->jsWrapper.set(scope.engine, result); } else { @@ -2631,7 +2731,9 @@ bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *comp } else { QVariant value; - if (binding->evaluatesToString()) { + if (binding->isTranslationBinding()) { + value = QVariant::fromValue<const QV4::CompiledData::Binding*>(binding); + } else if (binding->evaluatesToString()) { value = binding->valueAsString(qmlUnit); } else if (binding->type == QV4::CompiledData::Binding::Type_Number) { value = binding->valueAsNumber(); @@ -2693,6 +2795,7 @@ void QQmlListModelParser::applyBindings(QObject *obj, QV4::CompiledData::Compila QQmlListModel *rv = static_cast<QQmlListModel *>(obj); rv->m_engine = qmlEngine(rv)->handle(); + rv->m_compilationUnit = compilationUnit; const QV4::CompiledData::Unit *qmlUnit = compilationUnit->data; diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h index 0c0859dc80..56a1a95a02 100644 --- a/src/qml/types/qqmllistmodel_p.h +++ b/src/qml/types/qqmllistmodel_p.h @@ -64,6 +64,8 @@ #include <private/qv4engine_p.h> #include <private/qpodvector_p.h> +QT_REQUIRE_CONFIG(qml_list_model); + QT_BEGIN_NAMESPACE @@ -122,6 +124,7 @@ private: friend class ListElement; friend class DynamicRoleModelNode; friend class DynamicRoleModelNodeMetaObject; + friend struct StringOrTranslation; // Constructs a flat list model for a worker agent QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent); @@ -133,6 +136,7 @@ private: QQmlListModelWorkerAgent *m_agent; mutable QV4::ExecutionEngine *m_engine; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit; bool m_mainThread; bool m_primary; diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h index ad5e94c909..e4a850e8a5 100644 --- a/src/qml/types/qqmllistmodel_p_p.h +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -57,6 +57,8 @@ #include <private/qv4qobjectwrapper_p.h> #include <qqml.h> +QT_REQUIRE_CONFIG(qml_list_model); + QT_BEGIN_NAMESPACE @@ -142,16 +144,6 @@ protected: private: using QQmlOpenMetaObject::setValue; - void setValue(const QByteArray &name, const QVariant &val, bool force) - { - if (force) { - QVariant existingValue = value(name); - if (existingValue.isValid()) { - (*this)[name] = QVariant(); - } - } - setValue(name, val); - } void emitDirectNotifies(const int *changedRoles, int roleCount); @@ -181,12 +173,13 @@ struct ModelObject : public QObjectWrapper { struct ModelObject : public QObjectWrapper { - static bool put(Managed *m, String *name, const Value& value); - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); - V4_OBJECT2(ModelObject, QObjectWrapper) V4_NEEDS_DESTROY + +protected: + static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); }; } // namespace QV4 @@ -252,6 +245,22 @@ private: QStringHash<Role *> roleHash; }; +struct StringOrTranslation +{ + explicit StringOrTranslation(const QString &s); + explicit StringOrTranslation(const QV4::CompiledData::Binding *binding); + ~StringOrTranslation(); + bool isSet() const { return d.flag(); } + bool isTranslation() const { return d.isT2(); } + void setString(const QString &s); + void setTranslation(const QV4::CompiledData::Binding *binding); + QString toString(const QQmlListModel *owner) const; + QString asString() const; +private: + void clear(); + QBiPointer<QStringData, const QV4::CompiledData::Binding> d; +}; + /*! \internal */ @@ -287,6 +296,7 @@ private: int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt); int setFunctionProperty(const ListLayout::Role &role, const QJSValue &f); + int setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b); void setStringPropertyFast(const ListLayout::Role &role, const QString &s); void setDoublePropertyFast(const ListLayout::Role &role, double n); @@ -301,7 +311,7 @@ private: QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng); ListModel *getListProperty(const ListLayout::Role &role); - QString *getStringProperty(const ListLayout::Role &role); + StringOrTranslation *getStringProperty(const ListLayout::Role &role); QObject *getQObjectProperty(const ListLayout::Role &role); QPointer<QObject> *getGuardProperty(const ListLayout::Role &role); QVariantMap *getVariantMapProperty(const ListLayout::Role &role); diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h index 2120f25744..ae2d4b11e0 100644 --- a/src/qml/types/qqmllistmodelworkeragent_p.h +++ b/src/qml/types/qqmllistmodelworkeragent_p.h @@ -59,6 +59,8 @@ #include <private/qv8engine_p.h> +QT_REQUIRE_CONFIG(qml_list_model); + QT_BEGIN_NAMESPACE diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp index e217b63c6f..d9756704d1 100644 --- a/src/qml/types/qqmlmodelsmodule.cpp +++ b/src/qml/types/qqmlmodelsmodule.cpp @@ -39,7 +39,9 @@ #include "qqmlmodelsmodule_p.h" #include <QtCore/qitemselectionmodel.h> +#if QT_CONFIG(qml_list_model) #include <private/qqmllistmodel_p.h> +#endif #if QT_CONFIG(qml_delegate_model) #include <private/qqmldelegatemodel_p.h> #endif @@ -51,10 +53,13 @@ void QQmlModelsModule::defineModule() { const char uri[] = "QtQml.Models"; +#if QT_CONFIG(qml_list_model) qmlRegisterType<QQmlListElement>(uri, 2, 1, "ListElement"); qmlRegisterCustomType<QQmlListModel>(uri, 2, 1, "ListModel", new QQmlListModelParser); +#endif #if QT_CONFIG(qml_delegate_model) qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel"); + qmlRegisterType<QQmlDelegateModel,12>(uri, 2, 9, "DelegateModel"); qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup"); #endif qmlRegisterType<QQmlObjectModel>(uri, 2, 1, "ObjectModel"); diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h index 267828dcdd..4ac4f1c65b 100644 --- a/src/qml/types/qqmlobjectmodel_p.h +++ b/src/qml/types/qqmlobjectmodel_p.h @@ -60,6 +60,7 @@ QT_BEGIN_NAMESPACE class QObject; class QQmlChangeSet; +class QAbstractItemModel; class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject { @@ -83,6 +84,7 @@ public: virtual QQmlIncubator::Status incubationStatus(int index) = 0; virtual int indexOf(QObject *object, QObject *objectContext) const = 0; + virtual const QAbstractItemModel *abstractItemModel() const { return nullptr; } Q_SIGNALS: void countChanged(); diff --git a/src/qml/types/qqmltableinstancemodel.cpp b/src/qml/types/qqmltableinstancemodel.cpp new file mode 100644 index 0000000000..dcad84cfb8 --- /dev/null +++ b/src/qml/types/qqmltableinstancemodel.cpp @@ -0,0 +1,341 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltableinstancemodel_p.h" +#include "qqmltableinstancemodel_p.h" + +#include <QtCore/QTimer> + +#include <QtQml/private/qqmlincubator_p.h> +#include <QtQml/private/qqmlchangeset_p.h> +#include <QtQml/private/qqmlcomponent_p.h> + +QT_BEGIN_NAMESPACE + +const char* kModelItemTag = "_tableinstancemodel_modelItem"; + +bool QQmlTableInstanceModel::isDoneIncubating(QQmlDelegateModelItem *modelItem) +{ + if (!modelItem->incubationTask) + return true; + + const auto status = modelItem->incubationTask->status(); + return (status == QQmlIncubator::Ready) || (status == QQmlIncubator::Error); +} + +void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelItem) +{ + Q_ASSERT(modelItem); + + delete modelItem->object; + modelItem->object = nullptr; + + if (modelItem->contextData) { + modelItem->contextData->invalidate(); + Q_ASSERT(modelItem->contextData->refCount == 1); + modelItem->contextData = nullptr; + } + + modelItem->deleteLater(); +} + +QQmlTableInstanceModel::QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent) + : QQmlInstanceModel(*(new QObjectPrivate()), parent) + , m_qmlContext(qmlContext) + , m_metaType(new QQmlDelegateModelItemMetaType(m_qmlContext->engine()->handle(), nullptr, QStringList())) +{ +} + +QQmlTableInstanceModel::~QQmlTableInstanceModel() +{ + deleteAllFinishedIncubationTasks(); + + // If we have model items loaded at this point, it means that + // the view is still holding references to them. So basically + // the view needs to be deleted first (and thereby release all + // its items), before this destructor is run. + Q_ASSERT(m_modelItems.isEmpty()); +} + +QObject *QQmlTableInstanceModel::object(int index, QQmlIncubator::IncubationMode incubationMode) +{ + Q_ASSERT(m_delegate); + Q_ASSERT(index >= 0 && index < m_adaptorModel.count()); + Q_ASSERT(m_qmlContext && m_qmlContext->isValid()); + + // Check if we've already created an item for the given index + QQmlDelegateModelItem *modelItem = m_modelItems.value(index, nullptr); + + if (!modelItem) { + // Create a new model item + modelItem = m_adaptorModel.createItem(m_metaType, index); + if (!modelItem) { + qWarning() << Q_FUNC_INFO << "Adaptor model failed creating a model item"; + return nullptr; + } + m_modelItems.insert(index, modelItem); + } + + if (modelItem->object) { + // The model item has already been incubated. So + // just bump the ref-count and return it. + modelItem->referenceObject(); + return modelItem->object; + } + + // Guard the model item temporarily so that it's not deleted from + // incubatorStatusChanged(), in case the incubation is done synchronously. + modelItem->scriptRef++; + + if (modelItem->incubationTask) { + // We're already incubating the model item from a previous request. If the previous call requested + // the item async, but the current request needs it sync, we need to force-complete the incubation. + const bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested); + if (sync && modelItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) + modelItem->incubationTask->forceCompletion(); + } else { + modelItem->incubationTask = new QQmlTableInstanceModelIncubationTask(this, modelItem, incubationMode); + + QQmlContextData *ctxt = new QQmlContextData; + QQmlContext *creationContext = m_delegate->creationContext(); + ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data())); + ctxt->contextObject = modelItem; + modelItem->contextData = ctxt; + + QQmlComponentPrivate::get(m_delegate)->incubateObject( + modelItem->incubationTask, + m_delegate, + m_qmlContext->engine(), + ctxt, + QQmlContextData::get(m_qmlContext)); + } + + // Remove the temporary guard + modelItem->scriptRef--; + Q_ASSERT(modelItem->scriptRef == 0); + + if (!isDoneIncubating(modelItem)) + return nullptr; + + // When incubation is done, the task should be removed + Q_ASSERT(!modelItem->incubationTask); + + if (!modelItem->object) { + // The object was incubated synchronously (otherwise we would return above). But since + // we have no object, the incubation must have failed. And when we have no object, there + // should be no object references either. And there should also not be any internal script + // refs at this point. So we delete the model item. + Q_ASSERT(!modelItem->isObjectReferenced()); + Q_ASSERT(!modelItem->isReferenced()); + m_modelItems.remove(modelItem->index); + delete modelItem; + return nullptr; + } + + // Incubation was completed sync and successful + modelItem->referenceObject(); + return modelItem->object; +} + +QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object) +{ + Q_ASSERT(object); + auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag)); + Q_ASSERT(modelItem); + + if (!modelItem->releaseObject()) + return QQmlDelegateModel::Referenced; + + if (modelItem->isReferenced()) { + // We still have an internal reference to this object, which means that we are told to release an + // object while the createdItem signal for it is still on the stack. This can happen when objects + // are e.g delivered async, and the user flicks back and forth quicker than the loading can catch + // up with. The view might then find that the object is no longer visible and should be released. + // We detect this case in incubatorStatusChanged(), and delete it there instead. But from the callers + // points of view, it should consider it destroyed. + return QQmlDelegateModel::Destroyed; + } + + m_modelItems.remove(modelItem->index); + modelItem->destroyObject(); + emit destroyingItem(object); + + delete modelItem; + return QQmlInstanceModel::Destroyed; +} + +void QQmlTableInstanceModel::cancel(int index) +{ + auto modelItem = m_modelItems.value(index); + Q_ASSERT(modelItem); + + // Since the view expects the item to be incubating, there should be + // an incubation task. And since the incubation is not done, no-one + // should yet have received, and therfore hold a reference to, the object. + Q_ASSERT(modelItem->incubationTask); + Q_ASSERT(!modelItem->isObjectReferenced()); + + m_modelItems.remove(index); + + if (modelItem->object) + delete modelItem->object; + + // modelItem->incubationTask will be deleted from the modelItems destructor + delete modelItem; +} + +void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status) +{ + QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate; + Q_ASSERT(modelItem->incubationTask); + + modelItem->incubationTask = nullptr; + incubationTask->modelItemToIncubate = nullptr; + + if (status == QQmlIncubator::Ready) { + // Tag the incubated object with the model item for easy retrieval upon release etc. + modelItem->object->setProperty(kModelItemTag, QVariant::fromValue(modelItem)); + + // Emit that the item has been created. What normally happens next is that the view + // upon receiving the signal asks for the model item once more. And since the item is + // now in the map, it will be returned directly. + Q_ASSERT(modelItem->object); + modelItem->scriptRef++; + emit createdItem(modelItem->index, modelItem->object); + modelItem->scriptRef--; + } else if (status == QQmlIncubator::Error) { + qWarning() << "Error incubating delegate:" << incubationTask->errors(); + } + + if (!modelItem->isReferenced() && !modelItem->isObjectReferenced()) { + // We have no internal reference to the model item, and the view has no + // reference to the incubated object. So just delete the model item. + // Note that being here means that the object was incubated _async_ + // (otherwise modelItem->isReferenced() would be true). + m_modelItems.remove(modelItem->index); + + if (modelItem->object) { + modelItem->scriptRef++; + emit destroyingItem(modelItem->object); + modelItem->scriptRef--; + Q_ASSERT(!modelItem->isReferenced()); + } + + deleteModelItemLater(modelItem); + } + + deleteIncubationTaskLater(incubationTask); +} + +QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(int index) { + const auto modelItem = m_modelItems.value(index, nullptr); + if (!modelItem) + return QQmlIncubator::Null; + + if (modelItem->incubationTask) + return modelItem->incubationTask->status(); + + // Since we clear the incubation task when we're done + // incubating, it means that the status is Ready. + return QQmlIncubator::Ready; +} + +void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask) +{ + // We often need to post-delete incubation tasks, since we cannot + // delete them while we're in the middle of an incubation change callback. + Q_ASSERT(!m_finishedIncubationTasks.contains(incubationTask)); + m_finishedIncubationTasks.append(incubationTask); + if (m_finishedIncubationTasks.count() == 1) + QTimer::singleShot(1, this, &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks); +} + +void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks() +{ + qDeleteAll(m_finishedIncubationTasks); + m_finishedIncubationTasks.clear(); +} + +QVariant QQmlTableInstanceModel::model() const +{ + return m_adaptorModel.model(); +} + +void QQmlTableInstanceModel::setModel(const QVariant &model) +{ + m_adaptorModel.setModel(model, this, m_qmlContext->engine()); +} + +QQmlComponent *QQmlTableInstanceModel::delegate() const +{ + return m_delegate; +} + +void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate) +{ + m_delegate = delegate; +} + +const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel() const +{ + return m_adaptorModel.aim(); +} + +// -------------------------------------------------------- + +void QQmlTableInstanceModelIncubationTask::setInitialState(QObject *object) +{ + modelItemToIncubate->object = object; + emit tableInstanceModel->initItem(modelItemToIncubate->index, object); +} + +void QQmlTableInstanceModelIncubationTask::statusChanged(QQmlIncubator::Status status) +{ + if (!QQmlTableInstanceModel::isDoneIncubating(modelItemToIncubate)) + return; + + // We require the view to cancel any ongoing load + // requests before the tableInstanceModel is destructed. + Q_ASSERT(tableInstanceModel); + + tableInstanceModel->incubatorStatusChanged(this, status); +} + +QT_END_NAMESPACE + diff --git a/src/qml/types/qqmltableinstancemodel_p.h b/src/qml/types/qqmltableinstancemodel_p.h new file mode 100644 index 0000000000..fb222d8bc4 --- /dev/null +++ b/src/qml/types/qqmltableinstancemodel_p.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTABLEINSTANCEMODEL_P_H +#define QQMLTABLEINSTANCEMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQml/private/qqmldelegatemodel_p.h> +#include <QtQml/private/qqmldelegatemodel_p_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlTableInstanceModel; + +class QQmlTableInstanceModelIncubationTask : public QQDMIncubationTask +{ +public: + QQmlTableInstanceModelIncubationTask( + QQmlTableInstanceModel *tableInstanceModel + , QQmlDelegateModelItem* modelItemToIncubate + , IncubationMode mode) + : QQDMIncubationTask(nullptr, mode) + , modelItemToIncubate(modelItemToIncubate) + , tableInstanceModel(tableInstanceModel) { + clear(); + } + + void statusChanged(Status status) override; + void setInitialState(QObject *object) override; + + QQmlDelegateModelItem *modelItemToIncubate = nullptr; + QQmlTableInstanceModel *tableInstanceModel = nullptr; +}; + +class Q_QML_PRIVATE_EXPORT QQmlTableInstanceModel : public QQmlInstanceModel +{ +public: + QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent = nullptr); + ~QQmlTableInstanceModel() override; + + int count() const override { return m_adaptorModel.count(); } + int rows() const { return m_adaptorModel.rowCount(); } + int columns() const { return m_adaptorModel.columnCount(); } + + bool isValid() const override { return true; } + + QVariant model() const; + void setModel(const QVariant &model); + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent *); + + const QAbstractItemModel *abstractItemModel() const override; + + QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; + ReleaseFlags release(QObject *) override; + void cancel(int) override; + + QQmlIncubator::Status incubationStatus(int index) override; + + QString stringValue(int, const QString &) override { Q_UNREACHABLE(); return QString(); } + void setWatchedRoles(const QList<QByteArray> &) override { Q_UNREACHABLE(); } + int indexOf(QObject *, QObject *) const override { Q_UNREACHABLE(); return 0; } + +private: + QQmlComponent *m_delegate = nullptr; + QQmlAdaptorModel m_adaptorModel; + QPointer<QQmlContext> m_qmlContext; + QQmlDelegateModelItemMetaType *m_metaType; + + QHash<int, QQmlDelegateModelItem *> m_modelItems; + QList<QQmlIncubator *> m_finishedIncubationTasks; + + void incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *dmIncubationTask, QQmlIncubator::Status status); + void deleteIncubationTaskLater(QQmlIncubator *incubationTask); + void deleteAllFinishedIncubationTasks(); + + static bool isDoneIncubating(QQmlDelegateModelItem *modelItem); + static void deleteModelItemLater(QQmlDelegateModelItem *modelItem); + + friend class QQmlTableInstanceModelIncubationTask; +}; + +QT_END_NAMESPACE + +#endif // QQMLTABLEINSTANCEMODEL_P_H diff --git a/src/qml/types/qqmltimer_p.h b/src/qml/types/qqmltimer_p.h index d597869994..0160e97a2f 100644 --- a/src/qml/types/qqmltimer_p.h +++ b/src/qml/types/qqmltimer_p.h @@ -57,6 +57,8 @@ #include <private/qtqmlglobal_p.h> +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class QQmlTimerPrivate; diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index 98f819337b..f8879160b2 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -37,9 +37,12 @@ ** ****************************************************************************/ +#include "qtqmlglobal_p.h" #include "qquickworkerscript_p.h" +#if QT_CONFIG(qml_list_model) #include "qqmllistmodel_p.h" #include "qqmllistmodelworkeragent_p.h" +#endif #include <private/qqmlengine_p.h> #include <private/qqmlexpression_p.h> @@ -201,7 +204,7 @@ private: }; QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent) - : QV8Engine(nullptr, new QV4::ExecutionEngine), p(parent) + : QV8Engine(new QV4::ExecutionEngine), p(parent) #if QT_CONFIG(qml_network) , accessManager(nullptr) #endif @@ -241,14 +244,13 @@ void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() QV4::Scope scope(m_v4Engine); QV4::ExecutionContext *globalContext = scope.engine->rootContext(); - onmessage.set(scope.engine, QV4::Script(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro + onmessage.set(scope.engine, QV4::Script(globalContext, QV4::Compiler::ContextType::Global, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro Q_ASSERT(!scope.engine->hasException); - QV4::Script createsendscript(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro + QV4::Script createsendscript(globalContext, QV4::Compiler::ContextType::Global, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro QV4::ScopedFunctionObject createsendconstructor(scope, createsendscript.run()); Q_ASSERT(!scope.engine->hasException); QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage"))); - QV4::ScopedValue function(scope, QV4::FunctionObject::createBuiltinFunction(globalContext, name, - QQuickWorkerScriptEnginePrivate::method_sendMessage)); + QV4::ScopedValue function(scope, QV4::FunctionObject::createBuiltinFunction(m_v4Engine, name, method_sendMessage, 1)); QV4::JSCallData jsCallData(scope, 1); jsCallData->args[0] = function; *jsCallData->thisObject = m_v4Engine->global(); diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index 8bcbd6e544..6a74d70a0e 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -1,28 +1,36 @@ SOURCES += \ $$PWD/qqmlbind.cpp \ $$PWD/qqmlconnections.cpp \ - $$PWD/qqmllistmodel.cpp \ - $$PWD/qqmllistmodelworkeragent.cpp \ $$PWD/qqmlmodelsmodule.cpp \ $$PWD/qqmlmodelindexvaluetype.cpp \ $$PWD/qqmlobjectmodel.cpp \ $$PWD/qquickpackage.cpp \ $$PWD/qquickworkerscript.cpp \ - $$PWD/qqmlinstantiator.cpp + $$PWD/qqmlinstantiator.cpp \ + $$PWD/qqmltableinstancemodel.cpp HEADERS += \ $$PWD/qqmlbind_p.h \ $$PWD/qqmlconnections_p.h \ - $$PWD/qqmllistmodel_p.h \ - $$PWD/qqmllistmodel_p_p.h \ - $$PWD/qqmllistmodelworkeragent_p.h \ $$PWD/qqmlmodelsmodule_p.h \ $$PWD/qqmlmodelindexvaluetype_p.h \ $$PWD/qqmlobjectmodel_p.h \ $$PWD/qquickpackage_p.h \ $$PWD/qquickworkerscript_p.h \ $$PWD/qqmlinstantiator_p.h \ - $$PWD/qqmlinstantiator_p_p.h + $$PWD/qqmlinstantiator_p_p.h \ + $$PWD/qqmltableinstancemodel_p.h + +qtConfig(qml-list-model) { + SOURCES += \ + $$PWD/qqmllistmodel.cpp \ + $$PWD/qqmllistmodelworkeragent.cpp + + HEADERS += \ + $$PWD/qqmllistmodel_p.h \ + $$PWD/qqmllistmodel_p_p.h \ + $$PWD/qqmllistmodelworkeragent_p.h +} qtConfig(qml-delegate-model) { SOURCES += \ @@ -33,7 +41,7 @@ qtConfig(qml-delegate-model) { $$PWD/qqmldelegatemodel_p_p.h } -qtConfig(animation) { +qtConfig(qml-animation) { SOURCES += \ $$PWD/qqmltimer.cpp diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index b4bebb9d5d..24d9c14611 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -96,7 +96,7 @@ public: QQmlDMCachedModelData( QQmlDelegateModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, - int index); + int index, int row, int column); int metaCall(QMetaObject::Call call, int id, void **arguments); @@ -228,8 +228,8 @@ public: QV4::ScopedString name(scope, v4->newString(QString::fromUtf8(propertyName))); QV4::ExecutionContext *global = v4->rootContext(); - QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocObject<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::get_property)); - QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocObject<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::set_property)); + QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::get_property)); + QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::set_property)); p->setGetter(g); p->setSetter(s); proto->insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable); @@ -262,9 +262,8 @@ public: bool hasModelData; }; -QQmlDMCachedModelData::QQmlDMCachedModelData( - QQmlDelegateModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, int index) - : QQmlDelegateModelItem(metaType, index) +QQmlDMCachedModelData::QQmlDMCachedModelData(QQmlDelegateModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, int index, int row, int column) + : QQmlDelegateModelItem(metaType, index, row, column) , type(dataType) { if (index == -1) @@ -326,13 +325,12 @@ void QQmlDMCachedModelData::setValue(const QString &role, const QVariant &value) } } -bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &, int idx) +bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &adaptorModel, int idx) { if (index == -1) { Q_ASSERT(idx >= 0); - index = idx; cachedData.clear(); - emit modelIndexChanged(); + setModelIndex(idx, adaptorModel.rowAt(idx), adaptorModel.columnAt(idx)); const QMetaObject *meta = metaObject(); const int propertyCount = type->propertyRoles.count(); for (int i = 0; i < propertyCount; ++i) @@ -400,12 +398,13 @@ class QQmlDMAbstractItemModelData : public QQmlDMCachedModelData { Q_OBJECT Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT) + public: QQmlDMAbstractItemModelData( QQmlDelegateModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, - int index) - : QQmlDMCachedModelData(metaType, dataType, index) + int index, int row, int column) + : QQmlDMCachedModelData(metaType, dataType, index, row, column) { } @@ -413,7 +412,7 @@ public: { if (index >= 0 && *type->model) { const QAbstractItemModel * const model = type->model->aim(); - return model->hasChildren(model->index(index, 0, type->model->rootIndex)); + return model->hasChildren(model->index(row, column, type->model->rootIndex)); } else { return false; } @@ -421,13 +420,13 @@ public: QVariant value(int role) const override { - return type->model->aim()->index(index, 0, type->model->rootIndex).data(role); + return type->model->aim()->index(row, column, type->model->rootIndex).data(role); } void setValue(int role, const QVariant &value) override { type->model->aim()->setData( - type->model->aim()->index(index, 0, type->model->rootIndex), value, role); + type->model->aim()->index(row, column, type->model->rootIndex), value, role); } QV4::ReturnedValue get() override @@ -438,8 +437,8 @@ public: } QV4::Scope scope(v4); QV4::ScopedObject proto(scope, type->prototype.value()); - QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocObject<QQmlDelegateModelItemObject>(this)); - o->setPrototype(proto); + QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocate<QQmlDelegateModelItemObject>(this)); + o->setPrototypeOf(proto); ++scriptRef; return o.asReturnedValue(); } @@ -453,31 +452,18 @@ public: { } - int count(const QQmlAdaptorModel &model) const override + int rowCount(const QQmlAdaptorModel &model) const override { return model.aim()->rowCount(model.rootIndex); } - void cleanup(QQmlAdaptorModel &model, QQmlDelegateModel *vdm) const override - { - QAbstractItemModel * const aim = model.aim(); - if (aim && vdm) { - QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)), - vdm, SLOT(_q_rowsInserted(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - vdm, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)), - vdm, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - vdm, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); - QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - vdm, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - QObject::disconnect(aim, SIGNAL(modelReset()), - vdm, SLOT(_q_modelReset())); - QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - vdm, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); - } + int columnCount(const QQmlAdaptorModel &model) const override + { + return model.aim()->columnCount(model.rootIndex); + } + void cleanup(QQmlAdaptorModel &) const override + { const_cast<VDMAbstractItemModelDataType *>(this)->release(); } @@ -485,9 +471,9 @@ public: { QHash<QByteArray, int>::const_iterator it = roleNames.find(role.toUtf8()); if (it != roleNames.end()) { - return model.aim()->index(index, 0, model.rootIndex).data(*it); + return model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex).data(*it); } else if (role == QLatin1String("hasModelChildren")) { - return QVariant(model.aim()->hasChildren(model.aim()->index(index, 0, model.rootIndex))); + return QVariant(model.aim()->hasChildren(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex))); } else { return QVariant(); } @@ -503,7 +489,7 @@ public: QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override { return model - ? QVariant::fromValue(model.aim()->index(index, 0, model.rootIndex)) + ? QVariant::fromValue(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex)) : QVariant(); } @@ -521,12 +507,12 @@ public: QQmlDelegateModelItem *createItem( QQmlAdaptorModel &model, QQmlDelegateModelItemMetaType *metaType, - int index) const override + int index, int row, int column) const override { VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this); if (!metaObject) dataType->initializeMetaType(model); - return new QQmlDMAbstractItemModelData(metaType, dataType, index); + return new QQmlDMAbstractItemModelData(metaType, dataType, index, row, column); } void initializeMetaType(QQmlAdaptorModel &model) @@ -567,8 +553,8 @@ class QQmlDMListAccessorData : public QQmlDelegateModelItem Q_OBJECT Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged) public: - QQmlDMListAccessorData(QQmlDelegateModelItemMetaType *metaType, int index, const QVariant &value) - : QQmlDelegateModelItem(metaType, index) + QQmlDMListAccessorData(QQmlDelegateModelItemMetaType *metaType, int index, int row, int column, const QVariant &value) + : QQmlDelegateModelItem(metaType, index, row, column) , cachedData(value) { } @@ -613,9 +599,9 @@ public: { QQmlAdaptorModelEngineData *data = engineData(v4); QV4::Scope scope(v4); - QV4::ScopedObject o(scope, v4->memoryManager->allocObject<QQmlDelegateModelItemObject>(this)); + QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(this)); QV4::ScopedObject p(scope, data->listItemProto.value()); - o->setPrototype(p); + o->setPrototypeOf(p); ++scriptRef; return o.asReturnedValue(); } @@ -653,11 +639,16 @@ class VDMListDelegateDataType : public QQmlAdaptorModel::Accessors public: inline VDMListDelegateDataType() {} - int count(const QQmlAdaptorModel &model) const override + int rowCount(const QQmlAdaptorModel &model) const override { return model.list.count(); } + int columnCount(const QQmlAdaptorModel &) const override + { + return 1; + } + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override { return role == QLatin1String("modelData") @@ -668,11 +659,11 @@ public: QQmlDelegateModelItem *createItem( QQmlAdaptorModel &model, QQmlDelegateModelItemMetaType *metaType, - int index) const override + int index, int row, int column) const override { return new QQmlDMListAccessorData( metaType, - index, + index, row, column, index >= 0 && index < model.list.count() ? model.list.at(index) : QVariant()); } }; @@ -691,7 +682,7 @@ public: QQmlDMObjectData( QQmlDelegateModelItemMetaType *metaType, VDMObjectDelegateDataType *dataType, - int index, + int index, int row, int column, QObject *object); QObject *modelData() const { return object; } @@ -737,11 +728,16 @@ public: free(metaObject); } - int count(const QQmlAdaptorModel &model) const override + int rowCount(const QQmlAdaptorModel &model) const override { return model.list.count(); } + int columnCount(const QQmlAdaptorModel &) const override + { + return 1; + } + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override { if (QObject *object = model.list.at(index).value<QObject *>()) @@ -752,13 +748,13 @@ public: QQmlDelegateModelItem *createItem( QQmlAdaptorModel &model, QQmlDelegateModelItemMetaType *metaType, - int index) const override + int index, int row, int column) const override { VDMObjectDelegateDataType *dataType = const_cast<VDMObjectDelegateDataType *>(this); if (!metaObject) dataType->initializeMetaType(model); return index >= 0 && index < model.list.count() - ? new QQmlDMObjectData(metaType, dataType, index, qvariant_cast<QObject *>(model.list.at(index))) + ? new QQmlDMObjectData(metaType, dataType, index, row, column, qvariant_cast<QObject *>(model.list.at(index))) : nullptr; } @@ -769,7 +765,7 @@ public: metaObject = builder.toMetaObject(); } - void cleanup(QQmlAdaptorModel &, QQmlDelegateModel *) const override + void cleanup(QQmlAdaptorModel &) const override { const_cast<VDMObjectDelegateDataType *>(this)->release(); } @@ -872,12 +868,11 @@ public: VDMObjectDelegateDataType *m_type; }; -QQmlDMObjectData::QQmlDMObjectData( - QQmlDelegateModelItemMetaType *metaType, +QQmlDMObjectData::QQmlDMObjectData(QQmlDelegateModelItemMetaType *metaType, VDMObjectDelegateDataType *dataType, - int index, + int index, int row, int column, QObject *object) - : QQmlDelegateModelItem(metaType, index) + : QQmlDelegateModelItem(metaType, index, row, column) , object(object) { new QQmlDMObjectDataMetaObject(this, dataType); @@ -904,50 +899,34 @@ QQmlAdaptorModel::~QQmlAdaptorModel() accessors->cleanup(*this); } -void QQmlAdaptorModel::setModel(const QVariant &variant, QQmlDelegateModel *vdm, QQmlEngine *engine) +void QQmlAdaptorModel::setModel(const QVariant &variant, QObject *parent, QQmlEngine *engine) { - accessors->cleanup(*this, vdm); + accessors->cleanup(*this); list.setList(variant, engine); if (QObject *object = qvariant_cast<QObject *>(list.list())) { - setObject(object); - if (QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(object)) { + setObject(object, parent); + if (qobject_cast<QAbstractItemModel *>(object)) accessors = new VDMAbstractItemModelDataType(this); - - qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), - vdm, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int))); - qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), - vdm, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - vdm, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - qmlobject_connect(model, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - vdm, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); - qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - vdm, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - qmlobject_connect(model, QAbstractItemModel, SIGNAL(modelReset()), - vdm, QQmlDelegateModel, SLOT(_q_modelReset())); - qmlobject_connect(model, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - vdm, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); - } else { + else accessors = new VDMObjectDelegateDataType; - } } else if (list.type() == QQmlListAccessor::ListProperty) { - setObject(static_cast<const QQmlListReference *>(variant.constData())->object()); + setObject(static_cast<const QQmlListReference *>(variant.constData())->object(), parent); accessors = new VDMObjectDelegateDataType; } else if (list.type() != QQmlListAccessor::Invalid && list.type() != QQmlListAccessor::Instance) { // Null QObject - setObject(nullptr); + setObject(nullptr, parent); accessors = &qt_vdm_list_accessors; } else { - setObject(nullptr); + setObject(nullptr, parent); accessors = &qt_vdm_null_accessors; } } -void QQmlAdaptorModel::invalidateModel(QQmlDelegateModel *vdm) +void QQmlAdaptorModel::invalidateModel() { - accessors->cleanup(*this, vdm); + accessors->cleanup(*this); accessors = &qt_vdm_null_accessors; // Don't clear the model object as we still need the guard to clear the list variant if the // object is destroyed. @@ -958,6 +937,38 @@ bool QQmlAdaptorModel::isValid() const return accessors != &qt_vdm_null_accessors; } +int QQmlAdaptorModel::count() const +{ + return rowCount() * columnCount(); +} + +int QQmlAdaptorModel::rowCount() const +{ + return qMax(0, accessors->rowCount(*this)); +} + +int QQmlAdaptorModel::columnCount() const +{ + return qMax(isValid() ? 1 : 0, accessors->columnCount(*this)); +} + +int QQmlAdaptorModel::rowAt(int index) const +{ + int count = rowCount(); + return count <= 0 ? -1 : index % count; +} + +int QQmlAdaptorModel::columnAt(int index) const +{ + int count = rowCount(); + return count <= 0 ? -1 : index / count; +} + +int QQmlAdaptorModel::indexAt(int row, int column) const +{ + return row + (column * rowCount()); +} + void QQmlAdaptorModel::objectDestroyed(QObject *) { setModel(QVariant(), nullptr, nullptr); diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h index b152a886a5..b834704163 100644 --- a/src/qml/util/qqmladaptormodel_p.h +++ b/src/qml/util/qqmladaptormodel_p.h @@ -56,6 +56,7 @@ #include "private/qqmllistaccessor_p.h" #include <private/qqmlguard_p.h> +#include <private/qqmlnullablevalue_p.h> QT_REQUIRE_CONFIG(qml_delegate_model); @@ -67,7 +68,7 @@ class QQmlDelegateModel; class QQmlDelegateModelItem; class QQmlDelegateModelItemMetaType; -class QQmlAdaptorModel : public QQmlGuard<QObject> +class Q_QML_PRIVATE_EXPORT QQmlAdaptorModel : public QQmlStrongJSQObjectReference<QObject> { public: class Accessors @@ -75,8 +76,9 @@ public: public: inline Accessors() {} virtual ~Accessors(); - virtual int count(const QQmlAdaptorModel &) const { return 0; } - virtual void cleanup(QQmlAdaptorModel &, QQmlDelegateModel * = nullptr) const {} + virtual int rowCount(const QQmlAdaptorModel &) const { return 0; } + virtual int columnCount(const QQmlAdaptorModel &) const { return 0; } + virtual void cleanup(QQmlAdaptorModel &) const {} virtual QVariant value(const QQmlAdaptorModel &, int, const QString &) const { return QVariant(); } @@ -84,7 +86,7 @@ public: virtual QQmlDelegateModelItem *createItem( QQmlAdaptorModel &, QQmlDelegateModelItemMetaType *, - int) const { return nullptr; } + int, int, int) const { return nullptr; } virtual bool notify( const QQmlAdaptorModel &, @@ -112,19 +114,25 @@ public: ~QQmlAdaptorModel(); inline QVariant model() const { return list.list(); } - void setModel(const QVariant &variant, QQmlDelegateModel *vdm, QQmlEngine *engine); - void invalidateModel(QQmlDelegateModel *vdm); + void setModel(const QVariant &variant, QObject *parent, QQmlEngine *engine); + void invalidateModel(); bool isValid() const; - + int count() const; + int rowCount() const; + int columnCount() const; + int rowAt(int index) const; + int columnAt(int index) const; + int indexAt(int row, int column) const; + + inline bool adaptsAim() const { return qobject_cast<QAbstractItemModel *>(object()); } inline QAbstractItemModel *aim() { return static_cast<QAbstractItemModel *>(object()); } inline const QAbstractItemModel *aim() const { return static_cast<const QAbstractItemModel *>(object()); } - inline int count() const { return qMax(0, accessors->count(*this)); } inline QVariant value(int index, const QString &role) const { return accessors->value(*this, index, role); } inline QQmlDelegateModelItem *createItem(QQmlDelegateModelItemMetaType *metaType, int index) { - return accessors->createItem(*this, metaType, index); } + return accessors->createItem(*this, metaType, index, rowAt(index), columnAt(index)); } inline bool hasProxyObject() const { return list.type() == QQmlListAccessor::Instance || list.type() == QQmlListAccessor::ListProperty; } diff --git a/src/qml/util/qqmlpropertymap.cpp b/src/qml/util/qqmlpropertymap.cpp index 578c05086f..3f78ca6b69 100644 --- a/src/qml/util/qqmlpropertymap.cpp +++ b/src/qml/util/qqmlpropertymap.cpp @@ -122,7 +122,7 @@ QVariant QQmlPropertyMapMetaObject::propertyWriteValue(int index, const QVariant void QQmlPropertyMapMetaObject::propertyWritten(int index) { - priv->emitChanged(priv->propertyName(index), operator[](index)); + priv->emitChanged(priv->propertyName(index), value(index)); } void QQmlPropertyMapMetaObject::propertyCreated(int, QMetaPropertyBuilder &b) @@ -311,7 +311,7 @@ QVariant &QQmlPropertyMap::operator[](const QString &key) if (!d->keys.contains(key)) insert(key, QVariant());//force creation -- needed below - return (*(d->mo))[utf8key]; + return d->mo->valueRef(utf8key); } /*! |