diff options
Diffstat (limited to 'src/qml/compiler')
27 files changed, 3109 insertions, 1445 deletions
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..8ebd2da1c9 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) { 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..76245ced1d 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,10 +464,8 @@ 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) @@ -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..1d1e98ea58 100644 --- a/src/qml/compiler/qv4bytecodegenerator.cpp +++ b/src/qml/compiler/qv4bytecodegenerator.cpp @@ -185,6 +185,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 diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index e69f2cd310..78ce3624ea 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; @@ -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..8d1ac0bfd9 --- /dev/null +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -0,0 +1,520 @@ +/**************************************************************************** +** +** 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(GetLookupA) + COLLECTOR_END_INSTR(GetLookupA) + + COLLECTOR_BEGIN_INSTR(StoreProperty) + COLLECTOR_END_INSTR(StoreProperty) + + COLLECTOR_BEGIN_INSTR(SetLookup) + COLLECTOR_END_INSTR(SetLookup) + + 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(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(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..fc2fb86666 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,12 +135,17 @@ 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) @@ -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()); } void Codegen::variableDeclarationList(VariableDeclarationList *ast) @@ -424,6 +469,190 @@ void Codegen::variableDeclarationList(VariableDeclarationList *ast) } } +Codegen::Reference Codegen::targetForPatternElement(AST::PatternElement *p) +{ + if (!p->bindingIdentifier.isNull()) + return referenceForName(p->bindingIdentifier, 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) +{ + 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 (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); + } else if (PatternPropertyList *p = e->propertyList()) { + destructurePropertyList(varToStore, p); + } 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; + } +} + +void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternPropertyList *bindingList) +{ + RegisterScope scope(this); + + for (PatternPropertyList *it = bindingList; it; it = it->next) { + PatternProperty *p = it->property; + RegisterScope scope(this); + AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name); + Reference property; + if (cname) { + Reference computedName = expression(cname->expression); + if (hasError) + return; + computedName = computedName.storeOnStack(); + property = Reference::fromSubscript(object, computedName).asLValue(); + } else { + QString propertyName = p->name->asString(); + property = Reference::fromMember(object, propertyName); + } + initializeAndDestructureBindingElement(p, property); + if (hasError) + return; + } +} + +void Codegen::destructureElementList(const Codegen::Reference &array, PatternElementList *bindingList) +{ + 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)); + 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); + 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 +690,6 @@ bool Codegen::visit(DefaultClause *) return false; } -bool Codegen::visit(ElementList *) -{ - Q_UNREACHABLE(); - return false; -} - bool Codegen::visit(Elision *) { Q_UNREACHABLE(); @@ -485,37 +708,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 +804,15 @@ 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(); + throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Support for 'class' is unimplemented.")); return false; } @@ -609,50 +826,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; @@ -692,6 +1019,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 +1092,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 +1122,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: @@ -850,6 +1192,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: @@ -902,6 +1245,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(); @@ -1259,6 +1610,7 @@ bool Codegen::visit(CallExpression *ast) RegisterScope scope(this); Reference base = expression(ast->base); + if (hasError) return false; switch (base.type) { @@ -1275,10 +1627,42 @@ bool Codegen::visit(CallExpression *ast) 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 +1679,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 +1707,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 +1730,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,6 +1830,7 @@ bool Codegen::visit(DeleteExpression *ast) if (hasError) return false; + RegisterScope scope(this); Reference expr = expression(ast->expression); if (hasError) return false; @@ -1444,9 +1862,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 +1877,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,11 +1901,31 @@ bool Codegen::visit(FalseLiteral *) return false; } +bool Codegen::visit(SuperLiteral *ast) +{ + if (hasError) + return false; + + throwSyntaxError(ast->superToken, QLatin1String("Support for 'super' keyword not implemented")); + 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; + } + throwSyntaxError(ast->identifierToken, QLatin1String("Support for 'new.target' unimplemented.")); + } + } + Reference base = expression(ast->base); if (hasError) return false; @@ -1490,97 +1933,126 @@ bool Codegen::visit(FieldMemberExpression *ast) 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; 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) @@ -1656,11 +2128,19 @@ bool Codegen::visit(NewMemberExpression *ast) if (hasError) return false; - Instruction::Construct create; - create.func = base.stackSlot(); - create.argc = calldata.argc; - create.argv = calldata.argv; - bytecodeGenerator->addInstruction(create); + if (calldata.hasSpread) { + Instruction::ConstructWithSpread create; + create.func = base.stackSlot(); + create.argc = calldata.argc; + create.argv = calldata.argv; + bytecodeGenerator->addInstruction(create); + } else { + Instruction::Construct create; + create.func = base.stackSlot(); + create.argc = calldata.argc; + create.argv = calldata.argv; + bytecodeGenerator->addInstruction(create); + } _expr.setResult(Reference::fromAccumulator(this)); return false; } @@ -1696,133 +2176,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(); - } - } - - 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); - } - } + QStringList members; - 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 +2385,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 +2518,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 +2560,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 +2593,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 +2611,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, 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 +2716,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 +2734,7 @@ bool Codegen::visit(Block *ast) RegisterScope scope(this); + ControlFlowBlock controlFlow(this, ast); statementList(ast->statements); return false; } @@ -2256,13 +2744,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 +2758,7 @@ bool Codegen::visit(BreakStatement *ast) return false; } - _context->controlFlow->jumpToHandler(h); + bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel); return false; } @@ -2282,13 +2770,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 +2784,7 @@ bool Codegen::visit(ContinueStatement *ast) return false; } - _context->controlFlow->jumpToHandler(h); + bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel); return false; } @@ -2370,45 +2858,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); - - BytecodeGenerator::Label body = bytecodeGenerator->label(); + { + 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); + if (hasError) + goto error; + } else { + Q_UNREACHABLE(); + } - nextIterObj.loadInAccumulator(); - lhs.storeConsumeAccumulator(); + statement(ast->statement); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); - statement(ast->statement); - setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); + } - in.link(); + 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); + } - iterObj.loadInAccumulator(); - Instruction::ForeachNextPropertyName nextPropInstr; - bytecodeGenerator->addInstruction(nextPropInstr); - nextIterObj.storeConsumeAccumulator(); + end.link(); - Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator(); - bytecodeGenerator->jumpStrictNotEqual(nextIterObj.stackSlot(), body); + 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); + } - end.link(); + done.link(); return false; } @@ -2420,7 +2949,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 +2970,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 +2996,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 +3020,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 +3035,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 +3047,17 @@ bool Codegen::visit(LabelledStatement *ast) return false; } -bool Codegen::visit(LocalForEachStatement *ast) -{ - 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) +void Codegen::emitReturn(const Reference &expr) { - 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 +3065,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 +3078,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 +3088,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 +3138,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 +3159,7 @@ bool Codegen::visit(SwitchStatement *ast) statementList(clause->statements); } + insideSwitch = false; switchEnd.link(); @@ -2713,25 +3179,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 +3217,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 +3271,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 +3535,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; @@ -3209,6 +3680,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); } @@ -3313,7 +3806,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); @@ -3429,7 +3922,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,7 +3933,7 @@ QT_WARNING_POP } return; case Member: - if (codegen->useFastLookups) { + if (!disable_lookups && codegen->useFastLookups) { if (propertyBase.isAccumulator()) { Instruction::GetLookupA load; load.index = codegen->registerGetterLookup(propertyNameIndex); @@ -3452,34 +3945,17 @@ QT_WARNING_POP 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); - } + propertyBase.loadInAccumulator(); + Instruction::LoadProperty load; + 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; - codegen->bytecodeGenerator->addInstruction(load); - } else { - Instruction::LoadElement load; - load.base = elementBase; - load.index = elementSubscript.storeOnStack().stackSlot(); - codegen->bytecodeGenerator->addInstruction(load); - } + 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..83b8731179 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -101,7 +101,7 @@ public: const QString &sourceCode, AST::Program *ast, Module *module, - CompilationMode mode = GlobalCode); + ContextType contextType = ContextType::Global); public: class VolatileMemoryLocationScanner; @@ -172,6 +172,7 @@ public: } Q_REQUIRED_RESULT RValue storeOnStack() const; + void loadInAccumulator() const; }; struct Reference { enum Type { @@ -197,6 +198,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 { @@ -322,6 +325,8 @@ public: Q_REQUIRED_RESULT Reference storeRetainAccumulator() const; Reference storeConsumeAccumulator() const; + Q_REQUIRED_RESULT Reference baseObject() const; + bool storeWipesAccumulator() const; void loadInAccumulator() const; @@ -464,7 +469,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 +490,7 @@ protected: void addCJump(); +public: int registerString(const QString &name) { return jsUnitGenerator->registerString(name); } @@ -493,33 +502,35 @@ 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); - - void loadClosure(int index); + Reference targetForPatternElement(AST::PatternElement *p); + void initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &baseRef = Reference()); + void destructurePropertyList(const Reference &object, AST::PatternPropertyList *bindingList); + void destructureElementList(const Reference &array, AST::PatternElementList *bindingList); + void destructurePattern(AST::Pattern *p, const Reference &rhs); // 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 +538,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 +553,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 +580,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 +596,8 @@ 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; // statements bool visit(AST::Block *ast) override; @@ -601,8 +611,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 +639,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 +676,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; 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..c809d497fd 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->identifier(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,13 +237,31 @@ 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) @@ -344,7 +355,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 +382,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->identifier(runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); + runtimeBlocks[i] = ic->d(); } } @@ -391,7 +421,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 +506,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 +533,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 +659,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 +670,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..08170e1c3b 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,7 @@ 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"); // Qml data structures @@ -701,14 +740,15 @@ 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 blockTableSize; + quint32_le offsetToBlockTable; quint32_le lookupTableSize; quint32_le offsetToLookupTable; quint32_le regexpTableSize; @@ -753,11 +793,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 +810,7 @@ struct Unit } const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); } + 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 +818,12 @@ struct Unit return reinterpret_cast<const Function*>(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 +842,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) == 200, "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 +879,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 +926,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 +962,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 +988,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; @@ -970,7 +1015,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 +1055,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 @@ -1046,8 +1093,8 @@ 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(); diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index c9e535c93f..d5a02a3ac8 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,39 @@ 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, blockAndFunctionOffsets, (module->functions.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, blockAndFunctionOffsets, &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, blockAndFunctionOffsets, unit->functionTableSize * sizeof(quint32_le)); + memcpy(dataPtr + unit->offsetToBlockTable, blockAndFunctionOffsets + unit->functionTableSize, 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 + blockAndFunctionOffsets[i], function); + } + + for (int i = 0; i < module->blocks.size(); ++i) { + Context *block = module->blocks.at(i); + + writeBlock(dataPtr + blockAndFunctionOffsets[i + module->functions.size()], block); } CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable); @@ -300,17 +300,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 +323,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 +389,32 @@ 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) +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 +433,10 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.offsetToFunctionTable = nextOffset; nextOffset += unit.functionTableSize * 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 +463,21 @@ 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->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..c40d49213a 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -116,8 +116,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 +124,8 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { }; QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable); - // Returns bytes written void writeFunction(char *f, Context *irFunction) const; + 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..bd9d19f491 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,249 @@ 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; + 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; + return result; + } else { + result.index = argIdx + sizeof(CallData)/sizeof(Value) - 1; + result.scope = 0; + result.type = ResolvedName::Stack; + 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..070be7573a 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -66,14 +66,15 @@ 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; @@ -86,10 +87,11 @@ 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; Context *rootContext; QString fileName; QString finalUrl; @@ -104,8 +106,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 +121,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 +140,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 +165,7 @@ struct Context { UsesArgumentsObject usesArgumentsObject = ArgumentsObjectUnknown; - CompilationMode compilationMode; + ContextType contextType; template <typename T> class SmallSet: public QVarLengthArray<T, 8> @@ -204,24 +214,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 +253,36 @@ 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; + 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..7506b238f8 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,15 +221,25 @@ bool ScanFunctions::visit(ExpressionStatement *ast) bool ScanFunctions::visit(FunctionExpression *ast) { - enterFunction(ast, /*enterName*/ false); + return enterFunction(ast, /*enterName*/ false); +} + +bool ScanFunctions::visit(TemplateLiteral *ast) +{ + while (ast) { + if (ast->expression) + Node::accept(ast->expression, this); + ast = ast->next; + } return true; + } -void ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName) +bool ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName) { 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); + return enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName); } void ScanFunctions::endVisit(FunctionExpression *) @@ -269,40 +247,34 @@ void ScanFunctions::endVisit(FunctionExpression *) leaveEnvironment(); } -bool ScanFunctions::visit(ObjectLiteral *ast) +bool ScanFunctions::visit(ObjectPattern *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; - } - _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc); - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); Node::accept(ast->properties, this); return false; } -bool ScanFunctions::visit(PropertyGetterSetter *ast) +bool ScanFunctions::visit(PatternProperty *ast) { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); - enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/nullptr); + 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; } -void ScanFunctions::endVisit(PropertyGetterSetter *) +void ScanFunctions::endVisit(PatternProperty *) { - leaveEnvironment(); + // ### +// if (ast->type == PatternProperty::Getter || ast->type == PatternProperty::Setter) +// leaveEnvironment(); } bool ScanFunctions::visit(FunctionDeclaration *ast) { - enterFunction(ast, /*enterName*/ true); - return true; + return enterFunction(ast, /*enterName*/ true); } void ScanFunctions::endVisit(FunctionDeclaration *) @@ -310,24 +282,6 @@ void ScanFunctions::endVisit(FunctionDeclaration *) leaveEnvironment(); } -bool ScanFunctions::visit(TryStatement *) -{ - // ### should limit to catch(), as try{} finally{} should be ok without - _context->hasTry = true; - return true; -} - -bool ScanFunctions::visit(WithStatement *ast) -{ - if (_context->isStrict) { - _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode")); - return false; - } - - _context->hasWith = true; - return true; -} - bool ScanFunctions::visit(DoWhileStatement *ast) { { TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict); @@ -338,7 +292,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 +304,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 +320,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; + 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 +475,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 +590,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; + 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().canEscape << "isLexicallyScoped:" << it.value().isLexicallyScoped(); } } } diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h index 87b7210879..3120ce917c 100644 --- a/src/qml/compiler/qv4compilerscanfunctions_p.h +++ b/src/qml/compiler/qv4compilerscanfunctions_p.h @@ -81,11 +81,11 @@ class ScanFunctions: protected QQmlJS::AST::Visitor { typedef QV4::TemporaryAssignment<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 +95,54 @@ 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::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 +152,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..df5dd5610c 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -286,10 +286,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,12 +294,8 @@ 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 << ")"; @@ -341,6 +333,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 +376,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 +415,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,10 +477,9 @@ 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(CreateMappedArgumentsObject) @@ -454,12 +488,15 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st 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(ToObject) + MOTH_END_INSTR(ToObject) MOTH_BEGIN_INSTR(Jump) d << ABSOLUTE_OFFSET(); @@ -473,6 +510,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 +564,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 +634,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 7dd639c94c..85b30507a1 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 @@ -84,19 +86,19 @@ 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_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name) #define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, base) #define INSTR_GetLookupA(op) INSTRUCTION(op, GetLookupA, 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_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 +109,44 @@ 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_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_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 +161,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 +180,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) @@ -202,10 +215,8 @@ QT_BEGIN_NAMESPACE F(StoreNameSloppy) \ F(StoreNameStrict) \ F(LoadElement) \ - F(LoadElementA) \ F(StoreElement) \ F(LoadProperty) \ - F(LoadPropertyA) \ F(GetLookup) \ F(GetLookupA) \ F(StoreProperty) \ @@ -215,6 +226,8 @@ QT_BEGIN_NAMESPACE F(LoadScopeObjectProperty) \ F(LoadContextObjectProperty) \ F(LoadIdObject) \ + F(Yield) \ + F(Resume) \ F(CallValue) \ F(CallProperty) \ F(CallPropertyLookup) \ @@ -224,18 +237,28 @@ QT_BEGIN_NAMESPACE F(CallGlobalLookup) \ F(CallScopeObjectProperty) \ F(CallContextObjectProperty) \ - F(SetExceptionHandler) \ + 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(PushScriptContext) \ + F(PopScriptContext) \ F(PopContext) \ - F(ForeachIteratorObject) \ - F(ForeachNextPropertyName) \ - F(DeleteMember) \ - F(DeleteSubscript) \ + F(GetIterator) \ + F(IteratorNext) \ + F(IteratorClose) \ + F(DestructureRestElement) \ + F(DeleteProperty) \ F(DeleteName) \ F(TypeofName) \ F(TypeofValue) \ @@ -244,11 +267,14 @@ QT_BEGIN_NAMESPACE F(DefineObjectLiteral) \ F(CreateMappedArgumentsObject) \ F(CreateUnmappedArgumentsObject) \ + F(CreateRestParameter) \ F(ConvertThisToObject) \ - F(Construct) \ + F(ToObject) \ F(Jump) \ F(JumpTrue) \ F(JumpFalse) \ + F(JumpNoException) \ + F(JumpNotUndefined) \ F(CmpEqNull) \ F(CmpNeNull) \ F(CmpEqInt) \ @@ -263,8 +289,6 @@ QT_BEGIN_NAMESPACE F(CmpStrictNotEqual) \ F(CmpIn) \ F(CmpInstanceOf) \ - F(JumpStrictEqualStackSlotInt) \ - F(JumpStrictNotEqualStackSlotInt) \ F(UNot) \ F(UPlus) \ F(UMinus) \ @@ -284,6 +308,7 @@ QT_BEGIN_NAMESPACE F(UShrConst) \ F(ShrConst) \ F(ShlConst) \ + F(Exp) \ F(Mul) \ F(Div) \ F(Mod) \ @@ -347,7 +372,9 @@ QT_BEGIN_NAMESPACE nargs, #define MOTH_DECODE_ARG(arg, type, nargs, offset) \ - arg = qFromLittleEndian<type>(reinterpret_cast<const type *>(code)[-nargs + offset]); + arg = qFromLittleEndian<type>( \ + static_cast<const void *>( \ + &reinterpret_cast<const type *>(code)[-nargs + offset])); #define MOTH_ADJUST_CODE(type, nargs) \ code += static_cast<quintptr>(nargs*sizeof(type) + 1) |