Skip to content

Commit e8755d7

Browse files
committed
Support export namespace export * as ns
https://bugs.webkit.org/show_bug.cgi?id=214379 Reviewed by Ross Kirsling. JSTests: * stress/modules-syntax-error.js: * stress/modules-syntax.js: * test262/expectations.yaml: Source/JavaScriptCore: This patch supports `export * as ns from "module"` syntax. If it is used, we expose "module"'s namespace object as "ns". For each module environment, we create *namespace* (starNamespace) private symbol scope variable. And we fill it later with module namespace object. This way allows us to use module namespace object IC and super fast imported module binding lookup though environment variable lookup mechanism. * builtins/BuiltinNames.h: * bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::BytecodeGenerator): * parser/NodesAnalyzeModule.cpp: (JSC::ExportNamedDeclarationNode::analyzeModule): * parser/Parser.cpp: (JSC::Parser<LexerType>::parseExportDeclaration): * runtime/AbstractModuleRecord.cpp: (JSC::AbstractModuleRecord::ExportEntry::createNamespace): (JSC::AbstractModuleRecord::resolveExportImpl): (JSC::AbstractModuleRecord::getModuleNamespace): (JSC::AbstractModuleRecord::setModuleEnvironment): (JSC::AbstractModuleRecord::dump): * runtime/AbstractModuleRecord.h: * runtime/CommonIdentifiers.h: * runtime/JSFunction.cpp: (JSC::JSFunction::name): (JSC::JSFunction::reifyName): * runtime/JSModuleNamespaceObject.cpp: (JSC::JSModuleNamespaceObject::getOwnPropertySlotCommon): * runtime/JSModuleRecord.cpp: (JSC::JSModuleRecord::instantiateDeclarations): (JSC::JSModuleRecord::evaluate): * wasm/js/JSWebAssemblyModule.cpp: (JSC::JSWebAssemblyModule::finishCreation): * wasm/js/WebAssemblyModuleRecord.cpp: (JSC::WebAssemblyModuleRecord::link): Canonical link: https://commits.webkit.org/229460@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@267186 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent 21ac330 commit e8755d7

17 files changed

Lines changed: 205 additions & 50 deletions

‎JSTests/ChangeLog‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
2020-09-17 Yusuke Suzuki <ysuzuki@apple.com>
2+
3+
Support export namespace `export * as ns`
4+
https://bugs.webkit.org/show_bug.cgi?id=214379
5+
6+
Reviewed by Ross Kirsling.
7+
8+
* stress/modules-syntax-error.js:
9+
* stress/modules-syntax.js:
10+
* test262/expectations.yaml:
11+
112
2020-09-16 Saam Barati <sbarati@apple.com>
213

314
Don't IC a null custom accessor/value setter

‎JSTests/stress/modules-syntax-error.js‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,13 +199,30 @@ export a
199199
`, `SyntaxError: Unexpected identifier 'a'. Expected either a declaration or a variable statement.:2`);
200200

201201
checkModuleSyntaxError(String.raw`
202-
export * as b from "Cocoa"
203-
`, `SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name.:2`);
202+
export { * as b } from "Cocoa"
203+
`, `SyntaxError: Unexpected token '*'. Expected a variable name for the export declaration.:2`);
204+
205+
checkModuleSyntaxError(String.raw`
206+
export * as b
207+
`, `SyntaxError: Unexpected end of script:3`);
208+
209+
checkModuleSyntaxError(String.raw`
210+
export * as 42 from "Cocoa"
211+
`, `SyntaxError: Unexpected number '42'. Expected an exported name for the export declaration.:2`);
212+
213+
checkModuleSyntaxError(String.raw`
214+
export const b = 42;
215+
export * as b from "mod"
216+
`, `SyntaxError: Cannot export a duplicate name 'b'.:4`);
204217

205218
checkModuleSyntaxError(String.raw`
206219
export * "Cocoa"
207220
`, `SyntaxError: Unexpected string literal "Cocoa". Expected 'from' before exported module name.:2`);
208221

222+
checkModuleSyntaxError(String.raw`
223+
export *
224+
`, `SyntaxError: Unexpected end of script:3`);
225+
209226
checkModuleSyntaxError(String.raw`
210227
export const a;
211228
`, `SyntaxError: Unexpected token ';'. const declared variable 'a' must have an initializer.:2`);

‎JSTests/stress/modules-syntax.js‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ checkModuleSyntax(String.raw`
7070
export * from "mod";
7171
`);
7272

73+
checkModuleSyntax(String.raw`
74+
export * as b from "Cocoa"
75+
`);
76+
77+
checkModuleSyntax(String.raw`
78+
export * as delete from "Cocoa"
79+
`);
80+
7381
// semicolon is not needed.
7482
checkModuleSyntax(String.raw`
7583
export default function () { } 40;

‎JSTests/test262/expectations.yaml‎

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2461,25 +2461,9 @@ test/language/expressions/dynamic-import/for-await-resolution-and-error-agen-yie
24612461
test/language/expressions/dynamic-import/namespace/await-ns-define-own-property.js:
24622462
default: 'Test262:AsyncTestFailure:Test262Error: Test262Error: No change requested, Reflect.defineProperty: local1 Expected SameValue(«false», «true») to be true'
24632463
strict mode: 'Test262:AsyncTestFailure:Test262Error: Test262Error: No change requested, Reflect.defineProperty: local1 Expected SameValue(«false», «true») to be true'
2464-
test/language/expressions/dynamic-import/namespace/await-ns-get-nested-namespace-dflt-direct.js:
2465-
default: "Test262:AsyncTestFailure:SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
2466-
strict mode: "Test262:AsyncTestFailure:SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
2467-
test/language/expressions/dynamic-import/namespace/await-ns-get-nested-namespace-dflt-indirect.js:
2468-
module: "Test262:AsyncTestFailure:SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
2469-
test/language/expressions/dynamic-import/namespace/await-ns-get-nested-namespace-props-nrml.js:
2470-
default: "Test262:AsyncTestFailure:SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
2471-
strict mode: "Test262:AsyncTestFailure:SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
24722464
test/language/expressions/dynamic-import/namespace/promise-then-ns-define-own-property.js:
24732465
default: 'Test262:AsyncTestFailure:Test262Error: Test262Error: No change requested, Reflect.defineProperty: local1 Expected SameValue(«false», «true») to be true'
24742466
strict mode: 'Test262:AsyncTestFailure:Test262Error: Test262Error: No change requested, Reflect.defineProperty: local1 Expected SameValue(«false», «true») to be true'
2475-
test/language/expressions/dynamic-import/namespace/promise-then-ns-get-nested-namespace-dflt-direct.js:
2476-
default: "Test262:AsyncTestFailure:SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
2477-
strict mode: "Test262:AsyncTestFailure:SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
2478-
test/language/expressions/dynamic-import/namespace/promise-then-ns-get-nested-namespace-dflt-indirect.js:
2479-
module: "Test262:AsyncTestFailure:SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
2480-
test/language/expressions/dynamic-import/namespace/promise-then-ns-get-nested-namespace-props-nrml.js:
2481-
default: "Test262:AsyncTestFailure:SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
2482-
strict mode: "Test262:AsyncTestFailure:SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
24832467
test/language/expressions/function/eval-var-scope-syntax-err.js:
24842468
default: 'Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all'
24852469
test/language/expressions/function/scope-body-lex-distinct.js:
@@ -2997,28 +2981,10 @@ test/language/identifiers/start-unicode-13.0.0.js:
29972981
test/language/literals/regexp/u-astral-char-class-invert.js:
29982982
default: 'Test262Error: Expected SameValue(«�», «null») to be true'
29992983
strict mode: 'Test262Error: Expected SameValue(«�», «null») to be true'
3000-
test/language/module-code/eval-rqstd-once.js:
3001-
module: "SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
3002-
test/language/module-code/eval-rqstd-order.js:
3003-
module: "SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
3004-
test/language/module-code/eval-self-once.js:
3005-
module: "SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
30062984
test/language/module-code/export-default-asyncgenerator-declaration-binding.js:
30072985
module: "SyntaxError: Exported binding 'starDefault' needs to refer to a top-level declared variable."
3008-
test/language/module-code/export-star-as-dflt.js:
3009-
module: "SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
3010-
test/language/module-code/instn-once.js:
3011-
module: "SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
3012-
test/language/module-code/instn-star-as-props-dflt-skip.js:
3013-
module: "SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
3014-
test/language/module-code/instn-star-props-nrml.js:
3015-
module: "SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
30162986
test/language/module-code/namespace/internals/define-own-property.js:
30172987
module: 'Test262Error: Reflect.defineProperty: local1 Expected SameValue(«false», «true») to be true'
3018-
test/language/module-code/namespace/internals/get-nested-namespace-dflt-skip.js:
3019-
module: "SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
3020-
test/language/module-code/namespace/internals/get-nested-namespace-props-nrml.js:
3021-
module: "SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name."
30222988
test/language/module-code/parse-err-hoist-lex-fun.js:
30232989
module: 'Test262: This statement should not be evaluated.'
30242990
test/language/module-code/parse-err-hoist-lex-gen.js:

‎Source/JavaScriptCore/ChangeLog‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,43 @@
1+
2020-09-17 Yusuke Suzuki <ysuzuki@apple.com>
2+
3+
Support export namespace `export * as ns`
4+
https://bugs.webkit.org/show_bug.cgi?id=214379
5+
6+
Reviewed by Ross Kirsling.
7+
8+
This patch supports `export * as ns from "module"` syntax. If it is used, we expose "module"'s namespace object as "ns".
9+
For each module environment, we create *namespace* (starNamespace) private symbol scope variable. And we fill it later
10+
with module namespace object. This way allows us to use module namespace object IC and super fast imported module binding
11+
lookup though environment variable lookup mechanism.
12+
13+
* builtins/BuiltinNames.h:
14+
* bytecompiler/BytecodeGenerator.cpp:
15+
(JSC::BytecodeGenerator::BytecodeGenerator):
16+
* parser/NodesAnalyzeModule.cpp:
17+
(JSC::ExportNamedDeclarationNode::analyzeModule):
18+
* parser/Parser.cpp:
19+
(JSC::Parser<LexerType>::parseExportDeclaration):
20+
* runtime/AbstractModuleRecord.cpp:
21+
(JSC::AbstractModuleRecord::ExportEntry::createNamespace):
22+
(JSC::AbstractModuleRecord::resolveExportImpl):
23+
(JSC::AbstractModuleRecord::getModuleNamespace):
24+
(JSC::AbstractModuleRecord::setModuleEnvironment):
25+
(JSC::AbstractModuleRecord::dump):
26+
* runtime/AbstractModuleRecord.h:
27+
* runtime/CommonIdentifiers.h:
28+
* runtime/JSFunction.cpp:
29+
(JSC::JSFunction::name):
30+
(JSC::JSFunction::reifyName):
31+
* runtime/JSModuleNamespaceObject.cpp:
32+
(JSC::JSModuleNamespaceObject::getOwnPropertySlotCommon):
33+
* runtime/JSModuleRecord.cpp:
34+
(JSC::JSModuleRecord::instantiateDeclarations):
35+
(JSC::JSModuleRecord::evaluate):
36+
* wasm/js/JSWebAssemblyModule.cpp:
37+
(JSC::JSWebAssemblyModule::finishCreation):
38+
* wasm/js/WebAssemblyModuleRecord.cpp:
39+
(JSC::WebAssemblyModuleRecord::link):
40+
141
2020-09-17 Yusuke Suzuki <ysuzuki@apple.com>
242

343
[JSC] Optimize Promise#finally by avoiding creating multiple environments

‎Source/JavaScriptCore/builtins/BuiltinNames.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ namespace JSC {
8383
macro(push) \
8484
macro(repeatCharacter) \
8585
macro(starDefault) \
86+
macro(starNamespace) \
8687
macro(InspectorInstrumentation) \
8788
macro(keys) \
8889
macro(values) \

‎Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, ModuleProgramNode* moduleProgramNod
959959

960960
// Now declare all variables.
961961

962+
createVariable(m_vm.propertyNames->starNamespacePrivateName, VarKind::Scope, moduleEnvironmentSymbolTable, VerifyExisting);
962963
createVariable(m_vm.propertyNames->builtinNames().metaPrivateName(), VarKind::Scope, moduleEnvironmentSymbolTable, VerifyExisting);
963964

964965
for (auto& entry : moduleProgramNode->varDeclarations()) {

‎Source/JavaScriptCore/parser/NodesAnalyzeModule.cpp‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,14 @@ void ExportNamedDeclarationNode::analyzeModule(ModuleAnalyzer& analyzer)
8585
//
8686
// In this case, no local variable names are imported into the current module.
8787
// "v" indirectly points the binding in "mod".
88-
analyzer.moduleRecord()->addExportEntry(JSModuleRecord::ExportEntry::createIndirect(specifier->exportedName(), specifier->localName(), m_moduleName->moduleName()));
88+
//
89+
// export * as v from "mod"
90+
//
91+
// If it is namespace export, we should use createNamespace.
92+
if (specifier->localName() == analyzer.vm().propertyNames->starNamespacePrivateName)
93+
analyzer.moduleRecord()->addExportEntry(JSModuleRecord::ExportEntry::createNamespace(specifier->exportedName(), m_moduleName->moduleName()));
94+
else
95+
analyzer.moduleRecord()->addExportEntry(JSModuleRecord::ExportEntry::createIndirect(specifier->exportedName(), specifier->localName(), m_moduleName->moduleName()));
8996
}
9097
}
9198
}

‎Source/JavaScriptCore/parser/Parser.cpp‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3531,14 +3531,34 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
35313531
switch (m_token.m_type) {
35323532
case TIMES: {
35333533
// export * FromClause ;
3534+
// export * as IdentifierName FromClause ;
35343535
next();
35353536

3537+
const Identifier* exportedName = nullptr;
3538+
JSTokenLocation specifierLocation;
3539+
if (matchContextualKeyword(m_vm.propertyNames->as)) {
3540+
next();
3541+
specifierLocation = JSTokenLocation(tokenLocation());
3542+
failIfFalse(matchIdentifierOrKeyword(), "Expected an exported name for the export declaration");
3543+
exportedName = m_token.m_data.ident;
3544+
next();
3545+
}
3546+
35363547
failIfFalse(matchContextualKeyword(m_vm.propertyNames->from), "Expected 'from' before exported module name");
35373548
next();
35383549
auto moduleName = parseModuleName(context);
35393550
failIfFalse(moduleName, "Cannot parse the 'from' clause");
35403551
failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration");
35413552

3553+
if (exportedName) {
3554+
semanticFailIfFalse(exportName(*exportedName), "Cannot export a duplicate name '", exportedName->impl(), "'");
3555+
auto specifierList = context.createExportSpecifierList();
3556+
auto localName = &m_vm.propertyNames->starNamespacePrivateName;
3557+
auto specifier = context.createExportSpecifier(specifierLocation, *localName, *exportedName);
3558+
context.appendExportSpecifier(specifierList, specifier);
3559+
return context.createExportNamedDeclaration(exportLocation, specifierList, moduleName);
3560+
}
3561+
35423562
return context.createExportAllDeclaration(exportLocation, moduleName);
35433563
}
35443564

‎Source/JavaScriptCore/runtime/AbstractModuleRecord.cpp‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ auto AbstractModuleRecord::ExportEntry::createIndirect(const Identifier& exportN
118118
return ExportEntry { Type::Indirect, exportName, moduleName, importName, Identifier() };
119119
}
120120

121+
auto AbstractModuleRecord::ExportEntry::createNamespace(const Identifier& exportName, const Identifier& moduleName) -> ExportEntry
122+
{
123+
return ExportEntry { Type::Namespace, exportName, moduleName, Identifier(), Identifier() };
124+
}
125+
121126
auto AbstractModuleRecord::Resolution::notFound() -> Resolution
122127
{
123128
return Resolution { Type::NotFound, nullptr, Identifier() };
@@ -631,6 +636,18 @@ auto AbstractModuleRecord::resolveExportImpl(JSGlobalObject* globalObject, const
631636
pendingTasks.append(Task { ResolveQuery(importedModuleRecord, exportEntry.importName), Type::Query });
632637
continue;
633638
}
639+
640+
case ExportEntry::Type::Namespace: {
641+
AbstractModuleRecord* importedModuleRecord = moduleRecord->hostResolveImportedModule(globalObject, exportEntry.moduleName);
642+
RETURN_IF_EXCEPTION(scope, Resolution::error());
643+
644+
Resolution resolution { Resolution::Type::Resolved, importedModuleRecord, vm.propertyNames->starNamespacePrivateName };
645+
// 2. A module that has resolved a module namespace binding is always cacheable.
646+
cacheResolutionForQuery(query, resolution);
647+
if (!mergeToCurrentTop(resolution))
648+
return Resolution::ambiguous();
649+
continue;
650+
}
634651
}
635652
break;
636653
}
@@ -757,10 +774,37 @@ JSModuleNamespaceObject* AbstractModuleRecord::getModuleNamespace(JSGlobalObject
757774

758775
auto* moduleNamespaceObject = JSModuleNamespaceObject::create(globalObject, globalObject->moduleNamespaceObjectStructure(), this, WTFMove(resolutions));
759776
RETURN_IF_EXCEPTION(scope, nullptr);
777+
778+
// Materialize *namespace* slot with module namespace object unless the module environment is not yet materialized, in which case we'll do it in setModuleEnvironment
779+
if (m_moduleEnvironment) {
780+
bool putResult = false;
781+
constexpr bool shouldThrowReadOnlyError = false;
782+
constexpr bool ignoreReadOnlyErrors = true;
783+
symbolTablePutTouchWatchpointSet(m_moduleEnvironment.get(), globalObject, vm.propertyNames->starNamespacePrivateName, moduleNamespaceObject, shouldThrowReadOnlyError, ignoreReadOnlyErrors, putResult);
784+
RETURN_IF_EXCEPTION(scope, nullptr);
785+
}
760786
m_moduleNamespaceObject.set(vm, this, moduleNamespaceObject);
787+
761788
return moduleNamespaceObject;
762789
}
763790

791+
void AbstractModuleRecord::setModuleEnvironment(JSGlobalObject* globalObject, JSModuleEnvironment* moduleEnvironment)
792+
{
793+
VM& vm = globalObject->vm();
794+
auto scope = DECLARE_THROW_SCOPE(vm);
795+
796+
ASSERT(!m_moduleEnvironment);
797+
// If module namespace object is materialized, we will materialize *namespace* slot too.
798+
if (m_moduleNamespaceObject) {
799+
bool putResult = false;
800+
constexpr bool shouldThrowReadOnlyError = false;
801+
constexpr bool ignoreReadOnlyErrors = true;
802+
symbolTablePutTouchWatchpointSet(moduleEnvironment, globalObject, vm.propertyNames->starNamespacePrivateName, m_moduleNamespaceObject.get(), shouldThrowReadOnlyError, ignoreReadOnlyErrors, putResult);
803+
RETURN_IF_EXCEPTION(scope, void());
804+
}
805+
m_moduleEnvironment.set(vm, this, moduleEnvironment);
806+
}
807+
764808
void AbstractModuleRecord::link(JSGlobalObject* globalObject, JSValue scriptFetcher)
765809
{
766810
VM& vm = globalObject->vm();
@@ -823,6 +867,10 @@ void AbstractModuleRecord::dump()
823867
case ExportEntry::Type::Indirect:
824868
dataLog(" [Indirect] ", "export(", printableName(exportEntry.exportName), "), import(", printableName(exportEntry.importName), "), module(", printableName(exportEntry.moduleName), ")\n");
825869
break;
870+
871+
case ExportEntry::Type::Namespace:
872+
dataLog(" [Namespace] ", "export(", printableName(exportEntry.exportName), "), module(", printableName(exportEntry.moduleName), ")\n");
873+
break;
826874
}
827875
}
828876
for (const auto& moduleName : m_starExportEntries)

0 commit comments

Comments
 (0)