porffor 0.2.0-5ac7ea0 → 0.2.0-5ad562e

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 (49) hide show
  1. package/CONTRIBUTING.md +255 -0
  2. package/LICENSE +20 -20
  3. package/README.md +115 -82
  4. package/asur/index.js +624 -340
  5. package/byg/index.js +237 -0
  6. package/compiler/2c.js +1 -1
  7. package/compiler/{sections.js → assemble.js} +59 -12
  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/crypto.ts +29 -41
  13. package/compiler/builtins/date.ts +2071 -0
  14. package/compiler/builtins/escape.ts +141 -0
  15. package/compiler/builtins/int.ts +147 -0
  16. package/compiler/builtins/number.ts +527 -0
  17. package/compiler/builtins/porffor.d.ts +42 -9
  18. package/compiler/builtins/string.ts +1055 -0
  19. package/compiler/builtins/tostring.ts +45 -0
  20. package/compiler/builtins.js +58 -85
  21. package/compiler/{codeGen.js → codegen.js} +792 -279
  22. package/compiler/decompile.js +0 -1
  23. package/compiler/embedding.js +22 -22
  24. package/compiler/encoding.js +108 -10
  25. package/compiler/generated_builtins.js +1463 -7
  26. package/compiler/index.js +16 -14
  27. package/compiler/log.js +2 -2
  28. package/compiler/opt.js +23 -22
  29. package/compiler/parse.js +30 -22
  30. package/compiler/precompile.js +25 -26
  31. package/compiler/prefs.js +7 -6
  32. package/compiler/prototype.js +2 -18
  33. package/compiler/types.js +37 -0
  34. package/compiler/wasmSpec.js +11 -1
  35. package/compiler/wrap.js +41 -44
  36. package/package.json +9 -5
  37. package/porf +2 -0
  38. package/rhemyn/compile.js +44 -26
  39. package/rhemyn/parse.js +322 -320
  40. package/rhemyn/test/parse.js +58 -58
  41. package/runner/compare.js +34 -34
  42. package/runner/debug.js +122 -0
  43. package/runner/index.js +69 -12
  44. package/runner/profiler.js +45 -26
  45. package/runner/repl.js +42 -9
  46. package/runner/sizes.js +37 -37
  47. package/runner/info.js +0 -89
  48. package/runner/transform.js +0 -15
  49. 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,19 @@ export default (code, flags) => {
88
88
  if (compiler === 'zig') compiler = [ 'zig', 'cc' ];
89
89
  else compiler = [ compiler ];
90
90
 
91
- const tmpfile = 'tmp.c';
91
+ const tmpfile = 'porffor_tmp.c';
92
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
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' ];
94
94
  const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-ffast-math', '-fno-exceptions' ];
95
95
 
96
96
  const c = toc(out);
97
- writeFileSync(tmpfile, c);
97
+ fs.writeFileSync(tmpfile, c);
98
98
 
99
99
  // obvious command escape is obvious
100
100
  execSync(args.join(' '), { stdio: 'inherit' });
101
101
 
102
+ fs.unlinkSync(tmpfile);
103
+
102
104
  if (process.version) process.exit();
103
105
  }
104
106
 
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
@@ -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) {
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) {
182
183
  let j = i;
183
184
  for (; j < wasm.length; j++) {
184
185
  const op = wasm[j];
@@ -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
@@ -9,8 +9,10 @@ if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
9
9
  globalThis.process = { argv: ['', '', ...Deno.args], stdout: { write: str => Deno.writeAllSync(Deno.stdout, textEncoder.encode(str)) } };
10
10
  }
11
11
 
12
+ const file = process.argv.slice(2).find(x => x[0] !== '-');
13
+
12
14
  // should we try to support types (while parsing)
13
- const types = Prefs.parseTypes;
15
+ const types = Prefs.parseTypes || file?.endsWith('.ts');
14
16
  globalThis.typedInput = types && Prefs.optTypes;
15
17
 
16
18
  // todo: review which to use by default
@@ -20,37 +22,43 @@ globalThis.typedInput = types && Prefs.optTypes;
20
22
  // - hermes-parser
21
23
  // - @babel/parser
22
24
 
23
- let parser, parse;
25
+ globalThis.parser = '';
26
+ let parse;
24
27
  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));
28
+ parser = forceParser ?? process.argv.find(x => x.startsWith('--parser='))?.split('=')?.[1] ?? fallbackParser;
29
+ 0, { parse } = (await import((globalThis.document || globalThis.Deno ? 'https://esm.sh/' : '') + parser));
27
30
  };
