@scriptc/compiler 0.2.2 → 0.2.4

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.
Files changed (243) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +99 -27
  2. package/dist/backend/cycle-analysis.js +63 -50
  3. package/dist/backend/cycle-analysis.js.map +1 -1
  4. package/dist/backend/cycle-analysis.test.js +51 -0
  5. package/dist/backend/cycle-analysis.test.js.map +1 -1
  6. package/dist/backend/llvm/borrowed-inputs.d.ts +15 -0
  7. package/dist/backend/llvm/borrowed-inputs.js +28 -0
  8. package/dist/backend/llvm/borrowed-inputs.js.map +1 -0
  9. package/dist/backend/llvm/borrowed-receivers.d.ts +9 -0
  10. package/dist/backend/llvm/borrowed-receivers.js +58 -0
  11. package/dist/backend/llvm/borrowed-receivers.js.map +1 -0
  12. package/dist/backend/llvm/byte-numbers.d.ts +10 -0
  13. package/dist/backend/llvm/byte-numbers.js +235 -0
  14. package/dist/backend/llvm/byte-numbers.js.map +1 -0
  15. package/dist/backend/llvm/bytes-bounds.d.ts +9 -0
  16. package/dist/backend/llvm/bytes-bounds.js +189 -0
  17. package/dist/backend/llvm/bytes-bounds.js.map +1 -0
  18. package/dist/backend/llvm/call-lifetimes.d.ts +27 -0
  19. package/dist/backend/llvm/call-lifetimes.js +199 -0
  20. package/dist/backend/llvm/call-lifetimes.js.map +1 -0
  21. package/dist/backend/llvm/call-lifetimes.test.d.ts +1 -0
  22. package/dist/backend/llvm/call-lifetimes.test.js +259 -0
  23. package/dist/backend/llvm/call-lifetimes.test.js.map +1 -0
  24. package/dist/backend/llvm/checked-value-lifetimes.d.ts +9 -0
  25. package/dist/backend/llvm/checked-value-lifetimes.js +35 -0
  26. package/dist/backend/llvm/checked-value-lifetimes.js.map +1 -0
  27. package/dist/backend/llvm/counted-loops.d.ts +6 -0
  28. package/dist/backend/llvm/counted-loops.js +28 -0
  29. package/dist/backend/llvm/counted-loops.js.map +1 -0
  30. package/dist/backend/llvm/dyn.d.ts +2 -2
  31. package/dist/backend/llvm/dyn.js +79 -35
  32. package/dist/backend/llvm/dyn.js.map +1 -1
  33. package/dist/backend/llvm/emitter.d.ts +56 -7
  34. package/dist/backend/llvm/emitter.js +310 -37
  35. package/dist/backend/llvm/emitter.js.map +1 -1
  36. package/dist/backend/llvm/emitter.test.js +24 -5
  37. package/dist/backend/llvm/emitter.test.js.map +1 -1
  38. package/dist/backend/llvm/expr-async.js +4 -3
  39. package/dist/backend/llvm/expr-async.js.map +1 -1
  40. package/dist/backend/llvm/expr-bytes.d.ts +4 -4
  41. package/dist/backend/llvm/expr-bytes.js +68 -35
  42. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  43. package/dist/backend/llvm/expr-calls.js +50 -5
  44. package/dist/backend/llvm/expr-calls.js.map +1 -1
  45. package/dist/backend/llvm/expr-containers.d.ts +2 -1
  46. package/dist/backend/llvm/expr-containers.js +136 -57
  47. package/dist/backend/llvm/expr-containers.js.map +1 -1
  48. package/dist/backend/llvm/expr-context.d.ts +11 -0
  49. package/dist/backend/llvm/expr-control.js +26 -5
  50. package/dist/backend/llvm/expr-control.js.map +1 -1
  51. package/dist/backend/llvm/expr-dynamic.js +33 -31
  52. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  53. package/dist/backend/llvm/expr-primitives.js +61 -27
  54. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  55. package/dist/backend/llvm/expr-records.js +9 -3
  56. package/dist/backend/llvm/expr-records.js.map +1 -1
  57. package/dist/backend/llvm/expr-stream-bridges.js +5 -10
  58. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  59. package/dist/backend/llvm/integer-values.d.ts +10 -0
  60. package/dist/backend/llvm/integer-values.js +41 -0
  61. package/dist/backend/llvm/integer-values.js.map +1 -0
  62. package/dist/backend/llvm/integer-views.d.ts +7 -0
  63. package/dist/backend/llvm/integer-views.js +67 -0
  64. package/dist/backend/llvm/integer-views.js.map +1 -0
  65. package/dist/backend/llvm/integer-views.test.d.ts +1 -0
  66. package/dist/backend/llvm/integer-views.test.js +34 -0
  67. package/dist/backend/llvm/integer-views.test.js.map +1 -0
  68. package/dist/backend/llvm/lib-dispatch.js +3 -1
  69. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  70. package/dist/backend/llvm/lib-filesystem.js +5 -5
  71. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  72. package/dist/backend/llvm/lib-shared.js +35 -1
  73. package/dist/backend/llvm/lib-shared.js.map +1 -1
  74. package/dist/backend/llvm/local-array-reads.d.ts +42 -0
  75. package/dist/backend/llvm/local-array-reads.js +226 -0
  76. package/dist/backend/llvm/local-array-reads.js.map +1 -0
  77. package/dist/backend/llvm/local-array-reads.test.d.ts +1 -0
  78. package/dist/backend/llvm/local-array-reads.test.js +446 -0
  79. package/dist/backend/llvm/local-array-reads.test.js.map +1 -0
  80. package/dist/backend/llvm/map-read-lifetimes.d.ts +36 -0
  81. package/dist/backend/llvm/map-read-lifetimes.js +128 -0
  82. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -0
  83. package/dist/backend/llvm/map-read-lifetimes.test.d.ts +1 -0
  84. package/dist/backend/llvm/map-read-lifetimes.test.js +127 -0
  85. package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -0
  86. package/dist/backend/llvm/reference-effects.d.ts +22 -0
  87. package/dist/backend/llvm/reference-effects.js +148 -0
  88. package/dist/backend/llvm/reference-effects.js.map +1 -0
  89. package/dist/backend/llvm/reference-effects.test.d.ts +1 -0
  90. package/dist/backend/llvm/reference-effects.test.js +74 -0
  91. package/dist/backend/llvm/reference-effects.test.js.map +1 -0
  92. package/dist/backend/llvm/shapes.d.ts +7 -4
  93. package/dist/backend/llvm/shapes.js +24 -6
  94. package/dist/backend/llvm/shapes.js.map +1 -1
  95. package/dist/backend/llvm/split.js +15 -5
  96. package/dist/backend/llvm/split.js.map +1 -1
  97. package/dist/backend/llvm/split.test.js +17 -0
  98. package/dist/backend/llvm/split.test.js.map +1 -1
  99. package/dist/backend/llvm/stack-unions.d.ts +20 -0
  100. package/dist/backend/llvm/stack-unions.js +61 -0
  101. package/dist/backend/llvm/stack-unions.js.map +1 -0
  102. package/dist/backend/llvm/stack-unions.test.d.ts +1 -0
  103. package/dist/backend/llvm/stack-unions.test.js +262 -0
  104. package/dist/backend/llvm/stack-unions.test.js.map +1 -0
  105. package/dist/backend/llvm/string-construction.d.ts +10 -0
  106. package/dist/backend/llvm/string-construction.js +38 -0
  107. package/dist/backend/llvm/string-construction.js.map +1 -0
  108. package/dist/backend/llvm/string-construction.test.d.ts +1 -0
  109. package/dist/backend/llvm/string-construction.test.js +65 -0
  110. package/dist/backend/llvm/string-construction.test.js.map +1 -0
  111. package/dist/backend/llvm/string-lifetimes.d.ts +12 -0
  112. package/dist/backend/llvm/string-lifetimes.js +72 -0
  113. package/dist/backend/llvm/string-lifetimes.js.map +1 -0
  114. package/dist/backend/llvm/string-lifetimes.test.d.ts +1 -0
  115. package/dist/backend/llvm/string-lifetimes.test.js +239 -0
  116. package/dist/backend/llvm/string-lifetimes.test.js.map +1 -0
  117. package/dist/backend/mangle.d.ts +2 -0
  118. package/dist/backend/mangle.js +4 -0
  119. package/dist/backend/mangle.js.map +1 -1
  120. package/dist/backend/may-throw.js +67 -29
  121. package/dist/backend/may-throw.js.map +1 -1
  122. package/dist/backend/may-throw.test.js +104 -1
  123. package/dist/backend/may-throw.test.js.map +1 -1
  124. package/dist/backend/native-toolchain.d.ts +2 -2
  125. package/dist/backend/native-toolchain.js +2 -0
  126. package/dist/backend/native-toolchain.js.map +1 -1
  127. package/dist/frontend/input-tracker.js +3 -3
  128. package/dist/frontend/input-tracker.js.map +1 -1
  129. package/dist/frontend/input-tracker.test.js +38 -2
  130. package/dist/frontend/input-tracker.test.js.map +1 -1
  131. package/dist/frontend/lowering/collection-ingestion.d.ts +31 -0
  132. package/dist/frontend/lowering/collection-ingestion.js +224 -0
  133. package/dist/frontend/lowering/collection-ingestion.js.map +1 -0
  134. package/dist/frontend/lowering/containers/bytes.js +2 -0
  135. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  136. package/dist/frontend/lowering/containers/string-and-regexp.js +6 -0
  137. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  138. package/dist/frontend/lowering/import-meta.d.ts +10 -0
  139. package/dist/frontend/lowering/import-meta.js +52 -0
  140. package/dist/frontend/lowering/import-meta.js.map +1 -0
  141. package/dist/frontend/lowering/lower-assert.js +105 -41
  142. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  143. package/dist/frontend/lowering/lower-builtin-values.d.ts +2 -0
  144. package/dist/frontend/lowering/lower-builtin-values.js +22 -1
  145. package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
  146. package/dist/frontend/lowering/lower-builtins.d.ts +3 -13
  147. package/dist/frontend/lowering/lower-builtins.js +152 -46
  148. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  149. package/dist/frontend/lowering/lower-calls.js +6 -3
  150. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  151. package/dist/frontend/lowering/lower-classes.js +7 -7
  152. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  153. package/dist/frontend/lowering/lower-containers.d.ts +5 -1
  154. package/dist/frontend/lowering/lower-containers.js +52 -37
  155. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  156. package/dist/frontend/lowering/lower-exprs.js +47 -43
  157. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  158. package/dist/frontend/lowering/lower-inspect.js +43 -0
  159. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  160. package/dist/frontend/lowering/lower-stmts.js +72 -68
  161. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  162. package/dist/frontend/lowering/lower-system-errors.d.ts +6 -0
  163. package/dist/frontend/lowering/lower-system-errors.js +29 -0
  164. package/dist/frontend/lowering/lower-system-errors.js.map +1 -0
  165. package/dist/frontend/lowering/lowerer.d.ts +4 -3
  166. package/dist/frontend/lowering/lowerer.js +60 -25
  167. package/dist/frontend/lowering/lowerer.js.map +1 -1
  168. package/dist/frontend/lowering/surfaces.d.ts +2 -2
  169. package/dist/frontend/lowering/surfaces.js +69 -21
  170. package/dist/frontend/lowering/surfaces.js.map +1 -1
  171. package/dist/frontend/module-syntax.d.ts +1 -1
  172. package/dist/frontend/module-syntax.js +8 -1
  173. package/dist/frontend/module-syntax.js.map +1 -1
  174. package/dist/frontend/module-syntax.test.js +10 -0
  175. package/dist/frontend/module-syntax.test.js.map +1 -1
  176. package/dist/frontend/npm.d.ts +4 -0
  177. package/dist/frontend/npm.js +86 -33
  178. package/dist/frontend/npm.js.map +1 -1
  179. package/dist/frontend/npm.test.js +103 -0
  180. package/dist/frontend/npm.test.js.map +1 -1
  181. package/dist/frontend/pipeline-native.js +2 -1
  182. package/dist/frontend/pipeline-native.js.map +1 -1
  183. package/dist/frontend/services-node.js +3 -2
  184. package/dist/frontend/services-node.js.map +1 -1
  185. package/dist/frontend/services.d.ts +5 -1
  186. package/dist/frontend/services.js +9 -1
  187. package/dist/frontend/services.js.map +1 -1
  188. package/dist/frontend/services.test.js +21 -0
  189. package/dist/frontend/services.test.js.map +1 -1
  190. package/dist/frontend/ts7/ast-node.d.ts +2 -0
  191. package/dist/frontend/ts7/ast-node.js +30 -4
  192. package/dist/frontend/ts7/ast-node.js.map +1 -1
  193. package/dist/frontend/ts7/ast-node.test.js +60 -1
  194. package/dist/frontend/ts7/ast-node.test.js.map +1 -1
  195. package/dist/frontend/type-mapper.js +8 -2
  196. package/dist/frontend/type-mapper.js.map +1 -1
  197. package/dist/index.js +3 -2
  198. package/dist/index.js.map +1 -1
  199. package/dist/ir/analysis.js +4 -1
  200. package/dist/ir/analysis.js.map +1 -1
  201. package/dist/ir/byte-numbers.d.ts +20 -0
  202. package/dist/ir/byte-numbers.js +65 -0
  203. package/dist/ir/byte-numbers.js.map +1 -0
  204. package/dist/ir/byte-numbers.test.d.ts +1 -0
  205. package/dist/ir/byte-numbers.test.js +42 -0
  206. package/dist/ir/byte-numbers.test.js.map +1 -0
  207. package/dist/ir/integer-loops.d.ts +26 -0
  208. package/dist/ir/integer-loops.js +130 -1
  209. package/dist/ir/integer-loops.js.map +1 -1
  210. package/dist/ir/integer-loops.test.d.ts +1 -0
  211. package/dist/ir/integer-loops.test.js +91 -0
  212. package/dist/ir/integer-loops.test.js.map +1 -0
  213. package/dist/ir/integer-ranges.d.ts +8 -6
  214. package/dist/ir/integer-ranges.js +343 -89
  215. package/dist/ir/integer-ranges.js.map +1 -1
  216. package/dist/ir/integer-ranges.test.js +54 -3
  217. package/dist/ir/integer-ranges.test.js.map +1 -1
  218. package/dist/ir/ir.d.ts +29 -22
  219. package/dist/ir/ir.js +59 -14
  220. package/dist/ir/ir.js.map +1 -1
  221. package/dist/ir/ir.test.js +18 -0
  222. package/dist/ir/ir.test.js.map +1 -1
  223. package/dist/ir/validate.js +208 -105
  224. package/dist/ir/validate.js.map +1 -1
  225. package/dist/ir/validate.test.js +247 -0
  226. package/dist/ir/validate.test.js.map +1 -1
  227. package/dist/library/fence-eval.js +9 -2
  228. package/dist/library/fence-eval.js.map +1 -1
  229. package/dist/library/fence-eval.test.d.ts +1 -0
  230. package/dist/library/fence-eval.test.js +21 -0
  231. package/dist/library/fence-eval.test.js.map +1 -0
  232. package/dist/native/runtime-typescript.d.ts +4 -0
  233. package/dist/native/runtime-typescript.js +61 -0
  234. package/dist/native/runtime-typescript.js.map +1 -0
  235. package/dist/native/runtime-typescript.test.d.ts +1 -0
  236. package/dist/native/runtime-typescript.test.js +41 -0
  237. package/dist/native/runtime-typescript.test.js.map +1 -0
  238. package/dist/native/toolchain.js +6 -3
  239. package/dist/native/toolchain.js.map +1 -1
  240. package/dist/native/toolchain.test.js +43 -5
  241. package/dist/native/toolchain.test.js.map +1 -1
  242. package/package.json +20 -20
  243. package/surface-manifest.json +271 -17
