porffor 0.57.7 → 0.57.9

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.
@@ -95,7 +95,13 @@ export default (funcs, globals, tags, pages, data, noTreeshake = false) => {
95
95
 
96
96
  const importSection = importFuncs.length === 0 ? [] : createSection(
97
97
  Section.import,
98
- encodeVector(importFuncs.map(x => [ 0, ...encodeString(x.import), ExportDesc.func, getType(typeof x.params === 'object' ? x.params : new Array(x.params).fill(valtypeBinary), new Array(x.returns).fill(valtypeBinary)) ]))
98
+ encodeVector(importFuncs.map(x => {
99
+ return [
100
+ 0, 1, x.import.charCodeAt(0),
101
+ ExportDesc.func,
102
+ getType(x.params, x.returns)
103
+ ];
104
+ }))
99
105
  );
100
106
  time('import section');
101
107
 
@@ -5,61 +5,44 @@ import { TYPES, TYPE_NAMES } from './types.js';
5
5
  import { number, unsignedLEB128 } from './encoding.js';
6
6
  import './prefs.js';
7
7
 
8
- export const importedFuncs = [
9
- {
10
- name: 'print',
11
- import: 'p',
12
- params: 1,
13
- returns: 0
14
- },
15
- {
16
- name: 'printChar',
17
- import: 'c',
18
- params: 1,
19
- returns: 0
20
- },
21
- {
22
- name: 'time',
23
- import: 't',
24
- params: 0,
25
- returns: 1
26
- },
27
- {
28
- name: 'timeOrigin',
29
- import: 'u',
30
- params: 0,
31
- returns: 1
32
- },
33
- {
34
- name: 'profile1',
35
- import: 'y',
36
- params: [ Valtype.i32 ],
37
- returns: 0
38
- },
39
- {
40
- name: 'profile2',
41
- import: 'z',
42
- params: [ Valtype.i32 ],
43
- returns: 0
44
- },
45
- {
46
- name: '__Porffor_readArgv',
47
- import: 'w',
48
- params: 2,
49
- returns: 1
50
- },
51
- {
52
- name: '__Porffor_readFile',
53
- import: 'q',
54
- params: 2,
55
- returns: 1
56
- }
57
- ];
8
+ export const importedFuncs = {};
9
+ Object.defineProperty(importedFuncs, 'length', { configurable: true, writable: true, value: 0 });
10
+
11
+ export const createImport = (name, params, returns, js = null, c = null) => {
12
+ if (name in importedFuncs) return false;
13
+
14
+ const call = importedFuncs.length;
15
+ const ident = String.fromCharCode(97 + importedFuncs.length);
16
+ let obj;
17
+ const get = () => {
18
+ if (obj) return obj;
19
+
20
+ if (typeof params === 'function') params = params();
21
+ if (typeof returns === 'function') returns = returns();
22
+ if (typeof params === 'number') params = new Array(params).fill(valtypeBinary);
23
+ if (typeof returns === 'number') returns = new Array(returns).fill(valtypeBinary);
24
+
25
+ obj = new Number(call);
26
+ obj.name = name;
27
+ obj.import = ident;
28
+ obj.params = params;
29
+ obj.returns = returns;
30
+ obj.js = js;
31
+ obj.c = c;
32
+ return obj;
33
+ };
58
34
 
59
- for (let i = 0; i < importedFuncs.length; i++) {
60
- const f = importedFuncs[i];
61
- importedFuncs[f.name] = i;
62
- }
35
+ Object.defineProperty(importedFuncs, name, {
36
+ get,
37
+ configurable: true,
38
+ enumerable: true
39
+ });
40
+ Object.defineProperty(importedFuncs, call, {
41
+ get,
42
+ configurable: true
43
+ });
44
+ importedFuncs.length = call + 1;
45
+ };
63
46
 
64
47
  export const UNDEFINED = 0;
65
48
  export const NULL = 0;
@@ -2703,7 +2703,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2703
2703
 
2704
2704
  if (
2705
2705
  func?.returns?.length === 0 ||
2706
- (idx === importedFuncs[name] && importedFuncs[importedFuncs[name]]?.returns === 0)
2706
+ (idx === importedFuncs[name] && importedFuncs[importedFuncs[name]]?.returns?.length === 0)
2707
2707
  ) {
2708
2708
  out.push(number(UNDEFINED));
2709
2709
  }
@@ -5750,7 +5750,7 @@ const generateMember = (scope, decl, _global, _name) => {
5750
5750
  if (func) return withType(scope, [ number(countLength(func, name)) ], TYPES.number);
5751
5751
 
5752
5752
  if (Object.hasOwn(builtinFuncs, name)) return withType(scope, [ number(countLength(builtinFuncs[name], name)) ], TYPES.number);
5753
- if (Object.hasOwn(importedFuncs, name)) return withType(scope, [ number(importedFuncs[name].params.length ?? importedFuncs[name].params) ], TYPES.number);
5753
+ if (Object.hasOwn(importedFuncs, name)) return withType(scope, [ number(importedFuncs[name].params.length) ], TYPES.number);
5754
5754
  if (Object.hasOwn(internalConstrs, name)) return withType(scope, [ number(internalConstrs[name].length ?? 0) ], TYPES.number);
5755
5755
  }
5756
5756
 
@@ -102,8 +102,8 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
102
102
  const callFunc = funcs.find(x => x.index === idx);
103
103
  if (callFunc) out += ` ;; $${callFunc.name} ${makeSignature(callFunc.params, callFunc.returns)}`;
104
104
  if (globalThis.importFuncs && idx < importFuncs.length) {
105
- const importFunc = importFuncs[idx];
106
- out += ` ;; import ${importFunc.name} ${makeSignature(typeof importFunc.params === 'object' ? importFunc.params : new Array(importFunc.params).fill(valtypeBinary), new Array(importFunc.returns).fill(valtypeBinary),)}`;
105
+ const importFunc = globalThis.importFuncs[idx];
106
+ out += ` ;; import ${importFunc.name} ${makeSignature(importFunc.params, importFunc.returns)}`;
107
107
  }
108
108
  }