28
31
  globalThis._porf_loadParser = loadParser;
29
32
  await loadParser(types ? '@babel/parser' : undefined);
30
33
 
31
- if (types && !['@babel/parser', 'hermes-parser'].includes(parser)) log.warning('parser', `passed -types with a parser (${parser}) which does not support`);
34
+ if (types && !['@babel/parser', 'hermes-parser'].includes(parser)) log.warning('parse', `passed -parse-types with a parser (${parser}) which does not support`);
32
35
 
33
36
  export default (input, flags) => {
34
- const ast = parse(input, {
35
- // acorn
36
- ecmaVersion: 'latest',
37
+ try {
38
+ const ast = parse(input, {
39
+ // acorn
40
+ ecmaVersion: 'latest',
37
41
 
38
- // meriyah
39
- next: true,
40
- module: flags.includes('module'),
41
- webcompat: true,
42
+ // meriyah
43
+ next: true,
44
+ module: flags.includes('module'),
45
+ webcompat: true,
42
46
 
43
- // babel
44
- plugins: types ? ['estree', 'typescript'] : ['estree'],
47
+ // babel
48
+ plugins: types ? ['estree', 'typescript'] : ['estree'],
45
49
 
46
- // multiple
47
- sourceType: flags.includes('module') ? 'module' : 'script',
48
- ranges: false,
49
- tokens: false,
50
- comments: false,
51
- });
50
+ // multiple
51
+ sourceType: flags.includes('module') ? 'module' : 'script',
52
+ ranges: false,
53
+ tokens: false,
54
+ comments: false,
55
+ });
52
56
 
53
- if (ast.type === 'File') return ast.program;
57
+ if (ast.type === 'File') return ast.program;
54
58
 
55
- return ast;
59
+ return ast;
60
+ } catch (e) {
61
+ // normalize error class thrown by 3rd party parsers
62
+ throw new SyntaxError(e.message, { cause: e });
63
+ }
56
64
  };
@@ -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,33 +7,21 @@ 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
10
  // import porfParse from './parse.js';
26
11
  // import porfCodegen from './codeGen.js';
27
12
 
28
13
  const argv = process.argv.slice();
29
14
 
