porffor 0.0.0-579ef36

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.
@@ -0,0 +1,102 @@
1
+ import { Blocktype, Opcodes, Valtype } from "./wasmSpec.js";
2
+ import { read_ieee754_binary64, read_signedLEB128, read_unsignedLEB128 } from "./encoding.js";
3
+
4
+ const inv = (obj, keyMap = x => x) => Object.keys(obj).reduce((acc, x) => { acc[keyMap(obj[x])] = x; return acc; }, {});
5
+ const invOpcodes = inv(Opcodes);
6
+ const invValtype = inv(Valtype);
7
+
8
+ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = [], funcs = [], globals = {}, exceptions = []) => {
9
+ const invLocals = inv(locals, x => x.idx);
10
+ const invGlobals = inv(globals, x => x.idx);
11
+
12
+ const makeSignature = (params, returns) => `(${params.map(x => invValtype[x]).join(', ')}) -> (${returns.map(x => invValtype[x]).join(', ')})`;
13
+
14
+ let out = '', depth = name ? 1 : 0;
15
+ if (name) out += `${makeSignature(params, returns)} ;; $${name} (${ind})\n`;
16
+
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`;
19
+
20
+ let i = 0, lastInst;
21
+ for (let inst of wasm.concat(name ? [ [ Opcodes.end ] ] : [])) {
22
+ if (inst[0] === null) continue;
23
+
24
+ if (inst[0] === 0xfd) { // simd inst prefix
25
+ if (inst[1] >= 128) inst = [ [ inst[0], inst[1], inst[2] ], ...inst.slice(3) ];
26
+ else inst = [ [ inst[0], inst[1] ], ...inst.slice(2) ];
27
+ }
28
+
29
+ if (inst[0] === 0xfc) { // misc inst prefix
30
+ inst = [ [ inst[0], inst[1] ], ...inst.slice(2) ];
31
+ }
32
+
33
+ if (inst[0] === Opcodes.end || inst[0] === Opcodes.else || inst[0] === Opcodes.catch_all) depth--;
34
+
35
+ const opStr = invOpcodes[inst[0]];
36
+ if (!opStr) console.log(`decomp: unknown op ${inst[0].toString(16)}`)
37
+ out += /* ' '.repeat(3 - i.toString().length) + i + ' ' + */ ' '.repeat(Math.max(0, depth * 2)) + opStr.replace('_', '.').replace('return.', 'return_').replace('call.', 'call_').replace('br.', 'br_');
38
+
39
+ if (inst[0] === Opcodes.if || inst[0] === Opcodes.loop || inst[0] === Opcodes.block || inst[0] === Opcodes.else || inst[0] === Opcodes.try || inst[0] === Opcodes.catch_all) depth++;
40
+
41
+ if (inst[0] === Opcodes.f64_const) {
42
+ out += ` ${read_ieee754_binary64(inst.slice(1))}`;
43
+ } else if (inst[0] === Opcodes.i32_const || inst[0] === Opcodes.i64_const) {
44
+ out += ` ${read_signedLEB128(inst.slice(1))}`;
45
+ } else if (inst[0] === Opcodes.i32_load || inst[0] === Opcodes.i64_load || inst[0] === Opcodes.f64_load || inst[0] === Opcodes.i32_store || inst[0] === Opcodes.i64_store || inst[0] === Opcodes.f64_store) {
46
+ out += ` ${inst[1]} ${read_unsignedLEB128(inst.slice(2))}`
47
+ } else for (const operand of inst.slice(1)) {
48
+ if (inst[0] === Opcodes.if || inst[0] === Opcodes.loop || inst[0] === Opcodes.block) {
49
+ if (operand === Blocktype.void) continue;
50
+ out += ` ${invValtype[operand]}`;
51
+ } else {
52
+ out += ` ${operand}`;
53
+ }
54
+ }
55
+
56
+ if (inst[0] === Opcodes.if || inst[0] === Opcodes.loop || inst[0] === Opcodes.block || inst[0] === Opcodes.else) {
57
+ out += ` ;; label @${depth}`;
58
+ }
59
+
60
+ if (inst[0] === Opcodes.br) {
61
+ out += ` ;; goto @${depth - inst[1]}`;
62
+ }
63
+
64
+ if (inst[0] === Opcodes.call || inst[0] === Opcodes.return_call) {
65
+ const callFunc = funcs.find(x => x.index === inst[1]);
66
+ if (callFunc) out += ` ;; $${callFunc.name} ${makeSignature(callFunc.params, callFunc.returns)}`;
67
+ }
68
+
69
+ if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) {
70
+ const name = invLocals[inst[1]];
71
+ const type = invValtype[locals[name]?.type];
72
+ if (name) out += ` ;; $${name}${type !== valtype ? ` (${type})` : ''}`;
73
+ }
74
+
75
+ if (inst[0] === Opcodes.global_get || inst[0] === Opcodes.global_set) {
76
+ const name = invGlobals[inst[1]];
77
+ const type = invValtype[globals[name]?.type];
78
+ if (name) out += ` ;; $${name}${type !== valtype ? ` (${type})` : ''}`;
79
+ }
80
+
81
+ if (inst[0] === Opcodes.throw && lastInst && exceptions) {
82
+ const exception = exceptions[lastInst[1]];
83
+ if (exception) out += ` ;; ${exception.constructor ? `${exception.constructor}('${exception.message}')` : `'${exception.message}'`}`;
84
+ }
85
+
86
+ out += '\n';
87
+ lastInst = inst;
88
+ i++;
89
+ }
90
+
91
+ return highlightAsm(out);
92
+ };
93
+
94
+ export const highlightAsm = asm =>
95
+ asm
96
+ .replace(/(local|global|memory)\.[^\s]*/g, _ => `\x1B[31m${_}\x1B[0m`)
97
+ .replace(/(i(8|16|32|64)x[0-9]+|v128)(\.[^\s]*)?/g, _ => `\x1B[34m${_}\x1B[0m`)
98
+ .replace(/[^m](i32|i64|f32|f64)(\.[^\s]*)?/g, _ => `${_[0]}\x1B[36m${_.slice(1)}\x1B[0m`)
99
+ .replace(/(return_call|call|br_if|br|return|throw|rethrow)/g, _ => `\x1B[35m${_}\x1B[0m`)
100
+ .replace(/(block|loop|if|end|else|try|catch|catch_all|delegate)/g, _ => `\x1B[95m${_}\x1B[0m`)
101
+ .replace(/ \-?[0-9\.]+/g, _ => ` \x1B[33m${_.slice(1)}\x1B[0m`)
102
+ .replace(/ ;;.*$/gm, _ => `\x1B[90m${_.replaceAll(/\x1B\[[0-9]+m/g, '')}\x1B[0m`);
@@ -0,0 +1,19 @@
1
+ import { Opcodes, Valtype } from "./wasmSpec.js";
2
+ import { signedLEB128, ieee754_binary64 } from "./encoding.js";
3
+
4
+ export const number = (n, valtype = valtypeBinary) => {
5
+ switch (valtype) {
6
+ case Valtype.i32: return [ [ Opcodes.i32_const, ...signedLEB128(n) ] ];
7
+ case Valtype.i64: return [ [ Opcodes.i64_const, ...signedLEB128(n) ] ];
8
+ case Valtype.f64: return [ [ Opcodes.f64_const, ...ieee754_binary64(n) ] ];
9
+ }
10
+ };
11
+
12
+ const enforceTwoBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0, arr[2] ?? 0, arr[3] ?? 0 ];
13
+ export const i32x4 = (a, b, c, d) => [ [
14
+ ...Opcodes.v128_const,
15
+ ...enforceTwoBytes(signedLEB128(a)),
16
+ ...enforceTwoBytes(signedLEB128(b)),
17
+ ...enforceTwoBytes(signedLEB128(c)),
18
+ ...enforceTwoBytes(signedLEB128(d))
19
+ ] ];
@@ -0,0 +1,217 @@
1
+ export const codifyString = str => {
2
+ let out = [];
3
+ for (let i = 0; i < str.length; i++) {
4
+ out.push(str.charCodeAt(i));
5
+ }
6
+
7
+ return out;
8
+ };
9
+
10
+ export const encodeString = str => [
11
+ str.length,
12
+ ...codifyString(str)
13
+ ];
14
+
15
+ export const encodeVector = data => [
16
+ ...unsignedLEB128(data.length),
17
+ ...data.flat()
18
+ ];
19
+
20
+ export const encodeLocal = (count, type) => [
21
+ ...unsignedLEB128(count),
22
+ type
23
+ ];
24
+
25
+ export const signedLEB128 = n => {
26
+ // todo: this only works with integers within 32 bit range
27
+
28
+ // just input for small numbers (for perf as common)
29
+ if (n >= 0 && n <= 63) return [ n ];
30
+ if (n >= -64 && n <= 0) return [ 128 + n ];
31
+
32
+ const buffer = [];
33
+ n |= 0;
34
+
35
+ while (true) {
36
+ let byte = n & 0x7f;
37
+ n >>= 7;
38
+
39
+ if ((n === 0 && (byte & 0x40) === 0) || (n === -1 && (byte & 0x40) !== 0)) {
40
+ buffer.push(byte);
41
+ break;
42
+ } else {
43
+ byte |= 0x80;
44
+ }
45
+
46
+ buffer.push(byte);
47
+ }
48
+
49
+ return buffer;
50
+ };
51
+
52
+ export const unsignedLEB128 = n => {
53
+ // just input for small numbers (for perf as common)
54
+ if (n >= 0 && n <= 127) return [ n ];
55
+
56
+ const buffer = [];
57
+ do {
58
+ let byte = n & 0x7f;
59
+ n >>>= 7;
60
+ if (n !== 0) {
61
+ byte |= 0x80;
62
+ }
63
+ buffer.push(byte);
64
+ } while (n !== 0);
65
+ return buffer;
66
+ };
67
+
68
+ export const read_signedLEB128 = _input => {
69
+ const input = [..._input];
70
+ let result = 0, shift = 0;
71
+
72
+ while (true) {
73
+ const byte = input.shift();
74
+ result |= (byte & 0x7f) << shift;
75
+
76
+ shift += 7;
77
+
78
+ if ((0x80 & byte) === 0) {
79
+ if (shift < 32 && (byte & 0x40) !== 0) {
80
+ return result | (-1 << shift);
81
+ }
82
+
83
+ return result;
84
+ }
85
+ }
86
+ };
87
+
88
+ // todo: check this with large unsigned values
89
+ export const read_unsignedLEB128 = _input => {
90
+ const input = [..._input];
91
+ let result = 0, shift = 0;
92
+
93
+ while (true) {
94
+ const byte = input.shift();
95
+ result |= (byte & 0x7f) << shift;
96
+
97
+ shift += 7;
98
+
99
+ if ((0x80 & byte) === 0) {
100
+ return result;
101
+ }
102
+ }
103
+ };
104
+
105
+ // ieee 754 binary64
106
+
107
+ // from https://github.com/feross/ieee754
108
+ // BSD 3-Clause. Copyright 2008 Fair Oaks Labs, Inc. (https://github.com/feross/ieee754/blob/master/LICENSE)
109
+ export const ieee754_binary64 = value => {
110
+ let isLE = true, mLen = 52, nBytes = 8, offset = 0;
111
+ let buffer = new Array(nBytes).fill(0);
112
+
113
+ let e, m, c
114
+ let eLen = (nBytes * 8) - mLen - 1
115
+ const eMax = (1 << eLen) - 1
116
+ const eBias = eMax >> 1
117
+ const rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
118
+ let i = isLE ? 0 : (nBytes - 1)
119
+ const d = isLE ? 1 : -1
120
+ const s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
121
+
122
+ value = Math.abs(value)
123
+
124
+ if (isNaN(value) || value === Infinity) {
125
+ m = isNaN(value) ? 1 : 0
126
+ e = eMax
127
+ } else {
128
+ e = Math.floor(Math.log(value) / Math.LN2)
129
+ if (value * (c = Math.pow(2, -e)) < 1) {
130
+ e--
131
+ c *= 2
132
+ }
133
+ if (e + eBias >= 1) {
134
+ value += rt / c
135
+ } else {
136
+ value += rt * Math.pow(2, 1 - eBias)
137
+ }
138
+ if (value * c >= 2) {
139
+ e++
140
+ c /= 2
141
+ }
142
+
143
+ if (e + eBias >= eMax) {
144
+ m = 0
145
+ e = eMax
146
+ } else if (e + eBias >= 1) {
147
+ m = ((value * c) - 1) * Math.pow(2, mLen)
148
+ e = e + eBias
149
+ } else {
150
+ m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
151
+ e = 0
152
+ }
153
+ }
154
+
155
+ while (mLen >= 8) {
156
+ buffer[offset + i] = m & 0xff
157
+ i += d
158
+ m /= 256
159
+ mLen -= 8
160
+ }
161
+
162
+ e = (e << mLen) | m
163
+ eLen += mLen
164
+ while (eLen > 0) {
165
+ buffer[offset + i] = e & 0xff
166
+ i += d
167
+ e /= 256
168
+ eLen -= 8
169
+ }
170
+
171
+ buffer[offset + i - d] |= s * 128
172
+
173
+ return buffer;
174
+ };
175
+
176
+ export const read_ieee754_binary64 = buffer => {
177
+ let isLE = true, mLen = 52, nBytes = 8, offset = 0;
178
+
179
+ let e, m
180
+ const eLen = (nBytes * 8) - mLen - 1
181
+ const eMax = (1 << eLen) - 1
182
+ const eBias = eMax >> 1
183
+ let nBits = -7
184
+ let i = isLE ? (nBytes - 1) : 0
185
+ const d = isLE ? -1 : 1
186
+ let s = buffer[offset + i]
187
+
188
+ i += d
189
+
190
+ e = s & ((1 << (-nBits)) - 1)
191
+ s >>= (-nBits)
192
+ nBits += eLen
193
+ while (nBits > 0) {
194
+ e = (e * 256) + buffer[offset + i]
195
+ i += d
196
+ nBits -= 8
197
+ }
198
+
199
+ m = e & ((1 << (-nBits)) - 1)
200
+ e >>= (-nBits)
201
+ nBits += mLen
202
+ while (nBits > 0) {
203
+ m = (m * 256) + buffer[offset + i]
204
+ i += d
205
+ nBits -= 8
206
+ }
207
+
208
+ if (e === 0) {
209
+ e = 1 - eBias
210
+ } else if (e === eMax) {
211
+ return m ? NaN : ((s ? -1 : 1) * Infinity)
212
+ } else {
213
+ m = m + Math.pow(2, mLen)
214
+ e = e - eBias
215
+ }
216
+ return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
217
+ };
@@ -0,0 +1,70 @@
1
+ import { Opcodes } from "./wasmSpec.js";
2
+
3
+ export const operatorOpcode = {
4
+ i32: {
5
+ '+': Opcodes.i32_add,
6
+ '-': Opcodes.i32_sub,
7
+ '*': Opcodes.i32_mul,
8
+ '/': Opcodes.i32_div_s,
9
+ '%': Opcodes.i32_rem_s,
10
+
11
+ '&': Opcodes.i32_and,
12
+ '|': Opcodes.i32_or,
13
+ '^': Opcodes.i32_xor,
14
+ '<<': Opcodes.i32_shl,
15
+ '>>': Opcodes.i32_shr_s,
16
+ '>>>': Opcodes.i32_shr_u,
17
+
18
+ '==': Opcodes.i32_eq,
19
+ '===': Opcodes.i32_eq,
20
+ '!=': Opcodes.i32_ne,
21
+ '!==': Opcodes.i32_ne,
22
+
23
+ '>': Opcodes.i32_gt_s,
24
+ '>=': Opcodes.i32_ge_s,
25
+ '<': Opcodes.i32_lt_s,
26
+ '<=': Opcodes.i32_le_s
27
+ },
28
+
29
+ i64: {
30
+ '+': Opcodes.i64_add,
31
+ '-': Opcodes.i64_sub,
32
+ '*': Opcodes.i64_mul,
33
+ '/': Opcodes.i64_div_s,
34
+ '%': Opcodes.i64_rem_s,
35
+
36
+ '&': Opcodes.i64_and,
37
+ '|': Opcodes.i64_or,
38
+ '^': Opcodes.i64_xor,
39
+ '<<': Opcodes.i64_shl,
40
+ '>>': Opcodes.i64_shr_s,
41
+ '>>>': Opcodes.i64_shr_u,
42
+
43
+ '==': Opcodes.i64_eq,
44
+ '===': Opcodes.i64_eq,
45
+ '!=': Opcodes.i64_ne,
46
+ '!==': Opcodes.i64_ne,
47
+
48
+ '>': Opcodes.i64_gt_s,
49
+ '>=': Opcodes.i64_ge_s,
50
+ '<': Opcodes.i64_lt_s,
51
+ '<=': Opcodes.i64_le_s
52
+ },
53
+
54
+ f64: {
55
+ '+': Opcodes.f64_add,
56
+ '-': Opcodes.f64_sub,
57
+ '*': Opcodes.f64_mul,
58
+ '/': Opcodes.f64_div,
59
+
60
+ '==': Opcodes.f64_eq,
61
+ '===': Opcodes.f64_eq,
62
+ '!=': Opcodes.f64_ne,
63
+ '!==': Opcodes.f64_ne,
64
+
65
+ '>': Opcodes.f64_gt,
66
+ '>=': Opcodes.f64_ge,
67
+ '<': Opcodes.f64_lt,
68
+ '<=': Opcodes.f64_le
69
+ }
70
+ };
@@ -0,0 +1,67 @@
1
+ import parse from './parse.js';
2
+ import codeGen from './codeGen.js';
3
+ import opt from './opt.js';
4
+ import produceSections from './sections.js';
5
+ import decompile from './decompile.js';
6
+ import { BuiltinPreludes } from './builtins.js';
7
+
8
+ globalThis.decompile = decompile;
9
+
10
+ const rgb = (r, g, b, x) => `\x1b[38;2;${r};${g};${b}m${x}\u001b[0m`;
11
+ const underline = x => `\u001b[4m${x}\u001b[0m`;
12
+ const bold = x => `\u001b[1m${x}\u001b[0m`;
13
+
14
+ const areaColors = {
15
+ codegen: [ 20, 80, 250 ],
16
+ opt: [ 250, 20, 80 ],
17
+ sections: [ 20, 250, 80 ]
18
+ };
19
+
20
+ globalThis.log = (area, ...args) => console.log(`\u001b[90m[\u001b[0m${rgb(...areaColors[area], area)}\u001b[90m]\u001b[0m`, ...args);
21
+
22
+ const logFuncs = (funcs, globals, exceptions) => {
23
+ console.log('\n' + underline(bold('funcs')));
24
+
25
+ for (const f of funcs) {
26
+ console.log(`${underline(f.name)} (${f.index})`);
27
+
28
+ console.log(`params: ${f.params.map((_, i) => Object.keys(f.locals)[Object.values(f.locals).indexOf(Object.values(f.locals).find(x => x.idx === i))]).join(', ')}`);
29
+ console.log(`returns: ${f.returns.length > 0 ? true : false}`);
30
+ console.log(`locals: ${Object.keys(f.locals).sort((a, b) => f.locals[a].idx - f.locals[b].idx).map(x => `${x} (${f.locals[x].idx})`).join(', ')}`);
31
+ console.log();
32
+ console.log(decompile(f.wasm, f.name, f.index, f.locals, f.params, f.returns, funcs, globals, exceptions));
33
+ }
34
+
35
+ console.log();
36
+ };
37
+
38
+ export default (code, flags) => {
39
+ globalThis.optLog = process.argv.includes('-opt-log');
40
+ globalThis.codeLog = process.argv.includes('-code-log');
41
+
42
+ for (const x in BuiltinPreludes) {
43
+ if (code.indexOf(x + '(') !== -1) code = BuiltinPreludes[x] + code;
44
+ }
45
+
46
+ const t0 = performance.now();
47
+ const program = parse(code, flags);
48
+ if (flags.includes('info')) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
49
+
50
+ const t1 = performance.now();
51
+ const { funcs, globals, tags, exceptions, pages } = codeGen(program);
52
+ if (flags.includes('info')) console.log(`2. generated code in ${(performance.now() - t1).toFixed(2)}ms`);
53
+
54
+ if (process.argv.includes('-funcs')) logFuncs(funcs, globals, exceptions);
55
+
56
+ const t2 = performance.now();
57
+ opt(funcs, globals);
58
+ if (flags.includes('info')) console.log(`3. optimized code in ${(performance.now() - t2).toFixed(2)}ms`);
59
+
60
+ if (process.argv.includes('-opt-funcs')) logFuncs(funcs, globals, exceptions);
61
+
62
+ const t3 = performance.now();
63
+ const sections = produceSections(funcs, globals, tags, pages, flags);
64
+ if (flags.includes('info')) console.log(`4. produced sections in ${(performance.now() - t3).toFixed(2)}ms`);
65
+
66
+ return { wasm: sections, funcs, globals, tags, exceptions, pages };
67
+ };