@sproutboat/toolchain 0.4.22 → 0.4.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/acquire.test.ts +2 -0
- package/src/patch.ts +18 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @sproutboat/toolchain
|
|
2
2
|
|
|
3
|
+
## 0.4.23
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- a479611: Typed-array writes outside the array are ignored, as the spec requires, instead of overwriting the next allocation in memory (baronunread/sproutboat#241). `a[length] = x`, `a[-1] = x` (which wrote `a[0]`) and `a[1.5] = x` no longer touch any memory. The check uses the same index validation as reads and applies to user code; Porffor's own builtins write within bounds.
|
|
8
|
+
|
|
3
9
|
## 0.4.22
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/package.json
CHANGED
package/src/acquire.test.ts
CHANGED
|
@@ -225,6 +225,8 @@ test("the default call (no url/expectedSha256 override) never touches the networ
|
|
|
225
225
|
const codegen = await readFile(join(dir, "compiler/codegen.js"), "utf8");
|
|
226
226
|
expect(codegen).toContain("Convert(T.i32, toUint32(scope, numValue(toNumeric())), CONVERT_RANGE_KNOWN | CONVERT_SIGNED)");
|
|
227
227
|
expect(codegen).toContain("Box(f, Const(T.i32, TYPES.number)), valUndefined());");
|
|
228
|
+
// #241: out-of-range typed-array writes are ignored in user code.
|
|
229
|
+
expect(codegen).toContain("if (globalThis.precompile) store();");
|
|
228
230
|
// #238: integer typed-array stores wrap modulo 2^n instead of saturating.
|
|
229
231
|
expect(await readFile(join(dir, "compiler/codegen.js"), "utf8")).toContain(
|
|
230
232
|
"signed ? Convert(T.i32, toUint32(scope, f), CONVERT_RANGE_KNOWN | CONVERT_SIGNED) : toUint32(scope, f)",
|
package/src/patch.ts
CHANGED
|
@@ -346,6 +346,23 @@ const TA_GET_INJECT =
|
|
|
346
346
|
" Box(f, Const(T.i32, TYPES.number)), valUndefined());\n" +
|
|
347
347
|
" };";
|
|
348
348
|
const TA_GET_MARKER = "sproutboat #238: only a valid in-range integer index reads";
|
|
349
|
+
// baronunread/sproutboat#241: a typed-array write outside the array stored into
|
|
350
|
+
// whatever memory followed it (and a[-1] = x wrote a[0], via the saturating
|
|
351
|
+
// index conversion). ECMA-262 ignores the write. Same check as taGet, in user
|
|
352
|
+
// code only: builtins such as the constructor store elements before they set
|
|
353
|
+
// the length, and they only write in bounds anyway. Applies after TA_STORE,
|
|
354
|
+
// whose output line is the anchor.
|
|
355
|
+
const TA_SET_BOUNDS_ANCHOR =
|
|
356
|
+
" stmt(scope, Store(ctype, addr, 4, ctype === 'f64' || ctype === 'f32' ? f : signed ? Convert(T.i32, toUint32(scope, f), CONVERT_RANGE_KNOWN | CONVERT_SIGNED) : toUint32(scope, f)));";
|
|
357
|
+
const TA_SET_BOUNDS_INJECT =
|
|
358
|
+
" const store = () => stmt(scope, Store(ctype, addr, 4, ctype === 'f64' || ctype === 'f32' ? f : signed ? Convert(T.i32, toUint32(scope, f), CONVERT_RANGE_KNOWN | CONVERT_SIGNED) : toUint32(scope, f)));\n" +
|
|
359
|
+
" // sproutboat #241: a write outside the array is ignored, not stored into the next allocation.\n" +
|
|
360
|
+
" if (globalThis.precompile) store();\n" +
|
|
361
|
+
" else {\n" +
|
|
362
|
+
" const { idx: sbIdx, valid: sbValid } = denseArrayIndexKey(scope, prop);\n" +
|
|
363
|
+
" emitIf(scope, Bin('&&', T.i32, sbValid, Bin('<', T.i32, sbIdx, Load('u32', JvPtr(obj), 0))), store);\n" +
|
|
364
|
+
" }";
|
|
365
|
+
const TA_SET_BOUNDS_MARKER = "sproutboat #241: a write outside the array is ignored";
|
|
349
366
|
|
|
350
367
|
// baronunread/sproutboat#238: TypedArray.prototype.set(source) with no offset
|
|
351
368
|
// ran `offset = Math.trunc(undefined)`, which is NaN. The same-type fast path
|
|
@@ -1184,6 +1201,7 @@ export async function patchTypedArrayStore(root: string): Promise<void> {
|
|
|
1184
1201
|
[TA_STORE_MARKER, TA_STORE_ANCHOR, TA_STORE_INJECT, "typed-array store conversion"],
|
|
1185
1202
|
[BITNOT_MARKER, BITNOT_ANCHOR, BITNOT_INJECT, "bitwise NOT conversion"],
|
|
1186
1203
|
[TA_GET_MARKER, TA_GET_ANCHOR, TA_GET_INJECT, "typed-array read bounds"],
|
|
1204
|
+
[TA_SET_BOUNDS_MARKER, TA_SET_BOUNDS_ANCHOR, TA_SET_BOUNDS_INJECT, "typed-array write bounds"],
|
|
1187
1205
|
] as const) {
|
|
1188
1206
|
if (src.includes(marker)) continue;
|
|
1189
1207
|
if (!src.includes(anchor)) throw new Error(`could not patch Porffor's ${what}: anchor not found in ${file}`);
|