aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2018-08-08 17:49:59 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2018-08-10 20:33:59 +0000
commite839129b6020795483d1f9ba0b3bcfde9f881bab (patch)
tree6b04478a2077d939635e70971720bcc0643ddbb1 /src/qml
parent0bd3c9cda678b7e158c6142c1567ac927fde18b9 (diff)
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 <lars.knoll@qt.io>
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/compiler/qv4codegen.cpp30
-rw-r--r--src/qml/compiler/qv4codegen_p.h2
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp5
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h1
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp27
-rw-r--r--src/qml/parser/qqmljs.g10
-rw-r--r--src/qml/parser/qqmljsast.cpp25
-rw-r--r--src/qml/parser/qqmljsast_p.h8
8 files changed, 87 insertions, 21 deletions
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<FunctionDeclaration*>(ast->variableStatementOrDeclaration)) {
+ Result r;
+ qSwap(_expr, r);
+ visit(static_cast<FunctionExpression*>(fdecl));
+ qSwap(_expr, r);
+ exportedValue = r.result();
+ } else if (auto *classDecl = AST::cast<ClassDeclaration*>(ast->variableStatementOrDeclaration)) {
+ Result r;
+ qSwap(_expr, r);
+ visit(static_cast<ClassExpression*>(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<ImportEntry> importEntries;
QVector<ExportEntry> exportEntries;
+ bool hasDefaultExport = false;
QVector<Context *> 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<AST::ClassDeclaration*>(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<AST::StatementList*>(item->item);
- if (!listItem)
- continue;
+ if (!listItem) {
+ if (AST::ExportDeclaration *exportDecl = AST::cast<AST::ExportDeclaration*>(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;