porffor 0.2.0-c7b7423 → 0.2.0-eaee2da
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/README.md +34 -12
- package/compiler/2c.js +6 -1
- package/compiler/builtins.js +134 -6
- package/compiler/codeGen.js +362 -174
- package/compiler/decompile.js +3 -3
- package/compiler/encoding.js +2 -116
- package/compiler/index.js +1 -1
- package/compiler/opt.js +23 -2
- package/compiler/parse.js +11 -12
- package/compiler/prototype.js +171 -16
- package/compiler/sections.js +1 -1
- package/compiler/wasmSpec.js +6 -2
- package/compiler/wrap.js +98 -8
- package/package.json +1 -1
- package/runner/repl.js +2 -2
- package/tmp.c +0 -69
package/compiler/decompile.js
CHANGED
@@ -15,7 +15,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
|
|
15
15
|
if (name) out += `${makeSignature(params, returns)} ;; $${name} (${ind})\n`;
|
16
16
|
|
17
17
|
const justLocals = Object.values(locals).sort((a, b) => a.idx - b.idx).slice(params.length);
|
18
|
-
if (justLocals.length > 0) out += ` local ${justLocals.map(x => invValtype[x.type]).join(' ')}\n`;
|
18
|
+
if (name && justLocals.length > 0) out += ` local ${justLocals.map(x => invValtype[x.type]).join(' ')}\n`;
|
19
19
|
|
20
20
|
let i = -1, lastInst;
|
21
21
|
let byte = 0;
|
@@ -32,7 +32,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
|
|
32
32
|
inst = [ [ inst[0], inst[1] ], ...inst.slice(2) ];
|
33
33
|
}
|
34
34
|
|
35
|
-
if (inst[0] === Opcodes.end || inst[0] === Opcodes.else || inst[0] === Opcodes.catch_all) depth--;
|
35
|
+
if (depth > 0 && (inst[0] === Opcodes.end || inst[0] === Opcodes.else || inst[0] === Opcodes.catch_all)) depth--;
|
36
36
|
|
37
37
|
out += ' '.repeat(Math.max(0, depth * 2));
|
38
38
|
|
@@ -119,7 +119,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
|
|
119
119
|
export const highlightAsm = asm => asm
|
120
120
|
.replace(/(local|global|memory)\.[^\s]*/g, _ => `\x1B[31m${_}\x1B[0m`)
|
121
121
|
.replace(/(i(8|16|32|64)x[0-9]+|v128)(\.[^\s]*)?/g, _ => `\x1B[34m${_}\x1B[0m`)
|
122
|
-
.replace(/
|
122
|
+
.replace(/(i32|i64|f32|f64|drop)(\.[^\s]*)?/g, _ => `\x1B[36m${_}\x1B[0m`)
|
123
123
|
.replace(/(return_call|call|br_if|br|return|rethrow|throw)/g, _ => `\x1B[35m${_}\x1B[0m`)
|
124
124
|
.replace(/(block|loop|if|end|else|try|catch_all|catch|delegate)/g, _ => `\x1B[95m${_}\x1B[0m`)
|
125
125
|
.replace(/unreachable/g, _ => `\x1B[91m${_}\x1B[0m`)
|
package/compiler/encoding.js
CHANGED
@@ -105,119 +105,5 @@ export const read_unsignedLEB128 = _input => {
|
|
105
105
|
};
|
106
106
|
|
107
107
|
// ieee 754 binary64
|
108
|
-
|
109
|
-
|
110
|
-
// BSD 3-Clause. Copyright 2008 Fair Oaks Labs, Inc. (https://github.com/feross/ieee754/blob/master/LICENSE)
|
111
|
-
export const ieee754_binary64 = value => {
|
112
|
-
return [...new Uint8Array(new Float64Array([ value ]).buffer)];
|
113
|
-
|
114
|
-
let isLE = true, mLen = 52, nBytes = 8, offset = 0;
|
115
|
-
let buffer = new Array(nBytes).fill(0);
|
116
|
-
|
117
|
-
let e, m, c
|
118
|
-
let eLen = (nBytes * 8) - mLen - 1
|
119
|
-
const eMax = (1 << eLen) - 1
|
120
|
-
const eBias = eMax >> 1
|
121
|
-
const rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
|
122
|
-
let i = isLE ? 0 : (nBytes - 1)
|
123
|
-
const d = isLE ? 1 : -1
|
124
|
-
const s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
|
125
|
-
|
126
|
-
value = Math.abs(value)
|
127
|
-
|
128
|
-
if (isNaN(value) || value === Infinity) {
|
129
|
-
m = isNaN(value) ? 1 : 0
|
130
|
-
e = eMax
|
131
|
-
} else {
|
132
|
-
e = Math.floor(Math.log(value) / Math.LN2)
|
133
|
-
if (value * (c = Math.pow(2, -e)) < 1) {
|
134
|
-
e--
|
135
|
-
c *= 2
|
136
|
-
}
|
137
|
-
if (e + eBias >= 1) {
|
138
|
-
value += rt / c
|
139
|
-
} else {
|
140
|
-
value += rt * Math.pow(2, 1 - eBias)
|
141
|
-
}
|
142
|
-
if (value * c >= 2) {
|
143
|
-
e++
|
144
|
-
c /= 2
|
145
|
-
}
|
146
|
-
|
147
|
-
if (e + eBias >= eMax) {
|
148
|
-
m = 0
|
149
|
-
e = eMax
|
150
|
-
} else if (e + eBias >= 1) {
|
151
|
-
m = ((value * c) - 1) * Math.pow(2, mLen)
|
152
|
-
e = e + eBias
|
153
|
-
} else {
|
154
|
-
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
|
155
|
-
e = 0
|
156
|
-
}
|
157
|
-
}
|
158
|
-
|
159
|
-
while (mLen >= 8) {
|
160
|
-
buffer[offset + i] = m & 0xff
|
161
|
-
i += d
|
162
|
-
m /= 256
|
163
|
-
mLen -= 8
|
164
|
-
}
|
165
|
-
|
166
|
-
e = (e << mLen) | m
|
167
|
-
eLen += mLen
|
168
|
-
while (eLen > 0) {
|
169
|
-
buffer[offset + i] = e & 0xff
|
170
|
-
i += d
|
171
|
-
e /= 256
|
172
|
-
eLen -= 8
|
173
|
-
}
|
174
|
-
|
175
|
-
buffer[offset + i - d] |= s * 128
|
176
|
-
|
177
|
-
return buffer;
|
178
|
-
};
|
179
|
-
|
180
|
-
export const read_ieee754_binary64 = buffer => {
|
181
|
-
return new Float64Array(new Uint8Array(buffer).buffer)[0];
|
182
|
-
|
183
|
-
let isLE = true, mLen = 52, nBytes = 8, offset = 0;
|
184
|
-
|
185
|
-
let e, m
|
186
|
-
const eLen = (nBytes * 8) - mLen - 1
|
187
|
-
const eMax = (1 << eLen) - 1
|
188
|
-
const eBias = eMax >> 1
|
189
|
-
let nBits = -7
|
190
|
-
let i = isLE ? (nBytes - 1) : 0
|
191
|
-
const d = isLE ? -1 : 1
|
192
|
-
let s = buffer[offset + i]
|
193
|
-
|
194
|
-
i += d
|
195
|
-
|
196
|
-
e = s & ((1 << (-nBits)) - 1)
|
197
|
-
s >>= (-nBits)
|
198
|
-
nBits += eLen
|
199
|
-
while (nBits > 0) {
|
200
|
-
e = (e * 256) + buffer[offset + i]
|
201
|
-
i += d
|
202
|
-
nBits -= 8
|
203
|
-
}
|
204
|
-
|
205
|
-
m = e & ((1 << (-nBits)) - 1)
|
206
|
-
e >>= (-nBits)
|
207
|
-
nBits += mLen
|
208
|
-
while (nBits > 0) {
|
209
|
-
m = (m * 256) + buffer[offset + i]
|
210
|
-
i += d
|
211
|
-
nBits -= 8
|
212
|
-
}
|
213
|
-
|
214
|
-
if (e === 0) {
|
215
|
-
e = 1 - eBias
|
216
|
-
} else if (e === eMax) {
|
217
|
-
return m ? NaN : ((s ? -1 : 1) * Infinity)
|
218
|
-
} else {
|
219
|
-
m = m + Math.pow(2, mLen)
|
220
|
-
e = e - eBias
|
221
|
-
}
|
222
|
-
return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
|
223
|
-
};
|
108
|
+
export const ieee754_binary64 = value => [...new Uint8Array(new Float64Array([ value ]).buffer)];
|
109
|
+
export const read_ieee754_binary64 = buffer => new Float64Array(new Uint8Array(buffer).buffer)[0];
|
package/compiler/index.js
CHANGED
@@ -52,7 +52,7 @@ export default (code, flags) => {
|
|
52
52
|
if (process.argv.includes('-funcs')) logFuncs(funcs, globals, exceptions);
|
53
53
|
|
54
54
|
const t2 = performance.now();
|
55
|
-
opt(funcs, globals, pages);
|
55
|
+
opt(funcs, globals, pages, tags);
|
56
56
|
if (flags.includes('info')) console.log(`3. optimized code in ${(performance.now() - t2).toFixed(2)}ms`);
|
57
57
|
|
58
58
|
if (process.argv.includes('-opt-funcs')) logFuncs(funcs, globals, exceptions);
|
package/compiler/opt.js
CHANGED
@@ -11,7 +11,7 @@ const performWasmOp = (op, a, b) => {
|
|
11
11
|
}
|
12
12
|
};
|
13
13
|
|
14
|
-
export default (funcs, globals, pages) => {
|
14
|
+
export default (funcs, globals, pages, tags) => {
|
15
15
|
const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
|
16
16
|
if (optLevel === 0) return;
|
17
17
|
|
@@ -99,10 +99,14 @@ export default (funcs, globals, pages) => {
|
|
99
99
|
|
100
100
|
if (process.argv.includes('-opt-inline-only')) return;
|
101
101
|
|
102
|
+
const tagUse = tags.reduce((acc, x) => { acc[x.idx] = 0; return acc; }, {});
|
103
|
+
|
102
104
|
// wasm transform pass
|
103
105
|
for (const f of funcs) {
|
104
106
|
const wasm = f.wasm;
|
105
107
|
|
108
|
+
const lastType = f.locals['#last_type'];
|
109
|
+
|
106
110
|
let runs = 2; // how many by default? add arg?
|
107
111
|
while (runs > 0) {
|
108
112
|
runs--;
|
@@ -125,6 +129,8 @@ export default (funcs, globals, pages) => {
|
|
125
129
|
if (inst[0] === Opcodes.local_get) getCount[inst[1]]++;
|
126
130
|
if (inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) setCount[inst[1]]++;
|
127
131
|
|
132
|
+
if (inst[0] === Opcodes.throw) tagUse[inst[1]]++;
|
133
|
+
|
128
134
|
if (inst[0] === Opcodes.block) {
|
129
135
|
// remove unneeded blocks (no brs inside)
|
130
136
|
// block
|
@@ -141,7 +147,7 @@ export default (funcs, globals, pages) => {
|
|
141
147
|
depth--;
|
142
148
|
if (depth <= 0) break;
|
143
149
|
}
|
144
|
-
if (op === Opcodes.br || op === Opcodes.br_if) {
|
150
|
+
if (op === Opcodes.br || op === Opcodes.br_if || op === Opcodes.br_table) {
|
145
151
|
hasBranch = true;
|
146
152
|
break;
|
147
153
|
}
|
@@ -221,6 +227,7 @@ export default (funcs, globals, pages) => {
|
|
221
227
|
}
|
222
228
|
|
223
229
|
if (checks === 0) {
|
230
|
+
// todo: review indexes below
|
224
231
|
wasm.splice(j - 1, 2, [ Opcodes.drop ]); // remove typeswitch start
|
225
232
|
wasm.splice(i - 1, 1); // remove this inst
|
226
233
|
|
@@ -231,6 +238,13 @@ export default (funcs, globals, pages) => {
|
|
231
238
|
}
|
232
239
|
}
|
233
240
|
|
241
|
+
// remove setting last type if it is never gotten
|
242
|
+
if (!f.gotLastType && inst[0] === Opcodes.local_set && inst[1] === lastType?.idx) {
|
243
|
+
// replace this inst with drop
|
244
|
+
wasm.splice(i, 1, [ Opcodes.drop ]); // remove this and last inst
|
245
|
+
if (i > 0) i--;
|
246
|
+
}
|
247
|
+
|
234
248
|
if (i < 1) continue;
|
235
249
|
let lastInst = wasm[i - 1];
|
236
250
|
|
@@ -531,5 +545,12 @@ export default (funcs, globals, pages) => {
|
|
531
545
|
}
|
532
546
|
}
|
533
547
|
|
548
|
+
for (const x in tagUse) {
|
549
|
+
if (tagUse[x] === 0) {
|
550
|
+
const el = tags.find(y => y.idx === x);
|
551
|
+
tags.splice(tags.indexOf(el), 1);
|
552
|
+
}
|
553
|
+
}
|
554
|
+
|
534
555
|
// return funcs;
|
535
556
|
};
|
package/compiler/parse.js
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import { log } from "./log.js";
|
2
|
+
// import { parse } from 'acorn';
|
2
3
|
|
3
4
|
// deno compat
|
4
5
|
if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
|
@@ -6,16 +7,9 @@ if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
|
|
6
7
|
globalThis.process = { argv: ['', '', ...Deno.args], stdout: { write: str => Deno.writeAllSync(Deno.stdout, textEncoder.encode(str)) } };
|
7
8
|
}
|
8
9
|
|
9
|
-
//
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
const loadParser = async () => {
|
14
|
-
parser = process.argv.find(x => x.startsWith('-parser='))?.split('=')?.[1] ?? 'acorn';
|
15
|
-
0, { parse } = (await import((globalThis.document ? 'https://esm.sh/' : '') + parser));
|
16
|
-
};
|
17
|
-
globalThis._porf_loadParser = loadParser;
|
18
|
-
await loadParser();
|
10
|
+
// should we try to support types (while parsing)
|
11
|
+
const types = process.argv.includes('-parse-types');
|
12
|
+
globalThis.typedInput = types && process.argv.includes('-opt-types');
|
19
13
|
|
20
14
|
// todo: review which to use by default
|
21
15
|
// supported parsers:
|
@@ -24,8 +18,13 @@ await loadParser();
|
|
24
18
|
// - hermes-parser
|
25
19
|
// - @babel/parser
|
26
20
|
|
27
|
-
|
28
|
-
const
|
21
|
+
let parser, parse;
|
22
|
+
const loadParser = async (fallbackParser = 'acorn', forceParser) => {
|
23
|
+
parser = forceParser ?? process.argv.find(x => x.startsWith('-parser='))?.split('=')?.[1] ?? fallbackParser;
|
24
|
+
0, { parse } = (await import((globalThis.document ? 'https://esm.sh/' : '') + parser));
|
25
|
+
};
|
26
|
+
globalThis._porf_loadParser = loadParser;
|
27
|
+
await loadParser(types ? '@babel/parser' : undefined);
|
29
28
|
|
30
29
|
if (types && !['@babel/parser', 'hermes-parser'].includes(parser)) log.warning('parser', `passed -types with a parser (${parser}) which does not support`);
|
31
30
|
|
package/compiler/prototype.js
CHANGED
@@ -16,7 +16,8 @@ const TYPES = {
|
|
16
16
|
|
17
17
|
// these are not "typeof" types but tracked internally
|
18
18
|
_array: 0x10,
|
19
|
-
_regexp: 0x11
|
19
|
+
_regexp: 0x11,
|
20
|
+
_bytestring: 0x12
|
20
21
|
};
|
21
22
|
|
22
23
|
// todo: turn these into built-ins once arrays and these become less hacky
|
@@ -71,7 +72,7 @@ export const PrototypeFuncs = function() {
|
|
71
72
|
],
|
72
73
|
|
73
74
|
// todo: only for 1 argument
|
74
|
-
push: (pointer, length, wNewMember) => [
|
75
|
+
push: (pointer, length, wNewMember, _1, _2, _3, unusedValue) => [
|
75
76
|
// get memory offset of array at last index (length)
|
76
77
|
...length.getCachedI32(),
|
77
78
|
...number(ValtypeSize[valtype], Valtype.i32),
|
@@ -92,22 +93,28 @@ export const PrototypeFuncs = function() {
|
|
92
93
|
...number(1, Valtype.i32),
|
93
94
|
[ Opcodes.i32_add ],
|
94
95
|
|
95
|
-
...
|
96
|
-
|
96
|
+
...(unusedValue() ? [] : [
|
97
|
+
...length.setCachedI32(),
|
98
|
+
...length.getCachedI32(),
|
99
|
+
])
|
97
100
|
]),
|
98
101
|
|
99
|
-
...
|
100
|
-
|
102
|
+
...(unusedValue() ? [] : [
|
103
|
+
...length.getCachedI32(),
|
104
|
+
Opcodes.i32_from_u
|
105
|
+
])
|
101
106
|
|
102
107
|
// ...length.get()
|
103
108
|
],
|
104
109
|
|
105
|
-
pop: (pointer, length) => [
|
110
|
+
pop: (pointer, length, _1, _2, _3, _4, unusedValue) => [
|
106
111
|
// if length == 0, noop
|
107
112
|
...length.getCachedI32(),
|
108
113
|
[ Opcodes.i32_eqz ],
|
109
114
|
[ Opcodes.if, Blocktype.void ],
|
110
|
-
...
|
115
|
+
...(unusedValue() ? [] : [
|
116
|
+
...number(UNDEFINED),
|
117
|
+
]),
|
111
118
|
[ Opcodes.br, 1 ],
|
112
119
|
[ Opcodes.end ],
|
113
120
|
|
@@ -119,19 +126,23 @@ export const PrototypeFuncs = function() {
|
|
119
126
|
...number(1, Valtype.i32),
|
120
127
|
[ Opcodes.i32_sub ],
|
121
128
|
|
122
|
-
...
|
123
|
-
|
129
|
+
...(unusedValue() ? [] : [
|
130
|
+
...length.setCachedI32(),
|
131
|
+
...length.getCachedI32(),
|
132
|
+
])
|
124
133
|
]),
|
125
134
|
|
126
135
|
// load last element
|
127
|
-
...
|
128
|
-
|
129
|
-
|
136
|
+
...(unusedValue() ? [] : [
|
137
|
+
...length.getCachedI32(),
|
138
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
139
|
+
[ Opcodes.i32_mul ],
|
130
140
|
|
131
|
-
|
132
|
-
|
141
|
+
...pointer,
|
142
|
+
[ Opcodes.i32_add ],
|
133
143
|
|
134
|
-
|
144
|
+
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
|
145
|
+
])
|
135
146
|
],
|
136
147
|
|
137
148
|
shift: (pointer, length) => [
|
@@ -472,8 +483,152 @@ export const PrototypeFuncs = function() {
|
|
472
483
|
this[TYPES.string].at.returnType = TYPES.string;
|
473
484
|
this[TYPES.string].charAt.returnType = TYPES.string;
|
474
485
|
this[TYPES.string].charCodeAt.local = Valtype.i32;
|
486
|
+
this[TYPES.string].charCodeAt.noPointerCache = zeroChecks.charcodeat;
|
475
487
|
|
476
488
|
this[TYPES.string].isWellFormed.local = Valtype.i32;
|
477
489
|
this[TYPES.string].isWellFormed.local2 = Valtype.i32;
|
478
490
|
this[TYPES.string].isWellFormed.returnType = TYPES.boolean;
|
491
|
+
|
492
|
+
if (process.argv.includes('-bytestring')) {
|
493
|
+
this[TYPES._bytestring] = {
|
494
|
+
at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
|
495
|
+
const [ newOut, newPointer ] = arrayShell(1, 'i16');
|
496
|
+
|
497
|
+
return [
|
498
|
+
// setup new/out array
|
499
|
+
...newOut,
|
500
|
+
[ Opcodes.drop ],
|
501
|
+
|
502
|
+
...number(0, Valtype.i32), // base 0 for store later
|
503
|
+
|
504
|
+
...wIndex,
|
505
|
+
Opcodes.i32_to_u,
|
506
|
+
[ Opcodes.local_tee, iTmp ],
|
507
|
+
|
508
|
+
// if index < 0: access index + array length
|
509
|
+
...number(0, Valtype.i32),
|
510
|
+
[ Opcodes.i32_lt_s ],
|
511
|
+
[ Opcodes.if, Blocktype.void ],
|
512
|
+
[ Opcodes.local_get, iTmp ],
|
513
|
+
...length.getCachedI32(),
|
514
|
+
[ Opcodes.i32_add ],
|
515
|
+
[ Opcodes.local_set, iTmp ],
|
516
|
+
[ Opcodes.end ],
|
517
|
+
|
518
|
+
// if still < 0 or >= length: return undefined
|
519
|
+
[ Opcodes.local_get, iTmp ],
|
520
|
+
...number(0, Valtype.i32),
|
521
|
+
[ Opcodes.i32_lt_s ],
|
522
|
+
|
523
|
+
[ Opcodes.local_get, iTmp ],
|
524
|
+
...length.getCachedI32(),
|
525
|
+
[ Opcodes.i32_ge_s ],
|
526
|
+
[ Opcodes.i32_or ],
|
527
|
+
|
528
|
+
[ Opcodes.if, Blocktype.void ],
|
529
|
+
...number(UNDEFINED),
|
530
|
+
[ Opcodes.br, 1 ],
|
531
|
+
[ Opcodes.end ],
|
532
|
+
|
533
|
+
[ Opcodes.local_get, iTmp ],
|
534
|
+
|
535
|
+
...pointer,
|
536
|
+
[ Opcodes.i32_add ],
|
537
|
+
|
538
|
+
// load current string ind {arg}
|
539
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
540
|
+
|
541
|
+
// store to new string ind 0
|
542
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
543
|
+
|
544
|
+
// return new string (pointer)
|
545
|
+
...number(newPointer)
|
546
|
+
];
|
547
|
+
},
|
548
|
+
|
549
|
+
// todo: out of bounds properly
|
550
|
+
charAt: (pointer, length, wIndex, _1, _2, arrayShell) => {
|
551
|
+
const [ newOut, newPointer ] = arrayShell(1, 'i16');
|
552
|
+
|
553
|
+
return [
|
554
|
+
// setup new/out array
|
555
|
+
...newOut,
|
556
|
+
[ Opcodes.drop ],
|
557
|
+
|
558
|
+
...number(0, Valtype.i32), // base 0 for store later
|
559
|
+
|
560
|
+
...wIndex,
|
561
|
+
|
562
|
+
Opcodes.i32_to,
|
563
|
+
|
564
|
+
...pointer,
|
565
|
+
[ Opcodes.i32_add ],
|
566
|
+
|
567
|
+
// load current string ind {arg}
|
568
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
569
|
+
|
570
|
+
// store to new string ind 0
|
571
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
572
|
+
|
573
|
+
// return new string (page)
|
574
|
+
...number(newPointer)
|
575
|
+
];
|
576
|
+
},
|
577
|
+
|
578
|
+
charCodeAt: (pointer, length, wIndex, iTmp) => {
|
579
|
+
return [
|
580
|
+
...wIndex,
|
581
|
+
Opcodes.i32_to,
|
582
|
+
|
583
|
+
...(zeroChecks.charcodeat ? [] : [
|
584
|
+
[ Opcodes.local_set, iTmp ],
|
585
|
+
|
586
|
+
// index < 0
|
587
|
+
...(noUnlikelyChecks ? [] : [
|
588
|
+
[ Opcodes.local_get, iTmp ],
|
589
|
+
...number(0, Valtype.i32),
|
590
|
+
[ Opcodes.i32_lt_s ],
|
591
|
+
]),
|
592
|
+
|
593
|
+
// index >= length
|
594
|
+
[ Opcodes.local_get, iTmp ],
|
595
|
+
...length.getCachedI32(),
|
596
|
+
[ Opcodes.i32_ge_s ],
|
597
|
+
|
598
|
+
...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
|
599
|
+
[ Opcodes.if, Blocktype.void ],
|
600
|
+
...number(NaN),
|
601
|
+
[ Opcodes.br, 1 ],
|
602
|
+
[ Opcodes.end ],
|
603
|
+
|
604
|
+
[ Opcodes.local_get, iTmp ],
|
605
|
+
]),
|
606
|
+
|
607
|
+
...pointer,
|
608
|
+
[ Opcodes.i32_add ],
|
609
|
+
|
610
|
+
// load current string ind {arg}
|
611
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
612
|
+
Opcodes.i32_from_u
|
613
|
+
];
|
614
|
+
},
|
615
|
+
|
616
|
+
isWellFormed: () => {
|
617
|
+
return [
|
618
|
+
// we know it must be true as it is a bytestring lol
|
619
|
+
...number(1)
|
620
|
+
]
|
621
|
+
}
|
622
|
+
};
|
623
|
+
|
624
|
+
this[TYPES._bytestring].at.local = Valtype.i32;
|
625
|
+
this[TYPES._bytestring].at.returnType = TYPES._bytestring;
|
626
|
+
this[TYPES._bytestring].charAt.returnType = TYPES._bytestring;
|
627
|
+
this[TYPES._bytestring].charCodeAt.local = Valtype.i32;
|
628
|
+
this[TYPES._bytestring].charCodeAt.noPointerCache = zeroChecks.charcodeat;
|
629
|
+
|
630
|
+
this[TYPES._bytestring].isWellFormed.local = Valtype.i32;
|
631
|
+
this[TYPES._bytestring].isWellFormed.local2 = Valtype.i32;
|
632
|
+
this[TYPES._bytestring].isWellFormed.returnType = TYPES.boolean;
|
633
|
+
}
|
479
634
|
};
|
package/compiler/sections.js
CHANGED
@@ -150,7 +150,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
150
150
|
|
151
151
|
if (typeCount !== 0) localDecl.push(encodeLocal(typeCount, lastType));
|
152
152
|
|
153
|
-
return encodeVector([ ...encodeVector(localDecl), ...x.wasm.flat().filter(x => x <= 0xff), Opcodes.end ]);
|
153
|
+
return encodeVector([ ...encodeVector(localDecl), ...x.wasm.flat().filter(x => x != null && x <= 0xff), Opcodes.end ]);
|
154
154
|
}))
|
155
155
|
);
|
156
156
|
|
package/compiler/wasmSpec.js
CHANGED
@@ -32,8 +32,6 @@ export const Opcodes = {
|
|
32
32
|
throw: 0x08,
|
33
33
|
rethrow: 0x09,
|
34
34
|
|
35
|
-
return: 0x0F,
|
36
|
-
|
37
35
|
call: 0x10,
|
38
36
|
call_indirect: 0x11,
|
39
37
|
return_call: 0x12,
|
@@ -42,7 +40,10 @@ export const Opcodes = {
|
|
42
40
|
end: 0x0b,
|
43
41
|
br: 0x0c,
|
44
42
|
br_if: 0x0d,
|
43
|
+
br_table: 0x0e,
|
44
|
+
return: 0x0f,
|
45
45
|
call: 0x10,
|
46
|
+
|
46
47
|
drop: 0x1a,
|
47
48
|
|
48
49
|
local_get: 0x20,
|
@@ -56,9 +57,12 @@ export const Opcodes = {
|
|
56
57
|
i64_load: 0x29,
|
57
58
|
f64_load: 0x2b,
|
58
59
|
|
60
|
+
i32_load8_s: 0x2c,
|
61
|
+
i32_load8_u: 0x2d,
|
59
62
|
i32_load16_s: 0x2e,
|
60
63
|
i32_load16_u: 0x2f,
|
61
64
|
|
65
|
+
i32_store8: 0x3a,
|
62
66
|
i32_store16: 0x3b,
|
63
67
|
|
64
68
|
i32_store: 0x36,
|
package/compiler/wrap.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import compile from './index.js';
|
2
2
|
import decompile from './decompile.js';
|
3
|
+
import { encodeVector, encodeLocal } from './encoding.js';
|
3
4
|
// import fs from 'node:fs';
|
4
5
|
|
5
6
|
const bold = x => `\u001b[1m${x}\u001b[0m`;
|
@@ -18,7 +19,8 @@ const TYPES = {
|
|
18
19
|
|
19
20
|
// internal
|
20
21
|
[internalTypeBase]: '_array',
|
21
|
-
[internalTypeBase + 1]: '_regexp'
|
22
|
+
[internalTypeBase + 1]: '_regexp',
|
23
|
+
[internalTypeBase + 2]: '_bytestring'
|
22
24
|
};
|
23
25
|
|
24
26
|
export default async (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
|
@@ -35,14 +37,95 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
35
37
|
if (flags.includes('info')) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
|
36
38
|
|
37
39
|
const t2 = performance.now();
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
|
41
|
+
let instance;
|
42
|
+
try {
|
43
|
+
0, { instance } = await WebAssembly.instantiate(wasm, {
|
44
|
+
'': {
|
45
|
+
p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
|
46
|
+
c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
|
47
|
+
t: _ => performance.now(),
|
48
|
+
...customImports
|
49
|
+
}
|
50
|
+
});
|
51
|
+
} catch (e) {
|
52
|
+
const funcInd = parseInt(e.message.match(/function #([0-9]+) /)[1]);
|
53
|
+
const blobOffset = parseInt(e.message.split('@')[1]);
|
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
131
|
if (flags.includes('info')) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
|
@@ -95,6 +178,13 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
95
178
|
return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
96
179
|
}
|
97
180
|
|
181
|
+
case '_bytestring': {
|
182
|
+
const pointer = ret;
|
183
|
+
const length = new Int32Array(memory.buffer, pointer, 1);
|
184
|
+
|
185
|
+
return Array.from(new Uint8Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
186
|
+
}
|
187
|
+
|
98
188
|
case 'function': {
|
99
189
|
// wasm func index, including all imports
|
100
190
|
const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
|