Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement export * as ns from module syntax #5779

Merged
merged 1 commit into from
Oct 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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."));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hurray for better error messages!

}

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(""))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I would prefer if new code only used char16, those old Windows-style ALLCAPSTYPENAMES are pretty inscrutable.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These seem to be typedef'd to const char16_t (on xplat at least) but the windows CI failed if I used that here, wanting instead const wchar - not sure if I can use that on xplat.

If I was going to change here to avoid problems I'd probably need to change it up the chain i.e. in Error() and ParseExceptionObject (admittedly I wrote the code in those two places in #5761)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh interesting. this code may be old enough then that it still expects there to be a difference between wchar types. If thats the case, then theres no issue.

{
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");
rhuanjl marked this conversation as resolved.
Show resolved Hide resolved
} else {
print (`Failed - threw ${e.constructor.toString()} but should have thrown SyntaxError`);
}
});
Loading