@@ -1,3 +1,9 @@
1
+ /* IR validation — runs on every compile as the backstop for frontend bugs.
2
+ * A violation is an internal compiler error (the frontend should never
3
+ * produce invalid IR), but it still carries the user's source location:
4
+ * an ICE that points at source is a gift to whoever debugs it.
5
+ */
6
+ import { validByteNumberToken } from "./byte-numbers.js";
1
7
  import { arrayOf, BYTES_ELEMENT_NAME, BOOL, BYTES_U8, bytesOf, canAdaptDynFuncTo, canDynCheckTo, canConvertToDyn, canExitIslandToType, canMarshalIntoIsland, canMarshalTypedFuncIntoIsland, CHILD_T, CHILDSTREAM_T, CHILDWRITER_T, CRYPTOHASH_T, CRYPTOHMAC_T, DATE_T, DGRAMSOCK_T, DYN, DYN_HANDLE_KINDS, F64, ffiClassType, ffiSourceParamTypes, FILEHANDLE_T, FSWATCHER_T, HTTP2SESSION_T, HTTP2STREAM_T, HTTPCLIENTREQ_T, HTTPREQ_T, HTTPRES_T, islandPromisePayloadTag, isDynTypedRefType, isFfiCallbackParam, isFfiContextParam, isFfiReleaseParam, isJsonSafeType, isJsonStringifySafeType, isRefCounted, isSupportedArrayElem, isSupportedIndexValue, isSupportedMapKey, isSupportedMapValue, isSupportedSetElem, isUnitType, jsOpResultKind, JSVAL, NETSERVER_T, NETSOCKET_T, PROCSTREAM_T, REF_TRUTHY_KINDS, REGEX, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, SEARCH_PARAMS_T, SECURECTX_T, shapeHasAccessorSlots, SPAWNRES_T, STATS_T, STRING, SYMBOL_T, TESTCTX_T, typeEquals, typeKey, unionContainerArmsOk, URL_T, VOID } from "./ir.js";
