Skip to content

Commit accfb14

Browse files
authored
fix: fix panic when optional chain partially replaced with defines (#3554)
1 parent b76b54f commit accfb14

File tree

3 files changed

+100
-3
lines changed

3 files changed

+100
-3
lines changed

internal/bundler_tests/bundler_default_test.go

+58
Original file line numberDiff line numberDiff line change
@@ -5329,6 +5329,64 @@ func TestDefineOptionalChainLowered(t *testing.T) {
53295329
})
53305330
}
53315331

5332+
// See: https://github.com/evanw/esbuild/issues/3551
5333+
func TestDefineOptionalChainPanicIssue3551(t *testing.T) {
5334+
defines := config.ProcessDefines(map[string]config.DefineData{
5335+
"x": {
5336+
DefineExpr: &config.DefineExpr{
5337+
Constant: &js_ast.EObject{},
5338+
},
5339+
},
5340+
"a.b": {
5341+
DefineExpr: &config.DefineExpr{
5342+
Constant: &js_ast.EObject{},
5343+
},
5344+
},
5345+
})
5346+
default_suite.expectBundled(t, bundled{
5347+
files: map[string]string{
5348+
"/id-define.js": `
5349+
x?.y.z;
5350+
(x?.y).z;
5351+
x?.y["z"];
5352+
(x?.y)["z"];
5353+
x?.y();
5354+
(x?.y)();
5355+
x?.y.z();
5356+
(x?.y).z();
5357+
x?.y["z"]();
5358+
(x?.y)["z"]();
5359+
delete x?.y.z;
5360+
delete (x?.y).z;
5361+
delete x?.y["z"];
5362+
delete (x?.y)["z"];
5363+
`,
5364+
"/dot-define.js": `
5365+
a?.b.c;
5366+
(a?.b).c;
5367+
a?.b["c"];
5368+
(a?.b)["c"];
5369+
a?.b();
5370+
(a?.b)();
5371+
a?.b.c();
5372+
(a?.b).c();
5373+
a?.b["c"]();
5374+
(a?.b)["c"]();
5375+
delete a?.b.c;
5376+
delete (a?.b).c;
5377+
delete a?.b["c"];
5378+
delete (a?.b)["c"];
5379+
`,
5380+
},
5381+
entryPaths: []string{"/id-define.js", "/dot-define.js"},
5382+
options: config.Options{
5383+
Mode: config.ModeBundle,
5384+
AbsOutputDir: "/out",
5385+
Defines: &defines,
5386+
},
5387+
})
5388+
}
5389+
53325390
// See: https://github.com/evanw/esbuild/issues/2407
53335391
func TestDefineInfiniteLoopIssue2407(t *testing.T) {
53345392
defines := config.ProcessDefines(map[string]config.DefineData{

internal/bundler_tests/snapshots/snapshots_default.txt

+36
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,42 @@ console.log([
10811081
(_a = a[b]) == null ? void 0 : _a[c]
10821082
]);
10831083

1084+
================================================================================
1085+
TestDefineOptionalChainPanicIssue3551
1086+
---------- /out/id-define.js ----------
1087+
// id-define.js
1088+
({})?.y.z;
1089+
({}?.y).z;
1090+
({})?.y["z"];
1091+
({}?.y)["z"];
1092+
({})?.y();
1093+
({}?.y)();
1094+
({})?.y.z();
1095+
({}?.y).z();
1096+
({})?.y["z"]();
1097+
({}?.y)["z"]();
1098+
delete {}?.y.z;
1099+
delete ({}?.y).z;
1100+
delete {}?.y["z"];
1101+
delete ({}?.y)["z"];
1102+
1103+
---------- /out/dot-define.js ----------
1104+
// dot-define.js
1105+
({}).c;
1106+
({}).c;
1107+
({})["c"];
1108+
({})["c"];
1109+
({})();
1110+
({})();
1111+
({}).c();
1112+
({}).c();
1113+
({})["c"]();
1114+
({})["c"]();
1115+
delete {}.c;
1116+
delete {}.c;
1117+
delete {}["c"];
1118+
delete {}["c"];
1119+
10841120
================================================================================
10851121
TestDefineThis
10861122
---------- /out.js ----------

internal/js_parser/js_parser.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -13462,7 +13462,8 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1346213462
}
1346313463

1346413464
// Lower optional chaining if we're the top of the chain
13465-
containsOptionalChain := e.OptionalChain != js_ast.OptionalChainNone
13465+
containsOptionalChain := e.OptionalChain == js_ast.OptionalChainStart ||
13466+
(e.OptionalChain == js_ast.OptionalChainContinue && out.childContainsOptionalChain)
1346613467
if containsOptionalChain && !in.hasChainParent {
1346713468
return p.lowerOptionalChain(expr, in, out)
1346813469
}
@@ -13620,7 +13621,8 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1362013621
}
1362113622

1362213623
// Lower optional chaining if we're the top of the chain
13623-
containsOptionalChain := e.OptionalChain != js_ast.OptionalChainNone
13624+
containsOptionalChain := e.OptionalChain == js_ast.OptionalChainStart ||
13625+
(e.OptionalChain == js_ast.OptionalChainContinue && out.childContainsOptionalChain)
1362413626
if containsOptionalChain && !in.hasChainParent {
1362513627
return p.lowerOptionalChain(expr, in, out)
1362613628
}
@@ -14718,7 +14720,8 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1471814720
}
1471914721

1472014722
// Lower optional chaining if we're the top of the chain
14721-
containsOptionalChain := e.OptionalChain != js_ast.OptionalChainNone
14723+
containsOptionalChain := e.OptionalChain == js_ast.OptionalChainStart ||
14724+
(e.OptionalChain == js_ast.OptionalChainContinue && out.childContainsOptionalChain)
1472214725
if containsOptionalChain && !in.hasChainParent {
1472314726
return p.lowerOptionalChain(expr, in, out)
1472414727
}

0 commit comments

Comments
 (0)