porffor 0.14.0-f67c123a1 → 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.
- package/CONTRIBUTING.md +15 -9
- package/README.md +9 -13
- package/asur/index.js +1 -1
- package/compiler/2c.js +104 -51
- package/compiler/assemble.js +30 -4
- package/compiler/builtins/annexb_string.ts +1 -0
- package/compiler/builtins/array.ts +84 -4
- package/compiler/builtins/base64.ts +1 -0
- package/compiler/builtins/boolean.ts +3 -1
- package/compiler/builtins/console.ts +6 -0
- package/compiler/builtins/crypto.ts +1 -0
- package/compiler/builtins/date.ts +2 -0
- package/compiler/builtins/error.js +22 -0
- package/compiler/builtins/escape.ts +1 -2
- package/compiler/builtins/function.ts +2 -0
- package/compiler/builtins/int.ts +2 -0
- package/compiler/builtins/math.ts +410 -0
- package/compiler/builtins/number.ts +2 -0
- package/compiler/builtins/object.ts +2 -0
- package/compiler/builtins/porffor.d.ts +11 -0
- package/compiler/builtins/set.ts +20 -7
- package/compiler/builtins/string.ts +1 -0
- package/compiler/builtins/symbol.ts +62 -0
- package/compiler/builtins.js +50 -12
- package/compiler/codegen.js +576 -276
- package/compiler/cyclone.js +535 -0
- package/compiler/decompile.js +7 -1
- package/compiler/generated_builtins.js +555 -60
- package/compiler/havoc.js +93 -0
- package/compiler/index.js +80 -14
- package/compiler/opt.js +3 -39
- package/compiler/parse.js +2 -2
- package/compiler/pgo.js +206 -0
- package/compiler/precompile.js +5 -4
- package/compiler/prefs.js +12 -3
- package/compiler/prototype.js +180 -157
- package/compiler/wrap.js +127 -44
- package/no_pgo.txt +923 -0
- package/package.json +1 -1
- package/pgo.txt +916 -0
- package/runner/index.js +18 -12
- package/runner/repl.js +18 -2
- /package/runner/{profiler.js → profile.js} +0 -0
package/compiler/wrap.js
CHANGED
@@ -6,9 +6,26 @@ 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
|
|
11
|
-
const
|
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
|
+
|
28
|
+
const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
|
12
29
|
switch (type) {
|
13
30
|
case TYPES.boolean: return Boolean(value);
|
14
31
|
case TYPES.undefined: return undefined;
|
@@ -41,9 +58,22 @@ const porfToJSValue = (memory, funcs, value, type) => {
|
|
41
58
|
case TYPES.array: {
|
42
59
|
const length = (new Int32Array(memory.buffer, value, 1))[0];
|
43
60
|
|
44
|
-
|
45
|
-
|
46
|
-
|
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: {
|
@@ -66,66 +96,53 @@ const porfToJSValue = (memory, funcs, value, type) => {
|
|
66
96
|
// console.log(' memory:', Array.from(new Uint8Array(memory.buffer, offset, 9)).map(x => x.toString(16).padStart(2, '0')).join(' '));
|
67
97
|
// console.log(' read:', { value: v, type: t }, '\n');
|
68
98
|
|
69
|
-
out.add(porfToJSValue(memory, funcs, v, t));
|
99
|
+
out.add(porfToJSValue({ memory, funcs, pages }, v, t));
|
70
100
|
}
|
71
101
|
|
72
102
|
return out;
|
73
103
|
}
|
74
104
|
|
105
|
+
case TYPES.symbol: {
|
106
|
+
const descStore = pages.get('bytestring: __Porffor_symbol_descStore/ptr').ind * pageSize;
|
107
|
+
const offset = descStore + 4 + ((value - 1) * 9);
|
108
|
+
|
109
|
+
const v = (new Float64Array(memory.buffer.slice(offset, offset + 8), 0, 1))[0];
|
110
|
+
const t = (new Uint8Array(memory.buffer, offset + 8, 1))[0];
|
111
|
+
|
112
|
+
const desc = porfToJSValue({ memory, funcs, pages }, v, t);
|
113
|
+
|
114
|
+
return Symbol(desc);
|
115
|
+
}
|
116
|
+
|
75
117
|
default: return value;
|
76
118
|
}
|
77
119
|
};
|
78
120
|
|
79
|
-
export default
|
121
|
+
export default (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
|
80
122
|
const times = [];
|
81
123
|
|
82
124
|
const t1 = performance.now();
|
83
|
-
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);
|
84
126
|
|
85
127
|
globalThis.porfDebugInfo = { funcs, globals };
|
86
128
|
|
87
|
-
if (source.includes('export
|
129
|
+
if (source.includes?.('export ')) flags.push('module');
|
88
130
|
|
89
|
-
|
131
|
+
fs.writeFileSync('out.wasm', Buffer.from(wasm));
|
90
132
|
|
91
133
|
times.push(performance.now() - t1);
|
92
134
|
if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
|
93
135
|
|
94
|
-
const
|
95
|
-
|
96
|
-
|
97
|
-
try {
|
98
|
-
let wasmEngine = WebAssembly;
|
99
|
-
if (Prefs.asur) {
|
100
|
-
log.warning('wrap', 'using our !experimental! asur wasm engine instead of host to run');
|
101
|
-
wasmEngine = await import('../asur/index.js');
|
102
|
-
}
|
103
|
-
|
104
|
-
0, { instance } = await wasmEngine.instantiate(wasm, {
|
105
|
-
'': {
|
106
|
-
p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
|
107
|
-
c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
|
108
|
-
t: () => performance.now(),
|
109
|
-
u: () => performance.timeOrigin,
|
110
|
-
y: () => {},
|
111
|
-
z: () => {},
|
112
|
-
...customImports
|
113
|
-
}
|
114
|
-
});
|
115
|
-
} catch (e) {
|
116
|
-
// only backtrace for runner, not test262/etc
|
117
|
-
if (!process.argv[1].includes('/runner')) throw e;
|
118
|
-
|
119
|
-
const funcInd = parseInt(e.message.match(/function #([0-9]+) /)?.[1]);
|
120
|
-
const blobOffset = parseInt(e.message.split('@')?.[1]);
|
121
|
-
|
122
|
-
if (!funcInd) throw e;
|
136
|
+
const backtrace = (funcInd, blobOffset) => {
|
137
|
+
if (funcInd == null || blobOffset == null ||
|
138
|
+
Number.isNaN(funcInd) || Number.isNaN(blobOffset)) return false;
|
123
139
|
|
124
140
|
// convert blob offset -> function wasm offset.
|
125
141
|
// this is not good code and is somewhat duplicated
|
126
142
|
// I just want it to work for debugging, I don't care about perf/yes
|
127
|
-
|
128
143
|
const func = funcs.find(x => x.index === funcInd);
|
144
|
+
if (!func) return false;
|
145
|
+
|
129
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);
|
130
147
|
|
131
148
|
let localDecl = [], typeCount = 0, lastType;
|
@@ -142,7 +159,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
142
159
|
|
143
160
|
if (typeCount !== 0) localDecl.push(encodeLocal(typeCount, lastType));
|
144
161
|
|
145
|
-
const toFind = encodeVector(localDecl).concat(func.wasm.flat().filter(x => x != null && x <= 0xff).slice(0,
|
162
|
+
const toFind = encodeVector(localDecl).concat(func.wasm.flat().filter(x => x != null && x <= 0xff).slice(0, 60));
|
146
163
|
|
147
164
|
let i = 0;
|
148
165
|
for (; i < wasm.length; i++) {
|
@@ -157,7 +174,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
157
174
|
if (!mismatch) break;
|
158
175
|
}
|
159
176
|
|
160
|
-
if (i === wasm.length)
|
177
|
+
if (i === wasm.length) return false;
|
161
178
|
|
162
179
|
const offset = (blobOffset - i) + encodeVector(localDecl).length;
|
163
180
|
|
@@ -168,7 +185,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
168
185
|
if (cumLen === offset) break;
|
169
186
|
}
|
170
187
|
|
171
|
-
if (cumLen !== offset)
|
188
|
+
if (cumLen !== offset) return false;
|
172
189
|
|
173
190
|
i -= 1;
|
174
191
|
|
@@ -193,6 +210,59 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
193
210
|
console.log(decomp.join('\n'));
|
194
211
|
console.log('\x1B[90m...\x1B[0m\n');
|
195
212
|
|
213
|
+
return true;
|
214
|
+
};
|
215
|
+
|
216
|
+
const t2 = performance.now();
|
217
|
+
|
218
|
+
let instance;
|
219
|
+
try {
|
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, {
|
229
|
+
'': {
|
230
|
+
p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
|
231
|
+
c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
|
232
|
+
t: () => performance.now(),
|
233
|
+
u: () => performance.timeOrigin,
|
234
|
+
y: () => {},
|
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
|
+
},
|
254
|
+
...customImports
|
255
|
+
}
|
256
|
+
});
|
257
|
+
} catch (e) {
|
258
|
+
// only backtrace for runner, not test262/etc
|
259
|
+
if (!process.argv[1].includes('/runner')) throw e;
|
260
|
+
if (!(e instanceof WebAssembly.CompileError)) throw e;
|
261
|
+
|
262
|
+
const funcInd = parseInt(e.message.match(/function #([0-9]+) /)?.[1]);
|
263
|
+
const blobOffset = parseInt(e.message.split('@')?.[1]);
|
264
|
+
|
265
|
+
backtrace(funcInd, blobOffset);
|
196
266
|
throw e;
|
197
267
|
}
|
198
268
|
|
@@ -200,6 +270,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
200
270
|
if (Prefs.profileCompiler) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
|
201
271
|
|
202
272
|
const exports = {};
|
273
|
+
const rawValues = process.argv.includes('-i');
|
203
274
|
|
204
275
|
const exceptTag = instance.exports['0'], memory = instance.exports['$'];
|
205
276
|
for (const x in instance.exports) {
|
@@ -218,10 +289,11 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
218
289
|
exports[func.name] = function() {
|
219
290
|
try {
|
220
291
|
const ret = exp.apply(this, arguments);
|
221
|
-
|
222
292
|
if (ret == null) return undefined;
|
223
293
|
|
224
|
-
return porfToJSValue(memory, funcs, ret[0], ret[1]);
|
294
|
+
if (rawValues) return { value: ret[0], type: ret[1], js: porfToJSValue({ memory, funcs, pages }, ret[0], ret[1]) };
|
295
|
+
|
296
|
+
return porfToJSValue({ memory, funcs, pages }, ret[0], ret[1]);
|
225
297
|
} catch (e) {
|
226
298
|
if (e.is && e.is(exceptTag)) {
|
227
299
|
const exceptId = e.getArg(exceptTag, 0);
|
@@ -236,6 +308,17 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
236
308
|
throw new constructor(exception.message);
|
237
309
|
}
|
238
310
|
|
311
|
+
if (e instanceof WebAssembly.RuntimeError) {
|
312
|
+
// only backtrace for runner, not test262/etc
|
313
|
+
if (!process.argv[1].includes('/runner')) throw e;
|
314
|
+
|
315
|
+
const match = e.stack.match(/wasm-function\[([0-9]+)\]:([0-9a-z]+)/) ?? [];
|
316
|
+
const funcInd = parseInt(match[1]);
|
317
|
+
const blobOffset = parseInt(match[2]);
|
318
|
+
|
319
|
+
backtrace(funcInd, blobOffset);
|
320
|
+
}
|
321
|
+
|
239
322
|
throw e;
|
240
323
|
}
|
241
324
|
};
|