Skip to content

JSON.stringify(url.toString()) returns undefined when the toString() call is inlined as the argument #6488

Description

@proggeramlug

Summary

JSON.stringify(url.toString()) returns undefined when the URL.prototype.toString() call is inlined directly as the argument to JSON.stringify. Storing the string in a variable first works.

Repro

const u = new URL("http://x/en", "http://x/");
console.log(JSON.stringify(u.toString()));       // perry: undefined   node: "http://x/en"
const r = u.toString();
console.log(JSON.stringify(r));                   // both: "http://x/en"  (stored works)

Scope (narrow)

  • Only a URL receiver triggers it — Date, numbers, and { toString(){…} } objects all stringify correctly inline into JSON.stringify.
  • Only JSON.stringify as the callee triggers it — String(u.toString()), encodeURIComponent(u.toString()), Object.assign({}, {x: u.toString()}), u.toString() + "", u.toString().slice(0), (u.toString()).length (==11), a user function id(u.toString()), and console.log(u.toString()) all work.
  • typeof (u.toString()) is "string", so the value itself is a valid string.

Notes for whoever picks this up

  • Deterministic; reproduces with PERRY_GEN_GC=0 / PERRY_WRITE_BARRIERS=0 / PERRY_GC_FORCE_EVACUATE=1 — not GC.
  • HIR is JsonStringifyFull(<arg>, Null, Null) in both cases → js_json_stringify_full(v,r,i) (crates/perry-codegen/src/expr/math_simple.rs). --trace llvm shows identical IR for the working vs failing case: %p = call i64 @js_jsvalue_to_string_method(double u)or i64 %p, STRING_TAGbitcast to doublejs_json_stringify_full. The only difference is the working case shadow-slot-roots the string before the call; the failing case passes a bare SSA temp.
  • So the divergence appears to be in js_json_stringify_full (crates/perry-runtime/src/json/replacer.rs) misreading the URL.toString() string when it's the top-level value — suspect the try_stringify_lazy_array(value) fast path (only taken when no replacer/no spacer). Not root-caused.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions