Reading a prototype method as a value returns undefined for five web builtins, and returns the wrong function for a sixth. Confirmed empirically, not inferred.
These are the remaining members of the deliberate #10555 group in crates/perry-runtime/src/object/global_this/proto_methods.rs, whose arms install only a Symbol.toStringTag descriptor. The seventh member, URLSearchParams, is fixed by #10759 / PR #10807; this issue covers the other six, audited while fixing it.
Results
| builtin |
value-read of a prototype method |
verdict |
AbortController |
typeof undefined |
same defect |
AbortSignal |
typeof undefined |
same defect |
CustomEvent |
typeof undefined |
same defect |
Event |
typeof undefined |
same defect |
EventTarget |
typeof undefined |
same defect |
URL — .prototype.toJSON |
typeof undefined |
same defect |
URL — .prototype.toString |
resolves to a callable — the wrong one |
worse; see below |
Why URL.prototype.toString is the more dangerous case
The other failures are loud: you read a method, get undefined, and the next .call() throws. URL.prototype.toString instead resolves to the inherited generic Object.prototype.toString, which is callable and returns "[object URL]" rather than the URL string.
So it does not throw. It silently returns a plausible-looking wrong value. Any code that reads toString as a value — a serializer, a logger, a template helper, anything doing String(x) through an indirection — gets "[object URL]" where Node gives the href. That is a correctness bug that no crash will surface.
Mechanism
The #10555 group's arms were written on the premise that these builtins' methods are reached by type-directed static dispatch or handle tables, so reified prototype closures were unnecessary — each arm installs only the toStringTag descriptor for reflection. That premise holds for x.method() and has no answer for a method read as a value: the read finds nothing installed and falls through to undefined, or in URL's case to an inherited generic.
This is a gap in a deliberate design rather than a set of forgotten entries, which matters for the fix: the question is what a statically-dispatched builtin should do when one of its methods is read as a value, not why six entries were missed.
Note on scope
Filed as an audit result, deliberately not fixed alongside #10759 — that PR fixes one member and this issue records the other six with evidence. Whoever takes this should decide whether the right remedy is six more arms or a general answer for the group, and URL.prototype.toString should be handled first: it is the only one that fails silently.
Reading a prototype method as a value returns
undefinedfor five web builtins, and returns the wrong function for a sixth. Confirmed empirically, not inferred.These are the remaining members of the deliberate
#10555group incrates/perry-runtime/src/object/global_this/proto_methods.rs, whose arms install only aSymbol.toStringTagdescriptor. The seventh member,URLSearchParams, is fixed by #10759 / PR #10807; this issue covers the other six, audited while fixing it.Results
AbortControllertypeof undefinedAbortSignaltypeof undefinedCustomEventtypeof undefinedEventtypeof undefinedEventTargettypeof undefinedURL—.prototype.toJSONtypeof undefinedURL—.prototype.toStringWhy
URL.prototype.toStringis the more dangerous caseThe other failures are loud: you read a method, get
undefined, and the next.call()throws.URL.prototype.toStringinstead resolves to the inherited genericObject.prototype.toString, which is callable and returns"[object URL]"rather than the URL string.So it does not throw. It silently returns a plausible-looking wrong value. Any code that reads
toStringas a value — a serializer, a logger, a template helper, anything doingString(x)through an indirection — gets"[object URL]"where Node gives the href. That is a correctness bug that no crash will surface.Mechanism
The
#10555group's arms were written on the premise that these builtins' methods are reached by type-directed static dispatch or handle tables, so reified prototype closures were unnecessary — each arm installs only thetoStringTagdescriptor for reflection. That premise holds forx.method()and has no answer for a method read as a value: the read finds nothing installed and falls through toundefined, or inURL's case to an inherited generic.This is a gap in a deliberate design rather than a set of forgotten entries, which matters for the fix: the question is what a statically-dispatched builtin should do when one of its methods is read as a value, not why six entries were missed.
Note on scope
Filed as an audit result, deliberately not fixed alongside #10759 — that PR fixes one member and this issue records the other six with evidence. Whoever takes this should decide whether the right remedy is six more arms or a general answer for the group, and
URL.prototype.toStringshould be handled first: it is the only one that fails silently.