Keep int32 class fields beside Buffer.from over an ArrayBuffer range (#2070)

This commit is contained in:
Chris Tate
2026-10-23 23:02:51 -05:00
committed by GitHub
2 changed files with 51 additions and 0 deletions
@@ -357,6 +357,52 @@ test("an Error subclass constructor forwarding dynamic arguments stores no class
expect(slots.isField("C", "a")).toBe(true);
});
test("Buffer.from over an ArrayBuffer range and Buffer as a value keep class fields int32", () => {
// `Buffer.from(view.buffer, offset, length)` passes its operands as
// checked values, and a program that reads the Buffer function's value
// installs it in %main; neither stores into a class instance.
const classes = [cls("C", ["a"])];
const view: IrExpr = { loc, kind: "dynFrom", value: ref("o", obj("C")), type: DYN };
const libCall = (fnName: string, args: IrExpr[], type: IrType = VOID): IrStmt =>
stmt({ loc, kind: "libCall", fn: fnName, args, type } as IrExpr);
const slots = analyzeInt32Slots(
mod(classes, [
fn(
"load",
[
{ id: "o", type: obj("C") },
{ id: "storage", type: DYN },
],
[
set(ref("o", obj("C")), "C", "a", num(1)),
libCall("dyn.typedRefIs", [
view,
{ loc, kind: "strLit", value: "object:C", type: STRING },
]),
libCall("buffer.installValue", []),
libCall("buffer.constructor", [], DYN),
libCall(
"buffer.poolFromDynRange",
[ref("storage", DYN), num(2), num(4)].map((arg): IrExpr =>
arg.type.kind === "dyn" ? arg : { loc, kind: "dynFrom", value: arg, type: DYN },
),
),
libCall("buffer.poolFromDynOffset", [
ref("storage", DYN),
{
loc,
kind: "dynFrom",
value: num(2),
type: DYN,
},
]),
],
),
]),
);
expect(slots.isField("C", "a")).toBe(true);
});
test("captured bindings share one proof across the declaring function and its closures", () => {
const closure: IrExpr = { loc, kind: "closure", fnName: "inner", captures: ["s"], type: DYN };
const outer = (): IrFunction => ({
+5
View File
@@ -451,6 +451,11 @@ const DYN_READ_ONLY = new Set<string>([
"fs.utimesSync",
// Buffer.prototype members read as values: a lookup by key.
"buffer.prototypeMember",
// Buffer.from(value, offset[, length]) over checked values: an
// ArrayBuffer's view after Node's offset and length conversions, or a copy
// of a string, array-like or typed array's values.
"buffer.poolFromDynOffset",
"buffer.poolFromDynRange",
"insp.dyn",
"insp.dynS",
// The dynamic-tier operators run their dyn twins (scr_js_value.c); the