porffor 0.14.0-eca486960 → 0.16.0-053a03e10

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 (43) hide show
  1. package/CONTRIBUTING.md +15 -9
  2. package/README.md +9 -13
  3. package/asur/index.js +1 -1
  4. package/compiler/2c.js +104 -51
  5. package/compiler/assemble.js +18 -3
  6. package/compiler/builtins/annexb_string.ts +1 -0
  7. package/compiler/builtins/array.ts +84 -4
  8. package/compiler/builtins/base64.ts +1 -0
  9. package/compiler/builtins/boolean.ts +2 -0
  10. package/compiler/builtins/console.ts +6 -0
  11. package/compiler/builtins/crypto.ts +1 -0
  12. package/compiler/builtins/date.ts +2 -0
  13. package/compiler/builtins/error.js +22 -0
  14. package/compiler/builtins/escape.ts +1 -2
  15. package/compiler/builtins/function.ts +2 -0
  16. package/compiler/builtins/int.ts +2 -0
  17. package/compiler/builtins/math.ts +410 -0
  18. package/compiler/builtins/number.ts +2 -0
  19. package/compiler/builtins/object.ts +2 -0
  20. package/compiler/builtins/porffor.d.ts +11 -0
  21. package/compiler/builtins/set.ts +15 -1
  22. package/compiler/builtins/string.ts +1 -0
  23. package/compiler/builtins/symbol.ts +8 -7
  24. package/compiler/builtins.js +46 -10
  25. package/compiler/codegen.js +384 -192
  26. package/compiler/cyclone.js +535 -0
  27. package/compiler/decompile.js +6 -0
  28. package/compiler/generated_builtins.js +503 -53
  29. package/compiler/havoc.js +93 -0
  30. package/compiler/index.js +78 -7
  31. package/compiler/opt.js +3 -39
  32. package/compiler/parse.js +2 -2
  33. package/compiler/pgo.js +206 -0
  34. package/compiler/precompile.js +5 -4
  35. package/compiler/prefs.js +7 -2
  36. package/compiler/prototype.js +180 -157
  37. package/compiler/wrap.js +71 -16
  38. package/no_pgo.txt +923 -0
  39. package/package.json +1 -1
  40. package/pgo.txt +916 -0
  41. package/runner/index.js +18 -12
  42. package/runner/repl.js +18 -2
  43. /package/runner/{profiler.js → profile.js} +0 -0
