jz 0.8.0 → 0.9.0

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 (73) hide show
  1. package/README.md +56 -9
  2. package/bench/README.md +121 -50
  3. package/bench/bench.svg +27 -27
  4. package/cli.js +3 -1
  5. package/dist/interop.js +1 -1
  6. package/dist/jz.js +7541 -6178
  7. package/index.js +165 -74
  8. package/interop.js +189 -17
  9. package/jzify/arguments.js +32 -1
  10. package/jzify/async.js +409 -0
  11. package/jzify/classes.js +136 -18
  12. package/jzify/generators.js +639 -0
  13. package/jzify/hoist-vars.js +73 -1
  14. package/jzify/index.js +123 -4
  15. package/jzify/names.js +1 -0
  16. package/jzify/transform.js +239 -1
  17. package/layout.js +48 -3
  18. package/module/array.js +318 -43
  19. package/module/atomics.js +144 -0
  20. package/module/collection.js +1429 -155
  21. package/module/core.js +431 -40
  22. package/module/date.js +142 -120
  23. package/module/fs.js +144 -0
  24. package/module/function.js +6 -3
  25. package/module/index.js +4 -1
  26. package/module/json.js +270 -49
  27. package/module/math.js +1032 -145
  28. package/module/number.js +532 -163
  29. package/module/object.js +353 -95
  30. package/module/regex.js +157 -7
  31. package/module/schema.js +100 -2
  32. package/module/string.js +428 -93
  33. package/module/typedarray.js +264 -72
  34. package/module/web.js +36 -0
  35. package/package.json +5 -5
  36. package/src/abi/string.js +82 -11
  37. package/src/ast.js +11 -5
  38. package/src/autoload.js +28 -1
  39. package/src/bridge.js +7 -2
  40. package/src/compile/analyze-scans.js +22 -7
  41. package/src/compile/analyze.js +136 -30
  42. package/src/compile/dyn-closure-tables.js +277 -0
  43. package/src/compile/emit-assign.js +281 -15
  44. package/src/compile/emit.js +1055 -70
  45. package/src/compile/index.js +277 -29
  46. package/src/compile/infer.js +42 -4
  47. package/src/compile/inplace-store.js +329 -0
  48. package/src/compile/loop-recurrence.js +167 -0
  49. package/src/compile/narrow.js +555 -18
  50. package/src/compile/peel-stencil.js +5 -0
  51. package/src/compile/plan/index.js +45 -3
  52. package/src/compile/plan/inline.js +8 -1
  53. package/src/compile/plan/literals.js +39 -0
  54. package/src/compile/plan/scope.js +430 -23
  55. package/src/compile/program-facts.js +732 -38
  56. package/src/ctx.js +64 -9
  57. package/src/helper-counters.js +7 -1
  58. package/src/ir.js +113 -5
  59. package/src/kind-traits.js +66 -3
  60. package/src/kind.js +84 -12
  61. package/src/op-policy.js +7 -4
  62. package/src/optimize/index.js +1179 -704
  63. package/src/optimize/recurse.js +2 -2
  64. package/src/optimize/vectorize.js +982 -67
  65. package/src/prepare/index.js +809 -67
  66. package/src/prepare/math-kernel.js +331 -0
  67. package/src/prepare/pre-eval.js +714 -0
  68. package/src/snapshot.js +194 -0
  69. package/src/type.js +1170 -56
  70. package/src/wat/assemble.js +403 -65
  71. package/src/wat/codegen.js +74 -14
  72. package/transform.js +113 -4
  73. package/wasi.js +3 -0
package/module/date.js CHANGED
@@ -9,7 +9,7 @@
9
9
  * @module date
10
10
  */
11
11
 
12
- import { typed, asF64, toNumF64, allocPtr, temp } from '../src/ir.js'
12
+ import { typed, asF64, toNumF64, allocPtr, temp, NULL_WAT } from '../src/ir.js'
13
13
  import { emit, deps } from '../src/bridge.js'
14
14
  import { inc, PTR } from '../src/ctx.js'
15
15
  import { VAL } from '../src/reps.js'
