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.
- package/CONTRIBUTING.md +255 -0
- package/LICENSE +20 -20
- package/README.md +115 -82
- package/asur/index.js +624 -340
- package/byg/index.js +237 -0
- package/compiler/2c.js +1 -1
- package/compiler/{sections.js → assemble.js} +59 -12
- package/compiler/builtins/annexb_string.js +72 -0
- package/compiler/builtins/annexb_string.ts +18 -0
- package/compiler/builtins/array.ts +145 -0
- package/compiler/builtins/base64.ts +7 -84
- package/compiler/builtins/crypto.ts +29 -41
- package/compiler/builtins/date.ts +2071 -0
- package/compiler/builtins/escape.ts +141 -0
- package/compiler/builtins/int.ts +147 -0
- package/compiler/builtins/number.ts +527 -0
- package/compiler/builtins/porffor.d.ts +42 -9
- package/compiler/builtins/string.ts +1055 -0
- package/compiler/builtins/tostring.ts +45 -0
- package/compiler/builtins.js +58 -85
- package/compiler/{codeGen.js → codegen.js} +792 -279
- package/compiler/decompile.js +0 -1
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +108 -10
- package/compiler/generated_builtins.js +1463 -7
- package/compiler/index.js +16 -14
- package/compiler/log.js +2 -2
- package/compiler/opt.js +23 -22
- package/compiler/parse.js +30 -22
- package/compiler/precompile.js +25 -26
- package/compiler/prefs.js +7 -6
- package/compiler/prototype.js +2 -18
- package/compiler/types.js +37 -0
- package/compiler/wasmSpec.js +11 -1
- package/compiler/wrap.js +41 -44
- package/package.json +9 -5
- package/porf +2 -0
- package/rhemyn/compile.js +44 -26
- package/rhemyn/parse.js +322 -320
- package/rhemyn/test/parse.js +58 -58
- package/runner/compare.js +34 -34
- package/runner/debug.js +122 -0
- package/runner/index.js +69 -12
- package/runner/profiler.js +45 -26
- package/runner/repl.js +42 -9
- package/runner/sizes.js +37 -37
- package/runner/info.js +0 -89
- package/runner/transform.js +0 -15
- 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
|
3
|
+
import codegen from './codegen.js';
|
4
4
|
import opt from './opt.js';
|
5
|
-
import
|
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
|
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 (
|
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 } =
|
39
|
-
if (
|
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 (
|
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
|
51
|
-
if (
|
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
|
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(
|
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 = '
|
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
|
-
|
8
|
+
assemble: [ 20, 250, 80 ],
|
9
9
|
alloc: [ 250, 250, 20 ],
|
10
|
-
|
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
|
-
|
104
|
-
const
|
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?
|
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
|
-
|
135
|
+
// if (inst[0] === Opcodes.throw) {
|
136
|
+
// tagUse[inst[1]]++;
|
136
137
|
|
137
|
-
|
138
|
-
|
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.
|
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.
|
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
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
}
|
562
|
-
|
563
|
-
for (const x of Object.keys(exceptionUse).sort((a, b) => b - a)) {
|
564
|
-
|
565
|
-
|
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
|
-
|
25
|
+
globalThis.parser = '';
|
26
|
+
let parse;
|
24
27
|
const loadParser = async (fallbackParser = 'acorn', forceParser) => {
|
25
|
-
parser = forceParser ?? process.argv.find(x => x.startsWith('
|
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('
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
+
try {
|
38
|
+
const ast = parse(input, {
|
39
|
+
// acorn
|
40
|
+
ecmaVersion: 'latest',
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
+
// meriyah
|
43
|
+
next: true,
|
44
|
+
module: flags.includes('module'),
|
45
|
+
webcompat: true,
|
42
46
|
|
43
|
-
|
44
|
-
|
47
|
+
// babel
|
48
|
+
plugins: types ? ['estree', 'typescript'] : ['estree'],
|
45
49
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
// multiple
|
51
|
+
sourceType: flags.includes('module') ? 'module' : 'script',
|
52
|
+
ranges: false,
|
53
|
+
tokens: false,
|
54
|
+
comments: false,
|
55
|
+
});
|
52
56
|
|
53
|
-
|
57
|
+
if (ast.type === 'File') return ast.program;
|
54
58
|
|
55
|
-
|
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
|
};
|
package/compiler/precompile.js
CHANGED
@@ -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
|
-
|
32
|
-
|
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 = ['
|
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)
|
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, ${
|
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
|
-
|
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
|
-
|
13
|
-
if (process.argv.includes(
|
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(
|
16
|
-
if (valArg) return valArg.slice(name.length +
|
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;
|
package/compiler/prototype.js
CHANGED
@@ -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');
|
package/compiler/wasmSpec.js
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
-
|
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 (
|
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:
|
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 (
|
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 (
|
170
|
-
case
|
171
|
-
case
|
172
|
-
case
|
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
|
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
|
-
|
179
|
-
|
168
|
+
return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
169
|
+
}
|
180
170
|
|
181
|
-
|
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
|
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
|
-
|
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
|
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
|
199
|
-
|
200
|
-
const
|
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
|
-
|
205
|
-
return {[func.name]() {}}[func.name];
|
202
|
+
return new Date(value);
|
206
203
|
}
|
207
204
|
|
208
205
|
default: return ret;
|