From 0783585e3836d638ad6c8a76a7c80917d09a7653 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Sat, 9 May 2020 09:05:41 -0700 Subject: [PATCH] always dead-code-eliminate optional chains (#28) --- internal/parser/parser.go | 16 ++++++++++++---- internal/parser/parser_test.go | 12 ++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/internal/parser/parser.go b/internal/parser/parser.go index 0f7633cde2d..0e19c9e85cf 100644 --- a/internal/parser/parser.go +++ b/internal/parser/parser.go @@ -7224,6 +7224,7 @@ func (p *parser) lowerOptionalChain(expr ast.Expr, in exprIn, out exprOut, thisA valueWhenUndefined := ast.Expr{expr.Loc, &ast.EUndefined{}} endsWithPropertyAccess := false startsWithCall := false + originalExpr := expr chain := []ast.Expr{} loc := expr.Loc @@ -7275,6 +7276,13 @@ flatten: return valueWhenUndefined, exprOut{} } + // Don't lower this if we don't need to. This check must be done here instead + // of earlier so we can do the dead code elimination above when the target is + // null or undefined. + if p.target >= ES2020 { + return originalExpr, exprOut{} + } + // Step 2: Figure out if we need to capture the value for "this" for the // initial ECall. This will be passed to ".call(this, ...args)" later. var thisArg ast.Expr @@ -7779,7 +7787,7 @@ func (p *parser) visitExprInOut(expr ast.Expr, in exprIn) (ast.Expr, exprOut) { // Lower optional chaining if we're the top of the chain containsOptionalChain := e.IsOptionalChain || out.childContainsOptionalChain isEndOfChain := e.IsParenthesized || !in.hasChainParent - if p.target < ES2020 && containsOptionalChain && isEndOfChain { + if containsOptionalChain && isEndOfChain { return p.lowerOptionalChain(expr, in, out, nil) } @@ -7824,7 +7832,7 @@ func (p *parser) visitExprInOut(expr ast.Expr, in exprIn) (ast.Expr, exprOut) { // Lower optional chaining if present since we're guaranteed to be the // end of the chain - if p.target < ES2020 && out.childContainsOptionalChain { + if out.childContainsOptionalChain { return p.lowerOptionalChain(expr, in, out, nil) } @@ -7879,7 +7887,7 @@ func (p *parser) visitExprInOut(expr ast.Expr, in exprIn) (ast.Expr, exprOut) { // Lower optional chaining if we're the top of the chain containsOptionalChain := e.IsOptionalChain || out.childContainsOptionalChain isEndOfChain := e.IsParenthesized || !in.hasChainParent - if p.target < ES2020 && containsOptionalChain && isEndOfChain { + if containsOptionalChain && isEndOfChain { return p.lowerOptionalChain(expr, in, out, nil) } @@ -8012,7 +8020,7 @@ func (p *parser) visitExprInOut(expr ast.Expr, in exprIn) (ast.Expr, exprOut) { // Lower optional chaining if we're the top of the chain containsOptionalChain := e.IsOptionalChain || out.childContainsOptionalChain isEndOfChain := e.IsParenthesized || !in.hasChainParent - if p.target < ES2020 && containsOptionalChain && isEndOfChain { + if containsOptionalChain && isEndOfChain { result, out := p.lowerOptionalChain(expr, in, out, thisArgFunc) if thisArgWrapFunc != nil { result = thisArgWrapFunc(result) diff --git a/internal/parser/parser_test.go b/internal/parser/parser_test.go index bc7869c11b8..2d206768307 100644 --- a/internal/parser/parser_test.go +++ b/internal/parser/parser_test.go @@ -1477,13 +1477,13 @@ func TestLowerOptionalChain(t *testing.T) { expectPrintedTarget(t, ES2020, "x?.[y]", "x?.[y];\n") expectPrintedTarget(t, ES2020, "x?.(y)", "x?.(y);\n") - expectPrintedTarget(t, ES2020, "null?.x", "null?.x;\n") - expectPrintedTarget(t, ES2020, "null?.[x]", "null?.[x];\n") - expectPrintedTarget(t, ES2020, "null?.(x)", "null?.(x);\n") + expectPrintedTarget(t, ES2020, "null?.x", "void 0;\n") + expectPrintedTarget(t, ES2020, "null?.[x]", "void 0;\n") + expectPrintedTarget(t, ES2020, "null?.(x)", "void 0;\n") - expectPrintedTarget(t, ES2020, "undefined?.x", "(void 0)?.x;\n") - expectPrintedTarget(t, ES2020, "undefined?.[x]", "(void 0)?.[x];\n") - expectPrintedTarget(t, ES2020, "undefined?.(x)", "(void 0)?.(x);\n") + expectPrintedTarget(t, ES2020, "undefined?.x", "void 0;\n") + expectPrintedTarget(t, ES2020, "undefined?.[x]", "void 0;\n") + expectPrintedTarget(t, ES2020, "undefined?.(x)", "void 0;\n") // Check multiple levels of nesting expectPrintedTarget(t, ES2019, "a?.b?.c?.d", `var _a, _b;