mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-23 06:10:02 +08:00
- Keep null-initialized fields and inferred returns in mutable native storage, including inherited resets and saved callbacks. - Convert checked native values with Number using JavaScript coercion and catchable errors.
119 lines
3.7 KiB
JavaScript
119 lines
3.7 KiB
JavaScript
// JavaScript classes: fields declared by CONSTRUCTOR ASSIGNMENT (checkJs
|
|
// infers the property types), methods, getters, single inheritance with a
|
|
// virtual override dispatched through a base-typed element.
|
|
'use strict';
|
|
|
|
class Shape {
|
|
/** @param {string} name */
|
|
constructor(name) {
|
|
this.name = name;
|
|
this.hits = 0;
|
|
}
|
|
area() {
|
|
return 0;
|
|
}
|
|
describe() {
|
|
this.hits += 1;
|
|
return `${this.name}: ${this.area()}`;
|
|
}
|
|
get tag() {
|
|
return this.name.toUpperCase();
|
|
}
|
|
}
|
|
|
|
class Rect extends Shape {
|
|
/** @param {number} w @param {number} h */
|
|
constructor(w, h) {
|
|
super("rect");
|
|
this.w = w;
|
|
this.h = h;
|
|
}
|
|
area() {
|
|
return this.w * this.h;
|
|
}
|
|
}
|
|
|
|
class Square extends Rect {
|
|
/** @param {number} side */
|
|
constructor(side) {
|
|
super(side, side);
|
|
}
|
|
}
|
|
|
|
const shapes = [new Shape("blob"), new Rect(3, 4), new Square(5)];
|
|
for (const s of shapes) console.log(s.describe(), s.tag);
|
|
const r = new Rect(2, 6);
|
|
r.describe();
|
|
r.describe();
|
|
console.log("hits:", r.hits, "square?", shapes[2] instanceof Square, shapes[1] instanceof Square);
|
|
|
|
// An unused incompatible JS override must not prevent construction or
|
|
// unrelated methods. A deeper compatible override still dispatches normally.
|
|
class ReturnBase {
|
|
value = 10;
|
|
insert(value, anchor) { return 1; }
|
|
selected() { return "base"; }
|
|
read() { return this.value; }
|
|
dispatch(value) { return this.insert(value, undefined); }
|
|
}
|
|
class DormantReturns extends ReturnBase {
|
|
constructor() { super(); this.value = 20; }
|
|
// @ts-expect-error Exercise a valid JS override with a different return.
|
|
insert(value, anchor) { console.log("unused", value, anchor); }
|
|
// @ts-expect-error Exercise a valid JS override with a different return.
|
|
selected() { return 42; }
|
|
}
|
|
class RepairedReturns extends DormantReturns {
|
|
insert(value, anchor) { console.log("repaired", value, anchor); return 3; }
|
|
// @ts-expect-error The deeper override matches the original base ABI.
|
|
selected() { return "repaired"; }
|
|
}
|
|
class ShortDormantReturn extends ReturnBase {
|
|
// @ts-expect-error Both the return type and the unused tail differ.
|
|
insert(value) { console.log("unused short", value); }
|
|
}
|
|
const dormant = new DormantReturns();
|
|
console.log("dormant", dormant.read(), new ShortDormantReturn().read());
|
|
const repaired = new RepairedReturns();
|
|
console.log("repaired", repaired.dispatch("item"), repaired.selected());
|
|
|
|
// A null initializer does not restrict an inferred JS field to null forever.
|
|
class MutableNull {
|
|
value = null;
|
|
set(value) { this.value = value; }
|
|
read() { return this.value; }
|
|
get current() { return this.value; }
|
|
set current(value) { this.value = value; }
|
|
}
|
|
class ResetNull extends MutableNull {
|
|
value;
|
|
constructor(value) {
|
|
super();
|
|
console.log('reset', this.value === undefined);
|
|
this.value = value;
|
|
}
|
|
}
|
|
class ConstructorNull {
|
|
constructor() { this.value = null; }
|
|
set(value) { this.value = value; }
|
|
read() { return this.value; }
|
|
}
|
|
const mutableNull = new MutableNull();
|
|
console.log('null initial', mutableNull.read(), mutableNull.current);
|
|
mutableNull.set('updated');
|
|
console.log('null written', mutableNull.read(), mutableNull.current);
|
|
mutableNull.current = 42;
|
|
console.log('accessor', mutableNull.read());
|
|
console.log('numeric field', Number(mutableNull.value));
|
|
const resetNull = new ResetNull('derived');
|
|
console.log('inherited', resetNull.read());
|
|
const readMutableNull = () => mutableNull.value;
|
|
function readNullField() { return mutableNull.value; }
|
|
const savedNullReader = readNullField;
|
|
console.log('readers', readMutableNull(), savedNullReader());
|
|
const constructorNull = new ConstructorNull();
|
|
constructorNull.set('constructor');
|
|
console.log('constructor field', constructorNull.read());
|
|
mutableNull.set(null);
|
|
console.log('null reset', readMutableNull(), savedNullReader());
|