@@ -51,10 +51,23 @@ export default (ctx) => {
51
51
  __date_write2: [],
52
52
  __date_write3: [],
53
53
  __date_write4: [],
54
- __date_to_iso_string: ['__mkstr', '__alloc', '__itoa', '__date_year_from_time', '__date_month_from_time', '__date_date_from_time', '__date_hour_from_time', '__date_min_from_time', '__date_sec_from_time', '__date_ms_from_time', '__date_write2', '__date_write3', '__date_write4'],
55
- __date_to_utc_string: ['__mkstr', '__alloc', '__itoa', '__date_weekday', '__date_date_from_time', '__date_month_from_time', '__date_year_from_time', '__date_hour_from_time', '__date_min_from_time', '__date_sec_from_time', '__date_write2', '__date_write4'],
54
+ __date_to_iso_string: ['__mkstr', '__alloc', '__date_year_from_time', '__date_month_from_time', '__date_date_from_time', '__date_hour_from_time', '__date_min_from_time', '__date_sec_from_time', '__date_ms_from_time', '__date_write2', '__date_write3', '__date_write4'],
55
+ __date_to_utc_string: ['__mkstr', '__alloc', '__itoa', '__date_wd_name', '__date_mon_name', '__date_weekday', '__date_date_from_time', '__date_month_from_time', '__date_year_from_time', '__date_hour_from_time', '__date_min_from_time', '__date_sec_from_time', '__date_write2', '__date_write4'],
56
+ __date_wd_name: [],
57
+ __date_mon_name: [],
58
+ __date_invalid_string: ['__alloc', '__mkstr'],
59
+ __date_to_date_string: ['__mkstr', '__alloc', '__itoa', '__date_invalid_string', '__date_wd_name', '__date_mon_name', '__date_weekday', '__date_month_from_time', '__date_date_from_time', '__date_year_from_time', '__date_write2', '__date_write4'],
60
+ __date_to_time_string: ['__mkstr', '__alloc', '__date_invalid_string', '__date_hour_from_time', '__date_min_from_time', '__date_sec_from_time', '__date_write2'],
61
+ __date_to_json: ['__date_to_iso_string'],
56
62
  })
57
63
 
