Skip to content

fix(runtime): String.fromCharCode/fromCodePoint usable as first-class function values#4634

Merged
proggeramlug merged 1 commit into
mainfrom
fix-string-statics-name
Jun 5, 2026
Merged

fix(runtime): String.fromCharCode/fromCodePoint usable as first-class function values#4634
proggeramlug merged 1 commit into
mainfrom
fix-string-statics-name

Conversation

@proggeramlug
Copy link
Copy Markdown
Contributor

Completes the static-function-values series (#4622 Date, #4626 Array, #4631 Number). Unlike those, String.fromCharCode/fromCodePoint weren't reified at all, so this reifies them and wires the reroute-undo exception:

Byte-identical to node: typeof/name/length, value-calls (incl. astral code points like 😀), direct calls, RangeError on invalid code points; String.raw unchanged. Fixes test262 built-ins/String/{fromCharCode,fromCodePoint}/{name,length}.

(String.raw .name/.length still tracked in #4627.)

… function values

Completes the static-function-values series (#4622 Date, #4626 Array, #4631
Number). Unlike those, String.fromCharCode/fromCodePoint were not reified at all,
so this both reifies them and wires the reroute-undo exception:

- New js_string_from_code_point_array runtime helper (variadic fromCodePoint with
  per-element RangeError validation; lone surrogates → U+FFFD).
- Reify fromCharCode/fromCodePoint via install_constructor_static_with_call_arity
  (call-arity 0 = all args into rest; spec .length 1) — fromCharCode reuses the
  existing js_string_from_char_code_array.
- Explicit String fromCharCode/fromCodePoint exception in the #973/#4139
  reroute-undo (NOT the whole namespace — String.raw is not reified and stays on
  its intrinsic path, unaffected).

Byte-identical to node: typeof/name/length, value-calls (incl. astral code
points), direct calls, RangeError on invalid code points; String.raw unchanged.
Fixes test262 String/{fromCharCode,fromCodePoint}/{name,length}. (String.raw .name
/.length tracked in #4627.)
@proggeramlug proggeramlug merged commit 2f8fa32 into main Jun 5, 2026
13 checks passed
@proggeramlug proggeramlug deleted the fix-string-statics-name branch June 5, 2026 18:25
proggeramlug added a commit that referenced this pull request Jun 5, 2026
Completes the static-function-values series (Date #4622, Array #4626, Number
#4631, String.fromCharCode/fromCodePoint #4634). Reify String.raw as a real
function value: a thunk taking the template/cooked object as a fixed param plus
a rest of substitutions (call-arity 1, spec .length 1), forwarding to
js_string_raw. Add 'raw' to the String reroute-undo exception so value reads hit
the reified receiver.

Byte-identical to node: String.raw.name==='raw'/.length===1, value-calls
(const r=String.raw; r({raw:[...]}, ...)) and tagged-template calls
(String.raw`x${10}y`) work. Fixes test262 String/raw/{name,length}; closes the
String portion of #4627.

Co-authored-by: Ralph Küpper <ralph@skelpo.com>
proggeramlug added a commit that referenced this pull request Jun 6, 2026
…4521) (#4658)

Continues the static-function-values series (#4622 Date, #4626 Array, #4631
Number, #4634 String) for the Promise constructor. Promise.all / race /
allSettled / any / resolve / reject / withResolvers / try were not reified at
all, so reading them as values gave undefined (typeof undefined, no .name /
.length, not callable via reference / .call / .apply).

- Reify all eight statics via install_constructor_static[_with_call_arity] in
  install_builtin_constructor_statics ("Promise" arm). They delegate to the
  same runtime entry points the direct-call codegen path emits, so behavior is
  unchanged for direct calls; value reads and rebound usage now resolve to real
  native function objects with spec .name / .length (all combinators + resolve /
  reject / try = 1, withResolvers = 0), prop-desc {writable, !enumerable,
  configurable}, and non-constructor semantics (new Promise.all([]) throws
  TypeError).
- Wire the #973/#4139 reroute-undo exception in expr_member.rs so value reads of
  these members keep the reified Promise receiver instead of collapsing to
  GlobalGet(0).<name> (undefined). Direct calls keep the codegen fast path via
  the !member_is_call_callee gate.

Byte-identical to node: typeof / name / length / prop-desc, not-a-constructor
throws, value-calls and .call(Promise, ...) for every combinator, withResolvers
and try. Verified under both the runtime-only and auto-optimize build paths.
Existing direct-call combinators and then/catch/finally/await unchanged.

test262 built-ins/Promise/all parity 52.1% -> 55.2%; fixes the
name / length / prop-desc / not-a-constructor cases across all four combinator
dirs. Spec-internal observable semantics (per-iteration this.resolve, real
resolve-element closures with [[AlreadyCalled]], NewPromiseCapability(this)) are
a follow-up: they require dynamic `new <Promise-bound>(executor)` (today throws
"Promise is not a function") and routing the direct-call path through the
constructor-aware algorithm.

Co-authored-by: Ralph Küpper <ralph@skelpo.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant