fix(hir): queueMicrotask/structuredClone/atob/btoa as first-class function values (#5015)#5019
Merged
Merged
Conversation
…ction values (#5015) Bare value reads of these four callable globals (const m = queueMicrotask, or the object-literal property { scheduleMicrotask: queueMicrotask }) fell through ident lowering to the GlobalGet(0) sentinel and evaluated to the number 0 -- typeof reported "number" and calling the stored value threw "value is not a function". They were callable directly but missing from is_builtin_global_value_name (a gap left from #3986). react-reconciler's host config stores scheduleMicrotask: queueMicrotask and invokes it from scheduleImmediateRootScheduleTask, so updateContainerSync threw on the first render -- blocking every custom React renderer. Add them to is_builtin_global_value_name so the value read lowers to PropertyGet { GlobalGet(0), name }, resolving through the existing globalThis thunk (same path as fetch/parseInt/eval). Value-read-only; direct calls unaffected. Regression test matches Node byte-for-byte.
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.
Summary
Fixes #5015 — react-reconciler 0.33 production build threw
TypeError: value is not a functioninsideupdateContainerSyncon the first render, blocking every custom React renderer (ink #348, react-three-fiber, react-pdf, …).Root cause
Four callable global helpers —
queueMicrotask,structuredClone,atob,btoa— were callable directly (queueMicrotask(fn)is intercepted inexpr_call/globals.rs) but were never added tois_builtin_global_value_nameincrates/perry-hir/src/analysis/builtins.rs.So a bare value read —
const m = queueMicrotask, or the object-literal property{ scheduleMicrotask: queueMicrotask }— fell through ident lowering to theGlobalGet(0)sentinel and evaluated to the number0.typeofreported"number"and calling the stored value threw "value is not a function". (Thetypeof-of-bare-ident fold and theglobalThis.<name>thunk table already covered these names; only the value-read list was missing them — a gap left over from #3986.)react-reconciler's host config stores
scheduleMicrotask: queueMicrotaskinto a module var and later invokes it fromscheduleImmediateRootScheduleTask, reached viaupdateContainerSync → updateContainerImpl → scheduleUpdateOnFiber → ensureRootIsScheduled. The stored0is exactly the non-callable the issue's native backtrace pinpointed (…__96=scheduleImmediateRootScheduleTask).Fix
Add the four names to
is_builtin_global_value_nameso a bare value read lowers toPropertyGet { GlobalGet(0), <name> }, resolving through the existing globalThis thunk — the same pathfetch/parseInt/evalalready use. This fixes both the bare value read and the object-literal property form, and is value-read-only (direct calls are untouched, intercepted earlier).Validation
test-files/test_gap_global_fn_values_5015.ts— matchesnode --experimental-strip-typesbyte-for-byte (typeof, object-property extraction + call, direct calls).test_gap_global_builtins_2905_2889,test_gap_escape_unescape_global_4511,test_globalthis_builtins) still match Node.updateContainerSyncno longer throws ([C1] updateContainerSync ok).Follow-up wall (separate bug — not in this PR)
With this fix
updateContainerSyncsucceeds; the repro's subsequentflushSyncWork()now reachescommitRoot, whereflushPendingEffects— a forward-referenced, hoisted sibling function declaration captured bycommitRootinside the hugemodule.exports = function($$$config){…}factory — reads back asundefined. This is a distinct closure-capture/box issue, independent ofqueueMicrotask(it reproduces with an arrowscheduleMicrotasktoo). It is the next wall on the ink-end-to-end path and is filed as #5020.🤖 Generated with Claude Code