diff --git a/internal/bundler/snapshots/snapshots_lower.txt b/internal/bundler/snapshots/snapshots_lower.txt index 41637180377..b62aa07d48f 100644 --- a/internal/bundler/snapshots/snapshots_lower.txt +++ b/internal/bundler/snapshots/snapshots_lower.txt @@ -51,7 +51,7 @@ export default [ } }, function() { - return (_0) => __async(this, arguments, function* (bar) { + return (_0, ..._1) => __async(this, [_0, ..._1], function* (bar) { yield bar; return [this, arguments]; }); diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index 4e088809eac..3ad58d57f10 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -8987,7 +8987,7 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO p.pushScopeForVisitPass(js_ast.ScopeFunctionBody, e.Body.Loc) e.Body.Stmts = p.visitStmtsAndPrependTempRefs(e.Body.Stmts) p.popScope() - p.lowerFunction(&e.IsAsync, &e.Args, e.Body.Loc, &e.Body.Stmts, &e.PreferExpr, &e.HasRestArg) + p.lowerFunction(&e.IsAsync, &e.Args, e.Body.Loc, &e.Body.Stmts, &e.PreferExpr, &e.HasRestArg, true) p.popScope() if p.MangleSyntax && len(e.Body.Stmts) == 1 { @@ -9133,7 +9133,7 @@ func (p *parser) visitFn(fn *js_ast.Fn, scopeLoc logger.Loc) { p.pushScopeForVisitPass(js_ast.ScopeFunctionBody, fn.Body.Loc) fn.Body.Stmts = p.visitStmtsAndPrependTempRefs(fn.Body.Stmts) p.popScope() - p.lowerFunction(&fn.IsAsync, &fn.Args, fn.Body.Loc, &fn.Body.Stmts, nil, &fn.HasRestArg) + p.lowerFunction(&fn.IsAsync, &fn.Args, fn.Body.Loc, &fn.Body.Stmts, nil, &fn.HasRestArg, false) p.popScope() p.fnOrArrowDataVisit = oldFnOrArrowData diff --git a/internal/js_parser/js_parser_lower.go b/internal/js_parser/js_parser_lower.go index 6cb09068293..eddf472e93d 100644 --- a/internal/js_parser/js_parser_lower.go +++ b/internal/js_parser/js_parser_lower.go @@ -139,6 +139,7 @@ func (p *parser) lowerFunction( bodyStmts *[]js_ast.Stmt, preferExpr *bool, hasRestArg *bool, + isArrow bool, ) { // Lower object rest binding patterns in function arguments if p.UnsupportedFeatures.Has(compat.ObjectRestSpread) { @@ -228,7 +229,7 @@ func (p *parser) lowerFunction( // resulting promise, which needs more complex code to handle couldThrowErrors := false for _, arg := range *args { - if _, ok := arg.Binding.Data.(*js_ast.BIdentifier); !ok || arg.Default != nil { + if _, ok := arg.Binding.Data.(*js_ast.BIdentifier); !ok || (arg.Default != nil && couldPotentiallyThrow(arg.Default.Data)) { couldThrowErrors = true break } @@ -269,7 +270,7 @@ func (p *parser) lowerFunction( } // Forward all arguments from the outer function to the inner function - if p.fnOnlyDataVisit.argumentsRef != nil { + if !isArrow { // Normal functions can just use "arguments" to forward everything forwardedArgs = js_ast.Expr{Loc: bodyLoc, Data: &js_ast.EIdentifier{Ref: *p.fnOnlyDataVisit.argumentsRef}} } else { @@ -2083,3 +2084,11 @@ func (p *parser) maybeLowerSuperPropertyAccessInsideCall(call *js_ast.ECall) { thisExpr := js_ast.Expr{Loc: call.Target.Loc, Data: &js_ast.EThis{}} call.Args = append([]js_ast.Expr{thisExpr}, call.Args...) } + +func couldPotentiallyThrow(data js_ast.E) bool { + switch data.(type) { + case *js_ast.ENull, *js_ast.EUndefined, *js_ast.EBoolean, *js_ast.ENumber, *js_ast.EBigInt, *js_ast.EString, *js_ast.EFunction, *js_ast.EArrow: + return false + } + return true +} diff --git a/internal/js_parser/js_parser_test.go b/internal/js_parser/js_parser_test.go index 39f2b14b290..48248b6f20f 100644 --- a/internal/js_parser/js_parser_test.go +++ b/internal/js_parser/js_parser_test.go @@ -2655,6 +2655,28 @@ func TestLowerLogicalAssign(t *testing.T) { expectPrintedTarget(t, 2020, "a()[b()] ||= c", "var _a, _b;\n(_a = a())[_b = b()] || (_a[_b] = c);\n") } +func TestLowerAsyncFunctions(t *testing.T) { + // Lowered non-arrow functions with argument evaluations should merely use + // "arguments" rather than allocating a new array when forwarding arguments + expectPrintedTarget(t, 2015, "async function foo(a, b = couldThrowErrors()) {console.log(a, b);}", `function foo(_0) { + return __async(this, arguments, function* (a, b = couldThrowErrors()) { + console.log(a, b); + }); +} +import { + __async +} from ""; +`) + // Skip forwarding altogether when parameter evaluation obviously cannot throw + expectPrintedTarget(t, 2015, "async (a, b = 123) => {console.log(a, b);}", `(a, b = 123) => __async(this, null, function* () { + console.log(a, b); +}); +import { + __async +} from ""; +`) +} + func TestLowerClassSideEffectOrder(t *testing.T) { // The order of computed property side effects must not change expectPrintedTarget(t, 2015, `class Foo { diff --git a/scripts/end-to-end-tests.js b/scripts/end-to-end-tests.js index c41778ccb76..ed1388a1e6c 100644 --- a/scripts/end-to-end-tests.js +++ b/scripts/end-to-end-tests.js @@ -1032,6 +1032,20 @@ in.js:24:30: warning: Writing to getter-only property "#getter" will throw ) `, }, { async: true }), + test(['in.js', '--outfile=node.js', '--target=es6'], + { + 'in.js': ` + function nonArrowWrapper() { + return async (x, paramWithDefault = {}) => { + if (x !== 123) { + throw 'fail'; + } + console.log(paramWithDefault); + }; + } + exports.async = () => nonArrowWrapper()(123); + `, + }, { async: true }), test(['in.js', '--outfile=node.js', '--target=es6'], { 'in.js': ` exports.async = async () => {