@sproutboat/toolchain 0.4.23 → 0.4.24

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @sproutboat/toolchain
2
2
 
3
+ ## 0.4.24
4
+
5
+ ### Patch Changes
6
+
7
+ - 9225026: `join()` and `toString()` work on typed arrays (baronunread/sproutboat#242). Porffor generates typed-array methods from the Array ones by replacing `any[]` with the array's type, which also retyped `join`'s internal helper array. `new Uint8Array([1,2]).join("-")` returned garbage, and a three-element `Uint16Array.join` crashed. The generator now keeps that helper a plain array.
8
+
3
9
  ## 0.4.23
4
10
 
5
11
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutboat/toolchain",
3
- "version": "0.4.23",
3
+ "version": "0.4.24",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
@@ -40,6 +40,7 @@ async function fixture(): Promise<{ archive: string; sha256: string }> {
40
40
  "compiler/builtins_precompiled.js": "fixture:precompiled\n",
41
41
  "compiler/builtins/date.ts": "export const __ecma262_ParseDTSF = (string: string) => {\n let n: number = 0;\n let nInd: number = 0;\n\n const len: i32 = string.length;\n const endPtr: i32 = Porffor.IR.ptr(string) + len;\n let ptr: i32 = Porffor.IR.ptr(string);\n\n while (ptr <= endPtr) { // <= to include extra null byte to set last n\n const chr: i32 = Porffor.IR.loadU8(ptr++, 4);\n if (Porffor.fastAnd(chr >= 48, chr <= 57)) { // 0-9\n n *= 10;\n n += chr - 48;\n continue;\n }\n\n if (chr == 45) { // -\n if (Porffor.fastOr(ptr == Porffor.IR.ptr(string), nInd == 7)) n = -n;\n }\n\n if (n > 0) {\n if (nInd == 0) y = n;\n else if (nInd == 1) m = n - 1;\n else if (nInd == 2) dt = n;\n else if (nInd == 3) h = n;\n else if (nInd == 4) min = n;\n else if (nInd == 5) s = n;\n else if (nInd == 6) milli = n;\n else if (nInd == 7) tzHour = n;\n else if (nInd == 8) tzMin = n;\n\n n = 0;\n nInd++;\n }\n }\n\n h += tzHour;\n min += tzMin;\n};\n// RFC 7231 or Date.prototype.toString() parser\n",
42
42
  "compiler/builtins/typedarray.js":
43
+ "${typedArrayFuncs.reduce((acc, x) => acc + x.replace('// @porf-typed-array\\n', '').replaceAll('Array', name).replaceAll('any[]', name) + '\\n\\n', '')}`;\n" +
43
44
  " offset = Math.trunc(offset);\n if (Porffor.fastOr(offset < 0, offset > len)) throw new RangeError('Offset out of bounds');\n" +
44
45
  "export const __${name}_from = (arg: any, mapFn: any): ${name} => {\n" +
45
46
  " const arr: any[] = Porffor.array.new(4);\n" +
@@ -217,6 +218,10 @@ test("the default call (no url/expectedSha256 override) never touches the networ
217
218
  },
218
219
  });
219
220
  expect(await readFile(join(dir, "runtime/index.js"), "utf8")).not.toBe("");
221
+ // #242: generated typed-array join/toString keep a plain-array `parts`.
222
+ expect(await readFile(join(dir, "compiler/builtins/typedarray.js"), "utf8")).toContain(
223
+ ".replaceAll('const parts: ' + name + ' =', 'const parts: any[] =')",
224
+ );
220
225
  // #238: TypedArray.prototype.set(source) with no offset copies to index 0.
221
226
  expect(await readFile(join(dir, "compiler/builtins/typedarray.js"), "utf8")).toContain(
222
227
  "offset = ecma262.ToIntegerOrInfinity(offset);",
package/src/patch.ts CHANGED
@@ -375,6 +375,17 @@ const TA_SET_OFFSET_INJECT =
375
375
  " offset = ecma262.ToIntegerOrInfinity(offset); // sproutboat #238: undefined offset is 0, not NaN\n if (Porffor.fastOr(offset < 0, offset > len)) throw new RangeError('Offset out of bounds');";
376
376
  const TA_SET_OFFSET_MARKER = "sproutboat #238: undefined offset is 0";
377
377
 
378
+ // baronunread/sproutboat#242: typedarray.js generates each TypedArray method
379
+ // from array.ts with `.replaceAll('any[]', name)`. That also retyped join's and
380
+ // toString's internal `const parts: any[]` helper as e.g. a Uint8Array, so the
381
+ // joined strings were stored into a "typed array": Uint8Array join returned
382
+ // garbage and Uint16Array join crashed. Put `parts` back to a plain array after
383
+ // the replacement; map/filter's intentionally typed `out` is untouched.
384
+ const TA_JOIN_ANCHOR = ".replaceAll('Array', name).replaceAll('any[]', name) + '\\n\\n'";
385
+ const TA_JOIN_INJECT =
386
+ ".replaceAll('Array', name).replaceAll('any[]', name).replaceAll('const parts: ' + name + ' =', 'const parts: any[] =') /* sproutboat #242 */ + '\\n\\n'";
387
+ const TA_JOIN_MARKER = "sproutboat #242";
388
+
378
389
  // Porffor's TypedArray.from only handles iterables. An array-like input such
379
390
  // as { length: 16 } silently becomes an empty typed array, including HMAC
380
391
  // keys made with Uint8Array.from({ length: 16 }, mapFn). The compiler uses a
@@ -1219,6 +1230,11 @@ export async function patchTypedArrayFrom(root: string): Promise<void> {
1219
1230
  }
1220
1231
  let edited = src;
1221
1232
  if (!edited.includes(TYPED_ARRAY_FROM_MARKER)) edited = edited.replace(TYPED_ARRAY_FROM_ANCHOR, TYPED_ARRAY_FROM_INJECT);
1233
+ if (!edited.includes(TA_JOIN_MARKER)) {
1234
+ if (!edited.includes(TA_JOIN_ANCHOR))
1235
+ throw new Error(`could not patch Porffor's typed-array method generator: anchor not found in ${file}`);
1236
+ edited = edited.replace(TA_JOIN_ANCHOR, TA_JOIN_INJECT);
1237
+ }
1222
1238
  if (!edited.includes(TA_SET_OFFSET_MARKER)) {
1223
1239
  if (!edited.includes(TA_SET_OFFSET_ANCHOR))
1224
1240
  throw new Error(`could not patch Porffor's TypedArray.prototype.set offset: anchor not found in ${file}`);