Issue #2173 - Fix exporting array and object binding patterns

Examples:
  export const [name1, name2] = [1, 2];
  export var {...name} = get;

Based-on: m-c 1446811, m-c 1462286
This commit is contained in:
Martok 2023-03-22 23:46:47 +01:00 committed by roytam1
commit 0b2e8c4818
4 changed files with 204 additions and 13 deletions

View file

@ -1238,14 +1238,25 @@ ModuleBuilder::processExport(frontend::ParseNode* pn)
case PNK_CONST:
case PNK_LET: {
MOZ_ASSERT(kid->isArity(PN_LIST));
for (ParseNode* var = kid->pn_head; var; var = var->pn_next) {
if (var->isKind(PNK_ASSIGN))
var = var->pn_left;
MOZ_ASSERT(var->isKind(PNK_NAME));
RootedAtom localName(cx_, var->pn_atom);
RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get());
if (!appendExportEntry(exportName, localName))
return false;
for (ParseNode* binding = kid->pn_head; binding; binding = binding->pn_next) {
if (binding->isKind(PNK_ASSIGN))
binding = binding->pn_left;
else
MOZ_ASSERT(binding->isKind(PNK_NAME));
if (binding->isKind(PNK_NAME)) {
RootedAtom localName(cx_, binding->pn_atom);
RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get());
if (!appendExportEntry(exportName, localName))
return false;
} else if (binding->isKind(PNK_ARRAY)) {
if (!processExportArrayBinding(binding))
return false;
} else {
MOZ_ASSERT(binding->isKind(PNK_OBJECT));
if (!processExportObjectBinding(binding))
return false;
}
}
break;
}
@ -1268,6 +1279,75 @@ ModuleBuilder::processExport(frontend::ParseNode* pn)
return true;
}
bool
ModuleBuilder::processExportBinding(frontend::ParseNode* binding)
{
if (binding->isKind(PNK_NAME)) {
RootedAtom name(cx_, binding->pn_atom);
return appendExportEntry(name, name);
}
if (binding->isKind(PNK_ARRAY))
return processExportArrayBinding(binding);
MOZ_ASSERT(binding->isKind(PNK_OBJECT));
return processExportObjectBinding(binding);
}
bool
ModuleBuilder::processExportArrayBinding(frontend::ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(PNK_ARRAY));
MOZ_ASSERT(pn->isArity(PN_LIST));
for (ParseNode* node = pn->pn_head; node; node = node->pn_next) {
if (node->isKind(PNK_ELISION))
continue;
if (node->isKind(PNK_SPREAD))
node = node->pn_kid;
else if (node->isKind(PNK_ASSIGN))
node = node->pn_left;
if (!processExportBinding(node))
return false;
}
return true;
}
bool
ModuleBuilder::processExportObjectBinding(frontend::ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(PNK_OBJECT));
MOZ_ASSERT(pn->isArity(PN_LIST));
for (ParseNode* node = pn->pn_head; node; node = node->pn_next) {
MOZ_ASSERT(node->isKind(PNK_MUTATEPROTO) ||
node->isKind(PNK_COLON) ||
node->isKind(PNK_SHORTHAND) ||
node->isKind(PNK_SPREAD));
ParseNode* target;
if (node->isKind(PNK_SPREAD)) {
target = node->pn_kid;
} else {
if (node->isKind(PNK_MUTATEPROTO))
target = node->pn_kid;
else
target = node->pn_right;
if (target->isKind(PNK_ASSIGN))
target = target->pn_left;
}
if (!processExportBinding(target))
return false;
}
return true;
}
bool
ModuleBuilder::processExportFrom(frontend::ParseNode* pn)
{

View file

@ -352,6 +352,10 @@ class MOZ_STACK_CLASS ModuleBuilder
ImportEntryObject* importEntryFor(JSAtom* localName) const;
bool processExportBinding(frontend::ParseNode* pn);
bool processExportArrayBinding(frontend::ParseNode* pn);
bool processExportObjectBinding(frontend::ParseNode* pn);
bool appendExportEntry(HandleAtom exportName, HandleAtom localName,
frontend::ParseNode* node = nullptr);
bool appendExportFromEntry(HandleAtom exportName, HandleAtom moduleRequest,

View file

@ -5189,16 +5189,94 @@ Parser<SyntaxParseHandler>::checkExportedName(JSAtom* exportName)
return false;
}
template<>
bool
Parser<FullParseHandler>::checkExportedNamesForArrayBinding(ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(PNK_ARRAY));
MOZ_ASSERT(pn->isArity(PN_LIST));
for (ParseNode* node = pn->pn_head; node; node = node->pn_next) {
if (node->isKind(PNK_ELISION))
continue;
ParseNode* binding;
if (node->isKind(PNK_SPREAD))
binding = node->pn_kid;
else if (node->isKind(PNK_ASSIGN))
binding = node->pn_left;
else
binding = node;
if (!checkExportedNamesForDeclaration(binding))
return false;
}
return true;
}
template<>
inline bool
Parser<SyntaxParseHandler>::checkExportedNamesForArrayBinding(Node node)
{
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template<>
bool
Parser<FullParseHandler>::checkExportedNamesForObjectBinding(ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(PNK_OBJECT));
MOZ_ASSERT(pn->isArity(PN_LIST));
for (ParseNode* node = pn->pn_head; node; node = node->pn_next) {
MOZ_ASSERT(node->isKind(PNK_MUTATEPROTO) ||
node->isKind(PNK_COLON) ||
node->isKind(PNK_SHORTHAND) ||
node->isKind(PNK_SPREAD));
ParseNode* target;
if (node->isKind(PNK_SPREAD)) {
target = node->pn_kid;
} else {
if (node->isKind(PNK_MUTATEPROTO))
target = node->pn_kid;
else
target = node->pn_right;
if (target->isKind(PNK_ASSIGN))
target = target->pn_left;
}
if (!checkExportedNamesForDeclaration(target))
return false;
}
return true;
}
template<>
inline bool
Parser<SyntaxParseHandler>::checkExportedNamesForObjectBinding(Node node)
{
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template<>
bool
Parser<FullParseHandler>::checkExportedNamesForDeclaration(ParseNode* node)
{
MOZ_ASSERT(node->isArity(PN_LIST));
for (ParseNode* binding = node->pn_head; binding; binding = binding->pn_next) {
if (binding->isKind(PNK_ASSIGN))
binding = binding->pn_left;
MOZ_ASSERT(binding->isKind(PNK_NAME));
if (!checkExportedName(binding->pn_atom))
if (node->isKind(PNK_NAME)) {
if (!checkExportedName(node->pn_atom))
return false;
} else if (node->isKind(PNK_ARRAY)) {
if (!checkExportedNamesForArrayBinding(node))
return false;
} else {
MOZ_ASSERT(node->isKind(PNK_OBJECT));
if (!checkExportedNamesForObjectBinding(node))
return false;
}
@ -5213,6 +5291,32 @@ Parser<SyntaxParseHandler>::checkExportedNamesForDeclaration(Node node)
return false;
}
template<>
bool
Parser<FullParseHandler>::checkExportedNamesForDeclarationList(ParseNode* node)
{
MOZ_ASSERT(node->isArity(PN_LIST));
for (ParseNode* binding = node->pn_head; binding; binding = binding->pn_next) {
if (binding->isKind(PNK_ASSIGN))
binding = binding->pn_left;
else
MOZ_ASSERT(binding->isKind(PNK_NAME));
if (!checkExportedNamesForDeclaration(binding))
return false;
}
return true;
}
template<>
bool
Parser<SyntaxParseHandler>::checkExportedNamesForDeclarationList(Node node)
{
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template<>
bool
Parser<FullParseHandler>::checkExportedNameForClause(ParseNode* node)
@ -5486,7 +5590,7 @@ Parser<ParseHandler>::exportVariableStatement(uint32_t begin)
return null();
if (!matchOrInsertSemicolonAfterExpression())
return null();
if (!checkExportedNamesForDeclaration(kid))
if (!checkExportedNamesForDeclarationList(kid))
return null();
Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
@ -5565,7 +5669,7 @@ Parser<ParseHandler>::exportLexicalDeclaration(uint32_t begin, DeclarationKind k
Node kid = lexicalDeclaration(YieldIsName, kind);
if (!kid)
return null();
if (!checkExportedNamesForDeclaration(kid))
if (!checkExportedNamesForDeclarationList(kid))
return null();
Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));

View file

@ -1383,7 +1383,10 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
bool checkExportedName(JSAtom* exportName);
bool checkExportedNamesForArrayBinding(Node node);
bool checkExportedNamesForObjectBinding(Node node);
bool checkExportedNamesForDeclaration(Node node);
bool checkExportedNamesForDeclarationList(Node node);
bool checkExportedNameForClause(Node node);
bool checkExportedNameForFunction(Node node);