Conversation
robhogan
added this pull request to stack #1982
September 26, 2026 07:54
robhogan
marked this pull request as ready for review
September 28, 2026 12:08
There was a problem hiding this comment.
Copilot review overview
馃煛 Changes recommended
The decoder has unresolved correctness and malformed-input compatibility issues.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
Optimizes source-map VLQ decoding with a single-pass inline decoder and removes the direct vlq dependency.
Changes:
- Replaces per-segment decoding with in-place character-code parsing.
- Adds coverage for complex and malformed mappings.
- Removes
vlqfrommetro-source-mapdependencies.
| File | Summary | Findings |
|---|---|---|
packages/鈥媘etro-source-map/鈥媠rc/鈥婥onsumer/鈥婱appingsConsumer.js |
Implements single-pass VLQ decoding. | Critical unsigned-shift compatibility issue; truncated fields may be silently accepted; malformed-input error text changes. |
packages/鈥媘etro-source-map/鈥媠rc/鈥媉_tests__/鈥婥onsumer-test.js |
Adds decoding and malformed-input tests. | No findings. |
packages/鈥媘etro-source-map/鈥媝ackage.json |
Removes the obsolete dependency. | No findings. |
馃挕 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
robhogan
commented
Sep 28, 2026
| "nullthrows": "^1.1.1", | ||
| "ob1": "0.87.1", | ||
| "source-map": "^0.5.6", | ||
| "vlq": "^1.0.0" |
Collaborator
Author
There was a problem hiding this comment.
This is safe to merge without Meta import because even though we're dropping a dependency here, metro-symbolicate has the same vlq dependency, so no lockfile change.
(This code snippet makes it look like we're dropping two deps - we're not, source-map just loses the trailing comma)
`MappingsConsumer` decoded a map by slicing each segment out as a string, decoding it with `vlq` through a `Map` cache, and yielding it from a generator that was then spread into an array. This decodes base64 VLQ in place from character codes in one pass, straight into the array. Output is unchanged, including the errors thrown for segments with two or three fields. The only difference is the message for a character that isn't base64. `metro-source-map` no longer uses `vlq`, so it's removed from its dependencies (it's still a dependency of `metro-symbolicate`). `composeSourceMaps` with a bundle map and its Hermes map is 1.63x faster than the previous diff (95% CI 1.62-1.64x) and 1.89x faster than `main` (1.88-1.90x; 1,801ms -> 1,107ms on our benchmark app*). Composition's memory falls by 16.0% (15.8-16.1%; 1,069MB -> 898MB*). The output is byte-identical. | | Time (median) | vs previous | vs `main` | Composition memory* | vs previous | vs `main` | Peak RSS | |---|---|---|---|---|---|---|---| | `main` | 2,088ms (2,082 to 2,094) | | | 1,070MB (1,070 to 1,070) | | | 1,664MB | | Previous diff | 1,801ms (1,794 to 1,806) | | -13.6% (-13.8 to -13.3) | 1,069MB (1,069 to 1,069) | | -0.1% (-0.2 to -0.1) | 1,663MB | | This diff | 1,107ms (1,099 to 1,111) | -38.6% (-38.9 to -38.3) | -47.1% (-47.4 to -46.7) | 898MB (897 to 899) | -16.0% (-16.1 to -15.8) | -15.9% (-16.1 to -15.6) | 1,492MB | ## Changelog ``` - **[Performance]**: Decode source maps in one pass in `metro-source-map`'s `Consumer` ``` ## Test plan New tests cover multi-digit and negative deltas, empty segments and lines, and malformed segments. They pass on `main` and on this diff. ``` yarn jest packages/metro-source-map packages/metro-symbolicate yarn flow check yarn lint ``` \* Benchmark: Mattermost Mobile 2.45.0 (React Native 0.83.9, Metro 0.83.7), iOS release bundle, unminified: 7,086 sources, 52.6MB bundle, 82.4MB flat source map. Composed with its `hermesc -O -output-source-map` map (hermes-compiler 0.14.1; 10.7MB, 1.84M segments). Timings are `composeSourceMaps` alone, excluding JSON parsing, over 105 rounds on Node 22.13.1 on an M5 Pro; each round runs `main`, every diff in this stack and a memory baseline, in a shuffled order. Figures are medians with 95% bootstrap confidence intervals - for changes, of the per-round paired ratio. Composition memory is peak RSS less that of the same process parsing both maps without composing them (594MB).
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This branch has not been deployed
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.

MappingsConsumerdecoded a map by slicing each segment out as a string, decoding it withvlqthrough aMapcache, and spreading a generator into an array:metro/packages/metro-source-map/src/Consumer/MappingsConsumer.js
Lines 127 to 133 in b7c2055
This decodes base64 VLQ in place from character codes, in one pass. Output is unchanged, including the errors for malformed segments, and
vlqis dropped frommetro-source-map's dependencies.composeSourceMapsis 1.63x faster than #1978 (95% CI 1.62-1.64x) and 1.89x faster thanmain(1,801ms -> 1,107ms on our benchmark app), with 16% less memory (1,069MB -> 898MB) and identical output.Benchmark
AI-driven.
mainmainmainChangelog
Test plan
New tests for multi-digit and negative deltas, empty segments and lines, and malformed segments. They pass on
maintoo.Benchmark (AI-driven): Mattermost Mobile 2.45.0 (React Native 0.83.9, Metro 0.83.7), iOS release bundle, unminified: 7,086 sources, 52.6MB bundle, 82.4MB flat source map. Composed with its
hermesc -O -output-source-mapmap (hermes-compiler 0.14.1, 10.7MB, 1.84M segments). Timings arecomposeSourceMapsalone, excluding JSON parsing, over 105 rounds on Node 22.13.1 on an M5 Pro. Each round runsmain, every PR in this stack and a memory baseline, in a shuffled order. Figures are medians with 95% bootstrap confidence intervals, for changes of the per-round paired ratio. Composition memory is peak RSS less that of the same process parsing both maps without composing them (594MB).