porffor 0.2.0-e562242 → 0.2.0-e62542f
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.
- package/CONTRIBUTING.md +254 -0
- package/LICENSE +20 -20
- package/README.md +150 -89
- package/asur/README.md +2 -0
- package/asur/index.js +1262 -0
- package/byg/index.js +237 -0
- package/compiler/2c.js +317 -72
- package/compiler/{sections.js → assemble.js} +63 -15
- package/compiler/builtins/annexb_string.js +72 -0
- package/compiler/builtins/annexb_string.ts +18 -0
- package/compiler/builtins/array.ts +145 -0
- package/compiler/builtins/base64.ts +76 -0
- package/compiler/builtins/crypto.ts +120 -0
- package/compiler/builtins/date.ts +2071 -0
- package/compiler/builtins/escape.ts +141 -0
- package/compiler/builtins/int.ts +147 -0
- package/compiler/builtins/number.ts +527 -0
- package/compiler/builtins/porffor.d.ts +59 -0
- package/compiler/builtins/string.ts +1055 -0
- package/compiler/builtins/tostring.ts +45 -0
- package/compiler/builtins.js +580 -272
- package/compiler/{codeGen.js → codegen.js} +1173 -428
- package/compiler/decompile.js +3 -4
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +108 -10
- package/compiler/generated_builtins.js +1481 -0
- package/compiler/index.js +36 -34
- package/compiler/log.js +6 -3
- package/compiler/opt.js +51 -36
- package/compiler/parse.js +33 -23
- package/compiler/precompile.js +128 -0
- package/compiler/prefs.js +27 -0
- package/compiler/prototype.js +182 -42
- package/compiler/types.js +37 -0
- package/compiler/wasmSpec.js +30 -7
- package/compiler/wrap.js +141 -43
- package/package.json +9 -5
- package/porf +4 -0
- package/rhemyn/compile.js +46 -27
- package/rhemyn/parse.js +322 -320
- package/rhemyn/test/parse.js +58 -58
- package/runner/compare.js +34 -34
- package/runner/debug.js +122 -0
- package/runner/index.js +91 -11
- package/runner/profiler.js +102 -0
- package/runner/repl.js +42 -9
- package/runner/sizes.js +37 -37
- package/compiler/builtins/base64.js +0 -92
- package/node_trace.1.log +0 -1
- package/runner/info.js +0 -89
- package/runner/profile.js +0 -46
- package/runner/results.json +0 -1
- package/runner/transform.js +0 -15
- package/util/enum.js +0 -20
package/compiler/prototype.js
CHANGED
@@ -2,32 +2,19 @@ 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
|
-
|
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 =
|
26
|
-
|
27
|
-
|
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
|
-
this[TYPES.
|
17
|
+
this[TYPES.array] = {
|
31
18
|
// lX = local accessor of X ({ get, set }), iX = local index of X, wX = wasm ops of X
|
32
19
|
at: (pointer, length, wIndex, iTmp) => [
|
33
20
|
...wIndex,
|
@@ -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
|
-
...
|
96
|
-
|
82
|
+
...(unusedValue() ? [] : [
|
83
|
+
...length.setCachedI32(),
|
84
|
+
...length.getCachedI32(),
|
85
|
+
])
|
97
86
|
]),
|
98
87
|
|
99
|
-
...
|
100
|
-
|
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
|
-
...
|
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
|
-
...
|
123
|
-
|
115
|
+
...(unusedValue() ? [] : [
|
116
|
+
...length.setCachedI32(),
|
117
|
+
...length.getCachedI32(),
|
118
|
+
])
|
124
119
|
]),
|
125
120
|
|
126
121
|
// load last element
|
127
|
-
...
|
128
|
-
|
129
|
-
|
122
|
+
...(unusedValue() ? [] : [
|
123
|
+
...length.getCachedI32(),
|
124
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
125
|
+
[ Opcodes.i32_mul ],
|
130
126
|
|
131
|
-
|
132
|
-
|
127
|
+
...pointer,
|
128
|
+
[ Opcodes.i32_add ],
|
133
129
|
|
134
|
-
|
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 ],
|
@@ -256,10 +253,10 @@ export const PrototypeFuncs = function() {
|
|
256
253
|
]
|
257
254
|
};
|
258
255
|
|
259
|
-
this[TYPES.
|
260
|
-
this[TYPES.
|
261
|
-
this[TYPES.
|
262
|
-
this[TYPES.
|
256
|
+
this[TYPES.array].at.local = Valtype.i32;
|
257
|
+
this[TYPES.array].push.noArgRetLength = true;
|
258
|
+
this[TYPES.array].fill.local = valtypeBinary;
|
259
|
+
this[TYPES.array].fill.returnType = TYPES.array;
|
263
260
|
|
264
261
|
this[TYPES.string] = {
|
265
262
|
at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
|
@@ -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');
|
package/compiler/wasmSpec.js
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
-
|
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,16 @@ export const Opcodes = {
|
|
32
41
|
throw: 0x08,
|
33
42
|
rethrow: 0x09,
|
34
43
|
|
35
|
-
call: 0x10,
|
36
|
-
call_indirect: 0x11,
|
37
|
-
return_call: 0x12,
|
38
|
-
return_call_indirect: 0x13,
|
39
|
-
|
40
44
|
end: 0x0b,
|
41
45
|
br: 0x0c,
|
42
46
|
br_if: 0x0d,
|
43
47
|
br_table: 0x0e,
|
44
48
|
return: 0x0f,
|
49
|
+
|
45
50
|
call: 0x10,
|
51
|
+
call_indirect: 0x11,
|
52
|
+
return_call: 0x12,
|
53
|
+
return_call_indirect: 0x13,
|
46
54
|
|
47
55
|
drop: 0x1a,
|
48
56
|
|
@@ -57,15 +65,27 @@ export const Opcodes = {
|
|
57
65
|
i64_load: 0x29,
|
58
66
|
f64_load: 0x2b,
|
59
67
|
|
68
|
+
i32_load8_s: 0x2c,
|
69
|
+
i32_load8_u: 0x2d,
|
60
70
|
i32_load16_s: 0x2e,
|
61
71
|
i32_load16_u: 0x2f,
|
62
72
|
|
63
|
-
|
73
|
+
i64_load8_s: 0x30,
|
74
|
+
i64_load8_u: 0x31,
|
75
|
+
i64_load16_s: 0x32,
|
76
|
+
i64_load16_u: 0x33,
|
64
77
|
|
65
78
|
i32_store: 0x36,
|
66
79
|
i64_store: 0x37,
|
67
80
|
f64_store: 0x39,
|
68
81
|
|
82
|
+
i32_store8: 0x3a,
|
83
|
+
i32_store16: 0x3b,
|
84
|
+
|
85
|
+
i64_store8: 0x3c,
|
86
|
+
i64_store16: 0x3d,
|
87
|
+
|
88
|
+
memory_size: 0x3f,
|
69
89
|
memory_grow: 0x40,
|
70
90
|
|
71
91
|
i32_const: 0x41,
|
@@ -97,6 +117,8 @@ export const Opcodes = {
|
|
97
117
|
i32_shl: 0x74,
|
98
118
|
i32_shr_s: 0x75,
|
99
119
|
i32_shr_u: 0x76,
|
120
|
+
i32_rotl: 0x77,
|
121
|
+
i32_rotr: 0x78,
|
100
122
|
|
101
123
|
i64_eqz: 0x50,
|
102
124
|
i64_eq: 0x51,
|
@@ -120,6 +142,7 @@ export const Opcodes = {
|
|
120
142
|
i64_shr_s: 0x87,
|
121
143
|
i64_shr_u: 0x88,
|
122
144
|
i64_rotl: 0x89,
|
145
|
+
i64_rotr: 0x8a,
|
123
146
|
|
124
147
|
f64_eq: 0x61,
|
125
148
|
f64_ne: 0x62,
|
package/compiler/wrap.js
CHANGED
@@ -1,51 +1,134 @@
|
|
1
1
|
import compile from './index.js';
|
2
2
|
import decompile from './decompile.js';
|
3
|
-
import
|
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 (
|
23
|
+
if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
|
36
24
|
|
37
25
|
const t2 = performance.now();
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
+
u: () => performance.timeOrigin,
|
41
|
+
y: () => {},
|
42
|
+
z: () => {},
|
43
|
+
...customImports
|
44
|
+
}
|
45
|
+
});
|
46
|
+
} catch (e) {
|
47
|
+
// only backtrace for runner, not test262/etc
|
48
|
+
if (!process.argv[1].includes('/runner')) throw e;
|
49
|
+
|
50
|
+
const funcInd = parseInt(e.message.match(/function #([0-9]+) /)?.[1]);
|
51
|
+
const blobOffset = parseInt(e.message.split('@')?.[1]);
|
52
|
+
|
53
|
+
if (!funcInd) throw e;
|
54
|
+
|
55
|
+
// convert blob offset -> function wasm offset.
|
56
|
+
// this is not good code and is somewhat duplicated
|
57
|
+
// I just want it to work for debugging, I don't care about perf/yes
|
58
|
+
|
59
|
+
const func = funcs.find(x => x.index === funcInd);
|
60
|
+
const locals = Object.values(func.locals).sort((a, b) => a.idx - b.idx).slice(func.params.length).sort((a, b) => a.idx - b.idx);
|
61
|
+
|
62
|
+
let localDecl = [], typeCount = 0, lastType;
|
63
|
+
for (let i = 0; i < locals.length; i++) {
|
64
|
+
const local = locals[i];
|
65
|
+
if (i !== 0 && local.type !== lastType) {
|
66
|
+
localDecl.push(encodeLocal(typeCount, lastType));
|
67
|
+
typeCount = 0;
|
68
|
+
}
|
69
|
+
|
70
|
+
typeCount++;
|
71
|
+
lastType = local.type;
|
72
|
+
}
|
73
|
+
|
74
|
+
if (typeCount !== 0) localDecl.push(encodeLocal(typeCount, lastType));
|
75
|
+
|
76
|
+
const toFind = encodeVector(localDecl).concat(func.wasm.flat().filter(x => x != null && x <= 0xff).slice(0, 40));
|
77
|
+
|
78
|
+
let i = 0;
|
79
|
+
for (; i < wasm.length; i++) {
|
80
|
+
let mismatch = false;
|
81
|
+
for (let j = 0; j < toFind.length; j++) {
|
82
|
+
if (wasm[i + j] !== toFind[j]) {
|
83
|
+
mismatch = true;
|
84
|
+
break;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
if (!mismatch) break;
|
89
|
+
}
|
90
|
+
|
91
|
+
if (i === wasm.length) throw e;
|
92
|
+
|
93
|
+
const offset = (blobOffset - i) + encodeVector(localDecl).length;
|
94
|
+
|
95
|
+
let cumLen = 0;
|
96
|
+
i = 0;
|
97
|
+
for (; i < func.wasm.length; i++) {
|
98
|
+
cumLen += func.wasm[i].filter(x => x != null && x <= 0xff).length;
|
99
|
+
if (cumLen === offset) break;
|
100
|
+
}
|
101
|
+
|
102
|
+
if (cumLen !== offset) throw e;
|
103
|
+
|
104
|
+
i -= 1;
|
105
|
+
|
106
|
+
console.log(`\x1B[35m\x1B[1mporffor backtrace\u001b[0m`);
|
107
|
+
|
108
|
+
console.log('\x1B[4m' + func.name + '\x1B[0m');
|
109
|
+
|
110
|
+
const surrounding = 6;
|
111
|
+
|
112
|
+
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');
|
113
|
+
|
114
|
+
const noAnsi = s => s.replace(/\u001b\[[0-9]+m/g, '');
|
115
|
+
let longest = 0;
|
116
|
+
for (let j = 0; j < decomp.length; j++) {
|
117
|
+
longest = Math.max(longest, noAnsi(decomp[j]).length);
|
44
118
|
}
|
45
|
-
|
119
|
+
|
120
|
+
const middle = Math.floor(decomp.length / 2);
|
121
|
+
decomp[middle] = `\x1B[47m\x1B[30m${noAnsi(decomp[middle])}${'\u00a0'.repeat(longest - noAnsi(decomp[middle]).length)}\x1B[0m`;
|
122
|
+
|
123
|
+
console.log('\x1B[90m...\x1B[0m');
|
124
|
+
console.log(decomp.join('\n'));
|
125
|
+
console.log('\x1B[90m...\x1B[0m\n');
|
126
|
+
|
127
|
+
throw e;
|
128
|
+
}
|
46
129
|
|
47
130
|
times.push(performance.now() - t2);
|
48
|
-
if (
|
131
|
+
if (Prefs.profileCompiler) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
|
49
132
|
|
50
133
|
const exports = {};
|
51
134
|
|
@@ -73,14 +156,31 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
73
156
|
|
74
157
|
// if (ret >= typeBase && ret <= typeBase + 8) return ret > (typeBase + 7) ? 'object' : TYPES[ret];
|
75
158
|
|
76
|
-
switch (
|
77
|
-
case
|
78
|
-
case
|
79
|
-
case
|
159
|
+
switch (type) {
|
160
|
+
case TYPES.boolean: return Boolean(ret);
|
161
|
+
case TYPES.undefined: return undefined;
|
162
|
+
case TYPES.object: return ret === 0 ? null : {};
|
80
163
|
|
81
|
-
case
|
164
|
+
case TYPES.string: {
|
82
165
|
const pointer = ret;
|
83
|
-
const length = new Int32Array(memory.buffer, pointer, 1);
|
166
|
+
const length = (new Int32Array(memory.buffer, pointer, 1))[0];
|
167
|
+
|
168
|
+
return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
169
|
+
}
|
170
|
+
|
171
|
+
case TYPES.function: {
|
172
|
+
// wasm func index, including all imports
|
173
|
+
const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
|
174
|
+
// if (!func) return ret;
|
175
|
+
if (!func) return function () {};
|
176
|
+
|
177
|
+
// make fake empty func for repl/etc
|
178
|
+
return {[func.name]() {}}[func.name];
|
179
|
+
}
|
180
|
+
|
181
|
+
case TYPES.array: {
|
182
|
+
const pointer = ret;
|
183
|
+
const length = (new Int32Array(memory.buffer, pointer, 1))[0];
|
84
184
|
|
85
185
|
// have to slice because of memory alignment
|
86
186
|
const buf = memory.buffer.slice(pointer + 4, pointer + 4 + 8 * length);
|
@@ -88,20 +188,18 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
88
188
|
return Array.from(new Float64Array(buf));
|
89
189
|
}
|
90
190
|
|
91
|
-
case
|
191
|
+
case TYPES.bytestring: {
|
92
192
|
const pointer = ret;
|
93
|
-
const length = new Int32Array(memory.buffer, pointer, 1);
|
193
|
+
const length = (new Int32Array(memory.buffer, pointer, 1))[0];
|
94
194
|
|
95
|
-
return Array.from(new
|
195
|
+
return Array.from(new Uint8Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
96
196
|
}
|
97
197
|
|
98
|
-
case
|
99
|
-
|
100
|
-
const
|
101
|
-
if (!func) return ret;
|
198
|
+
case TYPES.date: {
|
199
|
+
const pointer = ret;
|
200
|
+
const value = (new Float64Array(memory.buffer, pointer, 1))[0];
|
102
201
|
|
103
|
-
|
104
|
-
return {[func.name]() {}}[func.name];
|
202
|
+
return new Date(value);
|
105
203
|
}
|
106
204
|
|
107
205
|
default: return ret;
|
package/package.json
CHANGED
@@ -1,21 +1,25 @@
|
|
1
1
|
{
|
2
2
|
"name": "porffor",
|
3
3
|
"description": "a basic experimental wip aot optimizing js -> wasm engine/compiler/runtime in js",
|
4
|
-
"version": "0.2.0-
|
4
|
+
"version": "0.2.0-e62542f",
|
5
5
|
"author": "CanadaHonk",
|
6
6
|
"license": "MIT",
|
7
|
+
"scripts": {
|
8
|
+
"precompile": "node ./compiler/precompile.js"
|
9
|
+
},
|
7
10
|
"dependencies": {
|
8
|
-
"acorn": "^8.
|
11
|
+
"acorn": "^8.11.3",
|
12
|
+
"node-repl-polyfill": "^0.1.1"
|
9
13
|
},
|
10
14
|
"optionalDependencies": {
|
11
|
-
"@babel/parser": "^7.
|
15
|
+
"@babel/parser": "^7.24.4",
|
12
16
|
"hermes-parser": "^0.18.2",
|
13
17
|
"meriyah": "^4.3.9"
|
14
18
|
},
|
15
19
|
"bin": {
|
16
20
|
"porf": "./runner/index.js"
|
17
21
|
},
|
18
|
-
"main": "./
|
22
|
+
"main": "./compiler/wrap.js",
|
19
23
|
"type": "module",
|
20
24
|
"repository": {
|
21
25
|
"type": "git",
|
@@ -25,4 +29,4 @@
|
|
25
29
|
"url": "https://github.com/CanadaHonk/porffor/issues"
|
26
30
|
},
|
27
31
|
"homepage": "https://porffor.goose.icu"
|
28
|
-
}
|
32
|
+
}
|
package/porf
ADDED