2
8
  import { BIGINT_T, DYN_CLASS_PROPERTIES } from "./ir.js";
3
9
  import { unionWideningTags } from "./analysis.js";
@@ -358,8 +364,13 @@ export const LIB_FN_SIGS = {
358
364
  "url.port": { argTypes: [URL_T], result: STRING },
359
365
  "url.pathname": { argTypes: [URL_T], result: STRING },
360
366
  "url.href": { argTypes: [URL_T], result: STRING },
367
+ "url.set": { argTypes: [URL_T, STRING, STRING], result: VOID },
368
+ "url.setChecked": { argTypes: [URL_T, STRING, DYN], result: VOID },
361
369
  "url.fileURLToPathUrl": { argTypes: [URL_T], result: STRING },
362
370
  "url.fileURLToPathChecked": { argTypes: [DYN], result: STRING },
371
+ "url.fileURLToPathOptions": { argTypes: [DYN, DYN], result: STRING },
372
+ "url.fileURLToPathBuffer": { argTypes: [DYN, DYN], result: BYTES_U8 },
373
+ "url.pathToFileURLChecked": { argTypes: [DYN, DYN], result: URL_T },
363
374
  "url.fileURLToPathStr": { argTypes: [STRING], result: STRING },
364
375
  "url.pathToFileURL": { argTypes: [STRING], result: URL_T },
365
376
  "url.pathToFileURLPlatform": { argTypes: [STRING, BOOL], result: URL_T },
@@ -400,6 +411,15 @@ export const LIB_FN_SIGS = {
400
411
  "qs.unescape": { argTypes: [STRING], result: STRING },
401
412
  "util.parseArgs": { argTypes: [DYN], result: DYN },
402
413
  "util.parseEnv": { argTypes: [DYN], result: DYN },
414
+ "util.getSystemErrorName": { argTypes: [DYN], result: STRING },
415
+ "util.getSystemErrorMessage": { argTypes: [DYN], result: STRING },
416
+ "util.systemErrorEntries": { argTypes: [], result: DYN },
417
+ "util.stripVTControlCharacters": { argTypes: [DYN], result: STRING },
418
+ "util.toUSVString": { argTypes: [DYN], result: STRING },
419
+ "util.isDeepStrictEqual": { argTypes: [DYN, DYN, DYN], result: BOOL },
420
+ "util.styleText": { argTypes: [DYN, DYN, DYN], result: STRING },
421
+ "util.typeIs": { argTypes: [DYN, STRING], result: BOOL },
422
+ "util.typeValue": { argTypes: [DYN, STRING], result: DYN },
403
423
  "fs.statSync": { argTypes: [STRING], result: STATS_T },
404
424
  "fs.lstatSync": { argTypes: [STRING], result: STATS_T },
405
425
  "fs.fstatSync": { argTypes: [F64], result: STATS_T },
@@ -407,9 +427,17 @@ export const LIB_FN_SIGS = {
407
427
  "fs.fsyncSync": { argTypes: [F64], result: VOID },
408
428
  "fs.fdatasyncSync": { argTypes: [F64], result: VOID },
409
429
  "fs.ftruncateSync": { argTypes: [F64, F64], result: VOID },
430
+ "fs.utimesSync": { argTypes: [DYN, DYN, DYN], result: VOID },
431
+ "fs.futimesSync": { argTypes: [DYN, DYN, DYN], result: VOID },
432
+ "fs.lutimesSync": { argTypes: [DYN, DYN, DYN], result: VOID },
410
433
  "fs.readvSync": { argTypes: [F64, arrayOf(BYTES_U8), F64], result: F64 },
411
434
  "fs.writevSync": { argTypes: [F64, arrayOf(BYTES_U8), F64], result: F64 },
412
- "fs.linkSync": { argTypes: [STRING, STRING], result: VOID },
435
+ "fs.linkSync": { argTypes: [DYN, DYN], result: VOID },
436
+ "fs.symlinkSync": { argTypes: [DYN, DYN, DYN], result: VOID },
437
+ "fs.readlinkSync": { argTypes: [DYN, DYN], result: DYN },
438
+ "fs.statfsSync": { argTypes: [DYN, DYN], result: DYN },
439
+ "fs.readlinkSyncStr": { argTypes: [DYN, DYN], result: STRING },
440
+ "fs.readlinkSyncBuffer": { argTypes: [DYN, DYN], result: BYTES_U8 },
413
441
  "fs.openSync": { argTypes: [STRING, STRING], result: F64 },
414
442
  "fs.openNumericSync": { argTypes: [STRING, F64, F64], result: F64 },
415
443
  "fs.readSync": { argTypes: [F64, BYTES_U8, F64, F64, F64], result: F64 },
@@ -1051,6 +1079,14 @@ export const LIB_FN_SIGS = {
1051
1079
  "fsp.mkdirRecursiveMode": { argTypes: [STRING, F64], result: { kind: "promise", inner: VOID } },
1052
1080
  "fsp.unlink": { argTypes: [STRING], result: { kind: "promise", inner: VOID } },
1053
1081
  "fsp.chmod": { argTypes: [STRING, F64], result: { kind: "promise", inner: VOID } },
1082
+ "fsp.link": { argTypes: [DYN, DYN], result: { kind: "promise", inner: VOID } },
1083
+ "fsp.symlink": { argTypes: [DYN, DYN, DYN], result: { kind: "promise", inner: VOID } },
1084
+ "fsp.readlink": { argTypes: [DYN, DYN], result: { kind: "promise", inner: DYN } },
1085
+ "fsp.statfs": { argTypes: [DYN, DYN], result: { kind: "promise", inner: DYN } },
1086
+ "fsp.readlinkStr": { argTypes: [DYN, DYN], result: { kind: "promise", inner: STRING } },
1087
+ "fsp.readlinkBuffer": { argTypes: [DYN, DYN], result: { kind: "promise", inner: BYTES_U8 } },
1088
+ "fsp.utimes": { argTypes: [DYN, DYN, DYN], result: { kind: "promise", inner: VOID } },
1089
+ "fsp.lutimes": { argTypes: [DYN, DYN, DYN], result: { kind: "promise", inner: VOID } },
1054
1090
  "fsp.rename": { argTypes: [STRING, STRING], result: { kind: "promise", inner: VOID } },
1055
1091
  "fsp.readdir": { argTypes: [STRING], result: { kind: "promise", inner: arrayOf(STRING) } },
1056
1092
  // Result is promise<call-site Dirent record array>; the libCall case
@@ -1067,6 +1103,7 @@ export const LIB_FN_SIGS = {
1067
1103
  "fileHandle.datasync": { argTypes: [FILEHANDLE_T], result: { kind: "promise", inner: VOID } },
1068
1104
  "fileHandle.truncate": { argTypes: [FILEHANDLE_T, F64], result: { kind: "promise", inner: VOID } },
1069
1105
  "fileHandle.chmod": { argTypes: [FILEHANDLE_T, F64], result: { kind: "promise", inner: VOID } },
1106
+ "fileHandle.utimes": { argTypes: [FILEHANDLE_T, DYN, DYN], result: { kind: "promise", inner: VOID } },
1070
1107
  "fileHandle.readv": { argTypes: [FILEHANDLE_T, arrayOf(BYTES_U8), F64], result: { kind: "promise", inner: VOID } },
1071
1108
  "fileHandle.writev": { argTypes: [FILEHANDLE_T, arrayOf(BYTES_U8), F64], result: { kind: "promise", inner: VOID } },
1072
1109
  // read/write carry call-site result record shapes; the validator checks
@@ -1390,6 +1427,9 @@ export const LIB_FN_SIGS = {
1390
1427
  "assert.shapeEnd": { argTypes: [STRING, BOOL], result: VOID },
1391
1428
  "assert.regexErrTest": { argTypes: [REGEX, { kind: "object", className: "%Error" }], result: BOOL },
1392
1429
  "assert.unwantedRejection": { argTypes: [{ kind: "object", className: "%Error" }, STRING, BOOL], result: VOID },
1430
+ "assert.unwantedError": { argTypes: [STRING, BOOL, STRING, BOOL], result: VOID },
1431
+ "assert.noErrorPredicate": { argTypes: [DYN, DYN], result: BOOL },
1432
+ "assert.regexDynTest": { argTypes: [REGEX, DYN], result: BOOL },
1393
1433
  "assert.expectsErrDyn": { argTypes: [DYN, DYN, STRING, BOOL], result: VOID },
1394
1434
  // assert.ifError's typed entries (always throw; unit args never
1395
1435
  // lower). The error slot is program-dependent — any %Error-hierarchy
@@ -1761,6 +1801,7 @@ export function validateModule(mod) {
1761
1801
  });
1762
1802
  }
1763
1803
  const classesByName = new Map();
1804
+ const classFields = new Map();
1764
1805
  for (const cls of mod.classes ?? []) {
1765
1806
  if (classesByName.has(cls.name)) {
1766
1807
  errors.push({ message: `duplicate class "${cls.name}"`, loc: cls.loc });
@@ -1813,16 +1854,24 @@ export function validateModule(mod) {
1813
1854
  errors.push({ message: `class ${cls.name}: invalid symbol field metadata`, loc: cls.loc });
1814
1855
  }
1815
1856
  }
1816
- const seen = new Set();
1857
+ // Reuse duplicate detection as the field index for function validation.
1858
+ // Array.find selected the first field, even in an invalid duplicate layout.
1859
+ const seen = new Map();
1817
1860
  for (const f of cls.fields) {
1818
1861
  if (seen.has(f.name)) {
1819
1862
  errors.push({ message: `class ${cls.name}: duplicate field "${f.name}"`, loc: cls.loc });
1820
1863
  }
1821
- seen.add(f.name);
1864
+ else
1865
+ seen.set(f.name, f.type);
1822
1866
  if (isUnitType(f.type)) {
1823
1867
  errors.push({ message: `class ${cls.name}: field "${f.name}" is ${f.type.kind}`, loc: cls.loc });
1824
1868
  }
1825
1869
  }
1870
+ // Duplicate class names, like classesByName, select the last declaration.
1871
+ if (seen.size > 0)
1872
+ classFields.set(cls.name, seen);
1873
+ else
1874
+ classFields.delete(cls.name);
1826
1875
  // Constructor presence is checked at `new` sites (below): a class kept
1827
1876
  // only for its layout/type (reachability never constructs it) carries
1828
1877
  // no constructor function, and that is fine — nothing calls it.
@@ -1899,30 +1948,41 @@ export function validateModule(mod) {
1899
1948
  }
1900
1949
  const noLoc = { file: mod.sourceFile, start: 0, end: 0 };
1901
1950
  const recordsById = new Map();
1951
+ const recordIds = new Set((mod.records ?? []).map((record) => record.id));
1952
+ const recordValidation = new Map();
1902
1953
  for (const rec of mod.records ?? []) {
1903
1954
  if (recordsById.has(rec.id)) {
1904
1955
  errors.push({ message: `duplicate record shape "${rec.id}"`, loc: noLoc });
1905
1956
  }
1906
1957
  recordsById.set(rec.id, rec);
1907
- const seen = new Set();
1958
+ const fields = new Map();
1908
1959
  for (const f of rec.fields) {
1909
- if (seen.has(f.name)) {
1960
+ if (fields.has(f.name)) {
1910
1961
  errors.push({ message: `record ${rec.id}: duplicate field "${f.name}"`, loc: noLoc });
1911
1962
  }
1912
- seen.add(f.name);
1963
+ else
1964
+ fields.set(f.name, f.type);
1913
1965
  // Unit kinds (undefinedT/nullT) exist only as union arms — a BARE
1914
1966
  // unit field is as malformed as a void one. dyn and jsval slots
1915
1967
  // are valid; both are refcounted values held by the record shape.
1916
1968
  if (f.type.kind === "void" || isUnitType(f.type)) {
1917
1969
  errors.push({ message: `record ${rec.id}: field "${f.name}" is ${f.type.kind}`, loc: noLoc });
1918
1970
  }
1919
- if (f.type.kind === "record" && !new Set((mod.records ?? []).map((r) => r.id)).has(f.type.shapeId)) {
1971
+ if (f.type.kind === "record" && !recordIds.has(f.type.shapeId)) {
1920
1972
  errors.push({
1921
1973
  message: `record ${rec.id}: field "${f.name}" references undeclared shape "${f.type.shapeId}"`,
1922
1974
  loc: noLoc,
1923
1975
  });
1924
1976
  }
1925
1977
  }
1978
+ // Reads historically use the first duplicate field, while literal and
1979
+ // clone checks use the last. Preserve both on malformed shapes; valid
1980
+ // shapes share one index. All indexes belong to this validation call.
1981
+ recordValidation.set(rec.id, {
1982
+ fields,
1983
+ initializationFields: fields.size === rec.fields.length ? fields : new Map(rec.fields.map((field) => [field.name, field.type])),
1984
+ hasAccessorSlots: shapeHasAccessorSlots(rec),
1985
+ });
1926
1986
  // Canonical order is the shape's identity — enforce it.
1927
1987
  const sorted = [...rec.fields].map((f) => f.name).sort();
1928
1988
  if (rec.fields.some((f, i) => f.name !== sorted[i])) {
@@ -1975,7 +2035,7 @@ export function validateModule(mod) {
1975
2035
  for (const candidate of cases) {
1976
2036
  const arm = u.arms[candidate.tag];
1977
2037
  const record = arm?.kind === "record" ? recordsById.get(arm.shapeId) : undefined;
1978
- const member = record?.fields.find((entry) => entry.name === field);
2038
+ const member = record ? recordValidation.get(record.id)?.fields.get(field) : undefined;
1979
2039
  if (!Number.isInteger(candidate.tag) || candidate.tag < 0 || !member || tags.has(candidate.tag)) {
1980
2040
  errors.push({ message: `union ${u.id}: invalid discriminant tag ${candidate.tag}`, loc: noLoc });
1981
2041
  }
@@ -1988,8 +2048,8 @@ export function validateModule(mod) {
1988
2048
  errors.push({ message: `union ${u.id}: undefined discriminant value`, loc: noLoc });
1989
2049
  continue;
1990
2050
  }
1991
- const fieldTypes = member?.type.kind === "union"
1992
- ? unionsById.get(member.type.unionId)?.arms ?? [] : member ? [member.type] : [];
2051
+ const fieldTypes = member?.kind === "union"
2052
+ ? unionsById.get(member.unionId)?.arms ?? [] : member ? [member] : [];
1993
2053
  const valid = fieldTypes.some((type) => typeof value === "string" ? type.kind === "string"
1994
2054
  : typeof value === "boolean" ? type.kind === "bool"
1995
2055
  : typeof value === "number" && Number.isFinite(value) && type.kind === "f64");
@@ -2152,12 +2212,34 @@ export function validateModule(mod) {
2152
2212
  }
2153
2213
  }
2154
2214
  }
2215
+ // Class metadata is invariant across functions. Keep field lookup, hierarchy
2216
+ // membership, and virtual-call resolution local to this validation invocation.
2217
+ const classValidation = {
2218
+ fields: classFields,
2219
+ hierarchy: new Set(), implementations: new Map(), virtualCalls: new Map(),
2220
+ };
2221
+ for (const cls of classesByName.values()) {
2222
+ if (cls.base !== undefined) {
2223
+ classValidation.hierarchy.add(cls.name);
2224
+ classValidation.hierarchy.add(cls.base);
2225
+ }
2226
+ if (cls.name === RUNTIME_EMITTER_CLASS)
2227
+ classValidation.hierarchy.add(cls.name);
2228
+ for (const method of new Set(cls.methods ?? [])) {
2229
+ if (cls.abstractMethods?.includes(method))
2230
+ continue;
2231
+ let implementations = classValidation.implementations.get(method);
2232
+ if (!implementations)
2233
+ classValidation.implementations.set(method, (implementations = []));
2234
+ implementations.push(cls);
2235
+ }
2236
+ }
2155
2237
  for (const fn of mod.functions) {
2156
- validateFunction(fn, functionsByName, ffiByName, classesByName, recordsById, unionsById, globalsById, errors);
2238
+ validateFunction(fn, functionsByName, ffiByName, classesByName, recordsById, recordValidation, unionsById, globalsById, classValidation, errors);
2157
2239
  }
2158
2240
  return errors;
2159
2241
  }
2160
- function validateFunction(fn, functions, ffiByName, classes, records, unions, globals, errors) {
2242
+ function validateFunction(fn, functions, ffiByName, classes, records, recordValidation, unions, globals, classValidation, errors) {
2161
2243
  const locals = new Map(fn.locals.map((l) => [l.id, l]));
2162
2244
  const err = (message, loc) => errors.push({ message: `in ${fn.name}: ${message}`, loc });
2163
2245
  const asyncCaches = [
@@ -2200,17 +2282,41 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
2200
2282
  }
2201
2283
  return false;
2202
2284
  };
2203
- const hierarchy = new Set();
2204
- for (const c of classes.values()) {
2205
- if (c.base !== undefined) {
2206
- hierarchy.add(c.name);
2207
- hierarchy.add(c.base);
2208
- }
2209
- // The runtime emitter class is ALWAYS a hierarchy member: ScrEmitter
2210
- // carries its vtable word even with no subclass in the program.
2211
- if (c.name === RUNTIME_EMITTER_CLASS)
2212
- hierarchy.add(c.name);
2213
- }
2285
+ const hierarchy = classValidation.hierarchy;
2286
+ const virtualCallInfo = (className, method) => {
2287
+ let methods = classValidation.virtualCalls.get(className);
2288
+ const cached = methods?.get(method);
2289
+ if (cached)
2290
+ return cached;
2291
+ if (!methods)
2292
+ classValidation.virtualCalls.set(className, (methods = new Map()));
2293
+ let declared = false;
2294
+ let implementation;
2295
+ for (let c = classes.get(className); c; c = c.base !== undefined ? classes.get(c.base) : undefined) {
2296
+ if (c.methods?.includes(method)) {
2297
+ declared = true;
2298
+ if (!c.abstractMethods?.includes(method)) {
2299
+ implementation = functions.get(`%${c.name}.${method}`);
2300
+ break;
2301
+ }
2302
+ }
2303
+ }
2304
+ let hasOverride = false;
2305
+ if (declared) {
2306
+ // Preserve class-table order when choosing an abstract slot's ABI.
2307
+ for (const c of classValidation.implementations.get(method) ?? []) {
2308
+ if (!isStrictSubclass(c.name, className))
2309
+ continue;
2310
+ hasOverride = true;
2311
+ implementation ??= functions.get(`%${c.name}.${method}`);
2312
+ if (implementation)
2313
+ break;
2314
+ }
2315
+ }
2316
+ const result = { declared, hasOverride, implementation };
2317
+ methods.set(method, result);
2318
+ return result;
2319
+ };
2214
2320
  // Unit kinds live only inside unions: a bare-unit local, param, or
2215
2321
  // return type is frontend breakage (mapType never produces them).
2216
2322
  for (const l of fn.locals) {
@@ -2636,15 +2742,15 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
2636
2742
  // dyn field with fieldDyn set (validated numeric read-modify-write).
2637
2743
  checkExpr(e.obj);
2638
2744
  const cls = classes.get(e.className);
2639
- const field = cls?.fields.find((f) => f.name === e.field);
2745
+ const fieldType = classValidation.fields.get(e.className)?.get(e.field);
2640
2746
  if (!cls)
2641
2747
  err(`fieldIncDec on undeclared class "${e.className}"`, e.loc);
2642
- else if (!field)
2748
+ else if (!fieldType)
2643
2749
  err(`class ${e.className} has no field "${e.field}"`, e.loc);
2644
2750
  else {
2645
2751
  expectType(e.obj, { kind: "object", className: e.className }, "fieldIncDec receiver");
2646
- if (e.fieldDyn ? field.type.kind !== "dyn" : field.type.kind !== "f64") {
2647
- err(`fieldIncDec ${e.className}.${e.field} field/flag mismatch (${field.type.kind})`, e.loc);
2752
+ if (e.fieldDyn ? fieldType.kind !== "dyn" : fieldType.kind !== "f64") {
2753
+ err(`fieldIncDec ${e.className}.${e.field} field/flag mismatch (${fieldType.kind})`, e.loc);
2648
2754
  }
2649
2755
  }
2650
2756
  if (e.type.kind !== "f64")
@@ -3140,17 +3246,17 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
3140
3246
  break;
3141
3247
  }
3142
3248
  const recv = e.receiver.type;
3143
- // The dvGet* getters read through a DataView — always a bytes<u8>
3249
+ // The DataView getters and setters use a bytes<u8>
3144
3250
  // view; dataViewNew and byteOffset take ANY elem kind (views form
3145
3251
  // over any typed array's storage, owners answer byteOffset 0).
3146
- const isDvGet = e.method.startsWith("dvGet");
3252
+ const isDv = e.method.startsWith("dvGet") || e.method.startsWith("dvSet");
3147
3253
  const isNum = e.method === "readNum" || e.method === "writeNum" || e.method === "readNumVar" || e.method === "writeNumVar";
3148
3254
  const U8_ONLY_EXTRA = new Set([
3149
3255
  "toString", "toStringVar", "equals", "compareBuf", "indexOf", "lastIndexOf", "includes",
3150
3256
  "indexOfNum", "lastIndexOfNum", "includesNum", "fill", "fillNum", "fillStr",
3151
3257
  "copy", "swap16", "swap32", "swap64", "writeStr",
3152
3258
  ]);
3153
- const u8Only = U8_ONLY_EXTRA.has(e.method) || isNum || isDvGet;
3259
+ const u8Only = U8_ONLY_EXTRA.has(e.method) || isNum || isDv;
3154
3260
  if (u8Only && recv.elem !== "u8") {
3155
3261
  err(`bytesIntrinsic ${e.method} on a ${recv.elem} receiver (u8 only)`, e.loc);
3156
3262
  }
@@ -3160,6 +3266,9 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
3160
3266
  if (isNum && e.args[0]?.kind !== "strLit") {
3161
3267
  err(`bytesIntrinsic ${e.method} args[0] must be a strLit kind token`, e.loc);
3162
3268
  }
3269
+ else if (isNum && e.args[0]?.kind === "strLit" && !validByteNumberToken(e.method, e.args[0].value)) {
3270
+ err(`bytesIntrinsic ${e.method} invalid kind token '${e.args[0].value}'`, e.loc);
3271
+ }
3163
3272
  // fillStr/writeStr carry their NORMALIZED encoding as args[1],
3164
3273
  // always a strLit (the frontend folds the aliases).
3165
3274
  if ((e.method === "fillStr" || e.method === "writeStr") && e.args[1]?.kind !== "strLit") {
@@ -3414,19 +3523,26 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
3414
3523
  }
3415
3524
  break;
3416
3525
  }
3417
- const sig = e.method === "set"
3418
- ? { argTypes: [key, value], result: VOID }
3419
- : e.method === "has" || e.method === "delete"
3420
- ? { argTypes: [key], result: BOOL }
3421
- : e.method === "clear" || e.method === "iterEnter" || e.method === "iterExit"
3422
- ? { argTypes: [], result: VOID }
3423
- : e.method === "iterLive"
3424
- ? { argTypes: [F64], result: BOOL }
3425
- : e.method === "iterKey"
3426
- ? { argTypes: [F64], result: key }
3427
- : e.method === "iterValue"
3428
- ? { argTypes: [F64], result: value }
3429
- : { argTypes: [], result: F64 }; // size / iterCount
3526
+ if (e.method === "valueSet" && !isSupportedSetElem(value, value.kind === "union" ? unions.get(value.unionId)?.arms : undefined)) {
3527
+ err(`mapIntrinsic valueSet element kind ${value.kind} (frontend must fence)`, e.loc);
3528
+ }
3529
+ const sig = e.method === "clone"
3530
+ ? { argTypes: [], result: e.receiver.type }
3531
+ : e.method === "keySet" || e.method === "valueSet"
3532
+ ? { argTypes: [], result: { kind: "set", elem: e.method === "keySet" ? key : value } }
3533
+ : e.method === "set"
3534
+ ? { argTypes: [key, value], result: VOID }
3535
+ : e.method === "has" || e.method === "delete"
3536
+ ? { argTypes: [key], result: BOOL }
3537
+ : e.method === "clear" || e.method === "iterEnter" || e.method === "iterExit"
3538
+ ? { argTypes: [], result: VOID }
3539
+ : e.method === "iterLive"
3540
+ ? { argTypes: [F64], result: BOOL }
3541
+ : e.method === "iterKey"
3542
+ ? { argTypes: [F64], result: key }
3543
+ : e.method === "iterValue"
3544
+ ? { argTypes: [F64], result: value }
3545
+ : { argTypes: [], result: F64 }; // size / iterCount
3430
3546
  if (e.args.length !== sig.argTypes.length) {
3431
3547
  err(`mapIntrinsic ${e.method}: ${e.args.length} args, expected ${sig.argTypes.length}`, e.loc);
3432
3548
  }
@@ -3463,19 +3579,21 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
3463
3579
  break;
3464
3580
  }
3465
3581
  const { elem } = e.receiver.type;
3466
- const sig = e.method === "add"
3467
- ? { argTypes: [elem], result: VOID }
3468
- : e.method === "has" || e.method === "delete"
3469
- ? { argTypes: [elem], result: BOOL }
3470
- : e.method === "clear" || e.method === "iterEnter" || e.method === "iterExit"
3471
- ? { argTypes: [], result: VOID }
3472
- : e.method === "iterLive"
3473
- ? { argTypes: [F64], result: BOOL }
3474
- : e.method === "iterKey"
3475
- ? { argTypes: [F64], result: elem }
3476
- : e.method === "toArray"
3477
- ? { argTypes: [], result: arrayOf(elem) }
3478
- : { argTypes: [], result: F64 }; // size / iterCount
3582
+ const sig = e.method === "clone"
3583
+ ? { argTypes: [], result: e.receiver.type }
3584
+ : e.method === "add"
3585
+ ? { argTypes: [elem], result: VOID }
3586
+ : e.method === "has" || e.method === "delete"
3587
+ ? { argTypes: [elem], result: BOOL }
3588
+ : e.method === "clear" || e.method === "iterEnter" || e.method === "iterExit"
3589
+ ? { argTypes: [], result: VOID }
3590
+ : e.method === "iterLive"
3591
+ ? { argTypes: [F64], result: BOOL }
3592
+ : e.method === "iterKey"
3593
+ ? { argTypes: [F64], result: elem }
3594
+ : e.method === "toArray"
3595
+ ? { argTypes: [], result: arrayOf(elem) }
3596
+ : { argTypes: [], result: F64 }; // size / iterCount
3479
3597
  if (e.args.length !== sig.argTypes.length) {
3480
3598
  err(`setIntrinsic ${e.method}: ${e.args.length} args, expected ${sig.argTypes.length}`, e.loc);
3481
3599
  }
@@ -3668,14 +3786,14 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
3668
3786
  case "fieldGet": {
3669
3787
  checkExpr(e.obj);
3670
3788
  const cls = classes.get(e.className);
3671
- const field = cls?.fields.find((f) => f.name === e.field);
3789
+ const fieldType = classValidation.fields.get(e.className)?.get(e.field);
3672
3790
  if (!cls)
3673
3791
  err(`fieldGet on undeclared class "${e.className}"`, e.loc);
3674
- else if (!field)
3792
+ else if (!fieldType)
3675
3793
  err(`class ${e.className} has no field "${e.field}"`, e.loc);
3676
3794
  else {
3677
3795
  expectType(e.obj, { kind: "object", className: e.className }, "fieldGet receiver");
3678
- if (!typeEquals(e.type, field.type)) {
3796
+ if (!typeEquals(e.type, fieldType)) {
3679
3797
  err(`fieldGet ${e.className}.${e.field} type mismatch`, e.loc);
3680
3798
  }
3681
3799
  }
@@ -3849,30 +3967,15 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
3849
3967
  // is then well-formed against any concrete override below — the
3850
3968
  // frontend's override-exactness rule makes every implementation
3851
3969
  // ABI-identical, so any one of them carries the slot's signature.
3852
- let declared = false;
3853
- let impl;
3854
- for (let c = classes.get(e.className); c; c = c.base !== undefined ? classes.get(c.base) : undefined) {
3855
- if (c.methods?.includes(e.method)) {
3856
- declared = true;
3857
- if (!c.abstractMethods?.includes(e.method)) {
3858
- impl = functions.get(`%${c.name}.${e.method}`);
3859
- break;
3860
- }
3861
- }
3862
- }
3863
- if (!declared) {
3970
+ const info = virtualCallInfo(e.className, e.method);
3971
+ if (!info.declared) {
3864
3972
  err(`virtualCall ${e.className}.${e.method}: no declaration on the base chain`, e.loc);
3865
3973
  break;
3866
3974
  }
3867
- const concreteBelow = [...classes.values()].filter((c) => c.methods?.includes(e.method) &&
3868
- !c.abstractMethods?.includes(e.method) &&
3869
- isStrictSubclass(c.name, e.className));
3870
- if (concreteBelow.length === 0) {
3975
+ if (!info.hasOverride) {
3871
3976
  err(`virtualCall ${e.className}.${e.method}: no concrete override below the static class`, e.loc);
3872
3977
  }
3873
- impl ??= concreteBelow
3874
- .map((c) => functions.get(`%${c.name}.${e.method}`))
3875
- .find((f) => f !== undefined);
3978
+ const impl = info.implementation;
3876
3979
  if (!impl) {
3877
3980
  err(`virtualCall ${e.className}.${e.method}: no implementation function exists`, e.loc);
3878
3981
  break;
@@ -3908,7 +4011,7 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
3908
4011
  err(`recordLit of undeclared shape "${e.type.shapeId}"`, e.loc);
3909
4012
  break;
3910
4013
  }
3911
- const want = new Map(shape.fields.map((f) => [f.name, f.type]));
4014
+ const want = recordValidation.get(shape.id).initializationFields;
3912
4015
  const seen = new Set();
3913
4016
  for (const f of e.fields) {
3914
4017
  checkExpr(f.value);
@@ -3964,11 +4067,11 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
3964
4067
  err(`recordClone of undeclared shape "${e.type.shapeId}"`, e.loc);
3965
4068
  break;
3966
4069
  }
3967
- if (shape.tuple || shape.indexValue || shapeHasAccessorSlots(shape)) {
4070
+ if (shape.tuple || shape.indexValue || recordValidation.get(shape.id).hasAccessorSlots) {
3968
4071
  err(`recordClone requires a plain declared-field shape, got ${shape.id}`, e.loc);
3969
4072
  }
3970
4073
  expectType(e.source, e.type, "recordClone source");
3971
- const want = new Map(shape.fields.map((f) => [f.name, f.type]));
4074
+ const want = recordValidation.get(shape.id).initializationFields;
3972
4075
  const seen = new Set();
3973
4076
  for (const f of e.overrides) {
3974
4077
  checkExpr(f.value);
@@ -3986,14 +4089,14 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
3986
4089
  case "recordGet": {
3987
4090
  checkExpr(e.obj);
3988
4091
  const shape = records.get(e.shapeId);
3989
- const field = shape?.fields.find((f) => f.name === e.field);
4092
+ const field = recordValidation.get(e.shapeId)?.fields.get(e.field);
3990
4093
  if (!shape)
3991
4094
  err(`recordGet on undeclared shape "${e.shapeId}"`, e.loc);
3992
4095
  else if (!field)
3993
4096
  err(`shape ${e.shapeId} has no field "${e.field}"`, e.loc);
3994
4097
  else {
3995
4098
  expectType(e.obj, { kind: "record", shapeId: e.shapeId }, "recordGet receiver");
3996
- if (!typeEquals(e.type, field.type)) {
4099
+ if (!typeEquals(e.type, field)) {
3997
4100
  err(`recordGet ${e.shapeId}.${e.field} type mismatch`, e.loc);
3998
4101
  }
3999
4102
  }
@@ -4365,9 +4468,9 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
4365
4468
  }
4366
4469
  def.arms.forEach((arm, i) => {
4367
4470
  const fieldType = arm.kind === "record"
4368
- ? records.get(arm.shapeId)?.fields.find((f) => f.name === e.field)?.type
4471
+ ? recordValidation.get(arm.shapeId)?.fields.get(e.field)
4369
4472
  : arm.kind === "object"
4370
- ? classes.get(arm.className)?.fields.find((f) => f.name === e.field)?.type
4473
+ ? classValidation.fields.get(arm.className)?.get(e.field)
4371
4474
  : undefined;
4372
4475
  if (!fieldType) {
4373
4476
  err(`unionDisc: arm ${i} of ${e.unionId} has no field "${e.field}"`, e.loc);
@@ -4430,7 +4533,7 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
4430
4533
  return;
4431
4534
  }
4432
4535
  const literal = e.key.kind === "strLit" ? e.key.value : null;
4433
- const declared = literal !== null ? shape.fields.find((f) => f.name === literal)?.type : undefined;
4536
+ const declared = literal !== null ? recordValidation.get(shape.id)?.fields.get(literal) : undefined;
4434
4537
  if (declared) {
4435
4538
  if (!surfaces(declared)) {
4436
4539
  err(`unionKeyGet: arm ${i} field "${literal}" of type ${declared.kind} cannot surface as the result`, e.loc);
@@ -4651,11 +4754,11 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
4651
4754
  const countName = e.fn === "fileHandle.read" || e.fn === "fileHandle.readv" ? "bytesRead" : "bytesWritten";
4652
4755
  const payloadName = e.fn === "fileHandle.readv" || e.fn === "fileHandle.writev" ? "buffers" : "buffer";
4653
4756
  const payload = e.args[1]?.type;
4654
- const count = shape?.fields.find((f) => f.name === countName);
4655
- const buffer = shape?.fields.find((f) => f.name === payloadName);
4757
+ const count = shape ? recordValidation.get(shape.id)?.fields.get(countName) : undefined;
4758
+ const buffer = shape ? recordValidation.get(shape.id)?.fields.get(payloadName) : undefined;
4656
4759
  const ok = shape !== undefined && !shape.tuple && shape.indexValue === undefined && shape.fields.length === 2 &&
4657
- count?.type.kind === "f64" && payload !== undefined && buffer !== undefined &&
4658
- typeEquals(buffer.type, payload);
4760
+ count?.kind === "f64" && payload !== undefined && buffer !== undefined &&
4761
+ typeEquals(buffer, payload);
4659
4762
  if (!ok) {
4660
4763
  err(`libCall ${e.fn} must return a promise of { ${countName}: number, ${payloadName} }`, e.loc);
4661
4764
  }
@@ -5948,17 +6051,17 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
5948
6051
  break;
5949
6052
  }
5950
6053
  const rec = records.get(resultT.shapeId);
5951
- const doneF = rec?.fields.find((f) => f.name === "done");
5952
- const valueF = rec?.fields.find((f) => f.name === "value");
5953
- if (!rec || rec.fields.length !== 2 || doneF?.type.kind !== "bool" || valueF === undefined) {
6054
+ const doneF = rec ? recordValidation.get(rec.id)?.fields.get("done") : undefined;
6055
+ const valueF = rec ? recordValidation.get(rec.id)?.fields.get("value") : undefined;
6056
+ if (!rec || rec.fields.length !== 2 || doneF?.kind !== "bool" || valueF === undefined) {
5954
6057
  err(`genResume result record ${resultT.shapeId} is not { done: bool, value: V }`, e.loc);
5955
6058
  break;
5956
6059
  }
5957
- if (valueF.type.kind === "dyn")
6060
+ if (valueF.kind === "dyn")
5958
6061
  break;
5959
- const vdef = valueF.type.kind === "union" ? unions.get(valueF.type.unionId) : undefined;
6062
+ const vdef = valueF.kind === "union" ? unions.get(valueF.unionId) : undefined;
5960
6063
  if (!vdef || !vdef.arms.some((a) => a.kind === "undefinedT")) {
5961
- err(`genResume value slot ${typeKey(valueF.type)} is neither dyn nor an undefined-armed union`, e.loc);
6064
+ err(`genResume value slot ${typeKey(valueF)} is neither dyn nor an undefined-armed union`, e.loc);
5962
6065
  }
5963
6066
  break;
5964
6067
  }
@@ -6042,10 +6145,10 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
6042
6145
  break;
6043
6146
  }
6044
6147
  const shape = records.get(e.type.shapeId);
6045
- const fields = new Map(shape?.fields.map((f) => [f.name, f.type]) ?? []);
6046
- const prom = fields.get("promise");
6047
- const resolve = fields.get("resolve");
6048
- const reject = fields.get("reject");
6148
+ const fields = recordValidation.get(e.type.shapeId)?.initializationFields;
6149
+ const prom = fields?.get("promise");
6150
+ const resolve = fields?.get("resolve");
6151
+ const reject = fields?.get("reject");
6049
6152
  if (!shape || shape.fields.length !== 3 || !prom || !resolve || !reject) {
6050
6153
  err("promiseWithResolvers record must be { promise, resolve, reject }", e.loc);
6051
6154
  break;
@@ -6357,14 +6460,14 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
6357
6460
  checkExpr(s.obj);
6358
6461
  checkExpr(s.value);
6359
6462
  const cls = classes.get(s.className);
6360
- const field = cls?.fields.find((f) => f.name === s.field);
6463
+ const fieldType = classValidation.fields.get(s.className)?.get(s.field);
6361
6464
  if (!cls)
6362
6465
  err(`fieldSet on undeclared class "${s.className}"`, s.loc);
6363
- else if (!field)
6466
+ else if (!fieldType)
6364
6467
  err(`class ${s.className} has no field "${s.field}"`, s.loc);
6365
6468
  else {
6366
6469
  expectType(s.obj, { kind: "object", className: s.className }, "fieldSet receiver");
6367
- expectType(s.value, field.type, `fieldSet ${s.className}.${s.field}`);
6470
+ expectType(s.value, fieldType, `fieldSet ${s.className}.${s.field}`);
6368
6471
  }
6369
6472
  break;
6370
6473
  }
@@ -6372,14 +6475,14 @@ function validateFunction(fn, functions, ffiByName, classes, records, unions, gl
6372
6475
  checkExpr(s.obj);
6373
6476
  checkExpr(s.value);
6374
6477
  const shape = records.get(s.shapeId);
6375
- const field = shape?.fields.find((f) => f.name === s.field);
6478
+ const field = recordValidation.get(s.shapeId)?.fields.get(s.field);
6376
6479
  if (!shape)
6377
6480
  err(`recordSet on undeclared shape "${s.shapeId}"`, s.loc);
6378
6481
  else if (!field)
6379
6482
  err(`shape ${s.shapeId} has no field "${s.field}"`, s.loc);
6380
6483
  else {
6381
6484
  expectType(s.obj, { kind: "record", shapeId: s.shapeId }, "recordSet receiver");
6382
- expectType(s.value, field.type, `recordSet ${s.shapeId}.${s.field}`);
6485
+ expectType(s.value, field, `recordSet ${s.shapeId}.${s.field}`);
6383
6486
  }
6384
6487
  break;
6385
6488
  }