Files
scriptc/tests/corpus/dyn-generator-method-value.js
T
Malte UblandClaude 5.5 460bd6fb38 Keep a generator method's generator ABI through the prototype-access widening
A class whose methods are reachable through `X.prototype` widens its method
signatures to checked values so a JS caller can hand anything across. The
widening replaced every non-void return with DYN, which erased a GENERATOR
method's generator type; the generator branch below then had no generator
channels to build its metadata from and refused the whole class (SC2001 in
`%init`, re-emitted as a runtime fence in a JS file). Every
`assert.sameValue(c.m, C.prototype.m)` variant of the campaign therefore
refused at runtime, 759 of them in the generator-method chapters alone.

A generator's return kind is already a checked-crossable ABI: dynFrom boxes a
generator by reference and dyn calls resume it through the dyn generator ops,
so only the arguments need the widening. Guard the return widening to
non-generator methods.

Test262, 1,110 affected variants, before/after on the candidate
(class/gen-method 52->76, class/async-gen-method 76->128,
class/scope-gen-meth 0->8); no pass->fail and no typed-path delta.

Co-Authored-By: Claude 5.5 <noreply@anthropic.com>
2026-10-28 19:51:09 -07:00

54 lines
1.7 KiB
JavaScript

/* A generator method VALUE crossing the checked-value boundary.
*
* A class whose methods are reachable through its prototype object answers
* its methods as checked (dynamic) values: the value read off an instance is
* the same function object the prototype carries, a checked callback can hold
* it and call it, and the generator object it produces keeps the generator
* protocol across the box. Node is the oracle. */
class Seq {
constructor(tag) {
this.tag = tag;
}
*items(n) {
for (let i = 0; i < n; i++) yield this.tag + ":" + i;
return n;
}
async *later(n) {
yield n + 1;
return n;
}
}
function sameValue(actual, expected, label) {
if (actual !== expected) throw new Error("not the same: " + label);
}
function typeofOf(value) {
return typeof value;
}
function callWith(receiver, fn, ...args) {
return fn.apply(receiver, args);
}
const seq = new Seq("a");
sameValue(seq.items, Seq.prototype.items, "instance method value");
sameValue(seq.items, seq.items, "repeated read");
sameValue(seq.later, Seq.prototype.later, "async instance method value");
console.log(typeofOf(seq.items), typeofOf(seq.later));
const gen = callWith(seq, seq.items, 2);
console.log(gen.next().value, gen.next().value);
const finished = gen.next();
console.log(finished.value, finished.done);
// The direct call answers its own generator object. (Identity and reflection
// on a generator object are separate, still-fenced operations.)
const direct = seq.items(1);
console.log(direct.next().value);
// An async generator value crosses the same boundary.
callWith(seq, seq.later, 4)
.next()
.then(function (step) {
console.log("async", step.value, step.done);
});