30
15
  const compile = async (file, [ _funcs, _globals ]) => {
31
- const source = fs.readFileSync(file, 'utf8');
32
- const first = source.slice(0, source.indexOf('\n'));
16
+ let source = fs.readFileSync(file, 'utf8');
17
+ let first = source.slice(0, source.indexOf('\n'));
18
+
19
+ if (first.startsWith('export default')) {
20
+ source = (await import(file)).default();
21
+ first = source.slice(0, source.indexOf('\n'));
22
+ }
33
23
 
34
- let args = ['-bytestring'];
35
- if (file.endsWith('.ts')) args.push('-parse-types', '-opt-types');
24
+ let args = ['--bytestring', '--todo-time=compile', '--no-aot-pointer-opt', '--no-treeshake-wasm-imports', '--scoped-page-names', '--parse-types', '--opt-types'];
36
25
  if (first.startsWith('// @porf')) {
37
26
  args = args.concat(first.slice('// @porf '.length).split(' '));
38
27
  }
@@ -49,9 +38,14 @@ const compile = async (file, [ _funcs, _globals ]) => {
49
38
 
50
39
  const allocated = new Set();
51
40
 
52
- const exports = funcs.filter(x => x.export);
41
+ const exports = funcs.filter(x => x.export && x.name !== 'main');
53
42
  for (const x of exports) {
54
- if (x.data) x.data = x.data.map(x => data[x]);
43
+ if (x.data) {
44
+ x.data = x.data.map(x => data[x]);
45
+ for (const y in x.data) {
46
+ x.data[y].offset -= x.data[0].offset;
47
+ }
48
+ }
55
49
  if (x.exceptions) x.exceptions = x.exceptions.map(x => {
56
50
  const obj = exceptions[x];
57
51
  if (obj) obj.exceptId = x;
@@ -83,7 +77,7 @@ const compile = async (file, [ _funcs, _globals ]) => {
83
77
  if (!pageName || allocated.has(pageName)) continue;
84
78
  allocated.add(pageName);
85
79
 
86
- y.splice(0, 10, 'alloc', pageName, x.pages.get(pageName).type);
80
+ y.splice(0, 10, 'alloc', pageName, x.pages.get(pageName).type, valtypeBinary);
87
81
  // y.push(x.pages.get(pageName));
88
82
  }
89
83
  }
@@ -99,9 +93,14 @@ const precompile = async () => {
99
93
  let funcs = [], globals = [];
100
94
  for (const file of fs.readdirSync(dir)) {
101
95
  if (file.endsWith('.d.ts')) continue;
96
+ console.log(file);
97
+
102
98
  await compile(join(dir, file), [ funcs, globals ]);
103
99
  }
104
100
 
101
+ // const a = funcs.find(x => x.name === '__ecma262_ToUTCDTSF');
102
+ // console.log(Object.values(a.locals).slice(a.params.length));
103
+
105
104
  // ${x.pages && x.pages.size > 0 ? ` pages: ${JSON.stringify(Object.fromEntries(x.pages.entries()))},` : ''}
106
105
  // ${x.used && x.used.length > 0 ? ` used: ${JSON.stringify(x.used)},` : ''}
107
106
 
@@ -110,16 +109,16 @@ import { number } from './embedding.js';
110
109
 
111
110
  export const BuiltinFuncs = function() {
112
111
  ${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}')]`)},
112
+ wasm: (scope, { allocPage, builtin }) => ${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}')]`)},
114
113
  params: ${JSON.stringify(x.params)},
115
114
  typedParams: true,
116
115
  returns: ${JSON.stringify(x.returns)},
117
- typedReturns: true,
116
+ ${x.returnType != null ? `returnType: ${JSON.stringify(x.returnType)}` : 'typedReturns: true'},
118
117
  locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length).map(x => x.type))},
119
118
  localNames: ${JSON.stringify(Object.keys(x.locals))},
120
119
  ${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)},` : ''}
121
120
  ${x.exceptions && x.exceptions.length > 0 ? ` exceptions: ${JSON.stringify(x.exceptions)},` : ''}
122
- };`.replaceAll('\n\n', '\n')).join('\n')}
121
+ };`.replaceAll('\n\n', '\n').replaceAll('\n\n', '\n')).join('\n')}
123
122
  };`;
124
123
  };
125
124
 
package/compiler/prefs.js CHANGED
@@ -1,6 +1,6 @@
1
- const onByDefault = [ 'bytestring' ];
1
+ const onByDefault = [ 'bytestring', 'aotPointerOpt', 'treeshakeWasmImports' ];
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;
@@ -3,23 +3,7 @@ import { number } from "./embedding.js";
3
3
  import { unsignedLEB128 } from "./encoding.js";
4
4
  import { UNDEFINED } from "./builtins.js";
5
5
  import Prefs from './prefs.js';
6
-
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
- };
6
+ import { TYPES } from './types.js';
23
7
 
24
8
  // todo: turn these into built-ins once arrays and these become less hacky
25
9
 
@@ -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 ],
@@ -0,0 +1,37 @@
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');
@@ -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,
package/compiler/wrap.js CHANGED
@@ -3,39 +3,24 @@ import decompile from './decompile.js';
3
3
  import { encodeVector, encodeLocal } from './encoding.js';
4
4
  import Prefs from './prefs.js';
5
5
  import { log } from './log.js';
6
+ import { TYPES } from './types.js';
6
7
 
7
8
  const bold = x => `\u001b[1m${x}\u001b[0m`;
8
9
 
9
- const typeBase = 0x00;
10
- const internalTypeBase = 0x10;
11
- const TYPES = {
12
- [typeBase]: 'number',
13
- [typeBase + 1]: 'boolean',
14
- [typeBase + 2]: 'string',
15
- [typeBase + 3]: 'undefined',
16
- [typeBase + 4]: 'object',
17
- [typeBase + 5]: 'function',
18
- [typeBase + 6]: 'symbol',
19
- [typeBase + 7]: 'bigint',
20
-
21
- // internal
22
- [internalTypeBase]: '_array',
23
- [internalTypeBase + 1]: '_regexp',
24
- [internalTypeBase + 2]: '_bytestring'
25
- };
26
-
27
10
  export default async (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
28
11
  const times = [];
29
12
 
30
13
  const t1 = performance.now();
31
14
  const { wasm, funcs, globals, tags, exceptions, pages, c } = compile(source, flags);
32
15
 
16
+ globalThis.porfDebugInfo = { funcs, globals };
17
+
33
18
  if (source.includes('export function')) flags.push('module');
34
19
 
35
20
  // (await import('node:fs')).writeFileSync('out.wasm', Buffer.from(wasm));
36
21
 
37
22
  times.push(performance.now() - t1);
38
- if (flags.includes('info')) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
23
+ if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
39
24
 
40
25
  const t2 = performance.now();
41
26
 
@@ -51,7 +36,10 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
51
36
  '': {
52
37
  p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
53
38
  c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
54
- t: _ => performance.now(),
39
+ t: () => performance.now(),
40
+ u: () => performance.timeOrigin,
41
+ y: () => {},
42
+ z: () => {},
55
43
  ...customImports
56
44
  }
57
45
  });
