porffor 0.2.0-6fd3f05 → 0.2.0-767de65

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 (56) hide show
  1. package/.vscode/launch.json +18 -0
  2. package/LICENSE +20 -20
  3. package/README.md +131 -71
  4. package/asur/README.md +2 -0
  5. package/asur/index.js +1262 -0
  6. package/byg/index.js +237 -0
  7. package/compiler/2c.js +322 -72
  8. package/compiler/{sections.js → assemble.js} +63 -15
  9. package/compiler/builtins/annexb_string.js +72 -0
  10. package/compiler/builtins/annexb_string.ts +19 -0
  11. package/compiler/builtins/array.ts +145 -0
  12. package/compiler/builtins/base64.ts +151 -0
  13. package/compiler/builtins/crypto.ts +120 -0
  14. package/compiler/builtins/escape.ts +141 -0
  15. package/compiler/builtins/int.ts +147 -0
  16. package/compiler/builtins/number.ts +527 -0
  17. package/compiler/builtins/porffor.d.ts +42 -0
  18. package/compiler/builtins/string.ts +1055 -0
  19. package/compiler/builtins/tostring.ts +45 -0
  20. package/compiler/builtins.js +591 -269
  21. package/compiler/{codeGen.js → codegen.js} +1212 -473
  22. package/compiler/decompile.js +3 -3
  23. package/compiler/embedding.js +22 -22
  24. package/compiler/encoding.js +108 -10
  25. package/compiler/generated_builtins.js +695 -0
  26. package/compiler/index.js +36 -34
  27. package/compiler/log.js +6 -3
  28. package/compiler/opt.js +65 -29
  29. package/compiler/parse.js +42 -35
  30. package/compiler/precompile.js +123 -0
  31. package/compiler/prefs.js +26 -0
  32. package/compiler/prototype.js +177 -37
  33. package/compiler/types.js +37 -0
  34. package/compiler/wasmSpec.js +30 -7
  35. package/compiler/wrap.js +137 -42
  36. package/empty.js +0 -0
  37. package/hello.js +2 -0
  38. package/package.json +9 -5
  39. package/porf +4 -0
  40. package/rhemyn/compile.js +5 -3
  41. package/rhemyn/parse.js +323 -320
  42. package/rhemyn/test/parse.js +58 -58
  43. package/runner/compare.js +34 -34
  44. package/runner/debug.js +122 -0
  45. package/runner/index.js +49 -10
  46. package/runner/profiler.js +102 -0
  47. package/runner/repl.js +42 -9
  48. package/runner/sizes.js +37 -37
  49. package/test262_changes_from_1afe9b87d2_to_04-09.md +270 -0
  50. package/compiler/builtins/base64.js +0 -92
  51. package/runner/info.js +0 -89
  52. package/runner/profile.js +0 -46
  53. package/runner/results.json +0 -1
  54. package/runner/transform.js +0 -15
  55. package/tmp.c +0 -69
  56. package/util/enum.js +0 -20