109
109
 
package/compiler/index.js CHANGED
@@ -62,6 +62,9 @@ const progressClear = () => {
62
62
  export default (code, module = undefined) => {
63
63
  if (module !== undefined) Prefs.module = module;
64
64
 
65
+ globalThis.valtype = Prefs.valtype ?? 'f64';
66
+ globalThis.valtypeBinary = Valtype[valtype];
67
+
65
68
  const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
66
69
 
67
70
  let target = Prefs.target ?? 'wasm';
package/compiler/pgo.js CHANGED
@@ -1,14 +1,12 @@
1
1
  import { Opcodes, Valtype } from './wasmSpec.js';
2
2
  import { number } from './encoding.js';
3
- import { importedFuncs } from './builtins.js';
3
+ import { createImport, importedFuncs } from './builtins.js';
4
4
  import assemble from './assemble.js';
5
5
  import wrap, { writeByteStr } from './wrap.js';
6
6
  import * as Havoc from './havoc.js';
7
7
  import './prefs.js';
8
8
 
9
9
  export const setup = () => {
10
- importedFuncs[importedFuncs.profile2].params = [ Valtype.i32, valtypeBinary ];
11
-
12
10
  // enable these prefs by default for pgo
13
11
  for (const x of [
14
12
  'typeswitchUniqueTmp', // use unique tmps for typeswitches
@@ -36,6 +34,15 @@ export const run = obj => {
36
34
 
37
35
  time(0, `injecting PGO logging...`);
38
36
 
37
+ let activeFunc = null, abort = false;
38
+ createImport('profile1', 1, 0, n => {
39
+ activeFunc = n;
40
+ });
41
+ createImport('profile2', 2, 0, (i, n) => {
42
+ if (activeFunc == null) throw 'fail';
43
+ localData[activeFunc][i].push(n);
44
+ });
45
+
39
46
  let funcs = [];
40
47
  for (let i = 0; i < wasmFuncs.length; i++) {
41
48
  const { name, internal, params, locals, wasm } = wasmFuncs[i];
@@ -79,7 +86,7 @@ export const run = obj => {
79
86
  time(0, `injected PGO logging`);
80
87
  time(1, `running with PGO logging...`);
81
88
 
82
- let activeFunc = null, abort = false;
89
+
83
90
  try {
84
91
  obj.wasm = assemble(obj.funcs, obj.globals, obj.tags, obj.pages, obj.data, true);
85
92
 
@@ -87,14 +94,7 @@ export const run = obj => {
87
94
  Prefs.profileCompiler = false;
88
95
 
89
96
  const { exports } = wrap(obj, undefined, {
90
- y: n => {
91
- activeFunc = n;
92
- },
93
- z: (i, n) => {
94
- if (activeFunc == null) throw 'fail';
95
- localData[activeFunc][i].push(n);
96
- },
97
- w: (ind, outPtr) => { // readArgv
97
+ readArgv: (ind, outPtr) => {
98
98
  const pgoInd = process.argv.indexOf('--pgo');
99
99
  let args = process.argv.slice(pgoInd);
100
100
  args = args.slice(args.findIndex(x => !x.startsWith('-')) + 1);
@@ -108,7 +108,7 @@ export const run = obj => {
108
108
  writeByteStr(exports.$, outPtr, str);
109
109
  return str.length;
110
110
  },
111
- q: (pathPtr, outPtr) => {
111
+ readFile: (pathPtr, outPtr) => {
112
112
  return -1;
113
113
  }
114
114
  }, () => {});
package/compiler/wrap.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { encodeVector } from './encoding.js';
2
- import { importedFuncs } from './builtins.js';
2
+ import { importedFuncs, createImport } from './builtins.js';
3
3
  import compile from './index.js';
4
4
  import disassemble from './disassemble.js';
5
5
  import { TYPES, TYPE_NAMES } from './types.js';
@@ -352,16 +352,61 @@ ${flags & 0b0001 ? ` get func idx: ${get}
352
352
  }
353
353
  };
354
354
 
355
+ export { createImport };
355
356
  export default (source, module = undefined, customImports = {}, print = str => process.stdout.write(str)) => {
357
+ createImport('print', 1, 0, i => print(i.toString()));
358
+ createImport('printChar', 1, 0, i => print(String.fromCharCode(i)));
359
+ createImport('time', 0, 1, () => performance.now());
360
+ createImport('timeOrigin', 0, 1, () => performance.timeOrigin);
361
+
362
+ // todo: these should be provided elsewhere in runtime itself
363
+ createImport('__Porffor_readArgv', 2, 1, (ind, outPtr) => {
364
+ let args = process.argv.slice(2);
365
+ args = args.slice(args.findIndex(x => !x.startsWith('-')) + 1);
366
+
367
+ const str = args[ind - 1];
368
+ if (!str) return -1;
369
+
370
+ writeByteStr(memory, outPtr, str);
371
+ return str.length;
372
+ });
373
+ createImport('__Porffor_readFile', 2, 1, (pathPtr, outPtr) => { // readFile
374
+ try {
375
+ const path = pathPtr === 0 ? 0 : readByteStr(memory, pathPtr);
376
+ const contents = fs.readFileSync(path, 'utf8');
377
+ writeByteStr(memory, outPtr, contents);
378
+ return contents.length;
379
+ } catch {
380
+ return -1;
381
+ }
382
+ });
383
+
384
+ for (const x in customImports) {
385
+ const custom = customImports[x];
386
+
387
+ if (x in importedFuncs) {
388
+ // overwrite with user custom import
389
+ const existing = importedFuncs[x];
390
+ if (typeof custom === 'function') {
391
+ existing.js = custom;
392
+ } else {
393
+ existing.params = custom.params;
394
+ existing.returns = custom.returns;
395
+ existing.js = custom.js;
396
+ existing.c = custom.c;
397
+ }
398
+ continue;
399
+ }
400
+
401
+ // todo: make a simpler api for just js functions at some point using function.length etc
402
+ createImport(x, custom.params, custom.returns, custom.js, custom.c);
403
+ }
404
+
356
405
  const times = [];
357
406
 
358
407
  const t1 = performance.now();
359
408
  const { wasm, funcs, globals, tags, exceptions, pages, c } = typeof source === 'object' ? source : compile(source, module);
360
409
 
361
- globalThis.porfDebugInfo = { funcs, globals };
362
-
363
- // fs.writeFileSync('out.wasm', Buffer.from(wasm));
364
-
365
410
  times.push(performance.now() - t1);
366
411
  if (Prefs.profileCompiler && !globalThis.onProgress) console.log(`\u001b[1mcompiled in ${times[0].toFixed(2)}ms\u001b[0m`);
367
412
 
@@ -448,35 +493,11 @@ export default (source, module = undefined, customImports = {}, print = str => p
448
493
  try {
449
494
  const module = new WebAssembly.Module(wasm);
450
495
  instance = new WebAssembly.Instance(module, {
451
- '': {
452
- p: i => print(i.toString()),
453
- c: i => print(String.fromCharCode(i)),
454
- t: () => performance.now(),
455
- u: () => performance.timeOrigin,
456
- y: () => {},
457
- z: () => {},
458
- w: (ind, outPtr) => { // readArgv
459
- let args = process.argv.slice(2);
460
- args = args.slice(args.findIndex(x => !x.startsWith('-')) + 1);
461
-
462
- const str = args[ind - 1];
463
- if (!str) return -1;
464
-
465
- writeByteStr(memory, outPtr, str);
466
- return str.length;
467
- },
468
- q: (pathPtr, outPtr) => { // readFile
469
- try {
470
- const path = pathPtr === 0 ? 0 : readByteStr(memory, pathPtr);
471
- const contents = fs.readFileSync(path, 'utf8');
472
- writeByteStr(memory, outPtr, contents);
473
- return contents.length;
474
- } catch {
475
- return -1;
476
- }
477
- },
478
- ...customImports
479
- }
496
+ '': Object.keys(importedFuncs).reduce((acc, y) => {
497
+ const x = importedFuncs[y];
498
+ acc[x.import] = x.js ?? (() => {});
499
+ return acc;
500
+ }, {})
480
501
  });
481
502
  } catch (e) {
482
503
  if (!Prefs.d) throw e;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "porffor",
3
3
  "description": "An ahead-of-time JavaScript compiler",
4
- "version": "0.57.7",
4
+ "version": "0.57.9",
5
5
  "author": "Oliver Medhurst <honk@goose.icu>",
6
6
  "license": "MIT",
7
7
  "scripts": {},
package/runtime/debug.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import compile from '../compiler/wrap.js';
2
+ import compile, { createImport } from '../compiler/wrap.js';
3
3
  import Byg from '../byg/index.js';
4
4
  import fs from 'node:fs';
5
5
 
@@ -41,75 +41,75 @@ let lastLine;
41
41
 
42
42
  let output = '';
43
43
 
44
- try {
45
- const { exports } = compile(source, undefined, {
46
- y: n => {
47
- if (callStarts[callStarts.length - 1] === n - 1) {
48
- // end of call
44
+ createImport('profile1', 1, 0, n => {
45
+ if (callStarts[callStarts.length - 1] === n - 1) {
46
+ // end of call
49
47
 
50
- callStarts.pop();
51
- callStack.pop();
48
+ callStarts.pop();
49
+ callStack.pop();
52
50
 
53
- paused = _paused;
54
- }
51
+ paused = _paused;
52
+ }
55
53
 
56
- lastLine = n;
57
-
58
- if (breakpoints[n]) paused = true;
59
-
60
- if (paused) {
61
- stepIn = false; stepOut = false;
62
-
63
- switch (byg(
64
- paused,
65
- n,
66
- `\x1b[1mporffor debugger\x1b[22m: ${file}@${n + 1} ${callStack.join('->')}`,
67
- [
68
- {
69
- x: termWidth - 1 - 40 - 6,
70
- y: () => 4,
71
- width: 40,
72
- height: 20,
73
- title: 'console',
74
- content: output.split('\n')
75
- }
76
- ]
77
- )) {
78
- case 'resume': {
79
- paused = false;
80
- break;
81
- }
82
-
83
- case 'stepOver': {
84
- break;
85
- }
86
-
87
- case 'stepIn': {
88
- stepIn = true;
89
- // paused = false;
90
- break;
91
- }
92
-
93
- case 'stepOut': {
94
- stepOut = true;
95
- paused = false;
96
- break;
97
- }
54
+ lastLine = n;
55
+
56
+ if (breakpoints[n]) paused = true;
57
+
58
+ if (paused) {
59
+ stepIn = false; stepOut = false;
60
+
61
+ switch (byg(
62
+ paused,
63
+ n,
64
+ `\x1b[1mporffor debugger\x1b[22m: ${file}@${n + 1} ${callStack.join('->')}`,
65
+ [
66
+ {
67
+ x: termWidth - 1 - 40 - 6,
68
+ y: () => 4,
69
+ width: 40,
70
+ height: 20,
71
+ title: 'console',
72
+ content: output.split('\n')
98
73
  }
74
+ ]
75
+ )) {
76
+ case 'resume': {
77
+ paused = false;
78
+ break;
99
79
  }
100
- },
101
- z: n => {
102
- // start of call
103
- callStack.push(funcs[n]);
104
80
 
105
- callStarts.push(lastLine);
81
+ case 'stepOver': {
82
+ break;
83
+ }
106
84
 
107
- _paused = paused;
108
- if (!stepIn) paused = false;
109
- else paused = true;
85
+ case 'stepIn': {
86
+ stepIn = true;
87
+ // paused = false;
88
+ break;
89
+ }
90
+
91
+ case 'stepOut': {
92
+ stepOut = true;
93
+ paused = false;
94
+ break;
95
+ }
110
96
  }
111
- }, s => output += s);
97
+ }
98
+ });
99
+
100
+ createImport('profile2', 1, 0, n => {
101
+ // start of call
102
+ callStack.push(funcs[n]);
103
+
104
+ callStarts.push(lastLine);
112
105
 
106
+ _paused = paused;
107
+ if (!stepIn) paused = false;
108
+ else paused = true;
109
+ });
110
+
111
+ try {
112
+ const { exports } = compile(source, undefined, {}, s => output += s);
113
113
  exports.main();
114
114
  } catch (e) {
115
115
  console.error(e);
@@ -2,7 +2,7 @@
2
2
  import { Opcodes, Valtype } from '../compiler/wasmSpec.js';
3
3
  import { number } from '../compiler/encoding.js';
4
4
  import { importedFuncs } from '../compiler/builtins.js';
5
- import compile from '../compiler/wrap.js';
5
+ import compile, { createImport } from '../compiler/wrap.js';
6
6
  import fs from 'node:fs';
7
7
 
8
8
  const file = process.argv.slice(2).find(x => x[0] !== '-');
@@ -148,7 +148,19 @@ const render = () => {
148
148
  lastRenderTime = performance.now() - renderStart;
149
149
  };
150
150
 
151
- // importedFuncs[importedFuncs.profile2].params = [ Valtype.i32, Valtype.f64 ];
151
+ createImport('profile1', 1, 0, f => { // pre-call
152
+ samplesStart.push(performance.now());
153
+ running[runningIdx++] = samplesFunc.push(f) - 1;
154
+ });
155
+ createImport('profile2', 1, 0, f => { // post-call
156
+ const now = performance.now();
157
+ samplesEnd[running[--runningIdx]] = now;
158
+
159
+ if (now > last) {
160
+ last = now + 500;
161
+ render();
162
+ }
163
+ });
152
164
 
153
165
  Prefs.treeshakeWasmImports = false;
154
166
  let funcLookup = new Map();
@@ -180,21 +192,7 @@ globalThis.compileCallback = ({ funcs }) => {
180
192
 
181
193
  let last = 0;
182
194
  let running = new Uint32Array(1024), runningIdx = 0;
183
- const { exports } = compile(source, undefined, {
184
- y: f => { // pre-call
185
- samplesStart.push(performance.now());
186
- running[runningIdx++] = samplesFunc.push(f) - 1;
187
- },
188
- z: f => { // post-call
189
- const now = performance.now();
190
- samplesEnd[running[--runningIdx]] = now;
191
-
192
- if (now > last) {
193
- last = now + 500;
194
- render();
195
- }
196
- }
197
- }, () => {});
195
+ const { exports } = compile(source, undefined, {}, () => {});
198
196
 
199
197
  start = performance.now();
200
198
  render();
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import compile from '../compiler/wrap.js';
2
+ import compile, { createImport } from '../compiler/wrap.js';
3
3
  import fs from 'node:fs';
4
4
 
5
5
  const file = process.argv.slice(2).find(x => x[0] !== '-');
@@ -18,29 +18,26 @@ const spinner = ['-', '\\', '|', '/'];
18
18
  let spin = 0;
19
19
  let last = 0;
20
20
 
21
- try {
22
- const { exports } = compile(source, undefined, {
23
- y: n => {
24
- tmp[n] = performance.now();
25
- },
26
- z: n => {
27
- const t = performance.now();
28
- times[n] += t - tmp[n];
29
-
30
- samples++;
31
- if (t > last) {
32
- process.stdout.write(`\r${spinner[spin++ % 4]} running: collected ${samples} samples...`);
33
- last = t + 100;
34
- }
35
- }
36
- });
21
+ createImport('profile1', 1, 0, n => {
22
+ tmp[n] = performance.now();
23
+ });
24
+ createImport('profile2', 1, 0, n => {
25
+ const t = performance.now();
26
+ times[n] += t - tmp[n];
37
27
 
38
- const start = performance.now();
28
+ samples++;
29
+ if (t > last) {
30
+ process.stdout.write(`\r${spinner[spin++ % 4]} running: collected ${samples} samples...`);
31
+ last = t + 100;
32
+ }
33
+ })
39
34
 
35
+ try {
36
+ const { exports } = compile(source, undefined);
37
+ const start = performance.now();
40
38
  exports.main();
41
39
 
42
40
  const total = performance.now() - start;
43
-
44
41
  console.log(`\ntotal: ${total}ms\nsamples: ${samples}\n\n\n` + source.split('\n').map(x => {
45
42
  let time = 0;
46
43
  if (x.startsWith('profile')) {
package/runtime/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from 'node:fs';
3
- globalThis.version = '0.57.7';
3
+ globalThis.version = '0.57.9';
4
4
 
5
5
  // deno compat
6
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {