@grain/stdlib 0.4.6 → 0.5.2

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 (85) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/array.gr +18 -18
  3. package/array.md +18 -18
  4. package/bigint.gr +497 -0
  5. package/bigint.md +811 -0
  6. package/buffer.gr +59 -223
  7. package/buffer.md +24 -17
  8. package/bytes.gr +100 -202
  9. package/bytes.md +19 -0
  10. package/char.gr +63 -133
  11. package/exception.gr +28 -2
  12. package/exception.md +43 -0
  13. package/float32.gr +76 -95
  14. package/float32.md +69 -30
  15. package/float64.gr +81 -95
  16. package/float64.md +69 -30
  17. package/hash.gr +37 -37
  18. package/int32.gr +152 -198
  19. package/int32.md +104 -0
  20. package/int64.gr +151 -197
  21. package/int64.md +104 -0
  22. package/list.gr +467 -70
  23. package/list.md +1141 -0
  24. package/map.gr +192 -7
  25. package/map.md +525 -0
  26. package/number.gr +111 -54
  27. package/number.md +100 -3
  28. package/option.md +1 -1
  29. package/package.json +3 -3
  30. package/pervasives.gr +499 -59
  31. package/pervasives.md +1116 -0
  32. package/queue.gr +4 -0
  33. package/queue.md +10 -0
  34. package/random.gr +196 -0
  35. package/random.md +179 -0
  36. package/regex.gr +1833 -842
  37. package/regex.md +11 -11
  38. package/result.md +1 -1
  39. package/runtime/bigint.gr +2045 -0
  40. package/runtime/bigint.md +326 -0
  41. package/runtime/dataStructures.gr +99 -278
  42. package/runtime/dataStructures.md +391 -0
  43. package/runtime/debug.md +6 -0
  44. package/runtime/equal.gr +5 -23
  45. package/runtime/equal.md +6 -0
  46. package/runtime/exception.md +30 -0
  47. package/runtime/gc.gr +20 -3
  48. package/runtime/gc.md +36 -0
  49. package/runtime/malloc.gr +13 -11
  50. package/runtime/malloc.md +55 -0
  51. package/runtime/numberUtils.gr +91 -41
  52. package/runtime/numberUtils.md +54 -0
  53. package/runtime/numbers.gr +1049 -391
  54. package/runtime/numbers.md +300 -0
  55. package/runtime/string.gr +136 -230
  56. package/runtime/string.md +24 -0
  57. package/runtime/stringUtils.gr +58 -38
  58. package/runtime/stringUtils.md +6 -0
  59. package/runtime/unsafe/constants.gr +17 -0
  60. package/runtime/unsafe/constants.md +72 -0
  61. package/runtime/unsafe/conv.md +71 -0
  62. package/runtime/unsafe/errors.md +204 -0
  63. package/runtime/unsafe/memory.md +54 -0
  64. package/runtime/unsafe/printWasm.md +24 -0
  65. package/runtime/unsafe/tags.gr +9 -8
  66. package/runtime/unsafe/tags.md +120 -0
  67. package/runtime/unsafe/wasmf32.md +168 -0
  68. package/runtime/unsafe/wasmf64.md +168 -0
  69. package/runtime/unsafe/wasmi32.md +282 -0
  70. package/runtime/unsafe/wasmi64.md +300 -0
  71. package/runtime/utils/printing.gr +62 -0
  72. package/runtime/utils/printing.md +18 -0
  73. package/runtime/wasi.gr +1 -1
  74. package/runtime/wasi.md +839 -0
  75. package/set.gr +17 -8
  76. package/set.md +24 -21
  77. package/stack.gr +3 -3
  78. package/stack.md +4 -6
  79. package/string.gr +194 -329
  80. package/string.md +3 -3
  81. package/sys/file.gr +245 -429
  82. package/sys/process.gr +27 -45
  83. package/sys/random.gr +47 -16
  84. package/sys/random.md +38 -0
  85. package/sys/time.gr +11 -27
@@ -1,9 +1,14 @@
1
- /* grainc-flags --compilation-mode=runtime */
1
+ /* grainc-flags --no-pervasives */
2
2
 
3
3
  import Memory from "runtime/unsafe/memory"
4
4
  import Tags from "runtime/unsafe/tags"
5
5
  import Exception from "runtime/exception"
6
+ import BI from "runtime/bigint"
6
7
 
8
+ import {
9
+ _SMAX32_I64 as _I32_MAX,
10
+ _SMIN32_I64 as _I32_MIN,
11
+ } from "runtime/unsafe/constants"
7
12
  import WasmI32 from "runtime/unsafe/wasmi32"
8
13
  import WasmI64 from "runtime/unsafe/wasmi64"
9
14
  import WasmF32 from "runtime/unsafe/wasmf32"
@@ -24,30 +29,29 @@ import {
24
29
  newFloat64,
25
30
  } from "runtime/dataStructures"
26
31
 
27
- export newRational
28
- export newInt32
29
- export newInt64
30
- export newFloat32
31
- export newFloat64
32
-
33
- let _I32_MAX = 0x7fffffffN
34
- let _I32_MIN = -0x7fffffffN
32
+ @unsafe
35
33
  let _F32_MAX = 3.40282347e+38W
34
+ @unsafe
36
35
  let _F32_MIN = 1.401298464324817e-45W
36
+ @unsafe
37
37
  let _F32_MAX_SAFE_INTEGER = 16777215.w
38
+ @unsafe
38
39
  let _F64_MAX_SAFE_INTEGER = 9007199254740991.W
39
40
 
40
41
  let (==) = WasmI32.eq
41
42
  let (!=) = WasmI32.ne
42
43
 
44
+ @unsafe
43
45
  let tagSimple = x => {
44
46
  WasmI32.xor(WasmI32.shl(x, 1n), 1n)
45
47
  }
46
48
 
49
+ @unsafe
47
50
  let untagSimple = x => {
48
51
  WasmI32.shrS(x, 1n)
49
52
  }
50
53
 