64
+ // Brand: dates carry their own registered 1-slot schema (a key no user
65
+ // identifier can produce) instead of aux=0 — which aliases schema id 0, the
66
+ // first real literal shape, so dyn-get / {...date} / structuredClone would
67
+ // read that schema's layout against the date's single-slot block. With the
68
+ // brand, every generic OBJECT consumer sees the true shape.
69
+ ctx.schema.dateSid = ctx.schema.register(['\x00time'])
70
+
58
71
  const dateArg = (node, fallback, required = false) => {
59
72
  if (node === undefined) return typed(['f64.const', required ? NaN : fallback], 'f64')
60
73
  if (Array.isArray(node) && node[0] == null && node[1] === undefined) return typed(['f64.const', NaN], 'f64')
@@ -381,27 +394,59 @@ export default (ctx) => {
381
394
  (i32.store8 (i32.add (local.get $buf) (i32.const 2)) (i32.add (i32.const 48) (local.get $d)))
382
395
  (i32.store8 (i32.add (local.get $buf) (i32.const 3)) (i32.add (i32.const 48) (i32.rem_u (local.get $v) (i32.const 10)))))`
383
396
 
397
+ // ── Name tables & fixed text ──────────────────────────────────────────────
398
+ // 3-letter weekday/month names as generated if-chains of byte stores — one
399
+ // table shared by toUTCString and toDateString.
400
+
401
+ const name3 = (fn, names) => `(func $${fn} (param $p i32) (param $i i32)
402
+ ${names.map((n, i) => `(if (i32.eq (local.get $i) (i32.const ${i}))
403
+ (then
404
+ (i32.store8 (local.get $p) (i32.const ${n.charCodeAt(0)}))
405
+ (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const ${n.charCodeAt(1)}))
406
+ (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const ${n.charCodeAt(2)}))))`).join('\n ')})`
407
+
408
+ ctx.core.stdlib['__date_wd_name'] = name3('__date_wd_name', ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'])
409
+ ctx.core.stdlib['__date_mon_name'] = name3('__date_mon_name', ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'])
410
+
411
+ // WAT fragment: store a fixed ASCII run at cursor $p, advance $p past it.
412
+ const storeLit = (s) => {
413
+ let out = ''
414
+ for (let k = 0; k < s.length; k++)
415
+ out += `(i32.store8 ${k ? `(i32.add (local.get $p) (i32.const ${k}))` : '(local.get $p)'} (i32.const ${s.charCodeAt(k)}))\n `
416
+ return out + `(local.set $p (i32.add (local.get $p) (i32.const ${s.length})))`
417
+ }
418
+
419
+ ctx.core.stdlib['__date_invalid_string'] = `(func $__date_invalid_string (result f64)
420
+ (local $buf i32) (local $p i32)
421
+ (local.set $buf (call $__alloc (i32.const 12)))
422
+ (local.set $p (local.get $buf))
423
+ ${storeLit('Invalid Date')}
424
+ (call $__mkstr (local.get $buf) (i32.const 12)))`
425
+
384
426
  // ── toISOString ───────────────────────────────────────────────────────────
385
427
 
386
428
  ctx.core.stdlib['__date_to_iso_string'] = `(func $__date_to_iso_string (param $t f64) (result f64)
387
- (local $buf i32) (local $p i32) (local $year f64) (local $yv i32) (local $nd i32)
429
+ (local $buf i32) (local $p i32) (local $year f64) (local $yv i32)
388
430
  (if (f64.ne (local.get $t) (local.get $t)) (then (return (call $__mkstr (i32.const 0) (i32.const 0)))))
389
431
  (local.set $buf (call $__alloc (i32.const 40)))
390
432
  (local.set $p (local.get $buf))
391
433
  (local.set $year (call $__date_year_from_time (local.get $t)))
392
- (if (f64.lt (local.get $year) (f64.const 0))
434
+ (if (i32.and (f64.ge (local.get $year) (f64.const 0)) (f64.le (local.get $year) (f64.const 9999)))
393
435
  (then
394
- (i32.store8 (local.get $p) (i32.const 45))
395
- (local.set $p (i32.add (local.get $p) (i32.const 1)))
396
- (local.set $year (f64.neg (local.get $year)))))
397
- (local.set $yv (i32.trunc_sat_f64_u (local.get $year)))
398
- (if (i32.le_u (local.get $yv) (i32.const 9999))
399
- (then
400
- (call $__date_write4 (local.get $p) (local.get $yv))
436
+ (call $__date_write4 (local.get $p) (i32.trunc_sat_f64_u (local.get $year)))
401
437
  (local.set $p (i32.add (local.get $p) (i32.const 4))))
402
438
  (else
403
- (local.set $nd (call $__itoa (local.get $yv) (local.get $p)))
404
- (local.set $p (i32.add (local.get $p) (local.get $nd)))))
439
+ ;; expanded years (spec: DateString): explicit sign + 6-digit zero-padded
440
+ (if (f64.lt (local.get $year) (f64.const 0))
441
+ (then
442
+ (i32.store8 (local.get $p) (i32.const 45))
443
+ (local.set $year (f64.neg (local.get $year))))
444
+ (else (i32.store8 (local.get $p) (i32.const 43))))
445
+ (local.set $p (i32.add (local.get $p) (i32.const 1)))
446
+ (local.set $yv (i32.trunc_sat_f64_u (local.get $year)))
447
+ (call $__date_write2 (local.get $p) (i32.div_u (local.get $yv) (i32.const 10000)))
448
+ (call $__date_write4 (i32.add (local.get $p) (i32.const 2)) (i32.rem_u (local.get $yv) (i32.const 10000)))
449
+ (local.set $p (i32.add (local.get $p) (i32.const 6)))))
405
450
  (i32.store8 (local.get $p) (i32.const 45))
406
451
  (local.set $p (i32.add (local.get $p) (i32.const 1)))
407
452
  (call $__date_write2 (local.get $p) (i32.add (i32.trunc_sat_f64_s (call $__date_month_from_time (local.get $t))) (i32.const 1)))
@@ -433,118 +478,19 @@ export default (ctx) => {
433
478
  // ── toUTCString ───────────────────────────────────────────────────────────
434
479
 
435
480
  ctx.core.stdlib['__date_to_utc_string'] = `(func $__date_to_utc_string (param $t f64) (result f64)
436
- (local $buf i32) (local $p i32) (local $wd i32) (local $year f64) (local $yv i32) (local $nd i32) (local $month i32)
481
+ (local $buf i32) (local $p i32) (local $year f64) (local $yv i32) (local $nd i32)
437
482
  (if (f64.ne (local.get $t) (local.get $t)) (then (return (call $__mkstr (i32.const 0) (i32.const 0)))))
438
483
  (local.set $buf (call $__alloc (i32.const 48)))
439
484
  (local.set $p (local.get $buf))
440
- (local.set $wd (i32.trunc_sat_f64_s (call $__date_weekday (local.get $t))))
441
- (if (i32.eq (local.get $wd) (i32.const 0))
442
- (then
443
- (i32.store8 (local.get $p) (i32.const 83))
444
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 117))
445
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 110))))
446
- (if (i32.eq (local.get $wd) (i32.const 1))
447
- (then
448
- (i32.store8 (local.get $p) (i32.const 77))
449
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 111))
450
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 110))))
451
- (if (i32.eq (local.get $wd) (i32.const 2))
452
- (then
453
- (i32.store8 (local.get $p) (i32.const 84))
454
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 117))
455
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 101))))
456
- (if (i32.eq (local.get $wd) (i32.const 3))
457
- (then
458
- (i32.store8 (local.get $p) (i32.const 87))
459
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 101))
460
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 100))))
461
- (if (i32.eq (local.get $wd) (i32.const 4))
462
- (then
463
- (i32.store8 (local.get $p) (i32.const 84))
464
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 104))
465
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 117))))
466
- (if (i32.eq (local.get $wd) (i32.const 5))
467
- (then
468
- (i32.store8 (local.get $p) (i32.const 70))
469
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 114))
470
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 105))))
471
- (if (i32.eq (local.get $wd) (i32.const 6))
472
- (then
473
- (i32.store8 (local.get $p) (i32.const 83))
474
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 97))
475
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 116))))
485
+ (call $__date_wd_name (local.get $p) (i32.trunc_sat_f64_s (call $__date_weekday (local.get $t))))
476
486
  (local.set $p (i32.add (local.get $p) (i32.const 3)))
477
- (i32.store8 (local.get $p) (i32.const 44))
478
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 32))
479
- (local.set $p (i32.add (local.get $p) (i32.const 2)))
487
+ ${storeLit(', ')}
480
488
  (call $__date_write2 (local.get $p) (i32.trunc_sat_f64_s (call $__date_date_from_time (local.get $t))))
481
489
  (local.set $p (i32.add (local.get $p) (i32.const 2)))
482
- (i32.store8 (local.get $p) (i32.const 32))
483
- (local.set $p (i32.add (local.get $p) (i32.const 1)))
484
- (local.set $month (i32.trunc_sat_f64_s (call $__date_month_from_time (local.get $t))))
485
- (if (i32.eq (local.get $month) (i32.const 0))
486
- (then
487
- (i32.store8 (local.get $p) (i32.const 74))
488
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 97))
489
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 110))))
490
- (if (i32.eq (local.get $month) (i32.const 1))
491
- (then
492
- (i32.store8 (local.get $p) (i32.const 70))
493
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 101))
494
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 98))))
495
- (if (i32.eq (local.get $month) (i32.const 2))
496
- (then
497
- (i32.store8 (local.get $p) (i32.const 77))
498
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 97))
499
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 114))))
500
- (if (i32.eq (local.get $month) (i32.const 3))
501
- (then
502
- (i32.store8 (local.get $p) (i32.const 65))
503
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 112))
504
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 114))))
505
- (if (i32.eq (local.get $month) (i32.const 4))
506
- (then
507
- (i32.store8 (local.get $p) (i32.const 77))
508
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 97))
509
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 121))))
510
- (if (i32.eq (local.get $month) (i32.const 5))
511
- (then
512
- (i32.store8 (local.get $p) (i32.const 74))
513
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 117))
514
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 110))))
515
- (if (i32.eq (local.get $month) (i32.const 6))
516
- (then
517
- (i32.store8 (local.get $p) (i32.const 74))
518
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 117))
519
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 108))))
520
- (if (i32.eq (local.get $month) (i32.const 7))
521
- (then
522
- (i32.store8 (local.get $p) (i32.const 65))
523
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 117))
524
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 103))))
525
- (if (i32.eq (local.get $month) (i32.const 8))
526
- (then
527
- (i32.store8 (local.get $p) (i32.const 83))
528
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 101))
529
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 112))))
530
- (if (i32.eq (local.get $month) (i32.const 9))
531
- (then
532
- (i32.store8 (local.get $p) (i32.const 79))
533
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 99))
534
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 116))))
535
- (if (i32.eq (local.get $month) (i32.const 10))
536
- (then
537
- (i32.store8 (local.get $p) (i32.const 78))
538
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 111))
539
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 118))))
540
- (if (i32.eq (local.get $month) (i32.const 11))
541
- (then
542
- (i32.store8 (local.get $p) (i32.const 68))
543
- (i32.store8 (i32.add (local.get $p) (i32.const 1)) (i32.const 101))
544
- (i32.store8 (i32.add (local.get $p) (i32.const 2)) (i32.const 99))))
490
+ ${storeLit(' ')}
491
+ (call $__date_mon_name (local.get $p) (i32.trunc_sat_f64_s (call $__date_month_from_time (local.get $t))))
545
492
  (local.set $p (i32.add (local.get $p) (i32.const 3)))
546
- (i32.store8 (local.get $p) (i32.const 32))
547
- (local.set $p (i32.add (local.get $p) (i32.const 1)))
493
+ ${storeLit(' ')}
548
494
  (local.set $year (call $__date_year_from_time (local.get $t)))
549
495
  (if (f64.lt (local.get $year) (f64.const 0))
550
496
  (then
@@ -578,6 +524,63 @@ export default (ctx) => {
578
524
  (local.set $p (i32.add (local.get $p) (i32.const 4)))
579
525
  (call $__mkstr (local.get $buf) (i32.sub (local.get $p) (local.get $buf))))`
580
526
 
527
+ // ── toDateString / toTimeString / toJSON ──────────────────────────────────
528
+ // jz is UTC-only: the "local" date/time slices are the UTC ones and the zone
529
+ // suffix is the fixed UTC designation — a documented divergence, same policy
530
+ // as getHours ≡ getUTCHours.
531
+
532
+ ctx.core.stdlib['__date_to_date_string'] = `(func $__date_to_date_string (param $t f64) (result f64)
533
+ (local $buf i32) (local $p i32) (local $year f64) (local $yv i32) (local $nd i32)
534
+ (if (f64.ne (local.get $t) (local.get $t)) (then (return (call $__date_invalid_string))))
535
+ (local.set $buf (call $__alloc (i32.const 32)))
536
+ (local.set $p (local.get $buf))
537
+ (call $__date_wd_name (local.get $p) (i32.trunc_sat_f64_s (call $__date_weekday (local.get $t))))
538
+ (local.set $p (i32.add (local.get $p) (i32.const 3)))
539
+ ${storeLit(' ')}
540
+ (call $__date_mon_name (local.get $p) (i32.trunc_sat_f64_s (call $__date_month_from_time (local.get $t))))
541
+ (local.set $p (i32.add (local.get $p) (i32.const 3)))
542
+ ${storeLit(' ')}
543
+ (call $__date_write2 (local.get $p) (i32.trunc_sat_f64_s (call $__date_date_from_time (local.get $t))))
544
+ (local.set $p (i32.add (local.get $p) (i32.const 2)))
545
+ ${storeLit(' ')}
546
+ (local.set $year (call $__date_year_from_time (local.get $t)))
547
+ (if (f64.lt (local.get $year) (f64.const 0))
548
+ (then
549
+ (i32.store8 (local.get $p) (i32.const 45))
550
+ (local.set $p (i32.add (local.get $p) (i32.const 1)))
551
+ (local.set $year (f64.neg (local.get $year)))))
552
+ (local.set $yv (i32.trunc_sat_f64_u (local.get $year)))
553
+ (if (i32.le_u (local.get $yv) (i32.const 9999))
554
+ (then
555
+ (call $__date_write4 (local.get $p) (local.get $yv))
556
+ (local.set $p (i32.add (local.get $p) (i32.const 4))))
557
+ (else
558
+ (local.set $nd (call $__itoa (local.get $yv) (local.get $p)))
559
+ (local.set $p (i32.add (local.get $p) (local.get $nd)))))
560
+ (call $__mkstr (local.get $buf) (i32.sub (local.get $p) (local.get $buf))))`
561
+
562
+ ctx.core.stdlib['__date_to_time_string'] = `(func $__date_to_time_string (param $t f64) (result f64)
563
+ (local $buf i32) (local $p i32)
564
+ (if (f64.ne (local.get $t) (local.get $t)) (then (return (call $__date_invalid_string))))
565
+ (local.set $buf (call $__alloc (i32.const 48)))
566
+ (local.set $p (local.get $buf))
567
+ (call $__date_write2 (local.get $p) (i32.trunc_sat_f64_s (call $__date_hour_from_time (local.get $t))))
568
+ (local.set $p (i32.add (local.get $p) (i32.const 2)))
569
+ ${storeLit(':')}
570
+ (call $__date_write2 (local.get $p) (i32.trunc_sat_f64_s (call $__date_min_from_time (local.get $t))))
571
+ (local.set $p (i32.add (local.get $p) (i32.const 2)))
572
+ ${storeLit(':')}
573
+ (call $__date_write2 (local.get $p) (i32.trunc_sat_f64_s (call $__date_sec_from_time (local.get $t))))
574
+ (local.set $p (i32.add (local.get $p) (i32.const 2)))
575
+ ${storeLit(' GMT+0000 (Coordinated Universal Time)')}
576
+ (call $__mkstr (local.get $buf) (i32.sub (local.get $p) (local.get $buf))))`
577
+
578
+ // Serialization protocol: invalid date → null (JSON.stringify emits null),
579
+ // finite date → ISO string.
580
+ ctx.core.stdlib['__date_to_json'] = `(func $__date_to_json (param $t f64) (result f64)
581
+ (if (f64.ne (local.get $t) (local.get $t)) (then (return ${NULL_WAT})))
582
+ (call $__date_to_iso_string (local.get $t)))`
583
+
581
584
  // === Minimal Date value object ===
582
585
  // Represented as PTR.OBJECT with a single f64 slot at offset 0 (the time value).
583
586
  // No schemaId (aux=0); dynamic property access falls through to undefined.
@@ -603,7 +606,7 @@ export default (ctx) => {
603
606
  inc('__date_from_value')
604
607
  timeVal = typed(['call', '$__date_from_value', ['i64.reinterpret_f64', asF64(emit(ms))]], 'f64')
605
608
  }
606
- const out = allocPtr({ type: PTR.OBJECT, len: 1, cap: 1, stride: 8, tag: 'date' })
609
+ const out = allocPtr({ type: PTR.OBJECT, aux: ctx.schema.dateSid, len: 1, cap: 1, stride: 8, tag: 'date' })
607
610
  return typed(['block', ['result', 'f64'],
608
611
  out.init,
609
612
  ['f64.store', ['local.get', `$${out.local}`], timeVal],
@@ -801,4 +804,23 @@ export default (ctx) => {
801
804
  return typed(['call', '$__date_to_utc_string', emitDateLoad(dateExpr)], 'f64')
802
805
  }
803
806
  ctx.core.emit[`.${VAL.DATE}:toUTCString`] = ctx.core.emit['.toUTCString']
807
+
808
+ ctx.core.emit['.toDateString'] = (dateExpr) => {
809
+ inc('__date_to_date_string')
810
+ return typed(['call', '$__date_to_date_string', emitDateLoad(dateExpr)], 'f64')
811
+ }
812
+ ctx.core.emit[`.${VAL.DATE}:toDateString`] = ctx.core.emit['.toDateString']
813
+
814
+ ctx.core.emit['.toTimeString'] = (dateExpr) => {
815
+ inc('__date_to_time_string')
816
+ return typed(['call', '$__date_to_time_string', emitDateLoad(dateExpr)], 'f64')
817
+ }
818
+ ctx.core.emit[`.${VAL.DATE}:toTimeString`] = ctx.core.emit['.toTimeString']
819
+
820
+ // Typed-key only: users legitimately define `toJSON` on plain objects, so the
821
+ // bare generic key stays free — only a *proven* Date routes here.
822
+ ctx.core.emit[`.${VAL.DATE}:toJSON`] = (dateExpr) => {
823
+ inc('__date_to_json')
824
+ return typed(['call', '$__date_to_json', emitDateLoad(dateExpr)], 'f64')
825
+ }
804
826
  }
package/module/fs.js ADDED
@@ -0,0 +1,144 @@
1
+ /**
2
+ * fs — basic file IO over WASI (host:'wasi' only).
3
+ *
4
+ * `fs.read(path)` → string, `fs.write(path, data)` → undefined. Synchronous by
5
+ * construction — WASI preview1 IS synchronous, so no event-loop breach. Paths
6
+ * resolve against the FIRST PREOPEN (fd 3, the wasi convention): run under
7
+ * wasmtime/node:wasi with a preopened directory. jz strings are raw UTF-8
8
+ * bytes, so read/write are binary-lossless (a byte is `s.charCodeAt(i)`).
9
+ *
10
+ * Failures throw the WASI errno as a number (e.g. 44 = NOENT) — jz errors are
11
+ * values; catch and inspect. host:'js' rejects at compile with a wiring hint.
12
+ *
13
+ * @module fs
14
+ */
15
+
16
+ import { typed, asI64 } from '../src/ir.js'
17
+ import { emit, hostImport } from '../src/bridge.js'
18
+ import { inc, err, LAYOUT } from '../src/ctx.js'
19
+
20
+ const setupWasi = (ctx) => {
21
+ const needPathOpen = () => hostImport('wasi_snapshot_preview1', 'path_open',
22
+ ['func', '$__path_open', ['param', 'i32'], ['param', 'i32'], ['param', 'i32'], ['param', 'i32'], ['param', 'i32'], ['param', 'i64'], ['param', 'i64'], ['param', 'i32'], ['param', 'i32'], ['result', 'i32']])
23
+ const needFdRead = () => hostImport('wasi_snapshot_preview1', 'fd_read',
24
+ ['func', '$__fd_read', ['param', 'i32'], ['param', 'i32'], ['param', 'i32'], ['param', 'i32'], ['result', 'i32']])
25
+ const needFdWrite = () => hostImport('wasi_snapshot_preview1', 'fd_write',
26
+ ['func', '$__fd_write', ['param', 'i32'], ['param', 'i32'], ['param', 'i32'], ['param', 'i32'], ['result', 'i32']])
27
+ const needFdClose = () => hostImport('wasi_snapshot_preview1', 'fd_close',
28
+ ['func', '$__fd_close', ['param', 'i32'], ['result', 'i32']])
29
+ const needFilestat = () => hostImport('wasi_snapshot_preview1', 'fd_filestat_get',
30
+ ['func', '$__fd_filestat_get', ['param', 'i32'], ['param', 'i32'], ['result', 'i32']])
31
+
32
+ // Stage a jz string's bytes into linear memory → packed (ptr<<32)|len in an
33
+ // i64 (SSO strings live in the box, heap strings in place — no copy).
34
+ ctx.core.stdlib['__fs_path'] = `(func $__fs_path (param $str i64) (result i64)
35
+ (local $aux i32) (local $len i32) (local $off i32) (local $buf i32)
36
+ (local.set $aux (call $__ptr_aux (local.get $str)))
37
+ (if (i32.and (local.get $aux) (i32.const ${LAYOUT.SSO_BIT}))
38
+ (then
39
+ (local.set $len (i32.and (i32.shr_u (local.get $aux) (i32.const 10)) (i32.const 7)))
40
+ (local.set $buf (call $__alloc (local.get $len)))
41
+ (local.set $off (i32.const 0))
42
+ (block $done (loop $loop
43
+ (br_if $done (i32.ge_s (local.get $off) (local.get $len)))
44
+ (i32.store8 (i32.add (local.get $buf) (local.get $off))
45
+ (call $__sso_char (local.get $str) (local.get $off)))
46
+ (local.set $off (i32.add (local.get $off) (i32.const 1)))
47
+ (br $loop))))
48
+ (else
49
+ (local.set $buf (call $__ptr_offset (local.get $str)))
50
+ (local.set $len (call $__str_len (local.get $str)))))
51
+ (i64.or (i64.shl (i64.extend_i32_u (local.get $buf)) (i64.const 32))
52
+ (i64.extend_i32_u (local.get $len))))`
53
+
54
+ // read: path_open(preopen fd 3) → filestat size → exact alloc → read loop → string
55
+ ctx.core.stdlib['__fs_read'] = `(func $__fs_read (param $path i64) (result f64)
56
+ (local $pl i64) (local $scratch i32) (local $fd i32) (local $e i32)
57
+ (local $size i32) (local $buf i32) (local $total i32) (local $n i32)
58
+ (local.set $pl (call $__fs_path (local.get $path)))
59
+ (local.set $scratch (call $__alloc (i32.const 64)))
60
+ ;; rights: FD_READ(2) | FD_FILESTAT_GET(1<<21)
61
+ (local.set $e (call $__path_open (i32.const 3) (i32.const 1)
62
+ (i32.wrap_i64 (i64.shr_u (local.get $pl) (i64.const 32)))
63
+ (i32.wrap_i64 (i64.and (local.get $pl) (i64.const 0xffffffff)))
64
+ (i32.const 0) (i64.const ${2 | (1 << 21)}) (i64.const 0) (i32.const 0)
65
+ (local.get $scratch)))
66
+ (if (local.get $e) (then (throw $__jz_err (f64.convert_i32_u (local.get $e)))))
67
+ (local.set $fd (i32.load (local.get $scratch)))
68
+ (local.set $e (call $__fd_filestat_get (local.get $fd) (local.get $scratch)))
69
+ (if (local.get $e) (then
70
+ (drop (call $__fd_close (local.get $fd)))
71
+ (throw $__jz_err (f64.convert_i32_u (local.get $e)))))
72
+ ;; filestat.size at offset 32 (u64; jz caps at i32 addressable)
73
+ (local.set $size (i32.wrap_i64 (i64.load offset=32 (local.get $scratch))))
74
+ (local.set $buf (call $__alloc (local.get $size)))
75
+ (local.set $total (i32.const 0))
76
+ (block $eof (loop $read
77
+ (br_if $eof (i32.ge_u (local.get $total) (local.get $size)))
78
+ (i32.store (local.get $scratch) (i32.add (local.get $buf) (local.get $total)))
79
+ (i32.store offset=4 (local.get $scratch) (i32.sub (local.get $size) (local.get $total)))
80
+ (local.set $e (call $__fd_read (local.get $fd) (local.get $scratch) (i32.const 1)
81
+ (i32.add (local.get $scratch) (i32.const 8))))
82
+ (if (local.get $e) (then
83
+ (drop (call $__fd_close (local.get $fd)))
84
+ (throw $__jz_err (f64.convert_i32_u (local.get $e)))))
85
+ (local.set $n (i32.load offset=8 (local.get $scratch)))
86
+ (br_if $eof (i32.eqz (local.get $n)))
87
+ (local.set $total (i32.add (local.get $total) (local.get $n)))
88
+ (br $read)))
89
+ (drop (call $__fd_close (local.get $fd)))
90
+ (call $__mkstr (local.get $buf) (local.get $total)))`
91
+
92
+ // write: path_open CREAT|TRUNC → fd_write loop → close
93
+ ctx.core.stdlib['__fs_write'] = `(func $__fs_write (param $path i64) (param $data i64)
94
+ (local $pl i64) (local $dl i64) (local $scratch i32) (local $fd i32) (local $e i32)
95
+ (local $buf i32) (local $len i32) (local $total i32) (local $n i32)
96
+ (local.set $pl (call $__fs_path (local.get $path)))
97
+ (local.set $dl (call $__fs_path (local.get $data)))
98
+ (local.set $scratch (call $__alloc (i32.const 16)))
99
+ ;; oflags: CREAT(1)|TRUNC(8); rights: FD_WRITE(64)
100
+ (local.set $e (call $__path_open (i32.const 3) (i32.const 1)
101
+ (i32.wrap_i64 (i64.shr_u (local.get $pl) (i64.const 32)))
102
+ (i32.wrap_i64 (i64.and (local.get $pl) (i64.const 0xffffffff)))
103
+ (i32.const 9) (i64.const 64) (i64.const 0) (i32.const 0)
104
+ (local.get $scratch)))
105
+ (if (local.get $e) (then (throw $__jz_err (f64.convert_i32_u (local.get $e)))))
106
+ (local.set $fd (i32.load (local.get $scratch)))
107
+ (local.set $buf (i32.wrap_i64 (i64.shr_u (local.get $dl) (i64.const 32))))
108
+ (local.set $len (i32.wrap_i64 (i64.and (local.get $dl) (i64.const 0xffffffff))))
109
+ (local.set $total (i32.const 0))
110
+ (block $done (loop $write
111
+ (br_if $done (i32.ge_u (local.get $total) (local.get $len)))
112
+ (i32.store (local.get $scratch) (i32.add (local.get $buf) (local.get $total)))
113
+ (i32.store offset=4 (local.get $scratch) (i32.sub (local.get $len) (local.get $total)))
114
+ (local.set $e (call $__fd_write (local.get $fd) (local.get $scratch) (i32.const 1)
115
+ (i32.add (local.get $scratch) (i32.const 8))))
116
+ (if (local.get $e) (then
117
+ (drop (call $__fd_close (local.get $fd)))
118
+ (throw $__jz_err (f64.convert_i32_u (local.get $e)))))
119
+ (local.set $n (i32.load offset=8 (local.get $scratch)))
120
+ (br_if $done (i32.eqz (local.get $n)))
121
+ (local.set $total (i32.add (local.get $total) (local.get $n)))
122
+ (br $write)))
123
+ (drop (call $__fd_close (local.get $fd))))`
124
+
125
+ ctx.core.emit['fs.read'] = (path) => {
126
+ needPathOpen(); needFdRead(); needFdClose(); needFilestat()
127
+ inc('__fs_path'); inc('__fs_read')
128
+ return typed(['call', '$__fs_read', asI64(emit(path))], 'f64')
129
+ }
130
+ ctx.core.emit['fs.write'] = (path, data) => {
131
+ needPathOpen(); needFdWrite(); needFdClose()
132
+ inc('__fs_path'); inc('__fs_write')
133
+ return ['call', '$__fs_write', asI64(emit(path)), asI64(emit(data))]
134
+ }
135
+ }
136
+
137
+ export default (ctx) => {
138
+ if (ctx.transform.host === 'wasi') setupWasi(ctx)
139
+ else {
140
+ const reject = (m) => () => err(`fs.${m} needs a WASI host — compile with host:'wasi' (CLI --host wasi), or wire your own file access via {imports}`)
141
+ ctx.core.emit['fs.read'] = reject('read')
142
+ ctx.core.emit['fs.write'] = reject('write')
143
+ }
144
+ }
@@ -10,7 +10,7 @@
10
10
  * @module fn
11
11
  */
12
12
 
13
- import { typed, asF64, asI32, mkPtrIR, temp, tempI32, MAX_CLOSURE_ARITY, UNDEF_NAN, appendStaticSlots } from '../src/ir.js'
13
+ import { typed, asF64, asI32, mkPtrIR, temp, tempI32, MAX_CLOSURE_ARITY, UNDEF_NAN, appendStaticSlots, carrierF64 } from '../src/ir.js'
14
14
  import { emit } from '../src/bridge.js'
15
15
  import { isReassigned } from '../src/ast.js'
16
16
  import { findFreeVars } from '../src/compile/analyze.js'
@@ -297,12 +297,15 @@ export default (ctx) => {
297
297
  ['i64.const', LAYOUT.AUX_MASK]]]]], 'f64')
