feat(globalThis): expose built-in constructors as properties#965
Merged
Conversation
PR #963 closed the `Function('return this')()` recogniser so lodash's module-init IIFE no longer threw before `runInContext` ran, but the next blocker surfaced at lodash.js:1463 — `var Array = context.Array; var arrayProto = Array.prototype` threw `TypeError: Cannot read properties of undefined (reading 'prototype')` because the canonical JS built-ins were never registered on `globalThis`. Two coordinated pieces fix this: 1. Runtime (`crates/perry-runtime/src/object.rs`): on first access, `js_get_global_this`'s CAS winner now calls `populate_global_this_builtins(singleton)`. Constructors get a ClosureHeader-backed sentinel (`typeof === "function"` via the CLOSURE_MAGIC tag); `Math`/`JSON`/`Reflect` get a plain ObjectHeader (`typeof === "object"`). Each constructor carries a `prototype` dynamic property pointing at an empty ObjectHeader so the lodash `Array.prototype` chained read returns a real pointer instead of throwing. 2. Codegen (`crates/perry-codegen/src/expr.rs`): the `Expr::PropertyGet { GlobalGet(_), <name> }` arm consults `is_global_this_builtin_name(<name>)` and, when matched, routes the read through `js_get_global_this` + `js_object_get_field_by_name_f64` (mirrors the existing IndexGet arm from #611). The `typeof` short- circuit gains a parallel arm for the same names. `test-files/test_globalthis_builtins.ts` validates the round-trip byte-for-byte against `node --experimental-strip-types`. The lodash `_.chunk` repro still fails to load `_` but the throw point moves from line 1463 (`var arrayProto = Array.prototype`) to line 1493 (`funcToString.call(Object)`) — three reads + one call deeper into `runInContext`.
f23ff3a to
1b70ab8
Compare
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
globalThissingleton with built-in constructor properties on first init soglobalThis.Array/context.Object(lodash'srunInContextpattern) no longer returnundefined.Expr::PropertyGet { GlobalGet(_), <Builtin> }throughjs_get_global_this+js_object_get_field_by_name_f64so the static AST shape sees the same populated singleton (mirrors the IndexGet arm added in perry-runtime: Effect framework SIGSEGVs duringUtils.tsmodule init — array-fast-path called withx0 = 0x1(Effect end-to-end blocker, post-#592) #611).ClosureHeader-backed sentinel (typeof === "function"via the existing CLOSURE_MAGIC tag);Math/JSON/Reflectget a plainObjectHeader(typeof === "object"). Each constructor carries aprototypedynamic property pointing at an empty backing object so<Builtin>.prototypereads don't trip the spec-shapedTypeError: Cannot read properties of undefined.Follows PR #963 (v0.5.977) which closed the
Function('return this')()IIFE recogniser. Today's blocker forimport _ from "lodash"was the next line down —var Array = context.Array; var arrayProto = Array.prototype— because the built-in constructors were never registered as properties on the singleton.Test plan
test-files/test_globalthis_builtins.ts— byte-for-byte parity withnode --experimental-strip-typesfortypeof globalThis.{Array,Object,Function,Math,JSON}+Array.prototype+new Array(3).length._.chunk([1,2,3,4], 2)still fails to load_, but the throw point advanced fromlodash.js:1463(var arrayProto = Array.prototype) tolodash.js:1493(funcToString.call(Object)). The new blocker needs realFunction.prototype.toStringsupport — tracked alongside the broader lodash compile-as-package work.Caveats
Array.prototype.toString === undefined(Node returns a function).globalThis.Array === Arrayisfalse. BareArraystill lowers toExpr::GlobalGet(0); the singleton form returns a closure pointer. Unifying these requires widening Perry's first-class representation of built-in constructors.new Array(n)is unaffected — it flows throughlower_newlike before. The thunk underneath each sentinel is a no-op returningundefined, soglobalThis.Array(3)as a call (not anew) returnsundefinedrather than constructing an array (best-effort, known gap).Version
Cargo.toml[workspace.package].version→0.5.978CLAUDE.md**Current Version:**→0.5.978CHANGELOG.md— prepended## v0.5.978 — feat(globalThis): ...block with full context