Skip to content

Commit f1f413f

Browse files
committed
fix #4252: preserve parentheses around functions
1 parent 1bc8091 commit f1f413f

File tree

9 files changed

+118
-59
lines changed

9 files changed

+118
-59
lines changed

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,35 @@
22

33
## Unreleased
44

5+
* Preserve parentheses around function expressions ([#4252](https://github.com/evanw/esbuild/issues/4252))
6+
7+
The V8 JavaScript VM uses parentheses around function expressions as an optimization hint to immediately compile the function. Otherwise the function would be lazily-compiled, which has additional overhead if that function is always called immediately as lazy compilation involves parsing the function twice. You can read [V8's blog post about this](https://v8.dev/blog/preparser) for more details.
8+
9+
Previously esbuild did not represent parentheses around functions in the AST so they were lost during compilation. With this change, esbuild will now preserve parentheses around function expressions when they are present in the original source code. This means these optimization hints will not be lost when bundling with esbuild. In addition, esbuild will now automatically add this optimization hint to immediately-invoked function expressions. Here's an example:
10+
11+
```js
12+
// Original code
13+
const fn0 = () => 0
14+
const fn1 = (() => 1)
15+
console.log(fn0, function() { return fn1() }())
16+
17+
// Old output
18+
const fn0 = () => 0;
19+
const fn1 = () => 1;
20+
console.log(fn0, function() {
21+
return fn1();
22+
}());
23+
24+
// New output
25+
const fn0 = () => 0;
26+
const fn1 = (() => 1);
27+
console.log(fn0, (function() {
28+
return fn1();
29+
})());
30+
```
31+
32+
Note that you do not want to wrap all function expressions in parentheses. This optimization hint should only be used for functions that are called on initial load. Using this hint for functions that are not called on initial load will unnecessarily delay the initial load. Again, see V8's blog post linked above for details.
33+
534
* Update Go from 1.23.10 to 1.23.12 ([#4257](https://github.com/evanw/esbuild/issues/4257), [#4258](https://github.com/evanw/esbuild/pull/4258))
635

736
This should have no effect on existing code as this version change does not change Go's operating system support. It may remove certain false positive reports (specifically CVE-2025-4674 and CVE-2025-47907) from vulnerability scanners that only detect which version of the Go compiler esbuild uses.

internal/bundler_tests/snapshots/snapshots_default.txt

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -84,31 +84,31 @@ TestArgumentsSpecialCaseNoBundle
8484
var arguments;
8585
return arguments;
8686
} });
87+
((n) => r);
88+
(() => r);
89+
(async () => r);
90+
((n = r) => r);
91+
(async (n = r) => r);
8792
(n) => r;
8893
() => r;
8994
async () => r;
9095
(n = r) => r;
9196
async (n = r) => r;
92-
(n) => r;
93-
() => r;
94-
async () => r;
95-
(n = r) => r;
96-
async (n = r) => r;
97-
(n) => {
97+
((n) => {
9898
return r;
99-
};
100-
() => {
99+
});
100+
(() => {
101101
return r;
102-
};
103-
async () => {
102+
});
103+
(async () => {
104104
return r;
105-
};
106-
(n = r) => {
105+
});
106+
((n = r) => {
107107
return r;
108-
};
109-
async (n = r) => {
108+
});
109+
(async (n = r) => {
110110
return r;
111-
};
111+
});
112112
(n) => {
113113
return r;
114114
};
@@ -131,10 +131,10 @@ TestArrowFnScope
131131
---------- /out.js ----------
132132
// entry.js
133133
tests = {
134-
0: (s = (e) => s + e, t) => s + t,
135-
1: (s, t = (e) => t + e) => t + s,
136-
2: (s = (a = (c) => s + a + c, b) => s + a + b, t, e) => s + t + e,
137-
3: (s, t, e = (a, b = (c) => e + b + c) => e + b + a) => e + s + t,
134+
0: ((s = (e) => s + e, t) => s + t),
135+
1: ((s, t = (e) => t + e) => t + s),
136+
2: ((s = (a = (c) => s + a + c, b) => s + a + b, t, e) => s + t + e),
137+
3: ((s, t, e = (a, b = (c) => e + b + c) => e + b + a) => e + s + t),
138138
4: (x = (s) => x + s, y, x + y),
139139
5: (y, x = (s) => x + s, x + y),
140140
6: (x = (s = (e) => x + s + e, t) => x + s + t, y, z, x + y + z),

internal/bundler_tests/snapshots/snapshots_lower.txt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,7 @@ var baz2_default = class _baz2_default extends x {
998998
};
999999

10001000
// outer.js
1001-
var outer_default = function() {
1001+
var outer_default = (function() {
10021002
return __async(this, null, function* () {
10031003
class y extends z {
10041004
constructor() {
@@ -1010,7 +1010,7 @@ var outer_default = function() {
10101010
}
10111011
yield new y().foo()();
10121012
});
1013-
}();
1013+
})();
10141014
export {
10151015
bar1_default as bar1,
10161016
bar2_default as bar2,
@@ -1126,7 +1126,7 @@ var baz2_default = class _baz2_default extends x {
11261126
};
11271127

11281128
// outer.js
1129-
var outer_default = function() {
1129+
var outer_default = (function() {
11301130
return __async(this, null, function* () {
11311131
class y extends z {
11321132
constructor() {
@@ -1138,7 +1138,7 @@ var outer_default = function() {
11381138
}
11391139
yield new y().foo()();
11401140
});
1141-
}();
1141+
})();
11421142
export {
11431143
bar1_default as bar1,
11441144
bar2_default as bar2,
@@ -3427,7 +3427,7 @@ var baz2_default = class _baz2_default extends x {
34273427
};
34283428

34293429
// outer.js
3430-
var outer_default = function() {
3430+
var outer_default = (function() {
34313431
return __async(this, null, function* () {
34323432
const _y = class _y extends z {
34333433
};
@@ -3437,7 +3437,7 @@ var outer_default = function() {
34373437
let y = _y;
34383438
yield y.foo()();
34393439
});
3440-
}();
3440+
})();
34413441
export {
34423442
bar1_default as bar1,
34433443
bar2_default as bar2,
@@ -3545,7 +3545,7 @@ var baz2_default = class _baz2_default extends x {
35453545
};
35463546

35473547
// outer.js
3548-
var outer_default = function() {
3548+
var outer_default = (function() {
35493549
return __async(this, null, function* () {
35503550
const _y = class _y extends z {
35513551
};
@@ -3555,7 +3555,7 @@ var outer_default = function() {
35553555
let y = _y;
35563556
yield y.foo()();
35573557
});
3558-
}();
3558+
})();
35593559
export {
35603560
bar1_default as bar1,
35613561
bar2_default as bar2,

internal/js_ast/js_ast.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,11 +698,19 @@ type EArrow struct {
698698
HasRestArg bool
699699
PreferExpr bool // Use shorthand if true and "Body" is a single return statement
700700

701+
// V8 uses parentheses as an optimization hint: https://v8.dev/blog/preparser#pife
702+
IsParenthesized bool
703+
701704
// See: https://github.com/rollup/rollup/pull/5024
702705
HasNoSideEffectsComment bool
703706
}
704707

705-
type EFunction struct{ Fn Fn }
708+
type EFunction struct {
709+
Fn Fn
710+
711+
// V8 uses parentheses as an optimization hint: https://v8.dev/blog/preparser#pife
712+
IsParenthesized bool
713+
}
706714

707715
type EClass struct{ Class Class }
708716

internal/js_parser/js_parser.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3505,7 +3505,12 @@ func (p *parser) parsePrefix(level js_ast.L, errors *deferredErrors, flags exprF
35053505
p.allowIn = true
35063506

35073507
value := p.parseExpr(js_ast.LLowest)
3508-
p.markExprAsParenthesized(value, loc, false)
3508+
3509+
// Don't consider the "@(...)" decorator syntax to be important parentheses to preserve
3510+
if (flags & exprFlagDecorator) == 0 {
3511+
p.markExprAsParenthesized(value, loc, false)
3512+
}
3513+
35093514
p.lexer.Expect(js_lexer.TCloseParen)
35103515

35113516
p.allowIn = oldAllowIn
@@ -11670,6 +11675,10 @@ func (p *parser) markExprAsParenthesized(value js_ast.Expr, openParenLoc logger.
1167011675
e.IsParenthesized = true
1167111676
case *js_ast.EObject:
1167211677
e.IsParenthesized = true
11678+
case *js_ast.EFunction:
11679+
e.IsParenthesized = true
11680+
case *js_ast.EArrow:
11681+
e.IsParenthesized = true
1167311682
}
1167411683
}
1167511684

@@ -14987,7 +14996,12 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1498714996
storeThisArgForParentOptionalChain: e.OptionalChain == js_ast.OptionalChainStart || isParenthesizedOptionalChain,
1498814997
})
1498914998
e.Target = target
14990-
p.warnAboutImportNamespaceCall(e.Target, exprKindCall)
14999+
p.warnAboutImportNamespaceCall(target, exprKindCall)
15000+
15001+
// Automatically mark immediately-invoked function expressions for eager compilation
15002+
if fn, ok := target.Data.(*js_ast.EFunction); ok {
15003+
fn.IsParenthesized = true
15004+
}
1499115005

1499215006
hasSpread := false
1499315007
oldIsControlFlowDead := p.isControlFlowDead

internal/js_parser/js_parser_test.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,10 @@ func TestFunction(t *testing.T) {
15801580
expectParseError(t, "switch (0) { case 1: let f; default: function f() {} }", redeclaredError)
15811581
expectParseError(t, "switch (0) { case 1: var f; default: function f() {} }", redeclaredError)
15821582
expectParseError(t, "switch (0) { case 1: var f; default: function *f() {} }", redeclaredError)
1583+
1584+
// Inject parentheses around IIFEs as they are an optimization hint for VMs
1585+
expectPrinted(t, "var x = function() { y() }()", "var x = (function() {\n y();\n})();\n")
1586+
expectPrinted(t, "var x = (true && function() { y() })()", "var x = (function() {\n y();\n})();\n")
15831587
}
15841588

15851589
func TestClass(t *testing.T) {
@@ -2331,12 +2335,12 @@ func TestAsync(t *testing.T) {
23312335
expectParseError(t, "new async => {}", "<stdin>: ERROR: Expected \";\" but found \"=>\"\n")
23322336
expectParseError(t, "new async () => {}", "<stdin>: ERROR: Expected \";\" but found \"=>\"\n")
23332337

2334-
expectPrinted(t, "(async x => y), z", "async (x) => y, z;\n")
2338+
expectPrinted(t, "(async x => y), z", "(async (x) => y), z;\n")
23352339
expectPrinted(t, "(async x => y, z)", "async (x) => y, z;\n")
2336-
expectPrinted(t, "(async x => (y, z))", "async (x) => (y, z);\n")
2337-
expectPrinted(t, "(async (x) => y), z", "async (x) => y, z;\n")
2340+
expectPrinted(t, "(async x => (y, z))", "(async (x) => (y, z));\n")
2341+
expectPrinted(t, "(async (x) => y), z", "(async (x) => y), z;\n")
23382342
expectPrinted(t, "(async (x) => y, z)", "async (x) => y, z;\n")
2339-
expectPrinted(t, "(async (x) => (y, z))", "async (x) => (y, z);\n")
2343+
expectPrinted(t, "(async (x) => (y, z))", "(async (x) => (y, z));\n")
23402344
expectPrinted(t, "async x => y, z", "async (x) => y, z;\n")
23412345
expectPrinted(t, "async x => (y, z)", "async (x) => (y, z);\n")
23422346
expectPrinted(t, "async (x) => y, z", "async (x) => y, z;\n")
@@ -2494,7 +2498,7 @@ func TestArrow(t *testing.T) {
24942498

24952499
expectPrinted(t, "x => function() {}", "(x) => function() {\n};\n")
24962500
expectPrinted(t, "(x) => function() {}", "(x) => function() {\n};\n")
2497-
expectPrinted(t, "(x => function() {})", "(x) => function() {\n};\n")
2501+
expectPrinted(t, "(x => function() {})", "((x) => function() {\n});\n")
24982502

24992503
expectPrinted(t, "(x = () => {}) => {}", "(x = () => {\n}) => {\n};\n")
25002504
expectPrinted(t, "async (x = () => {}) => {}", "async (x = () => {\n}) => {\n};\n")
@@ -4285,7 +4289,7 @@ func TestMangleNullOrUndefinedWithSideEffects(t *testing.T) {
42854289
expectPrintedNormalAndMangle(t, "x('' ?? 1)", "x(\"\");\n", "x(\"\");\n")
42864290
expectPrintedNormalAndMangle(t, "x(/./ ?? 1)", "x(/./);\n", "x(/./);\n")
42874291
expectPrintedNormalAndMangle(t, "x({} ?? 1)", "x({});\n", "x({});\n")
4288-
expectPrintedNormalAndMangle(t, "x((() => {}) ?? 1)", "x(() => {\n});\n", "x(() => {\n});\n")
4292+
expectPrintedNormalAndMangle(t, "x((() => {}) ?? 1)", "x((() => {\n}));\n", "x((() => {\n}));\n")
42894293
expectPrintedNormalAndMangle(t, "x(class {} ?? 1)", "x(class {\n});\n", "x(class {\n});\n")
42904294
expectPrintedNormalAndMangle(t, "x(function() {} ?? 1)", "x(function() {\n});\n", "x(function() {\n});\n")
42914295

@@ -4651,7 +4655,7 @@ func TestMangleIIFE(t *testing.T) {
46514655
expectPrintedNormalAndMangle(t, "(async () => { a() })()", "(async () => {\n a();\n})();\n", "(async () => a())();\n")
46524656
expectPrintedNormalAndMangle(t, "(async () => { let b = a; b() })()", "(async () => {\n let b = a;\n b();\n})();\n", "(async () => a())();\n")
46534657

4654-
expectPrintedNormalAndMangle(t, "var a = (function() {})()", "var a = /* @__PURE__ */ function() {\n}();\n", "var a = /* @__PURE__ */ function() {\n}();\n")
4658+
expectPrintedNormalAndMangle(t, "var a = (function() {})()", "var a = /* @__PURE__ */ (function() {\n})();\n", "var a = /* @__PURE__ */ (function() {\n})();\n")
46554659
expectPrintedNormalAndMangle(t, "(function() {})()", "/* @__PURE__ */ (function() {\n})();\n", "")
46564660
expectPrintedNormalAndMangle(t, "(function*() {})()", "(function* () {\n})();\n", "")
46574661
expectPrintedNormalAndMangle(t, "(async function() {})()", "(async function() {\n})();\n", "")
@@ -4998,7 +5002,7 @@ func TestMangleUnused(t *testing.T) {
49985002
expectPrintedNormalAndMangle(t, "this", "this;\n", "")
49995003
expectPrintedNormalAndMangle(t, "/regex/", "/regex/;\n", "")
50005004
expectPrintedNormalAndMangle(t, "(function() {})", "(function() {\n});\n", "")
5001-
expectPrintedNormalAndMangle(t, "(() => {})", "() => {\n};\n", "")
5005+
expectPrintedNormalAndMangle(t, "(() => {})", "(() => {\n});\n", "")
50025006
expectPrintedNormalAndMangle(t, "import.meta", "import.meta;\n", "")
50035007

50045008
// Unary operators

internal/js_parser/ts_parser_test.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ func TestTSSatisfies(t *testing.T) {
647647
expectPrintedTS(t, "const t2 = { a: 1, b: 1 } satisfies I1;", "const t2 = { a: 1, b: 1 };\n")
648648
expectPrintedTS(t, "const t3 = { } satisfies I1;", "const t3 = {};\n")
649649
expectPrintedTS(t, "const t4: T1 = { a: 'a' } satisfies T1;", "const t4 = { a: \"a\" };\n")
650-
expectPrintedTS(t, "const t5 = (m => m.substring(0)) satisfies T2;", "const t5 = (m) => m.substring(0);\n")
650+
expectPrintedTS(t, "const t5 = (m => m.substring(0)) satisfies T2;", "const t5 = ((m) => m.substring(0));\n")
651651
expectPrintedTS(t, "const t6 = [1, 2] satisfies [number, number];", "const t6 = [1, 2];\n")
652652
expectPrintedTS(t, "let t7 = { a: 'test' } satisfies A;", "let t7 = { a: \"test\" };\n")
653653
expectPrintedTS(t, "let t8 = { a: 'test', b: 'test' } satisfies A;", "let t8 = { a: \"test\", b: \"test\" };\n")
@@ -1688,7 +1688,7 @@ bar = 0 /* FOO */;
16881688
`)
16891689

16901690
// https://github.com/evanw/esbuild/issues/3205
1691-
expectPrintedTS(t, "(() => { const enum Foo { A } () => Foo.A })", `() => {
1691+
expectPrintedTS(t, "() => { const enum Foo { A } () => Foo.A }", `() => {
16921692
let Foo;
16931693
((Foo) => {
16941694
Foo[Foo["A"] = 0] = "A";
@@ -1839,11 +1839,11 @@ func TestTSDecl(t *testing.T) {
18391839
expectParseErrorTS(t, "var a!, b", "<stdin>: ERROR: Expected \":\" but found \",\"\n")
18401840

18411841
expectPrinted(t, "a ? ({b}) => {} : c", "a ? ({ b }) => {\n} : c;\n")
1842-
expectPrinted(t, "a ? (({b}) => {}) : c", "a ? ({ b }) => {\n} : c;\n")
1842+
expectPrinted(t, "a ? (({b}) => {}) : c", "a ? (({ b }) => {\n}) : c;\n")
18431843
expectPrinted(t, "a ? (({b})) : c", "a ? { b } : c;\n")
18441844
expectParseError(t, "a ? (({b})) => {} : c", "<stdin>: ERROR: Invalid binding pattern\n")
18451845
expectPrintedTS(t, "a ? ({b}) => {} : c", "a ? ({ b }) => {\n} : c;\n")
1846-
expectPrintedTS(t, "a ? (({b}) => {}) : c", "a ? ({ b }) => {\n} : c;\n")
1846+
expectPrintedTS(t, "a ? (({b}) => {}) : c", "a ? (({ b }) => {\n}) : c;\n")
18471847
expectPrintedTS(t, "a ? (({b})) : c", "a ? { b } : c;\n")
18481848
expectParseErrorTS(t, "a ? (({b})) => {} : c", "<stdin>: ERROR: Invalid binding pattern\n")
18491849
}
@@ -3150,12 +3150,12 @@ func TestTSJSX(t *testing.T) {
31503150
expectParseErrorTSX(t, "(<T>(x: X<Y>) => {}</Y></T>)", invalidWithHint)
31513151
expectParseErrorTSX(t, "(<T extends>(y) => {}</T>)", invalid)
31523152
expectParseErrorTSX(t, "(<T extends={false}>(y) => {}</T>)", invalid)
3153-
expectPrintedTSX(t, "(<T = X>(y) => {})", "(y) => {\n};\n")
3154-
expectPrintedTSX(t, "(<T extends X>(y) => {})", "(y) => {\n};\n")
3155-
expectPrintedTSX(t, "(<T extends X = Y>(y) => {})", "(y) => {\n};\n")
3156-
expectPrintedTSX(t, "(<T,>() => {})", "() => {\n};\n")
3157-
expectPrintedTSX(t, "(<T, X>(y) => {})", "(y) => {\n};\n")
3158-
expectPrintedTSX(t, "(<T, X>(y): (() => {}) => {})", "(y) => {\n};\n")
3153+
expectPrintedTSX(t, "(<T = X>(y) => {})", "((y) => {\n});\n")
3154+
expectPrintedTSX(t, "(<T extends X>(y) => {})", "((y) => {\n});\n")
3155+
expectPrintedTSX(t, "(<T extends X = Y>(y) => {})", "((y) => {\n});\n")
3156+
expectPrintedTSX(t, "(<T,>() => {})", "(() => {\n});\n")
3157+
expectPrintedTSX(t, "(<T, X>(y) => {})", "((y) => {\n});\n")
3158+
expectPrintedTSX(t, "(<T, X>(y): (() => {}) => {})", "((y) => {\n});\n")
31593159
expectParseErrorTSX(t, "(<T>() => {})", invalidWithHint+"<stdin>: ERROR: Unexpected end of file before a closing \"T\" tag\n<stdin>: NOTE: The opening \"T\" tag is here:\n")
31603160
expectParseErrorTSX(t, "(<T>(x: X<Y>) => {})", invalidWithHint+"<stdin>: ERROR: Unexpected end of file before a closing \"Y\" tag\n<stdin>: NOTE: The opening \"Y\" tag is here:\n")
31613161
expectParseErrorTSX(t, "(<T>(x: X<Y>) => {})</Y>", invalidWithHint+"<stdin>: ERROR: Unexpected end of file before a closing \"T\" tag\n<stdin>: NOTE: The opening \"T\" tag is here:\n")
@@ -3174,9 +3174,9 @@ func TestTSJSX(t *testing.T) {
31743174
}
31753175

31763176
func TestTSNoAmbiguousLessThan(t *testing.T) {
3177-
expectPrintedTSNoAmbiguousLessThan(t, "(<T,>() => {})", "() => {\n};\n")
3178-
expectPrintedTSNoAmbiguousLessThan(t, "(<T, X>() => {})", "() => {\n};\n")
3179-
expectPrintedTSNoAmbiguousLessThan(t, "(<T extends X>() => {})", "() => {\n};\n")
3177+
expectPrintedTSNoAmbiguousLessThan(t, "(<T,>() => {})", "(() => {\n});\n")
3178+
expectPrintedTSNoAmbiguousLessThan(t, "(<T, X>() => {})", "(() => {\n});\n")
3179+
expectPrintedTSNoAmbiguousLessThan(t, "(<T extends X>() => {})", "(() => {\n});\n")
31803180
expectParseErrorTSNoAmbiguousLessThan(t, "(<T>x)",
31813181
"<stdin>: ERROR: This syntax is not allowed in files with the \".mts\" or \".cts\" extension\n")
31823182
expectParseErrorTSNoAmbiguousLessThan(t, "(<T>() => {})",

internal/js_printer/js_printer.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2710,7 +2710,7 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla
27102710
}
27112711

27122712
case *js_ast.EArrow:
2713-
wrap := level >= js_ast.LAssign
2713+
wrap := e.IsParenthesized || level >= js_ast.LAssign
27142714

27152715
if wrap {
27162716
p.print("(")
@@ -2738,8 +2738,12 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla
27382738
wasPrinted := false
27392739
if len(e.Body.Block.Stmts) == 1 && e.PreferExpr {
27402740
if s, ok := e.Body.Block.Stmts[0].Data.(*js_ast.SReturn); ok && s.ValueOrNil.Data != nil {
2741+
var nestedFlags printExprFlags
2742+
if (flags&forbidIn) != 0 && !wrap {
2743+
nestedFlags |= forbidIn
2744+
}
27412745
p.arrowExprStart = len(p.js)
2742-
p.printExprWithoutLeadingNewline(s.ValueOrNil, js_ast.LComma, flags&forbidIn)
2746+
p.printExprWithoutLeadingNewline(s.ValueOrNil, js_ast.LComma, nestedFlags)
27432747
wasPrinted = true
27442748
}
27452749
}
@@ -2752,7 +2756,7 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla
27522756

27532757
case *js_ast.EFunction:
27542758
n := len(p.js)
2755-
wrap := p.stmtStart == n || p.exportDefaultStart == n ||
2759+
wrap := e.IsParenthesized || p.stmtStart == n || p.exportDefaultStart == n ||
27562760
((flags&isPropertyAccessTarget) != 0 && p.options.UnsupportedFeatures.Has(compat.FunctionOrClassPropertyAccess))
27572761
if wrap {
27582762
p.print("(")

0 commit comments

Comments
 (0)