porffor 0.2.0-6aff0fa → 0.2.0-6bc63ef

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 (54) hide show
  1. package/CONTRIBUTING.md +256 -0
  2. package/LICENSE +20 -20
  3. package/README.md +115 -82
  4. package/asur/index.js +624 -340
  5. package/byg/index.js +216 -0
  6. package/compiler/2c.js +2 -53
  7. package/compiler/{sections.js → assemble.js} +60 -14
  8. package/compiler/builtins/annexb_string.js +72 -0
  9. package/compiler/builtins/annexb_string.ts +18 -0
  10. package/compiler/builtins/array.ts +145 -0
  11. package/compiler/builtins/base64.ts +7 -84
  12. package/compiler/builtins/boolean.ts +18 -0
  13. package/compiler/builtins/crypto.ts +120 -0
  14. package/compiler/builtins/date.ts +2067 -0
  15. package/compiler/builtins/escape.ts +141 -0
  16. package/compiler/builtins/function.ts +5 -0
  17. package/compiler/builtins/int.ts +145 -0
  18. package/compiler/builtins/number.ts +529 -0
  19. package/compiler/builtins/object.ts +4 -0
  20. package/compiler/builtins/porffor.d.ts +44 -7
  21. package/compiler/builtins/set.ts +187 -0
  22. package/compiler/builtins/string.ts +1080 -0
  23. package/compiler/builtins.js +400 -120
  24. package/compiler/{codeGen.js → codegen.js} +850 -402
  25. package/compiler/decompile.js +2 -3
  26. package/compiler/embedding.js +22 -22
  27. package/compiler/encoding.js +94 -10
  28. package/compiler/expression.js +1 -1
  29. package/compiler/generated_builtins.js +1613 -3
  30. package/compiler/index.js +16 -16
  31. package/compiler/log.js +2 -2
  32. package/compiler/opt.js +28 -27
  33. package/compiler/parse.js +36 -30
  34. package/compiler/precompile.js +37 -46
  35. package/compiler/prefs.js +7 -6
  36. package/compiler/prototype.js +20 -36
  37. package/compiler/types.js +38 -0
  38. package/compiler/wasmSpec.js +14 -1
  39. package/compiler/wrap.js +79 -69
  40. package/package.json +9 -5
  41. package/porf +2 -0
  42. package/rhemyn/compile.js +44 -26
  43. package/rhemyn/parse.js +322 -320
  44. package/rhemyn/test/parse.js +58 -58
  45. package/runner/compare.js +33 -34
  46. package/runner/debug.js +117 -0
  47. package/runner/index.js +69 -12
  48. package/runner/profiler.js +22 -30
  49. package/runner/repl.js +40 -13
  50. package/runner/sizes.js +37 -37
  51. package/runner/version.js +3 -3
  52. package/runner/info.js +0 -89
  53. package/runner/transform.js +0 -15
  54. package/util/enum.js +0 -20
package/compiler/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { underline, bold, log } from './log.js';
2
2
  import parse from './parse.js';
3
- import codeGen from './codeGen.js';
3
+ import codegen from './codegen.js';
4
4
  import opt from './opt.js';
5
- import produceSections from './sections.js';
5
+ import assemble from './assemble.js';
6
6
  import decompile from './decompile.js';
7
7
  import toc from './2c.js';
8
8
  import Prefs from './prefs.js';
@@ -26,29 +26,29 @@ const logFuncs = (funcs, globals, exceptions) => {
26
26
  console.log();
27
27
  };
28
28
 
29
- const writeFileSync = (typeof process?.version !== 'undefined' ? (await import('node:fs')).writeFileSync : undefined);
29
+ const fs = (typeof process?.version !== 'undefined' ? (await import('node:fs')) : undefined);
30
30
  const execSync = (typeof process?.version !== 'undefined' ? (await import('node:child_process')).execSync : undefined);
31
31
 
