Skip to content

Commit

Permalink
Implement export * as ns from module syntax
Browse files Browse the repository at this point in the history
Also fix #5777
And minor syntax error message improvement
  • Loading branch information
rhuanjl committed Oct 30, 2018
1 parent d1e2c6a commit e198e64
Show file tree
Hide file tree
Showing 10 changed files with 445 additions and 218 deletions.
2 changes: 2 additions & 0 deletions lib/Common/ConfigFlagsList.h
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,7 @@ PHASE(All)
#define DEFAULT_CONFIG_ES7ValuesEntries (true)
#define DEFAULT_CONFIG_ESObjectGetOwnPropertyDescriptors (true)
#define DEFAULT_CONFIG_ESDynamicImport (false)
#define DEFAULT_CONFIG_ESExportNsAs (true)

#define DEFAULT_CONFIG_ESSharedArrayBuffer (false)

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

#ifndef COMPILE_DISABLE_ES6RegExPrototypeProperties
#define COMPILE_DISABLE_ES6RegExPrototypeProperties 0
Expand Down
60 changes: 54 additions & 6 deletions lib/Parser/Parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2366,12 +2366,12 @@ void Parser::ParseNamedImportOrExportClause(ModuleImportOrExportEntryList* impor
// If we are parsing an import statement, the token after 'as' must be a BindingIdentifier.
if (!isExportClause)
{
ChkCurTokNoScan(tkID, ERRsyntax);
ChkCurTokNoScan(tkID, ERRValidIfFollowedBy, _u("'as'"), _u("an identifier."));
}

if (!(m_token.IsIdentifier() || m_token.IsReservedWord()))
{
Error(ERRsyntax);
Error(ERRValidIfFollowedBy, _u("'as'"), _u("an identifier."));
}

identifierAs = m_token.GetIdentifier(this->GetHashTbl());
Expand Down Expand Up @@ -2496,7 +2496,7 @@ ModuleImportOrExportEntry* Parser::AddModuleImportOrExportEntry(ModuleImportOrEx
{
if (importOrExportEntry->exportName != nullptr)
{
CheckForDuplicateExportEntry(importOrExportEntryList, importOrExportEntry->exportName);
CheckForDuplicateExportEntry(importOrExportEntry->exportName);
}

importOrExportEntryList->Prepend(*importOrExportEntry);
Expand Down Expand Up @@ -2526,6 +2526,19 @@ void Parser::AddModuleLocalExportEntry(ParseNodePtr varDeclNode)
AddModuleImportOrExportEntry(EnsureModuleLocalExportEntryList(), nullptr, localName, localName, nullptr);
}

void Parser::CheckForDuplicateExportEntry(IdentPtr exportName)
{
if (m_currentNodeProg->AsParseNodeModule()->indirectExportEntries != nullptr)
{
CheckForDuplicateExportEntry(m_currentNodeProg->AsParseNodeModule()->indirectExportEntries, exportName);
}

if (m_currentNodeProg->AsParseNodeModule()->localExportEntries != nullptr)
{
CheckForDuplicateExportEntry(m_currentNodeProg->AsParseNodeModule()->localExportEntries, exportName);
}
}

void Parser::CheckForDuplicateExportEntry(ModuleImportOrExportEntryList* exportEntryList, IdentPtr exportName)
{
ModuleImportOrExportEntry* findResult = exportEntryList->Find([&](ModuleImportOrExportEntry exportEntry)
Expand All @@ -2539,7 +2552,7 @@ void Parser::CheckForDuplicateExportEntry(ModuleImportOrExportEntryList* exportE

if (findResult != nullptr)
{
Error(ERRsyntax);
Error(ERRDuplicateExport, exportName->Psz());
}
}

Expand Down Expand Up @@ -2927,7 +2940,34 @@ ParseNodePtr Parser::ParseExportDeclaration(bool *needTerminator)
switch (m_token.tk)
{
case tkStar:
{
this->GetScanner()->Scan();
IdentPtr exportName = nullptr;

if (m_scriptContext->GetConfig()->IsESExportNsAsEnabled())
{
// export * as name
if (m_token.tk == tkID)
{
// check for 'as'
if (wellKnownPropertyPids.as == m_token.GetIdentifier(this->GetHashTbl()))
{
// scan to the next token
this->GetScanner()->Scan();

// token after as must be an identifier
if (!(m_token.IsIdentifier() || m_token.IsReservedWord()))
{
Error(ERRValidIfFollowedBy, _u("'as'"), _u("an identifier."));
}

exportName = m_token.GetIdentifier(this->GetHashTbl());

// scan to next token
this->GetScanner()->Scan();
}
}
}

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

AddModuleSpecifier(moduleIdentifier);
IdentPtr importName = wellKnownPropertyPids._star;

AddModuleImportOrExportEntry(EnsureModuleStarExportEntryList(), importName, nullptr, nullptr, moduleIdentifier);
if (!exportName)
{
AddModuleImportOrExportEntry(EnsureModuleStarExportEntryList(), wellKnownPropertyPids._star, nullptr, nullptr, moduleIdentifier);
}
else
{
CheckForDuplicateExportEntry(exportName);
AddModuleImportOrExportEntry(EnsureModuleIndirectExportEntryList(), wellKnownPropertyPids._star, nullptr, exportName, moduleIdentifier);
}
}

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

break;
}

case tkLCurly:
{
Expand Down
5 changes: 3 additions & 2 deletions lib/Parser/Parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,7 @@ class Parser
ModuleImportOrExportEntry* AddModuleImportOrExportEntry(ModuleImportOrExportEntryList* importOrExportEntryList, IdentPtr importName, IdentPtr localName, IdentPtr exportName, IdentPtr moduleRequest);
ModuleImportOrExportEntry* AddModuleImportOrExportEntry(ModuleImportOrExportEntryList* importOrExportEntryList, ModuleImportOrExportEntry* importOrExportEntry);
void AddModuleLocalExportEntry(ParseNodePtr varDeclNode);
void CheckForDuplicateExportEntry(IdentPtr exportName);
void CheckForDuplicateExportEntry(ModuleImportOrExportEntryList* exportEntryList, IdentPtr exportName);

ParseNodeVar * CreateModuleImportDeclNode(IdentPtr localName);
Expand Down Expand Up @@ -1087,11 +1088,11 @@ class Parser
void AddToNodeList(ParseNode ** ppnodeList, ParseNode *** pppnodeLast, ParseNode * pnodeAdd);
void AddToNodeListEscapedUse(ParseNode ** ppnodeList, ParseNode *** pppnodeLast, ParseNode * pnodeAdd);

void ChkCurTokNoScan(int tk, int wErr)
void ChkCurTokNoScan(int tk, int wErr, LPCWSTR stringOne = _u(""), LPCWSTR stringTwo = _u(""))
{
if (m_token.tk != tk)
{
Error(wErr);
Error(wErr, stringOne, stringTwo);
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/Parser/perrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ LSC_ERROR_MSG(1094, ERRLabelFollowedByEOF, "Unexpected end of script after a lab
LSC_ERROR_MSG(1095, ERRFunctionAfterLabelInStrict, "Function declarations not allowed after a label in strict mode.")
LSC_ERROR_MSG(1096, ERRAwaitAsLabelInAsync, "Use of 'await' as label in async function is not allowed.")
LSC_ERROR_MSG(1097, ERRExperimental, "Use of disabled experimental feature")
LSC_ERROR_MSG(1098, ERRDuplicateExport, "Duplicate export of name '%s'")
//1098-1199 available for future use

// Generic errors intended to be re-usable
Expand Down
1 change: 1 addition & 0 deletions lib/Runtime/Base/ThreadConfigFlagsList.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ FLAG_RELEASE(IsESObjectGetOwnPropertyDescriptorsEnabled, ESObjectGetOwnPropertyD
FLAG_RELEASE(IsESSharedArrayBufferEnabled, ESSharedArrayBuffer)
FLAG_RELEASE(IsESDynamicImportEnabled, ESDynamicImport)
FLAG_RELEASE(IsESBigIntEnabled, ESBigInt)
FLAG_RELEASE(IsESExportNsAsEnabled, ESExportNsAs)
#ifdef ENABLE_PROJECTION
FLAG(AreWinRTDelegatesInterfaces, WinRTDelegateInterfaces)
FLAG_RELEASE(IsWinRTAdaptiveAppsEnabled, WinRTAdaptiveApps)
Expand Down
6 changes: 6 additions & 0 deletions lib/Runtime/Language/ModuleNamespace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@ namespace Js
// TODO: maybe we can cache the slot address & offset, instead of looking up everytime? We do need to look up the reference everytime.
if (unambiguousNonLocalExports->TryGetValue(propertyId, &moduleNameRecord))
{
// special case for export * as ns
if (moduleNameRecord.bindingName == Js::PropertyIds::star_)
{
*value = static_cast<Var>(moduleNameRecord.module->GetNamespace());
return PropertyQueryFlags::Property_Found;
}
return JavascriptConversion::BooleanToPropertyQueryFlags(moduleNameRecord.module->GetNamespace()->GetProperty(originalInstance, moduleNameRecord.bindingName, value, info, requestContext));
}
}
Expand Down
38 changes: 21 additions & 17 deletions lib/Runtime/Language/SourceTextModuleRecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -634,22 +634,25 @@ namespace Js
{
JavascriptError::ThrowReferenceError(scriptContext, JSERR_CannotResolveModule, exportEntry.moduleRequest->Psz());
}
else

if (exportEntry.importName->GetPropertyId() == PropertyIds::star_)
{
isAmbiguous = !childModuleRecord->ResolveExport(importNameId, resolveSet, exportRecord);
if (isAmbiguous)
{
// ambiguous; don't need to search further
return true;
}
else
{
// found a resolution. done;
if (*exportRecord != nullptr)
{
return true;
}
}
// export * as someName from "foo"
*exportRecord = childModuleRecord->GetNamespaceNameRecord();
return true;
}

isAmbiguous = !childModuleRecord->ResolveExport(importNameId, resolveSet, exportRecord);
if (isAmbiguous)
{
// ambiguous; don't need to search further
return true;
}

// found a resolution. done;
if (*exportRecord != nullptr)
{
return true;
}
return false;
});
Expand Down Expand Up @@ -1211,8 +1214,9 @@ namespace Js
this->errorObject = errorObj;
return;
}
if (!childModuleRecord->ResolveExport(propertyId, nullptr, &exportRecord) ||
(exportRecord == nullptr))
if (propertyId != PropertyIds::star_ &&
(!childModuleRecord->ResolveExport(propertyId, nullptr, &exportRecord) ||
(exportRecord == nullptr)))
{
JavascriptError* errorObj = scriptContext->GetLibrary()->CreateSyntaxError();
JavascriptError::SetErrorMessage(errorObj, JSERR_ModuleResolveExport, exportEntry.exportName->Psz(), scriptContext);
Expand Down
23 changes: 23 additions & 0 deletions test/es6module/bug_issue_5777.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

// Bug Issue 5777 https://github.com/Microsoft/ChakraCore/issues/5777
// Duplicate export names should cause an early syntax error

WScript.RegisterModuleSource("a.js",
`export const boo = 4;
export {bar as boo} from "b.js";
print ("Should not be printed")`);
WScript.RegisterModuleSource("b.js","export const bar = 5;");

import("a.js").then(()=>{
print("Failed - expected SyntaxError but no error thrown")
}).catch ((e)=>{
if (e instanceof SyntaxError) {
print("pass");
} else {
print (`Failed - threw ${e.constructor.toString()} but should have thrown SyntaxError`);
}
});
Loading

0 comments on commit e198e64

Please sign in to comment.