Skip to content

Commit

Permalink
fix for await transform when a label is present
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed May 18, 2024
1 parent efa3dd2 commit b004bbe
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 0 deletions.
60 changes: 60 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,65 @@
# Changelog

## Unreleased

* Fix `for await` transform when a label is present

This release fixes a bug where the `for await` transform, which wraps the loop in a `try` statement, previously failed to also move the loop's label into the `try` statement. This bug only affects code that uses both of these features in combination. Here's an example of some affected code:

```js
// Original code
async function test() {
outer: for await (const x of [Promise.resolve([0, 1])]) {
for (const y of x) if (y) break outer
throw 'fail'
}
}

// Old output (with --target=es6)
function test() {
return __async(this, null, function* () {
outer: try {
for (var iter = __forAwait([Promise.resolve([0, 1])]), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
const x = temp.value;
for (const y of x) if (y) break outer;
throw "fail";
}
} catch (temp) {
error = [temp];
} finally {
try {
more && (temp = iter.return) && (yield temp.call(iter));
} finally {
if (error)
throw error[0];
}
}
});
}

// New output (with --target=es6)
function test() {
return __async(this, null, function* () {
try {
outer: for (var iter = __forAwait([Promise.resolve([0, 1])]), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
const x = temp.value;
for (const y of x) if (y) break outer;
throw "fail";
}
} catch (temp) {
error = [temp];
} finally {
try {
more && (temp = iter.return) && (yield temp.call(iter));
} finally {
if (error)
throw error[0];
}
}
});
}
```

## 0.21.3

* Implement the decorator metadata proposal ([#3760](https://github.com/evanw/esbuild/issues/3760))
Expand Down
4 changes: 4 additions & 0 deletions internal/bundler_tests/bundler_lower_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2337,6 +2337,8 @@ func TestLowerForAwait2017(t *testing.T) {
async () => { for await (x.y of y) z(x) },
async () => { for await (let x of y) z(x) },
async () => { for await (const x of y) z(x) },
async () => { label: for await (const x of y) break label },
async () => { label: for await (const x of y) continue label },
]
`,
},
Expand All @@ -2358,6 +2360,8 @@ func TestLowerForAwait2015(t *testing.T) {
async () => { for await (x.y of y) z(x) },
async () => { for await (let x of y) z(x) },
async () => { for await (const x of y) z(x) },
async () => { label: for await (const x of y) break label },
async () => { label: for await (const x of y) continue label },
]
`,
},
Expand Down
68 changes: 68 additions & 0 deletions internal/bundler_tests/snapshots/snapshots_lower.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2060,6 +2060,40 @@ export default [
throw error[0];
}
}
}),
() => __async(void 0, null, function* () {
try {
label: for (var iter = __forAwait(y), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
const x2 = temp.value;
break label;
}
} catch (temp) {
error = [temp];
} finally {
try {
more && (temp = iter.return) && (yield temp.call(iter));
} finally {
if (error)
throw error[0];
}
}
}),
() => __async(void 0, null, function* () {
try {
label: for (var iter = __forAwait(y), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
const x2 = temp.value;
continue label;
}
} catch (temp) {
error = [temp];
} finally {
try {
more && (temp = iter.return) && (yield temp.call(iter));
} finally {
if (error)
throw error[0];
}
}
})
];

Expand Down Expand Up @@ -2134,6 +2168,40 @@ export default [
throw error[0];
}
}
},
async () => {
try {
label: for (var iter = __forAwait(y), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
const x2 = temp.value;
break label;
}
} catch (temp) {
error = [temp];
} finally {
try {
more && (temp = iter.return) && await temp.call(iter);
} finally {
if (error)
throw error[0];
}
}
},
async () => {
try {
label: for (var iter = __forAwait(y), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
const x2 = temp.value;
continue label;
}
} catch (temp) {
error = [temp];
} finally {
try {
more && (temp = iter.return) && await temp.call(iter);
} finally {
if (error)
throw error[0];
}
}
}
];

Expand Down
12 changes: 12 additions & 0 deletions internal/js_parser/js_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -10284,6 +10284,18 @@ func (p *parser) visitAndAppendStmt(stmts []js_ast.Stmt, stmt js_ast.Stmt) []js_
}
}

// Handle "for await" that has been lowered by moving this label inside the "try"
if try, ok := s.Stmt.Data.(*js_ast.STry); ok && len(try.Block.Stmts) > 0 {
if _, ok := try.Block.Stmts[0].Data.(*js_ast.SFor); ok {
try.Block.Stmts[0] = js_ast.Stmt{Loc: stmt.Loc, Data: &js_ast.SLabel{
Stmt: try.Block.Stmts[0],
Name: s.Name,
IsSingleLineStmt: s.IsSingleLineStmt,
}}
return append(stmts, s.Stmt)
}
}

case *js_ast.SLocal:
// Silently remove unsupported top-level "await" in dead code branches
if s.Kind == js_ast.LocalAwaitUsing && p.fnOrArrowDataVisit.isOutsideFnOrArrow {
Expand Down
17 changes: 17 additions & 0 deletions scripts/end-to-end-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -7149,6 +7149,23 @@ for (let flags of [[], ['--target=es2017'], ['--target=es6']]) {
}
`,
}, { async: true }),

// https://github.com/arogozine/LinqToTypeScript/issues/29
test(['in.js', '--outfile=node.js'].concat(flags), {
'in.js': `
exports.async = async () => {
let total = 0
outer:
for await (const n of [Promise.resolve(1), Promise.resolve(2), Promise.resolve(5)]) {
for (let i = 1; i <= n; i++) {
if (i === 4) continue outer
total += i
}
}
if (total !== 1 + (1 + 2) + (1 + 2 + 3)) throw 'fail'
}
`,
}, { async: true }),
)
}

Expand Down

0 comments on commit b004bbe

Please sign in to comment.