@@ -59,8 +47,10 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
59
47
  // only backtrace for runner, not test262/etc
60
48
  if (!process.argv[1].includes('/runner')) throw e;
61
49
 
62
- const funcInd = parseInt(e.message.match(/function #([0-9]+) /)[1]);
63
- const blobOffset = parseInt(e.message.split('@')[1]);
50
+ const funcInd = parseInt(e.message.match(/function #([0-9]+) /)?.[1]);
51
+ const blobOffset = parseInt(e.message.split('@')?.[1]);
52
+
53
+ if (!funcInd) throw e;
64
54
 
65
55
  // convert blob offset -> function wasm offset.
66
56
  // this is not good code and is somewhat duplicated
@@ -138,7 +128,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
138
128
  }
139
129
 
140
130
  times.push(performance.now() - t2);
141
- if (flags.includes('info')) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
131
+ if (Prefs.profileCompiler) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
142
132
 
143
133
  const exports = {};
144
134
 
@@ -166,43 +156,50 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
166
156
 
167
157
  // if (ret >= typeBase && ret <= typeBase + 8) return ret > (typeBase + 7) ? 'object' : TYPES[ret];
168
158
 
169
- switch (TYPES[type]) {
170
- case 'boolean': return Boolean(ret);
171
- case 'undefined': return undefined;
172
- case 'object': return ret === 0 ? null : {};
159
+ switch (type) {
160
+ case TYPES.boolean: return Boolean(ret);
161
+ case TYPES.undefined: return undefined;
162
+ case TYPES.object: return ret === 0 ? null : {};
173
163
 
174
- case '_array': {
164
+ case TYPES.string: {
175
165
  const pointer = ret;
176
- const length = new Int32Array(memory.buffer, pointer, 1);
166
+ const length = (new Int32Array(memory.buffer, pointer, 1))[0];
177
167
 
178
- // have to slice because of memory alignment
179
- const buf = memory.buffer.slice(pointer + 4, pointer + 4 + 8 * length);
168
+ return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
169
+ }
180
170
 
181
- return Array.from(new Float64Array(buf));
171
+ case TYPES.function: {
172
+ // wasm func index, including all imports
173
+ const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
174
+ // if (!func) return ret;
175
+ if (!func) return function () {};
176
+
177
+ // make fake empty func for repl/etc
178
+ return {[func.name]() {}}[func.name];
182
179
  }
183
180
 
184
- case 'string': {
181
+ case TYPES._array: {
185
182
  const pointer = ret;
186
- const length = new Int32Array(memory.buffer, pointer, 1);
183
+ const length = (new Int32Array(memory.buffer, pointer, 1))[0];
187
184
 
188
- return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
185
+ // have to slice because of memory alignment
186
+ const buf = memory.buffer.slice(pointer + 4, pointer + 4 + 8 * length);
187
+
188
+ return Array.from(new Float64Array(buf));
189
189
  }
190
190
 
191
- case '_bytestring': {
191
+ case TYPES._bytestring: {
192
192
  const pointer = ret;
193
- const length = new Int32Array(memory.buffer, pointer, 1);
193
+ const length = (new Int32Array(memory.buffer, pointer, 1))[0];
194
194
 
195
195
  return Array.from(new Uint8Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
196
196
  }
197
197
 
198
- case 'function': {
199
- // wasm func index, including all imports
200
- const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
201
- // if (!func) return ret;
202
- if (!func) return function () {};
198
+ case TYPES._date: {
199
+ const pointer = ret;
200
+ const value = (new Float64Array(memory.buffer, pointer, 1))[0];
203
201
 
204
- // make fake empty func for repl/etc
205
- return {[func.name]() {}}[func.name];
202
+ return new Date(value);
206
203
  }
207
204
 
208
205
  default: return ret;