From d4ca7f779d85b01a7e650abefeb8cd6502eff8e2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Aug 2018 14:53:28 +0200 Subject: Fix initialization of default exported functions and generators When registering a default export, make sure that the local name points either to an entry that we've entered into the environment or the synthetic entry we create. Change-Id: I37e160dc1e3231214bb68f72d6bb0746d7aee3b3 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4codegen.cpp | 5 ++++- src/qml/compiler/qv4compilercontext.cpp | 9 ++++++--- src/qml/compiler/qv4compilercontext_p.h | 2 +- src/qml/compiler/qv4compilerscanfunctions.cpp | 28 +++++++++++++++++++-------- 4 files changed, 31 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 185f1812f7..0d004bfe80 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -816,7 +816,10 @@ bool Codegen::visit(ExportDeclaration *ast) } exportedValue.loadInAccumulator(); - Reference defaultExportSlot = Reference::fromScopedLocal(this, 0, /*scope*/0); + + const int defaultExportIndex = _context->locals.indexOf(_context->localNameForDefaultExport); + Q_ASSERT(defaultExportIndex != -1); + Reference defaultExportSlot = Reference::fromScopedLocal(this, defaultExportIndex, /*scope*/0); defaultExportSlot.storeConsumeAccumulator(); return false; diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index c0fbb72c59..09be19e427 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -295,9 +295,12 @@ 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*")); + if (contextType == ContextType::ESModule && !localNameForDefaultExport.isEmpty()) { + if (!members.contains(localNameForDefaultExport)) { + // allocate a local slot for the default export, to be used in + // CodeGen::visit(ExportDeclaration*). + locals.append(localNameForDefaultExport); + } } switch (contextType) { diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 0f7f4c835d..bd0bd90a59 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -180,7 +180,7 @@ struct Context { QStringList moduleRequests; QVector importEntries; QVector exportEntries; - bool hasDefaultExport = false; + QString localNameForDefaultExport; QVector nestedContexts; ControlFlow *controlFlow = nullptr; diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index a3bdce05d4..7a8622dc02 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -170,6 +170,8 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) _context->moduleRequests << module; } + QString localNameForDefaultExport = QStringLiteral("*default*"); + if (declaration->exportAll) { Compiler::ExportEntry entry; entry.moduleRequest = declaration->fromClause->moduleSpecifier.toString(); @@ -209,25 +211,35 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) entry.localName = name; entry.exportName = name; _context->exportEntries << entry; + if (declaration->exportDefault) + localNameForDefaultExport = entry.localName; } } else if (auto *fdef = declaration->variableStatementOrDeclaration->asFunctionDefinition()) { - QString name = fdef->name.toString(); - // Our parser gives `export default (function() {}` the name "default", which - // we don't want to export here. - if (!name.isEmpty() && name != QStringLiteral("default")) { + QString functionName; + + // Only function definitions for which we enter their name into the local environment + // can result in exports. Nested expressions such as (function foo() {}) are not accessible + // as locals and can only be exported as default exports (further down). + auto ast = declaration->variableStatementOrDeclaration; + if (AST::cast(ast) || AST::cast(ast)) + functionName = fdef->name.toString(); + + if (!functionName.isEmpty()) { Compiler::ExportEntry entry; - entry.localName = name; - entry.exportName = name; + entry.localName = functionName; + entry.exportName = functionName; _context->exportEntries << entry; + if (declaration->exportDefault) + localNameForDefaultExport = entry.localName; } } if (declaration->exportDefault) { Compiler::ExportEntry entry; - entry.localName = QStringLiteral("*default*"); + entry.localName = localNameForDefaultExport; + _context->localNameForDefaultExport = localNameForDefaultExport; entry.exportName = QStringLiteral("default"); _context->exportEntries << entry; - _context->hasDefaultExport = true; } return true; // scan through potential assignment expression code, etc. -- cgit v1.2.3