54
+ @unsafe
51
55
  let isSimpleNumber = x => {
52
56
  WasmI32.eq(
53
57
  WasmI32.and(x, Tags._GRAIN_NUMBER_TAG_MASK),
@@ -55,6 +59,7 @@ let isSimpleNumber = x => {
55
59
  )
56
60
  }
57
61
 
62
+ @unsafe
58
63
  export let isBoxedNumber = x => {
59
64
  if (
60
65
  WasmI32.eq(
@@ -68,6 +73,7 @@ export let isBoxedNumber = x => {
68
73
  }
69
74
  }
70
75
 
76
+ @unsafe
71
77
  export let isFloat = x => {
72
78
  if (isBoxedNumber(x)) {
73
79
  let tag = WasmI32.load(x, 4n)
@@ -78,11 +84,23 @@ export let isFloat = x => {
78
84
  }
79
85
  }
80
86
 
87
+ @unsafe
88
+ let isBigInt = x => {
89
+ if (isBoxedNumber(x)) {
90
+ let tag = WasmI32.load(x, 4n)
91
+ WasmI32.eq(tag, Tags._GRAIN_BIGINT_BOXED_NUM_TAG)
92
+ } else {
93
+ false
94
+ }
95
+ }
96
+
97
+ @unsafe
81
98
  export let isNumber = x => {
82
99
  // x is a number if it is a literal number or a boxed_num heap value
83
100
  isSimpleNumber(x) || isBoxedNumber(x)
84
101
  }
85
102
 
103
+ @unsafe
86
104
  let safeI64toI32 = x => {
87
105
  if (WasmI64.gtS(x, _I32_MAX) || WasmI64.ltS(x, _I32_MIN)) {
88
106
  throw Exception.Overflow
@@ -91,12 +109,16 @@ let safeI64toI32 = x => {
91
109
  }
92
110
  }
93
111
 
112
+ @unsafe
94
113
  let i32neg = x => WasmI32.sub(0n, x)
95
114
 
115
+ @unsafe
96
116
  let i64not = x => WasmI64.xor(x, 0xffffffffffffffffN)
117
+ @unsafe
97
118
  let i64neg = x => WasmI64.sub(0N, x)
98
119
 
99
120
  // https://en.wikipedia.org/wiki/Binary_GCD_algorithm
121
+ @unsafe
100
122
  let rec gcdHelp = (x, y) => {
101
123
  if (WasmI64.eq(x, y) || WasmI64.eqz(x)) {
102
124
  y
@@ -120,6 +142,7 @@ let rec gcdHelp = (x, y) => {
120
142
  }
121
143
  }
122
144
 
145
+ @unsafe
123
146
  let gcd = (x, y) => {
124
147
  // Algorithm above breaks on negatives, so
125
148
  // we make sure that they are positive at the beginning
@@ -136,10 +159,12 @@ let gcd = (x, y) => {
136
159
  gcdHelp(x, y)
137
160
  }
138
161
 
162
+ @unsafe
139
163
  let gcd32 = (x, y) => {
140
164
  WasmI32.wrapI64(gcd(WasmI64.extendI32S(x), WasmI64.extendI32S(y)))
141
165
  }
142
166
 
167
+ @unsafe
143
168
  export let reducedInteger = x => {
144
169
  if (WasmI64.gtS(x, _I32_MAX) || WasmI64.ltS(x, _I32_MIN)) {
145
170
  newInt64(x)
@@ -153,29 +178,71 @@ export let reducedInteger = x => {
153
178
  }
154
179
  }
155
180
 
156
- let reducedFraction = (x, y) => {
181
+ @unsafe
182
+ let reducedBigInteger = x => {
183
+ if (BI.canConvertToInt64(x)) {
184
+ // CONVENTION: We assume that this function is called in
185
+ // some sort of tail position, meaning that
186
+ // the original input is no longer used after
187
+ // this function returns.
188
+ let ret = reducedInteger(BI.toInt64(x))
189
+ Memory.decRef(x)
190
+ ret
191
+ } else {
192
+ x
193
+ }
194
+ }
195
+
196
+ @unsafe
197
+ let reducedFractionBigInt = (x, y) => {
157
198
  let mut x = x
158
199
  let mut y = y
200
+ let mut needsDecref = false
159
201
 
160
- if (WasmI32.ltS(y, 0n)) {
202
+ if (BI.isNegative(y)) {
161
203
  // Normalization 1: Never do negative/negative
162
204
  // Normalization 2: Never allow a negative denominator
163
- x = i32neg(x)
164
- y = i32neg(y)
205
+ needsDecref = true
206
+ x = BI.negate(x)
207
+ y = BI.negate(y)
165
208
  }
166
- if (WasmI32.eqz(y)) {
209
+ if (BI.eqz(y)) {
167
210
  throw Exception.DivisionByZero
168
211
  }
169
- if (WasmI32.eqz(WasmI32.remS(x, y))) {
170
- // Avoid allocation if possible
171
- reducedInteger(WasmI64.extendI32S(WasmI32.divS(x, y)))
212
+ let quotremResult = Memory.malloc(8n)
213
+ BI.quotRem(x, y, quotremResult)
214
+ // Note that the contents of quotRem are malloc'ed
215
+ // inside of quotRem and need to be manually freed.
216
+ let q = WasmI32.load(quotremResult, 0n)
217
+ let r = WasmI32.load(quotremResult, 4n)
218
+ // free container used to store quotrem result
219
+ Memory.free(quotremResult)
220
+ let ret = if (BI.eqz(r)) {
221
+ // if remainder is zero, then return the quotient.
222
+ // We decRef the remainder, since we no longer need it
223
+ Memory.decRef(r)
224
+ reducedBigInteger(q)
172
225
  } else {
173
- // x not evenly divisible by y
174
- let factor = gcd32(x, y)
175
- newRational(WasmI32.divS(x, factor), WasmI32.divS(y, factor))
226
+ // remainder is nonzero. we don't need the quotient and
227
+ // remainder anymore, so we discard them.
228
+ Memory.decRef(q)
229
+ Memory.decRef(r)
230
+ let factor = BI.gcd(x, y)
231
+ let xdiv = BI.div(x, factor)
232
+ let ydiv = BI.div(y, factor)
233
+ let ret = newRational(xdiv, ydiv)
234
+ Memory.decRef(factor)
235
+ ret
236
+ }
237
+ if (needsDecref) {
238
+ Memory.decRef(x)
239
+ Memory.decRef(y)
240
+ void
176
241
  }
242
+ ret
177
243
  }
178
244
 
245
+ @unsafe
179
246
  let reducedFraction64 = (x, y) => {
180
247
  let mut x = x
181
248
  let mut y = y
@@ -193,28 +260,10 @@ let reducedFraction64 = (x, y) => {
193
260
  reducedInteger(WasmI64.divS(x, y))
194
261
  } else {
195
262
  let factor = gcd(x, y)
196
- let xdiv = safeI64toI32(WasmI64.divS(x, factor))
197
- let ydiv = safeI64toI32(WasmI64.divS(y, factor))
198
- newRational(xdiv, ydiv)
199
- }
200
- }
201
-
202
- let safeI32Multiply = (x, y) => {
203
- let prod = WasmI64.mul(WasmI64.extendI32S(x), WasmI64.extendI32S(y))
204
- if (WasmI64.gtS(prod, _I32_MAX) || WasmI64.ltS(prod, _I32_MIN)) {
205
- throw Exception.Overflow
206
- }
207
- WasmI32.wrapI64(prod)
208
- }
209
-
210
- let safeI64Multiply = (x, y) => {
211
- let prod = WasmI64.mul(x, y)
212
- if (WasmI64.ne(x, 0N)) {
213
- if (WasmI64.ne(WasmI64.divS(prod, x), y)) {
214
- throw Exception.Overflow
215
- }
263
+ let xdiv = WasmI64.divS(x, factor)
264
+ let ydiv = WasmI64.divS(y, factor)
265
+ newRational(BI.makeWrappedInt64(xdiv), BI.makeWrappedInt64(ydiv))
216
266
  }
217
- prod
218
267
  }
219
268
 
220
269
  // Accessor functions
@@ -240,34 +289,42 @@ let safeI64Multiply = (x, y) => {
240
289
  * [numerator, denominator]
241
290
  */
242
291
 
292
+ @unsafe
243
293
  export let boxedNumberTag = xptr => {
244
294
  WasmI32.load(xptr, 4n)
245
295
  }
246
296
 
297
+ @unsafe
247
298
  export let boxedInt32Number = xptr => {
248
299
  WasmI32.load(xptr, 8n)
249
300
  }
250
301
 
302
+ @unsafe
251
303
  export let boxedInt64Number = xptr => {
252
304
  WasmI64.load(xptr, 8n)
253
305
  }
254
306
 
307
+ @unsafe
255
308
  export let boxedFloat32Number = xptr => {
256
309
  WasmF32.load(xptr, 8n)
257
310
  }
258
311
 
312
+ @unsafe
259
313
  export let boxedFloat64Number = xptr => {
260
314
  WasmF64.load(xptr, 8n)
261
315
  }
262
316
 
317
+ @unsafe
263
318
  export let boxedRationalNumerator = xptr => {
264
319
  WasmI32.load(xptr, 8n)
265
320
  }
266
321
 
322
+ @unsafe
267
323
  export let boxedRationalDenominator = xptr => {
268
324
  WasmI32.load(xptr, 12n)
269
325
  }
270
326
 
327
+ @unsafe
271
328
  export let coerceNumberToWasmF32 = (x: Number) => {
272
329
  let x = WasmI32.fromGrain(x)
273
330
  if (isSimpleNumber(x)) {
@@ -281,10 +338,13 @@ export let coerceNumberToWasmF32 = (x: Number) => {
281
338
  t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
282
339
  WasmF32.convertI64S(boxedInt64Number(x))
283
340
  },
341
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
342
+ BI.toFloat32(x)
343
+ },
284
344
  t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
285
345
  WasmF32.div(
286
- WasmF32.convertI32S(boxedRationalNumerator(x)),
287
- WasmF32.convertI32S(boxedRationalDenominator(x))
346
+ BI.toFloat32(boxedRationalNumerator(x)),
347
+ BI.toFloat32(boxedRationalDenominator(x))
288
348
  )
289
349
  },
290
350
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
@@ -306,6 +366,7 @@ export let coerceNumberToWasmF32 = (x: Number) => {
306
366
  }
307
367
  }
308
368
 
369
+ @unsafe
309
370
  export let coerceNumberToWasmF64 = (x: Number) => {
310
371
  let x = WasmI32.fromGrain(x)
311
372
  if (isSimpleNumber(x)) {
@@ -319,10 +380,13 @@ export let coerceNumberToWasmF64 = (x: Number) => {
319
380
  t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
320
381
  WasmF64.convertI64S(boxedInt64Number(x))
321
382
  },
383
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
384
+ BI.toFloat64(x)
385
+ },
322
386
  t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
323
387
  WasmF64.div(
324
- WasmF64.convertI32S(boxedRationalNumerator(x)),
325
- WasmF64.convertI32S(boxedRationalDenominator(x))
388
+ BI.toFloat64(boxedRationalNumerator(x)),
389
+ BI.toFloat64(boxedRationalDenominator(x))
326
390
  )
327
391
  },
328
392
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
@@ -338,6 +402,7 @@ export let coerceNumberToWasmF64 = (x: Number) => {
338
402
  }
339
403
  }
340
404
 
405
+ @unsafe
341
406
  export let coerceNumberToWasmI64 = (x: Number) => {
342
407
  let x = WasmI32.fromGrain(x)
343
408
  if (isSimpleNumber(x)) {
@@ -351,6 +416,9 @@ export let coerceNumberToWasmI64 = (x: Number) => {
351
416
  t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
352
417
  boxedInt64Number(x)
353
418
  },
419
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
420
+ BI.toInt64(x)
421
+ },
354
422
  _ => {
355
423
  // rationals are never integral, and we refuse to coerce floats to ints
356
424
  throw Exception.NumberNotIntlike
@@ -359,6 +427,7 @@ export let coerceNumberToWasmI64 = (x: Number) => {
359
427
  }
360
428
  }
361
429
 
430
+ @unsafe
362
431
  export let coerceNumberToWasmI32 = (x: Number) => {
363
432
  let x = WasmI32.fromGrain(x)
364
433
  if (isSimpleNumber(x)) {
@@ -376,6 +445,35 @@ export let coerceNumberToWasmI32 = (x: Number) => {
376
445
  }
377
446
  WasmI32.wrapI64(int64)
378
447
  },
448
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
449
+ BI.toInt32(x)
450
+ },
451
+ _ => {
452
+ // rationals are never integral, and we refuse to coerce floats to ints
453
+ throw Exception.NumberNotIntlike
454
+ },
455
+ }
456
+ }
457
+ }
458
+
459
+ @unsafe
460
+ export let coerceNumberToBigInt = (x: Number) => {
461
+ let x = WasmI32.fromGrain(x)
462
+ if (isSimpleNumber(x)) {
463
+ BI.makeWrappedInt32(untagSimple(x))
464
+ } else {
465
+ let xtag = boxedNumberTag(x)
466
+ match (xtag) {
467
+ t when WasmI32.eq(t, Tags._GRAIN_INT32_BOXED_NUM_TAG) => {
468
+ BI.makeWrappedInt32(boxedInt32Number(x))
469
+ },
470
+ t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
471
+ BI.makeWrappedInt64(boxedInt64Number(x))
472
+ },
473
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
474
+ Memory.incRef(x)
475
+ x
476
+ },
379
477
  _ => {
380
478
  // rationals are never integral, and we refuse to coerce floats to ints
381
479
  throw Exception.NumberNotIntlike
@@ -384,19 +482,23 @@ export let coerceNumberToWasmI32 = (x: Number) => {
384
482
  }
385
483
  }
386
484
 
485
+ @unsafe
387
486
  let isIntegerF32 = value => {
388
487
  WasmF32.eq(value, WasmF32.trunc(value))
389
488
  }
390
489
 
490
+ @unsafe
391
491
  let isIntegerF64 = value => {
392
492
  WasmF64.eq(value, WasmF64.trunc(value))
393
493
  }
394
494
 
495
+ @unsafe
395
496
  let isSafeIntegerF32 = value => {
396
497
  WasmF32.le(WasmF32.abs(value), _F32_MAX_SAFE_INTEGER) &&
397
498
  WasmF32.eq(WasmF32.trunc(value), value)
398
499
  }
399
500
 
501
+ @unsafe
400
502
  let isSafeIntegerF64 = value => {
401
503
  WasmF64.le(WasmF64.abs(value), _F64_MAX_SAFE_INTEGER) &&
402
504
  WasmF64.eq(WasmF64.trunc(value), value)
@@ -411,6 +513,7 @@ let isSafeIntegerF64 = value => {
411
513
  * export them!
412
514
  */
413
515
 
516
+ @unsafe
414
517
  let numberEqualSimpleHelp = (x, y) => {
415
518
  // PRECONDITION: x is a "simple" number (value tag is 0) and x !== y and isNumber(y)
416
519
  if (isSimpleNumber(y)) {
@@ -428,6 +531,9 @@ let numberEqualSimpleHelp = (x, y) => {
428
531
  let yBoxedVal = boxedInt64Number(y)
429
532
  WasmI64.eq(WasmI64.extendI32S(xval), yBoxedVal)
430
533
  },
534
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
535
+ WasmI32.eqz(BI.cmpI64(y, WasmI64.extendI32S(xval)))
536
+ },
431
537
  t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
432
538
  // NOTE: we always store in most reduced form, so a rational and an int are never equal
433
539
  false
@@ -449,6 +555,7 @@ let numberEqualSimpleHelp = (x, y) => {
449
555
  }
450
556
  }
451
557
 
558
+ @unsafe
452
559
  let numberEqualInt64Help = (xBoxedVal, y) => {
453
560
  // PRECONDITION: x !== y and isNumber(y)
454
561
  // Basic number:
@@ -466,6 +573,9 @@ let numberEqualInt64Help = (xBoxedVal, y) => {
466
573
  let yBoxedVal = boxedInt64Number(y)
467
574
  WasmI64.eq(xBoxedVal, yBoxedVal)
468
575
  },
576
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
577
+ WasmI32.eqz(BI.cmpI64(y, xBoxedVal))
578
+ },
469
579
  t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
470
580
  // NOTE: we always store in most reduced form, so a rational and an int are never equal
471
581
  false
@@ -487,11 +597,13 @@ let numberEqualInt64Help = (xBoxedVal, y) => {
487
597
  }
488
598
  }
489
599
 
600
+ @unsafe
490
601
  let numberEqualInt32Help = (xBoxedVal, y) => {
491
602
  // We can just pretend it's 64-bit for the equality check
492
603
  numberEqualInt64Help(WasmI64.extendI32S(xBoxedVal), y)
493
604
  }
494
605
 
606
+ @unsafe
495
607
  let numberEqualRationalHelp = (xptr, y) => {
496
608
  // PRECONDITION: x is rational and x !== y and isNumber(y)
497
609
  // Basic number: (we know it's not equal, since we never store ints as rationals)
@@ -509,17 +621,19 @@ let numberEqualRationalHelp = (xptr, y) => {
509
621
  t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
510
622
  false
511
623
  },
624
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
625
+ false
626
+ },
512
627
  t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
513
628
  let yNumerator = boxedRationalNumerator(y)
514
629
  let yDenominator = boxedRationalDenominator(y)
515
- WasmI32.eq(xNumerator, yNumerator) &&
516
- WasmI32.eq(xDenominator, yDenominator)
630
+ BI.eq(xNumerator, yNumerator) && BI.eq(xDenominator, yDenominator)
517
631
  },
518
632
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
519
633
  let yBoxedVal = boxedFloat32Number(y)
520
634
  let xAsFloat = WasmF32.div(
521
- WasmF32.convertI32S(xNumerator),
522
- WasmF32.convertI32S(xDenominator)
635
+ BI.toFloat32(xNumerator),
636
+ BI.toFloat32(xDenominator)
523
637
  )
524
638
  // TODO: (#303) maybe we should have some sort of tolerance?
525
639
  WasmF32.eq(xAsFloat, yBoxedVal)
@@ -527,8 +641,8 @@ let numberEqualRationalHelp = (xptr, y) => {
527
641
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT64_BOXED_NUM_TAG) => {
528
642
  let yBoxedVal = boxedFloat64Number(y)
529
643
  let xAsFloat = WasmF64.div(
530
- WasmF64.convertI32S(xNumerator),
531
- WasmF64.convertI32S(xDenominator)
644
+ BI.toFloat64(xNumerator),
645
+ BI.toFloat64(xDenominator)
532
646
  )
533
647
  // TODO: (#303) maybe we should have some sort of tolerance?
534
648
  WasmF64.eq(xAsFloat, yBoxedVal)
@@ -540,6 +654,7 @@ let numberEqualRationalHelp = (xptr, y) => {
540
654
  }
541
655
  }
542
656
 
657
+ @unsafe
543
658
  let numberEqualFloat64Help = (x, y) => {
544
659
  let xIsInteger = isIntegerF64(x)
545
660
  // Basic number:
@@ -557,12 +672,15 @@ let numberEqualFloat64Help = (x, y) => {
557
672
  let yBoxedVal = boxedInt64Number(y)
558
673
  isSafeIntegerF64(x) && WasmF64.eq(x, WasmF64.convertI64S(yBoxedVal))
559
674
  },
675
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
676
+ WasmI32.eqz(BI.cmpF64(y, x))
677
+ },
560
678
  t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
561
679
  let yNumerator = boxedRationalNumerator(y)
562
680
  let yDenominator = boxedRationalDenominator(y)
563
681
  let yAsFloat = WasmF64.div(
564
- WasmF64.convertI32S(yNumerator),
565
- WasmF64.convertI32S(yDenominator)
682
+ BI.toFloat64(yNumerator),
683
+ BI.toFloat64(yDenominator)
566
684
  )
567
685
  WasmF64.eq(x, yAsFloat)
568
686
  },
@@ -583,6 +701,7 @@ let numberEqualFloat64Help = (x, y) => {
583
701
  }
584
702
  }
585
703
 
704
+ @unsafe
586
705
  let numberEqualFloat32Help = (x, y) => {
587
706
  let xIsInteger = isIntegerF32(x)
588
707
  // Basic number:
@@ -603,6 +722,45 @@ let numberEqualFloat32Help = (x, y) => {
603
722
  }
604
723
  }
605
724
 
725
+ @unsafe
726
+ let numberEqualBigIntHelp = (x, y) => {
727
+ if (isSimpleNumber(y)) {
728
+ WasmI32.eqz(BI.cmpI64(x, WasmI64.extendI32S(untagSimple(y))))
729
+ } else {
730
+ // Boxed number
731
+ let yBoxedNumberTag = boxedNumberTag(y)
732
+ match (yBoxedNumberTag) {
733
+ t when WasmI32.eq(t, Tags._GRAIN_INT32_BOXED_NUM_TAG) => {
734
+ let yBoxedVal = boxedInt32Number(y)
735
+ WasmI32.eqz(BI.cmpI64(x, WasmI64.extendI32S(yBoxedVal)))
736
+ },
737
+ t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
738
+ let yBoxedVal = boxedInt64Number(y)
739
+ WasmI32.eqz(BI.cmpI64(x, yBoxedVal))
740
+ },
741
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
742
+ BI.eq(x, y)
743
+ },
744
+ t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
745
+ // Rationals are reduced, so it must be unequal
746
+ false
747
+ },
748
+ t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
749
+ let yBoxedVal = boxedFloat32Number(y)
750
+ WasmI32.eqz(BI.cmpF32(x, yBoxedVal))
751
+ },
752
+ t when WasmI32.eq(t, Tags._GRAIN_FLOAT64_BOXED_NUM_TAG) => {
753
+ let yBoxedVal = boxedFloat64Number(y)
754
+ WasmI32.eqz(BI.cmpF64(x, yBoxedVal))
755
+ },
756
+ _ => {
757
+ throw UnknownNumberTag
758
+ },
759
+ }
760
+ }
761
+ }
762
+
763
+ @unsafe
606
764
  export let numberEqual = (x, y) => {
607
765
  if (isSimpleNumber(x)) {
608
766
  // Short circuit if non-pointer value is the same
@@ -628,6 +786,9 @@ export let numberEqual = (x, y) => {
628
786
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT64_BOXED_NUM_TAG) => {
629
787
  numberEqualFloat64Help(boxedFloat64Number(x), y)
630
788
  },
789
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
790
+ numberEqualBigIntHelp(x, y)
791
+ },
631
792
  _ => {
632
793
  throw UnknownNumberTag
633
794
  },
@@ -640,6 +801,7 @@ export let numberEqual = (x, y) => {
640
801
  * (same schema as equal())
641
802
  */
642
803
 
804
+ @unsafe
643
805
  let numberAddSubSimpleHelp = (x, y, isSub) => {
644
806
  // PRECONDITION: x is a "simple" number (value tag is 0) and isNumber(y)
645
807
  if (isSimpleNumber(y)) {
@@ -667,27 +829,44 @@ let numberAddSubSimpleHelp = (x, y, isSub) => {
667
829
  let xval64 = WasmI64.extendI32S(xval)
668
830
  let result = if (isSub) WasmI64.sub(xval64, yBoxedVal)
669
831
  else WasmI64.add(xval64, yBoxedVal)
670
- if (WasmI64.geS(yBoxedVal, 0N) && WasmI64.ltS(result, xval64)) {
671
- throw Exception.Overflow
672
- } else if (WasmI64.ltS(yBoxedVal, 0N) && WasmI64.gtS(result, xval64)) {
673
- throw Exception.Overflow
832
+ if (
833
+ WasmI64.geS(yBoxedVal, 0N) && WasmI64.ltS(result, xval64) ||
834
+ WasmI64.ltS(yBoxedVal, 0N) && WasmI64.gtS(result, xval64)
835
+ ) {
836
+ // Overflow. Promote to BigInt
837
+ let xBig = BI.makeWrappedInt32(xval)
838
+ let yBig = BI.makeWrappedInt64(yBoxedVal)
839
+ let res = if (isSub) {
840
+ BI.sub(xBig, yBig)
841
+ } else {
842
+ BI.add(xBig, yBig)
843
+ }
844
+ Memory.decRef(xBig)
845
+ Memory.decRef(yBig)
846
+ reducedBigInteger(res)
674
847
  } else {
675
848
  reducedInteger(result)
676
849
  }
677
850
  },
851
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
852
+ // Promote x to bigint and do operation
853
+ let xBig = BI.makeWrappedInt32(xval)
854
+ let result = if (isSub) BI.sub(xBig, y) else BI.add(xBig, y)
855
+ Memory.decRef(xBig)
856
+ reducedBigInteger(result)
857
+ },
678
858
  t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
679
- let yNumerator = WasmI64.extendI32S(boxedRationalNumerator(y))
680
- let yDenominator = WasmI64.extendI32S(boxedRationalDenominator(y))
681
- let expandedXNumerator = safeI64Multiply(
682
- WasmI64.extendI32S(xval),
683
- yDenominator
684
- )
685
- let result = if (isSub) WasmI64.sub(expandedXNumerator, yNumerator)
686
- else WasmI64.add(expandedXNumerator, yNumerator)
687
- if (WasmI64.ltS(result, _I32_MIN) || WasmI64.gtS(result, _I32_MAX)) {
688
- throw Exception.Overflow
689
- }
690
- reducedFraction64(result, yDenominator)
859
+ let xBig = BI.makeWrappedInt32(xval)
860
+ let yNumerator = boxedRationalNumerator(y)
861
+ let yDenominator = boxedRationalDenominator(y)
862
+ let expandedXNumerator = BI.mul(xBig, yDenominator)
863
+ Memory.decRef(xBig)
864
+ let result = if (isSub) BI.sub(expandedXNumerator, yNumerator)
865
+ else BI.add(expandedXNumerator, yNumerator)
866
+ let ret = reducedFractionBigInt(result, yDenominator)
867
+ Memory.decRef(expandedXNumerator)
868
+ Memory.decRef(result)
869
+ ret
691
870
  },
692
871
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
693
872
  let yBoxedVal = boxedFloat32Number(y)
@@ -711,16 +890,29 @@ let numberAddSubSimpleHelp = (x, y, isSub) => {
711
890
  }
712
891
  }
713
892
 
893
+ @unsafe
714
894
  let numberAddSubInt64Help = (xval, y, isSub) => {
715
895
  if (isSimpleNumber(y)) {
716
896
  let yval = WasmI64.extendI32S(untagSimple(y))
717
897
  let result = if (isSub) WasmI64.sub(xval, yval) else WasmI64.add(xval, yval)
718
- if (WasmI64.geS(yval, 0N) && WasmI64.ltS(result, xval)) {
719
- throw Exception.Overflow
720
- } else if (WasmI64.ltS(yval, 0N) && WasmI64.gtS(result, xval)) {
721
- throw Exception.Overflow
898
+ if (
899
+ WasmI64.geS(yval, 0N) && WasmI64.ltS(result, xval) ||
900
+ WasmI64.ltS(yval, 0N) && WasmI64.gtS(result, xval)
901
+ ) {
902
+ // Overflow. Promote to BigInt
903
+ let xBig = BI.makeWrappedInt64(xval)
904
+ let yBig = BI.makeWrappedInt64(yval)
905
+ let res = if (isSub) {
906
+ BI.sub(xBig, yBig)
907
+ } else {
908
+ BI.add(xBig, yBig)
909
+ }
910
+ Memory.decRef(xBig)
911
+ Memory.decRef(yBig)
912
+ reducedBigInteger(res)
913
+ } else {
914
+ reducedInteger(result)
722
915
  }
723
- reducedInteger(result)
724
916
  } else {
725
917
  let yBoxedNumberTag = boxedNumberTag(y)
726
918
  match (yBoxedNumberTag) {
@@ -735,31 +927,51 @@ let numberAddSubInt64Help = (xval, y, isSub) => {
735
927
  let xval64 = xval
736
928
  let result = if (isSub) WasmI64.sub(xval64, yBoxedVal)
737
929
  else WasmI64.add(xval64, yBoxedVal)
738
- if (WasmI64.geS(yBoxedVal, 0N) && WasmI64.ltS(result, xval64)) {
739
- throw Exception.Overflow
740
- } else if (WasmI64.ltS(yBoxedVal, 0N) && WasmI64.gtS(result, xval64)) {
741
- throw Exception.Overflow
930
+ if (
931
+ WasmI64.geS(yBoxedVal, 0N) && WasmI64.ltS(result, xval64) ||
932
+ WasmI64.ltS(yBoxedVal, 0N) && WasmI64.gtS(result, xval64)
933
+ ) {
934
+ // Overflow. Promote to BigInt
935
+ let xBig = BI.makeWrappedInt64(xval64)
936
+ let yBig = BI.makeWrappedInt64(yBoxedVal)
937
+ let res = if (isSub) {
938
+ BI.sub(xBig, yBig)
939
+ } else {
940
+ BI.add(xBig, yBig)
941
+ }
942
+ Memory.decRef(xBig)
943
+ Memory.decRef(yBig)
944
+ reducedBigInteger(res)
742
945
  } else {
743
946
  reducedInteger(result)
744
947
  }
745
948
  },
949
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
950
+ // Promote x to bigint and do operation
951
+ let xBig = BI.makeWrappedInt64(xval)
952
+ let result = if (isSub) BI.sub(xBig, y) else BI.add(xBig, y)
953
+ Memory.decRef(xBig)
954
+ reducedBigInteger(result)
955
+ },
746
956
  t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
747
- let yNumerator = WasmI64.extendI32S(boxedRationalNumerator(y))
748
- let yDenominator = WasmI64.extendI32S(boxedRationalDenominator(y))
749
- let expandedXNumerator = safeI64Multiply(xval, yDenominator)
750
- let result = if (isSub) WasmI64.sub(expandedXNumerator, yNumerator)
751
- else WasmI64.add(expandedXNumerator, yNumerator)
752
- if (WasmI64.ltS(result, _I32_MIN) || WasmI64.gtS(result, _I32_MAX)) {
753
- throw Exception.Overflow
754
- }
755
- reducedFraction64(result, yDenominator)
957
+ let xBig = BI.makeWrappedInt64(xval)
958
+ let yNumerator = boxedRationalNumerator(y)
959
+ let yDenominator = boxedRationalDenominator(y)
960
+ let expandedXNumerator = BI.mul(xBig, yDenominator)
961
+ Memory.decRef(xBig)
962
+ let result = if (isSub) BI.sub(expandedXNumerator, yNumerator)
963
+ else BI.add(expandedXNumerator, yNumerator)
964
+ let ret = reducedFractionBigInt(result, yDenominator)
965
+ Memory.decRef(expandedXNumerator)
966
+ Memory.decRef(result)
967
+ ret
756
968
  },
757
969
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
758
970
  let xval = WasmF32.convertI64S(xval)
759
971
  let yBoxedVal = boxedFloat32Number(y)
760
972
  let result = if (isSub) WasmF32.sub(xval, yBoxedVal)
761
973
  else WasmF32.add(xval, yBoxedVal)
762
- // TODO: (#304) this isn't safe enough
974
+ // TODO(#304): this isn't safe enough
763
975
  newFloat32(result)
764
976
  },
765
977
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT64_BOXED_NUM_TAG) => {
@@ -776,6 +988,7 @@ let numberAddSubInt64Help = (xval, y, isSub) => {
776
988
  }
777
989
  }
778
990
 
991
+ @unsafe
779
992
  let numberAddSubFloat32Help = (xval, y, isSub) => {
780
993
  if (
781
994
  !isSimpleNumber(y) &&
@@ -787,85 +1000,230 @@ let numberAddSubFloat32Help = (xval, y, isSub) => {
787
1000
  let result = if (isSub) WasmF64.sub(xval, yval) else WasmF64.add(xval, yval)
788
1001
  newFloat64(result)
789
1002
  } else {
1003
+ // incRef y to reuse it via WasmI32.toGrain
1004
+ Memory.incRef(y)
790
1005
  let yval = coerceNumberToWasmF32(WasmI32.toGrain(y): Number)
791
1006
  let result = if (isSub) WasmF32.sub(xval, yval) else WasmF32.add(xval, yval)
792
1007
  newFloat32(result)
793
1008
  }
794
1009
  }
795
1010
 
1011
+ @unsafe
796
1012
  let numberAddSubFloat64Help = (xval, y, isSub) => {
1013
+ // incRef y to reuse it via WasmI32.toGrain
1014
+ Memory.incRef(y)
797
1015
  let yval = coerceNumberToWasmF64(WasmI32.toGrain(y): Number)
798
1016
  let result = if (isSub) WasmF64.sub(xval, yval) else WasmF64.add(xval, yval)
799
1017
  newFloat64(result)
800
1018
  }
801
1019
 
1020
+ @unsafe
802
1021
  let numberAddSubInt32Help = (xval, y, isSub) => {
803
1022
  numberAddSubInt64Help(WasmI64.extendI32S(xval), y, isSub)
804
1023
  }
805
1024
 
806
- let rec numberAddSubHelp = (x, y, isSub) => {
807
- if (isSimpleNumber(x)) {
808
- numberAddSubSimpleHelp(x, y, isSub)
1025
+ @unsafe
1026
+ let numberAddSubBigIntHelp = (x, y, isSub) => {
1027
+ if (isSimpleNumber(y)) {
1028
+ let yval = WasmI64.extendI32S(untagSimple(y))
1029
+ let yBig = BI.makeWrappedInt64(yval)
1030
+ let res = if (isSub) {
1031
+ BI.sub(x, yBig)
1032
+ } else {
1033
+ BI.add(x, yBig)
1034
+ }
1035
+ Memory.decRef(yBig)
1036
+ reducedBigInteger(res)
809
1037
  } else {
810
- let xtag = boxedNumberTag(x)
811
- match (xtag) {
1038
+ let yBoxedNumberTag = boxedNumberTag(y)
1039
+ match (yBoxedNumberTag) {
812
1040
  t when WasmI32.eq(t, Tags._GRAIN_INT32_BOXED_NUM_TAG) => {
813
- numberAddSubInt32Help(boxedInt32Number(x), y, isSub)
1041
+ let yBoxedVal = WasmI64.extendI32S(boxedInt32Number(y))
1042
+ let yBig = BI.makeWrappedInt64(yBoxedVal)
1043
+ let res = if (isSub) {
1044
+ BI.sub(x, yBig)
1045
+ } else {
1046
+ BI.add(x, yBig)
1047
+ }
1048
+ Memory.decRef(yBig)
1049
+ reducedBigInteger(res)
814
1050
  },
815
1051
  t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
816
- numberAddSubInt64Help(boxedInt64Number(x), y, isSub)
1052
+ let yBoxedVal = boxedInt64Number(y)
1053
+ let yBig = BI.makeWrappedInt64(yBoxedVal)
1054
+ let res = if (isSub) {
1055
+ BI.sub(x, yBig)
1056
+ } else {
1057
+ BI.add(x, yBig)
1058
+ }
1059
+ Memory.decRef(yBig)
1060
+ reducedBigInteger(res)
1061
+ },
1062
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
1063
+ let res = if (isSub) {
1064
+ BI.sub(x, y)
1065
+ } else {
1066
+ BI.add(x, y)
1067
+ }
1068
+ reducedBigInteger(res)
817
1069
  },
818
1070
  t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
819
- numberAddSubRationalHelp(x, y, isSub)
1071
+ let yNumerator = boxedRationalNumerator(y)
1072
+ let yDenominator = boxedRationalDenominator(y)
1073
+ let expandedXNumerator = BI.mul(x, yDenominator)
1074
+ let result = if (isSub) BI.sub(expandedXNumerator, yNumerator)
1075
+ else BI.add(expandedXNumerator, yNumerator)
1076
+ Memory.decRef(expandedXNumerator)
1077
+ let ret = reducedFractionBigInt(result, yDenominator)
1078
+ Memory.decRef(result)
1079
+ ret
820
1080
  },
821
1081
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
822
- numberAddSubFloat32Help(boxedFloat32Number(x), y, isSub)
1082
+ let xval = BI.toFloat32(x)
1083
+ let yBoxedVal = boxedFloat32Number(y)
1084
+ let result = if (isSub) WasmF32.sub(xval, yBoxedVal)
1085
+ else WasmF32.add(xval, yBoxedVal)
1086
+ // TODO: (#304) this isn't safe enough
1087
+ newFloat32(result)
823
1088
  },
824
1089
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT64_BOXED_NUM_TAG) => {
825
- numberAddSubFloat64Help(boxedFloat64Number(x), y, isSub)
1090
+ let xval = BI.toFloat64(x)
1091
+ let yBoxedVal = boxedFloat64Number(y)
1092
+ let result = if (isSub) WasmF64.sub(xval, yBoxedVal)
1093
+ else WasmF64.add(xval, yBoxedVal)
1094
+ newFloat64(result)
826
1095
  },
827
1096
  _ => {
828
1097
  throw UnknownNumberTag
829
1098
  },
830
1099
  }
831
1100
  }
832
- }, numberAddSubRationalHelp = (x, y, isSub) => {
1101
+ }
1102
+
1103
+ @unsafe
1104
+ let numberAddSubRationalHelp = (x, y, isSub) => {
1105
+ let xNumerator = boxedRationalNumerator(x)
1106
+ let xDenominator = boxedRationalDenominator(x)
833
1107
  if (isSimpleNumber(y)) {
834
- numberAddSubSimpleHelp(y, x, isSub)
1108
+ let yval = untagSimple(y)
1109
+ let yBig = BI.makeWrappedInt32(yval)
1110
+ let expandedYNumerator = BI.mul(xDenominator, yBig)
1111
+ let result = if (isSub) BI.sub(xNumerator, expandedYNumerator)
1112
+ else BI.add(xNumerator, expandedYNumerator)
1113
+ Memory.decRef(expandedYNumerator)
1114
+ Memory.decRef(yBig)
1115
+ let ret = reducedFractionBigInt(result, xDenominator)
1116
+ Memory.decRef(result)
1117
+ ret
835
1118
  } else {
836
1119
  let ytag = boxedNumberTag(y)
837
1120
  match (ytag) {
838
1121
  t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
839
1122
  // The one case we don't delegate is rational +/- rational
840
- let xNumerator = WasmI64.extendI32S(boxedRationalNumerator(x))
841
- let xDenominator = WasmI64.extendI32S(boxedRationalDenominator(x))
842
- let yNumerator = WasmI64.extendI32S(boxedRationalNumerator(y))
843
- let yDenominator = WasmI64.extendI32S(boxedRationalDenominator(y))
844
- // TODO: {#304) this could be written in a more overflow-proof way
845
- if (WasmI64.eq(xDenominator, yDenominator)) {
846
- let result = if (isSub) WasmI64.sub(xNumerator, yNumerator)
847
- else WasmI64.add(xNumerator, yNumerator)
848
- reducedFraction64(result, xDenominator)
1123
+ let xNumerator = boxedRationalNumerator(x)
1124
+ let xDenominator = boxedRationalDenominator(x)
1125
+ let yNumerator = boxedRationalNumerator(y)
1126
+ let yDenominator = boxedRationalDenominator(y)
1127
+ if (BI.eq(xDenominator, yDenominator)) {
1128
+ let newNumerator = if (isSub) BI.sub(xNumerator, yNumerator)
1129
+ else BI.add(xNumerator, yNumerator)
1130
+ let ret = reducedFractionBigInt(newNumerator, xDenominator)
1131
+ Memory.decRef(newNumerator)
1132
+ ret
849
1133
  } else {
850
- let numerator1 = safeI64Multiply(xNumerator, yDenominator)
851
- let numerator2 = safeI64Multiply(yNumerator, xDenominator)
852
- let numerator = if (isSub) WasmI64.sub(numerator1, numerator2)
853
- else WasmI64.add(numerator1, numerator2)
854
- let denominator = safeI64Multiply(xDenominator, yDenominator)
855
- reducedFraction64(numerator, denominator)
1134
+ let numerator1 = BI.mul(xNumerator, yDenominator)
1135
+ let numerator2 = BI.mul(yNumerator, xDenominator)
1136
+ let numerator = if (isSub) BI.sub(numerator1, numerator2)
1137
+ else BI.add(numerator1, numerator2)
1138
+ let denominator = BI.mul(xDenominator, yDenominator)
1139
+ let ret = reducedFractionBigInt(numerator, denominator)
1140
+ Memory.decRef(numerator1)
1141
+ Memory.decRef(numerator2)
1142
+ Memory.decRef(numerator)
1143
+ ret
856
1144
  }
857
1145
  },
858
1146
  t when WasmI32.eq(t, Tags._GRAIN_INT32_BOXED_NUM_TAG) => {
859
- numberAddSubHelp(y, x, isSub)
1147
+ let yBig = BI.makeWrappedInt32(boxedInt32Number(y))
1148
+ let expandedYNumerator = BI.mul(yBig, xDenominator)
1149
+ Memory.decRef(yBig)
1150
+ let result = if (isSub) BI.sub(xNumerator, expandedYNumerator)
1151
+ else BI.add(xNumerator, expandedYNumerator)
1152
+ let ret = reducedFractionBigInt(result, xDenominator)
1153
+ Memory.decRef(expandedYNumerator)
1154
+ Memory.decRef(result)
1155
+ ret
1156
+ },
1157
+ t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
1158
+ let yBig = BI.makeWrappedInt64(boxedInt64Number(y))
1159
+ let expandedYNumerator = BI.mul(yBig, xDenominator)
1160
+ Memory.decRef(yBig)
1161
+ let result = if (isSub) BI.sub(xNumerator, expandedYNumerator)
1162
+ else BI.add(xNumerator, expandedYNumerator)
1163
+ let ret = reducedFractionBigInt(result, xDenominator)
1164
+ Memory.decRef(expandedYNumerator)
1165
+ Memory.decRef(result)
1166
+ ret
1167
+ },
1168
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
1169
+ let expandedYNumerator = BI.mul(xDenominator, y)
1170
+ let result = if (isSub) BI.sub(xNumerator, expandedYNumerator)
1171
+ else BI.add(xNumerator, expandedYNumerator)
1172
+ Memory.decRef(expandedYNumerator)
1173
+ let ret = reducedFractionBigInt(result, xDenominator)
1174
+ Memory.decRef(result)
1175
+ ret
1176
+ },
1177
+ t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
1178
+ let xval = WasmF32.div(
1179
+ BI.toFloat32(xNumerator),
1180
+ BI.toFloat32(xDenominator)
1181
+ )
1182
+ let result = if (isSub) WasmF32.sub(xval, boxedFloat32Number(y))
1183
+ else WasmF32.add(xval, boxedFloat32Number(y))
1184
+ newFloat32(result)
1185
+ },
1186
+ t when WasmI32.eq(t, Tags._GRAIN_FLOAT64_BOXED_NUM_TAG) => {
1187
+ let xnumfloat = BI.toFloat64(xNumerator)
1188
+ let xdenfloat = BI.toFloat64(xDenominator)
1189
+ let xval = WasmF64.div(xnumfloat, xdenfloat)
1190
+ let yval = boxedFloat64Number(y)
1191
+ let result = if (isSub) WasmF64.sub(xval, yval)
1192
+ else WasmF64.add(xval, yval)
1193
+ let ret = newFloat64(result)
1194
+ ret
1195
+ },
1196
+ _ => {
1197
+ throw UnknownNumberTag
1198
+ },
1199
+ }
1200
+ }
1201
+ }
1202
+
1203
+ @unsafe
1204
+ let numberAddSubHelp = (x, y, isSub) => {
1205
+ if (isSimpleNumber(x)) {
1206
+ numberAddSubSimpleHelp(x, y, isSub)
1207
+ } else {
1208
+ let xtag = boxedNumberTag(x)
1209
+ match (xtag) {
1210
+ t when WasmI32.eq(t, Tags._GRAIN_INT32_BOXED_NUM_TAG) => {
1211
+ numberAddSubInt32Help(boxedInt32Number(x), y, isSub)
860
1212
  },
861
1213
  t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
862
- numberAddSubHelp(y, x, isSub)
1214
+ numberAddSubInt64Help(boxedInt64Number(x), y, isSub)
1215
+ },
1216
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
1217
+ numberAddSubBigIntHelp(x, y, isSub)
1218
+ },
1219
+ t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
1220
+ numberAddSubRationalHelp(x, y, isSub)
863
1221
  },
864
1222
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
865
- numberAddSubHelp(y, x, isSub)
1223
+ numberAddSubFloat32Help(boxedFloat32Number(x), y, isSub)
866
1224
  },
867
1225
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT64_BOXED_NUM_TAG) => {
868
- numberAddSubHelp(y, x, isSub)
1226
+ numberAddSubFloat64Help(boxedFloat64Number(x), y, isSub)
869
1227
  },
870
1228
  _ => {
871
1229
  throw UnknownNumberTag
@@ -874,10 +1232,12 @@ let rec numberAddSubHelp = (x, y, isSub) => {
874
1232
  }
875
1233
  }
876
1234
 
1235
+ @unsafe
877
1236
  let numberAdd = (x, y) => {
878
1237
  WasmI32.toGrain(numberAddSubHelp(x, y, false)): Number
879
1238
  }
880
1239
 
1240
+ @unsafe
881
1241
  let numberSub = (x, y) => {
882
1242
  WasmI32.toGrain(numberAddSubHelp(x, y, true)): Number
883
1243
  }
@@ -887,12 +1247,32 @@ let numberSub = (x, y) => {
887
1247
  * (same schema as equal())
888
1248
  */
889
1249
 
1250
+ @unsafe
1251
+ let safeI64Multiply = (x, y) => {
1252
+ let prod = WasmI64.mul(x, y)
1253
+ if (WasmI64.ne(x, 0N)) {
1254
+ if (WasmI64.ne(WasmI64.divS(prod, x), y)) {
1255
+ let xBig = BI.makeWrappedInt64(x)
1256
+ let yBig = BI.makeWrappedInt64(y)
1257
+ let result = BI.mul(xBig, yBig)
1258
+ Memory.decRef(xBig)
1259
+ Memory.decRef(yBig)
1260
+ result
1261
+ } else {
1262
+ reducedInteger(prod)
1263
+ }
1264
+ } else {
1265
+ reducedInteger(prod)
1266
+ }
1267
+ }
1268
+
1269
+ @unsafe
890
1270
  let numberTimesDivideInt64Help = (xval, y, isDivide) => {
891
1271
  if (isSimpleNumber(y)) {
892
1272
  if (isDivide) {
893
1273
  reducedFraction64(xval, WasmI64.extendI32S(untagSimple(y)))
894
1274
  } else {
895
- reducedInteger(safeI64Multiply(xval, WasmI64.extendI32S(untagSimple(y))))
1275
+ safeI64Multiply(xval, WasmI64.extendI32S(untagSimple(y)))
896
1276
  }
897
1277
  } else {
898
1278
  let yBoxedNumberTag = boxedNumberTag(y)
@@ -902,7 +1282,7 @@ let numberTimesDivideInt64Help = (xval, y, isDivide) => {
902
1282
  if (isDivide) {
903
1283
  reducedFraction64(xval, yBoxedVal)
904
1284
  } else {
905
- reducedInteger(safeI64Multiply(xval, yBoxedVal))
1285
+ safeI64Multiply(xval, yBoxedVal)
906
1286
  }
907
1287
  },
908
1288
  t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
@@ -910,21 +1290,38 @@ let numberTimesDivideInt64Help = (xval, y, isDivide) => {
910
1290
  if (isDivide) {
911
1291
  reducedFraction64(xval, yBoxedVal)
912
1292
  } else {
913
- reducedInteger(safeI64Multiply(xval, yBoxedVal))
1293
+ safeI64Multiply(xval, yBoxedVal)
1294
+ }
1295
+ },
1296
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
1297
+ let xBig = BI.makeWrappedInt64(xval)
1298
+ let ret = if (isDivide) {
1299
+ reducedFractionBigInt(xBig, y)
1300
+ } else {
1301
+ reducedBigInteger(BI.mul(xBig, y))
914
1302
  }
1303
+ Memory.decRef(xBig)
1304
+ ret
915
1305
  },
916
1306
  t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
917
- let yNumerator = WasmI64.extendI32S(boxedRationalNumerator(y))
918
- let yDenominator = WasmI64.extendI32S(boxedRationalDenominator(y))
919
- if (isDivide) {
1307
+ let yNumerator = boxedRationalNumerator(y)
1308
+ let yDenominator = boxedRationalDenominator(y)
1309
+ let xBig = BI.makeWrappedInt64(xval)
1310
+ let ret = if (isDivide) {
920
1311
  // x / (a / b) == (x * b) / a
921
- let numerator = safeI64Multiply(xval, yDenominator)
922
- reducedFraction64(numerator, yNumerator)
1312
+ let numerator = BI.mul(xBig, yDenominator)
1313
+ let ret = reducedFractionBigInt(numerator, yNumerator)
1314
+ Memory.decRef(numerator)
1315
+ ret
923
1316
  } else {
924
1317
  // x * (a / b) == (x * a) / b
925
- let numerator = safeI64Multiply(xval, yNumerator)
926
- reducedFraction64(numerator, yDenominator)
1318
+ let numerator = BI.mul(xBig, yNumerator)
1319
+ let ret = reducedFractionBigInt(numerator, yDenominator)
1320
+ Memory.decRef(numerator)
1321
+ ret
927
1322
  }
1323
+ Memory.decRef(xBig)
1324
+ ret
928
1325
  },
929
1326
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
930
1327
  let xval = WasmF32.convertI64S(xval)
@@ -952,88 +1349,198 @@ let numberTimesDivideInt64Help = (xval, y, isDivide) => {
952
1349
  }
953
1350
  }
954
1351
 
1352
+ @unsafe
1353
+ let numberTimesDivideBigIntHelp = (x, y, isDivide) => {
1354
+ if (isSimpleNumber(y)) {
1355
+ let yBig = BI.makeWrappedInt32(untagSimple(y))
1356
+ let ret = if (isDivide) {
1357
+ reducedFractionBigInt(x, yBig)
1358
+ } else {
1359
+ reducedBigInteger(BI.mul(x, yBig))
1360
+ }
1361
+ Memory.decRef(yBig)
1362
+ ret
1363
+ } else {
1364
+ let yBoxedNumberTag = boxedNumberTag(y)
1365
+ match (yBoxedNumberTag) {
1366
+ t when WasmI32.eq(t, Tags._GRAIN_INT32_BOXED_NUM_TAG) => {
1367
+ let yBoxedVal = WasmI64.extendI32S(boxedInt32Number(y))
1368
+ let yBig = BI.makeWrappedInt64(yBoxedVal)
1369
+ let ret = if (isDivide) {
1370
+ reducedFractionBigInt(x, yBig)
1371
+ } else {
1372
+ reducedBigInteger(BI.mul(x, yBig))
1373
+ }
1374
+ Memory.decRef(yBig)
1375
+ ret
1376
+ },
1377
+ t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
1378
+ let yBoxedVal = boxedInt64Number(y)
1379
+ let yBig = BI.makeWrappedInt64(yBoxedVal)
1380
+ let ret = if (isDivide) {
1381
+ reducedFractionBigInt(x, yBig)
1382
+ } else {
1383
+ reducedBigInteger(BI.mul(x, yBig))
1384
+ }
1385
+ Memory.decRef(yBig)
1386
+ ret
1387
+ },
1388
+ t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
1389
+ let yNumerator = boxedRationalNumerator(y)
1390
+ let yDenominator = boxedRationalDenominator(y)
1391
+ if (isDivide) {
1392
+ // x / (a / b) == (x * b) / a
1393
+ let numerator = BI.mul(x, yDenominator)
1394
+ let ret = reducedFractionBigInt(numerator, yNumerator)
1395
+ Memory.decRef(numerator)
1396
+ ret
1397
+ } else {
1398
+ // x * (a / b) == (x * a) / b
1399
+ let numerator = BI.mul(x, yNumerator)
1400
+ let ret = reducedFractionBigInt(numerator, yDenominator)
1401
+ Memory.decRef(numerator)
1402
+ ret
1403
+ }
1404
+ },
1405
+ t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
1406
+ // We make the decision to always promote to f64 here, since
1407
+ // we can only fit rather small bigints (<4 limbs) into an F32
1408
+ let xval = BI.toFloat64(x)
1409
+ let yBoxedVal = WasmF64.promoteF32(boxedFloat32Number(y))
1410
+ // TODO: (#304) is this safe?
1411
+ if (isDivide) {
1412
+ newFloat64(WasmF64.div(xval, yBoxedVal))
1413
+ } else {
1414
+ newFloat64(WasmF64.mul(xval, yBoxedVal))
1415
+ }
1416
+ },
1417
+ t when WasmI32.eq(t, Tags._GRAIN_FLOAT64_BOXED_NUM_TAG) => {
1418
+ let xval = BI.toFloat64(x)
1419
+ let yBoxedVal = boxedFloat64Number(y)
1420
+ if (isDivide) {
1421
+ newFloat64(WasmF64.div(xval, yBoxedVal))
1422
+ } else {
1423
+ newFloat64(WasmF64.mul(xval, yBoxedVal))
1424
+ }
1425
+ },
1426
+ _ => {
1427
+ throw UnknownNumberTag
1428
+ },
1429
+ }
1430
+ }
1431
+ }
1432
+
1433
+ @unsafe
955
1434
  let numberTimesDivideSimpleHelp = (x, y, isDivide) => {
956
1435
  // PRECONDITION: x is a "simple" number (value tag is 0) and isNumber(y)
957
1436
  let xval = untagSimple(x) // <- actual int value of x
958
1437
  numberTimesDivideInt64Help(WasmI64.extendI32S(xval), y, isDivide)
959
1438
  }
960
1439
 
1440
+ @unsafe
961
1441
  let numberTimesDivideInt32Help = (xval, y, isDivide) => {
962
1442
  numberTimesDivideInt64Help(WasmI64.extendI32S(xval), y, isDivide)
963
1443
  }
964
1444
 
1445
+ @unsafe
965
1446
  let numberTimesDivideRationalHelp = (x, y, isDivide) => {
966
1447
  // Division isn't commutative, so we actually need to do the work
967
- let xNumerator = WasmI64.extendI32S(boxedRationalNumerator(x))
968
- let xDenominator = WasmI64.extendI32S(boxedRationalDenominator(x))
1448
+ let xNumerator = boxedRationalNumerator(x)
1449
+ let xDenominator = boxedRationalDenominator(x)
969
1450
  if (isSimpleNumber(y)) {
970
- if (isDivide) {
1451
+ let yBig = BI.makeWrappedInt32(untagSimple(y))
1452
+ let ret = if (isDivide) {
971
1453
  // (a / b) / y == a / (b * y)
972
- let denominator = safeI64Multiply(
973
- xDenominator,
974
- WasmI64.extendI32S(untagSimple(y))
975
- )
976
- reducedFraction64(xNumerator, denominator)
1454
+ let denominator = BI.mul(xDenominator, yBig)
1455
+ let ret = reducedFractionBigInt(xNumerator, denominator)
1456
+ Memory.decRef(denominator)
1457
+ ret
977
1458
  } else {
978
1459
  // (a / b) * y == (a * y) / b
979
- let numerator = safeI64Multiply(
980
- xNumerator,
981
- WasmI64.extendI32S(untagSimple(y))
982
- )
983
- reducedFraction64(numerator, xDenominator)
1460
+ let numerator = BI.mul(xNumerator, yBig)
1461
+ let ret = reducedFractionBigInt(numerator, xDenominator)
1462
+ Memory.decRef(numerator)
1463
+ ret
984
1464
  }
1465
+ if (WasmI32.ne(yBig, ret)) {
1466
+ Memory.decRef(yBig)
1467
+ void
1468
+ }
1469
+ ret
985
1470
  } else {
986
1471
  let ytag = boxedNumberTag(y)
987
1472
  match (ytag) {
988
1473
  t when WasmI32.eq(t, Tags._GRAIN_INT32_BOXED_NUM_TAG) => {
989
1474
  // Same idea as above
990
- if (isDivide) {
1475
+ let yBig = BI.makeWrappedInt32(boxedInt32Number(y))
1476
+ let ret = if (isDivide) {
991
1477
  // (a / b) / y == a / (b * y)
992
- let denominator = safeI64Multiply(
993
- xDenominator,
994
- WasmI64.extendI32S(boxedInt32Number(y))
995
- )
996
- reducedFraction64(xNumerator, denominator)
1478
+ let denominator = BI.mul(xDenominator, yBig)
1479
+ let ret = reducedFractionBigInt(xNumerator, denominator)
1480
+ Memory.decRef(denominator)
1481
+ ret
997
1482
  } else {
998
1483
  // (a / b) * y == (a * y) / b
999
- let numerator = safeI64Multiply(
1000
- xNumerator,
1001
- WasmI64.extendI32S(boxedInt32Number(y))
1002
- )
1003
- reducedFraction64(numerator, xDenominator)
1484
+ let numerator = BI.mul(xNumerator, yBig)
1485
+ let ret = reducedFractionBigInt(numerator, xDenominator)
1486
+ Memory.decRef(numerator)
1487
+ ret
1004
1488
  }
1489
+ Memory.decRef(yBig)
1490
+ ret
1005
1491
  },
1006
1492
  t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
1007
1493
  // Same idea as above
1494
+ let yBig = BI.makeWrappedInt64(boxedInt64Number(y))
1495
+ let ret = if (isDivide) {
1496
+ // (a / b) / y == a / (b * y)
1497
+ let denominator = BI.mul(xDenominator, yBig)
1498
+ let ret = reducedFractionBigInt(xNumerator, denominator)
1499
+ Memory.decRef(denominator)
1500
+ ret
1501
+ } else {
1502
+ // (a / b) * y == (a * y) / b
1503
+ let numerator = BI.mul(xNumerator, yBig)
1504
+ let ret = reducedFractionBigInt(numerator, xDenominator)
1505
+ Memory.decRef(numerator)
1506
+ ret
1507
+ }
1508
+ Memory.decRef(yBig)
1509
+ ret
1510
+ },
1511
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
1008
1512
  if (isDivide) {
1009
1513
  // (a / b) / y == a / (b * y)
1010
- let denominator = safeI64Multiply(xDenominator, boxedInt64Number(y))
1011
- reducedFraction64(xNumerator, denominator)
1514
+ let denominator = BI.mul(xDenominator, y)
1515
+ let ret = reducedFractionBigInt(xNumerator, denominator)
1516
+ Memory.decRef(denominator)
1517
+ ret
1012
1518
  } else {
1013
1519
  // (a / b) * y == (a * y) / b
1014
- let numerator = safeI64Multiply(xNumerator, boxedInt64Number(y))
1015
- reducedFraction64(numerator, xDenominator)
1520
+ let numerator = BI.mul(xNumerator, y)
1521
+ let ret = reducedFractionBigInt(numerator, xDenominator)
1522
+ Memory.decRef(numerator)
1523
+ ret
1016
1524
  }
1017
1525
  },
1018
1526
  t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
1019
- let xNumerator = WasmI64.extendI32S(boxedRationalNumerator(x))
1020
- let xDenominator = WasmI64.extendI32S(boxedRationalDenominator(x))
1021
- let yNumerator = WasmI64.extendI32S(boxedRationalNumerator(y))
1022
- let yDenominator = WasmI64.extendI32S(boxedRationalDenominator(y))
1527
+ let xNumerator = boxedRationalNumerator(x)
1528
+ let xDenominator = boxedRationalDenominator(x)
1529
+ let yNumerator = boxedRationalNumerator(y)
1530
+ let yDenominator = boxedRationalDenominator(y)
1023
1531
  // (a / b) * (c / d) == (a * c) / (b * d)
1024
1532
  // (a / b) / (c / d) == (a * d) / (b * c)
1025
1533
  // TODO: (#304) this could maybe be written in a more overflow-proof way
1026
- let numerator = if (isDivide) safeI64Multiply(xNumerator, yDenominator)
1027
- else safeI64Multiply(xNumerator, yNumerator)
1028
- let denominator = if (isDivide)
1029
- safeI64Multiply(xDenominator, yNumerator)
1030
- else safeI64Multiply(xDenominator, yDenominator)
1031
- reducedFraction64(numerator, denominator)
1534
+ let numerator = if (isDivide) BI.mul(xNumerator, yDenominator)
1535
+ else BI.mul(xNumerator, yNumerator)
1536
+ let denominator = if (isDivide) BI.mul(xDenominator, yNumerator)
1537
+ else BI.mul(xDenominator, yDenominator)
1538
+ reducedFractionBigInt(numerator, denominator)
1032
1539
  },
1033
1540
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
1034
- let asFloat = WasmF32.div(
1035
- WasmF32.convertI64S(xNumerator),
1036
- WasmF32.convertI64S(xDenominator)
1541
+ // TODO(#1190): We should probably use something more accurate if possible here
1542
+ let asFloat = WasmF32.demoteF64(
1543
+ WasmF64.div(BI.toFloat64(xNumerator), BI.toFloat64(xDenominator))
1037
1544
  )
1038
1545
  if (isDivide) {
1039
1546
  newFloat32(WasmF32.div(asFloat, boxedFloat32Number(y)))
@@ -1042,9 +1549,10 @@ let numberTimesDivideRationalHelp = (x, y, isDivide) => {
1042
1549
  }
1043
1550
  },
1044
1551
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT64_BOXED_NUM_TAG) => {
1552
+ // TODO(#1190): We should probably use something more accurate if possible here
1045
1553
  let asFloat = WasmF64.div(
1046
- WasmF64.convertI64S(xNumerator),
1047
- WasmF64.convertI64S(xDenominator)
1554
+ BI.toFloat64(xNumerator),
1555
+ BI.toFloat64(xDenominator)
1048
1556
  )
1049
1557
  if (isDivide) {
1050
1558
  newFloat64(WasmF64.div(asFloat, boxedFloat64Number(y)))
@@ -1059,7 +1567,10 @@ let numberTimesDivideRationalHelp = (x, y, isDivide) => {
1059
1567
  }
1060
1568
  }
1061
1569
 
1570
+ @unsafe
1062
1571
  let numberTimesDivideFloat64Help = (x, y, isDivide) => {
1572
+ // incRef y to reuse it via WasmI32.toGrain
1573
+ Memory.incRef(y)
1063
1574
  let yAsFloat = coerceNumberToWasmF64(WasmI32.toGrain(y): Number)
1064
1575
  if (isDivide) {
1065
1576
  newFloat64(WasmF64.div(x, yAsFloat))
@@ -1068,6 +1579,7 @@ let numberTimesDivideFloat64Help = (x, y, isDivide) => {
1068
1579
  }
1069
1580
  }
1070
1581
 
1582
+ @unsafe
1071
1583
  let numberTimesDivideFloat32Help = (x, y, isDivide) => {
1072
1584
  if (
1073
1585
  isBoxedNumber(y) &&
@@ -1080,6 +1592,8 @@ let numberTimesDivideFloat32Help = (x, y, isDivide) => {
1080
1592
  newFloat64(WasmF64.mul(WasmF64.promoteF32(x), boxedFloat64Number(y)))
1081
1593
  }
1082
1594
  } else {
1595
+ // incRef y to reuse it via WasmI32.toGrain
1596
+ Memory.incRef(y)
1083
1597
  let yAsFloat = coerceNumberToWasmF32(WasmI32.toGrain(y): Number)
1084
1598
  if (isDivide) {
1085
1599
  newFloat32(WasmF32.div(x, yAsFloat))
@@ -1089,6 +1603,7 @@ let numberTimesDivideFloat32Help = (x, y, isDivide) => {
1089
1603
  }
1090
1604
  }
1091
1605
 
1606
+ @unsafe
1092
1607
  let numberTimesDivideHelp = (x, y, isDivide) => {
1093
1608
  if (isSimpleNumber(x)) {
1094
1609
  numberTimesDivideSimpleHelp(x, y, isDivide)
@@ -1101,6 +1616,9 @@ let numberTimesDivideHelp = (x, y, isDivide) => {
1101
1616
  t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
1102
1617
  numberTimesDivideInt64Help(boxedInt64Number(x), y, isDivide)
1103
1618
  },
1619
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
1620
+ numberTimesDivideBigIntHelp(x, y, isDivide)
1621
+ },
1104
1622
  t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
1105
1623
  numberTimesDivideRationalHelp(x, y, isDivide)
1106
1624
  },
@@ -1117,10 +1635,12 @@ let numberTimesDivideHelp = (x, y, isDivide) => {
1117
1635
  }
1118
1636
  }
1119
1637
 
1638
+ @unsafe
1120
1639
  let numberTimes = (x, y) => {
1121
1640
  WasmI32.toGrain(numberTimesDivideHelp(x, y, false)): Number
1122
1641
  }
1123
1642
 
1643
+ @unsafe
1124
1644
  let numberDivide = (x, y) => {
1125
1645
  WasmI32.toGrain(numberTimesDivideHelp(x, y, true)): Number
1126
1646
  }
@@ -1130,9 +1650,14 @@ let numberDivide = (x, y) => {
1130
1650
  * (same schema as equal())
1131
1651
  */
1132
1652
 
1653
+ @unsafe
1133
1654
  let i64abs = x => if (WasmI64.geS(x, 0N)) x else WasmI64.sub(0N, x)
1134
1655
 
1656
+ @unsafe
1135
1657
  let numberMod = (x, y) => {
1658
+ // incRef x and y to reuse them via WasmI32.toGrain
1659
+ Memory.incRef(x)
1660
+ Memory.incRef(y)
1136
1661
  let xval = coerceNumberToWasmI64(WasmI32.toGrain(x): Number)
1137
1662
  let yval = coerceNumberToWasmI64(WasmI32.toGrain(y): Number)
1138
1663
  if (WasmI64.eqz(yval)) {
@@ -1160,147 +1685,264 @@ let numberMod = (x, y) => {
1160
1685
  * Coerce to float64 and then do comparisons
1161
1686
  * TODO: (#305) Could probably be made more efficient
1162
1687
  */
1688
+
1689
+ @unsafe
1690
+ let cmpBigInt = (x: WasmI32, y: WasmI32) => {
1691
+ if (isSimpleNumber(y)) {
1692
+ BI.cmpI64(x, WasmI64.extendI32S(untagSimple(y)))
1693
+ } else {
1694
+ let yBoxedNumberTag = boxedNumberTag(y)
1695
+ match (yBoxedNumberTag) {
1696
+ t when WasmI32.eq(t, Tags._GRAIN_INT32_BOXED_NUM_TAG) => {
1697
+ BI.cmpI64(x, WasmI64.extendI32S(boxedInt32Number(y)))
1698
+ },
1699
+ t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => {
1700
+ BI.cmpI64(x, boxedInt64Number(y))
1701
+ },
1702
+ t when WasmI32.eq(t, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) => {
1703
+ BI.cmp(x, y)
1704
+ },
1705
+ t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => {
1706
+ let tmp = BI.mul(x, boxedRationalDenominator(y))
1707
+ let ret = BI.cmp(tmp, boxedRationalNumerator(y))
1708
+ Memory.decRef(tmp)
1709
+ ret
1710
+ },
1711
+ t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
1712
+ BI.cmpF32(x, boxedFloat32Number(y))
1713
+ },
1714
+ t when WasmI32.eq(t, Tags._GRAIN_FLOAT64_BOXED_NUM_TAG) => {
1715
+ BI.cmpF64(x, boxedFloat64Number(y))
1716
+ },
1717
+ _ => {
1718
+ throw UnknownNumberTag
1719
+ },
1720
+ }
1721
+ }
1722
+ }
1723
+
1163
1724
  // TODO: (#305) is this safe? I think it's safe?
1164
- export let rec (<) = (x: Number, y: Number) => {
1165
- let xval = coerceNumberToWasmF64(x)
1166
- let yval = coerceNumberToWasmF64(y)
1167
- Memory.decRef(WasmI32.fromGrain(x))
1168
- Memory.decRef(WasmI32.fromGrain(y))
1169
- let ret = WasmF64.lt(xval, yval)
1170
- Memory.decRef(WasmI32.fromGrain((<)))
1171
- ret
1725
+ @unsafe
1726
+ export let (<) = (x: Number, y: Number) => {
1727
+ let xw32 = WasmI32.fromGrain(x)
1728
+ let yw32 = WasmI32.fromGrain(y)
1729
+ if (isBigInt(xw32)) {
1730
+ WasmI32.ltS(cmpBigInt(xw32, yw32), 0n)
1731
+ } else if (isBigInt(yw32)) {
1732
+ WasmI32.geS(cmpBigInt(yw32, xw32), 0n)
1733
+ } else {
1734
+ let xval = coerceNumberToWasmF64(x)
1735
+ let yval = coerceNumberToWasmF64(y)
1736
+ WasmF64.lt(xval, yval)
1737
+ }
1172
1738
  }
1173
1739
 
1174
- export let rec (>) = (x: Number, y: Number) => {
1175
- let xval = coerceNumberToWasmF64(x)
1176
- let yval = coerceNumberToWasmF64(y)
1177
- Memory.decRef(WasmI32.fromGrain(x))
1178
- Memory.decRef(WasmI32.fromGrain(y))
1179
- let ret = WasmF64.gt(xval, yval)
1180
- Memory.decRef(WasmI32.fromGrain((>)))
1181
- ret
1740
+ @unsafe
1741
+ export let (>) = (x: Number, y: Number) => {
1742
+ let xw32 = WasmI32.fromGrain(x)
1743
+ let yw32 = WasmI32.fromGrain(y)
1744
+ if (isBigInt(xw32)) {
1745
+ WasmI32.gtS(cmpBigInt(xw32, yw32), 0n)
1746
+ } else if (isBigInt(yw32)) {
1747
+ WasmI32.leS(cmpBigInt(yw32, xw32), 0n)
1748
+ } else {
1749
+ let xval = coerceNumberToWasmF64(x)
1750
+ let yval = coerceNumberToWasmF64(y)
1751
+ WasmF64.gt(xval, yval)
1752
+ }
1182
1753
  }
1183
1754
 
1184
- export let rec (<=) = (x: Number, y: Number) => {
1185
- // Equality is finicky, so delegate
1186
- let xval = coerceNumberToWasmF64(x)
1187
- let yval = coerceNumberToWasmF64(y)
1188
- let ret = if (WasmF64.lt(xval, yval)) {
1189
- true
1755
+ @unsafe
1756
+ export let (<=) = (x: Number, y: Number) => {
1757
+ let xw32 = WasmI32.fromGrain(x)
1758
+ let yw32 = WasmI32.fromGrain(y)
1759
+ if (isBigInt(xw32)) {
1760
+ WasmI32.leS(cmpBigInt(xw32, yw32), 0n)
1761
+ } else if (isBigInt(yw32)) {
1762
+ WasmI32.geS(cmpBigInt(yw32, xw32), 0n)
1190
1763
  } else {
1191
- let x = WasmI32.fromGrain(x)
1192
- let y = WasmI32.fromGrain(y)
1193
- numberEqual(x, y)
1764
+ // Equality is finicky, so delegate
1765
+ let xval = coerceNumberToWasmF64(x)
1766
+ let yval = coerceNumberToWasmF64(y)
1767
+ if (WasmF64.lt(xval, yval)) {
1768
+ true
1769
+ } else {
1770
+ let x = WasmI32.fromGrain(x)
1771
+ let y = WasmI32.fromGrain(y)
1772
+ numberEqual(x, y)
1773
+ }
1194
1774
  }
1195
- Memory.decRef(WasmI32.fromGrain(x))
1196
- Memory.decRef(WasmI32.fromGrain(y))
1197
- Memory.decRef(WasmI32.fromGrain((<=)))
1198
- ret
1199
1775
  }
1200
1776
 
1201
- export let rec (>=) = (x: Number, y: Number) => {
1202
- // Equality is finicky, so delegate
1203
- let xval = coerceNumberToWasmF64(x)
1204
- let yval = coerceNumberToWasmF64(y)
1205
- let ret = if (WasmF64.gt(xval, yval)) {
1206
- true
1777
+ @unsafe
1778
+ export let (>=) = (x: Number, y: Number) => {
1779
+ let xw32 = WasmI32.fromGrain(x)
1780
+ let yw32 = WasmI32.fromGrain(y)
1781
+ if (isBigInt(xw32)) {
1782
+ WasmI32.leS(cmpBigInt(xw32, yw32), 0n)
1783
+ } else if (isBigInt(yw32)) {
1784
+ WasmI32.geS(cmpBigInt(yw32, xw32), 0n)
1207
1785
  } else {
1208
- let x = WasmI32.fromGrain(x)
1209
- let y = WasmI32.fromGrain(y)
1210
- numberEqual(x, y)
1786
+ // Equality is finicky, so delegate
1787
+ let xval = coerceNumberToWasmF64(x)
1788
+ let yval = coerceNumberToWasmF64(y)
1789
+ if (WasmF64.gt(xval, yval)) {
1790
+ true
1791
+ } else {
1792
+ let x = WasmI32.fromGrain(x)
1793
+ let y = WasmI32.fromGrain(y)
1794
+ numberEqual(x, y)
1795
+ }
1211
1796
  }
1212
- Memory.decRef(WasmI32.fromGrain(x))
1213
- Memory.decRef(WasmI32.fromGrain(y))
1214
- Memory.decRef(WasmI32.fromGrain((>=)))
1215
- ret
1216
1797
  }
1217
1798
 
1218
1799
  /*
1219
1800
  * ===== EQUAL =====
1220
1801
  */
1221
1802
 
1222
- export let rec numberEq = (x: Number, y: Number) => {
1803
+ @unsafe
1804
+ export let numberEq = (x: Number, y: Number) => {
1223
1805
  let x = WasmI32.fromGrain(x)
1224
1806
  let y = WasmI32.fromGrain(y)
1225
- let ret = numberEqual(x, y)
1226
- Memory.decRef(x)
1227
- Memory.decRef(y)
1228
- Memory.decRef(WasmI32.fromGrain(numberEq))
1229
- ret
1807
+ numberEqual(x, y)
1230
1808
  }
1231
1809
 
1232
1810
  /*
1233
1811
  * ===== LOGICAL OPERATIONS =====
1234
- * Only valid for int-like numbers. Coerce to i64 and do operations
1812
+ * Only valid for int-like numbers. Coerce to i64/bigInt and do operations
1235
1813
  */
1236
1814
  // TODO: (#306) Semantics around when things should stay i32/i64
1237
1815
 
1238
- export let rec lnot = (x: Number) => {
1239
- let xval = coerceNumberToWasmI64(x)
1240
- let ret = WasmI32.toGrain(reducedInteger(i64not(xval))): Number
1241
- Memory.decRef(WasmI32.fromGrain(x))
1242
- Memory.decRef(WasmI32.fromGrain(lnot))
1243
- ret
1816
+ @unsafe
1817
+ export let lnot = (x: Number) => {
1818
+ let xw32 = WasmI32.fromGrain(x)
1819
+ if (isBigInt(xw32)) {
1820
+ WasmI32.toGrain(reducedBigInteger(BI.bitwiseNot(xw32))): Number
1821
+ } else {
1822
+ let xval = coerceNumberToWasmI64(x)
1823
+ WasmI32.toGrain(reducedInteger(i64not(xval))): Number
1824
+ }
1244
1825
  }
1245
1826
 
1246
- export let rec (<<) = (x: Number, y: Number) => {
1247
- let xval = coerceNumberToWasmI64(x)
1248
- let yval = coerceNumberToWasmI64(y)
1249
- let ret = WasmI32.toGrain(reducedInteger(WasmI64.shl(xval, yval))): Number
1250
- Memory.decRef(WasmI32.fromGrain(x))
1251
- Memory.decRef(WasmI32.fromGrain(y))
1252
- Memory.decRef(WasmI32.fromGrain((<<)))
1253
- ret
1827
+ @unsafe
1828
+ export let (<<) = (x: Number, y: Number) => {
1829
+ let xw32 = WasmI32.fromGrain(x)
1830
+ if (isBigInt(xw32)) {
1831
+ let yval = coerceNumberToWasmI32(y)
1832
+ WasmI32.toGrain(reducedBigInteger(BI.shl(xw32, yval))): Number
1833
+ } else {
1834
+ let xval = coerceNumberToWasmI64(x)
1835
+ let yval = coerceNumberToWasmI64(y)
1836
+ WasmI32.toGrain(reducedInteger(WasmI64.shl(xval, yval))): Number
1837
+ }
1254
1838
  }
1255
1839
 
1256
- export let rec (>>>) = (x: Number, y: Number) => {
1257
- let xval = coerceNumberToWasmI64(x)
1258
- let yval = coerceNumberToWasmI64(y)
1259
- let ret = WasmI32.toGrain(reducedInteger(WasmI64.shrU(xval, yval))): Number
1260
- Memory.decRef(WasmI32.fromGrain(x))
1261
- Memory.decRef(WasmI32.fromGrain(y))
1262
- Memory.decRef(WasmI32.fromGrain((>>>)))
1263
- ret
1840
+ @unsafe
1841
+ export let (>>>) = (x: Number, y: Number) => {
1842
+ let xw32 = WasmI32.fromGrain(x)
1843
+ if (isBigInt(xw32)) {
1844
+ let yval = coerceNumberToWasmI32(y)
1845
+ // [NOTE]: For BigInts, shrU is the same as shrS because there
1846
+ // are an *infinite* number of leading ones
1847
+ WasmI32.toGrain(reducedBigInteger(BI.shrS(xw32, yval))): Number
1848
+ } else {
1849
+ let xval = coerceNumberToWasmI64(x)
1850
+ let yval = coerceNumberToWasmI64(y)
1851
+ WasmI32.toGrain(reducedInteger(WasmI64.shrU(xval, yval))): Number
1852
+ }
1264
1853
  }
1265
1854
 
1266
- export let rec (&) = (x: Number, y: Number) => {
1267
- let xval = coerceNumberToWasmI64(x)
1268
- let yval = coerceNumberToWasmI64(y)
1269
- let ret = WasmI32.toGrain(reducedInteger(WasmI64.and(xval, yval))): Number
1270
- Memory.decRef(WasmI32.fromGrain(x))
1271
- Memory.decRef(WasmI32.fromGrain(y))
1272
- Memory.decRef(WasmI32.fromGrain((&)))
1273
- ret
1855
+ @unsafe
1856
+ export let (&) = (x: Number, y: Number) => {
1857
+ let xw32 = WasmI32.fromGrain(x)
1858
+ let yw32 = WasmI32.fromGrain(y)
1859
+ if (isBigInt(xw32) || isBigInt(yw32)) {
1860
+ let xval = coerceNumberToBigInt(x)
1861
+ let yval = coerceNumberToBigInt(y)
1862
+ let ret = WasmI32.toGrain(
1863
+ reducedBigInteger(BI.bitwiseAnd(xval, yval))
1864
+ ): Number
1865
+ if (!WasmI32.eq(xw32, xval)) {
1866
+ Memory.decRef(xval)
1867
+ void
1868
+ }
1869
+ if (!WasmI32.eq(yw32, yval)) {
1870
+ Memory.decRef(yval)
1871
+ void
1872
+ }
1873
+ ret
1874
+ } else {
1875
+ let xval = coerceNumberToWasmI64(x)
1876
+ let yval = coerceNumberToWasmI64(y)
1877
+ WasmI32.toGrain(reducedInteger(WasmI64.and(xval, yval))): Number
1878
+ }
1274
1879
  }
1275
1880
 
1276
- export let rec (|) = (x: Number, y: Number) => {
1277
- let xval = coerceNumberToWasmI64(x)
1278
- let yval = coerceNumberToWasmI64(y)
1279
- let ret = WasmI32.toGrain(reducedInteger(WasmI64.or(xval, yval))): Number
1280
- Memory.decRef(WasmI32.fromGrain(x))
1281
- Memory.decRef(WasmI32.fromGrain(y))
1282
- Memory.decRef(WasmI32.fromGrain((|)))
1283
- ret
1881
+ @unsafe
1882
+ export let (|) = (x: Number, y: Number) => {
1883
+ let xw32 = WasmI32.fromGrain(x)
1884
+ let yw32 = WasmI32.fromGrain(y)
1885
+ if (isBigInt(xw32) || isBigInt(yw32)) {
1886
+ let xval = coerceNumberToBigInt(x)
1887
+ let yval = coerceNumberToBigInt(y)
1888
+ let ret = WasmI32.toGrain(
1889
+ reducedBigInteger(BI.bitwiseOr(xval, yval))
1890
+ ): Number
1891
+ if (!WasmI32.eq(xw32, xval)) {
1892
+ Memory.decRef(xval)
1893
+ void
1894
+ }
1895
+ if (!WasmI32.eq(yw32, yval)) {
1896
+ Memory.decRef(yval)
1897
+ void
1898
+ }
1899
+ ret
1900
+ } else {
1901
+ let xval = coerceNumberToWasmI64(x)
1902
+ let yval = coerceNumberToWasmI64(y)
1903
+ WasmI32.toGrain(reducedInteger(WasmI64.or(xval, yval))): Number
1904
+ }
1284
1905
  }
1285
1906
 
1286
- export let rec (^) = (x: Number, y: Number) => {
1287
- let xval = coerceNumberToWasmI64(x)
1288
- let yval = coerceNumberToWasmI64(y)
1289
- let ret = WasmI32.toGrain(reducedInteger(WasmI64.xor(xval, yval))): Number
1290
- Memory.decRef(WasmI32.fromGrain(x))
1291
- Memory.decRef(WasmI32.fromGrain(y))
1292
- Memory.decRef(WasmI32.fromGrain((^)))
1293
- ret
1907
+ @unsafe
1908
+ export let (^) = (x: Number, y: Number) => {
1909
+ let xw32 = WasmI32.fromGrain(x)
1910
+ let yw32 = WasmI32.fromGrain(y)
1911
+ if (isBigInt(xw32) || isBigInt(yw32)) {
1912
+ let xval = coerceNumberToBigInt(x)
1913
+ let yval = coerceNumberToBigInt(y)
1914
+ let ret = WasmI32.toGrain(
1915
+ reducedBigInteger(BI.bitwiseXor(xval, yval))
1916
+ ): Number
1917
+ if (!WasmI32.eq(xw32, xval)) {
1918
+ Memory.decRef(xval)
1919
+ void
1920
+ }
1921
+ if (!WasmI32.eq(yw32, yval)) {
1922
+ Memory.decRef(yval)
1923
+ void
1924
+ }
1925
+ ret
1926
+ } else {
1927
+ let xval = coerceNumberToWasmI64(x)
1928
+ let yval = coerceNumberToWasmI64(y)
1929
+ WasmI32.toGrain(reducedInteger(WasmI64.xor(xval, yval))): Number
1930
+ }
1294
1931
  }
1295
1932
 
1296
- export let rec (>>) = (x: Number, y: Number) => {
1297
- let xval = coerceNumberToWasmI64(x)
1298
- let yval = coerceNumberToWasmI64(y)
1299
- let ret = WasmI32.toGrain(reducedInteger(WasmI64.shrS(xval, yval))): Number
1300
- Memory.decRef(WasmI32.fromGrain(x))
1301
- Memory.decRef(WasmI32.fromGrain(y))
1302
- Memory.decRef(WasmI32.fromGrain((>>)))
1303
- ret
1933
+ @unsafe
1934
+ export let (>>) = (x: Number, y: Number) => {
1935
+ let xw32 = WasmI32.fromGrain(x)
1936
+ if (isBigInt(xw32)) {
1937
+ let yval = coerceNumberToWasmI32(y)
1938
+ // [NOTE]: For BigInts, shrU is the same as shrS because there
1939
+ // are an *infinite* number of leading ones
1940
+ WasmI32.toGrain(reducedBigInteger(BI.shrS(xw32, yval))): Number
1941
+ } else {
1942
+ let xval = coerceNumberToWasmI64(x)
1943
+ let yval = coerceNumberToWasmI64(y)
1944
+ WasmI32.toGrain(reducedInteger(WasmI64.shrS(xval, yval))): Number
1945
+ }
1304
1946
  }
1305
1947
 
1306
1948
  /// USER-EXPOSED COERCION FUNCTIONS
@@ -1308,151 +1950,186 @@ export let rec (>>) = (x: Number, y: Number) => {
1308
1950
  // [NOTE]: Coercion is a *conservative* process! For example, even if a float is 1.0,
1309
1951
  // we will fail if attempting to coerce to an int!
1310
1952
 
1953
+ @unsafe
1311
1954
  export let rec coerceNumberToInt32 = (x: Number) => {
1312
1955
  let x = WasmI32.fromGrain(x)
1313
1956
  let result = if (
1314
1957
  !isSimpleNumber(x) &&
1315
1958
  WasmI32.eq(boxedNumberTag(x), Tags._GRAIN_INT32_BOXED_NUM_TAG)
1316
1959
  ) {
1317
- // avoid extra malloc
1960
+ // avoid extra malloc and prevent x from being freed
1961
+ Memory.incRef(x)
1318
1962
  x
1319
1963
  } else {
1964
+ // incRef x to reuse it via WasmI32.toGrain
1965
+ Memory.incRef(x)
1320
1966
  // can possibly fail
1321
1967
  newInt32(coerceNumberToWasmI32(WasmI32.toGrain(x): Number))
1322
1968
  }
1323
- let ret = WasmI32.toGrain(result): Int32
1324
- if (WasmI32.fromGrain(ret) != WasmI32.fromGrain(x)) {
1325
- Memory.decRef(WasmI32.fromGrain(x))
1326
- void
1327
- }
1328
- Memory.decRef(WasmI32.fromGrain(coerceNumberToInt32))
1329
- ret
1969
+ WasmI32.toGrain(result): Int32
1330
1970
  }
1331
1971
 
1972
+ @unsafe
1332
1973
  export let rec coerceNumberToInt64 = (x: Number) => {
1333
1974
  let x = WasmI32.fromGrain(x)
1334
1975
  let result = if (
1335
1976
  !isSimpleNumber(x) &&
1336
1977
  WasmI32.eq(boxedNumberTag(x), Tags._GRAIN_INT64_BOXED_NUM_TAG)
1337
1978
  ) {
1338
- // avoid extra malloc
1979
+ // avoid extra malloc and prevent x from being freed
1980
+ Memory.incRef(x)
1339
1981
  x
1340
1982
  } else {
1983
+ // incRef x to reuse it via WasmI32.toGrain
1984
+ Memory.incRef(x)
1341
1985
  newInt64(coerceNumberToWasmI64(WasmI32.toGrain(x): Number))
1342
1986
  }
1343
- let ret = WasmI32.toGrain(result): Int64
1344
- if (WasmI32.fromGrain(ret) != WasmI32.fromGrain(x)) {
1345
- Memory.decRef(WasmI32.fromGrain(x))
1346
- void
1987
+ WasmI32.toGrain(result): Int64
1988
+ }
1989
+
1990
+ @unsafe
1991
+ export let rec coerceNumberToBigInt = (x: Number) => {
1992
+ let x = WasmI32.fromGrain(x)
1993
+ let result = if (isBigInt(x)) {
1994
+ // avoid extra malloc and prevent x from being freed
1995
+ Memory.incRef(x)
1996
+ x
1997
+ } else {
1998
+ // incRef x to reuse it via WasmI32.toGrain
1999
+ Memory.incRef(x)
2000
+ BI.makeWrappedInt64(coerceNumberToWasmI64(WasmI32.toGrain(x): Number))
1347
2001
  }
1348
- Memory.decRef(WasmI32.fromGrain(coerceNumberToInt64))
1349
- ret
2002
+ WasmI32.toGrain(result): BigInt
1350
2003
  }
1351
2004
 
1352
- export let rec coerceNumberToRational = (x: Number) => {
2005
+ @unsafe
2006
+ export let coerceNumberToRational = (x: Number) => {
1353
2007
  let x = WasmI32.fromGrain(x)
1354
2008
  let result = if (isSimpleNumber(x)) {
1355
- newRational(untagSimple(x), 1n)
2009
+ newRational(BI.makeWrappedInt32(untagSimple(x)), BI.makeWrappedInt32(1n))
1356
2010
  } else {
1357
2011
  let tag = boxedNumberTag(x)
1358
2012
  if (WasmI32.eq(tag, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG)) {
2013
+ // avoid extra malloc and prevent x from being freed
2014
+ Memory.incRef(x)
1359
2015
  x
1360
2016
  } else if (WasmI32.eq(tag, Tags._GRAIN_INT32_BOXED_NUM_TAG)) {
1361
- newRational(boxedInt32Number(x), 1n)
2017
+ newRational(
2018
+ BI.makeWrappedInt32(boxedInt32Number(x)),
2019
+ BI.makeWrappedInt32(1n)
2020
+ )
1362
2021
  } else if (WasmI32.eq(tag, Tags._GRAIN_INT64_BOXED_NUM_TAG)) {
1363
- newRational(coerceNumberToWasmI32(WasmI32.toGrain(x): Number), 1n)
2022
+ // incRef x to reuse it via WasmI32.toGrain
2023
+ Memory.incRef(x)
2024
+ newRational(
2025
+ BI.makeWrappedInt32(coerceNumberToWasmI32(WasmI32.toGrain(x): Number)),
2026
+ BI.makeWrappedInt32(1n)
2027
+ )
1364
2028
  } else {
1365
2029
  throw Exception.NumberNotRational
1366
2030
  }
1367
2031
  }
1368
- let ret = WasmI32.toGrain(result): Rational
1369
- if (WasmI32.fromGrain(ret) != WasmI32.fromGrain(x)) {
1370
- Memory.decRef(WasmI32.fromGrain(x))
1371
- void
1372
- }
1373
- Memory.decRef(WasmI32.fromGrain(coerceNumberToRational))
1374
- ret
2032
+ WasmI32.toGrain(result): Rational
1375
2033
  }
1376
2034
 
1377
- export let rec coerceNumberToFloat32 = (x: Number) => {
2035
+ @unsafe
2036
+ export let coerceNumberToFloat32 = (x: Number) => {
1378
2037
  let x = WasmI32.fromGrain(x)
1379
2038
  let result = if (
1380
2039
  !isSimpleNumber(x) &&
1381
2040
  WasmI32.eq(boxedNumberTag(x), Tags._GRAIN_FLOAT32_BOXED_NUM_TAG)
1382
2041
  ) {
1383
- // avoid extra malloc
2042
+ // avoid extra malloc and prevent x from being freed
2043
+ Memory.incRef(x)
1384
2044
  x
1385
2045
  } else {
2046
+ // incRef x to reuse it via WasmI32.toGrain
2047
+ Memory.incRef(x)
1386
2048
  newFloat32(coerceNumberToWasmF32(WasmI32.toGrain(x): Number))
1387
2049
  }
1388
- let ret = WasmI32.toGrain(result): Float32
1389
- if (WasmI32.fromGrain(ret) != WasmI32.fromGrain(x)) {
1390
- Memory.decRef(WasmI32.fromGrain(x))
1391
- void
1392
- }
1393
- Memory.decRef(WasmI32.fromGrain(coerceNumberToFloat32))
1394
- ret
2050
+ WasmI32.toGrain(result): Float32
1395
2051
  }
1396
2052
 
1397
- export let rec coerceNumberToFloat64 = (x: Number) => {
2053
+ @unsafe
2054
+ export let coerceNumberToFloat64 = (x: Number) => {
1398
2055
  let x = WasmI32.fromGrain(x)
1399
2056
  let result = if (
1400
2057
  !isSimpleNumber(x) &&
1401
2058
  WasmI32.eq(boxedNumberTag(x), Tags._GRAIN_FLOAT64_BOXED_NUM_TAG)
1402
2059
  ) {
1403
- // avoid extra malloc
2060
+ // avoid extra malloc and prevent x from being freed
2061
+ Memory.incRef(x)
1404
2062
  x
1405
2063
  } else {
2064
+ // incRef x to reuse it via WasmI32.toGrain
2065
+ Memory.incRef(x)
1406
2066
  newFloat64(coerceNumberToWasmF64(WasmI32.toGrain(x): Number))
1407
2067
  }
1408
- let ret = WasmI32.toGrain(result): Float64
1409
- if (WasmI32.fromGrain(ret) != WasmI32.fromGrain(x)) {
1410
- Memory.decRef(WasmI32.fromGrain(x))
1411
- void
1412
- }
1413
- Memory.decRef(WasmI32.fromGrain(coerceNumberToFloat64))
1414
- ret
2068
+ WasmI32.toGrain(result): Float64
1415
2069
  }
1416
2070
 
1417
- // These coerceXX functions don't need Memory.decRef calls since they simply return their argument
1418
- export let rec coerceInt32ToNumber = (x: Int32) => {
1419
- let ret = WasmI32.toGrain(WasmI32.fromGrain(x)): Number
1420
- Memory.decRef(WasmI32.fromGrain(coerceInt32ToNumber))
1421
- ret
2071
+ @unsafe
2072
+ export let coerceInt32ToNumber = (x: Int32) => {
2073
+ WasmI32.toGrain(
2074
+ reducedInteger(WasmI64.extendI32S(boxedInt32Number(WasmI32.fromGrain(x))))
2075
+ ): Number
1422
2076
  }
1423
2077
 
1424
- export let rec coerceInt64ToNumber = (x: Int64) => {
1425
- let ret = WasmI32.toGrain(WasmI32.fromGrain(x)): Number
1426
- Memory.decRef(WasmI32.fromGrain(coerceInt64ToNumber))
1427
- ret
2078
+ @unsafe
2079
+ export let coerceInt64ToNumber = (x: Int64) => {
2080
+ WasmI32.toGrain(
2081
+ reducedInteger(boxedInt64Number(WasmI32.fromGrain(x)))
2082
+ ): Number
1428
2083
  }
1429
2084
 
1430
- export let rec coerceRationalToNumber = (x: Rational) => {
1431
- let ret = WasmI32.toGrain(WasmI32.fromGrain(x)): Number
1432
- Memory.decRef(WasmI32.fromGrain(coerceRationalToNumber))
1433
- ret
2085
+ @unsafe
2086
+ export let coerceBigIntToNumber = (x: BigInt) => {
2087
+ let x = WasmI32.fromGrain(x)
2088
+ // reducedBigInteger assumes that the bigint is dead,
2089
+ // but in our case, it is not
2090
+ Memory.incRef(x)
2091
+ WasmI32.toGrain(reducedBigInteger(x)): Number
2092
+ }
2093
+
2094
+ @unsafe
2095
+ export let coerceRationalToNumber = (x: Rational) => {
2096
+ if (WasmI32.eq(boxedRationalDenominator(WasmI32.fromGrain(x)), 1n)) {
2097
+ WasmI32.toGrain(
2098
+ reducedInteger(
2099
+ WasmI64.extendI32S(boxedRationalNumerator(WasmI32.fromGrain(x)))
2100
+ )
2101
+ ): Number
2102
+ } else {
2103
+ let x = WasmI32.fromGrain(x)
2104
+ // incRef x to reuse it via WasmI32.toGrain
2105
+ Memory.incRef(x)
2106
+ WasmI32.toGrain(x): Number
2107
+ }
1434
2108
  }
1435
2109
 
1436
- export let rec coerceFloat32ToNumber = (x: Float32) => {
1437
- let ret = WasmI32.toGrain(WasmI32.fromGrain(x)): Number
1438
- Memory.decRef(WasmI32.fromGrain(coerceFloat32ToNumber))
1439
- ret
2110
+ @unsafe
2111
+ export let coerceFloat32ToNumber = (x: Float32) => {
2112
+ WasmI32.toGrain(
2113
+ newFloat64(WasmF64.promoteF32(boxedFloat32Number(WasmI32.fromGrain(x))))
2114
+ ): Number
1440
2115
  }
1441
2116
 
1442
- export let rec coerceFloat64ToNumber = (x: Float64) => {
1443
- let ret = WasmI32.toGrain(WasmI32.fromGrain(x)): Number
1444
- Memory.decRef(WasmI32.fromGrain(coerceFloat64ToNumber))
1445
- ret
2117
+ @unsafe
2118
+ export let coerceFloat64ToNumber = (x: Float64) => {
2119
+ let x = WasmI32.fromGrain(x)
2120
+ // incRef x to reuse it via WasmI32.toGrain
2121
+ Memory.incRef(x)
2122
+ WasmI32.toGrain(x): Number
1446
2123
  }
1447
2124
 
1448
2125
  /// USER-EXPOSED CONVERSION FUNCTIONS
1449
2126
 
1450
- export let rec convertExactToInexact = (x: Number) => {
1451
- let ret = x
1452
- Memory.decRef(WasmI32.fromGrain(convertExactToInexact))
1453
- ret
2127
+ @unsafe
2128
+ export let convertExactToInexact = (x: Number) => {
2129
+ x
1454
2130
  }
1455
2131
 
2132
+ @unsafe
1456
2133
  let convertInexactToExactHelp = x => {
1457
2134
  if (isSimpleNumber(x)) {
1458
2135
  x
@@ -1461,20 +2138,21 @@ let convertInexactToExactHelp = x => {
1461
2138
  if (
1462
2139
  WasmI32.eq(tag, Tags._GRAIN_INT32_BOXED_NUM_TAG) ||
1463
2140
  WasmI32.eq(tag, Tags._GRAIN_INT64_BOXED_NUM_TAG) ||
2141
+ WasmI32.eq(tag, Tags._GRAIN_BIGINT_BOXED_NUM_TAG) ||
1464
2142
  WasmI32.eq(tag, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG)
1465
2143
  ) {
2144
+ Memory.incRef(x)
1466
2145
  x
1467
2146
  } else {
1468
2147
  match (tag) {
1469
- t when WasmI32.eq(t, Tags._GRAIN_INT32_BOXED_NUM_TAG) => x,
1470
- t when WasmI32.eq(t, Tags._GRAIN_INT64_BOXED_NUM_TAG) => x,
1471
- t when WasmI32.eq(t, Tags._GRAIN_RATIONAL_BOXED_NUM_TAG) => x,
1472
2148
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG) => {
2149
+ // TODO(#1191): Investigate if BigInt is more accurate
1473
2150
  reducedInteger(
1474
2151
  WasmI64.truncF32S(WasmF32.nearest(boxedFloat32Number(x)))
1475
2152
  )
1476
2153
  },
1477
2154
  t when WasmI32.eq(t, Tags._GRAIN_FLOAT64_BOXED_NUM_TAG) => {
2155
+ // TODO(#1191): Investigate if BigInt is more accurate
1478
2156
  reducedInteger(
1479
2157
  WasmI64.truncF64S(WasmF64.nearest(boxedFloat64Number(x)))
1480
2158
  )
@@ -1487,78 +2165,58 @@ let convertInexactToExactHelp = x => {
1487
2165
  }
1488
2166
  }
1489
2167
 
1490
- export let rec convertInexactToExact = (x: Number) => {
1491
- let ret = WasmI32.toGrain(
1492
- convertInexactToExactHelp(WasmI32.fromGrain(x))
1493
- ): Number
1494
- if (WasmI32.fromGrain(ret) != WasmI32.fromGrain(x)) {
1495
- Memory.decRef(WasmI32.fromGrain(x))
1496
- void
1497
- }
1498
- Memory.decRef(WasmI32.fromGrain(convertInexactToExact))
1499
- ret
2168
+ @unsafe
2169
+ export let convertInexactToExact = (x: Number) => {
2170
+ WasmI32.toGrain(convertInexactToExactHelp(WasmI32.fromGrain(x))): Number
1500
2171
  }
1501
2172
 
1502
- export let rec (+) = (x: Number, y: Number) => {
2173
+ @unsafe
2174
+ export let (+) = (x: Number, y: Number) => {
1503
2175
  let x = WasmI32.fromGrain(x)
1504
2176
  let y = WasmI32.fromGrain(y)
1505
- let ret = numberAdd(x, y)
1506
- Memory.decRef(x)
1507
- Memory.decRef(y)
1508
- Memory.decRef(WasmI32.fromGrain((+)))
1509
- ret
2177
+ numberAdd(x, y)
1510
2178
  }
1511
2179
 
1512
- export let rec (-) = (x: Number, y: Number) => {
2180
+ @unsafe
2181
+ export let (-) = (x: Number, y: Number) => {
1513
2182
  let x = WasmI32.fromGrain(x)
1514
2183
  let y = WasmI32.fromGrain(y)
1515
- let ret = numberSub(x, y)
1516
- Memory.decRef(x)
1517
- Memory.decRef(y)
1518
- Memory.decRef(WasmI32.fromGrain((-)))
1519
- ret
2184
+ numberSub(x, y)
1520
2185
  }
1521
2186
 
1522
- export let rec (*) = (x: Number, y: Number) => {
2187
+ @unsafe
2188
+ export let (*) = (x: Number, y: Number) => {
1523
2189
  let x = WasmI32.fromGrain(x)
1524
2190
  let y = WasmI32.fromGrain(y)
1525
- let ret = numberTimes(x, y)
1526
- Memory.decRef(x)
1527
- Memory.decRef(y)
1528
- Memory.decRef(WasmI32.fromGrain((*)))
1529
- ret
2191
+ numberTimes(x, y)
1530
2192
  }
1531
2193
 
1532
- export let rec (/) = (x: Number, y: Number) => {
2194
+ @unsafe
2195
+ export let (/) = (x: Number, y: Number) => {
1533
2196
  let x = WasmI32.fromGrain(x)
1534
2197
  let y = WasmI32.fromGrain(y)
1535
- let ret = numberDivide(x, y)
1536
- Memory.decRef(x)
1537
- Memory.decRef(y)
1538
- Memory.decRef(WasmI32.fromGrain((/)))
1539
- ret
2198
+ numberDivide(x, y)
1540
2199
  }
1541
2200
 
1542
- export let rec (%) = (x: Number, y: Number) => {
2201
+ @unsafe
2202
+ export let (%) = (x: Number, y: Number) => {
1543
2203
  let x = WasmI32.fromGrain(x)
1544
2204
  let y = WasmI32.fromGrain(y)
1545
- let ret = WasmI32.toGrain(numberMod(x, y)): Number
1546
- Memory.decRef(x)
1547
- Memory.decRef(y)
1548
- Memory.decRef(WasmI32.fromGrain((%)))
1549
- ret
2205
+ WasmI32.toGrain(numberMod(x, y)): Number
1550
2206
  }
1551
2207
 
1552
2208
  // inc/dec
1553
2209
 
1554
2210
  export let incr = x => {
1555
- Memory.incRef(WasmI32.fromGrain((+)))
1556
- // skip incRef on x (to pass through)
1557
2211
  x + 1
1558
2212
  }
1559
2213
 
1560
2214
  export let decr = x => {
1561
- Memory.incRef(WasmI32.fromGrain((-)))
1562
- // skip incRef on x (to pass through)
1563
2215
  x - 1
1564
2216
  }
2217
+
2218
+ @unsafe
2219
+ export let isBigInt = x => {
2220
+ let x = WasmI32.fromGrain(x)
2221
+ isBigInt(x)
2222
+ }