porffor 0.14.0-f67c123a1 → 0.16.0-0c4120280

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 (43) hide show
  1. package/CONTRIBUTING.md +15 -9
  2. package/README.md +9 -13
  3. package/asur/index.js +1 -1
  4. package/compiler/2c.js +104 -51
  5. package/compiler/assemble.js +30 -4
  6. package/compiler/builtins/annexb_string.ts +1 -0
  7. package/compiler/builtins/array.ts +84 -4
  8. package/compiler/builtins/base64.ts +1 -0
  9. package/compiler/builtins/boolean.ts +3 -1
  10. package/compiler/builtins/console.ts +6 -0
  11. package/compiler/builtins/crypto.ts +1 -0
  12. package/compiler/builtins/date.ts +2 -0
  13. package/compiler/builtins/error.js +22 -0
  14. package/compiler/builtins/escape.ts +1 -2
  15. package/compiler/builtins/function.ts +2 -0
  16. package/compiler/builtins/int.ts +2 -0
  17. package/compiler/builtins/math.ts +410 -0
  18. package/compiler/builtins/number.ts +2 -0
  19. package/compiler/builtins/object.ts +2 -0
  20. package/compiler/builtins/porffor.d.ts +11 -0
  21. package/compiler/builtins/set.ts +20 -7
  22. package/compiler/builtins/string.ts +1 -0
  23. package/compiler/builtins/symbol.ts +62 -0
  24. package/compiler/builtins.js +50 -12
  25. package/compiler/codegen.js +579 -279
  26. package/compiler/cyclone.js +535 -0
  27. package/compiler/decompile.js +7 -1
  28. package/compiler/generated_builtins.js +555 -60
  29. package/compiler/havoc.js +93 -0
  30. package/compiler/index.js +93 -14
  31. package/compiler/opt.js +3 -39
  32. package/compiler/parse.js +2 -2
  33. package/compiler/pgo.js +207 -0
  34. package/compiler/precompile.js +5 -4
  35. package/compiler/prefs.js +12 -3
  36. package/compiler/prototype.js +180 -157
  37. package/compiler/wrap.js +127 -44
  38. package/no_pgo.txt +923 -0
  39. package/package.json +2 -2
  40. package/pgo.txt +916 -0
  41. package/runner/index.js +18 -12
  42. package/runner/repl.js +18 -2
  43. /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 porfToJSValue = (memory, funcs, value, type) => {
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
- // have to slice because of memory alignment (?)
45
- const buf = memory.buffer.slice(value + 4, value + 4 + 8 * length);
46
- return Array.from(new Float64Array(buf, 0, length));
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 async (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
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 function')) flags.push('module');
129
+ if (source.includes?.('export ')) flags.push('module');
88
130
 
89
- // (await import('node:fs')).writeFileSync('out.wasm', Buffer.from(wasm));
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 t2 = performance.now();
95
-
96
- let instance;
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, 40));
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) throw e;
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) throw e;
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
  };