mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-02 07:43:42 +08:00
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>
54 lines
1.7 KiB
JavaScript
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);
|
|
});
|