Skip to content

Commit 320dbae

Browse files
Joonhyung Hwangclaude
authored andcommitted
Fix export name leak from nested function bodies
`export const a = t => { const o = ...; }` registered the arrow-local `o` as an exported name: the nameDeclaredCallback installed while parsing an exported declaration stayed active while nested function bodies were parsed. A second exported arrow declaring `o` then failed the whole module with "duplicate export name 'o'" -- make-plural's cardinals module (which openstreetmap.org loads for localization) is exactly this shape. A single occurrence did not collide, so the module parsed but still leaked the body-local into the namespace (Object.keys gained the extra name); the duplicate-name parse error was just the loud case. Both nested-body parse paths saved the callback but never cleared it (the expression-body arrow path didn't even restore its dead saved copy); null it during the nested parse and restore afterwards. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Joonhyung Hwang <jh1984.hwang@samsung.com>
1 parent 1130c0f commit 320dbae

2 files changed

Lines changed: 12 additions & 1 deletion

File tree

src/parser/esprima_cpp/esprima.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3407,12 +3407,18 @@ class Parser {
34073407
if (this->match(LeftBrace)) {
34083408
this->parseFunctionSourceElements(newBuilder);
34093409
} else {
3410+
// names declared while parsing the arrow body belong to
3411+
// the arrow's scope; they must not reach an outer
3412+
// collector (e.g. `export const a = () => ...` collecting
3413+
// exported names)
34103414
auto oldNameCallback = this->nameDeclaredCallback;
3415+
this->nameDeclaredCallback = nullptr;
34113416
#if defined(ESCARGOT_SMALL_CONFIG)
34123417
this->isolateCoverGrammar(newBuilder, &Parser::parseAssignmentExpression<ASTBuilder, false>);
34133418
#else
34143419
this->isolateCoverGrammar(newBuilder, &Parser::parseAssignmentExpression<SyntaxChecker, false>);
34153420
#endif
3421+
this->nameDeclaredCallback = oldNameCallback;
34163422

34173423
this->currentScopeContext->m_bodyEndLOC.index = this->lastMarker.index;
34183424
#if !(defined NDEBUG) || defined ESCARGOT_DEBUGGER
@@ -5224,6 +5230,11 @@ class Parser {
52245230

52255231
bool oldAllowLexicalDeclaration = this->context->allowLexicalDeclaration;
52265232
auto oldNameCallback = this->nameDeclaredCallback;
5233+
// names declared inside a nested function body belong to that
5234+
// function; without this, `export const a = t => { const o = ...; }`
5235+
// reported the arrow-local `o` as an exported name ("duplicate
5236+
// export name" once a second arrow declared `o` too)
5237+
this->nameDeclaredCallback = nullptr;
52275238
this->context->allowLexicalDeclaration = true;
52285239

52295240
bool oldInCatchClause = this->context->inCatchClause;

0 commit comments

Comments
 (0)