From d585f74f85a6f11ad38b6a7712f0e8c0befa3a11 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Thu, 1 Oct 2026 17:40:11 -0500 Subject: [PATCH 1/2] Honor inherited object formatters for Effect shard hashes --- packages/runtime/src/scr_json.c | 9 ++++--- tests/corpus/object-inherited-to-string.js | 28 ++++++++++++++++++++++ tests/fixtures/effect4/data-shard-hash.ts | 9 +++++++ 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 tests/corpus/object-inherited-to-string.js create mode 100644 tests/fixtures/effect4/data-shard-hash.ts diff --git a/packages/runtime/src/scr_json.c b/packages/runtime/src/scr_json.c index be05553a8..4082d5cfe 100644 --- a/packages/runtime/src/scr_json.c +++ b/packages/runtime/src/scr_json.c @@ -8822,10 +8822,13 @@ ScrMap *scr_dyn_native_collection_check(const ScrDyn *value, int map, const char } ScrStr *scr_dyn_to_string_method(const ScrDyn *d, const ScrStr *enc, const ScrStr *what) { - if (d->kind == SCR_DYN_FUNC) { - ScrDyn *member = scr_dyn_fn_get(d, "toString", 8); + /* Object.create receivers must run their inherited formatter before the + * default object tag; Effect shard keys use it to produce distinct hashes. */ + const bool object_method = d->kind == SCR_DYN_OBJ && scr_dyn_property_owner(d, "toString", 8); + if (d->kind == SCR_DYN_FUNC || object_method) { + ScrDyn *member = object_method ? scr_dyn_obj_read(d, "toString", 8) : scr_dyn_fn_get(d, "toString", 8); if (scr_exc_pending()) { scr_dyn_release(member); return NULL; } - if (member && member->kind != SCR_DYN_UNDEF) { + if (member && (object_method || member->kind != SCR_DYN_UNDEF)) { scr_dyn_this_push_dyn(d); ScrDyn *value = scr_dyn_call(member, NULL, 0, what->data); scr_dyn_this_pop(); diff --git a/tests/corpus/object-inherited-to-string.js b/tests/corpus/object-inherited-to-string.js new file mode 100644 index 000000000..69d1b4fb4 --- /dev/null +++ b/tests/corpus/object-inherited-to-string.js @@ -0,0 +1,28 @@ +const proto = { + toString() { return `value:${this.label}`; } +}; +const value = Object.create(proto); +value.label = "first"; +const child = Object.create(value); +child.label = "second"; +console.log(value.toString(), child.toString(), String(child)); +Object.defineProperty(proto, "toString", { + configurable: true, + get() { + const label = this.label; + return function() { return `${label}/${this.label}`; }; + } +}); +console.log(value.toString(), child.toString()); +const shadow = Object.create(value); +Object.defineProperty(shadow, "toString", { value: undefined }); +try { shadow.toString(); } catch (error) { console.log(error.name, error.message); } +Object.defineProperty(proto, "toString", { + configurable: true, + get() { throw new Error("formatter getter"); } +}); +try { child.toString(); } catch (error) { console.log(error.name, error.message); } +const own = Object.create(null); +own.label = "own"; +own.toString = function() { return this.label; }; +console.log(own.toString()); diff --git a/tests/fixtures/effect4/data-shard-hash.ts b/tests/fixtures/effect4/data-shard-hash.ts new file mode 100644 index 000000000..12856c1ae --- /dev/null +++ b/tests/fixtures/effect4/data-shard-hash.ts @@ -0,0 +1,9 @@ +import * as ShardId from "effect/cluster/ShardId" +import * as Hash from "effect/Hash" +import * as Equal from "effect/Equal" + +const first = ShardId.make("default", 1) +const second = ShardId.make("default", 2) +console.log(first.toString(), second.toString()) +console.log(Hash.hash(first), Hash.hash(second), Hash.string("default:1"), Hash.string("default:2")) +console.log(Equal.equals(first, second), Equal.equals(first, ShardId.make("default", 1))) From a17fce1cf8f376dafb942442f9640ed2d108b115 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Thu, 1 Oct 2026 17:49:10 -0500 Subject: [PATCH 2/2] Record inherited formatter corpus preflight baseline --- packages/compiler/test/ts7/baselines/order-parity.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/compiler/test/ts7/baselines/order-parity.json b/packages/compiler/test/ts7/baselines/order-parity.json index d4b408427..afb2db298 100644 --- a/packages/compiler/test/ts7/baselines/order-parity.json +++ b/packages/compiler/test/ts7/baselines/order-parity.json @@ -10329,6 +10329,12 @@ ], "diags": [] }, + "/tests/corpus/object-inherited-to-string.js": { + "order": [ + "/tests/corpus/object-inherited-to-string.js" + ], + "diags": [] + }, "/tests/corpus/object-prototype-identity.mjs": { "order": [ "/tests/corpus/object-prototype-identity.mjs"