@@ -16,7 +16,7 @@ export const PrototypeFuncs = function() {
16
16
 
17
17
  this[TYPES.array] = {
18
18
  // lX = local accessor of X ({ get, set }), iX = local index of X, wX = wasm ops of X
19
- at: (pointer, length, wIndex, iTmp) => [
19
+ at: (pointer, length, wIndex, wType, iTmp) => [
20
20
  ...wIndex,
21
21
  Opcodes.i32_to,
22
22
  [ Opcodes.local_tee, iTmp ],
@@ -47,31 +47,39 @@ export const PrototypeFuncs = function() {
47
47
  [ Opcodes.end ],
48
48
 
49
49
  [ Opcodes.local_get, iTmp ],
50
- ...number(ValtypeSize[valtype], Valtype.i32),
50
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
51
51
  [ Opcodes.i32_mul ],
52
-
53
52
  ...pointer,
54
53
  [ Opcodes.i32_add ],
54
+ [ Opcodes.local_set, iTmp ],
55
55
 
56
56
  // read from memory
57
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
57
+ [ Opcodes.local_get, iTmp ],
58
+ [ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
59
+
60
+ [ Opcodes.local_get, iTmp ],
61
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ]
58
62
  ],
59
63
 
60
64
  // todo: only for 1 argument
61
- push: (pointer, length, wNewMember, _1, _2, _3, unusedValue) => [
65
+ push: (pointer, length, wNewMember, wType, iTmp, _2, _3, unusedValue) => [
62
66
  // get memory offset of array at last index (length)
63
67
  ...length.getCachedI32(),
64
- ...number(ValtypeSize[valtype], Valtype.i32),
68
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
65
69
  [ Opcodes.i32_mul ],
66
-
67
70
  ...pointer,
68
71
  [ Opcodes.i32_add ],
72
+ [ Opcodes.local_set, iTmp ],
69
73
 
70
- // generated wasm for new member
74
+ // store value
75
+ [ Opcodes.local_get, iTmp ],
71
76
  ...wNewMember,
77
+ [ Opcodes.store, 0, ...unsignedLEB128(ValtypeSize.i32) ],
72
78
 
73
- // store in memory
74
- [ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
79
+ // store type
80
+ [ Opcodes.local_get, iTmp ],
81
+ ...wType,
82
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
75
83
 
76
84
  // bump array length by 1 and return it
77
85
  ...length.setI32([
@@ -93,7 +101,7 @@ export const PrototypeFuncs = function() {
93
101
  // ...length.get()
94
102
  ],
95
103
 
96
- pop: (pointer, length, _1, _2, _3, _4, unusedValue) => [
104
+ pop: (pointer, length, _1, _2, iTmp, _3, _4, unusedValue) => [
97
105
  // if length == 0, noop
98
106
  ...length.getCachedI32(),
99
107
  [ Opcodes.i32_eqz ],
@@ -121,13 +129,18 @@ export const PrototypeFuncs = function() {
121
129
  // load last element
122
130
  ...(unusedValue() ? [] : [
123
131
  ...length.getCachedI32(),
124
- ...number(ValtypeSize[valtype], Valtype.i32),
132
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
125
133
  [ Opcodes.i32_mul ],
126
134
 
127
135
  ...pointer,
128
136
  [ Opcodes.i32_add ],
137
+ [ Opcodes.local_set, iTmp ],
129
138
 
130
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
139
+ [ Opcodes.local_get, iTmp ],
140
+ [ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
141
+
142
+ [ Opcodes.local_get, iTmp ],
143
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ]
131
144
  ])
132
145
  ],
133
146
 
@@ -153,8 +166,12 @@ export const PrototypeFuncs = function() {
153
166
  ]),
154
167
 
155
168
  // load first element
169
+ // todo/perf: unusedValue opt
156
170
  ...pointer,
157
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
171
+ [ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
172
+
173
+ ...pointer,
174
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
158
175
 
159
176
  // offset page by -1 ind
160
177
  // ...number(pointer + ValtypeSize.i32, Valtype.i32), // dst = base array index + length size
@@ -170,13 +187,13 @@ export const PrototypeFuncs = function() {
170
187
  [ Opcodes.i32_add ],
171
188
 
172
189
  // src = base array index + length size + an index
173
- ...number(ValtypeSize.i32 + ValtypeSize[valtype], Valtype.i32),
190
+ ...number(ValtypeSize.i32 + ValtypeSize[valtype] + 1, Valtype.i32),
174
191
  ...pointer,
175
192
  [ Opcodes.i32_add ],
176
193
 
177
194
  // size = new length * sizeof element
178
195
  ...length.getCachedI32(),
179
- ...number(ValtypeSize[valtype], Valtype.i32),
196
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
180
197
  [ Opcodes.i32_mul ],
181
198
  [ ...Opcodes.memory_copy, 0x00, 0x00 ]
182
199
 
@@ -194,7 +211,7 @@ export const PrototypeFuncs = function() {
194
211
  // ]),
195
212
  ],
196
213
 
197
- fill: (pointer, length, wElement, iTmp) => [
214
+ fill: (pointer, length, wElement, wType, iTmp, iTmp2) => [
198
215
  ...wElement,
199
216
  [ Opcodes.local_set, iTmp ],
200
217
 
@@ -206,7 +223,7 @@ export const PrototypeFuncs = function() {
206
223
  [ Opcodes.i32_sub ],
207
224
 
208
225
  // * sizeof value
209
- ...number(ValtypeSize[valtype], Valtype.i32),
226
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
210
227
  [ Opcodes.i32_mul ],
211
228
 
212
229
  ...length.setCachedI32(),
@@ -224,17 +241,25 @@ export const PrototypeFuncs = function() {
224
241
 
225
242
  [ Opcodes.loop, Blocktype.void ],
226
243
 
227
- // set element using pointer
244
+ // calculate offset
228
245
  ...length.getCachedI32(),
229
246
  ...pointer,
230
247
  [ Opcodes.i32_add ],
248
+ [ Opcodes.local_set, iTmp2 ],
231
249
 
250
+ // store value
251
+ [ Opcodes.local_get, iTmp2 ],
232
252
  [ Opcodes.local_get, iTmp ],
233
- [ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128( ValtypeSize.i32) ],
253
+ [ Opcodes.store, 0, ...unsignedLEB128(ValtypeSize.i32) ],
254
+
255
+ // store type
256
+ [ Opcodes.local_get, iTmp2 ],
257
+ ...wType,
258
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
234
259
 
235
260
  // pointer - sizeof value
236
261
  ...length.getCachedI32(),
237
- ...number(ValtypeSize[valtype], Valtype.i32),
262
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
238
263
  [ Opcodes.i32_sub ],
239
264
 
240
265
  ...length.setCachedI32(),
@@ -254,12 +279,16 @@ export const PrototypeFuncs = function() {
254
279
  };
255
280
 
256
281
  this[TYPES.array].at.local = Valtype.i32;
282
+ this[TYPES.array].push.returnType = TYPES.number;
257
283
  this[TYPES.array].push.noArgRetLength = true;
284
+ this[TYPES.array].push.local = Valtype.i32;
285
+ this[TYPES.array].pop.local = Valtype.i32;
258
286
  this[TYPES.array].fill.local = valtypeBinary;
287
+ this[TYPES.array].fill.local2 = Valtype.i32;
259
288
  this[TYPES.array].fill.returnType = TYPES.array;
260
289
 
261
290
  this[TYPES.string] = {
262
- at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
291
+ at: (pointer, length, wIndex, wType, iTmp, _, arrayShell) => {
263
292
  const [ newOut, newPointer ] = arrayShell(1, 'i16');
264
293
 
265
294
  return [
@@ -317,7 +346,7 @@ export const PrototypeFuncs = function() {
317
346
  },
318
347
 
319
348
  // todo: out of bounds properly
320
- charAt: (pointer, length, wIndex, _1, _2, arrayShell) => {
349
+ charAt: (pointer, length, wIndex, wType, _1, _2, arrayShell) => {
321
350
  const [ newOut, newPointer ] = arrayShell(1, 'i16');
322
351
 
323
352
  return [
@@ -347,127 +376,124 @@ export const PrototypeFuncs = function() {
347
376
  ];
348
377
  },
349
378
 
350
- charCodeAt: (pointer, length, wIndex, iTmp) => {
351
- return [
352
- ...wIndex,
353
- Opcodes.i32_to,
354
-
355
- ...(zeroChecks.charcodeat ? [] : [
356
- [ Opcodes.local_set, iTmp ],
379
+ charCodeAt: (pointer, length, wIndex, wType, iTmp) => [
380
+ ...wIndex,
381
+ Opcodes.i32_to,
357
382
 
358
- // index < 0
359
- ...(noUnlikelyChecks ? [] : [
360
- [ Opcodes.local_get, iTmp ],
361
- ...number(0, Valtype.i32),
362
- [ Opcodes.i32_lt_s ],
363
- ]),
383
+ ...(zeroChecks.charcodeat ? [] : [
384
+ [ Opcodes.local_set, iTmp ],
364
385
 
365
- // index >= length
386
+ // index < 0
387
+ ...(noUnlikelyChecks ? [] : [
366
388
  [ Opcodes.local_get, iTmp ],
367
- ...length.getCachedI32(),
368
- [ Opcodes.i32_ge_s ],
389
+ ...number(0, Valtype.i32),
390
+ [ Opcodes.i32_lt_s ],
391
+ ]),
369
392
 
370
- ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
371
- [ Opcodes.if, Blocktype.void ],
372
- ...number(valtype === 'i32' ? -1 : NaN),
373
- [ Opcodes.br, 1 ],
374
- [ Opcodes.end ],
393
+ // index >= length
394
+ [ Opcodes.local_get, iTmp ],
395
+ ...length.getCachedI32(),
396
+ [ Opcodes.i32_ge_s ],
375
397
 
376
- [ Opcodes.local_get, iTmp ],
377
- ]),
398
+ ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
399
+ [ Opcodes.if, Blocktype.void ],
400
+ ...number(valtype === 'i32' ? -1 : NaN),
401
+ [ Opcodes.br, 1 ],
402
+ [ Opcodes.end ],
378
403
 
379
- ...number(ValtypeSize.i16, Valtype.i32),
380
- [ Opcodes.i32_mul ],
404
+ [ Opcodes.local_get, iTmp ],
405
+ ]),
381
406
 
382
- ...pointer,
383
- [ Opcodes.i32_add ],
407
+ ...number(ValtypeSize.i16, Valtype.i32),
408
+ [ Opcodes.i32_mul ],
384
409
 
385
- // load current string ind {arg}
386
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
387
- Opcodes.i32_from_u
388
- ];
389
- },
410
+ ...pointer,
411
+ [ Opcodes.i32_add ],
390
412
 
391
- isWellFormed: (pointer, length, wIndex, iTmp, iTmp2) => {
392
- return [
393
- // note: we cannot presume it begins as 0 in case it was used previously
394
- ...pointer,
395
- [ Opcodes.local_set, iTmp ],
413
+ // load current string ind {arg}
414
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
415
+ Opcodes.i32_from_u
416
+ ],
396
417
 
397
- // use cached length as end pointer
398
- ...length.getCachedI32(),
399
- ...number(ValtypeSize.i16, Valtype.i32),
400
- [ Opcodes.i32_mul ],
401
- ...pointer,
402
- [ Opcodes.i32_add ],
403
- ...length.setCachedI32(),
418
+ isWellFormed: (pointer, length, _1, _2, iTmp, iTmp2) => [
419
+ // note: we cannot presume it begins as 0 in case it was used previously
420
+ ...pointer,
421
+ [ Opcodes.local_set, iTmp ],
422
+
423
+ // use cached length as end pointer
424
+ ...length.getCachedI32(),
425
+ ...number(ValtypeSize.i16, Valtype.i32),
426
+ [ Opcodes.i32_mul ],
427
+ ...pointer,
428
+ [ Opcodes.i32_add ],
429
+ ...length.setCachedI32(),
404
430
 
405
- [ Opcodes.loop, Blocktype.void ],
431
+ [ Opcodes.loop, Blocktype.void ],
406
432
 
407
- [ Opcodes.block, Blocktype.void ],
433
+ [ Opcodes.block, Blocktype.void ],
408
434
 
409
- [ Opcodes.local_get, iTmp ],
410
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
411
- [ Opcodes.local_set, iTmp2 ],
412
-
413
- // if not surrogate, continue
414
- [ Opcodes.local_get, iTmp2 ],
415
- ...number(0xF800, Valtype.i32),
416
- [ Opcodes.i32_and ],
417
- ...number(0xD800, Valtype.i32),
418
- [ Opcodes.i32_ne ],
419
- [ Opcodes.br_if, 0 ],
420
-
421
- // if not leading surrogate, return false
422
- [ Opcodes.local_get, iTmp2 ],
423
- ...number(0xDC00, Valtype.i32),
424
- [ Opcodes.i32_ge_s ],
425
- [ Opcodes.if, Blocktype.void ],
426
- ...number(0),
427
- [ Opcodes.br, 3 ],
428
- [ Opcodes.end ],
435
+ [ Opcodes.local_get, iTmp ],
436
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
437
+ [ Opcodes.local_set, iTmp2 ],
438
+
439
+ // if not surrogate, continue
440
+ [ Opcodes.local_get, iTmp2 ],
441
+ ...number(0xF800, Valtype.i32),
442
+ [ Opcodes.i32_and ],
443
+ ...number(0xD800, Valtype.i32),
444
+ [ Opcodes.i32_ne ],
445
+ [ Opcodes.br_if, 0 ],
429
446
 
430
- // if not followed by trailing surrogate, return false
431
- [ Opcodes.local_get, iTmp ],
432
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize.i16) ],
433
- ...number(0xFC00, Valtype.i32),
434
- [ Opcodes.i32_and ],
435
- ...number(0xDC00, Valtype.i32),
436
- [ Opcodes.i32_ne ],
437
- [ Opcodes.if, Blocktype.void ],
438
- ...number(0),
439
- [ Opcodes.br, 3 ],
440
- [ Opcodes.end ],
447
+ // if not leading surrogate, return false
448
+ [ Opcodes.local_get, iTmp2 ],
449
+ ...number(0xDC00, Valtype.i32),
450
+ [ Opcodes.i32_ge_s ],
451
+ [ Opcodes.if, Blocktype.void ],
452
+ ...number(0),
453
+ [ Opcodes.br, 3 ],
454
+ [ Opcodes.end ],
441
455
 
442
- // bump index again since gone through two valid chars
443
- [ Opcodes.local_get, iTmp ],
444
- ...number(ValtypeSize.i16, Valtype.i32),
445
- [ Opcodes.i32_add ],
446
- [ Opcodes.local_set, iTmp ],
456
+ // if not followed by trailing surrogate, return false
457
+ [ Opcodes.local_get, iTmp ],
458
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize.i16) ],
459
+ ...number(0xFC00, Valtype.i32),
460
+ [ Opcodes.i32_and ],
461
+ ...number(0xDC00, Valtype.i32),
462
+ [ Opcodes.i32_ne ],
463
+ [ Opcodes.if, Blocktype.void ],
464
+ ...number(0),
465
+ [ Opcodes.br, 3 ],
466
+ [ Opcodes.end ],
447
467
 
448
- [ Opcodes.end ],
468
+ // bump index again since gone through two valid chars
469
+ [ Opcodes.local_get, iTmp ],
470
+ ...number(ValtypeSize.i16, Valtype.i32),
471
+ [ Opcodes.i32_add ],
472
+ [ Opcodes.local_set, iTmp ],
449
473
 
450
- // bump pointer and loop if not at the end
451
- [ Opcodes.local_get, iTmp ],
452
- ...number(ValtypeSize.i16, Valtype.i32),
453
- [ Opcodes.i32_add ],
454
- [ Opcodes.local_tee, iTmp ],
474
+ [ Opcodes.end ],
455
475
 
456
- ...length.getCachedI32(), // end pointer
457
- [ Opcodes.i32_ne ],
458
- [ Opcodes.br_if, 0 ],
476
+ // bump pointer and loop if not at the end
477
+ [ Opcodes.local_get, iTmp ],
478
+ ...number(ValtypeSize.i16, Valtype.i32),
479
+ [ Opcodes.i32_add ],
480
+ [ Opcodes.local_tee, iTmp ],
459
481
 
460
- [ Opcodes.end ],
482
+ ...length.getCachedI32(), // end pointer
483
+ [ Opcodes.i32_ne ],
484
+ [ Opcodes.br_if, 0 ],
461
485
 
462
- // return true
463
- ...number(1)
464
- ]
465
- }
486
+ [ Opcodes.end ],
487
+
488
+ // return true
489
+ ...number(1)
490
+ ]
466
491
  };
467
492
 
468
493
  this[TYPES.string].at.local = Valtype.i32;
469
494
  this[TYPES.string].at.returnType = TYPES.string;
470
495
  this[TYPES.string].charAt.returnType = TYPES.string;
496
+ this[TYPES.string].charCodeAt.returnType = TYPES.number;
471
497
  this[TYPES.string].charCodeAt.local = Valtype.i32;
472
498
  this[TYPES.string].charCodeAt.noPointerCache = zeroChecks.charcodeat;
473
499
 
@@ -477,7 +503,7 @@ export const PrototypeFuncs = function() {
477
503
 
478
504
  if (Prefs.bytestring) {
479
505
  this[TYPES.bytestring] = {
480
- at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
506
+ at: (pointer, length, wIndex, wType, iTmp, _, arrayShell) => {
481
507
  const [ newOut, newPointer ] = arrayShell(1, 'i8');
482
508
 
483
509
  return [
@@ -533,7 +559,7 @@ export const PrototypeFuncs = function() {
533
559
  },
534
560
 
535
561
  // todo: out of bounds properly
536
- charAt: (pointer, length, wIndex, _1, _2, arrayShell) => {
562
+ charAt: (pointer, length, wIndex, wType, _1, _2, arrayShell) => {
537
563
  const [ newOut, newPointer ] = arrayShell(1, 'i8');
538
564
 
539
565
  return [
@@ -560,55 +586,52 @@ export const PrototypeFuncs = function() {
560
586
  ];
561
587
  },
562
588
 
563
- charCodeAt: (pointer, length, wIndex, iTmp) => {
564
- return [
565
- ...wIndex,
566
- Opcodes.i32_to,
567
-
568
- ...(zeroChecks.charcodeat ? [] : [
569
- [ Opcodes.local_set, iTmp ],
589
+ charCodeAt: (pointer, length, wIndex, wType, iTmp) => [
590
+ ...wIndex,
591
+ Opcodes.i32_to,
570
592
 
571
- // index < 0
572
- ...(noUnlikelyChecks ? [] : [
573
- [ Opcodes.local_get, iTmp ],
574
- ...number(0, Valtype.i32),
575
- [ Opcodes.i32_lt_s ],
576
- ]),
593
+ ...(zeroChecks.charcodeat ? [] : [
594
+ [ Opcodes.local_set, iTmp ],
577
595
 
578
- // index >= length
596
+ // index < 0
597
+ ...(noUnlikelyChecks ? [] : [
579
598
  [ Opcodes.local_get, iTmp ],
580
- ...length.getCachedI32(),
581
- [ Opcodes.i32_ge_s ],
599
+ ...number(0, Valtype.i32),
600
+ [ Opcodes.i32_lt_s ],
601
+ ]),
582
602
 
583
- ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
584
- [ Opcodes.if, Blocktype.void ],
585
- ...number(valtype === 'i32' ? -1 : NaN),
586
- [ Opcodes.br, 1 ],
587
- [ Opcodes.end ],
603
+ // index >= length
604
+ [ Opcodes.local_get, iTmp ],
605
+ ...length.getCachedI32(),
606
+ [ Opcodes.i32_ge_s ],
588
607
 
589
- [ Opcodes.local_get, iTmp ],
590
- ]),
608
+ ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
609
+ [ Opcodes.if, Blocktype.void ],
610
+ ...number(valtype === 'i32' ? -1 : NaN),
611
+ [ Opcodes.br, 1 ],
612
+ [ Opcodes.end ],
591
613
 
592
- ...pointer,
593
- [ Opcodes.i32_add ],
614
+ [ Opcodes.local_get, iTmp ],
615
+ ]),
594
616
 
595
- // load current string ind {arg}
596
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
597
- Opcodes.i32_from_u
598
- ];
599
- },
617
+ ...pointer,
618
+ [ Opcodes.i32_add ],
600
619
 
601
- isWellFormed: () => {
602
- return [
603
- // we know it must be true as it is a bytestring lol
604
- ...number(1)
605
- ]
606
- }
620
+ // load current string ind {arg}
621
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
622
+ Opcodes.i32_from_u
623
+ ],
624
+
625
+ isWellFormed: () => [
626
+ // we know it must be true as it is a bytestring lol
627
+ ...number(1)
628
+ ]
607
629
  };
608
630
 
609
631
  this[TYPES.bytestring].at.local = Valtype.i32;
610
632
  this[TYPES.bytestring].at.returnType = TYPES.bytestring;
611
633
  this[TYPES.bytestring].charAt.returnType = TYPES.bytestring;
634
+ this[TYPES.bytestring].charCodeAt.returnType = TYPES.number;
612
635
  this[TYPES.bytestring].charCodeAt.local = Valtype.i32;
613
636
  this[TYPES.bytestring].charCodeAt.noPointerCache = zeroChecks.charcodeat;
614
637
 
package/compiler/wrap.js CHANGED
@@ -6,8 +6,25 @@ import { TYPES } from './types.js';
6
6
  import { log } from './log.js';
7
7
  import Prefs from './prefs.js';
8
8
 
9
+ const fs = (typeof process?.version !== 'undefined' ? (await import('node:fs')) : undefined);
10
+
9
11
  const bold = x => `\u001b[1m${x}\u001b[0m`;
10
12
 
13
+ export const readByteStr = (memory, ptr) => {
14
+ const length = (new Int32Array(memory.buffer, ptr, 1))[0];
15
+ return Array.from(new Uint8Array(memory.buffer, ptr + 4, length)).map(x => String.fromCharCode(x)).join('');
16
+ };
17
+
18
+ export const writeByteStr = (memory, ptr, str) => {
19
+ const length = str.length;
20
+ (new Int32Array(memory.buffer, ptr, 1))[0] = length;
21
+
22
+ const arr = new Uint8Array(memory.buffer, ptr + 4, length);
23
+ for (let i = 0; i < length; i++) {
24
+ arr[i] = str.charCodeAt(i);
25
+ }
26
+ };
27
+
11
28
  const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
12
29
  switch (type) {
13
30
  case TYPES.boolean: return Boolean(value);
@@ -41,9 +58,22 @@ const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
41
58
  case TYPES.array: {
42
59
  const length = (new Int32Array(memory.buffer, value, 1))[0];
43
60
 
44
- // have to slice because of memory alignment (?)
45
- const buf = memory.buffer.slice(value + 4, value + 4 + 8 * length);
46
- return Array.from(new Float64Array(buf, 0, length));
61
+ const out = [];
62
+ for (let i = 0; i < length; i++) {
63
+ const offset = value + 4 + (i * 9);
64
+
65
+ // have to slice because of memory alignment (?)
66
+ const v = (new Float64Array(memory.buffer.slice(offset, offset + 8), 0, 1))[0];
67
+ const t = (new Uint8Array(memory.buffer, offset + 8, 1))[0];
68
+
69
+ // console.log(`reading value at index ${i}...`)
70
+ // console.log(' memory:', Array.from(new Uint8Array(memory.buffer, offset, 9)).map(x => x.toString(16).padStart(2, '0')).join(' '));
71
+ // console.log(' read:', { value: v, type: t }, '\n');
72
+
73
+ out.push(porfToJSValue({ memory, funcs, pages }, v, t));
74
+ }
75
+
76
+ return out;
47
77
  }
48
78
 
49
79
  case TYPES.date: {
@@ -88,28 +118,31 @@ const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
88
118
  }
89
119
  };
90
120
 
91
- export default async (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
121
+ export default (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
92
122
  const times = [];
93
123
 
94
124
  const t1 = performance.now();
95
- const { wasm, funcs, globals, tags, exceptions, pages, c } = compile(source, flags);
125
+ const { wasm, funcs, globals, tags, exceptions, pages, c } = typeof source === 'object' ? source : compile(source, flags);
96
126
 
97
127
  globalThis.porfDebugInfo = { funcs, globals };
98
128
 
99
- if (source.includes('export function')) flags.push('module');
129
+ if (source.includes?.('export ')) flags.push('module');
100
130
 
101
- // (await import('node:fs')).writeFileSync('out.wasm', Buffer.from(wasm));
131
+ fs.writeFileSync('out.wasm', Buffer.from(wasm));
102
132
 
103
133
  times.push(performance.now() - t1);
104
134
  if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
105
135
 
106
136
  const backtrace = (funcInd, blobOffset) => {
107
- if (funcInd == null || blobOffset == null) return false;
137
+ if (funcInd == null || blobOffset == null ||
138
+ Number.isNaN(funcInd) || Number.isNaN(blobOffset)) return false;
108
139
 
109
140
  // convert blob offset -> function wasm offset.
110
141
  // this is not good code and is somewhat duplicated
111
142
  // I just want it to work for debugging, I don't care about perf/yes
112
143
  const func = funcs.find(x => x.index === funcInd);
144
+ if (!func) return false;
145
+
113
146
  const locals = Object.values(func.locals).sort((a, b) => a.idx - b.idx).slice(func.params.length).sort((a, b) => a.idx - b.idx);
114
147
 
115
148
  let localDecl = [], typeCount = 0, lastType;
@@ -184,13 +217,15 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
184
217
 
185
218
  let instance;
186
219
  try {
187
- let wasmEngine = WebAssembly;
188
- if (Prefs.asur) {
189
- log.warning('wrap', 'using our !experimental! asur wasm engine instead of host to run');
190
- wasmEngine = await import('../asur/index.js');
191
- }
192
-
193
- 0, { instance } = await wasmEngine.instantiate(wasm, {
220
+ // let wasmEngine = WebAssembly;
221
+ // if (Prefs.asur) {
222
+ // log.warning('wrap', 'using our !experimental! asur wasm engine instead of host to run');
223
+ // wasmEngine = await import('../asur/index.js');
224
+ // }
225
+
226
+ // 0, { instance } = await wasmEngine.instantiate(wasm, {
227
+ const module = new WebAssembly.Module(wasm);
228
+ instance = new WebAssembly.Instance(module, {
194
229
  '': {
195
230
  p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
196
231
  c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
@@ -198,12 +233,31 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
198
233
  u: () => performance.timeOrigin,
199
234
  y: () => {},
200
235
  z: () => {},
236
+ w: (ind, outPtr) => { // readArgv
237
+ const args = process.argv.slice(2).filter(x => !x.startsWith('-'));
238
+ const str = args[ind];
239
+ if (!str) return -1;
240
+
241
+ writeByteStr(memory, outPtr, str);
242
+ return str.length;
243
+ },
244
+ q: (pathPtr, outPtr) => { // readFile
245
+ try {
246
+ const path = readByteStr(memory, pathPtr);
247
+ const contents = fs.readFileSync(path, 'utf8');
248
+ writeByteStr(memory, outPtr, contents);
249
+ return contents.length;
250
+ } catch {
251
+ return -1;
252
+ }
253
+ },
201
254
  ...customImports
202
255
  }
203
256
  });
204
257
  } catch (e) {
205
258
  // only backtrace for runner, not test262/etc
206
259
  if (!process.argv[1].includes('/runner')) throw e;
260
+ if (!(e instanceof WebAssembly.CompileError)) throw e;
207
261
 
208
262
  const funcInd = parseInt(e.message.match(/function #([0-9]+) /)?.[1]);
209
263
  const blobOffset = parseInt(e.message.split('@')?.[1]);
@@ -216,6 +270,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
216
270
  if (Prefs.profileCompiler) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
217
271
 
218
272
  const exports = {};
273
+ const rawValues = process.argv.includes('-i');
219
274
 
220
275
  const exceptTag = instance.exports['0'], memory = instance.exports['$'];
221
276
  for (const x in instance.exports) {
@@ -236,7 +291,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
236
291
  const ret = exp.apply(this, arguments);
237
292
  if (ret == null) return undefined;
238
293
 
239
- if (Prefs.rawValue) return { value: ret[0], type: ret[1] };
294
+ if (rawValues) return { value: ret[0], type: ret[1], js: porfToJSValue({ memory, funcs, pages }, ret[0], ret[1]) };
240
295
 
241
296
  return porfToJSValue({ memory, funcs, pages }, ret[0], ret[1]);
242
297
  } catch (e) {