You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit ba0b3db
Browse filesBrowse the repository at this point in the historyBrowse files
Babel preset: Remove regenerator from Hermes dev builds (#59303)
Summary:
## Motivation
- Dev/release consistency in correctness and performance shape.
- ~10% faster dev cold builds
- More responsive dev apps (~2x faster promise handling)
- Improved debugging support
Since #52651, Hermes profiles have lowered generators (with `enableBabelRuntime: false`), `for...of`, `?.`, `??` and optional catch binding in dev builds only, because Hermes couldn't show local variables when paused on a breakpoint in a native generator.
That turned out to be a Hermes bug: a breakpoint on that a `yield` line resolved to scope-less entry code before the body had even run. It was fixed in facebook/hermes@d1238c8 (hermes-v260318099.0.2), which main has used since #58397.
This removes `enableRegenerator`, so Hermes-profile dev output matches release. It also fixes a dev-only divergence - loose `for-of` never calls `iterator.return()`, so breaking out of a `for...of` over a generator skipped its `finally` in dev but not release.
Note that with the default `enableBabelRuntime`, generators were already native in dev, so in practice the generator change mostly affects `enableBabelRuntime: false` setups, including RN's own Jest. (There's no good reason generator was ever conditional on `enableBabelRuntime === false` - it seems to date from #44668 and probably a misunderstanding of what the `regenerator` argument to `babel/plugin-transform-runtime` did - it doesn't make that plugin transform generators, it just rewrites the helper import.)
Native generator debugging still isn't perfect on .0.2 - stepping through `try`/`catch` can stop on a `catch` line that doesn't run, and breakpoints on `yield*` lines or on a `throw` after a resume don't fire. Those already affect every default config though, so there's no regression in most cases. Regenerator has its own problems, eg breakpoints often stop a line early because statements merge into its `case` blocks. Overall, native is now a better experience.
Detailed comparison in https://robhogan.github.io/reports/react-native-regenerator-removal/
## Changelog
[GENERAL] [FIXED] - Babel preset: Hermes profiles no longer lower generators, `for...of`, optional chaining, nullish coalescing or optional catch binding in dev, matching release
Pull Request resolved: #59303
Test Plan:
Reproduced the original bug through Hermes's `CDPAgent` test harness with `Debugger.setBreakpointByUrl`. On hermes-v250829098.0.16 and v260318099.0.1, a breakpoint on a generator's last `yield` line pauses at the generator call with only the global scope, and every local evaluates to `undefined`. On v260318099.0.2 and .0.4 it pauses at the real code with all locals.
AI-driven breakpoint sweep: this preset with `dev: true, enableBabelRuntime: false`, before and after this change, over 248 source lines of generator, async function, async generator, exception and loop fixtures. A breakpoint was set on every line, each pause was mapped back through the source map, and every local was evaluated. Same results with eager and lazy compilation.
| Hermes | Identical | Different, equivalent | Native better | Native worse | Mixed (loop headers) | Total |
| --- | --- | --- | --- | --- | --- | --- |
| 250829098.0.16 | 164 | 10 | 49 | 22 | 3 | 248 |
| 260318099.0.2 | 183 | 11 | 47 | 3 | 3 | 247 |
| 260318099.0.4 | 184 | 11 | 47 | 3 | 3 | 248 |
Reviewed By: christophpurrer
Differential Revision: D126904757
Pulled By: javache
fbshipit-source-id: f2d492ec6c326b9ec8b8101832c85b921836f5ce
function_createForOfIteratorHelperLoose(r,e){vart="undefined"!=typeofSymbol&&r[Symbol.iterator]||r["@@iterator"];if(t)return(t=t.call(r)).next.bind(t);if(Array.isArray(r)||(t=_unsupportedIterableToArray(r))||e&&r&&"number"==typeofr.length){t&&(r=t);varo=0;returnfunction(){returno>=r.length ? {done: !0} : {done: !1,value: r[o++]};};}thrownewTypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}
function_createForOfIteratorHelperLoose(r,e){vart="undefined"!=typeofSymbol&&r[Symbol.iterator]||r["@@iterator"];if(t)return(t=t.call(r)).next.bind(t);if(Array.isArray(r)||(t=_unsupportedIterableToArray(r))||e&&r&&"number"==typeofr.length){t&&(r=t);varo=0;returnfunction(){returno>=r.length ? {done: !0} : {done: !1,value: r[o++]};};}thrownewTypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}
Copy file name to clipboardExpand all lines: packages/react-native-babel-preset/src/__tests__/__fixtures__/output/hermes-stable-dev-preserve-block-scoping.js
+5-10Lines changed: 5 additions & 10 deletions
Original file line number
Diff line number
Diff line change
@@ -53,9 +53,6 @@ var _dataUtils = require("./data-utils");
function_createForOfIteratorHelperLoose(r,e){vart="undefined"!=typeofSymbol&&r[Symbol.iterator]||r["@@iterator"];if(t)return(t=t.call(r)).next.bind(t);if(Array.isArray(r)||(t=_unsupportedIterableToArray(r))||e&&r&&"number"==typeofr.length){t&&(r=t);varo=0;returnfunction(){returno>=r.length ? {done: !0} : {done: !1,value: r[o++]};};}thrownewTypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}
0 commit comments