298
298
  }
299
299
 
300
- // Inline path: emit each arg, pad missing slots with UNDEF
300
+ // Inline path: emit each arg, pad missing slots with UNDEF. Closure ABI slots
301
+ // are untyped boxed-value positions — a bool arg crosses as its atom box so
302
+ // the callee observes boolean identity (typeof/String/strict-eq); pre-emitted
303
+ // IR (has .type) has no AST node to consult and keeps the plain box.
301
304
  const n = args.length
302
305
  if (n > MAX_CLOSURE_ARITY) err(`Closure call with ${n} args exceeds MAX_CLOSURE_ARITY=${MAX_CLOSURE_ARITY}`)
303
306
  const W = ctx.closure.width ?? MAX_CLOSURE_ARITY
304
307
  const slots = []
305
- for (let i = 0; i < n; i++) slots.push(asF64(args[i]?.type ? args[i] : emit(args[i])))
308
+ for (let i = 0; i < n; i++) slots.push(args[i]?.type ? asF64(args[i]) : carrierF64(args[i], emit(args[i])))
306
309
  for (let i = n; i < W; i++) slots.push(UNDEF_LIT())
307
310
 
308
311
  return typed(['block', ['result', 'f64'],
package/module/index.js CHANGED
@@ -10,8 +10,11 @@ import collection from './collection.js'
10
10
  import symbol from './symbol.js'
11
11
  import console from './console.js'
12
12
  import json from './json.js'
13
+ import atomics from './atomics.js'
13
14
  import regex from './regex.js'
14
15
  import timer from './timer.js'
15
16
  import date from './date.js'
16
17
  import simd from './simd.js'
17
- export { math, core, array, object, string, number, fn, typedarray, collection, symbol, console, json, regex, timer, date, simd }
18
+ import fs from './fs.js'
19
+ import web from './web.js'
20
+ export { math, core, array, object, string, number, fn, typedarray, collection, symbol, console, json, regex, timer, date, simd , atomics, fs, web }