Skip to content

Commit

Permalink
implement late constant-folding of string enums
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed May 24, 2024
1 parent 1b29ac7 commit 7d50a50
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 5 deletions.
88 changes: 88 additions & 0 deletions internal/bundler_tests/bundler_dce_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3260,6 +3260,94 @@ func TestCrossModuleConstantFoldingNumber(t *testing.T) {
})
}

func TestCrossModuleConstantFoldingString(t *testing.T) {
dce_suite.expectBundled(t, bundled{
files: map[string]string{
"/enum-constants.ts": `
export enum x {
a = 'foo',
b = 'bar',
}
`,
"/enum-entry.ts": `
import { x } from './enum-constants'
console.log([
typeof x.b,
], [
x.a + x.b,
], [
x.a < x.b,
x.a > x.b,
x.a <= x.b,
x.a >= x.b,
x.a == x.b,
x.a != x.b,
x.a === x.b,
x.a !== x.b,
], [
x.a && x.b,
x.a || x.b,
x.a ?? x.b,
])
`,

"/const-constants.js": `
export const a = 'foo'
export const b = 'bar'
`,
"/const-entry.js": `
import { a, b } from './const-constants'
console.log([
typeof b,
], [
a + b,
], [
a < b,
a > b,
a <= b,
a >= b,
a == b,
a != b,
a === b,
a !== b,
], [
a && b,
a || b,
a ?? b,
])
`,

"/nested-constants.ts": `
export const a = 'foo'
export const b = 'bar'
export const c = 'baz'
export enum x {
a = 'FOO',
b = 'BAR',
c = 'BAZ',
}
`,
"/nested-entry.ts": `
import { a, b, c, x } from './nested-constants'
console.log({
'should be foobarbaz': a + b + c,
'should be FOOBARBAZ': x.a + x.b + x.c,
})
`,
},
entryPaths: []string{
"/enum-entry.ts",
"/const-entry.js",
"/nested-entry.ts",
},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputDir: "/out",
MinifySyntax: true,
},
})
}

func TestMultipleDeclarationTreeShaking(t *testing.T) {
dce_suite.expectBundled(t, bundled{
files: map[string]string{
Expand Down
57 changes: 57 additions & 0 deletions internal/bundler_tests/snapshots/snapshots_dce.txt
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,63 @@ console.log({
"should be 32": 32
});

================================================================================
TestCrossModuleConstantFoldingString
---------- /out/enum-entry.js ----------
// enum-entry.ts
console.log([
typeof "bar" /* b */
], [
"foobar"
], [
!1,
!0,
!1,
!0,
!1,
!0,
!1,
!0
], [
"foo" /* a */ && "bar" /* b */,
"foo" /* a */ || "bar" /* b */,
"foo" /* a */ ?? "bar" /* b */
]);

---------- /out/const-entry.js ----------
// const-constants.js
var a = "foo", b = "bar";

// const-entry.js
console.log([
typeof b
], [
a + b
], [
a < b,
a > b,
a <= b,
a >= b,
a == b,
a != b,
a === b,
a !== b
], [
a && b,
a || b,
a ?? b
]);

---------- /out/nested-entry.js ----------
// nested-constants.ts
var a = "foo", b = "bar", c = "baz";

// nested-entry.ts
console.log({
"should be foobarbaz": a + b + c,
"should be FOOBARBAZ": "FOOBARBAZ"
});

================================================================================
TestDCEClassStaticBlocks
---------- /out.js ----------
Expand Down
8 changes: 8 additions & 0 deletions internal/js_ast/js_ast_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,11 @@ func ShouldFoldBinaryOperatorWhenMinifying(binary *EBinary) bool {
return true
}

// String addition should pretty much always be more compact when folded
if _, _, ok := extractStringValues(binary.Left, binary.Right); ok {
return true
}

case BinOpSub:
// Subtraction of small-ish integers can definitely be folded without issues
// "3 - 1" => "2"
Expand Down Expand Up @@ -1215,6 +1220,9 @@ func FoldBinaryOperator(loc logger.Loc, e *EBinary) Expr {
if left, right, ok := extractNumericValues(e.Left, e.Right); ok {
return Expr{Loc: loc, Data: &ENumber{Value: left + right}}
}
if left, right, ok := extractStringValues(e.Left, e.Right); ok {
return Expr{Loc: loc, Data: &EString{Value: joinStrings(left, right)}}
}

case BinOpSub:
if left, right, ok := extractNumericValues(e.Left, e.Right); ok {
Expand Down
15 changes: 10 additions & 5 deletions internal/js_printer/js_printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -1758,17 +1758,22 @@ func (p *printer) lateConstantFoldUnaryOrBinaryExpr(expr js_ast.Expr) js_ast.Exp
}

case *js_ast.EDot:
if value, ok := p.tryToGetImportedEnumValue(e.Target, e.Name); ok && value.String == nil {
value := js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: value.Number}}
if value, ok := p.tryToGetImportedEnumValue(e.Target, e.Name); ok {
var inlinedValue js_ast.Expr
if value.String != nil {
inlinedValue = js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: value.String}}
} else {
inlinedValue = js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: value.Number}}
}

if strings.Contains(e.Name, "*/") {
// Don't wrap with a comment
return value
return inlinedValue
}

// Wrap with a comment
return js_ast.Expr{Loc: value.Loc, Data: &js_ast.EInlinedEnum{
Value: value,
return js_ast.Expr{Loc: inlinedValue.Loc, Data: &js_ast.EInlinedEnum{
Value: inlinedValue,
Comment: e.Name,
}}
}
Expand Down

0 comments on commit 7d50a50

Please sign in to comment.