fix: built-in prototype methods on all built-in prototypes resolve as property values (#2142)#2176
Merged
Merged
Conversation
… property values (#2142) Extends #2058/#2077 to the remaining built-in prototypes (`Array`/`Date`/`RegExp`/`String`/`Promise`/`Map`/`Set`/`Error`/typed arrays/etc.). Before this change, the typeof-fold made `typeof Array.prototype.map === "function"` pass at AST level, but any INDIRECT read — storing into a local, an array literal, or otherwise hiding the value behind a variable — returned `undefined`. The issue's repro (`for ([n,v] of [[ ... , Array.prototype.map], ...])`) hit exactly that gap. Two coordinated changes: 1. HIR (`expr_member.rs`) — broaden the #2060 typed-array-only collapse exemption. The #973 builtin-ident reroute collapsed `<Ctor>.prototype` to `globalThis.prototype` (which is undefined, not the constructor's real prototype). Typed arrays already exempted themselves so the per-kind proto with the `length` / `byteLength` / `byteOffset` / `buffer` accessor descriptors stayed reachable; the same logic applies to every built-in proto, so the exemption now fires unconditionally when the outer member is `.prototype`. 2. Runtime (`global_this.rs`) — extend `populate_builtin_prototype_methods` to install named callable closures for every well-known method on `Array`/`Object`/`Function`/`String`/`Number`/`Boolean`/`Date`/ `RegExp`/`Promise`/`Map`/`Set`/`WeakMap`/`WeakSet`/`Error*`/typed arrays. Each closure is backed by the shared `noop_thunk` so `typeof === "function"`, `.name` reads the method name, and `.length` reads the spec arity — covering Test262's `verifyProperty` / `assert.throws` introspection paths. `Array.prototype.slice` and `Object.prototype.toString` keep their dedicated thunks (ramda's curry/variadic helpers depend on `Array.prototype.slice.call(args,…)` returning a real sliced array even via indirection, and ramda's `_isArguments.js` IIFE calls `Object.prototype.toString.call(arguments)` at module-init time). The hot-path calls — `arr.map(fn)` (codegen NativeMethodCall) and `Array.prototype.map.call(arr, fn)` (HIR rewrite via `try_builtin_prototype_method_apply_call`) — are unaffected. Adds `test-files/test_issue_2142_builtin_proto_method_values.ts`, byte-identical to `node --experimental-strip-types`. Per #2142, this is the single biggest remaining cascade in Test262's `built-ins/{Object,Array,TypedArray,Date,RegExp,Function}` categories after #2058/#2059/#2060/#2063 — the `(no output)` / `reading 'name'` / `reading 'constructor'` cascades all collapse on the same prototype- method-value gap.
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 #2142.
Problem
After #2058/#2059/#2060/#2063 landed, the value-form gap for built-in prototype methods narrowed but didn't close. Reading a method off
Array.prototype/Date.prototype/RegExp.prototype/String.prototype/Promise.prototype/%TypedArray%.prototypeetc. as a property value still returnedundefined— even forObject.prototype/Number.prototype/Function.prototype(the typeof fold from #2058 only catchestypeof <Ctor>.prototype.<m>at AST level; once the value lands in a local or array literal, the indirect typeof goes through the same broken member-access path).Per #2142, this is the dominant cascade in Test262's
built-ins/{Object,Array,TypedArray,Date,RegExp,Function}re-harvest after #2058/#2059/#2060/#2063 — driving the bulk of the(no output)/reading 'name'/reading 'constructor'failures.Fix
Two coordinated changes:
HIR (
expr_member.rs) — broaden the bug: built-in function .name own-property returns undefined (function foo(){}; foo.name === undefined) #2060 typed-array-only collapse exemption. The feat(runtime): .constructor on Date/Array/Object instances #973 builtin-ident reroute turnedArray.prototypeintoglobalThis.prototype(which doesn't exist; the prototype object lives on the constructor closure's dynamic-prop side table). Typed arrays already exempted themselves so the per-kind proto with accessor descriptors stayed reachable; the same logic applies to every built-in proto, so the exemption now fires unconditionally when the outer member is.prototype.Runtime (
global_this.rs) — extendpopulate_builtin_prototype_methodsto install named callable closures for every well-known method onArray/Object/Function/String/Number/Boolean/Date/RegExp/Promise/Map/Set/WeakMap/WeakSet/Error*/typed arrays. Each closure is backed by the sharednoop_thunksotypeof === "function",.namereads the method name, and.lengthreads the spec arity — covering Test262'sverifyProperty/assert.throwsintrospection paths.Array.prototype.sliceandObject.prototype.toStringkeep their dedicated thunks (ramda's curry/variadic helpers depend onArray.prototype.slice.call(args, ...)returning a real sliced array even via indirection, and ramda's_isArguments.jsIIFE callsObject.prototype.toString.call(arguments)at module-init time).The hot-path forms are unaffected:
arr.map(fn)(codegen NativeMethodCall) andArray.prototype.map.call(arr, fn)(HIR rewrite viatry_builtin_prototype_method_apply_call) bypass the closure entirely. The reified noop closures only get invoked through indirect calls (const m = Array.prototype.map; m.call(arr, fn)), which is rare in real code but now returnsundefinedinstead of throwingTypeError: undefined is not a function.Validation
test-files/test_issue_2142_builtin_proto_method_values.tsis byte-identical tonode --experimental-strip-types.test_gap_array_methods/test_gap_date_methods/test_gap_string_methods/test_gap_object_methods/test_gap_typed_arrays/test_gap_arguments_in_object_literal_method/test_gap_async_advanced/test_gap_closures.cargo test -p perry-hirgreen (111/111).cargo fmt --all -- --checkclean.Test plan
Array.prototype.slice.call(arr, ...)still returns a real sliced array (no regression on ramda path)Object.prototype.toString.call(arr)still returns"[object Array]"(no regression)