32
32
  export default (code, flags) => {
33
33
  const t0 = performance.now();
34
34
  const program = parse(code, flags);
35
- if (flags.includes('info')) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
35
+ if (Prefs.profileCompiler) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
36
36
 
37
37
  const t1 = performance.now();
38
- const { funcs, globals, tags, exceptions, pages, data } = codeGen(program);
39
- if (flags.includes('info')) console.log(`2. generated code in ${(performance.now() - t1).toFixed(2)}ms`);
38
+ const { funcs, globals, tags, exceptions, pages, data } = codegen(program);
39
+ if (Prefs.profileCompiler) console.log(`2. generated code in ${(performance.now() - t1).toFixed(2)}ms`);
40
40
 
41
41
  if (Prefs.funcs) logFuncs(funcs, globals, exceptions);
42
42
 
43
43
  const t2 = performance.now();
44
44
  opt(funcs, globals, pages, tags, exceptions);
45
- if (flags.includes('info')) console.log(`3. optimized code in ${(performance.now() - t2).toFixed(2)}ms`);
45
+ if (Prefs.profileCompiler) console.log(`3. optimized in ${(performance.now() - t2).toFixed(2)}ms`);
46
46
 
47
47
  if (Prefs.optFuncs) logFuncs(funcs, globals, exceptions);
48
48
 
49
49
  const t3 = performance.now();
50
- const sections = produceSections(funcs, globals, tags, pages, data, flags);
51
- if (flags.includes('info')) console.log(`4. produced sections in ${(performance.now() - t3).toFixed(2)}ms`);
50
+ const wasm = assemble(funcs, globals, tags, pages, data, flags);
51
+ if (Prefs.profileCompiler) console.log(`4. assembled in ${(performance.now() - t3).toFixed(2)}ms`);
52
52
 
53
53
  if (Prefs.allocLog) {
54
54
  const wasmPages = Math.ceil((pages.size * pageSize) / 65536);
@@ -57,13 +57,13 @@ export default (code, flags) => {
57
57
  console.log([...pages.keys()].map(x => `\x1B[36m - ${x}\x1B[0m`).join('\n') + '\n');
58
58
  }
59
59
 
60
- const out = { wasm: sections, funcs, globals, tags, exceptions, pages, data };
60
+ const out = { wasm, funcs, globals, tags, exceptions, pages, data };
61
61
 
62
62
  const target = Prefs.target ?? 'wasm';
63
63
  const outFile = Prefs.o;
64
64
 
65
65
  if (target === 'wasm' && outFile) {
66
- writeFileSync(outFile, Buffer.from(sections));
66
+ fs.writeFileSync(outFile, Buffer.from(wasm));
67
67
 
68
68
  if (process.version) process.exit();
69
69
  }
@@ -73,7 +73,7 @@ export default (code, flags) => {
73
73
  out.c = c;
74
74
 
75
75
  if (outFile) {
76
- writeFileSync(outFile, c);
76
+ fs.writeFileSync(outFile, c);
77
77
  } else {
78
78
  console.log(c);
79
79
  }
@@ -88,17 +88,17 @@ export default (code, flags) => {
88
88
  if (compiler === 'zig') compiler = [ 'zig', 'cc' ];
89
89
  else compiler = [ compiler ];
90
90
 
91
- const tmpfile = 'tmp.c';
92
- // const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-fno-unwind-tables', '-fno-asynchronous-unwind-tables', '-ffunction-sections', '-fdata-sections', '-Wl', '-fno-ident', '-fno-exceptions', '-ffast-math' ];
93
- // const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-ffast-math', '-fno-exceptions', '-target', 'x86_64-linux' ];
91
+ const tmpfile = 'porffor_tmp.c';
94
92
  const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-ffast-math', '-fno-exceptions' ];
95
93
 
96
94
  const c = toc(out);
97
- writeFileSync(tmpfile, c);
95
+ fs.writeFileSync(tmpfile, c);
98
96
 
99
97
  // obvious command escape is obvious
100
98
  execSync(args.join(' '), { stdio: 'inherit' });
101
99
 
100
+ fs.unlinkSync(tmpfile);
101
+
102
102
  if (process.version) process.exit();
103
103
  }
104
104
 
package/compiler/log.js CHANGED
@@ -5,9 +5,9 @@ export const bold = x => `\u001b[1m${x}\u001b[0m`;
5
5
  const areaColors = {
6
6
  codegen: [ 20, 80, 250 ],
7
7
  opt: [ 250, 20, 80 ],
8
- sections: [ 20, 250, 80 ],
8
+ assemble: [ 20, 250, 80 ],
9
9
  alloc: [ 250, 250, 20 ],
10
- parser: [ 240, 240, 240 ],
10
+ parse: [ 240, 240, 240 ],
11
11
  '2c': [ 20, 250, 250 ],
12
12
  wrap: [ 250, 100, 20 ]
13
13
  };
package/compiler/opt.js CHANGED
@@ -1,7 +1,7 @@
1
- import { Opcodes, Valtype } from "./wasmSpec.js";
2
- import { number } from "./embedding.js";
3
- import { read_signedLEB128, read_ieee754_binary64 } from "./encoding.js";
4
- import { log } from "./log.js";
1
+ import { Opcodes, Valtype } from './wasmSpec.js';
2
+ import { number } from './embedding.js';
3
+ import { read_signedLEB128, read_ieee754_binary64 } from './encoding.js';
4
+ import { log } from './log.js';
5
5
  import Prefs from './prefs.js';
6
6
 
7
7
  const performWasmOp = (op, a, b) => {
@@ -100,8 +100,9 @@ export default (funcs, globals, pages, tags, exceptions) => {
100
100
 
101
101
  if (Prefs.optInlineOnly) return;
102
102
 
103
- const tagUse = tags.reduce((acc, x) => { acc[x.idx] = 0; return acc; }, {});
104
- const exceptionUse = exceptions.reduce((acc, _, i) => { acc[i] = 0; return acc; }, {});
103
+ // todo: this breaks exceptions after due to indexes not being adjusted
104
+ // const tagUse = tags.reduce((acc, x) => { acc[x.idx] = 0; return acc; }, {});
105
+ // const exceptionUse = exceptions.reduce((acc, _, i) => { acc[i] = 0; return acc; }, {});
105
106
 
106
107
  // wasm transform pass
107
108
  for (const f of funcs) {
@@ -109,7 +110,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
109
110
 
110
111
  const lastType = f.locals['#last_type'];
111
112
 
112
- let runs = 2; // how many by default? add arg?
113
+ let runs = (+Prefs.optWasmRuns) || 2; // todo: how many by default?
113
114
  while (runs > 0) {
114
115
  runs--;
115
116
 
@@ -131,12 +132,12 @@ export default (funcs, globals, pages, tags, exceptions) => {
131
132
  if (inst[0] === Opcodes.local_get) getCount[inst[1]]++;
132
133
  if (inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) setCount[inst[1]]++;
133
134
 
134
- if (inst[0] === Opcodes.throw) {
135
- tagUse[inst[1]]++;
135
+ // if (inst[0] === Opcodes.throw) {
136
+ // tagUse[inst[1]]++;
136
137
 
137
- const exceptId = read_signedLEB128(wasm[i - 1].slice(1));
138
- exceptionUse[exceptId]++;
139
- }
138
+ // const exceptId = read_signedLEB128(wasm[i - 1].slice(1));
139
+ // exceptionUse[exceptId]++;
140
+ // }
140
141
 
141
142
  if (inst[0] === Opcodes.block) {
142
143
  // remove unneeded blocks (no brs inside)
@@ -171,14 +172,14 @@ export default (funcs, globals, pages, tags, exceptions) => {
171
172
  }
172
173
  }
173
174
 
174
- if (inst[inst.length - 1] === 'string_only' && !pages.hasString) {
175
+ if (inst[inst.length - 1] === 'string_only' && !pages.hasAnyString && !Prefs.noRmUnusedTypes) {
175
176
  // remove this inst
176
177
  wasm.splice(i, 1);
177
178
  if (i > 0) i--;
178
179
  inst = wasm[i];
179
180
  }
180
181
 
181
- if (inst[inst.length - 1] === 'string_only|start' && !pages.hasString) {
182
+ if (inst[inst.length - 1] === 'string_only|start' && !pages.hasAnyString&& !Prefs.noRmUnusedTypes) {
182
183
  let j = i;
183
184
  for (; j < wasm.length; j++) {
184
185
  const op = wasm[j];
@@ -192,7 +193,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
192
193
  inst = wasm[i];
193
194
  }
194
195
 
195
- if (inst[0] === Opcodes.if && typeof inst[2] === 'string') {
196
+ if (inst[0] === Opcodes.if && typeof inst[2] === 'string' && !Prefs.noRmUnusedTypes) {
196
197
  // remove unneeded typeswitch checks
197
198
 
198
199
  const type = inst[2].split('|')[1];
@@ -553,18 +554,18 @@ export default (funcs, globals, pages, tags, exceptions) => {
553
554
  }
554
555
  }
555
556
 
556
- for (const x in tagUse) {
557
- if (tagUse[x] === 0) {
558
- const el = tags.find(y => y.idx === x);
559
- tags.splice(tags.indexOf(el), 1);
560
- }
561
- }
562
-
563
- for (const x of Object.keys(exceptionUse).sort((a, b) => b - a)) {
564
- if (exceptionUse[x] === 0) {
565
- exceptions.splice(+x, 1);
566
- }
567
- }
557
+ // for (const x in tagUse) {
558
+ // if (tagUse[x] === 0) {
559
+ // const el = tags.find(y => y.idx === x);
560
+ // tags.splice(tags.indexOf(el), 1);
561
+ // }
562
+ // }
563
+
564
+ // for (const x of Object.keys(exceptionUse).sort((a, b) => b - a)) {
565
+ // if (exceptionUse[x] === 0) {
566
+ // exceptions.splice(+x, 1);
567
+ // }
568
+ // }
568
569
 
569
570
  // return funcs;
570
571
  };
package/compiler/parse.js CHANGED
@@ -1,16 +1,16 @@
1
- import { log } from "./log.js";
1
+ import { log } from './log.js';
2
2
  import Prefs from './prefs.js';
3
3
 
4
- // import { parse } from 'acorn';
5
-
6
4
  // deno compat
7
5
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
8
6
  const textEncoder = new TextEncoder();
9
7
  globalThis.process = { argv: ['', '', ...Deno.args], stdout: { write: str => Deno.writeAllSync(Deno.stdout, textEncoder.encode(str)) } };
10
8
  }
11
9
 
10
+ const file = process.argv.slice(2).find(x => x[0] !== '-');
11
+
12
12
  // should we try to support types (while parsing)
13
- const types = Prefs.parseTypes;
13
+ const types = Prefs.parseTypes || file?.endsWith('.ts');
14
14
  globalThis.typedInput = types && Prefs.optTypes;
15
15
 
16
16
  // todo: review which to use by default
@@ -20,37 +20,43 @@ globalThis.typedInput = types && Prefs.optTypes;
20
20
  // - hermes-parser
21
21
  // - @babel/parser
22
22
 
23
- let parser, parse;
23
+ globalThis.parser = '';
24
+ let parse;
24
25
  const loadParser = async (fallbackParser = 'acorn', forceParser) => {
25
- parser = forceParser ?? process.argv.find(x => x.startsWith('-parser='))?.split('=')?.[1] ?? fallbackParser;
26
- 0, { parse } = (await import((globalThis.document ? 'https://esm.sh/' : '') + parser));
26
+ parser = forceParser ?? process.argv.find(x => x.startsWith('--parser='))?.split('=')?.[1] ?? fallbackParser;
27
+ 0, { parse } = (await import((globalThis.document || globalThis.Deno ? 'https://esm.sh/' : '') + parser));
27
28
  };
28
29
  globalThis._porf_loadParser = loadParser;
29
30
  await loadParser(types ? '@babel/parser' : undefined);
30
31
 
31
- if (types && !['@babel/parser', 'hermes-parser'].includes(parser)) log.warning('parser', `passed -types with a parser (${parser}) which does not support`);
32
+ if (types && !['@babel/parser', 'hermes-parser'].includes(parser)) log.warning('parse', `passed -parse-types with a parser (${parser}) which does not support`);
32
33
 
33
34
  export default (input, flags) => {
34
- const ast = parse(input, {
35
- // acorn
36
- ecmaVersion: 'latest',
37
-
38
- // meriyah
39
- next: true,
40
- module: flags.includes('module'),
41
- webcompat: true,
42
-
43
- // babel
44
- plugins: types ? ['estree', 'typescript'] : ['estree'],
45
-
46
- // multiple
47
- sourceType: flags.includes('module') ? 'module' : 'script',
48
- ranges: false,
49
- tokens: false,
50
- comments: false,
51
- });
52
-
53
- if (ast.type === 'File') return ast.program;
54
-
55
- return ast;
35
+ try {
36
+ const ast = parse(input, {
37
+ // acorn
38
+ ecmaVersion: 'latest',
39
+
40
+ // meriyah
41
+ next: true,
42
+ module: flags.includes('module'),
43
+ webcompat: true,
44
+
45
+ // babel
46
+ plugins: types ? ['estree', 'typescript'] : ['estree'],
47
+
48
+ // multiple
49
+ sourceType: flags.includes('module') ? 'module' : 'script',
50
+ ranges: false,
51
+ tokens: false,
52
+ comments: false,
53
+ });
54
+
55
+ if (ast.type === 'File') return ast.program;
56
+
57
+ return ast;
58
+ } catch (e) {
59
+ // normalize error class thrown by 3rd party parsers
60
+ throw new SyntaxError(e.message, { cause: e });
61
+ }
56
62
  };
@@ -1,4 +1,5 @@
1
1
  import { Opcodes } from './wasmSpec.js';
2
+ import { TYPES } from './types.js';
2
3
 
3
4
  import fs from 'node:fs';
4
5
  import { join } from 'node:path';
@@ -6,52 +7,37 @@ import { join } from 'node:path';
6
7
  import { fileURLToPath } from 'node:url';
7
8
  const __dirname = fileURLToPath(new URL('.', import.meta.url));
8
9
 
9
- const TYPES = {
10
- number: 0x00,
11
- boolean: 0x01,
12
- string: 0x02,
13
- undefined: 0x03,
14
- object: 0x04,
15
- function: 0x05,
16
- symbol: 0x06,
17
- bigint: 0x07,
18
-
19
- // these are not "typeof" types but tracked internally
20
- _array: 0x10,
21
- _regexp: 0x11,
22
- _bytestring: 0x12
23
- };
24
-
25
- // import porfParse from './parse.js';
26
- // import porfCodegen from './codeGen.js';
27
-
28
10
  const argv = process.argv.slice();
29
11
 
30
12
  const compile = async (file, [ _funcs, _globals ]) => {
31
- const source = fs.readFileSync(file, 'utf8');
32
- const first = source.slice(0, source.indexOf('\n'));
13
+ let source = fs.readFileSync(file, 'utf8');
14
+ let first = source.slice(0, source.indexOf('\n'));
15
+
16
+ if (first.startsWith('export default')) {
17
+ source = (await import(file)).default();
18
+ first = source.slice(0, source.indexOf('\n'));
19
+ }
33
20
 
34
- let args = ['-bytestring'];
35
- if (file.endsWith('.ts')) args.push('-parse-types', '-opt-types');
21
+ let args = ['--bytestring', '--todo-time=compile', '--no-aot-pointer-opt', '--no-treeshake-wasm-imports', '--no-rm-unused-types', '--scoped-page-names', '--funsafe-no-unlikely-proto-checks', '--parse-types', '--opt-types'];
36
22
  if (first.startsWith('// @porf')) {
37
23
  args = args.concat(first.slice('// @porf '.length).split(' '));
38
24
  }
39
25
  process.argv = argv.concat(args);
40
26
 
41
- // const porfParse = (await import(`./parse.js?_=${Date.now()}`)).default;
42
- // const porfCodegen = (await import(`./codeGen.js?_=${Date.now()}`)).default;
43
-
44
- // let { funcs, globals, data } = porfCodegen(porfParse(source, ['module']));
45
-
46
27
  const porfCompile = (await import(`./index.js?_=${Date.now()}`)).default;
47
28
 
48
29
  let { funcs, globals, data, exceptions } = porfCompile(source, ['module']);
49
30
 
50
31
  const allocated = new Set();
51
32
 
52
- const exports = funcs.filter(x => x.export);
33
+ const exports = funcs.filter(x => x.export && x.name !== 'main');
53
34
  for (const x of exports) {
54
- if (x.data) x.data = x.data.map(x => data[x]);
35
+ if (x.data) {
36
+ x.data = x.data.map(x => data[x]);
37
+ for (const y in x.data) {
38
+ x.data[y].offset -= x.data[0].offset;
39
+ }
40
+ }
55
41
  if (x.exceptions) x.exceptions = x.exceptions.map(x => {
56
42
  const obj = exceptions[x];
57
43
  if (obj) obj.exceptId = x;
@@ -76,15 +62,22 @@ const compile = async (file, [ _funcs, _globals ]) => {
76
62
  if (y[0] === Opcodes.const && (n[0] === Opcodes.local_set || n[0] === Opcodes.local_tee)) {
77
63
  const l = locals[n[1]];
78
64
  if (!l) continue;
79
- if (![TYPES.string, TYPES._array, TYPES._bytestring].includes(l.metadata?.type)) continue;
65
+ if (![TYPES.string, TYPES.array, TYPES.bytestring].includes(l.metadata?.type)) continue;
80
66
  if (!x.pages) continue;
81
67
 
82
68
  const pageName = [...x.pages.keys()].find(z => z.endsWith(l.name));
83
69
  if (!pageName || allocated.has(pageName)) continue;
84
70
  allocated.add(pageName);
85
71
 
86
- y.splice(0, 10, 'alloc', pageName, x.pages.get(pageName).type);
87
- // y.push(x.pages.get(pageName));
72
+ y.splice(0, 10, 'alloc', pageName, x.pages.get(pageName).type, valtypeBinary);
73
+ }
74
+
75
+ if (y[0] === Opcodes.i32_const && n[0] === Opcodes.throw) {
76
+ const id = y[1];
77
+ y.splice(0, 10, 'throw', exceptions[id].constructor, exceptions[id].message);
78
+
79
+ // remove throw inst
80
+ x.wasm.splice(i + 1, 1);
88
81
  }
89
82
  }
90
83
  }
@@ -99,31 +92,29 @@ const precompile = async () => {
99
92
  let funcs = [], globals = [];
100
93
  for (const file of fs.readdirSync(dir)) {
101
94
  if (file.endsWith('.d.ts')) continue;
95
+ console.log(file);
96
+
102
97
  await compile(join(dir, file), [ funcs, globals ]);
103
98
  }
104
99
 
105
- // ${x.pages && x.pages.size > 0 ? ` pages: ${JSON.stringify(Object.fromEntries(x.pages.entries()))},` : ''}
106
- // ${x.used && x.used.length > 0 ? ` used: ${JSON.stringify(x.used)},` : ''}
107
-
108
100
  return `// autogenerated by compiler/precompile.js
109
101
  import { number } from './embedding.js';
110
102
 
111
103
  export const BuiltinFuncs = function() {
112
- ${funcs.map(x => ` this.${x.name} = {
113
- wasm: (scope, { allocPage, builtin }) => ${JSON.stringify(x.wasm.filter(x => x.length && x[0] != null)).replace(/\["alloc","(.*?)","(.*?)"\]/g, (_, reason, type) => `...number(allocPage(scope, '${reason}', '${type}') * pageSize, ${valtypeBinary})`).replace(/\[16,"(.*?)"]/g, (_, name) => `[16, builtin('${name}')]`)},
104
+ ${funcs.map(x => {
105
+ const wasm = JSON.stringify(x.wasm.filter(x => x.length && x[0] != null)).replace(/\["alloc","(.*?)","(.*?)",(.*?)\]/g, (_, reason, type, valtype) => `...number(allocPage(scope, '${reason}', '${type}') * pageSize, ${valtype})`).replace(/\[16,"(.*?)"]/g, (_, name) => `[16, builtin('${name}')]`).replace(/\["throw","(.*?)","(.*?)"\]/g, (_, constructor, message) => `...internalThrow(scope, '${constructor}', \`${message}\`)`);
106
+ return ` this.${x.name} = {
107
+ wasm: (scope, {${wasm.includes('allocPage(') ? 'allocPage,' : ''}${wasm.includes('builtin(') ? 'builtin,' : ''}${wasm.includes('internalThrow(') ? 'internalThrow,' : ''}}) => ${wasm},
114
108
  params: ${JSON.stringify(x.params)},
115
109
  typedParams: true,
116
110
  returns: ${JSON.stringify(x.returns)},
117
- typedReturns: true,
111
+ ${x.returnType != null ? `returnType: ${JSON.stringify(x.returnType)}` : 'typedReturns: true'},
118
112
  locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length).map(x => x.type))},
119
113
  localNames: ${JSON.stringify(Object.keys(x.locals))},
120
- ${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)},` : ''}
121
- ${x.exceptions && x.exceptions.length > 0 ? ` exceptions: ${JSON.stringify(x.exceptions)},` : ''}
122
- };`.replaceAll('\n\n', '\n')).join('\n')}
114
+ ${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)}` : ''}
115
+ };`.replaceAll('\n\n', '\n').replaceAll('\n\n', '\n')
116
+ }).join('\n')}
123
117
  };`;
124
118
  };
125
119
 
126
- const code = await precompile();
127
- // console.log(code);
128
-
129
- fs.writeFileSync(join(__dirname, 'generated_builtins.js'), code);
120
+ fs.writeFileSync(join(__dirname, 'generated_builtins.js'), await precompile());
package/compiler/prefs.js CHANGED
@@ -1,6 +1,6 @@
1
- const onByDefault = [ 'bytestring' ];
1
+ const onByDefault = [ 'bytestring', 'aotPointerOpt', 'treeshakeWasmImports', 'alwaysMemory' ];
2
2
 
3
- const cache = {};
3
+ let cache = {};
4
4
  const obj = new Proxy({}, {
5
5
  get(_, p) {
6
6
  // intentionally misses with undefined values cached
@@ -9,11 +9,12 @@ const obj = new Proxy({}, {
9
9
  return cache[p] = (() => {
10
10
  // fooBar -> foo-bar
11
11
  const name = p[0] === '_' ? p : p.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`);
12
- if (process.argv.includes('-' + name)) return true;
13
- if (process.argv.includes('-no-' + name)) return false;
12
+ const prefix = name.length === 1 ? '-' : '--';
13
+ if (process.argv.includes(prefix + name)) return true;
14
+ if (process.argv.includes(prefix + 'no-' + name)) return false;
14
15
 
15
- const valArg = process.argv.find(x => x.startsWith(`-${name}=`));
16
- if (valArg) return valArg.slice(name.length + 2);
16
+ const valArg = process.argv.find(x => x.startsWith(`${prefix}${name}=`));
17
+ if (valArg) return valArg.slice(name.length + 1 + prefix.length);
17
18
 
18
19
  if (onByDefault.includes(p)) return true;
19
20
  return undefined;
@@ -1,26 +1,10 @@
1
- import { Opcodes, Blocktype, Valtype, ValtypeSize, PageSize } from "./wasmSpec.js";
2
- import { number } from "./embedding.js";
3
- import { unsignedLEB128 } from "./encoding.js";
4
- import { UNDEFINED } from "./builtins.js";
1
+ import { Opcodes, Blocktype, Valtype, ValtypeSize } from './wasmSpec.js';
2
+ import { number } from './embedding.js';
3
+ import { unsignedLEB128 } from './encoding.js';
4
+ import { UNDEFINED } from './builtins.js';
5
+ import { TYPES } from './types.js';
5
6
  import Prefs from './prefs.js';
6
7
 
7
- // todo: do not duplicate this
8
- const TYPES = {
9
- number: 0x00,
10
- boolean: 0x01,
11
- string: 0x02,
12
- undefined: 0x03,
13
- object: 0x04,
14
- function: 0x05,
15
- symbol: 0x06,
16
- bigint: 0x07,
17
-
18
- // these are not "typeof" types but tracked internally
19
- _array: 0x10,
20
- _regexp: 0x11,
21
- _bytestring: 0x12
22
- };
23
-
24
8
  // todo: turn these into built-ins once arrays and these become less hacky
25
9
 
26
10
  export const PrototypeFuncs = function() {
@@ -30,7 +14,7 @@ export const PrototypeFuncs = function() {
30
14
  if (Prefs.zeroChecks) zeroChecks = Prefs.zeroChecks.split('=')[1].split(',').reduce((acc, x) => { acc[x.toLowerCase()] = true; return acc; }, {});
31
15
  else zeroChecks = {};
32
16
 
33
- this[TYPES._array] = {
17
+ this[TYPES.array] = {
34
18
  // lX = local accessor of X ({ get, set }), iX = local index of X, wX = wasm ops of X
35
19
  at: (pointer, length, wIndex, iTmp) => [
36
20
  ...wIndex,
@@ -150,7 +134,7 @@ export const PrototypeFuncs = function() {
150
134
  shift: (pointer, length) => [
151
135
  // if length == 0, noop
152
136
  ...length.getCachedI32(),
153
- Opcodes.i32_eqz,
137
+ [ Opcodes.i32_eqz ],
154
138
  [ Opcodes.if, Blocktype.void ],
155
139
  ...number(UNDEFINED),
156
140
  [ Opcodes.br, 1 ],
@@ -269,10 +253,10 @@ export const PrototypeFuncs = function() {
269
253
  ]
270
254
  };
271
255
 
272
- this[TYPES._array].at.local = Valtype.i32;
273
- this[TYPES._array].push.noArgRetLength = true;
274
- this[TYPES._array].fill.local = valtypeBinary;
275
- this[TYPES._array].fill.returnType = TYPES._array;
256
+ this[TYPES.array].at.local = Valtype.i32;
257
+ this[TYPES.array].push.noArgRetLength = true;
258
+ this[TYPES.array].fill.local = valtypeBinary;
259
+ this[TYPES.array].fill.returnType = TYPES.array;
276
260
 
277
261
  this[TYPES.string] = {
278
262
  at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
@@ -492,7 +476,7 @@ export const PrototypeFuncs = function() {
492
476
  this[TYPES.string].isWellFormed.returnType = TYPES.boolean;
493
477
 
494
478
  if (Prefs.bytestring) {
495
- this[TYPES._bytestring] = {
479
+ this[TYPES.bytestring] = {
496
480
  at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
497
481
  const [ newOut, newPointer ] = arrayShell(1, 'i8');
498
482
 
@@ -622,14 +606,14 @@ export const PrototypeFuncs = function() {
622
606
  }
623
607
  };
624
608
 
625
- this[TYPES._bytestring].at.local = Valtype.i32;
626
- this[TYPES._bytestring].at.returnType = TYPES._bytestring;
627
- this[TYPES._bytestring].charAt.returnType = TYPES._bytestring;
628
- this[TYPES._bytestring].charCodeAt.local = Valtype.i32;
629
- this[TYPES._bytestring].charCodeAt.noPointerCache = zeroChecks.charcodeat;
609
+ this[TYPES.bytestring].at.local = Valtype.i32;
610
+ this[TYPES.bytestring].at.returnType = TYPES.bytestring;
611
+ this[TYPES.bytestring].charAt.returnType = TYPES.bytestring;
612
+ this[TYPES.bytestring].charCodeAt.local = Valtype.i32;
613
+ this[TYPES.bytestring].charCodeAt.noPointerCache = zeroChecks.charcodeat;
630
614
 
631
- this[TYPES._bytestring].isWellFormed.local = Valtype.i32;
632
- this[TYPES._bytestring].isWellFormed.local2 = Valtype.i32;
633
- this[TYPES._bytestring].isWellFormed.returnType = TYPES.boolean;
615
+ this[TYPES.bytestring].isWellFormed.local = Valtype.i32;
616
+ this[TYPES.bytestring].isWellFormed.local2 = Valtype.i32;
617
+ this[TYPES.bytestring].isWellFormed.returnType = TYPES.boolean;
634
618
  }
635
619
  };
@@ -0,0 +1,38 @@
1
+ export const TYPES = {
2
+ number: 0x00,
3
+ boolean: 0x01,
4
+ string: 0x02,
5
+ undefined: 0x03,
6
+ object: 0x04,
7
+ function: 0x05,
8
+ symbol: 0x06,
9
+ bigint: 0x07
10
+ };
11
+
12
+ export const TYPE_NAMES = {
13
+ [TYPES.number]: 'Number',
14
+ [TYPES.boolean]: 'Boolean',
15
+ [TYPES.string]: 'String',
16
+ [TYPES.undefined]: 'undefined',
17
+ [TYPES.object]: 'Object',
18
+ [TYPES.function]: 'Function',
19
+ [TYPES.symbol]: 'Symbol',
20
+ [TYPES.bigint]: 'BigInt'
21
+ };
22
+
23
+ export const INTERNAL_TYPE_BASE = 0x10;
24
+ let internalTypeIndex = INTERNAL_TYPE_BASE;
25
+ const registerInternalType = name => {
26
+ const n = internalTypeIndex++;
27
+ TYPES[name.toLowerCase()] = n;
28
+ TYPE_NAMES[n] = name;
29
+ };
30
+
31
+ // note: when adding a new internal type, please also add a deserializer to wrap.js
32
+ // (it is okay to add a throw todo deserializer for wips)
33
+
34
+ registerInternalType('Array');
35
+ registerInternalType('RegExp');
36
+ registerInternalType('ByteString');
37
+ registerInternalType('Date');
38
+ registerInternalType('Set');
@@ -1,4 +1,13 @@
1
- import { enumify } from "../util/enum.js";
1
+ const enumify = (...args) => {
2
+ const obj = {};
3
+
4
+ for (let i = 0; i < args.length; i++) {
5
+ obj[i] = args[i];
6
+ obj[args[i]] = i;
7
+ }
8
+
9
+ return obj;
10
+ };
2
11
 
3
12
  export const Section = enumify('custom', 'type', 'import', 'func', 'table', 'memory', 'global', 'export', 'start', 'element', 'code', 'data', 'data_count', 'tag');
4
13
  export const ExportDesc = enumify('func', 'table', 'mem', 'global', 'tag');
@@ -76,6 +85,7 @@ export const Opcodes = {
76
85
  i64_store8: 0x3c,
77
86
  i64_store16: 0x3d,
78
87
 
88
+ memory_size: 0x3f,
79
89
  memory_grow: 0x40,
80
90
 
81
91
  i32_const: 0x41,
@@ -107,6 +117,8 @@ export const Opcodes = {
107
117
  i32_shl: 0x74,
108
118
  i32_shr_s: 0x75,
109
119
  i32_shr_u: 0x76,
120
+ i32_rotl: 0x77,
121
+ i32_rotr: 0x78,
110
122
 
111
123
  i64_eqz: 0x50,
112
124
  i64_eq: 0x51,
@@ -130,6 +142,7 @@ export const Opcodes = {
130
142
  i64_shr_s: 0x87,
131
143
  i64_shr_u: 0x88,
132
144
  i64_rotl: 0x89,
145
+ i64_rotr: 0x8a,
133
146
 
134
147
  f64_eq: 0x61,
135
148
  f64_ne: 0x62,