Skip to content

runtime: String(re) / ${re} ignore an own toString override (the ToString coercion path never consults own properties) #6370

Description

@proggeramlug

Summary

String(re) and `${re}` ignore an own toString override on a RegExp — both data and accessor forms. Only the explicit re.toString() call honours it.

Repro

const r1: any = /a/;
r1.toString = () => "DATA-toString";
console.log("data .toString():", r1.toString());   // perry: DATA-toString  node: DATA-toString
console.log("data String(r)  :", String(r1));      // perry: /a/            node: DATA-toString
console.log("data `${r}`     :", `${r1}`);         // perry: /a/            node: DATA-toString

const r2: any = /b/;
Object.defineProperty(r2, "toString", { get() { return () => "ACC-toString"; }, configurable: true });
console.log("acc  String(r)  :", String(r2));      // perry: /b/            node: ACC-toString

Reproduces identically on main and on the branch for #5897 — pre-existing, not introduced by that work.

Why

There are two distinct paths:

  • the .toString() foldjs_jsvalue_to_string_method (value/to_string.rs). Codegen rewrites x.toString() straight into it, bypassing method dispatch. fix(regex,hir): UTF-16 indices, own-property method shadowing, RegExp static inheritance (#5897) #6358 taught it to consult the receiver's own toString (data), and a follow-up made that lookup accessor-aware.
  • the ToString coercion pathjs_jsvalue_to_string, reached from String(x), template literals, + string concat, and every other implicit ToString. It maps a regex straight back to its /source/flags literal and never consults own properties.

So an override is honoured on one path and silently ignored on the other, which is worse than being wrong on both — the same regex stringifies two different ways depending on how you ask.

Scope to check when fixing

Per spec this is ordinary ToPrimitive -> OrdinaryToPrimitive -> [[Get]]("toString"), so the receiver's own property (data or accessor) must win over RegExp.prototype.toString on every coercion site, not just String():

  • String(re), `${re}`, "" + re
  • re + "", [re].join(""), console.log(re) (inspect may differ — check node)
  • and the same question for the other exotics that use the exotic_expando side table (Error, and anything else ExoticKind covers), not only RegExp.

Also worth checking whether an own Symbol.toPrimitive is honoured on these paths.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regression

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions