Skip to content

Commit 0e1e761

Browse files
committed
Implement export * as ns from module syntax
Also fix #5777 And minor syntax error message improvement
1 parent 2b6e9a2 commit 0e1e761

File tree

10 files changed

+445
-218
lines changed

10 files changed

+445
-218
lines changed

lib/Common/ConfigFlagsList.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,7 @@ PHASE(All)
673673
#define DEFAULT_CONFIG_ES7ValuesEntries (true)
674674
#define DEFAULT_CONFIG_ESObjectGetOwnPropertyDescriptors (true)
675675
#define DEFAULT_CONFIG_ESDynamicImport (false)
676+
#define DEFAULT_CONFIG_ESExportNsAs (true)
676677

677678
#define DEFAULT_CONFIG_ESSharedArrayBuffer (false)
678679

@@ -1149,6 +1150,7 @@ FLAGPR (Boolean, ES6, ES6UnicodeVerbose , "Enable ES6 Unicode 6.0
11491150
FLAGPR (Boolean, ES6, ES6Unscopables , "Enable ES6 With Statement Unscopables" , DEFAULT_CONFIG_ES6Unscopables)
11501151
FLAGPR (Boolean, ES6, ES6RegExSticky , "Enable ES6 RegEx sticky flag" , DEFAULT_CONFIG_ES6RegExSticky)
11511152
FLAGPR (Boolean, ES6, ES2018RegExDotAll , "Enable ES2018 RegEx dotAll flag" , DEFAULT_CONFIG_ES2018RegExDotAll)
1153+
FLAGPR (Boolean, ES6, ESExportNsAs , "Enable ES experimental export * as name" , DEFAULT_CONFIG_ESExportNsAs)
11521154

11531155
#ifndef COMPILE_DISABLE_ES6RegExPrototypeProperties
11541156
#define COMPILE_DISABLE_ES6RegExPrototypeProperties 0

lib/Parser/Parse.cpp

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2366,12 +2366,12 @@ void Parser::ParseNamedImportOrExportClause(ModuleImportOrExportEntryList* impor
23662366
// If we are parsing an import statement, the token after 'as' must be a BindingIdentifier.
23672367
if (!isExportClause)
23682368
{
2369-
ChkCurTokNoScan(tkID, ERRsyntax);
2369+
ChkCurTokNoScan(tkID, ERRValidIfFollowedBy, _u("'as'"), _u("an identifier."));
23702370
}
23712371

23722372
if (!(m_token.IsIdentifier() || m_token.IsReservedWord()))
23732373
{
2374-
Error(ERRsyntax);
2374+
Error(ERRValidIfFollowedBy, _u("'as'"), _u("an identifier."));
23752375
}
23762376

23772377
identifierAs = m_token.GetIdentifier(this->GetHashTbl());
@@ -2496,7 +2496,7 @@ ModuleImportOrExportEntry* Parser::AddModuleImportOrExportEntry(ModuleImportOrEx
24962496
{
24972497
if (importOrExportEntry->exportName != nullptr)
24982498
{
2499-
CheckForDuplicateExportEntry(importOrExportEntryList, importOrExportEntry->exportName);
2499+
CheckForDuplicateExportEntry(importOrExportEntry->exportName);
25002500
}
25012501

25022502
importOrExportEntryList->Prepend(*importOrExportEntry);
@@ -2526,6 +2526,19 @@ void Parser::AddModuleLocalExportEntry(ParseNodePtr varDeclNode)
25262526
AddModuleImportOrExportEntry(EnsureModuleLocalExportEntryList(), nullptr, localName, localName, nullptr);
25272527
}
25282528

2529+
void Parser::CheckForDuplicateExportEntry(IdentPtr exportName)
2530+
{
2531+
if (m_currentNodeProg->AsParseNodeModule()->indirectExportEntries != nullptr)
2532+
{
2533+
CheckForDuplicateExportEntry(m_currentNodeProg->AsParseNodeModule()->indirectExportEntries, exportName);
2534+
}
2535+
2536+
if (m_currentNodeProg->AsParseNodeModule()->localExportEntries != nullptr)
2537+
{
2538+
CheckForDuplicateExportEntry(m_currentNodeProg->AsParseNodeModule()->localExportEntries, exportName);
2539+
}
2540+
}
2541+
25292542
void Parser::CheckForDuplicateExportEntry(ModuleImportOrExportEntryList* exportEntryList, IdentPtr exportName)
25302543
{
25312544
ModuleImportOrExportEntry* findResult = exportEntryList->Find([&](ModuleImportOrExportEntry exportEntry)
@@ -2539,7 +2552,7 @@ void Parser::CheckForDuplicateExportEntry(ModuleImportOrExportEntryList* exportE
25392552

25402553
if (findResult != nullptr)
25412554
{
2542-
Error(ERRsyntax);
2555+
Error(ERRDuplicateExport, exportName->Psz());
25432556
}
25442557
}
25452558

@@ -2927,7 +2940,34 @@ ParseNodePtr Parser::ParseExportDeclaration(bool *needTerminator)
29272940
switch (m_token.tk)
29282941
{
29292942
case tkStar:
2943+
{
29302944
this->GetScanner()->Scan();
2945+
IdentPtr exportName = nullptr;
2946+
2947+
if (m_scriptContext->GetConfig()->IsESExportNsAsEnabled())
2948+
{
2949+
// export * as name
2950+
if (m_token.tk == tkID)
2951+
{
2952+
// check for 'as'
2953+
if (wellKnownPropertyPids.as == m_token.GetIdentifier(this->GetHashTbl()))
2954+
{
2955+
// scan to the next token
2956+
this->GetScanner()->Scan();
2957+
2958+
// token after as must be an identifier
2959+
if (!(m_token.IsIdentifier() || m_token.IsReservedWord()))
2960+
{
2961+
Error(ERRValidIfFollowedBy, _u("'as'"), _u("an identifier."));
2962+
}
2963+
2964+
exportName = m_token.GetIdentifier(this->GetHashTbl());
2965+
2966+
// scan to next token
2967+
this->GetScanner()->Scan();
2968+
}
2969+
}
2970+
}
29312971

29322972
// A star token in an export declaration must be followed by a from clause which begins with a token 'from'.
29332973
moduleIdentifier = ParseImportOrExportFromClause<buildAST>(true);
@@ -2937,9 +2977,16 @@ ParseNodePtr Parser::ParseExportDeclaration(bool *needTerminator)
29372977
Assert(moduleIdentifier != nullptr);
29382978

29392979
AddModuleSpecifier(moduleIdentifier);
2940-
IdentPtr importName = wellKnownPropertyPids._star;
29412980

2942-
AddModuleImportOrExportEntry(EnsureModuleStarExportEntryList(), importName, nullptr, nullptr, moduleIdentifier);
2981+
if (!exportName)
2982+
{
2983+
AddModuleImportOrExportEntry(EnsureModuleStarExportEntryList(), wellKnownPropertyPids._star, nullptr, nullptr, moduleIdentifier);
2984+
}
2985+
else
2986+
{
2987+
CheckForDuplicateExportEntry(exportName);
2988+
AddModuleImportOrExportEntry(EnsureModuleIndirectExportEntryList(), wellKnownPropertyPids._star, nullptr, exportName, moduleIdentifier);
2989+
}
29432990
}
29442991

29452992
if (needTerminator != nullptr)
@@ -2948,6 +2995,7 @@ ParseNodePtr Parser::ParseExportDeclaration(bool *needTerminator)
29482995
}
29492996

29502997
break;
2998+
}
29512999

29523000
case tkLCurly:
29533001
{

lib/Parser/Parse.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,7 @@ class Parser
645645
ModuleImportOrExportEntry* AddModuleImportOrExportEntry(ModuleImportOrExportEntryList* importOrExportEntryList, IdentPtr importName, IdentPtr localName, IdentPtr exportName, IdentPtr moduleRequest);
646646
ModuleImportOrExportEntry* AddModuleImportOrExportEntry(ModuleImportOrExportEntryList* importOrExportEntryList, ModuleImportOrExportEntry* importOrExportEntry);
647647
void AddModuleLocalExportEntry(ParseNodePtr varDeclNode);
648+
void CheckForDuplicateExportEntry(IdentPtr exportName);
648649
void CheckForDuplicateExportEntry(ModuleImportOrExportEntryList* exportEntryList, IdentPtr exportName);
649650

650651
ParseNodeVar * CreateModuleImportDeclNode(IdentPtr localName);
@@ -1087,11 +1088,11 @@ class Parser
10871088
void AddToNodeList(ParseNode ** ppnodeList, ParseNode *** pppnodeLast, ParseNode * pnodeAdd);
10881089
void AddToNodeListEscapedUse(ParseNode ** ppnodeList, ParseNode *** pppnodeLast, ParseNode * pnodeAdd);
10891090

1090-
void ChkCurTokNoScan(int tk, int wErr)
1091+
void ChkCurTokNoScan(int tk, int wErr, LPCWSTR stringOne = _u(""), LPCWSTR stringTwo = _u(""))
10911092
{
10921093
if (m_token.tk != tk)
10931094
{
1094-
Error(wErr);
1095+
Error(wErr, stringOne, stringTwo);
10951096
}
10961097
}
10971098

lib/Parser/perrors.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ LSC_ERROR_MSG(1094, ERRLabelFollowedByEOF, "Unexpected end of script after a lab
110110
LSC_ERROR_MSG(1095, ERRFunctionAfterLabelInStrict, "Function declarations not allowed after a label in strict mode.")
111111
LSC_ERROR_MSG(1096, ERRAwaitAsLabelInAsync, "Use of 'await' as label in async function is not allowed.")
112112
LSC_ERROR_MSG(1097, ERRExperimental, "Use of disabled experimental feature")
113+
LSC_ERROR_MSG(1098, ERRDuplicateExport, "Duplicate export of name '%s'")
113114
//1098-1199 available for future use
114115

115116
// Generic errors intended to be re-usable

lib/Runtime/Base/ThreadConfigFlagsList.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ FLAG_RELEASE(IsESObjectGetOwnPropertyDescriptorsEnabled, ESObjectGetOwnPropertyD
4848
FLAG_RELEASE(IsESSharedArrayBufferEnabled, ESSharedArrayBuffer)
4949
FLAG_RELEASE(IsESDynamicImportEnabled, ESDynamicImport)
5050
FLAG_RELEASE(IsESBigIntEnabled, ESBigInt)
51+
FLAG_RELEASE(IsESExportNsAsEnabled, ESExportNsAs)
5152
#ifdef ENABLE_PROJECTION
5253
FLAG(AreWinRTDelegatesInterfaces, WinRTDelegateInterfaces)
5354
FLAG_RELEASE(IsWinRTAdaptiveAppsEnabled, WinRTAdaptiveApps)

lib/Runtime/Language/ModuleNamespace.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,12 @@ namespace Js
272272
// TODO: maybe we can cache the slot address & offset, instead of looking up everytime? We do need to look up the reference everytime.
273273
if (unambiguousNonLocalExports->TryGetValue(propertyId, &moduleNameRecord))
274274
{
275+
// special case for export * as ns
276+
if (moduleNameRecord.bindingName == Js::PropertyIds::star_)
277+
{
278+
*value = static_cast<Var>(moduleNameRecord.module->GetNamespace());
279+
return PropertyQueryFlags::Property_Found;
280+
}
275281
return JavascriptConversion::BooleanToPropertyQueryFlags(moduleNameRecord.module->GetNamespace()->GetProperty(originalInstance, moduleNameRecord.bindingName, value, info, requestContext));
276282
}
277283
}

lib/Runtime/Language/SourceTextModuleRecord.cpp

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -634,22 +634,25 @@ namespace Js
634634
{
635635
JavascriptError::ThrowReferenceError(scriptContext, JSERR_CannotResolveModule, exportEntry.moduleRequest->Psz());
636636
}
637-
else
637+
638+
if (exportEntry.importName->GetPropertyId() == PropertyIds::star_)
638639
{
639-
isAmbiguous = !childModuleRecord->ResolveExport(importNameId, resolveSet, exportRecord);
640-
if (isAmbiguous)
641-
{
642-
// ambiguous; don't need to search further
643-
return true;
644-
}
645-
else
646-
{
647-
// found a resolution. done;
648-
if (*exportRecord != nullptr)
649-
{
650-
return true;
651-
}
652-
}
640+
// export * as someName from "foo"
641+
*exportRecord = childModuleRecord->GetNamespaceNameRecord();
642+
return true;
643+
}
644+
645+
isAmbiguous = !childModuleRecord->ResolveExport(importNameId, resolveSet, exportRecord);
646+
if (isAmbiguous)
647+
{
648+
// ambiguous; don't need to search further
649+
return true;
650+
}
651+
652+
// found a resolution. done;
653+
if (*exportRecord != nullptr)
654+
{
655+
return true;
653656
}
654657
return false;
655658
});
@@ -1211,8 +1214,9 @@ namespace Js
12111214
this->errorObject = errorObj;
12121215
return;
12131216
}
1214-
if (!childModuleRecord->ResolveExport(propertyId, nullptr, &exportRecord) ||
1215-
(exportRecord == nullptr))
1217+
if (propertyId != PropertyIds::star_ &&
1218+
(!childModuleRecord->ResolveExport(propertyId, nullptr, &exportRecord) ||
1219+
(exportRecord == nullptr)))
12161220
{
12171221
JavascriptError* errorObj = scriptContext->GetLibrary()->CreateSyntaxError();
12181222
JavascriptError::SetErrorMessage(errorObj, JSERR_ModuleResolveExport, exportEntry.exportName->Psz(), scriptContext);

test/es6module/bug_issue_5777.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
// Bug Issue 5777 https://github.com/Microsoft/ChakraCore/issues/5777
7+
// Duplicate export names should cause an early syntax error
8+
9+
WScript.RegisterModuleSource("a.js",
10+
`export const boo = 4;
11+
export {bar as boo} from "b.js";
12+
print ("Should not be printed")`);
13+
WScript.RegisterModuleSource("b.js","export const bar = 5;");
14+
15+
import("a.js").then(()=>{
16+
print("Failed - expected SyntaxError but no error thrown")
17+
}).catch ((e)=>{
18+
if (e instanceof SyntaxError) {
19+
print("pass");
20+
} else {
21+
print (`Failed - threw ${e.constructor.toString()} but should have thrown SyntaxError`);
22+
}
23+
});

0 commit comments

Comments
 (0)