@@ -0,0 +1,26 @@
1
+ const onByDefault = [ 'bytestring', 'aotPointerOpt' ];
2
+
3
+ let cache = {};
4
+ const obj = new Proxy({}, {
5
+ get(_, p) {
6
+ // intentionally misses with undefined values cached
7
+ if (cache[p]) return cache[p];
8
+
9
+ return cache[p] = (() => {
10
+ // fooBar -> foo-bar
11
+ const name = p[0] === '_' ? p : p.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`);
12
+ if (process.argv.includes('-' + name)) return true;
13
+ if (process.argv.includes('-no-' + name)) return false;
14
+
15
+ const valArg = process.argv.find(x => x.startsWith(`-${name}=`));
16
+ if (valArg) return valArg.slice(name.length + 2);
17
+
18
+ if (onByDefault.includes(p)) return true;
19
+ return undefined;
20
+ })();
21
+ }
22
+ });
23
+
24
+ obj.uncache = () => cache = {};
25
+
26
+ export default obj;
@@ -2,29 +2,16 @@ import { Opcodes, Blocktype, Valtype, ValtypeSize, PageSize } from "./wasmSpec.j
2
2
  import { number } from "./embedding.js";
3
3
  import { unsignedLEB128 } from "./encoding.js";
4
4
  import { UNDEFINED } from "./builtins.js";
5
-
6
- // todo: do not duplicate this
7
- const TYPES = {
8
- number: 0x00,
9
- boolean: 0x01,
10
- string: 0x02,
11
- undefined: 0x03,
12
- object: 0x04,
13
- function: 0x05,
14
- symbol: 0x06,
15
- bigint: 0x07,
16
-
17
- // these are not "typeof" types but tracked internally
18
- _array: 0x10,
19
- _regexp: 0x11
20
- };
5
+ import Prefs from './prefs.js';
6
+ import { TYPES } from './types.js';
21
7
 
22
8
  // todo: turn these into built-ins once arrays and these become less hacky
23
9
 
24
10
  export const PrototypeFuncs = function() {
25
- const noUnlikelyChecks = process.argv.includes('-funsafe-no-unlikely-proto-checks');
26
- let zeroChecks = process.argv.find(x => x.startsWith('-funsafe-zero-proto-checks='));
27
- if (zeroChecks) zeroChecks = zeroChecks.split('=')[1].split(',').reduce((acc, x) => { acc[x.toLowerCase()] = true; return acc; }, {});
11
+ const noUnlikelyChecks = Prefs.funsafeNoUnlikelyProtoChecks;
12
+
13
+ let zeroChecks;
14
+ if (Prefs.zeroChecks) zeroChecks = Prefs.zeroChecks.split('=')[1].split(',').reduce((acc, x) => { acc[x.toLowerCase()] = true; return acc; }, {});
28
15
  else zeroChecks = {};
29
16
 
30
17
  this[TYPES._array] = {
@@ -71,7 +58,7 @@ export const PrototypeFuncs = function() {
71
58
  ],
72
59
 
73
60
  // todo: only for 1 argument
74
- push: (pointer, length, wNewMember) => [
61
+ push: (pointer, length, wNewMember, _1, _2, _3, unusedValue) => [
75
62
  // get memory offset of array at last index (length)
76
63
  ...length.getCachedI32(),
77
64
  ...number(ValtypeSize[valtype], Valtype.i32),
@@ -92,22 +79,28 @@ export const PrototypeFuncs = function() {
92
79
  ...number(1, Valtype.i32),
93
80
  [ Opcodes.i32_add ],
94
81
 
95
- ...length.setCachedI32(),
96
- ...length.getCachedI32(),
82
+ ...(unusedValue() ? [] : [
83
+ ...length.setCachedI32(),
84
+ ...length.getCachedI32(),
85
+ ])
97
86
  ]),
98
87
 
99
- ...length.getCachedI32(),
100
- Opcodes.i32_from_u
88
+ ...(unusedValue() ? [] : [
89
+ ...length.getCachedI32(),
90
+ Opcodes.i32_from_u
91
+ ])
101
92
 
102
93
  // ...length.get()
103
94
  ],
104
95
 
105
- pop: (pointer, length) => [
96
+ pop: (pointer, length, _1, _2, _3, _4, unusedValue) => [
106
97
  // if length == 0, noop
107
98
  ...length.getCachedI32(),
108
99
  [ Opcodes.i32_eqz ],
109
100
  [ Opcodes.if, Blocktype.void ],
110
- ...number(UNDEFINED),
101
+ ...(unusedValue() ? [] : [
102
+ ...number(UNDEFINED),
103
+ ]),
111
104
  [ Opcodes.br, 1 ],
112
105
  [ Opcodes.end ],
113
106
 
@@ -119,25 +112,29 @@ export const PrototypeFuncs = function() {
119
112
  ...number(1, Valtype.i32),
120
113
  [ Opcodes.i32_sub ],
121
114
 
122
- ...length.setCachedI32(),
123
- ...length.getCachedI32(),
115
+ ...(unusedValue() ? [] : [
116
+ ...length.setCachedI32(),
117
+ ...length.getCachedI32(),
118
+ ])
124
119
  ]),
125
120
 
126
121
  // load last element
127
- ...length.getCachedI32(),
128
- ...number(ValtypeSize[valtype], Valtype.i32),
129
- [ Opcodes.i32_mul ],
122
+ ...(unusedValue() ? [] : [
123
+ ...length.getCachedI32(),
124
+ ...number(ValtypeSize[valtype], Valtype.i32),
125
+ [ Opcodes.i32_mul ],
130
126
 
131
- ...pointer,
132
- [ Opcodes.i32_add ],
127
+ ...pointer,
128
+ [ Opcodes.i32_add ],
133
129
 
134
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
130
+ [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
131
+ ])
135
132
  ],
136
133
 
137
134
  shift: (pointer, length) => [
138
135
  // if length == 0, noop
139
136
  ...length.getCachedI32(),
140
- Opcodes.i32_eqz,
137
+ [ Opcodes.i32_eqz ],
141
138
  [ Opcodes.if, Blocktype.void ],
142
139
  ...number(UNDEFINED),
143
140
  [ Opcodes.br, 1 ],
@@ -331,8 +328,8 @@ export const PrototypeFuncs = function() {
331
328
  ...number(0, Valtype.i32), // base 0 for store later
332
329
 
333
330
  ...wIndex,
334
-
335
331
  Opcodes.i32_to,
332
+
336
333
  ...number(ValtypeSize.i16, Valtype.i32),
337
334
  [ Opcodes.i32_mul ],
338
335
 
@@ -372,7 +369,7 @@ export const PrototypeFuncs = function() {
372
369
 
373
370
  ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
374
371
  [ Opcodes.if, Blocktype.void ],
375
- ...number(NaN),
372
+ ...number(valtype === 'i32' ? -1 : NaN),
376
373
  [ Opcodes.br, 1 ],
377
374
  [ Opcodes.end ],
378
375
 
@@ -472,8 +469,151 @@ export const PrototypeFuncs = function() {
472
469
  this[TYPES.string].at.returnType = TYPES.string;
473
470
  this[TYPES.string].charAt.returnType = TYPES.string;
474
471
  this[TYPES.string].charCodeAt.local = Valtype.i32;
472
+ this[TYPES.string].charCodeAt.noPointerCache = zeroChecks.charcodeat;
475
473
 
476
474
  this[TYPES.string].isWellFormed.local = Valtype.i32;
477
475
  this[TYPES.string].isWellFormed.local2 = Valtype.i32;
478
476
  this[TYPES.string].isWellFormed.returnType = TYPES.boolean;
477
+
478
+ if (Prefs.bytestring) {
479
+ this[TYPES._bytestring] = {
480
+ at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
481
+ const [ newOut, newPointer ] = arrayShell(1, 'i8');
482
+
483
+ return [
484
+ // setup new/out array
485
+ ...newOut,
486
+ [ Opcodes.drop ],
487
+
488
+ ...number(0, Valtype.i32), // base 0 for store later
489
+
490
+ ...wIndex,
491
+ Opcodes.i32_to_u,
492
+ [ Opcodes.local_tee, iTmp ],
493
+
494
+ // if index < 0: access index + array length
495
+ ...number(0, Valtype.i32),
496
+ [ Opcodes.i32_lt_s ],
497
+ [ Opcodes.if, Blocktype.void ],
498
+ [ Opcodes.local_get, iTmp ],
499
+ ...length.getCachedI32(),
500
+ [ Opcodes.i32_add ],
501
+ [ Opcodes.local_set, iTmp ],
502
+ [ Opcodes.end ],
503
+
504
+ // if still < 0 or >= length: return undefined
505
+ [ Opcodes.local_get, iTmp ],
506
+ ...number(0, Valtype.i32),
507
+ [ Opcodes.i32_lt_s ],
508
+
509
+ [ Opcodes.local_get, iTmp ],
510
+ ...length.getCachedI32(),
511
+ [ Opcodes.i32_ge_s ],
512
+ [ Opcodes.i32_or ],
513
+
514
+ [ Opcodes.if, Blocktype.void ],
515
+ ...number(UNDEFINED),
516
+ [ Opcodes.br, 1 ],
517
+ [ Opcodes.end ],
518
+
519
+ [ Opcodes.local_get, iTmp ],
520
+
521
+ ...pointer,
522
+ [ Opcodes.i32_add ],
523
+
524
+ // load current string ind {arg}
525
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
526
+
527
+ // store to new string ind 0
528
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
529
+
530
+ // return new string (pointer)
531
+ ...number(newPointer)
532
+ ];
533
+ },
534
+
535
+ // todo: out of bounds properly
536
+ charAt: (pointer, length, wIndex, _1, _2, arrayShell) => {
537
+ const [ newOut, newPointer ] = arrayShell(1, 'i8');
538
+
539
+ return [
540
+ // setup new/out array
541
+ ...newOut,
542
+ [ Opcodes.drop ],
543
+
544
+ ...number(0, Valtype.i32), // base 0 for store later
545
+
546
+ ...wIndex,
547
+ Opcodes.i32_to,
548
+
549
+ ...pointer,
550
+ [ Opcodes.i32_add ],
551
+
552
+ // load current string ind {arg}
553
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
554
+
555
+ // store to new string ind 0
556
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
557
+
558
+ // return new string (page)
559
+ ...number(newPointer)
560
+ ];
561
+ },
562
+
563
+ charCodeAt: (pointer, length, wIndex, iTmp) => {
564
+ return [
565
+ ...wIndex,
566
+ Opcodes.i32_to,
567
+
568
+ ...(zeroChecks.charcodeat ? [] : [
569
+ [ Opcodes.local_set, iTmp ],
570
+
571
+ // index < 0
572
+ ...(noUnlikelyChecks ? [] : [
573
+ [ Opcodes.local_get, iTmp ],
574
+ ...number(0, Valtype.i32),
575
+ [ Opcodes.i32_lt_s ],
576
+ ]),
577
+
578
+ // index >= length
579
+ [ Opcodes.local_get, iTmp ],
580
+ ...length.getCachedI32(),
581
+ [ Opcodes.i32_ge_s ],
582
+
583
+ ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
584
+ [ Opcodes.if, Blocktype.void ],
585
+ ...number(valtype === 'i32' ? -1 : NaN),
586
+ [ Opcodes.br, 1 ],
587
+ [ Opcodes.end ],
588
+
589
+ [ Opcodes.local_get, iTmp ],
590
+ ]),
591
+
592
+ ...pointer,
593
+ [ Opcodes.i32_add ],
594
+
595
+ // load current string ind {arg}
596
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
597
+ Opcodes.i32_from_u
598
+ ];
599
+ },
600
+
601
+ isWellFormed: () => {
602
+ return [
603
+ // we know it must be true as it is a bytestring lol
604
+ ...number(1)
605
+ ]
606
+ }
607
+ };
608
+
609
+ this[TYPES._bytestring].at.local = Valtype.i32;
610
+ this[TYPES._bytestring].at.returnType = TYPES._bytestring;
611
+ this[TYPES._bytestring].charAt.returnType = TYPES._bytestring;
612
+ this[TYPES._bytestring].charCodeAt.local = Valtype.i32;
613
+ this[TYPES._bytestring].charCodeAt.noPointerCache = zeroChecks.charcodeat;
614
+
615
+ this[TYPES._bytestring].isWellFormed.local = Valtype.i32;
616
+ this[TYPES._bytestring].isWellFormed.local2 = Valtype.i32;
617
+ this[TYPES._bytestring].isWellFormed.returnType = TYPES.boolean;
618
+ }
479
619
  };
@@ -0,0 +1,37 @@
1
+ export const TYPES = {
2
+ number: 0x00,
3
+ boolean: 0x01,
4
+ string: 0x02,
5
+ undefined: 0x03,
6
+ object: 0x04,
7
+ function: 0x05,
8
+ symbol: 0x06,
9
+ bigint: 0x07
10
+ };
11
+
12
+ export const TYPE_NAMES = {
13
+ [TYPES.number]: 'Number',
14
+ [TYPES.boolean]: 'Boolean',
15
+ [TYPES.string]: 'String',
16
+ [TYPES.undefined]: 'undefined',
17
+ [TYPES.object]: 'Object',
18
+ [TYPES.function]: 'Function',
19
+ [TYPES.symbol]: 'Symbol',
20
+ [TYPES.bigint]: 'BigInt'
21
+ };
22
+
23
+ export const INTERNAL_TYPE_BASE = 0x10;
24
+ let internalTypeIndex = INTERNAL_TYPE_BASE;
25
+ const registerInternalType = name => {
26
+ const n = internalTypeIndex++;
27
+ TYPES['_' + name.toLowerCase()] = n;
28
+ TYPE_NAMES[n] = name;
29
+ };
30
+
31
+ // note: when adding a new internal type, please also add a deserializer to wrap.js
32
+ // (it is okay to add a throw todo deserializer for wips)
33
+
34
+ registerInternalType('Array');
35
+ registerInternalType('RegExp');
36
+ registerInternalType('ByteString');
37
+ registerInternalType('Date');
@@ -1,4 +1,13 @@
1
- import { enumify } from "../util/enum.js";
1
+ const enumify = (...args) => {
2
+ const obj = {};
3
+
4
+ for (let i = 0; i < args.length; i++) {
5
+ obj[i] = args[i];
6
+ obj[args[i]] = i;
7
+ }
8
+
9
+ return obj;
10
+ };
2
11
 
3
12
  export const Section = enumify('custom', 'type', 'import', 'func', 'table', 'memory', 'global', 'export', 'start', 'element', 'code', 'data', 'data_count', 'tag');
4
13
  export const ExportDesc = enumify('func', 'table', 'mem', 'global', 'tag');
@@ -32,17 +41,17 @@ export const Opcodes = {
32
41
  throw: 0x08,
33
42
  rethrow: 0x09,
34
43
 
35
- return: 0x0F,
44
+ end: 0x0b,
45
+ br: 0x0c,
46
+ br_if: 0x0d,
47
+ br_table: 0x0e,
48
+ return: 0x0f,
36
49
 
37
50
  call: 0x10,
38
51
  call_indirect: 0x11,
39
52
  return_call: 0x12,
40
53
  return_call_indirect: 0x13,
41
54
 
42
- end: 0x0b,
43
- br: 0x0c,
44
- br_if: 0x0d,
45
- call: 0x10,
46
55
  drop: 0x1a,
47
56
 
48
57
  local_get: 0x20,
@@ -56,15 +65,26 @@ export const Opcodes = {
56
65
  i64_load: 0x29,
57
66
  f64_load: 0x2b,
58
67
 
68
+ i32_load8_s: 0x2c,
69
+ i32_load8_u: 0x2d,
59
70
  i32_load16_s: 0x2e,
60
71
  i32_load16_u: 0x2f,
61
72
 
62
- i32_store16: 0x3b,
73
+ i64_load8_s: 0x30,
74
+ i64_load8_u: 0x31,
75
+ i64_load16_s: 0x32,
76
+ i64_load16_u: 0x33,
63
77
 
64
78
  i32_store: 0x36,
65
79
  i64_store: 0x37,
66
80
  f64_store: 0x39,
67
81
 
82
+ i32_store8: 0x3a,
83
+ i32_store16: 0x3b,
84
+
85
+ i64_store8: 0x3c,
86
+ i64_store16: 0x3d,
87
+
68
88
  memory_grow: 0x40,
69
89
 
70
90
  i32_const: 0x41,
@@ -96,6 +116,8 @@ export const Opcodes = {
96
116
  i32_shl: 0x74,
97
117
  i32_shr_s: 0x75,
98
118
  i32_shr_u: 0x76,
119
+ i32_rotl: 0x77,
120
+ i32_rotr: 0x78,
99
121
 
100
122
  i64_eqz: 0x50,
101
123
  i64_eq: 0x51,
@@ -119,6 +141,7 @@ export const Opcodes = {
119
141
  i64_shr_s: 0x87,
120
142
  i64_shr_u: 0x88,
121
143
  i64_rotl: 0x89,
144
+ i64_rotr: 0x8a,
122
145
 
123
146
  f64_eq: 0x61,
124
147
  f64_ne: 0x62,
package/compiler/wrap.js CHANGED
@@ -1,51 +1,133 @@
1
1
  import compile from './index.js';
2
2
  import decompile from './decompile.js';
3
- // import fs from 'node:fs';
3
+ import { encodeVector, encodeLocal } from './encoding.js';
4
+ import Prefs from './prefs.js';
5
+ import { log } from './log.js';
6
+ import { TYPES } from './types.js';
4
7
 
5
8
  const bold = x => `\u001b[1m${x}\u001b[0m`;
6
9
 
7
- const typeBase = 0x00;
8
- const internalTypeBase = 0x10;
9
- const TYPES = {
10
- [typeBase]: 'number',
11
- [typeBase + 1]: 'boolean',
12
- [typeBase + 2]: 'string',
13
- [typeBase + 3]: 'undefined',
14
- [typeBase + 4]: 'object',
15
- [typeBase + 5]: 'function',
16
- [typeBase + 6]: 'symbol',
17
- [typeBase + 7]: 'bigint',
18
-
19
- // internal
20
- [internalTypeBase]: '_array',
21
- [internalTypeBase + 1]: '_regexp'
22
- };
23
-
24
10
  export default async (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
25
11
  const times = [];
26
12
 
27
13
  const t1 = performance.now();
28
14
  const { wasm, funcs, globals, tags, exceptions, pages, c } = compile(source, flags);
29
15
 
16
+ globalThis.porfDebugInfo = { funcs, globals };
17
+
30
18
  if (source.includes('export function')) flags.push('module');
31
19
 
32
- // fs.writeFileSync('out.wasm', Buffer.from(wasm));
20
+ // (await import('node:fs')).writeFileSync('out.wasm', Buffer.from(wasm));
33
21
 
34
22
  times.push(performance.now() - t1);
35
- if (flags.includes('info')) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
23
+ if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
36
24
 
37
25
  const t2 = performance.now();
38
- const { instance } = await WebAssembly.instantiate(wasm, {
39
- '': {
40
- p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
41
- c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
42
- t: _ => performance.now(),
43
- ...customImports
26
+
27
+ let instance;
28
+ try {
29
+ let wasmEngine = WebAssembly;
30
+ if (Prefs.asur) {
31
+ log.warning('wrap', 'using our !experimental! asur wasm engine instead of host to run');
32
+ wasmEngine = await import('../asur/index.js');
33
+ }
34
+
35
+ 0, { instance } = await wasmEngine.instantiate(wasm, {
36
+ '': {
37
+ p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
38
+ c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
39
+ t: () => performance.now(),
40
+ y: () => {},
41
+ z: () => {},
42
+ ...customImports
43
+ }
44
+ });
45
+ } catch (e) {
46
+ // only backtrace for runner, not test262/etc
47
+ if (!process.argv[1].includes('/runner')) throw e;
48
+
49
+ const funcInd = parseInt(e.message.match(/function #([0-9]+) /)?.[1]);
50
+ const blobOffset = parseInt(e.message.split('@')?.[1]);
51
+
52
+ if (!funcInd) throw e;
53
+
54
+ // convert blob offset -> function wasm offset.
55
+ // this is not good code and is somewhat duplicated
56
+ // I just want it to work for debugging, I don't care about perf/yes
57
+
58
+ const func = funcs.find(x => x.index === funcInd);
59
+ const locals = Object.values(func.locals).sort((a, b) => a.idx - b.idx).slice(func.params.length).sort((a, b) => a.idx - b.idx);
60
+
61
+ let localDecl = [], typeCount = 0, lastType;
62
+ for (let i = 0; i < locals.length; i++) {
63
+ const local = locals[i];
64
+ if (i !== 0 && local.type !== lastType) {
65
+ localDecl.push(encodeLocal(typeCount, lastType));
66
+ typeCount = 0;
67
+ }
68
+
69
+ typeCount++;
70
+ lastType = local.type;
71
+ }
72
+
73
+ if (typeCount !== 0) localDecl.push(encodeLocal(typeCount, lastType));
74
+
75
+ const toFind = encodeVector(localDecl).concat(func.wasm.flat().filter(x => x != null && x <= 0xff).slice(0, 40));
76
+
77
+ let i = 0;
78
+ for (; i < wasm.length; i++) {
79
+ let mismatch = false;
80
+ for (let j = 0; j < toFind.length; j++) {
81
+ if (wasm[i + j] !== toFind[j]) {
82
+ mismatch = true;
83
+ break;
84
+ }
85
+ }
86
+
87
+ if (!mismatch) break;
88
+ }
89
+
90
+ if (i === wasm.length) throw e;
91
+
92
+ const offset = (blobOffset - i) + encodeVector(localDecl).length;
93
+
94
+ let cumLen = 0;
95
+ i = 0;
96
+ for (; i < func.wasm.length; i++) {
97
+ cumLen += func.wasm[i].filter(x => x != null && x <= 0xff).length;
98
+ if (cumLen === offset) break;
44
99
  }
45
- });
100
+
101
+ if (cumLen !== offset) throw e;
102
+
103
+ i -= 1;
104
+
105
+ console.log(`\x1B[35m\x1B[1mporffor backtrace\u001b[0m`);
106
+
107
+ console.log('\x1B[4m' + func.name + '\x1B[0m');
108
+
109
+ const surrounding = 6;
110
+
111
+ const decomp = decompile(func.wasm.slice(i - surrounding, i + surrounding + 1), '', 0, func.locals, func.params, func.returns, funcs, globals, exceptions).slice(0, -1).split('\n');
112
+
113
+ const noAnsi = s => s.replace(/\u001b\[[0-9]+m/g, '');
114
+ let longest = 0;
115
+ for (let j = 0; j < decomp.length; j++) {
116
+ longest = Math.max(longest, noAnsi(decomp[j]).length);
117
+ }
118
+
119
+ const middle = Math.floor(decomp.length / 2);
120
+ decomp[middle] = `\x1B[47m\x1B[30m${noAnsi(decomp[middle])}${'\u00a0'.repeat(longest - noAnsi(decomp[middle]).length)}\x1B[0m`;
121
+
122
+ console.log('\x1B[90m...\x1B[0m');
123
+ console.log(decomp.join('\n'));
124
+ console.log('\x1B[90m...\x1B[0m\n');
125
+
126
+ throw e;
127
+ }
46
128
 
47
129
  times.push(performance.now() - t2);
48
- if (flags.includes('info')) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
130
+ if (Prefs.profileCompiler) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
49
131
 
50
132
  const exports = {};
51
133
 
@@ -73,12 +155,29 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
73
155
 
74
156
  // if (ret >= typeBase && ret <= typeBase + 8) return ret > (typeBase + 7) ? 'object' : TYPES[ret];
75
157
 
76
- switch (TYPES[type]) {
77
- case 'boolean': return Boolean(ret);
78
- case 'undefined': return undefined;
79
- case 'object': return ret === 0 ? null : {};
158
+ switch (type) {
159
+ case TYPES.boolean: return Boolean(ret);
160
+ case TYPES.undefined: return undefined;
161
+ case TYPES.object: return ret === 0 ? null : {};
80
162
 
81
- case '_array': {
163
+ case TYPES.string: {
164
+ const pointer = ret;
165
+ const length = new Int32Array(memory.buffer, pointer, 1);
166
+
167
+ return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
168
+ }
169
+
170
+ case TYPES.function: {
171
+ // wasm func index, including all imports
172
+ const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
173
+ // if (!func) return ret;
174
+ if (!func) return function () {};
175
+
176
+ // make fake empty func for repl/etc
177
+ return {[func.name]() {}}[func.name];
178
+ }
179
+
180
+ case TYPES._array: {
82
181
  const pointer = ret;
83
182
  const length = new Int32Array(memory.buffer, pointer, 1);
84
183
 
@@ -88,20 +187,16 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
88
187
  return Array.from(new Float64Array(buf));
89
188
  }
90
189
 
91
- case 'string': {
190
+ case TYPES._bytestring: {
92
191
  const pointer = ret;
93
192
  const length = new Int32Array(memory.buffer, pointer, 1);
94
193
 
95
- return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
194
+ return Array.from(new Uint8Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
96
195
  }
97
196
 
98
- case 'function': {
99
- // wasm func index, including all imports
100
- const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
101
- if (!func) return ret;
102
-
103
- // make fake empty func for repl/etc
104
- return {[func.name]() {}}[func.name];
197
+ case TYPES._date: {
198
+ // todo
199
+ throw new Error('todo! deserialize date');
105
200
  }
106
201
 
107
202
  default: return ret;
package/empty.js ADDED
File without changes
package/hello.js ADDED
@@ -0,0 +1,2 @@
1
+ console.log('hello world!');
2
+