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.
- package/CONTRIBUTING.md +256 -0
- package/LICENSE +20 -20
- package/README.md +115 -82
- package/asur/index.js +624 -340
- package/byg/index.js +216 -0
- package/compiler/2c.js +2 -53
- package/compiler/{sections.js → assemble.js} +60 -14
- 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/boolean.ts +18 -0
- package/compiler/builtins/crypto.ts +120 -0
- package/compiler/builtins/date.ts +2067 -0
- package/compiler/builtins/escape.ts +141 -0
- package/compiler/builtins/function.ts +5 -0
- package/compiler/builtins/int.ts +145 -0
- package/compiler/builtins/number.ts +529 -0
- package/compiler/builtins/object.ts +4 -0
- package/compiler/builtins/porffor.d.ts +44 -7
- package/compiler/builtins/set.ts +187 -0
- package/compiler/builtins/string.ts +1080 -0
- package/compiler/builtins.js +400 -120
- package/compiler/{codeGen.js → codegen.js} +850 -402
- package/compiler/decompile.js +2 -3
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +94 -10
- package/compiler/expression.js +1 -1
- package/compiler/generated_builtins.js +1613 -3
- package/compiler/index.js +16 -16
- package/compiler/log.js +2 -2
- package/compiler/opt.js +28 -27
- package/compiler/parse.js +36 -30
- package/compiler/precompile.js +37 -46
- package/compiler/prefs.js +7 -6
- package/compiler/prototype.js +20 -36
- package/compiler/types.js +38 -0
- package/compiler/wasmSpec.js +14 -1
- package/compiler/wrap.js +79 -69
- 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 +33 -34
- package/runner/debug.js +117 -0
- package/runner/index.js +69 -12
- package/runner/profiler.js +22 -30
- package/runner/repl.js +40 -13
- package/runner/sizes.js +37 -37
- package/runner/version.js +3 -3
- 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,17 @@ export default (code, flags) => {
|
|
88
88
|
if (compiler === 'zig') compiler = [ 'zig', 'cc' ];
|
89
89
|
else compiler = [ compiler ];
|
90
90
|
|
91
|
-
const tmpfile = '
|
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
|
-
|
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
@@ -1,7 +1,7 @@
|
|
1
|
-
import { Opcodes, Valtype } from
|
2
|
-
import { number } from
|
3
|
-
import { read_signedLEB128, read_ieee754_binary64 } from
|
4
|
-
import { log } from
|
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
|
-
|
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 && !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.
|
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
|
-
|
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
@@ -1,16 +1,16 @@
|
|
1
|
-
import { log } from
|
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
|
-
|
23
|
+
globalThis.parser = '';
|
24
|
+
let parse;
|
24
25
|
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));
|
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('
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
};
|
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,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
|
-
|
32
|
-
|
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 = ['
|
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)
|
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.
|
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
|
-
|
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 =>
|
113
|
-
|
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
|
-
|
122
|
-
|
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
|
-
|
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
|
-
|
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
@@ -1,26 +1,10 @@
|
|
1
|
-
import { Opcodes, Blocktype, Valtype, ValtypeSize
|
2
|
-
import { number } from
|
3
|
-
import { unsignedLEB128 } from
|
4
|
-
import { UNDEFINED } from
|
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.
|
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.
|
273
|
-
this[TYPES.
|
274
|
-
this[TYPES.
|
275
|
-
this[TYPES.
|
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.
|
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.
|
626
|
-
this[TYPES.
|
627
|
-
this[TYPES.
|
628
|
-
this[TYPES.
|
629
|
-
this[TYPES.
|
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.
|
632
|
-
this[TYPES.
|
633
|
-
this[TYPES.
|
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');
|
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,
|
@@ -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,
|