Skip to content

Commit 730df4f

Browse files
authored
Drop superfluous __name() calls (#2062)
1 parent ea3b3d3 commit 730df4f

File tree

7 files changed

+269
-49
lines changed

7 files changed

+269
-49
lines changed

CHANGELOG.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,48 @@
5656

5757
Some JavaScript environments such as Cloudflare Workers or Deno Deploy don't allow `new Function` because they disallow dynamic JavaScript evaluation. Previously esbuild's WebAssembly-based library used this to construct the WebAssembly worker function. With this release, the code is now inlined without using `new Function` so it will be able to run even when this restriction is in place.
5858

59+
* Drop superfluous `__name()` calls ([#2062](https://github.com/evanw/esbuild/pull/2062))
60+
61+
When the `--keep-names` option is specified, esbuild inserts calls to a `__name` helper function to ensure that the `.name` property on function and class objects remains consistent even if the function or class name is renamed to avoid a name collision or because name minification is enabled. With this release, esbuild will now try to omit these calls to the `__name` helper function when the name of the function or class object was not renamed during the linking process after all:
62+
63+
```js
64+
// Original code
65+
import { foo as foo1 } from 'data:text/javascript,export function foo() { return "foo1" }'
66+
import { foo as foo2 } from 'data:text/javascript,export function foo() { return "foo2" }'
67+
console.log(foo1.name, foo2.name)
68+
69+
// Old output (with --bundle --keep-names)
70+
(() => {
71+
var __defProp = Object.defineProperty;
72+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
73+
function foo() {
74+
return "foo1";
75+
}
76+
__name(foo, "foo");
77+
function foo2() {
78+
return "foo2";
79+
}
80+
__name(foo2, "foo");
81+
console.log(foo.name, foo2.name);
82+
})();
83+
84+
// New output (with --bundle --keep-names)
85+
(() => {
86+
var __defProp = Object.defineProperty;
87+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
88+
function foo() {
89+
return "foo1";
90+
}
91+
function foo2() {
92+
return "foo2";
93+
}
94+
__name(foo2, "foo");
95+
console.log(foo.name, foo2.name);
96+
})();
97+
```
98+
99+
Notice how one of the calls to `__name` is now no longer printed. This change was contributed by [@indutny](https://github.com/indutny).
100+
59101
## 0.14.25
60102

61103
* Reduce minification of CSS transforms to avoid Safari bugs ([#2057](https://github.com/evanw/esbuild/issues/2057))

internal/bundler/bundler_default_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4358,6 +4358,71 @@ func TestDefineThis(t *testing.T) {
43584358
})
43594359
}
43604360

4361+
func TestKeepNamesWithNecessaryHelperFunctionCalls(t *testing.T) {
4362+
default_suite.expectBundled(t, bundled{
4363+
files: map[string]string{
4364+
"/entry.js": `
4365+
import {
4366+
functionStmt as functionStmt1,
4367+
functionExpr as functionExpr1,
4368+
classStmt as classStmt1,
4369+
classExpr as classExpr1,
4370+
functionAnonExpr as functionAnonExpr1,
4371+
classAnonExpr as classAnonExpr1,
4372+
} from './copy1'
4373+
4374+
import {
4375+
functionStmt as functionStmt2,
4376+
functionExpr as functionExpr2,
4377+
classStmt as classStmt2,
4378+
classExpr as classExpr2,
4379+
functionAnonExpr as functionAnonExpr2,
4380+
classAnonExpr as classAnonExpr2,
4381+
} from './copy2'
4382+
4383+
console.log([
4384+
functionStmt1, functionStmt2,
4385+
functionExpr1, functionExpr2,
4386+
classStmt1, classStmt2,
4387+
classExpr1, classExpr2,
4388+
functionAnonExpr1, functionAnonExpr2,
4389+
classAnonExpr1, classAnonExpr2,
4390+
])
4391+
`,
4392+
4393+
"/copy1.js": `
4394+
export function functionStmt() { return 'copy1' }
4395+
export class classStmt { foo = 'copy1' }
4396+
export let functionExpr = function fn() { return 'copy1' }
4397+
export let classExpr = class cls { foo = 'copy1' }
4398+
export let functionAnonExpr = function() { return 'copy1' }
4399+
export let classAnonExpr = class { foo = 'copy1' }
4400+
4401+
class classStmtSideEffect { static [copy1]() {} }
4402+
let classExprSideEffect = class clsSideEffect { static [copy1]() {} }
4403+
`,
4404+
4405+
"/copy2.js": `
4406+
export function functionStmt() { return 'copy2' }
4407+
export class classStmt { foo = 'copy2' }
4408+
export let functionExpr = function fn() { return 'copy2' }
4409+
export let classExpr = class cls { foo = 'copy2' }
4410+
export let functionAnonExpr = function() { return 'copy2' }
4411+
export let classAnonExpr = class { foo = 'copy2' }
4412+
4413+
class classStmtSideEffect { static [copy2]() {} }
4414+
let classExprSideEffect = class clsSideEffect { static [copy2]() {} }
4415+
`,
4416+
},
4417+
entryPaths: []string{"/entry.js"},
4418+
options: config.Options{
4419+
Mode: config.ModeBundle,
4420+
AbsOutputFile: "/out.js",
4421+
KeepNames: true,
4422+
},
4423+
})
4424+
}
4425+
43614426
func TestKeepNamesTreeShaking(t *testing.T) {
43624427
default_suite.expectBundled(t, bundled{
43634428
files: map[string]string{

internal/bundler/snapshots/snapshots_default.txt

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,19 +1401,95 @@ TestKeepNamesTreeShaking
14011401
// entry.js
14021402
function fnStmtKeep() {
14031403
}
1404-
__name(fnStmtKeep, "fnStmtKeep");
14051404
x = fnStmtKeep;
14061405
var fnExprKeep = /* @__PURE__ */ __name(function() {
14071406
}, "keep");
14081407
x = fnExprKeep;
14091408
var clsStmtKeep = class {
14101409
};
1411-
__name(clsStmtKeep, "clsStmtKeep");
14121410
new clsStmtKeep();
14131411
var clsExprKeep = /* @__PURE__ */ __name(class {
14141412
}, "keep");
14151413
new clsExprKeep();
14161414

1415+
================================================================================
1416+
TestKeepNamesWithNecessaryHelperFunctionCalls
1417+
---------- /out.js ----------
1418+
// copy1.js
1419+
function functionStmt() {
1420+
return "copy1";
1421+
}
1422+
var classStmt = class {
1423+
foo = "copy1";
1424+
};
1425+
var functionExpr = function fn() {
1426+
return "copy1";
1427+
};
1428+
var classExpr = class cls {
1429+
foo = "copy1";
1430+
};
1431+
var functionAnonExpr = function() {
1432+
return "copy1";
1433+
};
1434+
var classAnonExpr = class {
1435+
foo = "copy1";
1436+
};
1437+
var classStmtSideEffect = class {
1438+
static [copy1]() {
1439+
}
1440+
};
1441+
var classExprSideEffect = class clsSideEffect {
1442+
static [copy1]() {
1443+
}
1444+
};
1445+
1446+
// copy2.js
1447+
function functionStmt2() {
1448+
return "copy2";
1449+
}
1450+
__name(functionStmt2, "functionStmt");
1451+
var classStmt2 = class {
1452+
foo = "copy2";
1453+
};
1454+
__name(classStmt2, "classStmt");
1455+
var functionExpr2 = /* @__PURE__ */ __name(function fn2() {
1456+
return "copy2";
1457+
}, "fn");
1458+
var classExpr2 = /* @__PURE__ */ __name(class cls2 {
1459+
foo = "copy2";
1460+
}, "cls");
1461+
var functionAnonExpr2 = /* @__PURE__ */ __name(function() {
1462+
return "copy2";
1463+
}, "functionAnonExpr");
1464+
var classAnonExpr2 = /* @__PURE__ */ __name(class {
1465+
foo = "copy2";
1466+
}, "classAnonExpr");
1467+
var classStmtSideEffect2 = class {
1468+
static [copy2]() {
1469+
}
1470+
};
1471+
__name(classStmtSideEffect2, "classStmtSideEffect");
1472+
var classExprSideEffect2 = /* @__PURE__ */ __name(class clsSideEffect2 {
1473+
static [copy2]() {
1474+
}
1475+
}, "clsSideEffect");
1476+
1477+
// entry.js
1478+
console.log([
1479+
functionStmt,
1480+
functionStmt2,
1481+
functionExpr,
1482+
functionExpr2,
1483+
classStmt,
1484+
classStmt2,
1485+
classExpr,
1486+
classExpr2,
1487+
functionAnonExpr,
1488+
functionAnonExpr2,
1489+
classAnonExpr,
1490+
classAnonExpr2
1491+
]);
1492+
14171493
================================================================================
14181494
TestLegalCommentsAvoidSlashTagEndOfFile
14191495
---------- /out/entry.js ----------
@@ -2810,12 +2886,11 @@ export function outer() {
28102886
let inner = function() {
28112887
return Math.random();
28122888
};
2813-
__name(inner, "inner");
28142889
const x = inner();
28152890
console.log(x);
28162891
}
28172892
}
2818-
__name(outer, "outer"), outer();
2893+
outer();
28192894

28202895
================================================================================
28212896
TestSwitchScopeNoBundle

internal/bundler/snapshots/snapshots_ts.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1102,7 +1102,6 @@ TestTypeScriptDecoratorsKeepNames
11021102
// entry.ts
11031103
var Foo = class {
11041104
};
1105-
__name(Foo, "Foo");
11061105
Foo = __decorateClass([
11071106
decoratorMustComeAfterName
11081107
], Foo);

internal/js_ast/js_ast.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,8 @@ type ECall struct {
516516
// call itself is removed due to this annotation, the arguments must remain
517517
// if they have side effects.
518518
CanBeUnwrappedIfUnused bool
519+
520+
IsKeepName bool
519521
}
520522

521523
func (a *ECall) HasSameFlagsAs(b *ECall) bool {
@@ -848,11 +850,6 @@ type SLazyExport struct {
848850

849851
type SExpr struct {
850852
Value Expr
851-
852-
// This is set to true for automatically-generated expressions that should
853-
// not affect tree shaking. For example, calling a function from the runtime
854-
// that doesn't have externally-visible side effects.
855-
DoesNotAffectTreeShaking bool
856853
}
857854

858855
type EnumValue struct {

internal/js_parser/js_parser.go

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7512,7 +7512,6 @@ func (p *parser) mangleStmts(stmts []js_ast.Stmt, kind stmtsKind) []js_ast.Stmt
75127512
prevStmt := result[len(result)-1]
75137513
if prevS, ok := prevStmt.Data.(*js_ast.SExpr); ok && !js_ast.IsSuperCall(prevStmt) && !js_ast.IsSuperCall(stmt) {
75147514
prevS.Value = js_ast.JoinWithComma(prevS.Value, s.Value)
7515-
prevS.DoesNotAffectTreeShaking = prevS.DoesNotAffectTreeShaking && s.DoesNotAffectTreeShaking
75167515
continue
75177516
}
75187517
}
@@ -8758,21 +8757,20 @@ func (p *parser) keepExprSymbolName(value js_ast.Expr, name string) js_ast.Expr
87588757

87598758
// Make sure tree shaking removes this if the function is never used
87608759
value.Data.(*js_ast.ECall).CanBeUnwrappedIfUnused = true
8760+
value.Data.(*js_ast.ECall).IsKeepName = true
87618761
return value
87628762
}
87638763

87648764
func (p *parser) keepStmtSymbolName(loc logger.Loc, ref js_ast.Ref, name string) js_ast.Stmt {
87658765
p.symbols[ref.InnerIndex].Flags |= js_ast.DidKeepName
87668766

8767-
return js_ast.Stmt{Loc: loc, Data: &js_ast.SExpr{
8768-
Value: p.callRuntime(loc, "__name", []js_ast.Expr{
8769-
{Loc: loc, Data: &js_ast.EIdentifier{Ref: ref}},
8770-
{Loc: loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(name)}},
8771-
}),
8767+
call := p.callRuntime(loc, "__name", []js_ast.Expr{
8768+
{Loc: loc, Data: &js_ast.EIdentifier{Ref: ref}},
8769+
{Loc: loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(name)}},
8770+
})
8771+
call.Data.(*js_ast.ECall).IsKeepName = true
87728772

8773-
// Make sure tree shaking removes this if the function is never used
8774-
DoesNotAffectTreeShaking: true,
8775-
}}
8773+
return js_ast.Stmt{Loc: loc, Data: &js_ast.SExpr{Value: call}}
87768774
}
87778775

87788776
func (p *parser) visitAndAppendStmt(stmts []js_ast.Stmt, stmt js_ast.Stmt) []js_ast.Stmt {
@@ -14285,12 +14283,6 @@ func (p *parser) stmtsCanBeRemovedIfUnused(stmts []js_ast.Stmt) bool {
1428514283
}
1428614284

1428714285
case *js_ast.SExpr:
14288-
if s.DoesNotAffectTreeShaking {
14289-
// Expressions marked with this are automatically generated and have
14290-
// no side effects by construction.
14291-
break
14292-
}
14293-
1429414286
if !p.exprCanBeRemovedIfUnused(s.Value) {
1429514287
return false
1429614288
}
@@ -14464,7 +14456,7 @@ func (p *parser) exprCanBeRemovedIfUnused(expr js_ast.Expr) bool {
1446414456
return true
1446514457

1446614458
case *js_ast.ECall:
14467-
canCallBeRemoved := e.CanBeUnwrappedIfUnused
14459+
canCallBeRemoved := e.CanBeUnwrappedIfUnused || e.IsKeepName
1446814460

1446914461
// Consider calls to our runtime "__publicField" function to be free of
1447014462
// side effects for the purpose of expression removal. This allows class

0 commit comments

Comments
 (0)