Repository navigation
fix(globals): legacy escape()/unescape() + Node global alias (#4511) - #4527
Merged
Merged
Conversation
`escape()` / `unescape()` (ES Annex B B.2.1) were undefined, so bare calls threw `ReferenceError`. `qs` (pulled in via `stripe`) uses them for `%uXXXX` decoding, so any app importing `qs` hit this. The Node `global` object was likewise unresolved — `(global as any).x` emitted the unknown-identifier warning. Implementation routes both through the existing reflective globalThis function-value path (no dedicated HIR variant): - runtime: `js_escape` / `js_unescape` in builtins/globals.rs implement Annex B encoding over UTF-16 code units (code units <256 -> `%XX`, else `%uXXXX`; astral scalars emit their surrogate pair, matching Node). `unescape` reassembles UTF-16 units before decoding so `%uD835%uDFD8`-style pairs recombine. - runtime: register `escape`/`unescape` thunks in GLOBAL_THIS_BUILTIN_ FUNCTIONS; install a self-referential `global` property on the globalThis singleton (`global === globalThis`). - HIR: add escape/unescape/global to `is_builtin_global_value_name` so bare calls and value reads resolve to `globalThis.<name>` instead of throwing (also makes them `is_known_global_identifier_name`). - codegen: add escape/unescape/global to `is_global_this_builtin_name`; exclude `global` from the function-typeof set so `typeof global === "object"`. Byte-identical to Node on both the default auto-optimize and PERRY_NO_AUTO_OPTIMIZE paths, including non-ASCII, astral, malformed escapes, and local shadowing. Adds test_gap_escape_unescape_global_4511.ts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4511.
Problem
escape()/unescape()(ES Annex B B.2.1, still standard and present in Node) were undefined — bare calls threwReferenceError.qs(qs/lib/utils.js) uses them for%uXXXXdecoding, so any app pulling inqs(e.g. viastripe) hit this.globalobject was likewise unresolved —(global as any).xemittedWarning: unknown identifier 'global'. (globalThisalready worked.)Approach
Both are routed through Perry's existing reflective
globalThisfunction-value path — no dedicated HIRExprvariant (lighter than the ~20-file encodeURI-style approach; correctness, not a fast path, is what's needed here).builtins/globals.rs):js_escape/js_unescapeimplement Annex B encoding over UTF-16 code units — code units<256→%XX, else%uXXXX; astral scalars emit their surrogate pair (matching Node).unescapereassembles UTF-16 units before decoding so%uD835%uDFD8-style pairs recombine.global_this*.rs): registerescape/unescapethunks inGLOBAL_THIS_BUILTIN_FUNCTIONS; install a self-referentialglobalproperty on the globalThis singleton (global === globalThis).analysis/builtins.rs): addescape/unescape/globaltois_builtin_global_value_nameso bare calls and value reads resolve toglobalThis.<name>instead of throwing (also makes themis_known_global_identifier_name).expr/helpers.rs): add the three tois_global_this_builtin_name; excludeglobalfrom the function-typeof set sotypeof global === "object".Verification
Byte-identical to Node on both the default auto-optimize and
PERRY_NO_AUTO_OPTIMIZE=1paths, including non-ASCII (café), BMP (☃), astral (𝟘→%uD835%uDFD8), malformed/partial escapes (%,%2,%zz,100%), rebound values (const e = escape), and local shadowing (function escape(){}/const global = {}correctly win). Allperry-runtime/perry-hir/perry-codegentests pass;cargo fmt --checkclean. Addstest_gap_escape_unescape_global_4511.ts.Note: the issue's "Expected" shows
a%20b%2Bc, but+is inescape's unescaped set — real Node returnsa%20b+c, which this matches.