porffor 0.14.0-7bef6473d → 0.14.0-7f4307d22
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 +9 -3
- package/README.md +8 -12
- package/asur/index.js +1 -1
- package/compiler/2c.js +65 -2
- package/compiler/assemble.js +26 -1
- package/compiler/builtins/annexb_string.ts +1 -0
- package/compiler/builtins/array.ts +84 -4
- package/compiler/builtins/base64.ts +1 -0
- package/compiler/builtins/boolean.ts +3 -1
- package/compiler/builtins/crypto.ts +1 -0
- package/compiler/builtins/date.ts +2 -0
- package/compiler/builtins/error.js +22 -0
- package/compiler/builtins/escape.ts +1 -2
- package/compiler/builtins/function.ts +2 -0
- package/compiler/builtins/int.ts +2 -0
- package/compiler/builtins/math.ts +410 -0
- package/compiler/builtins/number.ts +2 -0
- package/compiler/builtins/object.ts +2 -0
- package/compiler/builtins/set.ts +19 -7
- package/compiler/builtins/string.ts +1 -0
- package/compiler/builtins/symbol.ts +62 -0
- package/compiler/builtins.js +37 -8
- package/compiler/codegen.js +635 -263
- package/compiler/decompile.js +1 -1
- package/compiler/generated_builtins.js +545 -60
- package/compiler/index.js +5 -9
- package/compiler/parse.js +1 -1
- package/compiler/precompile.js +5 -4
- package/compiler/prefs.js +6 -2
- package/compiler/prototype.js +180 -157
- package/compiler/wrap.js +91 -44
- package/package.json +1 -1
- package/runner/index.js +5 -4
- package/runner/repl.js +18 -2
package/compiler/wrap.js
CHANGED
@@ -1,22 +1,27 @@
|
|
1
|
+
import { encodeVector, encodeLocal } from './encoding.js';
|
2
|
+
import { importedFuncs } from './builtins.js';
|
1
3
|
import compile from './index.js';
|
2
4
|
import decompile from './decompile.js';
|
3
|
-
import { encodeVector, encodeLocal } from './encoding.js';
|
4
5
|
import { TYPES } from './types.js';
|
5
6
|
import { log } from './log.js';
|
6
7
|
import Prefs from './prefs.js';
|
7
8
|
|
8
9
|
const bold = x => `\u001b[1m${x}\u001b[0m`;
|
9
10
|
|
10
|
-
const porfToJSValue = (memory, funcs, value, type) => {
|
11
|
+
const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
|
11
12
|
switch (type) {
|
12
13
|
case TYPES.boolean: return Boolean(value);
|
13
14
|
case TYPES.undefined: return undefined;
|
14
15
|
case TYPES.object: return value === 0 ? null : {};
|
15
16
|
|
16
17
|
case TYPES.function: {
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
let func;
|
19
|
+
if (value < 0) {
|
20
|
+
func = importedFuncs[value + importedFuncs.length];
|
21
|
+
} else {
|
22
|
+
func = funcs.find(x => ((x.originalIndex ?? x.index) - importedFuncs.length) === value);
|
23
|
+
}
|
24
|
+
|
20
25
|
if (!func) return function () {};
|
21
26
|
|
22
27
|
// make fake empty func for repl/etc
|
@@ -36,9 +41,22 @@ const porfToJSValue = (memory, funcs, value, type) => {
|
|
36
41
|
case TYPES.array: {
|
37
42
|
const length = (new Int32Array(memory.buffer, value, 1))[0];
|
38
43
|
|
39
|
-
|
40
|
-
|
41
|
-
|
44
|
+
const out = [];
|
45
|
+
for (let i = 0; i < length; i++) {
|
46
|
+
const offset = value + 4 + (i * 9);
|
47
|
+
|
48
|
+
// have to slice because of memory alignment (?)
|
49
|
+
const v = (new Float64Array(memory.buffer.slice(offset, offset + 8), 0, 1))[0];
|
50
|
+
const t = (new Uint8Array(memory.buffer, offset + 8, 1))[0];
|
51
|
+
|
52
|
+
// console.log(`reading value at index ${i}...`)
|
53
|
+
// console.log(' memory:', Array.from(new Uint8Array(memory.buffer, offset, 9)).map(x => x.toString(16).padStart(2, '0')).join(' '));
|
54
|
+
// console.log(' read:', { value: v, type: t }, '\n');
|
55
|
+
|
56
|
+
out.push(porfToJSValue({ memory, funcs, pages }, v, t));
|
57
|
+
}
|
58
|
+
|
59
|
+
return out;
|
42
60
|
}
|
43
61
|
|
44
62
|
case TYPES.date: {
|
@@ -61,12 +79,24 @@ const porfToJSValue = (memory, funcs, value, type) => {
|
|
61
79
|
// console.log(' memory:', Array.from(new Uint8Array(memory.buffer, offset, 9)).map(x => x.toString(16).padStart(2, '0')).join(' '));
|
62
80
|
// console.log(' read:', { value: v, type: t }, '\n');
|
63
81
|
|
64
|
-
out.add(porfToJSValue(memory, funcs, v, t));
|
82
|
+
out.add(porfToJSValue({ memory, funcs, pages }, v, t));
|
65
83
|
}
|
66
84
|
|
67
85
|
return out;
|
68
86
|
}
|
69
87
|
|
88
|
+
case TYPES.symbol: {
|
89
|
+
const descStore = pages.get('bytestring: __Porffor_symbol_descStore/ptr').ind * pageSize;
|
90
|
+
const offset = descStore + 4 + ((value - 1) * 9);
|
91
|
+
|
92
|
+
const v = (new Float64Array(memory.buffer.slice(offset, offset + 8), 0, 1))[0];
|
93
|
+
const t = (new Uint8Array(memory.buffer, offset + 8, 1))[0];
|
94
|
+
|
95
|
+
const desc = porfToJSValue({ memory, funcs, pages }, v, t);
|
96
|
+
|
97
|
+
return Symbol(desc);
|
98
|
+
}
|
99
|
+
|
70
100
|
default: return value;
|
71
101
|
}
|
72
102
|
};
|
@@ -86,40 +116,12 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
86
116
|
times.push(performance.now() - t1);
|
87
117
|
if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
|
88
118
|
|
89
|
-
const
|
90
|
-
|
91
|
-
let instance;
|
92
|
-
try {
|
93
|
-
let wasmEngine = WebAssembly;
|
94
|
-
if (Prefs.asur) {
|
95
|
-
log.warning('wrap', 'using our !experimental! asur wasm engine instead of host to run');
|
96
|
-
wasmEngine = await import('../asur/index.js');
|
97
|
-
}
|
98
|
-
|
99
|
-
0, { instance } = await wasmEngine.instantiate(wasm, {
|
100
|
-
'': {
|
101
|
-
p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
|
102
|
-
c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
|
103
|
-
t: () => performance.now(),
|
104
|
-
u: () => performance.timeOrigin,
|
105
|
-
y: () => {},
|
106
|
-
z: () => {},
|
107
|
-
...customImports
|
108
|
-
}
|
109
|
-
});
|
110
|
-
} catch (e) {
|
111
|
-
// only backtrace for runner, not test262/etc
|
112
|
-
if (!process.argv[1].includes('/runner')) throw e;
|
113
|
-
|
114
|
-
const funcInd = parseInt(e.message.match(/function #([0-9]+) /)?.[1]);
|
115
|
-
const blobOffset = parseInt(e.message.split('@')?.[1]);
|
116
|
-
|
117
|
-
if (!funcInd) throw e;
|
119
|
+
const backtrace = (funcInd, blobOffset) => {
|
120
|
+
if (funcInd == null || blobOffset == null) return false;
|
118
121
|
|
119
122
|
// convert blob offset -> function wasm offset.
|
120
123
|
// this is not good code and is somewhat duplicated
|
121
124
|
// I just want it to work for debugging, I don't care about perf/yes
|
122
|
-
|
123
125
|
const func = funcs.find(x => x.index === funcInd);
|
124
126
|
const locals = Object.values(func.locals).sort((a, b) => a.idx - b.idx).slice(func.params.length).sort((a, b) => a.idx - b.idx);
|
125
127
|
|
@@ -137,7 +139,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
137
139
|
|
138
140
|
if (typeCount !== 0) localDecl.push(encodeLocal(typeCount, lastType));
|
139
141
|
|
140
|
-
const toFind = encodeVector(localDecl).concat(func.wasm.flat().filter(x => x != null && x <= 0xff).slice(0,
|
142
|
+
const toFind = encodeVector(localDecl).concat(func.wasm.flat().filter(x => x != null && x <= 0xff).slice(0, 60));
|
141
143
|
|
142
144
|
let i = 0;
|
143
145
|
for (; i < wasm.length; i++) {
|
@@ -152,7 +154,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
152
154
|
if (!mismatch) break;
|
153
155
|
}
|
154
156
|
|
155
|
-
if (i === wasm.length)
|
157
|
+
if (i === wasm.length) return false;
|
156
158
|
|
157
159
|
const offset = (blobOffset - i) + encodeVector(localDecl).length;
|
158
160
|
|
@@ -163,7 +165,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
163
165
|
if (cumLen === offset) break;
|
164
166
|
}
|
165
167
|
|
166
|
-
if (cumLen !== offset)
|
168
|
+
if (cumLen !== offset) return false;
|
167
169
|
|
168
170
|
i -= 1;
|
169
171
|
|
@@ -188,6 +190,38 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
188
190
|
console.log(decomp.join('\n'));
|
189
191
|
console.log('\x1B[90m...\x1B[0m\n');
|
190
192
|
|
193
|
+
return true;
|
194
|
+
};
|
195
|
+
|
196
|
+
const t2 = performance.now();
|
197
|
+
|
198
|
+
let instance;
|
199
|
+
try {
|
200
|
+
let wasmEngine = WebAssembly;
|
201
|
+
if (Prefs.asur) {
|
202
|
+
log.warning('wrap', 'using our !experimental! asur wasm engine instead of host to run');
|
203
|
+
wasmEngine = await import('../asur/index.js');
|
204
|
+
}
|
205
|
+
|
206
|
+
0, { instance } = await wasmEngine.instantiate(wasm, {
|
207
|
+
'': {
|
208
|
+
p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
|
209
|
+
c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
|
210
|
+
t: () => performance.now(),
|
211
|
+
u: () => performance.timeOrigin,
|
212
|
+
y: () => {},
|
213
|
+
z: () => {},
|
214
|
+
...customImports
|
215
|
+
}
|
216
|
+
});
|
217
|
+
} catch (e) {
|
218
|
+
// only backtrace for runner, not test262/etc
|
219
|
+
if (!process.argv[1].includes('/runner')) throw e;
|
220
|
+
|
221
|
+
const funcInd = parseInt(e.message.match(/function #([0-9]+) /)?.[1]);
|
222
|
+
const blobOffset = parseInt(e.message.split('@')?.[1]);
|
223
|
+
|
224
|
+
backtrace(funcInd, blobOffset);
|
191
225
|
throw e;
|
192
226
|
}
|
193
227
|
|
@@ -195,6 +229,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
195
229
|
if (Prefs.profileCompiler) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
|
196
230
|
|
197
231
|
const exports = {};
|
232
|
+
const rawValues = process.argv.includes('-i');
|
198
233
|
|
199
234
|
const exceptTag = instance.exports['0'], memory = instance.exports['$'];
|
200
235
|
for (const x in instance.exports) {
|
@@ -213,10 +248,11 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
213
248
|
exports[func.name] = function() {
|
214
249
|
try {
|
215
250
|
const ret = exp.apply(this, arguments);
|
216
|
-
|
217
251
|
if (ret == null) return undefined;
|
218
252
|
|
219
|
-
return porfToJSValue(memory, funcs, ret[0], ret[1])
|
253
|
+
if (rawValues) return { value: ret[0], type: ret[1], js: porfToJSValue({ memory, funcs, pages }, ret[0], ret[1]) };
|
254
|
+
|
255
|
+
return porfToJSValue({ memory, funcs, pages }, ret[0], ret[1]);
|
220
256
|
} catch (e) {
|
221
257
|
if (e.is && e.is(exceptTag)) {
|
222
258
|
const exceptId = e.getArg(exceptTag, 0);
|
@@ -231,6 +267,17 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
231
267
|
throw new constructor(exception.message);
|
232
268
|
}
|
233
269
|
|
270
|
+
if (e instanceof WebAssembly.RuntimeError) {
|
271
|
+
// only backtrace for runner, not test262/etc
|
272
|
+
if (!process.argv[1].includes('/runner')) throw e;
|
273
|
+
|
274
|
+
const match = e.stack.match(/wasm-function\[([0-9]+)\]:([0-9a-z]+)/) ?? [];
|
275
|
+
const funcInd = parseInt(match[1]);
|
276
|
+
const blobOffset = parseInt(match[2]);
|
277
|
+
|
278
|
+
backtrace(funcInd, blobOffset);
|
279
|
+
}
|
280
|
+
|
234
281
|
throw e;
|
235
282
|
}
|
236
283
|
};
|
package/package.json
CHANGED
package/runner/index.js
CHANGED
@@ -25,7 +25,7 @@ if (process.argv.includes('--help')) {
|
|
25
25
|
console.log(`Usage: \x1B[1mporf [command] path/to/script.js [...prefs] [...args]\x1B[0m`);
|
26
26
|
|
27
27
|
// commands
|
28
|
-
console.log(`\n\
|
28
|
+
console.log(`\n\x1B[1mCommands:\x1B[0m`);
|
29
29
|
for (const [ cmd, [ color, desc ] ] of Object.entries({
|
30
30
|
run: [ 34, 'Run a JS file' ],
|
31
31
|
wasm: [ 34, 'Compile a JS file to a Wasm binary\n' ],
|
@@ -60,8 +60,10 @@ if (process.argv.includes('--help')) {
|
|
60
60
|
|
61
61
|
let file = process.argv.slice(2).find(x => x[0] !== '-');
|
62
62
|
if (['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(file)) {
|
63
|
+
// remove this arg
|
64
|
+
process.argv.splice(process.argv.indexOf(file), 1);
|
65
|
+
|
63
66
|
if (file === 'profile') {
|
64
|
-
process.argv.splice(process.argv.indexOf(file), 1);
|
65
67
|
await import('./profiler.js');
|
66
68
|
await new Promise(() => {}); // do nothing for the rest of this file
|
67
69
|
}
|
@@ -75,12 +77,11 @@ if (['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(fi
|
|
75
77
|
}
|
76
78
|
|
77
79
|
if (file === 'debug') {
|
78
|
-
process.argv.splice(process.argv.indexOf(file), 1);
|
79
80
|
await import('./debug.js');
|
80
81
|
await new Promise(() => {}); // do nothing for the rest of this file
|
81
82
|
}
|
82
83
|
|
83
|
-
file = process.argv.slice(
|
84
|
+
file = process.argv.slice(2).find(x => x[0] !== '-');
|
84
85
|
|
85
86
|
const nonOptOutFile = process.argv.slice(process.argv.indexOf(file) + 1).find(x => x[0] !== '-');
|
86
87
|
if (nonOptOutFile) {
|
package/runner/repl.js
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
+
import { TYPE_NAMES } from '../compiler/types.js';
|
1
2
|
import compile from '../compiler/wrap.js';
|
2
3
|
import version from './version.js';
|
3
4
|
|
5
|
+
import util from 'node:util';
|
6
|
+
|
7
|
+
process.argv.push('--no-opt-unused');
|
8
|
+
|
4
9
|
let repl;
|
5
10
|
try {
|
6
11
|
// try importing node:repl
|
@@ -86,10 +91,21 @@ const run = async (source, _context, _filename, callback, run = true) => {
|
|
86
91
|
lastPages = [...pages.keys()];
|
87
92
|
}
|
88
93
|
|
89
|
-
|
90
|
-
|
94
|
+
let ret = run ? exports.main() : undefined;
|
95
|
+
let value, type;
|
96
|
+
if (ret?.type != null) {
|
97
|
+
value = ret.value;
|
98
|
+
type = ret.type;
|
99
|
+
ret = ret.js;
|
100
|
+
}
|
101
|
+
|
102
|
+
console.log(util.inspect(ret, false, 2, true), (value != null ? `\x1B[34m\x1B[3m(value: ${value}, type: ${TYPE_NAMES[type]})\x1B[0m` : ''));
|
103
|
+
|
104
|
+
// callback(null, ret);
|
91
105
|
|
92
106
|
prev = prev + ';\n' + source.trim();
|
107
|
+
|
108
|
+
callback();
|
93
109
|
};
|
94
110
|
|
95
111
|
const replServer = repl.start({ prompt: '> ', eval: run });
|