From e839129b6020795483d1f9ba0b3bcfde9f881bab Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 8 Aug 2018 17:49:59 +0200 Subject: Fix support for default exports in modules Default export declarations require a binding setup step at run-time, so we hook it into the ESModule's statement list to make it visible to the code gen visitor. We also reserve local slot zero for the default export. Change-Id: Ie064caad0422b92cfdadbd7d94db72a05e95c0cc Reviewed-by: Lars Knoll --- src/qml/compiler/qv4codegen.cpp | 30 +++++++++++++++++++++++++++ src/qml/compiler/qv4codegen_p.h | 2 ++ src/qml/compiler/qv4compilercontext.cpp | 5 +++++ src/qml/compiler/qv4compilercontext_p.h | 1 + src/qml/compiler/qv4compilerscanfunctions.cpp | 27 ++++++++++++++++-------- src/qml/parser/qqmljs.g | 10 ++++++++- src/qml/parser/qqmljsast.cpp | 25 +++++++++++++++------- src/qml/parser/qqmljsast_p.h | 8 +++---- 8 files changed, 87 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 7b059b3fb4..fc43031c5c 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -789,6 +789,36 @@ bool Codegen::visit(PatternPropertyList *) return false; } +bool Codegen::visit(ExportDeclaration *ast) +{ + if (!ast->exportDefault) + return true; + + Reference exportedValue; + + if (auto *fdecl = AST::cast(ast->variableStatementOrDeclaration)) { + Result r; + qSwap(_expr, r); + visit(static_cast(fdecl)); + qSwap(_expr, r); + exportedValue = r.result(); + } else if (auto *classDecl = AST::cast(ast->variableStatementOrDeclaration)) { + Result r; + qSwap(_expr, r); + visit(static_cast(classDecl)); + qSwap(_expr, r); + exportedValue = r.result(); + } else if (ExpressionNode *expr = ast->variableStatementOrDeclaration->expressionCast()) { + exportedValue = expression(expr); + } + + exportedValue.loadInAccumulator(); + Reference defaultExportSlot = Reference::fromScopedLocal(this, 0, /*scope*/0); + defaultExportSlot.storeConsumeAccumulator(); + + return false; +} + bool Codegen::visit(StatementList *) { Q_UNREACHABLE(); diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index e3617254a7..516f506396 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -589,6 +589,8 @@ protected: bool visit(AST::PatternProperty *ast) override; bool visit(AST::PatternPropertyList *ast) override; + bool visit(AST::ExportDeclaration *ast) override; + // expressions bool visit(AST::Expression *ast) override; bool visit(AST::ArrayPattern *ast) override; diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index 77ac703ee3..c0fbb72c59 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -295,6 +295,11 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator) Q_ASSERT(nRegisters == 0); registerOffset = bytecodeGenerator->currentRegister(); + if (contextType == ContextType::ESModule && hasDefaultExport) { + // allocate a local slot for the default export + locals.append(QStringLiteral("*default*")); + } + switch (contextType) { case ContextType::ESModule: case ContextType::Block: diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 6a54be2aca..216d5ca56b 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -178,6 +178,7 @@ struct Context { QStringList locals; QVector importEntries; QVector exportEntries; + bool hasDefaultExport = false; QVector nestedContexts; ControlFlow *controlFlow = nullptr; diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index 63ed748048..8f16f9a362 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -201,21 +201,30 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) } } else if (auto *classDecl = AST::cast(declaration->variableStatementOrDeclaration)) { QString name = classDecl->name.toString(); - Compiler::ExportEntry entry; - entry.localName = name; - entry.exportName = name; - _context->exportEntries << entry; + if (!name.isEmpty()) { + Compiler::ExportEntry entry; + entry.localName = name; + entry.exportName = name; + _context->exportEntries << entry; + } } else if (auto *fdef = declaration->variableStatementOrDeclaration->asFunctionDefinition()) { QString name = fdef->name.toString(); - Compiler::ExportEntry entry; - entry.localName = name; - entry.exportName = name; - _context->exportEntries << entry; - } else if (declaration->exportDefault) { + // Our parser gives `export default (function() {}` the name "default", which + // we don't want to export here. + if (!name.isEmpty() && name != QStringLiteral("default")) { + Compiler::ExportEntry entry; + entry.localName = name; + entry.exportName = name; + _context->exportEntries << entry; + } + } + + if (declaration->exportDefault) { Compiler::ExportEntry entry; entry.localName = QStringLiteral("*default*"); entry.exportName = QStringLiteral("default"); _context->exportEntries << entry; + _context->hasDefaultExport = true; } return true; // scan through potential assignment expression code, etc. diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index 4c5748f818..faf465e7b9 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -3991,7 +3991,7 @@ ScriptBody: StatementList; Module: ModuleBodyOpt; /. case $rule_number: { - sym(1).Node = new (pool) AST::ESModule(sym(1).ModuleItemList); + sym(1).Node = new (pool) AST::ESModule(sym(1).ModuleItemList, pool); } break; ./ @@ -4233,6 +4233,14 @@ ExportDeclaration: T_EXPORT T_DEFAULT ExportDeclarationLookahead T_FORCE_DECLARA ExportDeclaration: T_EXPORT T_DEFAULT ExportDeclarationLookahead AssignmentExpression_In; -- [lookahead ∉ { function, class }] /. case $rule_number: { + // if lhs is an identifier expression and rhs is an anonymous function expression, we need to assign the name of lhs to the function + if (auto *f = asAnonymousFunctionDefinition(sym(4).Node)) { + f->name = stringRef(2); + } + if (auto *c = asAnonymousClassDefinition(sym(4).Expression)) { + c->name = stringRef(2); + } + auto exportDeclaration = new (pool) AST::ExportDeclaration(/*exportDefault=*/true, sym(4).Node); exportDeclaration->exportToken = loc(1); sym(1).ExportDeclaration = exportDeclaration; diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp index 6b315c9140..0b1b97a41a 100644 --- a/src/qml/parser/qqmljsast.cpp +++ b/src/qml/parser/qqmljsast.cpp @@ -1166,10 +1166,17 @@ void ModuleItemList::accept0(Visitor *visitor) { if (visitor->visit(this)) { for (ModuleItemList *it = this; it; it = it->next) { - // The statement list is concatenated together between module list - // items and stored in the ESModule, thus not visited from there. - if (it->item && it->item->kind == Kind_StatementList) - continue; + if (it->item) { + // The statement list is concatenated together between module list + // items and stored in the ESModule, thus not visited from there. + if (it->item->kind == Kind_StatementList) + continue; + // Export declaration are also injected into the statement list for + // processing of declarations an variable statements, so don't visit + // it here to avoid double visits. + if (it->item->kind == Kind_ExportDeclaration) + continue; + } accept(it->item, visitor); } } @@ -1177,13 +1184,17 @@ void ModuleItemList::accept0(Visitor *visitor) visitor->endVisit(this); } -StatementList *ModuleItemList::buildStatementList() const +StatementList *ModuleItemList::buildStatementList(MemoryPool *pool) const { StatementList *statements = nullptr; for (const ModuleItemList *item = this; item; item = item->next) { AST::StatementList *listItem = AST::cast(item->item); - if (!listItem) - continue; + if (!listItem) { + if (AST::ExportDeclaration *exportDecl = AST::cast(item->item)) + listItem = new (pool) AST::StatementList(exportDecl); + else + continue; + } if (statements) statements = statements->append(listItem); else diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index 0729c99931..9d82e9546b 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -2675,7 +2675,7 @@ public: ExportsList *exportsList = nullptr; }; -class QML_PARSER_EXPORT ExportDeclaration: public Node +class QML_PARSER_EXPORT ExportDeclaration: public Statement { public: QQMLJS_DECLARE_AST_NODE(ExportDeclaration) @@ -2753,7 +2753,7 @@ public: return head; } - StatementList *buildStatementList() const; + StatementList *buildStatementList(MemoryPool *pool) const; void accept0(Visitor *visitor) override; @@ -2773,12 +2773,12 @@ class QML_PARSER_EXPORT ESModule: public Node public: QQMLJS_DECLARE_AST_NODE(Module) - ESModule(ModuleItemList *body) + ESModule(ModuleItemList *body, MemoryPool *pool) : body(body) { kind = K; if (body) - statements = body->buildStatementList(); + statements = body->buildStatementList(pool); } void accept0(Visitor *visitor) override; -- cgit v1.2.3