Repository: evanw/esbuild
File: internal/js_parser/js_parser_lower.go (Go)

Code before:
				// Simple case: the arguments can stay on the outer function. It's
				// worth separating out the simple case because it's the common case
				// and it generates smaller code.
				forwardedArgs = js_ast.Expr{Loc: bodyLoc, Data: &js_ast.ENull{}}
			} else {
				// Complex case: the arguments must be moved to the inner function
				fn.Args = *args
				fn.HasRestArg = *hasRestArg
				*args = nil
				*hasRestArg = false

				// Make sure to not change the value of the "length" property
				for i, arg := range fn.Args {
					if arg.Default != nil || fn.HasRestArg && i+1 == len(fn.Args) {
						// Arguments from here on don't add to the "length"
						break
					}

					// Generate a dummy variable
					argRef := p.newSymbol(js_ast.SymbolOther, fmt.Sprintf("_%d", i))
					p.currentScope.Generated = append(p.currentScope.Generated, argRef)
					*args = append(*args, js_ast.Arg{Binding: js_ast.Binding{Loc: arg.Binding.Loc, Data: &js_ast.BIdentifier{Ref: argRef}}})
				}

				// If we need to forward more than the current number of arguments,
				// add a rest argument to the set of forwarding variables
				if usesArguments || fn.HasRestArg || len(*args) < len(fn.Args) {
					argRef := p.newSymbol(js_ast.SymbolOther, fmt.Sprintf("_%d", len(*args)))
					p.currentScope.Generated = append(p.currentScope.Generated, argRef)
					*args = append(*args, js_ast.Arg{Binding: js_ast.Binding{Loc: bodyLoc, Data: &js_ast.BIdentifier{Ref: argRef}}})
					*hasRestArg = true
				}

				// Forward all of the arguments
				items := make([]js_ast.Expr, 0, len(*args))
				for i, arg := range *args {
					id := arg.Binding.Data.(*js_ast.BIdentifier)
					item := js_ast.Expr{Loc: arg.Binding.Loc, Data: &js_ast.EIdentifier{Ref: id.Ref}}
					if *hasRestArg && i+1 == len(*args) {
						item.Data = &js_ast.ESpread{Value: item}
					}
					items = append(items, item)
				}
				forwardedArgs = js_ast.Expr{Loc: bodyLoc, Data: &js_ast.EArray{Items: items, IsSingleLine: true}}
			}
		}

		// "async function foo(a, b) { stmts }" => "function foo(a, b) { return __async(this, null, function* () { stmts }) }"
		*isAsync = false
		callAsync := p.callRuntime(bodyLoc, "__async", []js_ast.Expr{
			thisValue,

Review comment (bug):
The problem seems to be `argumentsRef` is non-nil if the async arrow function is nested inside a regular function. This code path only triggers when the parameters have a default value (and hence `couldThrowErrors = true`), so both conditions are required for this bug.

Code after:
			if !couldThrowErrors && !usesArguments {
				// Simple case: the arguments can stay on the outer function. It's
				// worth separating out the simple case because it's the common case
				// and it generates smaller code.
				forwardedArgs = js_ast.Expr{Loc: bodyLoc, Data: &js_ast.ENull{}}
			} else {
				// Complex case: the arguments must be moved to the inner function
				fn.Args = *args
				fn.HasRestArg = *hasRestArg
				*args = nil
				*hasRestArg = false

				// Make sure to not change the value of the "length" property
				for i, arg := range fn.Args {
					if arg.Default != nil || fn.HasRestArg && i+1 == len(fn.Args) {
						// Arguments from here on don't add to the "length"
						break
					}

					// Generate a dummy variable
					argRef := p.newSymbol(js_ast.SymbolOther, fmt.Sprintf("_%d", i))
					p.currentScope.Generated = append(p.currentScope.Generated, argRef)
					*args = append(*args, js_ast.Arg{Binding: js_ast.Binding{Loc: arg.Binding.Loc, Data: &js_ast.BIdentifier{Ref: argRef}}})
				}

				// Forward all arguments from the outer function to the inner function
				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 {
					// Arrow functions can't use "arguments", so we need to forward
					// the arguments manually

					// If we need to forward more than the current number of arguments,
					// add a rest argument to the set of forwarding variables
					if usesArguments || fn.HasRestArg || len(*args) < len(fn.Args) {
						argRef := p.newSymbol(js_ast.SymbolOther, fmt.Sprintf("_%d", len(*args)))
						p.currentScope.Generated = append(p.currentScope.Generated, argRef)
						*args = append(*args, js_ast.Arg{Binding: js_ast.Binding{Loc: bodyLoc, Data: &js_ast.BIdentifier{Ref: argRef}}})
						*hasRestArg = true
					}

					// Forward all of the arguments
					items := make([]js_ast.Expr, 0, len(*args))
					for i, arg := range *args {
						id := arg.Binding.Data.(*js_ast.BIdentifier)
						item := js_ast.Expr{Loc: arg.Binding.Loc, Data: &js_ast.EIdentifier{Ref: id.Ref}}
						if *hasRestArg && i+1 == len(*args) {
							item.Data = &js_ast.ESpread{Value: item}
						}
						items = append(items, item)