porffor 0.2.0-c7b7423 → 0.2.0-c908b46
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 +157 -77
- package/asur/README.md +2 -0
- package/asur/index.js +1262 -0
- package/byg/index.js +237 -0
- package/compiler/2c.js +322 -72
- package/compiler/{sections.js → assemble.js} +64 -16
- 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 +76 -0
- package/compiler/builtins/boolean.ts +21 -0
- package/compiler/builtins/crypto.ts +120 -0
- package/compiler/builtins/date.ts +2070 -0
- package/compiler/builtins/escape.ts +141 -0
- package/compiler/builtins/int.ts +147 -0
- package/compiler/builtins/number.ts +534 -0
- package/compiler/builtins/porffor.d.ts +59 -0
- package/compiler/builtins/string.ts +1070 -0
- package/compiler/builtins/tostring.ts +37 -0
- package/compiler/builtins.js +580 -272
- package/compiler/{codeGen.js → codegen.js} +1390 -553
- package/compiler/decompile.js +3 -4
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +98 -114
- package/compiler/generated_builtins.js +1517 -0
- package/compiler/index.js +36 -34
- package/compiler/log.js +6 -3
- package/compiler/opt.js +65 -29
- package/compiler/parse.js +38 -29
- package/compiler/precompile.js +128 -0
- package/compiler/prefs.js +27 -0
- package/compiler/prototype.js +182 -42
- package/compiler/types.js +37 -0
- package/compiler/wasmSpec.js +31 -7
- package/compiler/wrap.js +141 -43
- package/package.json +9 -5
- package/porf +4 -0
- package/rhemyn/compile.js +46 -27
- 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 +91 -11
- package/runner/profiler.js +102 -0
- package/runner/repl.js +44 -11
- package/runner/sizes.js +37 -37
- package/compiler/builtins/base64.js +0 -92
- package/runner/info.js +0 -89
- package/runner/profile.js +0 -46
- package/runner/results.json +0 -1
- package/runner/transform.js +0 -15
- package/tmp.c +0 -69
- package/util/enum.js +0 -20
@@ -1,5 +1,5 @@
|
|
1
1
|
import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from "./wasmSpec.js";
|
2
|
-
import { ieee754_binary64, signedLEB128, unsignedLEB128 } from "./encoding.js";
|
2
|
+
import { ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector } from "./encoding.js";
|
3
3
|
import { operatorOpcode } from "./expression.js";
|
4
4
|
import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from "./builtins.js";
|
5
5
|
import { PrototypeFuncs } from "./prototype.js";
|
@@ -7,6 +7,8 @@ import { number, i32x4, enforceOneByte, enforceTwoBytes, enforceFourBytes, enfor
|
|
7
7
|
import { log } from "./log.js";
|
8
8
|
import parse from "./parse.js";
|
9
9
|
import * as Rhemyn from "../rhemyn/compile.js";
|
10
|
+
import Prefs from './prefs.js';
|
11
|
+
import { TYPES, TYPE_NAMES } from './types.js';
|
10
12
|
|
11
13
|
let globals = {};
|
12
14
|
let globalInd = 0;
|
@@ -23,39 +25,41 @@ const debug = str => {
|
|
23
25
|
const logChar = n => {
|
24
26
|
code.push(...number(n));
|
25
27
|
|
26
|
-
code.push(Opcodes.call);
|
27
|
-
code.push(...unsignedLEB128(0));
|
28
|
+
code.push([ Opcodes.call, 0 ]);
|
28
29
|
};
|
29
30
|
|
30
31
|
for (let i = 0; i < str.length; i++) {
|
31
32
|
logChar(str.charCodeAt(i));
|
32
33
|
}
|
33
34
|
|
34
|
-
logChar(
|
35
|
+
logChar(10); // new line
|
35
36
|
|
36
37
|
return code;
|
37
38
|
};
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
this.name = 'TodoError';
|
44
|
-
}
|
40
|
+
class TodoError extends Error {
|
41
|
+
constructor(message) {
|
42
|
+
super(message);
|
43
|
+
this.name = 'TodoError';
|
45
44
|
}
|
45
|
+
}
|
46
|
+
const todo = (scope, msg, expectsValue = undefined) => {
|
47
|
+
switch (Prefs.todoTime ?? 'runtime') {
|
48
|
+
case 'compile':
|
49
|
+
throw new TodoError(msg);
|
46
50
|
|
47
|
-
|
48
|
-
|
49
|
-
const code = [];
|
50
|
-
|
51
|
-
code.push(...debug(`todo! ` + msg));
|
52
|
-
code.push(Opcodes.unreachable);
|
51
|
+
case 'runtime':
|
52
|
+
return internalThrow(scope, 'TodoError', msg, expectsValue);
|
53
53
|
|
54
|
-
|
54
|
+
// return [
|
55
|
+
// ...debug(`todo! ${msg}`),
|
56
|
+
// [ Opcodes.unreachable ]
|
57
|
+
// ];
|
58
|
+
}
|
55
59
|
};
|
56
60
|
|
57
61
|
const isFuncType = type => type === 'FunctionDeclaration' || type === 'FunctionExpression' || type === 'ArrowFunctionExpression';
|
58
|
-
const generate = (scope, decl, global = false, name = undefined) => {
|
62
|
+
const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
|
59
63
|
switch (decl.type) {
|
60
64
|
case 'BinaryExpression':
|
61
65
|
return generateBinaryExp(scope, decl, global, name);
|
@@ -68,7 +72,12 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
68
72
|
|
69
73
|
case 'ArrowFunctionExpression':
|
70
74
|
case 'FunctionDeclaration':
|
71
|
-
generateFunc(scope, decl);
|
75
|
+
const func = generateFunc(scope, decl);
|
76
|
+
|
77
|
+
if (decl.type.endsWith('Expression')) {
|
78
|
+
return number(func.index);
|
79
|
+
}
|
80
|
+
|
72
81
|
return [];
|
73
82
|
|
74
83
|
case 'BlockStatement':
|
@@ -81,7 +90,7 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
81
90
|
return generateExp(scope, decl);
|
82
91
|
|
83
92
|
case 'CallExpression':
|
84
|
-
return generateCall(scope, decl, global, name);
|
93
|
+
return generateCall(scope, decl, global, name, valueUnused);
|
85
94
|
|
86
95
|
case 'NewExpression':
|
87
96
|
return generateNew(scope, decl, global, name);
|
@@ -99,7 +108,7 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
99
108
|
return generateUnary(scope, decl);
|
100
109
|
|
101
110
|
case 'UpdateExpression':
|
102
|
-
return generateUpdate(scope, decl);
|
111
|
+
return generateUpdate(scope, decl, global, name, valueUnused);
|
103
112
|
|
104
113
|
case 'IfStatement':
|
105
114
|
return generateIf(scope, decl);
|
@@ -110,6 +119,9 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
110
119
|
case 'WhileStatement':
|
111
120
|
return generateWhile(scope, decl);
|
112
121
|
|
122
|
+
case 'DoWhileStatement':
|
123
|
+
return generateDoWhile(scope, decl);
|
124
|
+
|
113
125
|
case 'ForOfStatement':
|
114
126
|
return generateForOf(scope, decl);
|
115
127
|
|
@@ -119,6 +131,9 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
119
131
|
case 'ContinueStatement':
|
120
132
|
return generateContinue(scope, decl);
|
121
133
|
|
134
|
+
case 'LabeledStatement':
|
135
|
+
return generateLabel(scope, decl);
|
136
|
+
|
122
137
|
case 'EmptyStatement':
|
123
138
|
return generateEmpty(scope, decl);
|
124
139
|
|
@@ -132,7 +147,7 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
132
147
|
return generateTry(scope, decl);
|
133
148
|
|
134
149
|
case 'DebuggerStatement':
|
135
|
-
// todo:
|
150
|
+
// todo: hook into terminal debugger
|
136
151
|
return [];
|
137
152
|
|
138
153
|
case 'ArrayExpression':
|
@@ -146,16 +161,22 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
146
161
|
const funcsBefore = funcs.length;
|
147
162
|
generate(scope, decl.declaration);
|
148
163
|
|
149
|
-
if (funcsBefore
|
164
|
+
if (funcsBefore !== funcs.length) {
|
165
|
+
// new func added
|
166
|
+
const newFunc = funcs[funcs.length - 1];
|
167
|
+
newFunc.export = true;
|
168
|
+
}
|
169
|
+
|
170
|
+
// if (funcsBefore === funcs.length) throw new Error('no new func added in export');
|
150
171
|
|
151
|
-
const newFunc = funcs[funcs.length - 1];
|
152
|
-
newFunc.export = true;
|
172
|
+
// const newFunc = funcs[funcs.length - 1];
|
173
|
+
// newFunc.export = true;
|
153
174
|
|
154
175
|
return [];
|
155
176
|
|
156
177
|
case 'TaggedTemplateExpression': {
|
157
178
|
const funcs = {
|
158
|
-
|
179
|
+
__Porffor_wasm: str => {
|
159
180
|
let out = [];
|
160
181
|
|
161
182
|
for (const line of str.split('\n')) {
|
@@ -163,8 +184,8 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
163
184
|
if (asm[0] === '') continue; // blank
|
164
185
|
|
165
186
|
if (asm[0] === 'local') {
|
166
|
-
const [ name,
|
167
|
-
scope.locals[name] = { idx:
|
187
|
+
const [ name, type ] = asm.slice(1);
|
188
|
+
scope.locals[name] = { idx: scope.localInd++, type: Valtype[type] };
|
168
189
|
continue;
|
169
190
|
}
|
170
191
|
|
@@ -174,47 +195,74 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
174
195
|
}
|
175
196
|
|
176
197
|
if (asm[0] === 'memory') {
|
177
|
-
allocPage('asm instrinsic');
|
198
|
+
allocPage(scope, 'asm instrinsic');
|
178
199
|
// todo: add to store/load offset insts
|
179
200
|
continue;
|
180
201
|
}
|
181
202
|
|
182
203
|
let inst = Opcodes[asm[0].replace('.', '_')];
|
183
|
-
if (
|
204
|
+
if (inst == null) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
184
205
|
|
185
206
|
if (!Array.isArray(inst)) inst = [ inst ];
|
186
|
-
const immediates = asm.slice(1).map(x =>
|
207
|
+
const immediates = asm.slice(1).map(x => {
|
208
|
+
const int = parseInt(x);
|
209
|
+
if (Number.isNaN(int)) return scope.locals[x]?.idx;
|
210
|
+
return int;
|
211
|
+
});
|
187
212
|
|
188
|
-
out.push([ ...inst, ...immediates ]);
|
213
|
+
out.push([ ...inst, ...immediates.flatMap(x => signedLEB128(x)) ]);
|
189
214
|
}
|
190
215
|
|
191
216
|
return out;
|
192
217
|
},
|
193
218
|
|
194
|
-
|
195
|
-
|
219
|
+
__Porffor_bs: str => [
|
220
|
+
...makeString(scope, str, global, name, true),
|
196
221
|
|
197
|
-
|
198
|
-
...number(
|
199
|
-
|
222
|
+
...(name ? setType(scope, name, TYPES.bytestring) : [
|
223
|
+
...number(TYPES.bytestring, Valtype.i32),
|
224
|
+
...setLastType(scope)
|
225
|
+
])
|
226
|
+
],
|
227
|
+
__Porffor_s: str => [
|
228
|
+
...makeString(scope, str, global, name, false),
|
200
229
|
|
201
|
-
|
202
|
-
...number(
|
203
|
-
|
204
|
-
]
|
205
|
-
|
206
|
-
}
|
230
|
+
...(name ? setType(scope, name, TYPES.string) : [
|
231
|
+
...number(TYPES.string, Valtype.i32),
|
232
|
+
...setLastType(scope)
|
233
|
+
])
|
234
|
+
],
|
235
|
+
};
|
207
236
|
|
208
|
-
const
|
237
|
+
const func = decl.tag.name;
|
209
238
|
// hack for inline asm
|
210
|
-
if (!funcs[
|
239
|
+
if (!funcs[func]) return todo(scope, 'tagged template expressions not implemented', true);
|
240
|
+
|
241
|
+
const { quasis, expressions } = decl.quasi;
|
242
|
+
let str = quasis[0].value.raw;
|
211
243
|
|
212
|
-
|
213
|
-
|
244
|
+
for (let i = 0; i < expressions.length; i++) {
|
245
|
+
const e = expressions[i];
|
246
|
+
if (!e.name) {
|
247
|
+
if (e.type === 'BinaryExpression' && e.operator === '+' && e.left.type === 'Identifier' && e.right.type === 'Literal') {
|
248
|
+
str += lookupName(scope, e.left.name)[0].idx + e.right.value;
|
249
|
+
} else todo(scope, 'unsupported expression in intrinsic');
|
250
|
+
} else str += lookupName(scope, e.name)[0].idx;
|
251
|
+
|
252
|
+
str += quasis[i + 1].value.raw;
|
253
|
+
}
|
254
|
+
|
255
|
+
return funcs[func](str);
|
214
256
|
}
|
215
257
|
|
216
258
|
default:
|
217
|
-
|
259
|
+
// ignore typescript nodes
|
260
|
+
if (decl.type.startsWith('TS') ||
|
261
|
+
decl.type === 'ImportDeclaration' && decl.importKind === 'type') {
|
262
|
+
return [];
|
263
|
+
}
|
264
|
+
|
265
|
+
return todo(scope, `no generation for ${decl.type}!`);
|
218
266
|
}
|
219
267
|
};
|
220
268
|
|
@@ -242,7 +290,7 @@ const lookupName = (scope, _name) => {
|
|
242
290
|
return [ undefined, undefined ];
|
243
291
|
};
|
244
292
|
|
245
|
-
const internalThrow = (scope, constructor, message, expectsValue =
|
293
|
+
const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysValueInternalThrows) => [
|
246
294
|
...generateThrow(scope, {
|
247
295
|
argument: {
|
248
296
|
type: 'NewExpression',
|
@@ -264,25 +312,33 @@ const generateIdent = (scope, decl) => {
|
|
264
312
|
const name = mapName(rawName);
|
265
313
|
let local = scope.locals[rawName];
|
266
314
|
|
267
|
-
if (builtinVars
|
315
|
+
if (Object.hasOwn(builtinVars, name)) {
|
268
316
|
if (builtinVars[name].floatOnly && valtype[0] === 'i') throw new Error(`Cannot use ${unhackName(name)} with integer valtype`);
|
269
|
-
|
317
|
+
|
318
|
+
let wasm = builtinVars[name];
|
319
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name });
|
320
|
+
return wasm.slice();
|
321
|
+
}
|
322
|
+
|
323
|
+
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
324
|
+
// todo: return an actual something
|
325
|
+
return number(1);
|
270
326
|
}
|
271
327
|
|
272
|
-
if (
|
328
|
+
if (isExistingProtoFunc(name)) {
|
273
329
|
// todo: return an actual something
|
274
330
|
return number(1);
|
275
331
|
}
|
276
332
|
|
277
|
-
if (local === undefined) {
|
333
|
+
if (local?.idx === undefined) {
|
278
334
|
// no local var with name
|
279
|
-
if (
|
280
|
-
if (funcIndex
|
335
|
+
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
336
|
+
if (Object.hasOwn(funcIndex, name)) return number(funcIndex[name]);
|
281
337
|
|
282
|
-
if (globals
|
338
|
+
if (Object.hasOwn(globals, name)) return [ [ Opcodes.global_get, globals[name].idx ] ];
|
283
339
|
}
|
284
340
|
|
285
|
-
if (local === undefined && rawName.startsWith('__')) {
|
341
|
+
if (local?.idx === undefined && rawName.startsWith('__')) {
|
286
342
|
// return undefined if unknown key in already known var
|
287
343
|
let parent = rawName.slice(2).split('_').slice(0, -1).join('_');
|
288
344
|
if (parent.includes('_')) parent = '__' + parent;
|
@@ -291,7 +347,7 @@ const generateIdent = (scope, decl) => {
|
|
291
347
|
if (!parentLookup[1]) return number(UNDEFINED);
|
292
348
|
}
|
293
349
|
|
294
|
-
if (local === undefined) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
350
|
+
if (local?.idx === undefined) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
295
351
|
|
296
352
|
return [ [ Opcodes.local_get, local.idx ] ];
|
297
353
|
};
|
@@ -304,14 +360,18 @@ const generateReturn = (scope, decl) => {
|
|
304
360
|
// just bare "return"
|
305
361
|
return [
|
306
362
|
...number(UNDEFINED), // "undefined" if func returns
|
307
|
-
...
|
363
|
+
...(scope.returnType != null ? [] : [
|
364
|
+
...number(TYPES.undefined, Valtype.i32) // type undefined
|
365
|
+
]),
|
308
366
|
[ Opcodes.return ]
|
309
367
|
];
|
310
368
|
}
|
311
369
|
|
312
370
|
return [
|
313
371
|
...generate(scope, decl.argument),
|
314
|
-
...
|
372
|
+
...(scope.returnType != null ? [] : [
|
373
|
+
...getNodeType(scope, decl.argument)
|
374
|
+
]),
|
315
375
|
[ Opcodes.return ]
|
316
376
|
];
|
317
377
|
};
|
@@ -325,7 +385,8 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
325
385
|
return idx;
|
326
386
|
};
|
327
387
|
|
328
|
-
const isIntOp = op => op && (op[0] >=
|
388
|
+
const isIntOp = op => op && ((op[0] >= 0x45 && op[0] <= 0x4f) || (op[0] >= 0x67 && op[0] <= 0x78) || op[0] === 0x41);
|
389
|
+
const isFloatToIntOp = op => op && (op[0] >= 0xb7 && op[0] <= 0xba);
|
329
390
|
|
330
391
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
331
392
|
const checks = {
|
@@ -334,7 +395,7 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
334
395
|
'??': nullish
|
335
396
|
};
|
336
397
|
|
337
|
-
if (!checks[op]) return todo(`logic operator ${op} not implemented yet
|
398
|
+
if (!checks[op]) return todo(scope, `logic operator ${op} not implemented yet`, true);
|
338
399
|
|
339
400
|
// generic structure for {a} OP {b}
|
340
401
|
// -->
|
@@ -342,8 +403,8 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
342
403
|
|
343
404
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
344
405
|
// (like if we are in an if condition - very common)
|
345
|
-
const leftIsInt =
|
346
|
-
const rightIsInt =
|
406
|
+
const leftIsInt = isFloatToIntOp(left[left.length - 1]);
|
407
|
+
const rightIsInt = isFloatToIntOp(right[right.length - 1]);
|
347
408
|
|
348
409
|
const canInt = leftIsInt && rightIsInt;
|
349
410
|
|
@@ -360,12 +421,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
360
421
|
...right,
|
361
422
|
// note type
|
362
423
|
...rightType,
|
363
|
-
|
424
|
+
...setLastType(scope),
|
364
425
|
[ Opcodes.else ],
|
365
426
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
366
427
|
// note type
|
367
428
|
...leftType,
|
368
|
-
|
429
|
+
...setLastType(scope),
|
369
430
|
[ Opcodes.end ],
|
370
431
|
Opcodes.i32_from
|
371
432
|
];
|
@@ -379,17 +440,17 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
379
440
|
...right,
|
380
441
|
// note type
|
381
442
|
...rightType,
|
382
|
-
|
443
|
+
...setLastType(scope),
|
383
444
|
[ Opcodes.else ],
|
384
445
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
385
446
|
// note type
|
386
447
|
...leftType,
|
387
|
-
|
448
|
+
...setLastType(scope),
|
388
449
|
[ Opcodes.end ]
|
389
450
|
];
|
390
451
|
};
|
391
452
|
|
392
|
-
const concatStrings = (scope, left, right, global, name, assign) => {
|
453
|
+
const concatStrings = (scope, left, right, global, name, assign = false, bytestrings = false) => {
|
393
454
|
// todo: this should be rewritten into a built-in/func: String.prototype.concat
|
394
455
|
// todo: convert left and right to strings if not
|
395
456
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -399,11 +460,8 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
399
460
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
400
461
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
401
462
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
if (assign) {
|
406
|
-
const pointer = arrays.get(name ?? '$undeclared');
|
463
|
+
if (assign && Prefs.aotPointerOpt) {
|
464
|
+
const pointer = scope.arrays?.get(name ?? '$undeclared');
|
407
465
|
|
408
466
|
return [
|
409
467
|
// setup right
|
@@ -428,11 +486,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
428
486
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
|
429
487
|
|
430
488
|
// copy right
|
431
|
-
// dst = out pointer + length size + current length *
|
489
|
+
// dst = out pointer + length size + current length * sizeof valtype
|
432
490
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
433
491
|
|
434
492
|
[ Opcodes.local_get, leftLength ],
|
435
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
493
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
436
494
|
[ Opcodes.i32_mul ],
|
437
495
|
[ Opcodes.i32_add ],
|
438
496
|
|
@@ -441,9 +499,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
441
499
|
...number(ValtypeSize.i32, Valtype.i32),
|
442
500
|
[ Opcodes.i32_add ],
|
443
501
|
|
444
|
-
// size = right length *
|
502
|
+
// size = right length * sizeof valtype
|
445
503
|
[ Opcodes.local_get, rightLength ],
|
446
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
504
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
447
505
|
[ Opcodes.i32_mul ],
|
448
506
|
|
449
507
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -501,11 +559,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
501
559
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
502
560
|
|
503
561
|
// copy right
|
504
|
-
// dst = out pointer + length size + left length *
|
562
|
+
// dst = out pointer + length size + left length * sizeof valtype
|
505
563
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
506
564
|
|
507
565
|
[ Opcodes.local_get, leftLength ],
|
508
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
566
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
509
567
|
[ Opcodes.i32_mul ],
|
510
568
|
[ Opcodes.i32_add ],
|
511
569
|
|
@@ -514,9 +572,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
514
572
|
...number(ValtypeSize.i32, Valtype.i32),
|
515
573
|
[ Opcodes.i32_add ],
|
516
574
|
|
517
|
-
// size = right length *
|
575
|
+
// size = right length * sizeof valtype
|
518
576
|
[ Opcodes.local_get, rightLength ],
|
519
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
577
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
520
578
|
[ Opcodes.i32_mul ],
|
521
579
|
|
522
580
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -526,7 +584,7 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
526
584
|
];
|
527
585
|
};
|
528
586
|
|
529
|
-
const compareStrings = (scope, left, right) => {
|
587
|
+
const compareStrings = (scope, left, right, bytestrings = false) => {
|
530
588
|
// todo: this should be rewritten into a func
|
531
589
|
// todo: convert left and right to strings if not
|
532
590
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -535,7 +593,6 @@ const compareStrings = (scope, left, right) => {
|
|
535
593
|
const leftPointer = localTmp(scope, 'compare_left_pointer', Valtype.i32);
|
536
594
|
const leftLength = localTmp(scope, 'compare_left_length', Valtype.i32);
|
537
595
|
const rightPointer = localTmp(scope, 'compare_right_pointer', Valtype.i32);
|
538
|
-
const rightLength = localTmp(scope, 'compare_right_length', Valtype.i32);
|
539
596
|
|
540
597
|
const index = localTmp(scope, 'compare_index', Valtype.i32);
|
541
598
|
const indexEnd = localTmp(scope, 'compare_index_end', Valtype.i32);
|
@@ -563,7 +620,6 @@ const compareStrings = (scope, left, right) => {
|
|
563
620
|
|
564
621
|
[ Opcodes.local_get, rightPointer ],
|
565
622
|
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
566
|
-
[ Opcodes.local_tee, rightLength ],
|
567
623
|
|
568
624
|
// fast path: check leftLength != rightLength
|
569
625
|
[ Opcodes.i32_ne ],
|
@@ -578,11 +634,13 @@ const compareStrings = (scope, left, right) => {
|
|
578
634
|
...number(0, Valtype.i32),
|
579
635
|
[ Opcodes.local_set, index ],
|
580
636
|
|
581
|
-
// setup index end as length * sizeof
|
637
|
+
// setup index end as length * sizeof valtype (1 for bytestring, 2 for string)
|
582
638
|
// we do this instead of having to do mul/div each iter for perf™
|
583
639
|
[ Opcodes.local_get, leftLength ],
|
584
|
-
...
|
585
|
-
|
640
|
+
...(bytestrings ? [] : [
|
641
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
642
|
+
[ Opcodes.i32_mul ],
|
643
|
+
]),
|
586
644
|
[ Opcodes.local_set, indexEnd ],
|
587
645
|
|
588
646
|
// iterate over each char and check if eq
|
@@ -592,13 +650,17 @@ const compareStrings = (scope, left, right) => {
|
|
592
650
|
[ Opcodes.local_get, index ],
|
593
651
|
[ Opcodes.local_get, leftPointer ],
|
594
652
|
[ Opcodes.i32_add ],
|
595
|
-
|
653
|
+
bytestrings ?
|
654
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
655
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
596
656
|
|
597
657
|
// fetch right
|
598
658
|
[ Opcodes.local_get, index ],
|
599
659
|
[ Opcodes.local_get, rightPointer ],
|
600
660
|
[ Opcodes.i32_add ],
|
601
|
-
|
661
|
+
bytestrings ?
|
662
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
663
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
602
664
|
|
603
665
|
// not equal, "return" false
|
604
666
|
[ Opcodes.i32_ne ],
|
@@ -607,13 +669,13 @@ const compareStrings = (scope, left, right) => {
|
|
607
669
|
[ Opcodes.br, 2 ],
|
608
670
|
[ Opcodes.end ],
|
609
671
|
|
610
|
-
// index += sizeof
|
672
|
+
// index += sizeof valtype (1 for bytestring, 2 for string)
|
611
673
|
[ Opcodes.local_get, index ],
|
612
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
674
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
613
675
|
[ Opcodes.i32_add ],
|
614
676
|
[ Opcodes.local_tee, index ],
|
615
677
|
|
616
|
-
// if index != index end (length * sizeof
|
678
|
+
// if index != index end (length * sizeof valtype), loop
|
617
679
|
[ Opcodes.local_get, indexEnd ],
|
618
680
|
[ Opcodes.i32_ne ],
|
619
681
|
[ Opcodes.br_if, 0 ],
|
@@ -634,16 +696,18 @@ const compareStrings = (scope, left, right) => {
|
|
634
696
|
};
|
635
697
|
|
636
698
|
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
637
|
-
if (
|
699
|
+
if (isFloatToIntOp(wasm[wasm.length - 1])) return [
|
638
700
|
...wasm,
|
639
701
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
640
702
|
];
|
703
|
+
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
641
704
|
|
642
|
-
const
|
705
|
+
const useTmp = knownType(scope, type) == null;
|
706
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
643
707
|
|
644
708
|
const def = [
|
645
709
|
// if value != 0
|
646
|
-
[ Opcodes.local_get, tmp ],
|
710
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
647
711
|
|
648
712
|
// ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
649
713
|
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
|
@@ -655,16 +719,16 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
655
719
|
|
656
720
|
return [
|
657
721
|
...wasm,
|
658
|
-
[ Opcodes.local_set, tmp ],
|
722
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
659
723
|
|
660
724
|
...typeSwitch(scope, type, {
|
661
725
|
// [TYPES.number]: def,
|
662
|
-
[TYPES.
|
726
|
+
[TYPES.array]: [
|
663
727
|
// arrays are always truthy
|
664
728
|
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
665
729
|
],
|
666
730
|
[TYPES.string]: [
|
667
|
-
[ Opcodes.local_get, tmp ],
|
731
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
668
732
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
669
733
|
|
670
734
|
// get length
|
@@ -675,24 +739,46 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
675
739
|
[ Opcodes.i32_eqz ], */
|
676
740
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
677
741
|
],
|
742
|
+
[TYPES.bytestring]: [ // duplicate of string
|
743
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
744
|
+
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
745
|
+
|
746
|
+
// get length
|
747
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
748
|
+
|
749
|
+
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
750
|
+
],
|
678
751
|
default: def
|
679
752
|
}, intOut ? Valtype.i32 : valtypeBinary)
|
680
753
|
];
|
681
754
|
};
|
682
755
|
|
683
756
|
const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
684
|
-
const
|
757
|
+
const useTmp = knownType(scope, type) == null;
|
758
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
759
|
+
|
685
760
|
return [
|
686
761
|
...wasm,
|
687
|
-
[ Opcodes.local_set, tmp ],
|
762
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
688
763
|
|
689
764
|
...typeSwitch(scope, type, {
|
690
|
-
[TYPES.
|
765
|
+
[TYPES.array]: [
|
691
766
|
// arrays are always truthy
|
692
767
|
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
693
768
|
],
|
694
769
|
[TYPES.string]: [
|
695
|
-
[ Opcodes.local_get, tmp ],
|
770
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
771
|
+
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
772
|
+
|
773
|
+
// get length
|
774
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
775
|
+
|
776
|
+
// if length == 0
|
777
|
+
[ Opcodes.i32_eqz ],
|
778
|
+
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
779
|
+
],
|
780
|
+
[TYPES.bytestring]: [ // duplicate of string
|
781
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
696
782
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
697
783
|
|
698
784
|
// get length
|
@@ -704,7 +790,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
704
790
|
],
|
705
791
|
default: [
|
706
792
|
// if value == 0
|
707
|
-
[ Opcodes.local_get, tmp ],
|
793
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
708
794
|
|
709
795
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
710
796
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -714,10 +800,12 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
714
800
|
};
|
715
801
|
|
716
802
|
const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
717
|
-
const
|
803
|
+
const useTmp = knownType(scope, type) == null;
|
804
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
805
|
+
|
718
806
|
return [
|
719
807
|
...wasm,
|
720
|
-
[ Opcodes.local_set, tmp ],
|
808
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
721
809
|
|
722
810
|
...typeSwitch(scope, type, {
|
723
811
|
[TYPES.undefined]: [
|
@@ -726,7 +814,7 @@ const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
726
814
|
],
|
727
815
|
[TYPES.object]: [
|
728
816
|
// object, null if == 0
|
729
|
-
[ Opcodes.local_get, tmp ],
|
817
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
730
818
|
|
731
819
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
732
820
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -755,11 +843,14 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
755
843
|
return performLogicOp(scope, op, left, right, leftType, rightType);
|
756
844
|
}
|
757
845
|
|
846
|
+
const knownLeft = knownType(scope, leftType);
|
847
|
+
const knownRight = knownType(scope, rightType);
|
848
|
+
|
758
849
|
const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
|
759
850
|
const strictOp = op === '===' || op === '!==';
|
760
851
|
|
761
852
|
const startOut = [], endOut = [];
|
762
|
-
const
|
853
|
+
const finalize = out => startOut.concat(out, endOut);
|
763
854
|
|
764
855
|
// if strict (in)equal check types match
|
765
856
|
if (strictOp) {
|
@@ -804,31 +895,59 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
804
895
|
// todo: if equality op and an operand is undefined, return false
|
805
896
|
// todo: niche null hell with 0
|
806
897
|
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
898
|
+
if (knownLeft === TYPES.string || knownRight === TYPES.string) {
|
899
|
+
if (op === '+') {
|
900
|
+
// todo: this should be dynamic too but for now only static
|
901
|
+
// string concat (a + b)
|
902
|
+
return concatStrings(scope, left, right, _global, _name, assign, false);
|
903
|
+
}
|
904
|
+
|
905
|
+
// not an equality op, NaN
|
906
|
+
if (!eqOp) return number(NaN);
|
907
|
+
|
908
|
+
// else leave bool ops
|
909
|
+
// todo: convert string to number if string and number/bool
|
910
|
+
// todo: string (>|>=|<|<=) string
|
911
|
+
|
912
|
+
// string comparison
|
913
|
+
if (op === '===' || op === '==') {
|
914
|
+
return compareStrings(scope, left, right);
|
915
|
+
}
|
916
|
+
|
917
|
+
if (op === '!==' || op === '!=') {
|
918
|
+
return [
|
919
|
+
...compareStrings(scope, left, right),
|
920
|
+
[ Opcodes.i32_eqz ]
|
921
|
+
];
|
922
|
+
}
|
923
|
+
}
|
924
|
+
|
925
|
+
if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) {
|
926
|
+
if (op === '+') {
|
927
|
+
// todo: this should be dynamic too but for now only static
|
928
|
+
// string concat (a + b)
|
929
|
+
return concatStrings(scope, left, right, _global, _name, assign, true);
|
930
|
+
}
|
931
|
+
|
932
|
+
// not an equality op, NaN
|
933
|
+
if (!eqOp) return number(NaN);
|
934
|
+
|
935
|
+
// else leave bool ops
|
936
|
+
// todo: convert string to number if string and number/bool
|
937
|
+
// todo: string (>|>=|<|<=) string
|
938
|
+
|
939
|
+
// string comparison
|
940
|
+
if (op === '===' || op === '==') {
|
941
|
+
return compareStrings(scope, left, right, true);
|
942
|
+
}
|
943
|
+
|
944
|
+
if (op === '!==' || op === '!=') {
|
945
|
+
return [
|
946
|
+
...compareStrings(scope, left, right, true),
|
947
|
+
[ Opcodes.i32_eqz ]
|
948
|
+
];
|
949
|
+
}
|
950
|
+
}
|
832
951
|
|
833
952
|
let ops = operatorOpcode[valtype][op];
|
834
953
|
|
@@ -838,24 +957,69 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
838
957
|
includeBuiltin(scope, builtinName);
|
839
958
|
const idx = funcIndex[builtinName];
|
840
959
|
|
841
|
-
return
|
960
|
+
return finalize([
|
842
961
|
...left,
|
843
962
|
...right,
|
844
963
|
[ Opcodes.call, idx ]
|
845
964
|
]);
|
846
965
|
}
|
847
966
|
|
848
|
-
if (!ops) return todo(`operator ${op} not implemented yet
|
967
|
+
if (!ops) return todo(scope, `operator ${op} not implemented yet`, true);
|
849
968
|
|
850
969
|
if (!Array.isArray(ops)) ops = [ ops ];
|
851
970
|
ops = [ ops ];
|
852
971
|
|
853
972
|
let tmpLeft, tmpRight;
|
854
973
|
// if equal op, check if strings for compareStrings
|
855
|
-
|
974
|
+
// todo: intelligent partial skip later
|
975
|
+
// if neither known are string, stop this madness
|
976
|
+
// we already do known checks earlier, so don't need to recheck
|
977
|
+
|
978
|
+
if ((op === '===' || op === '==' || op === '!==' || op === '!=') && (knownLeft == null && knownRight == null)) {
|
856
979
|
tmpLeft = localTmp(scope, '__tmpop_left');
|
857
980
|
tmpRight = localTmp(scope, '__tmpop_right');
|
858
981
|
|
982
|
+
// returns false for one string, one not - but more ops/slower
|
983
|
+
// ops.unshift(...stringOnly([
|
984
|
+
// // if left is string
|
985
|
+
// ...leftType,
|
986
|
+
// ...number(TYPES.string, Valtype.i32),
|
987
|
+
// [ Opcodes.i32_eq ],
|
988
|
+
|
989
|
+
// // if right is string
|
990
|
+
// ...rightType,
|
991
|
+
// ...number(TYPES.string, Valtype.i32),
|
992
|
+
// [ Opcodes.i32_eq ],
|
993
|
+
|
994
|
+
// // if either are true
|
995
|
+
// [ Opcodes.i32_or ],
|
996
|
+
// [ Opcodes.if, Blocktype.void ],
|
997
|
+
|
998
|
+
// // todo: convert non-strings to strings, for now fail immediately if one is not
|
999
|
+
// // if left is not string
|
1000
|
+
// ...leftType,
|
1001
|
+
// ...number(TYPES.string, Valtype.i32),
|
1002
|
+
// [ Opcodes.i32_ne ],
|
1003
|
+
|
1004
|
+
// // if right is not string
|
1005
|
+
// ...rightType,
|
1006
|
+
// ...number(TYPES.string, Valtype.i32),
|
1007
|
+
// [ Opcodes.i32_ne ],
|
1008
|
+
|
1009
|
+
// // if either are true
|
1010
|
+
// [ Opcodes.i32_or ],
|
1011
|
+
// [ Opcodes.if, Blocktype.void ],
|
1012
|
+
// ...number(0, Valtype.i32),
|
1013
|
+
// [ Opcodes.br, 2 ],
|
1014
|
+
// [ Opcodes.end ],
|
1015
|
+
|
1016
|
+
// ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1017
|
+
// ...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
1018
|
+
// [ Opcodes.br, 1 ],
|
1019
|
+
// [ Opcodes.end ],
|
1020
|
+
// ]));
|
1021
|
+
|
1022
|
+
// does not handle one string, one not (such cases go past)
|
859
1023
|
ops.unshift(...stringOnly([
|
860
1024
|
// if left is string
|
861
1025
|
...leftType,
|
@@ -867,30 +1031,28 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
867
1031
|
...number(TYPES.string, Valtype.i32),
|
868
1032
|
[ Opcodes.i32_eq ],
|
869
1033
|
|
870
|
-
// if
|
871
|
-
[ Opcodes.
|
1034
|
+
// if both are true
|
1035
|
+
[ Opcodes.i32_and ],
|
872
1036
|
[ Opcodes.if, Blocktype.void ],
|
1037
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1038
|
+
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
1039
|
+
[ Opcodes.br, 1 ],
|
1040
|
+
[ Opcodes.end ],
|
873
1041
|
|
874
|
-
//
|
875
|
-
// if left is not string
|
1042
|
+
// if left is bytestring
|
876
1043
|
...leftType,
|
877
|
-
...number(TYPES.
|
878
|
-
[ Opcodes.
|
1044
|
+
...number(TYPES.bytestring, Valtype.i32),
|
1045
|
+
[ Opcodes.i32_eq ],
|
879
1046
|
|
880
|
-
// if right is
|
1047
|
+
// if right is bytestring
|
881
1048
|
...rightType,
|
882
|
-
...number(TYPES.
|
883
|
-
[ Opcodes.
|
1049
|
+
...number(TYPES.bytestring, Valtype.i32),
|
1050
|
+
[ Opcodes.i32_eq ],
|
884
1051
|
|
885
|
-
// if
|
886
|
-
[ Opcodes.
|
1052
|
+
// if both are true
|
1053
|
+
[ Opcodes.i32_and ],
|
887
1054
|
[ Opcodes.if, Blocktype.void ],
|
888
|
-
...
|
889
|
-
[ Opcodes.br, 1 ],
|
890
|
-
[ Opcodes.end ],
|
891
|
-
|
892
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
893
|
-
// ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1055
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], true),
|
894
1056
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
895
1057
|
[ Opcodes.br, 1 ],
|
896
1058
|
[ Opcodes.end ],
|
@@ -904,7 +1066,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
904
1066
|
// }
|
905
1067
|
}
|
906
1068
|
|
907
|
-
return
|
1069
|
+
return finalize([
|
908
1070
|
...left,
|
909
1071
|
...(tmpLeft != null ? stringOnly([ [ Opcodes.local_tee, tmpLeft ] ]) : []),
|
910
1072
|
...right,
|
@@ -921,7 +1083,22 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
921
1083
|
return out;
|
922
1084
|
};
|
923
1085
|
|
924
|
-
const
|
1086
|
+
const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals = [], returns = [], localInd = 0 }) => {
|
1087
|
+
return func({ name, params, locals, returns, localInd }, {
|
1088
|
+
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage,
|
1089
|
+
builtin: name => {
|
1090
|
+
let idx = funcIndex[name] ?? importedFuncs[name];
|
1091
|
+
if (idx === undefined && builtinFuncs[name]) {
|
1092
|
+
includeBuiltin(null, name);
|
1093
|
+
idx = funcIndex[name];
|
1094
|
+
}
|
1095
|
+
|
1096
|
+
return idx;
|
1097
|
+
}
|
1098
|
+
});
|
1099
|
+
};
|
1100
|
+
|
1101
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
925
1102
|
const existing = funcs.find(x => x.name === name);
|
926
1103
|
if (existing) return existing;
|
927
1104
|
|
@@ -933,6 +1110,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
933
1110
|
locals[nameParam(i)] = { idx: i, type: allLocals[i] };
|
934
1111
|
}
|
935
1112
|
|
1113
|
+
for (const x of _data) {
|
1114
|
+
const copy = { ...x };
|
1115
|
+
copy.offset += pages.size * pageSize;
|
1116
|
+
data.push(copy);
|
1117
|
+
}
|
1118
|
+
|
1119
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name, params, locals, returns, localInd: allLocals.length });
|
1120
|
+
|
936
1121
|
let baseGlobalIdx, i = 0;
|
937
1122
|
for (const type of globalTypes) {
|
938
1123
|
if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
|
@@ -955,7 +1140,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
955
1140
|
params,
|
956
1141
|
locals,
|
957
1142
|
returns,
|
958
|
-
returnType:
|
1143
|
+
returnType: returnType ?? TYPES.number,
|
959
1144
|
wasm,
|
960
1145
|
internal: true,
|
961
1146
|
index: currentFuncIndex++
|
@@ -978,6 +1163,7 @@ const generateLogicExp = (scope, decl) => {
|
|
978
1163
|
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
979
1164
|
};
|
980
1165
|
|
1166
|
+
// potential future ideas for nan boxing (unused):
|
981
1167
|
// T = JS type, V = value/pointer
|
982
1168
|
// 0bTTT
|
983
1169
|
// qNAN: 0 11111111111 1000000000000000000000000000000000000000000000000001
|
@@ -991,7 +1177,6 @@ const generateLogicExp = (scope, decl) => {
|
|
991
1177
|
// js type: 4 bits
|
992
1178
|
// internal type: ? bits
|
993
1179
|
// pointer: 32 bits
|
994
|
-
|
995
1180
|
// generic
|
996
1181
|
// 1 23 4 5
|
997
1182
|
// 0 11111111111 11TTTTIIII??????????PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
|
@@ -1001,47 +1186,29 @@ const generateLogicExp = (scope, decl) => {
|
|
1001
1186
|
// 4: internal type
|
1002
1187
|
// 5: pointer
|
1003
1188
|
|
1004
|
-
const
|
1005
|
-
|
1006
|
-
|
1007
|
-
string: 0x02,
|
1008
|
-
undefined: 0x03,
|
1009
|
-
object: 0x04,
|
1010
|
-
function: 0x05,
|
1011
|
-
symbol: 0x06,
|
1012
|
-
bigint: 0x07,
|
1189
|
+
const isExistingProtoFunc = name => {
|
1190
|
+
if (name.startsWith('__Array_prototype')) return !!prototypeFuncs[TYPES.array][name.slice(18)];
|
1191
|
+
if (name.startsWith('__String_prototype_')) return !!prototypeFuncs[TYPES.string][name.slice(19)];
|
1013
1192
|
|
1014
|
-
|
1015
|
-
_array: 0x10,
|
1016
|
-
_regexp: 0x11
|
1017
|
-
};
|
1018
|
-
|
1019
|
-
const TYPE_NAMES = {
|
1020
|
-
[TYPES.number]: 'Number',
|
1021
|
-
[TYPES.boolean]: 'Boolean',
|
1022
|
-
[TYPES.string]: 'String',
|
1023
|
-
[TYPES.undefined]: 'undefined',
|
1024
|
-
[TYPES.object]: 'Object',
|
1025
|
-
[TYPES.function]: 'Function',
|
1026
|
-
[TYPES.symbol]: 'Symbol',
|
1027
|
-
[TYPES.bigint]: 'BigInt',
|
1028
|
-
|
1029
|
-
[TYPES._array]: 'Array',
|
1030
|
-
[TYPES._regexp]: 'RegExp'
|
1193
|
+
return false;
|
1031
1194
|
};
|
1032
1195
|
|
1033
1196
|
const getType = (scope, _name) => {
|
1034
1197
|
const name = mapName(_name);
|
1035
1198
|
|
1199
|
+
// if (scope.locals[name] && !scope.locals[name + '#type']) console.log(name);
|
1200
|
+
|
1201
|
+
if (typedInput && scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
|
1036
1202
|
if (scope.locals[name]) return [ [ Opcodes.local_get, scope.locals[name + '#type'].idx ] ];
|
1203
|
+
|
1204
|
+
if (typedInput && globals[name]?.metadata?.type != null) return number(globals[name].metadata.type, Valtype.i32);
|
1037
1205
|
if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
|
1038
1206
|
|
1039
1207
|
let type = TYPES.undefined;
|
1040
|
-
if (builtinVars[name]) type =
|
1208
|
+
if (builtinVars[name]) type = builtinVars[name].type ?? TYPES.number;
|
1041
1209
|
if (builtinFuncs[name] !== undefined || importedFuncs[name] !== undefined || funcIndex[name] !== undefined || internalConstrs[name] !== undefined) type = TYPES.function;
|
1042
1210
|
|
1043
|
-
if (name
|
1044
|
-
name.startsWith('__String_prototype_') && prototypeFuncs[TYPES.string][name.slice(19)]) type = TYPES.function;
|
1211
|
+
if (isExistingProtoFunc(name)) type = TYPES.function;
|
1045
1212
|
|
1046
1213
|
return number(type, Valtype.i32);
|
1047
1214
|
};
|
@@ -1051,23 +1218,37 @@ const setType = (scope, _name, type) => {
|
|
1051
1218
|
|
1052
1219
|
const out = typeof type === 'number' ? number(type, Valtype.i32) : type;
|
1053
1220
|
|
1221
|
+
if (typedInput && scope.locals[name]?.metadata?.type != null) return [];
|
1054
1222
|
if (scope.locals[name]) return [
|
1055
1223
|
...out,
|
1056
1224
|
[ Opcodes.local_set, scope.locals[name + '#type'].idx ]
|
1057
1225
|
];
|
1058
1226
|
|
1227
|
+
if (typedInput && globals[name]?.metadata?.type != null) return [];
|
1059
1228
|
if (globals[name]) return [
|
1060
1229
|
...out,
|
1061
1230
|
[ Opcodes.global_set, globals[name + '#type'].idx ]
|
1062
1231
|
];
|
1063
1232
|
|
1064
1233
|
// throw new Error('could not find var');
|
1234
|
+
return [];
|
1235
|
+
};
|
1236
|
+
|
1237
|
+
const getLastType = scope => {
|
1238
|
+
scope.gotLastType = true;
|
1239
|
+
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1240
|
+
};
|
1241
|
+
|
1242
|
+
const setLastType = scope => {
|
1243
|
+
return [ [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1065
1244
|
};
|
1066
1245
|
|
1067
1246
|
const getNodeType = (scope, node) => {
|
1068
1247
|
const inner = () => {
|
1069
1248
|
if (node.type === 'Literal') {
|
1070
|
-
if (node.regex) return TYPES.
|
1249
|
+
if (node.regex) return TYPES.regexp;
|
1250
|
+
|
1251
|
+
if (typeof node.value === 'string' && byteStringable(node.value)) return TYPES.bytestring;
|
1071
1252
|
|
1072
1253
|
return TYPES[typeof node.value];
|
1073
1254
|
}
|
@@ -1082,6 +1263,27 @@ const getNodeType = (scope, node) => {
|
|
1082
1263
|
|
1083
1264
|
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
1084
1265
|
const name = node.callee.name;
|
1266
|
+
if (!name) {
|
1267
|
+
// iife
|
1268
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1269
|
+
|
1270
|
+
// presume
|
1271
|
+
// todo: warn here?
|
1272
|
+
return TYPES.number;
|
1273
|
+
}
|
1274
|
+
|
1275
|
+
if (node.type === 'NewExpression' && builtinFuncs[name + '$constructor']) {
|
1276
|
+
if (builtinFuncs[name + '$constructor'].typedReturns) {
|
1277
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1278
|
+
|
1279
|
+
// presume
|
1280
|
+
// todo: warn here?
|
1281
|
+
return TYPES.number;
|
1282
|
+
}
|
1283
|
+
|
1284
|
+
return builtinFuncs[name + '$constructor'].returnType ?? TYPES.number;
|
1285
|
+
}
|
1286
|
+
|
1085
1287
|
const func = funcs.find(x => x.name === name);
|
1086
1288
|
|
1087
1289
|
if (func) {
|
@@ -1089,10 +1291,27 @@ const getNodeType = (scope, node) => {
|
|
1089
1291
|
if (func.returnType) return func.returnType;
|
1090
1292
|
}
|
1091
1293
|
|
1092
|
-
if (builtinFuncs[name]) return
|
1294
|
+
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
|
1093
1295
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1094
1296
|
|
1095
|
-
|
1297
|
+
// check if this is a prototype function
|
1298
|
+
// if so and there is only one impl (eg charCodeAt)
|
1299
|
+
// use that return type as that is the only possibility
|
1300
|
+
// (if non-matching type it would error out)
|
1301
|
+
if (name.startsWith('__')) {
|
1302
|
+
const spl = name.slice(2).split('_');
|
1303
|
+
|
1304
|
+
const func = spl[spl.length - 1];
|
1305
|
+
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1306
|
+
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1307
|
+
}
|
1308
|
+
|
1309
|
+
if (name.startsWith('__Porffor_wasm_')) {
|
1310
|
+
// todo: return undefined for non-returning ops
|
1311
|
+
return TYPES.number;
|
1312
|
+
}
|
1313
|
+
|
1314
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1096
1315
|
|
1097
1316
|
// presume
|
1098
1317
|
// todo: warn here?
|
@@ -1135,11 +1354,20 @@ const getNodeType = (scope, node) => {
|
|
1135
1354
|
}
|
1136
1355
|
|
1137
1356
|
if (node.type === 'ArrayExpression') {
|
1138
|
-
return TYPES.
|
1357
|
+
return TYPES.array;
|
1139
1358
|
}
|
1140
1359
|
|
1141
1360
|
if (node.type === 'BinaryExpression') {
|
1142
1361
|
if (['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(node.operator)) return TYPES.boolean;
|
1362
|
+
if (node.operator !== '+') return TYPES.number;
|
1363
|
+
|
1364
|
+
const knownLeft = knownType(scope, getNodeType(scope, node.left));
|
1365
|
+
const knownRight = knownType(scope, getNodeType(scope, node.right));
|
1366
|
+
|
1367
|
+
// todo: this should be dynamic but for now only static
|
1368
|
+
if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
|
1369
|
+
if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) return TYPES.bytestring;
|
1370
|
+
|
1143
1371
|
return TYPES.number;
|
1144
1372
|
|
1145
1373
|
// todo: string concat types
|
@@ -1164,7 +1392,7 @@ const getNodeType = (scope, node) => {
|
|
1164
1392
|
if (node.operator === '!') return TYPES.boolean;
|
1165
1393
|
if (node.operator === 'void') return TYPES.undefined;
|
1166
1394
|
if (node.operator === 'delete') return TYPES.boolean;
|
1167
|
-
if (node.operator === 'typeof') return TYPES.string;
|
1395
|
+
if (node.operator === 'typeof') return Prefs.bytestring ? TYPES.bytestring : TYPES.string;
|
1168
1396
|
|
1169
1397
|
return TYPES.number;
|
1170
1398
|
}
|
@@ -1173,11 +1401,23 @@ const getNodeType = (scope, node) => {
|
|
1173
1401
|
// hack: if something.length, number type
|
1174
1402
|
if (node.property.name === 'length') return TYPES.number;
|
1175
1403
|
|
1176
|
-
//
|
1404
|
+
// ts hack
|
1405
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES.string) return TYPES.string;
|
1406
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES.bytestring) return TYPES.bytestring;
|
1407
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES.array) return TYPES.number;
|
1408
|
+
|
1409
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1410
|
+
|
1411
|
+
// presume
|
1177
1412
|
return TYPES.number;
|
1178
1413
|
}
|
1179
1414
|
|
1180
|
-
if (
|
1415
|
+
if (node.type === 'TaggedTemplateExpression') {
|
1416
|
+
// hack
|
1417
|
+
if (node.tag.name.startsWith('__Porffor_')) return TYPES.number;
|
1418
|
+
}
|
1419
|
+
|
1420
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1181
1421
|
|
1182
1422
|
// presume
|
1183
1423
|
// todo: warn here?
|
@@ -1193,8 +1433,8 @@ const getNodeType = (scope, node) => {
|
|
1193
1433
|
const generateLiteral = (scope, decl, global, name) => {
|
1194
1434
|
if (decl.value === null) return number(NULL);
|
1195
1435
|
|
1436
|
+
// hack: just return 1 for regex literals
|
1196
1437
|
if (decl.regex) {
|
1197
|
-
scope.regex[name] = decl.regex;
|
1198
1438
|
return number(1);
|
1199
1439
|
}
|
1200
1440
|
|
@@ -1207,19 +1447,10 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1207
1447
|
return number(decl.value ? 1 : 0);
|
1208
1448
|
|
1209
1449
|
case 'string':
|
1210
|
-
|
1211
|
-
const rawElements = new Array(str.length);
|
1212
|
-
let j = 0;
|
1213
|
-
for (let i = 0; i < str.length; i++) {
|
1214
|
-
rawElements[i] = str.charCodeAt(i);
|
1215
|
-
}
|
1216
|
-
|
1217
|
-
return makeArray(scope, {
|
1218
|
-
rawElements
|
1219
|
-
}, global, name, false, 'i16')[0];
|
1450
|
+
return makeString(scope, decl.value, global, name);
|
1220
1451
|
|
1221
1452
|
default:
|
1222
|
-
return todo(`cannot generate literal of type ${typeof decl.value}
|
1453
|
+
return todo(scope, `cannot generate literal of type ${typeof decl.value}`, true);
|
1223
1454
|
}
|
1224
1455
|
};
|
1225
1456
|
|
@@ -1228,6 +1459,8 @@ const countLeftover = wasm => {
|
|
1228
1459
|
|
1229
1460
|
for (let i = 0; i < wasm.length; i++) {
|
1230
1461
|
const inst = wasm[i];
|
1462
|
+
if (inst[0] == null) continue;
|
1463
|
+
|
1231
1464
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
1232
1465
|
if (inst[0] === Opcodes.if) count--;
|
1233
1466
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -1236,18 +1469,25 @@ const countLeftover = wasm => {
|
|
1236
1469
|
if (inst[0] === Opcodes.end) depth--;
|
1237
1470
|
|
1238
1471
|
if (depth === 0)
|
1239
|
-
if ([Opcodes.throw,Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1240
|
-
else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x0a)) {}
|
1241
|
-
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const].includes(inst[0])) count++;
|
1242
|
-
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16].includes(inst[0])) count -= 2;
|
1472
|
+
if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1473
|
+
else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x0a)) {}
|
1474
|
+
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const, Opcodes.memory_size].includes(inst[0])) count++;
|
1475
|
+
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1243
1476
|
else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
|
1244
1477
|
else if (inst[0] === Opcodes.return) count = 0;
|
1245
1478
|
else if (inst[0] === Opcodes.call) {
|
1246
1479
|
let func = funcs.find(x => x.index === inst[1]);
|
1247
|
-
if (
|
1248
|
-
count
|
1249
|
-
} else
|
1250
|
-
|
1480
|
+
if (inst[1] === -1) {
|
1481
|
+
// todo: count for calling self
|
1482
|
+
} else if (!func && inst[1] < importedFuncs.length) {
|
1483
|
+
count -= importedFuncs[inst[1]].params;
|
1484
|
+
count += importedFuncs[inst[1]].returns;
|
1485
|
+
} else {
|
1486
|
+
if (func) {
|
1487
|
+
count -= func.params.length;
|
1488
|
+
} else count--;
|
1489
|
+
if (func) count += func.returns.length;
|
1490
|
+
}
|
1251
1491
|
} else count--;
|
1252
1492
|
|
1253
1493
|
// console.log(count, decompile([ inst ]).slice(0, -1));
|
@@ -1265,7 +1505,7 @@ const disposeLeftover = wasm => {
|
|
1265
1505
|
const generateExp = (scope, decl) => {
|
1266
1506
|
const expression = decl.expression;
|
1267
1507
|
|
1268
|
-
const out = generate(scope, expression);
|
1508
|
+
const out = generate(scope, expression, undefined, undefined, true);
|
1269
1509
|
disposeLeftover(out);
|
1270
1510
|
|
1271
1511
|
return out;
|
@@ -1323,7 +1563,7 @@ const RTArrayUtil = {
|
|
1323
1563
|
]
|
1324
1564
|
};
|
1325
1565
|
|
1326
|
-
const generateCall = (scope, decl, _global, _name) => {
|
1566
|
+
const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
1327
1567
|
/* const callee = decl.callee;
|
1328
1568
|
const args = decl.arguments;
|
1329
1569
|
|
@@ -1339,10 +1579,21 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1339
1579
|
name = func.name;
|
1340
1580
|
}
|
1341
1581
|
|
1342
|
-
if (name === 'eval' && decl.arguments[0]
|
1582
|
+
if (name === 'eval' && decl.arguments[0]?.type === 'Literal') {
|
1343
1583
|
// literal eval hack
|
1344
|
-
const code = decl.arguments[0]
|
1345
|
-
|
1584
|
+
const code = decl.arguments[0]?.value ?? '';
|
1585
|
+
|
1586
|
+
let parsed;
|
1587
|
+
try {
|
1588
|
+
parsed = parse(code, []);
|
1589
|
+
} catch (e) {
|
1590
|
+
if (e.name === 'SyntaxError') {
|
1591
|
+
// throw syntax errors of evals at runtime instead
|
1592
|
+
return internalThrow(scope, 'SyntaxError', e.message, true);
|
1593
|
+
}
|
1594
|
+
|
1595
|
+
throw e;
|
1596
|
+
}
|
1346
1597
|
|
1347
1598
|
const out = generate(scope, {
|
1348
1599
|
type: 'BlockStatement',
|
@@ -1356,13 +1607,13 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1356
1607
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1357
1608
|
out.push(
|
1358
1609
|
...getNodeType(scope, finalStatement),
|
1359
|
-
|
1610
|
+
...setLastType(scope)
|
1360
1611
|
);
|
1361
1612
|
} else if (countLeftover(out) === 0) {
|
1362
1613
|
out.push(...number(UNDEFINED));
|
1363
1614
|
out.push(
|
1364
1615
|
...number(TYPES.undefined, Valtype.i32),
|
1365
|
-
|
1616
|
+
...setLastType(scope)
|
1366
1617
|
);
|
1367
1618
|
}
|
1368
1619
|
|
@@ -1380,39 +1631,47 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1380
1631
|
if (name && name.startsWith('__')) {
|
1381
1632
|
const spl = name.slice(2).split('_');
|
1382
1633
|
|
1383
|
-
|
1384
|
-
protoName = func;
|
1634
|
+
protoName = spl[spl.length - 1];
|
1385
1635
|
|
1386
1636
|
target = { ...decl.callee };
|
1387
1637
|
target.name = spl.slice(0, -1).join('_');
|
1638
|
+
|
1639
|
+
// failed to lookup name, abort
|
1640
|
+
if (!lookupName(scope, target.name)[0]) protoName = null;
|
1388
1641
|
}
|
1389
1642
|
|
1390
1643
|
// literal.func()
|
1391
1644
|
if (!name && decl.callee.type === 'MemberExpression') {
|
1392
1645
|
// megahack for /regex/.func()
|
1393
|
-
|
1394
|
-
|
1395
|
-
const
|
1646
|
+
const funcName = decl.callee.property.name;
|
1647
|
+
if (decl.callee.object.regex && Object.hasOwn(Rhemyn, funcName)) {
|
1648
|
+
const regex = decl.callee.object.regex.pattern;
|
1649
|
+
const rhemynName = `regex_${funcName}_${regex}`;
|
1396
1650
|
|
1397
|
-
funcIndex[
|
1398
|
-
|
1651
|
+
if (!funcIndex[rhemynName]) {
|
1652
|
+
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
1399
1653
|
|
1654
|
+
funcIndex[func.name] = func.index;
|
1655
|
+
funcs.push(func);
|
1656
|
+
}
|
1657
|
+
|
1658
|
+
const idx = funcIndex[rhemynName];
|
1400
1659
|
return [
|
1401
1660
|
// make string arg
|
1402
1661
|
...generate(scope, decl.arguments[0]),
|
1662
|
+
Opcodes.i32_to_u,
|
1663
|
+
...getNodeType(scope, decl.arguments[0]),
|
1403
1664
|
|
1404
1665
|
// call regex func
|
1405
|
-
Opcodes.
|
1406
|
-
[ Opcodes.call, func.index ],
|
1666
|
+
[ Opcodes.call, idx ],
|
1407
1667
|
Opcodes.i32_from_u,
|
1408
1668
|
|
1409
1669
|
...number(TYPES.boolean, Valtype.i32),
|
1410
|
-
|
1670
|
+
...setLastType(scope)
|
1411
1671
|
];
|
1412
1672
|
}
|
1413
1673
|
|
1414
|
-
|
1415
|
-
protoName = func;
|
1674
|
+
protoName = decl.callee.property.name;
|
1416
1675
|
|
1417
1676
|
target = decl.callee.object;
|
1418
1677
|
}
|
@@ -1433,23 +1692,48 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1433
1692
|
// }
|
1434
1693
|
|
1435
1694
|
if (protoName) {
|
1695
|
+
const protoBC = {};
|
1696
|
+
|
1697
|
+
const builtinProtoCands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + protoName));
|
1698
|
+
|
1699
|
+
if (!decl._protoInternalCall && builtinProtoCands.length > 0) {
|
1700
|
+
for (const x of builtinProtoCands) {
|
1701
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
1702
|
+
if (type == null) continue;
|
1703
|
+
|
1704
|
+
protoBC[type] = generateCall(scope, {
|
1705
|
+
callee: {
|
1706
|
+
type: 'Identifier',
|
1707
|
+
name: x
|
1708
|
+
},
|
1709
|
+
arguments: [ target, ...decl.arguments ],
|
1710
|
+
_protoInternalCall: true
|
1711
|
+
});
|
1712
|
+
}
|
1713
|
+
}
|
1714
|
+
|
1436
1715
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1437
|
-
|
1438
|
-
if (f) acc[x] = f;
|
1716
|
+
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1439
1717
|
return acc;
|
1440
1718
|
}, {});
|
1441
1719
|
|
1442
|
-
// no prototype function candidates, ignore
|
1443
1720
|
if (Object.keys(protoCands).length > 0) {
|
1444
1721
|
// use local for cached i32 length as commonly used
|
1445
1722
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
1446
1723
|
const pointerLocal = localTmp(scope, '__proto_pointer_cache', Valtype.i32);
|
1447
|
-
const getPointer = [ [ Opcodes.local_get, pointerLocal ] ];
|
1448
1724
|
|
1449
1725
|
// TODO: long-term, prototypes should be their individual separate funcs
|
1450
1726
|
|
1727
|
+
const rawPointer = [
|
1728
|
+
...generate(scope, target),
|
1729
|
+
Opcodes.i32_to_u
|
1730
|
+
];
|
1731
|
+
|
1732
|
+
const usePointerCache = !Object.values(protoCands).every(x => x.noPointerCache === true);
|
1733
|
+
const getPointer = usePointerCache ? [ [ Opcodes.local_get, pointerLocal ] ] : rawPointer;
|
1734
|
+
|
1735
|
+
let allOptUnused = true;
|
1451
1736
|
let lengthI32CacheUsed = false;
|
1452
|
-
const protoBC = {};
|
1453
1737
|
for (const x in protoCands) {
|
1454
1738
|
const protoFunc = protoCands[x];
|
1455
1739
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
@@ -1457,7 +1741,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1457
1741
|
...RTArrayUtil.getLength(getPointer),
|
1458
1742
|
|
1459
1743
|
...number(TYPES.number, Valtype.i32),
|
1460
|
-
|
1744
|
+
...setLastType(scope)
|
1461
1745
|
];
|
1462
1746
|
continue;
|
1463
1747
|
}
|
@@ -1467,6 +1751,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1467
1751
|
const protoLocal = protoFunc.local ? localTmp(scope, `__${protoName}_tmp`, protoFunc.local) : -1;
|
1468
1752
|
const protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${protoName}_tmp2`, protoFunc.local2) : -1;
|
1469
1753
|
|
1754
|
+
let optUnused = false;
|
1470
1755
|
const protoOut = protoFunc(getPointer, {
|
1471
1756
|
getCachedI32: () => {
|
1472
1757
|
lengthI32CacheUsed = true;
|
@@ -1481,23 +1766,30 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1481
1766
|
return makeArray(scope, {
|
1482
1767
|
rawElements: new Array(length)
|
1483
1768
|
}, _global, _name, true, itemType);
|
1769
|
+
}, () => {
|
1770
|
+
optUnused = true;
|
1771
|
+
return unusedValue;
|
1484
1772
|
});
|
1485
1773
|
|
1774
|
+
if (!optUnused) allOptUnused = false;
|
1775
|
+
|
1486
1776
|
protoBC[x] = [
|
1487
|
-
[ Opcodes.block, valtypeBinary ],
|
1777
|
+
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1488
1778
|
...protoOut,
|
1489
1779
|
|
1490
1780
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1491
|
-
|
1781
|
+
...setLastType(scope),
|
1492
1782
|
[ Opcodes.end ]
|
1493
1783
|
];
|
1494
1784
|
}
|
1495
1785
|
|
1496
|
-
|
1497
|
-
...generate(scope, target),
|
1786
|
+
// todo: if some cands use optUnused and some don't, we will probably crash
|
1498
1787
|
|
1499
|
-
|
1500
|
-
|
1788
|
+
return [
|
1789
|
+
...(usePointerCache ? [
|
1790
|
+
...rawPointer,
|
1791
|
+
[ Opcodes.local_set, pointerLocal ],
|
1792
|
+
] : []),
|
1501
1793
|
|
1502
1794
|
...(!lengthI32CacheUsed ? [] : [
|
1503
1795
|
...RTArrayUtil.getLengthI32(getPointer),
|
@@ -1509,13 +1801,22 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1509
1801
|
|
1510
1802
|
// TODO: error better
|
1511
1803
|
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1512
|
-
}, valtypeBinary),
|
1804
|
+
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1513
1805
|
];
|
1514
1806
|
}
|
1807
|
+
|
1808
|
+
if (Object.keys(protoBC).length > 0) {
|
1809
|
+
return typeSwitch(scope, getNodeType(scope, target), {
|
1810
|
+
...protoBC,
|
1811
|
+
|
1812
|
+
// TODO: error better
|
1813
|
+
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1814
|
+
}, valtypeBinary);
|
1815
|
+
}
|
1515
1816
|
}
|
1516
1817
|
|
1517
1818
|
// TODO: only allows callee as literal
|
1518
|
-
if (!name) return todo(`only literal callees (got ${decl.callee.type})`);
|
1819
|
+
if (!name) return todo(scope, `only literal callees (got ${decl.callee.type})`);
|
1519
1820
|
|
1520
1821
|
let idx = funcIndex[name] ?? importedFuncs[name];
|
1521
1822
|
if (idx === undefined && builtinFuncs[name]) {
|
@@ -1525,20 +1826,20 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1525
1826
|
idx = funcIndex[name];
|
1526
1827
|
|
1527
1828
|
// infer arguments types from builtins params
|
1528
|
-
const func = funcs.find(x => x.name === name);
|
1529
|
-
for (let i = 0; i < decl.arguments.length; i++) {
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
}
|
1829
|
+
// const func = funcs.find(x => x.name === name);
|
1830
|
+
// for (let i = 0; i < decl.arguments.length; i++) {
|
1831
|
+
// const arg = decl.arguments[i];
|
1832
|
+
// if (!arg.name) continue;
|
1833
|
+
|
1834
|
+
// const local = scope.locals[arg.name];
|
1835
|
+
// if (!local) continue;
|
1836
|
+
|
1837
|
+
// local.type = func.params[i];
|
1838
|
+
// if (local.type === Valtype.v128) {
|
1839
|
+
// // specify vec subtype inferred from last vec type in function name
|
1840
|
+
// local.vecType = name.split('_').reverse().find(x => x.includes('x'));
|
1841
|
+
// }
|
1842
|
+
// }
|
1542
1843
|
}
|
1543
1844
|
|
1544
1845
|
if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
@@ -1548,15 +1849,63 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1548
1849
|
idx = -1;
|
1549
1850
|
}
|
1550
1851
|
|
1852
|
+
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1853
|
+
const wasmOps = {
|
1854
|
+
// pointer, align, offset
|
1855
|
+
i32_load: { imms: 2, args: [ true ], returns: 1 },
|
1856
|
+
// pointer, value, align, offset
|
1857
|
+
i32_store: { imms: 2, args: [ true, true ], returns: 0 },
|
1858
|
+
// pointer, align, offset
|
1859
|
+
i32_load8_u: { imms: 2, args: [ true ], returns: 1 },
|
1860
|
+
// pointer, value, align, offset
|
1861
|
+
i32_store8: { imms: 2, args: [ true, true ], returns: 0 },
|
1862
|
+
// pointer, align, offset
|
1863
|
+
i32_load16_u: { imms: 2, args: [ true ], returns: 1 },
|
1864
|
+
// pointer, value, align, offset
|
1865
|
+
i32_store16: { imms: 2, args: [ true, true ], returns: 0 },
|
1866
|
+
|
1867
|
+
// pointer, align, offset
|
1868
|
+
f64_load: { imms: 2, args: [ true ], returns: 0 }, // 0 due to not i32
|
1869
|
+
// pointer, value, align, offset
|
1870
|
+
f64_store: { imms: 2, args: [ true, false ], returns: 0 },
|
1871
|
+
|
1872
|
+
// value
|
1873
|
+
i32_const: { imms: 1, args: [], returns: 1 },
|
1874
|
+
};
|
1875
|
+
|
1876
|
+
const opName = name.slice('__Porffor_wasm_'.length);
|
1877
|
+
|
1878
|
+
if (wasmOps[opName]) {
|
1879
|
+
const op = wasmOps[opName];
|
1880
|
+
|
1881
|
+
const argOut = [];
|
1882
|
+
for (let i = 0; i < op.args.length; i++) argOut.push(
|
1883
|
+
...generate(scope, decl.arguments[i]),
|
1884
|
+
...(op.args[i] ? [ Opcodes.i32_to ] : [])
|
1885
|
+
);
|
1886
|
+
|
1887
|
+
// literals only
|
1888
|
+
const imms = decl.arguments.slice(op.args.length).map(x => x.value);
|
1889
|
+
|
1890
|
+
return [
|
1891
|
+
...argOut,
|
1892
|
+
[ Opcodes[opName], ...imms ],
|
1893
|
+
...(new Array(op.returns).fill(Opcodes.i32_from))
|
1894
|
+
];
|
1895
|
+
}
|
1896
|
+
}
|
1897
|
+
|
1551
1898
|
if (idx === undefined) {
|
1552
|
-
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function
|
1553
|
-
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined
|
1899
|
+
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1900
|
+
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1554
1901
|
}
|
1555
1902
|
|
1556
1903
|
const func = funcs.find(x => x.index === idx);
|
1557
1904
|
|
1558
1905
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1559
|
-
const
|
1906
|
+
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1907
|
+
const typedReturns = (func ? func.returnType == null : userFunc) || builtinFuncs[name]?.typedReturns;
|
1908
|
+
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1560
1909
|
|
1561
1910
|
let args = decl.arguments;
|
1562
1911
|
if (func && args.length < paramCount) {
|
@@ -1572,14 +1921,24 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1572
1921
|
if (func && func.throws) scope.throws = true;
|
1573
1922
|
|
1574
1923
|
let out = [];
|
1575
|
-
for (
|
1924
|
+
for (let i = 0; i < args.length; i++) {
|
1925
|
+
const arg = args[i];
|
1576
1926
|
out = out.concat(generate(scope, arg));
|
1577
|
-
|
1927
|
+
|
1928
|
+
if (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1929
|
+
out.push(Opcodes.i32_to);
|
1930
|
+
}
|
1931
|
+
|
1932
|
+
if (importedFuncs[name] && name.startsWith('profile')) {
|
1933
|
+
out.push(Opcodes.i32_to);
|
1934
|
+
}
|
1935
|
+
|
1936
|
+
if (typedParams) out = out.concat(getNodeType(scope, arg));
|
1578
1937
|
}
|
1579
1938
|
|
1580
1939
|
out.push([ Opcodes.call, idx ]);
|
1581
1940
|
|
1582
|
-
if (!
|
1941
|
+
if (!typedReturns) {
|
1583
1942
|
// let type;
|
1584
1943
|
// if (builtinFuncs[name]) type = TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1585
1944
|
// if (internalConstrs[name]) type = internalConstrs[name].type;
|
@@ -1589,7 +1948,11 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1589
1948
|
// ...number(type, Valtype.i32),
|
1590
1949
|
// [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1591
1950
|
// );
|
1592
|
-
} else out.push(
|
1951
|
+
} else out.push(...setLastType(scope));
|
1952
|
+
|
1953
|
+
if (builtinFuncs[name] && builtinFuncs[name].returns?.[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1954
|
+
out.push(Opcodes.i32_from);
|
1955
|
+
}
|
1593
1956
|
|
1594
1957
|
return out;
|
1595
1958
|
};
|
@@ -1597,8 +1960,21 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1597
1960
|
const generateNew = (scope, decl, _global, _name) => {
|
1598
1961
|
// hack: basically treat this as a normal call for builtins for now
|
1599
1962
|
const name = mapName(decl.callee.name);
|
1963
|
+
|
1600
1964
|
if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1601
|
-
|
1965
|
+
|
1966
|
+
if (builtinFuncs[name + '$constructor']) {
|
1967
|
+
// custom ...$constructor override builtin func
|
1968
|
+
return generateCall(scope, {
|
1969
|
+
...decl,
|
1970
|
+
callee: {
|
1971
|
+
type: 'Identifier',
|
1972
|
+
name: name + '$constructor'
|
1973
|
+
}
|
1974
|
+
}, _global, _name);
|
1975
|
+
}
|
1976
|
+
|
1977
|
+
if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
|
1602
1978
|
|
1603
1979
|
return generateCall(scope, decl, _global, _name);
|
1604
1980
|
};
|
@@ -1614,44 +1990,151 @@ const unhackName = name => {
|
|
1614
1990
|
return name;
|
1615
1991
|
};
|
1616
1992
|
|
1617
|
-
const
|
1618
|
-
|
1993
|
+
const knownType = (scope, type) => {
|
1994
|
+
if (type.length === 1 && type[0][0] === Opcodes.i32_const) {
|
1995
|
+
return type[0][1];
|
1996
|
+
}
|
1619
1997
|
|
1620
|
-
|
1621
|
-
|
1622
|
-
[ Opcodes.local_set, tmp ],
|
1623
|
-
[ Opcodes.block, returns ]
|
1624
|
-
];
|
1998
|
+
if (typedInput && type.length === 1 && type[0][0] === Opcodes.local_get) {
|
1999
|
+
const idx = type[0][1];
|
1625
2000
|
|
1626
|
-
|
2001
|
+
// type idx = var idx + 1
|
2002
|
+
const v = Object.values(scope.locals).find(x => x.idx === idx - 1);
|
2003
|
+
if (v.metadata?.type != null) return v.metadata.type;
|
2004
|
+
}
|
1627
2005
|
|
1628
|
-
|
1629
|
-
|
2006
|
+
return null;
|
2007
|
+
};
|
1630
2008
|
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1634
|
-
|
2009
|
+
const brTable = (input, bc, returns) => {
|
2010
|
+
const out = [];
|
2011
|
+
const keys = Object.keys(bc);
|
2012
|
+
const count = keys.length;
|
1635
2013
|
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
2014
|
+
if (count === 1) {
|
2015
|
+
// return [
|
2016
|
+
// ...input,
|
2017
|
+
// ...bc[keys[0]]
|
2018
|
+
// ];
|
2019
|
+
return bc[keys[0]];
|
1640
2020
|
}
|
1641
2021
|
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
2022
|
+
if (count === 2) {
|
2023
|
+
// just use if else
|
2024
|
+
const other = keys.find(x => x !== 'default');
|
2025
|
+
return [
|
2026
|
+
...input,
|
2027
|
+
...number(other, Valtype.i32),
|
2028
|
+
[ Opcodes.i32_eq ],
|
2029
|
+
[ Opcodes.if, returns ],
|
2030
|
+
...bc[other],
|
2031
|
+
[ Opcodes.else ],
|
2032
|
+
...bc.default,
|
2033
|
+
[ Opcodes.end ]
|
2034
|
+
];
|
2035
|
+
}
|
1645
2036
|
|
1646
|
-
|
2037
|
+
for (let i = 0; i < count; i++) {
|
2038
|
+
if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2039
|
+
else out.push([ Opcodes.block, Blocktype.void ]);
|
2040
|
+
}
|
1647
2041
|
|
1648
|
-
|
1649
|
-
|
2042
|
+
const nums = keys.filter(x => +x);
|
2043
|
+
const offset = Math.min(...nums);
|
2044
|
+
const max = Math.max(...nums);
|
1650
2045
|
|
1651
|
-
const
|
1652
|
-
|
2046
|
+
const table = [];
|
2047
|
+
let br = 1;
|
1653
2048
|
|
1654
|
-
|
2049
|
+
for (let i = offset; i <= max; i++) {
|
2050
|
+
// if branch for this num, go to that block
|
2051
|
+
if (bc[i]) {
|
2052
|
+
table.push(br);
|
2053
|
+
br++;
|
2054
|
+
continue;
|
2055
|
+
}
|
2056
|
+
|
2057
|
+
// else default
|
2058
|
+
table.push(0);
|
2059
|
+
}
|
2060
|
+
|
2061
|
+
out.push(
|
2062
|
+
[ Opcodes.block, Blocktype.void ],
|
2063
|
+
...input,
|
2064
|
+
...(offset > 0 ? [
|
2065
|
+
...number(offset, Valtype.i32),
|
2066
|
+
[ Opcodes.i32_sub ]
|
2067
|
+
] : []),
|
2068
|
+
[ Opcodes.br_table, ...encodeVector(table), 0 ]
|
2069
|
+
);
|
2070
|
+
|
2071
|
+
// if you can guess why we sort the wrong way and then reverse
|
2072
|
+
// (instead of just sorting the correct way)
|
2073
|
+
// dm me and if you are correct and the first person
|
2074
|
+
// I will somehow shout you out or something
|
2075
|
+
const orderedBc = keys.sort((a, b) => b - a).reverse();
|
2076
|
+
|
2077
|
+
br = count - 1;
|
2078
|
+
for (const x of orderedBc) {
|
2079
|
+
out.push(
|
2080
|
+
[ Opcodes.end ],
|
2081
|
+
...bc[x],
|
2082
|
+
...(br === 0 ? [] : [ [ Opcodes.br, br ] ])
|
2083
|
+
);
|
2084
|
+
br--;
|
2085
|
+
}
|
2086
|
+
|
2087
|
+
return [
|
2088
|
+
...out,
|
2089
|
+
[ Opcodes.end, 'br table end' ]
|
2090
|
+
];
|
2091
|
+
};
|
2092
|
+
|
2093
|
+
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
2094
|
+
if (!Prefs.bytestring) delete bc[TYPES.bytestring];
|
2095
|
+
|
2096
|
+
const known = knownType(scope, type);
|
2097
|
+
if (known != null) {
|
2098
|
+
return bc[known] ?? bc.default;
|
2099
|
+
}
|
2100
|
+
|
2101
|
+
if (Prefs.typeswitchUseBrtable)
|
2102
|
+
return brTable(type, bc, returns);
|
2103
|
+
|
2104
|
+
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
2105
|
+
const out = [
|
2106
|
+
...type,
|
2107
|
+
[ Opcodes.local_set, tmp ],
|
2108
|
+
[ Opcodes.block, returns ]
|
2109
|
+
];
|
2110
|
+
|
2111
|
+
for (const x in bc) {
|
2112
|
+
if (x === 'default') continue;
|
2113
|
+
|
2114
|
+
// if type == x
|
2115
|
+
out.push([ Opcodes.local_get, tmp ]);
|
2116
|
+
out.push(...number(x, Valtype.i32));
|
2117
|
+
out.push([ Opcodes.i32_eq ]);
|
2118
|
+
|
2119
|
+
out.push([ Opcodes.if, Blocktype.void, `TYPESWITCH|${TYPE_NAMES[x]}` ]);
|
2120
|
+
out.push(...bc[x]);
|
2121
|
+
out.push([ Opcodes.br, 1 ]);
|
2122
|
+
out.push([ Opcodes.end ]);
|
2123
|
+
}
|
2124
|
+
|
2125
|
+
// default
|
2126
|
+
if (bc.default) out.push(...bc.default);
|
2127
|
+
else if (returns !== Blocktype.void) out.push(...number(0, returns));
|
2128
|
+
|
2129
|
+
out.push([ Opcodes.end, 'TYPESWITCH_end' ]);
|
2130
|
+
|
2131
|
+
return out;
|
2132
|
+
};
|
2133
|
+
|
2134
|
+
const allocVar = (scope, name, global = false, type = true) => {
|
2135
|
+
const target = global ? globals : scope.locals;
|
2136
|
+
|
2137
|
+
// already declared
|
1655
2138
|
if (target[name]) {
|
1656
2139
|
// parser should catch this but sanity check anyway
|
1657
2140
|
// if (decl.kind !== 'var') return internalThrow(scope, 'SyntaxError', `Identifier '${unhackName(name)}' has already been declared`);
|
@@ -1662,12 +2145,62 @@ const allocVar = (scope, name, global = false) => {
|
|
1662
2145
|
let idx = global ? globalInd++ : scope.localInd++;
|
1663
2146
|
target[name] = { idx, type: valtypeBinary };
|
1664
2147
|
|
1665
|
-
|
1666
|
-
|
2148
|
+
if (type) {
|
2149
|
+
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2150
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2151
|
+
}
|
1667
2152
|
|
1668
2153
|
return idx;
|
1669
2154
|
};
|
1670
2155
|
|
2156
|
+
const addVarMetadata = (scope, name, global = false, metadata = {}) => {
|
2157
|
+
const target = global ? globals : scope.locals;
|
2158
|
+
|
2159
|
+
target[name].metadata ??= {};
|
2160
|
+
for (const x in metadata) {
|
2161
|
+
if (metadata[x] != null) target[name].metadata[x] = metadata[x];
|
2162
|
+
}
|
2163
|
+
};
|
2164
|
+
|
2165
|
+
const typeAnnoToPorfType = x => {
|
2166
|
+
if (!x) return null;
|
2167
|
+
if (TYPES[x.toLowerCase()] != null) return TYPES[x.toLowerCase()];
|
2168
|
+
if (TYPES['_' + x.toLowerCase()] != null) return TYPES['_' + x.toLowerCase()];
|
2169
|
+
|
2170
|
+
switch (x) {
|
2171
|
+
case 'i32':
|
2172
|
+
case 'i64':
|
2173
|
+
case 'f64':
|
2174
|
+
return TYPES.number;
|
2175
|
+
}
|
2176
|
+
|
2177
|
+
return null;
|
2178
|
+
};
|
2179
|
+
|
2180
|
+
const extractTypeAnnotation = decl => {
|
2181
|
+
let a = decl;
|
2182
|
+
while (a.typeAnnotation) a = a.typeAnnotation;
|
2183
|
+
|
2184
|
+
let type = null, elementType = null;
|
2185
|
+
if (a.typeName) {
|
2186
|
+
type = a.typeName.name;
|
2187
|
+
} else if (a.type.endsWith('Keyword')) {
|
2188
|
+
type = a.type.slice(2, -7).toLowerCase();
|
2189
|
+
} else if (a.type === 'TSArrayType') {
|
2190
|
+
type = 'array';
|
2191
|
+
elementType = extractTypeAnnotation(a.elementType).type;
|
2192
|
+
}
|
2193
|
+
|
2194
|
+
const typeName = type;
|
2195
|
+
type = typeAnnoToPorfType(type);
|
2196
|
+
|
2197
|
+
if (type === TYPES.bytestring && !Prefs.bytestring) type = TYPES.string;
|
2198
|
+
|
2199
|
+
// if (decl.name) console.log(decl.name, { type, elementType });
|
2200
|
+
|
2201
|
+
return { type, typeName, elementType };
|
2202
|
+
};
|
2203
|
+
|
1671
2204
|
const generateVar = (scope, decl) => {
|
1672
2205
|
let out = [];
|
1673
2206
|
|
@@ -1675,10 +2208,13 @@ const generateVar = (scope, decl) => {
|
|
1675
2208
|
|
1676
2209
|
// global variable if in top scope (main) and var ..., or if wanted
|
1677
2210
|
const global = topLevel || decl._bare; // decl.kind === 'var';
|
2211
|
+
const target = global ? globals : scope.locals;
|
1678
2212
|
|
1679
2213
|
for (const x of decl.declarations) {
|
1680
2214
|
const name = mapName(x.id.name);
|
1681
2215
|
|
2216
|
+
if (!name) return todo(scope, 'destructuring is not supported yet');
|
2217
|
+
|
1682
2218
|
if (x.init && isFuncType(x.init.type)) {
|
1683
2219
|
// hack for let a = function () { ... }
|
1684
2220
|
x.init.id = { name };
|
@@ -1694,11 +2230,29 @@ const generateVar = (scope, decl) => {
|
|
1694
2230
|
continue; // always ignore
|
1695
2231
|
}
|
1696
2232
|
|
1697
|
-
|
1698
|
-
|
1699
|
-
|
2233
|
+
// // generate init before allocating var
|
2234
|
+
// let generated;
|
2235
|
+
// if (x.init) generated = generate(scope, x.init, global, name);
|
2236
|
+
|
2237
|
+
const typed = typedInput && x.id.typeAnnotation;
|
2238
|
+
let idx = allocVar(scope, name, global, !(typed && extractTypeAnnotation(x.id).type != null));
|
2239
|
+
|
2240
|
+
if (typed) {
|
2241
|
+
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
2242
|
+
}
|
1700
2243
|
|
1701
|
-
|
2244
|
+
if (x.init) {
|
2245
|
+
const generated = generate(scope, x.init, global, name);
|
2246
|
+
if (scope.arrays?.get(name) != null) {
|
2247
|
+
// hack to set local as pointer before
|
2248
|
+
out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2249
|
+
if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
|
2250
|
+
generated.pop();
|
2251
|
+
out = out.concat(generated);
|
2252
|
+
} else {
|
2253
|
+
out = out.concat(generated);
|
2254
|
+
out.push([ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2255
|
+
}
|
1702
2256
|
out.push(...setType(scope, name, getNodeType(scope, x.init)));
|
1703
2257
|
}
|
1704
2258
|
|
@@ -1709,7 +2263,8 @@ const generateVar = (scope, decl) => {
|
|
1709
2263
|
return out;
|
1710
2264
|
};
|
1711
2265
|
|
1712
|
-
|
2266
|
+
// todo: optimize this func for valueUnused
|
2267
|
+
const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
1713
2268
|
const { type, name } = decl.left;
|
1714
2269
|
|
1715
2270
|
if (type === 'ObjectPattern') {
|
@@ -1724,22 +2279,30 @@ const generateAssign = (scope, decl) => {
|
|
1724
2279
|
return [];
|
1725
2280
|
}
|
1726
2281
|
|
2282
|
+
const op = decl.operator.slice(0, -1) || '=';
|
2283
|
+
|
1727
2284
|
// hack: .length setter
|
1728
2285
|
if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
|
1729
2286
|
const name = decl.left.object.name;
|
1730
|
-
const pointer = arrays
|
2287
|
+
const pointer = scope.arrays?.get(name);
|
1731
2288
|
|
1732
|
-
const aotPointer = pointer != null;
|
2289
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
1733
2290
|
|
1734
2291
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
2292
|
+
const pointerTmp = op === '=' ? null : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
1735
2293
|
|
1736
2294
|
return [
|
1737
2295
|
...(aotPointer ? number(0, Valtype.i32) : [
|
1738
2296
|
...generate(scope, decl.left.object),
|
1739
2297
|
Opcodes.i32_to_u
|
1740
2298
|
]),
|
2299
|
+
...(!pointerTmp ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
1741
2300
|
|
1742
|
-
...generate(scope, decl.right),
|
2301
|
+
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2302
|
+
[ Opcodes.local_get, pointerTmp ],
|
2303
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
2304
|
+
Opcodes.i32_from_u
|
2305
|
+
], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right))),
|
1743
2306
|
[ Opcodes.local_tee, newValueTmp ],
|
1744
2307
|
|
1745
2308
|
Opcodes.i32_to_u,
|
@@ -1749,21 +2312,19 @@ const generateAssign = (scope, decl) => {
|
|
1749
2312
|
];
|
1750
2313
|
}
|
1751
2314
|
|
1752
|
-
const op = decl.operator.slice(0, -1) || '=';
|
1753
|
-
|
1754
2315
|
// arr[i]
|
1755
2316
|
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
1756
2317
|
const name = decl.left.object.name;
|
1757
|
-
const pointer = arrays
|
2318
|
+
const pointer = scope.arrays?.get(name);
|
1758
2319
|
|
1759
|
-
const aotPointer = pointer != null;
|
2320
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
1760
2321
|
|
1761
2322
|
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
1762
2323
|
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
1763
2324
|
|
1764
2325
|
return [
|
1765
2326
|
...typeSwitch(scope, getNodeType(scope, decl.left.object), {
|
1766
|
-
[TYPES.
|
2327
|
+
[TYPES.array]: [
|
1767
2328
|
...(aotPointer ? [] : [
|
1768
2329
|
...generate(scope, decl.left.object),
|
1769
2330
|
Opcodes.i32_to_u
|
@@ -1812,6 +2373,8 @@ const generateAssign = (scope, decl) => {
|
|
1812
2373
|
];
|
1813
2374
|
}
|
1814
2375
|
|
2376
|
+
if (!name) return todo(scope, 'destructuring is not supported yet', true);
|
2377
|
+
|
1815
2378
|
const [ local, isGlobal ] = lookupName(scope, name);
|
1816
2379
|
|
1817
2380
|
if (local === undefined) {
|
@@ -1858,9 +2421,7 @@ const generateAssign = (scope, decl) => {
|
|
1858
2421
|
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
1859
2422
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1860
2423
|
|
1861
|
-
|
1862
|
-
// hack: type is idx+1
|
1863
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2424
|
+
...setType(scope, name, getLastType(scope))
|
1864
2425
|
];
|
1865
2426
|
}
|
1866
2427
|
|
@@ -1871,9 +2432,7 @@ const generateAssign = (scope, decl) => {
|
|
1871
2432
|
|
1872
2433
|
// todo: string concat types
|
1873
2434
|
|
1874
|
-
|
1875
|
-
...number(TYPES.number, Valtype.i32),
|
1876
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2435
|
+
...setType(scope, name, TYPES.number)
|
1877
2436
|
];
|
1878
2437
|
};
|
1879
2438
|
|
@@ -1919,7 +2478,7 @@ const generateUnary = (scope, decl) => {
|
|
1919
2478
|
return out;
|
1920
2479
|
}
|
1921
2480
|
|
1922
|
-
case 'delete':
|
2481
|
+
case 'delete': {
|
1923
2482
|
let toReturn = true, toGenerate = true;
|
1924
2483
|
|
1925
2484
|
if (decl.argument.type === 'Identifier') {
|
@@ -1941,38 +2500,60 @@ const generateUnary = (scope, decl) => {
|
|
1941
2500
|
|
1942
2501
|
out.push(...number(toReturn ? 1 : 0));
|
1943
2502
|
return out;
|
2503
|
+
}
|
2504
|
+
|
2505
|
+
case 'typeof': {
|
2506
|
+
let overrideType, toGenerate = true;
|
2507
|
+
|
2508
|
+
if (decl.argument.type === 'Identifier') {
|
2509
|
+
const out = generateIdent(scope, decl.argument);
|
1944
2510
|
|
1945
|
-
|
1946
|
-
|
2511
|
+
// if ReferenceError (undeclared var), ignore and return undefined
|
2512
|
+
if (out[1]) {
|
2513
|
+
// does not exist (2 ops from throw)
|
2514
|
+
overrideType = number(TYPES.undefined, Valtype.i32);
|
2515
|
+
toGenerate = false;
|
2516
|
+
}
|
2517
|
+
}
|
2518
|
+
|
2519
|
+
const out = toGenerate ? generate(scope, decl.argument) : [];
|
2520
|
+
disposeLeftover(out);
|
2521
|
+
|
2522
|
+
out.push(...typeSwitch(scope, overrideType ?? getNodeType(scope, decl.argument), {
|
1947
2523
|
[TYPES.number]: makeString(scope, 'number', false, '#typeof_result'),
|
1948
2524
|
[TYPES.boolean]: makeString(scope, 'boolean', false, '#typeof_result'),
|
1949
2525
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
1950
2526
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
1951
2527
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
1952
2528
|
|
2529
|
+
[TYPES.bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2530
|
+
|
1953
2531
|
// object and internal types
|
1954
2532
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
1955
|
-
});
|
2533
|
+
}));
|
2534
|
+
|
2535
|
+
return out;
|
2536
|
+
}
|
1956
2537
|
|
1957
2538
|
default:
|
1958
|
-
return todo(`unary operator ${decl.operator} not implemented yet
|
2539
|
+
return todo(scope, `unary operator ${decl.operator} not implemented yet`, true);
|
1959
2540
|
}
|
1960
2541
|
};
|
1961
2542
|
|
1962
|
-
const generateUpdate = (scope, decl) => {
|
2543
|
+
const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
|
1963
2544
|
const { name } = decl.argument;
|
1964
2545
|
|
1965
2546
|
const [ local, isGlobal ] = lookupName(scope, name);
|
1966
2547
|
|
1967
2548
|
if (local === undefined) {
|
1968
|
-
return todo(`update expression with undefined variable
|
2549
|
+
return todo(scope, `update expression with undefined variable`, true);
|
1969
2550
|
}
|
1970
2551
|
|
1971
2552
|
const idx = local.idx;
|
1972
2553
|
const out = [];
|
1973
2554
|
|
1974
2555
|
out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
1975
|
-
if (!decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2556
|
+
if (!decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
1976
2557
|
|
1977
2558
|
switch (decl.operator) {
|
1978
2559
|
case '++':
|
@@ -1985,7 +2566,7 @@ const generateUpdate = (scope, decl) => {
|
|
1985
2566
|
}
|
1986
2567
|
|
1987
2568
|
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
1988
|
-
if (decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2569
|
+
if (decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
1989
2570
|
|
1990
2571
|
return out;
|
1991
2572
|
};
|
@@ -2025,7 +2606,7 @@ const generateConditional = (scope, decl) => {
|
|
2025
2606
|
// note type
|
2026
2607
|
out.push(
|
2027
2608
|
...getNodeType(scope, decl.consequent),
|
2028
|
-
|
2609
|
+
...setLastType(scope)
|
2029
2610
|
);
|
2030
2611
|
|
2031
2612
|
out.push([ Opcodes.else ]);
|
@@ -2034,7 +2615,7 @@ const generateConditional = (scope, decl) => {
|
|
2034
2615
|
// note type
|
2035
2616
|
out.push(
|
2036
2617
|
...getNodeType(scope, decl.alternate),
|
2037
|
-
|
2618
|
+
...setLastType(scope)
|
2038
2619
|
);
|
2039
2620
|
|
2040
2621
|
out.push([ Opcodes.end ]);
|
@@ -2048,15 +2629,17 @@ const generateFor = (scope, decl) => {
|
|
2048
2629
|
const out = [];
|
2049
2630
|
|
2050
2631
|
if (decl.init) {
|
2051
|
-
out.push(...generate(scope, decl.init));
|
2632
|
+
out.push(...generate(scope, decl.init, false, undefined, true));
|
2052
2633
|
disposeLeftover(out);
|
2053
2634
|
}
|
2054
2635
|
|
2055
2636
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
2056
2637
|
depth.push('for');
|
2057
2638
|
|
2058
|
-
out.push(...generate(scope, decl.test));
|
2059
|
-
|
2639
|
+
if (decl.test) out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2640
|
+
else out.push(...number(1, Valtype.i32));
|
2641
|
+
|
2642
|
+
out.push([ Opcodes.if, Blocktype.void ]);
|
2060
2643
|
depth.push('if');
|
2061
2644
|
|
2062
2645
|
out.push([ Opcodes.block, Blocktype.void ]);
|
@@ -2064,8 +2647,7 @@ const generateFor = (scope, decl) => {
|
|
2064
2647
|
out.push(...generate(scope, decl.body));
|
2065
2648
|
out.push([ Opcodes.end ]);
|
2066
2649
|
|
2067
|
-
out.push(...generate(scope, decl.update));
|
2068
|
-
depth.pop();
|
2650
|
+
if (decl.update) out.push(...generate(scope, decl.update, false, undefined, true));
|
2069
2651
|
|
2070
2652
|
out.push([ Opcodes.br, 1 ]);
|
2071
2653
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2093,6 +2675,36 @@ const generateWhile = (scope, decl) => {
|
|
2093
2675
|
return out;
|
2094
2676
|
};
|
2095
2677
|
|
2678
|
+
const generateDoWhile = (scope, decl) => {
|
2679
|
+
const out = [];
|
2680
|
+
|
2681
|
+
out.push([ Opcodes.loop, Blocktype.void ]);
|
2682
|
+
depth.push('dowhile');
|
2683
|
+
|
2684
|
+
// block for break (includes all)
|
2685
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2686
|
+
depth.push('block');
|
2687
|
+
|
2688
|
+
// block for continue
|
2689
|
+
// includes body but not test+loop so we can exit body at anytime
|
2690
|
+
// and still test+loop after
|
2691
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2692
|
+
depth.push('block');
|
2693
|
+
|
2694
|
+
out.push(...generate(scope, decl.body));
|
2695
|
+
|
2696
|
+
out.push([ Opcodes.end ]);
|
2697
|
+
depth.pop();
|
2698
|
+
|
2699
|
+
out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2700
|
+
out.push([ Opcodes.br_if, 1 ]);
|
2701
|
+
|
2702
|
+
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
2703
|
+
depth.pop(); depth.pop();
|
2704
|
+
|
2705
|
+
return out;
|
2706
|
+
};
|
2707
|
+
|
2096
2708
|
const generateForOf = (scope, decl) => {
|
2097
2709
|
const out = [];
|
2098
2710
|
|
@@ -2122,8 +2734,17 @@ const generateForOf = (scope, decl) => {
|
|
2122
2734
|
// setup local for left
|
2123
2735
|
generate(scope, decl.left);
|
2124
2736
|
|
2125
|
-
|
2737
|
+
let leftName = decl.left.declarations?.[0]?.id?.name;
|
2738
|
+
if (!leftName && decl.left.name) {
|
2739
|
+
leftName = decl.left.name;
|
2740
|
+
|
2741
|
+
generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
|
2742
|
+
}
|
2743
|
+
|
2744
|
+
// if (!leftName) console.log(decl.left?.declarations?.[0]?.id ?? decl.left);
|
2745
|
+
|
2126
2746
|
const [ local, isGlobal ] = lookupName(scope, leftName);
|
2747
|
+
if (!local) return todo(scope, 'for of failed to get left local (probably destructure)');
|
2127
2748
|
|
2128
2749
|
depth.push('block');
|
2129
2750
|
depth.push('block');
|
@@ -2131,15 +2752,17 @@ const generateForOf = (scope, decl) => {
|
|
2131
2752
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2132
2753
|
// hack: this is naughty and will break things!
|
2133
2754
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2134
|
-
if (pages.
|
2755
|
+
if (pages.hasAnyString) {
|
2756
|
+
// todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
|
2135
2757
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2136
2758
|
rawElements: new Array(1)
|
2137
2759
|
}, isGlobal, leftName, true, 'i16');
|
2138
2760
|
}
|
2139
2761
|
|
2140
2762
|
// set type for local
|
2763
|
+
// todo: optimize away counter and use end pointer
|
2141
2764
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2142
|
-
[TYPES.
|
2765
|
+
[TYPES.array]: [
|
2143
2766
|
...setType(scope, leftName, TYPES.number),
|
2144
2767
|
|
2145
2768
|
[ Opcodes.loop, Blocktype.void ],
|
@@ -2222,6 +2845,56 @@ const generateForOf = (scope, decl) => {
|
|
2222
2845
|
[ Opcodes.end ],
|
2223
2846
|
[ Opcodes.end ]
|
2224
2847
|
],
|
2848
|
+
[TYPES.bytestring]: [
|
2849
|
+
...setType(scope, leftName, TYPES.bytestring),
|
2850
|
+
|
2851
|
+
[ Opcodes.loop, Blocktype.void ],
|
2852
|
+
|
2853
|
+
// setup new/out array
|
2854
|
+
...newOut,
|
2855
|
+
[ Opcodes.drop ],
|
2856
|
+
|
2857
|
+
...number(0, Valtype.i32), // base 0 for store after
|
2858
|
+
|
2859
|
+
// load current string ind {arg}
|
2860
|
+
[ Opcodes.local_get, pointer ],
|
2861
|
+
[ Opcodes.local_get, counter ],
|
2862
|
+
[ Opcodes.i32_add ],
|
2863
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2864
|
+
|
2865
|
+
// store to new string ind 0
|
2866
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2867
|
+
|
2868
|
+
// return new string (page)
|
2869
|
+
...number(newPointer),
|
2870
|
+
|
2871
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2872
|
+
|
2873
|
+
[ Opcodes.block, Blocktype.void ],
|
2874
|
+
[ Opcodes.block, Blocktype.void ],
|
2875
|
+
...generate(scope, decl.body),
|
2876
|
+
[ Opcodes.end ],
|
2877
|
+
|
2878
|
+
// increment iter pointer
|
2879
|
+
// [ Opcodes.local_get, pointer ],
|
2880
|
+
// ...number(1, Valtype.i32),
|
2881
|
+
// [ Opcodes.i32_add ],
|
2882
|
+
// [ Opcodes.local_set, pointer ],
|
2883
|
+
|
2884
|
+
// increment counter by 1
|
2885
|
+
[ Opcodes.local_get, counter ],
|
2886
|
+
...number(1, Valtype.i32),
|
2887
|
+
[ Opcodes.i32_add ],
|
2888
|
+
[ Opcodes.local_tee, counter ],
|
2889
|
+
|
2890
|
+
// loop if counter != length
|
2891
|
+
[ Opcodes.local_get, length ],
|
2892
|
+
[ Opcodes.i32_ne ],
|
2893
|
+
[ Opcodes.br_if, 1 ],
|
2894
|
+
|
2895
|
+
[ Opcodes.end ],
|
2896
|
+
[ Opcodes.end ]
|
2897
|
+
],
|
2225
2898
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2226
2899
|
}, Blocktype.void));
|
2227
2900
|
|
@@ -2232,28 +2905,65 @@ const generateForOf = (scope, decl) => {
|
|
2232
2905
|
return out;
|
2233
2906
|
};
|
2234
2907
|
|
2908
|
+
// find the nearest loop in depth map by type
|
2235
2909
|
const getNearestLoop = () => {
|
2236
2910
|
for (let i = depth.length - 1; i >= 0; i--) {
|
2237
|
-
if (
|
2911
|
+
if (['while', 'dowhile', 'for', 'forof'].includes(depth[i])) return i;
|
2238
2912
|
}
|
2239
2913
|
|
2240
2914
|
return -1;
|
2241
2915
|
};
|
2242
2916
|
|
2243
2917
|
const generateBreak = (scope, decl) => {
|
2244
|
-
const
|
2918
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2919
|
+
const type = depth[target];
|
2920
|
+
|
2921
|
+
// different loop types have different branch offsets
|
2922
|
+
// as they have different wasm block/loop/if structures
|
2923
|
+
// we need to use the right offset by type to branch to the one we want
|
2924
|
+
// for a break: exit the loop without executing anything else inside it
|
2925
|
+
const offset = ({
|
2926
|
+
for: 2, // loop > if (wanted branch) > block (we are here)
|
2927
|
+
while: 2, // loop > if (wanted branch) (we are here)
|
2928
|
+
dowhile: 2, // loop > block (wanted branch) > block (we are here)
|
2929
|
+
forof: 2, // loop > block (wanted branch) > block (we are here)
|
2930
|
+
if: 1 // break inside if, branch 0 to skip the rest of the if
|
2931
|
+
})[type];
|
2932
|
+
|
2245
2933
|
return [
|
2246
|
-
[ Opcodes.br, ...signedLEB128(
|
2934
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2247
2935
|
];
|
2248
2936
|
};
|
2249
2937
|
|
2250
2938
|
const generateContinue = (scope, decl) => {
|
2251
|
-
const
|
2939
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2940
|
+
const type = depth[target];
|
2941
|
+
|
2942
|
+
// different loop types have different branch offsets
|
2943
|
+
// as they have different wasm block/loop/if structures
|
2944
|
+
// we need to use the right offset by type to branch to the one we want
|
2945
|
+
// for a continue: do test for the loop, and then loop depending on that success
|
2946
|
+
const offset = ({
|
2947
|
+
for: 3, // loop (wanted branch) > if > block (we are here)
|
2948
|
+
while: 1, // loop (wanted branch) > if (we are here)
|
2949
|
+
dowhile: 3, // loop > block > block (wanted branch) (we are here)
|
2950
|
+
forof: 3 // loop > block > block (wanted branch) (we are here)
|
2951
|
+
})[type];
|
2952
|
+
|
2252
2953
|
return [
|
2253
|
-
[ Opcodes.br, ...signedLEB128(
|
2954
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2254
2955
|
];
|
2255
2956
|
};
|
2256
2957
|
|
2958
|
+
const generateLabel = (scope, decl) => {
|
2959
|
+
scope.labels ??= new Map();
|
2960
|
+
|
2961
|
+
const name = decl.label.name;
|
2962
|
+
scope.labels.set(name, depth.length);
|
2963
|
+
|
2964
|
+
return generate(scope, decl.body);
|
2965
|
+
};
|
2966
|
+
|
2257
2967
|
const generateThrow = (scope, decl) => {
|
2258
2968
|
scope.throws = true;
|
2259
2969
|
|
@@ -2262,7 +2972,7 @@ const generateThrow = (scope, decl) => {
|
|
2262
2972
|
// hack: throw new X("...") -> throw "..."
|
2263
2973
|
if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
|
2264
2974
|
constructor = decl.argument.callee.name;
|
2265
|
-
message = decl.argument.arguments[0]
|
2975
|
+
message = decl.argument.arguments[0]?.value ?? '';
|
2266
2976
|
}
|
2267
2977
|
|
2268
2978
|
if (tags.length === 0) tags.push({
|
@@ -2274,6 +2984,9 @@ const generateThrow = (scope, decl) => {
|
|
2274
2984
|
let exceptId = exceptions.push({ constructor, message }) - 1;
|
2275
2985
|
let tagIdx = tags[0].idx;
|
2276
2986
|
|
2987
|
+
scope.exceptions ??= [];
|
2988
|
+
scope.exceptions.push(exceptId);
|
2989
|
+
|
2277
2990
|
// todo: write a description of how this works lol
|
2278
2991
|
|
2279
2992
|
return [
|
@@ -2283,7 +2996,7 @@ const generateThrow = (scope, decl) => {
|
|
2283
2996
|
};
|
2284
2997
|
|
2285
2998
|
const generateTry = (scope, decl) => {
|
2286
|
-
if (decl.finalizer) return todo('try finally not implemented yet');
|
2999
|
+
if (decl.finalizer) return todo(scope, 'try finally not implemented yet');
|
2287
3000
|
|
2288
3001
|
const out = [];
|
2289
3002
|
|
@@ -2314,29 +3027,35 @@ const generateAssignPat = (scope, decl) => {
|
|
2314
3027
|
// TODO
|
2315
3028
|
// if identifier declared, use that
|
2316
3029
|
// else, use default (right)
|
2317
|
-
return todo('assignment pattern (optional arg)');
|
3030
|
+
return todo(scope, 'assignment pattern (optional arg)');
|
2318
3031
|
};
|
2319
3032
|
|
2320
3033
|
let pages = new Map();
|
2321
|
-
const allocPage = (reason, type) => {
|
3034
|
+
const allocPage = (scope, reason, type) => {
|
2322
3035
|
if (pages.has(reason)) return pages.get(reason).ind;
|
2323
3036
|
|
2324
3037
|
if (reason.startsWith('array:')) pages.hasArray = true;
|
2325
3038
|
if (reason.startsWith('string:')) pages.hasString = true;
|
3039
|
+
if (reason.startsWith('bytestring:')) pages.hasByteString = true;
|
3040
|
+
if (reason.includes('string:')) pages.hasAnyString = true;
|
2326
3041
|
|
2327
3042
|
const ind = pages.size;
|
2328
3043
|
pages.set(reason, { ind, type });
|
2329
3044
|
|
2330
|
-
|
3045
|
+
scope.pages ??= new Map();
|
3046
|
+
scope.pages.set(reason, { ind, type });
|
3047
|
+
|
3048
|
+
if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
2331
3049
|
|
2332
3050
|
return ind;
|
2333
3051
|
};
|
2334
3052
|
|
3053
|
+
// todo: add scope.pages
|
2335
3054
|
const freePage = reason => {
|
2336
3055
|
const { ind } = pages.get(reason);
|
2337
3056
|
pages.delete(reason);
|
2338
3057
|
|
2339
|
-
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
3058
|
+
if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2340
3059
|
|
2341
3060
|
return ind;
|
2342
3061
|
};
|
@@ -2356,38 +3075,53 @@ const StoreOps = {
|
|
2356
3075
|
f64: Opcodes.f64_store,
|
2357
3076
|
|
2358
3077
|
// expects i32 input!
|
2359
|
-
|
3078
|
+
i8: Opcodes.i32_store8,
|
3079
|
+
i16: Opcodes.i32_store16,
|
2360
3080
|
};
|
2361
3081
|
|
2362
3082
|
let data = [];
|
2363
3083
|
|
2364
|
-
const compileBytes = (val, itemType
|
3084
|
+
const compileBytes = (val, itemType) => {
|
2365
3085
|
// todo: this is a mess and needs confirming / ????
|
2366
3086
|
switch (itemType) {
|
2367
3087
|
case 'i8': return [ val % 256 ];
|
2368
|
-
case 'i16': return [ val % 256,
|
2369
|
-
|
2370
|
-
case 'i32':
|
2371
|
-
|
2372
|
-
return enforceFourBytes(signedLEB128(val));
|
3088
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
3089
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
3090
|
+
case 'i32': return [...new Uint8Array(new Int32Array([ val ]).buffer)];
|
3091
|
+
// todo: i64
|
2373
3092
|
|
2374
3093
|
case 'f64': return ieee754_binary64(val);
|
2375
3094
|
}
|
2376
3095
|
};
|
2377
3096
|
|
3097
|
+
const getAllocType = itemType => {
|
3098
|
+
switch (itemType) {
|
3099
|
+
case 'i8': return 'bytestring';
|
3100
|
+
case 'i16': return 'string';
|
3101
|
+
|
3102
|
+
default: return 'array';
|
3103
|
+
}
|
3104
|
+
};
|
3105
|
+
|
2378
3106
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2379
3107
|
const out = [];
|
2380
3108
|
|
3109
|
+
scope.arrays ??= new Map();
|
3110
|
+
|
2381
3111
|
let firstAssign = false;
|
2382
|
-
if (!arrays.has(name) || name === '$undeclared') {
|
3112
|
+
if (!scope.arrays.has(name) || name === '$undeclared') {
|
2383
3113
|
firstAssign = true;
|
2384
3114
|
|
2385
3115
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2386
3116
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2387
|
-
|
3117
|
+
|
3118
|
+
if (Prefs.scopedPageNames) scope.arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType) * pageSize);
|
3119
|
+
else scope.arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2388
3120
|
}
|
2389
3121
|
|
2390
|
-
const pointer = arrays.get(name);
|
3122
|
+
const pointer = scope.arrays.get(name);
|
3123
|
+
|
3124
|
+
const local = global ? globals[name] : scope.locals[name];
|
2391
3125
|
|
2392
3126
|
const useRawElements = !!decl.rawElements;
|
2393
3127
|
const elements = useRawElements ? decl.rawElements : decl.elements;
|
@@ -2395,19 +3129,25 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2395
3129
|
const valtype = itemTypeToValtype[itemType];
|
2396
3130
|
const length = elements.length;
|
2397
3131
|
|
2398
|
-
if (firstAssign && useRawElements) {
|
2399
|
-
|
3132
|
+
if (firstAssign && useRawElements && !Prefs.noData) {
|
3133
|
+
// if length is 0 memory/data will just be 0000... anyway
|
3134
|
+
if (length !== 0) {
|
3135
|
+
let bytes = compileBytes(length, 'i32');
|
2400
3136
|
|
2401
|
-
|
2402
|
-
|
3137
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3138
|
+
if (elements[i] == null) continue;
|
2403
3139
|
|
2404
|
-
|
2405
|
-
|
3140
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
3141
|
+
}
|
2406
3142
|
|
2407
|
-
|
2408
|
-
|
2409
|
-
|
2410
|
-
|
3143
|
+
const ind = data.push({
|
3144
|
+
offset: pointer,
|
3145
|
+
bytes
|
3146
|
+
}) - 1;
|
3147
|
+
|
3148
|
+
scope.data ??= [];
|
3149
|
+
scope.data.push(ind);
|
3150
|
+
}
|
2411
3151
|
|
2412
3152
|
// local value as pointer
|
2413
3153
|
out.push(...number(pointer));
|
@@ -2415,11 +3155,22 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2415
3155
|
return [ out, pointer ];
|
2416
3156
|
}
|
2417
3157
|
|
3158
|
+
const pointerTmp = local != null ? localTmp(scope, '#makearray_pointer_tmp', Valtype.i32) : null;
|
3159
|
+
if (pointerTmp != null) {
|
3160
|
+
out.push(
|
3161
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
3162
|
+
Opcodes.i32_to_u,
|
3163
|
+
[ Opcodes.local_set, pointerTmp ]
|
3164
|
+
);
|
3165
|
+
}
|
3166
|
+
|
3167
|
+
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3168
|
+
|
2418
3169
|
// store length as 0th array
|
2419
3170
|
out.push(
|
2420
|
-
...
|
3171
|
+
...pointerWasm,
|
2421
3172
|
...number(length, Valtype.i32),
|
2422
|
-
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1,
|
3173
|
+
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
|
2423
3174
|
);
|
2424
3175
|
|
2425
3176
|
const storeOp = StoreOps[itemType];
|
@@ -2428,43 +3179,78 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2428
3179
|
if (elements[i] == null) continue;
|
2429
3180
|
|
2430
3181
|
out.push(
|
2431
|
-
...
|
3182
|
+
...pointerWasm,
|
2432
3183
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
2433
|
-
[ storeOp, Math.log2(ValtypeSize[itemType]) - 1, ...unsignedLEB128(
|
3184
|
+
[ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2434
3185
|
);
|
2435
3186
|
}
|
2436
3187
|
|
2437
3188
|
// local value as pointer
|
2438
|
-
out.push(...
|
3189
|
+
out.push(...pointerWasm, Opcodes.i32_from_u);
|
2439
3190
|
|
2440
3191
|
return [ out, pointer ];
|
2441
3192
|
};
|
2442
3193
|
|
2443
|
-
const
|
3194
|
+
const byteStringable = str => {
|
3195
|
+
if (!Prefs.bytestring) return false;
|
3196
|
+
|
3197
|
+
for (let i = 0; i < str.length; i++) {
|
3198
|
+
if (str.charCodeAt(i) > 0xFF) return false;
|
3199
|
+
}
|
3200
|
+
|
3201
|
+
return true;
|
3202
|
+
};
|
3203
|
+
|
3204
|
+
const makeString = (scope, str, global = false, name = '$undeclared', forceBytestring = undefined) => {
|
2444
3205
|
const rawElements = new Array(str.length);
|
3206
|
+
let byteStringable = Prefs.bytestring;
|
2445
3207
|
for (let i = 0; i < str.length; i++) {
|
2446
|
-
|
3208
|
+
const c = str.charCodeAt(i);
|
3209
|
+
rawElements[i] = c;
|
3210
|
+
|
3211
|
+
if (byteStringable && c > 0xFF) byteStringable = false;
|
2447
3212
|
}
|
2448
3213
|
|
3214
|
+
if (byteStringable && forceBytestring === false) byteStringable = false;
|
3215
|
+
|
2449
3216
|
return makeArray(scope, {
|
2450
3217
|
rawElements
|
2451
|
-
}, global, name, false, 'i16')[0];
|
3218
|
+
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2452
3219
|
};
|
2453
3220
|
|
2454
|
-
let arrays = new Map();
|
2455
3221
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
2456
3222
|
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
2457
3223
|
};
|
2458
3224
|
|
2459
3225
|
export const generateMember = (scope, decl, _global, _name) => {
|
2460
3226
|
const name = decl.object.name;
|
2461
|
-
const pointer = arrays
|
3227
|
+
const pointer = scope.arrays?.get(name);
|
2462
3228
|
|
2463
|
-
const aotPointer = pointer != null;
|
3229
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2464
3230
|
|
2465
3231
|
// hack: .length
|
2466
3232
|
if (decl.property.name === 'length') {
|
2467
|
-
|
3233
|
+
const func = funcs.find(x => x.name === name);
|
3234
|
+
if (func) {
|
3235
|
+
const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
|
3236
|
+
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
3237
|
+
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3238
|
+
}
|
3239
|
+
|
3240
|
+
if (builtinFuncs[name + '$constructor']) {
|
3241
|
+
const regularFunc = builtinFuncs[name];
|
3242
|
+
const regularParams = regularFunc.typedParams ? (regularFunc.params.length / 2) : regularFunc.params.length;
|
3243
|
+
|
3244
|
+
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3245
|
+
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3246
|
+
|
3247
|
+
return number(Math.max(regularParams, constructorParams));
|
3248
|
+
}
|
3249
|
+
|
3250
|
+
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3251
|
+
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3252
|
+
if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
|
3253
|
+
|
2468
3254
|
return [
|
2469
3255
|
...(aotPointer ? number(0, Valtype.i32) : [
|
2470
3256
|
...generate(scope, decl.object),
|
@@ -2476,19 +3262,22 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2476
3262
|
];
|
2477
3263
|
}
|
2478
3264
|
|
3265
|
+
const object = generate(scope, decl.object);
|
3266
|
+
const property = generate(scope, decl.property);
|
3267
|
+
|
2479
3268
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2480
3269
|
// hack: this is naughty and will break things!
|
2481
|
-
let newOut = number(0,
|
2482
|
-
if (pages.
|
3270
|
+
let newOut = number(0, valtypeBinary), newPointer = -1;
|
3271
|
+
if (pages.hasAnyString) {
|
2483
3272
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2484
3273
|
rawElements: new Array(1)
|
2485
3274
|
}, _global, _name, true, 'i16');
|
2486
3275
|
}
|
2487
3276
|
|
2488
3277
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
2489
|
-
[TYPES.
|
3278
|
+
[TYPES.array]: [
|
2490
3279
|
// get index as valtype
|
2491
|
-
...
|
3280
|
+
...property,
|
2492
3281
|
|
2493
3282
|
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2494
3283
|
Opcodes.i32_to_u,
|
@@ -2496,7 +3285,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2496
3285
|
[ Opcodes.i32_mul ],
|
2497
3286
|
|
2498
3287
|
...(aotPointer ? [] : [
|
2499
|
-
...
|
3288
|
+
...object,
|
2500
3289
|
Opcodes.i32_to_u,
|
2501
3290
|
[ Opcodes.i32_add ]
|
2502
3291
|
]),
|
@@ -2505,7 +3294,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2505
3294
|
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2506
3295
|
|
2507
3296
|
...number(TYPES.number, Valtype.i32),
|
2508
|
-
|
3297
|
+
...setLastType(scope)
|
2509
3298
|
],
|
2510
3299
|
|
2511
3300
|
[TYPES.string]: [
|
@@ -2515,14 +3304,14 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2515
3304
|
|
2516
3305
|
...number(0, Valtype.i32), // base 0 for store later
|
2517
3306
|
|
2518
|
-
...
|
2519
|
-
|
3307
|
+
...property,
|
2520
3308
|
Opcodes.i32_to_u,
|
3309
|
+
|
2521
3310
|
...number(ValtypeSize.i16, Valtype.i32),
|
2522
3311
|
[ Opcodes.i32_mul ],
|
2523
3312
|
|
2524
3313
|
...(aotPointer ? [] : [
|
2525
|
-
...
|
3314
|
+
...object,
|
2526
3315
|
Opcodes.i32_to_u,
|
2527
3316
|
[ Opcodes.i32_add ]
|
2528
3317
|
]),
|
@@ -2537,10 +3326,38 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2537
3326
|
...number(newPointer),
|
2538
3327
|
|
2539
3328
|
...number(TYPES.string, Valtype.i32),
|
2540
|
-
|
3329
|
+
...setLastType(scope)
|
2541
3330
|
],
|
3331
|
+
[TYPES.bytestring]: [
|
3332
|
+
// setup new/out array
|
3333
|
+
...newOut,
|
3334
|
+
[ Opcodes.drop ],
|
2542
3335
|
|
2543
|
-
|
3336
|
+
...number(0, Valtype.i32), // base 0 for store later
|
3337
|
+
|
3338
|
+
...property,
|
3339
|
+
Opcodes.i32_to_u,
|
3340
|
+
|
3341
|
+
...(aotPointer ? [] : [
|
3342
|
+
...object,
|
3343
|
+
Opcodes.i32_to_u,
|
3344
|
+
[ Opcodes.i32_add ]
|
3345
|
+
]),
|
3346
|
+
|
3347
|
+
// load current string ind {arg}
|
3348
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3349
|
+
|
3350
|
+
// store to new string ind 0
|
3351
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
3352
|
+
|
3353
|
+
// return new string (page)
|
3354
|
+
...number(newPointer),
|
3355
|
+
|
3356
|
+
...number(TYPES.bytestring, Valtype.i32),
|
3357
|
+
...setLastType(scope)
|
3358
|
+
],
|
3359
|
+
|
3360
|
+
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
2544
3361
|
});
|
2545
3362
|
};
|
2546
3363
|
|
@@ -2550,25 +3367,36 @@ const objectHack = node => {
|
|
2550
3367
|
if (!node) return node;
|
2551
3368
|
|
2552
3369
|
if (node.type === 'MemberExpression') {
|
2553
|
-
|
3370
|
+
const out = (() => {
|
3371
|
+
if (node.computed || node.optional) return;
|
3372
|
+
|
3373
|
+
let objectName = node.object.name;
|
2554
3374
|
|
2555
|
-
|
3375
|
+
// if object is not identifier or another member exp, give up
|
3376
|
+
if (node.object.type !== 'Identifier' && node.object.type !== 'MemberExpression') return;
|
3377
|
+
if (objectName && ['undefined', 'null', 'NaN', 'Infinity'].includes(objectName)) return;
|
2556
3378
|
|
2557
|
-
|
2558
|
-
if (node.object.type !== 'Identifier' && node.object.type !== 'MemberExpression') return node;
|
3379
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
2559
3380
|
|
2560
|
-
|
3381
|
+
// if .length, give up (hack within a hack!)
|
3382
|
+
if (node.property.name === 'length') {
|
3383
|
+
node.object = objectHack(node.object);
|
3384
|
+
return;
|
3385
|
+
}
|
2561
3386
|
|
2562
|
-
|
2563
|
-
|
3387
|
+
// no object name, give up
|
3388
|
+
if (!objectName) return;
|
2564
3389
|
|
2565
|
-
|
2566
|
-
|
3390
|
+
const name = '__' + objectName + '_' + node.property.name;
|
3391
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2567
3392
|
|
2568
|
-
|
2569
|
-
|
2570
|
-
|
2571
|
-
|
3393
|
+
return {
|
3394
|
+
type: 'Identifier',
|
3395
|
+
name
|
3396
|
+
};
|
3397
|
+
})();
|
3398
|
+
|
3399
|
+
if (out) return out;
|
2572
3400
|
}
|
2573
3401
|
|
2574
3402
|
for (const x in node) {
|
@@ -2582,11 +3410,11 @@ const objectHack = node => {
|
|
2582
3410
|
};
|
2583
3411
|
|
2584
3412
|
const generateFunc = (scope, decl) => {
|
2585
|
-
if (decl.async) return todo('async functions are not supported');
|
2586
|
-
if (decl.generator) return todo('generator functions are not supported');
|
3413
|
+
if (decl.async) return todo(scope, 'async functions are not supported');
|
3414
|
+
if (decl.generator) return todo(scope, 'generator functions are not supported');
|
2587
3415
|
|
2588
3416
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
2589
|
-
const params = decl.params
|
3417
|
+
const params = decl.params ?? [];
|
2590
3418
|
|
2591
3419
|
// const innerScope = { ...scope };
|
2592
3420
|
// TODO: share scope/locals between !!!
|
@@ -2599,8 +3427,17 @@ const generateFunc = (scope, decl) => {
|
|
2599
3427
|
name
|
2600
3428
|
};
|
2601
3429
|
|
3430
|
+
if (typedInput && decl.returnType) {
|
3431
|
+
innerScope.returnType = extractTypeAnnotation(decl.returnType).type;
|
3432
|
+
innerScope.returns = [ valtypeBinary ];
|
3433
|
+
}
|
3434
|
+
|
2602
3435
|
for (let i = 0; i < params.length; i++) {
|
2603
|
-
allocVar(innerScope, params[i], false);
|
3436
|
+
allocVar(innerScope, params[i].name, false);
|
3437
|
+
|
3438
|
+
if (typedInput && params[i].typeAnnotation) {
|
3439
|
+
addVarMetadata(innerScope, params[i].name, false, extractTypeAnnotation(params[i]));
|
3440
|
+
}
|
2604
3441
|
}
|
2605
3442
|
|
2606
3443
|
let body = objectHack(decl.body);
|
@@ -2616,13 +3453,13 @@ const generateFunc = (scope, decl) => {
|
|
2616
3453
|
const func = {
|
2617
3454
|
name,
|
2618
3455
|
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
2619
|
-
|
2620
|
-
|
2621
|
-
throws: innerScope.throws,
|
2622
|
-
index: currentFuncIndex++
|
3456
|
+
index: currentFuncIndex++,
|
3457
|
+
...innerScope
|
2623
3458
|
};
|
2624
3459
|
funcIndex[name] = func.index;
|
2625
3460
|
|
3461
|
+
if (name === 'main') func.gotLastType = true;
|
3462
|
+
|
2626
3463
|
// quick hack fixes
|
2627
3464
|
for (const inst of wasm) {
|
2628
3465
|
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
@@ -2639,117 +3476,6 @@ const generateFunc = (scope, decl) => {
|
|
2639
3476
|
);
|
2640
3477
|
}
|
2641
3478
|
|
2642
|
-
// change v128 params into many <type> (i32x4 -> i32/etc) instead as unsupported param valtype
|
2643
|
-
let offset = 0, vecParams = 0;
|
2644
|
-
for (let i = 0; i < params.length; i++) {
|
2645
|
-
const name = params[i];
|
2646
|
-
const local = func.locals[name];
|
2647
|
-
if (local.type === Valtype.v128) {
|
2648
|
-
vecParams++;
|
2649
|
-
|
2650
|
-
/* wasm.unshift( // add v128 load for param
|
2651
|
-
[ Opcodes.i32_const, 0 ],
|
2652
|
-
[ ...Opcodes.v128_load, 0, i * 16 ],
|
2653
|
-
[ Opcodes.local_set, local.idx ]
|
2654
|
-
); */
|
2655
|
-
|
2656
|
-
// using params and replace_lane is noticably faster than just loading from memory (above) somehow
|
2657
|
-
|
2658
|
-
// extract valtype and lane count from vec type (i32x4 = i32 4, i8x16 = i8 16, etc)
|
2659
|
-
const { vecType } = local;
|
2660
|
-
let [ type, lanes ] = vecType.split('x');
|
2661
|
-
if (!type || !lanes) throw new Error('bad metadata from vec params'); // sanity check
|
2662
|
-
|
2663
|
-
lanes = parseInt(lanes);
|
2664
|
-
type = Valtype[type];
|
2665
|
-
|
2666
|
-
const name = params[i]; // get original param name
|
2667
|
-
|
2668
|
-
func.params.splice(offset, 1, ...new Array(lanes).fill(type)); // add new params of {type}, {lanes} times
|
2669
|
-
|
2670
|
-
// update index of original local
|
2671
|
-
// delete func.locals[name];
|
2672
|
-
|
2673
|
-
// add new locals for params
|
2674
|
-
for (let j = 0; j < lanes; j++) {
|
2675
|
-
func.locals[name + j] = { idx: offset + j, type, vecParamAutogen: true };
|
2676
|
-
}
|
2677
|
-
|
2678
|
-
// prepend wasm to generate expected v128 locals
|
2679
|
-
wasm.splice(i * 2 + offset * 2, 0,
|
2680
|
-
...i32x4(0, 0, 0, 0),
|
2681
|
-
...new Array(lanes).fill(0).flatMap((_, j) => [
|
2682
|
-
[ Opcodes.local_get, offset + j ],
|
2683
|
-
[ ...Opcodes[vecType + '_replace_lane'], j ]
|
2684
|
-
]),
|
2685
|
-
[ Opcodes.local_set, i ]
|
2686
|
-
);
|
2687
|
-
|
2688
|
-
offset += lanes;
|
2689
|
-
|
2690
|
-
// note: wrapping is disabled for now due to perf/dx concerns (so this will never run)
|
2691
|
-
/* if (!func.name.startsWith('#')) func.name = '##' + func.name;
|
2692
|
-
|
2693
|
-
// add vec type index to hash name prefix for wrapper to know how to wrap
|
2694
|
-
const vecTypeIdx = [ 'i8x16', 'i16x8', 'i32x4', 'i64x2', 'f32x4', 'f64x2' ].indexOf(local.vecType);
|
2695
|
-
const secondHash = func.name.slice(1).indexOf('#');
|
2696
|
-
func.name = '#' + func.name.slice(1, secondHash) + vecTypeIdx + func.name.slice(secondHash); */
|
2697
|
-
}
|
2698
|
-
}
|
2699
|
-
|
2700
|
-
if (offset !== 0) {
|
2701
|
-
// bump local indexes for all other locals after
|
2702
|
-
for (const x in func.locals) {
|
2703
|
-
const local = func.locals[x];
|
2704
|
-
if (!local.vecParamAutogen) local.idx += offset;
|
2705
|
-
}
|
2706
|
-
|
2707
|
-
// bump local indexes in wasm local.get/set
|
2708
|
-
for (let j = 0; j < wasm.length; j++) {
|
2709
|
-
const inst = wasm[j];
|
2710
|
-
if (j < offset * 2 + vecParams * 2) {
|
2711
|
-
if (inst[0] === Opcodes.local_set) inst[1] += offset;
|
2712
|
-
continue;
|
2713
|
-
}
|
2714
|
-
|
2715
|
-
if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set) inst[1] += offset;
|
2716
|
-
}
|
2717
|
-
}
|
2718
|
-
|
2719
|
-
// change v128 return into many <type> instead as unsupported return valtype
|
2720
|
-
const lastReturnLocal = wasm.length > 2 && wasm[wasm.length - 1][0] === Opcodes.return && Object.values(func.locals).find(x => x.idx === wasm[wasm.length - 2][1]);
|
2721
|
-
if (lastReturnLocal && lastReturnLocal.type === Valtype.v128) {
|
2722
|
-
const name = Object.keys(func.locals)[Object.values(func.locals).indexOf(lastReturnLocal)];
|
2723
|
-
// extract valtype and lane count from vec type (i32x4 = i32 4, i8x16 = i8 16, etc)
|
2724
|
-
const { vecType } = lastReturnLocal;
|
2725
|
-
let [ type, lanes ] = vecType.split('x');
|
2726
|
-
if (!type || !lanes) throw new Error('bad metadata from vec params'); // sanity check
|
2727
|
-
|
2728
|
-
lanes = parseInt(lanes);
|
2729
|
-
type = Valtype[type];
|
2730
|
-
|
2731
|
-
const vecIdx = lastReturnLocal.idx;
|
2732
|
-
|
2733
|
-
const lastIdx = Math.max(0, ...Object.values(func.locals).map(x => x.idx));
|
2734
|
-
const tmpIdx = [];
|
2735
|
-
for (let i = 0; i < lanes; i++) {
|
2736
|
-
const idx = lastIdx + i + 1;
|
2737
|
-
tmpIdx.push(idx);
|
2738
|
-
func.locals[name + i] = { idx, type, vecReturnAutogen: true };
|
2739
|
-
}
|
2740
|
-
|
2741
|
-
wasm.splice(wasm.length - 1, 1,
|
2742
|
-
...new Array(lanes).fill(0).flatMap((_, i) => [
|
2743
|
-
i === 0 ? null : [ Opcodes.local_get, vecIdx ],
|
2744
|
-
[ ...Opcodes[vecType + '_extract_lane'], i ],
|
2745
|
-
[ Opcodes.local_set, tmpIdx[i] ],
|
2746
|
-
].filter(x => x !== null)),
|
2747
|
-
...new Array(lanes).fill(0).map((_, i) => [ Opcodes.local_get, tmpIdx[i]])
|
2748
|
-
);
|
2749
|
-
|
2750
|
-
func.returns = new Array(lanes).fill(type);
|
2751
|
-
}
|
2752
|
-
|
2753
3479
|
func.wasm = wasm;
|
2754
3480
|
|
2755
3481
|
funcs.push(func);
|
@@ -2785,7 +3511,7 @@ const internalConstrs = {
|
|
2785
3511
|
|
2786
3512
|
// todo: check in wasm instead of here
|
2787
3513
|
const literalValue = arg.value ?? 0;
|
2788
|
-
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length');
|
3514
|
+
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
|
2789
3515
|
|
2790
3516
|
return [
|
2791
3517
|
...number(0, Valtype.i32),
|
@@ -2796,7 +3522,8 @@ const internalConstrs = {
|
|
2796
3522
|
...number(pointer)
|
2797
3523
|
];
|
2798
3524
|
},
|
2799
|
-
type: TYPES.
|
3525
|
+
type: TYPES.array,
|
3526
|
+
length: 1
|
2800
3527
|
},
|
2801
3528
|
|
2802
3529
|
__Array_of: {
|
@@ -2807,27 +3534,134 @@ const internalConstrs = {
|
|
2807
3534
|
elements: decl.arguments
|
2808
3535
|
}, global, name);
|
2809
3536
|
},
|
2810
|
-
type: TYPES.
|
3537
|
+
type: TYPES.array,
|
3538
|
+
notConstr: true,
|
3539
|
+
length: 0
|
3540
|
+
},
|
3541
|
+
|
3542
|
+
__Porffor_fastOr: {
|
3543
|
+
generate: (scope, decl) => {
|
3544
|
+
const out = [];
|
3545
|
+
|
3546
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3547
|
+
out.push(
|
3548
|
+
...generate(scope, decl.arguments[i]),
|
3549
|
+
Opcodes.i32_to_u,
|
3550
|
+
...(i > 0 ? [ [ Opcodes.i32_or ] ] : [])
|
3551
|
+
);
|
3552
|
+
}
|
3553
|
+
|
3554
|
+
out.push(Opcodes.i32_from_u);
|
3555
|
+
|
3556
|
+
return out;
|
3557
|
+
},
|
3558
|
+
type: TYPES.boolean,
|
2811
3559
|
notConstr: true
|
2812
|
-
}
|
2813
|
-
};
|
3560
|
+
},
|
2814
3561
|
|
2815
|
-
|
2816
|
-
|
2817
|
-
|
2818
|
-
// for (const x of arr) {
|
2819
|
-
// if (x === undefined) {
|
2820
|
-
// console.trace(arr);
|
2821
|
-
// process.exit();
|
2822
|
-
// }
|
2823
|
-
// if (Array.isArray(x)) check(x);
|
2824
|
-
// }
|
2825
|
-
// };
|
2826
|
-
// if (Array.isArray(a) && !new Error().stack.includes('node:')) check(a);
|
2827
|
-
// // if (Array.isArray(a)) check(a);
|
3562
|
+
__Porffor_fastAnd: {
|
3563
|
+
generate: (scope, decl) => {
|
3564
|
+
const out = [];
|
2828
3565
|
|
2829
|
-
|
2830
|
-
|
3566
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3567
|
+
out.push(
|
3568
|
+
...generate(scope, decl.arguments[i]),
|
3569
|
+
Opcodes.i32_to_u,
|
3570
|
+
...(i > 0 ? [ [ Opcodes.i32_and ] ] : [])
|
3571
|
+
);
|
3572
|
+
}
|
3573
|
+
|
3574
|
+
out.push(Opcodes.i32_from_u);
|
3575
|
+
|
3576
|
+
return out;
|
3577
|
+
},
|
3578
|
+
type: TYPES.boolean,
|
3579
|
+
notConstr: true
|
3580
|
+
},
|
3581
|
+
|
3582
|
+
Boolean: {
|
3583
|
+
generate: (scope, decl) => {
|
3584
|
+
// todo: boolean object when used as constructor
|
3585
|
+
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3586
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3587
|
+
},
|
3588
|
+
type: TYPES.boolean,
|
3589
|
+
length: 1
|
3590
|
+
},
|
3591
|
+
|
3592
|
+
__Math_max: {
|
3593
|
+
generate: (scope, decl) => {
|
3594
|
+
const out = [
|
3595
|
+
...number(-Infinity)
|
3596
|
+
];
|
3597
|
+
|
3598
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3599
|
+
out.push(
|
3600
|
+
...generate(scope, decl.arguments[i]),
|
3601
|
+
[ Opcodes.f64_max ]
|
3602
|
+
);
|
3603
|
+
}
|
3604
|
+
|
3605
|
+
return out;
|
3606
|
+
},
|
3607
|
+
type: TYPES.number,
|
3608
|
+
notConstr: true,
|
3609
|
+
length: 2
|
3610
|
+
},
|
3611
|
+
|
3612
|
+
__Math_min: {
|
3613
|
+
generate: (scope, decl) => {
|
3614
|
+
const out = [
|
3615
|
+
...number(Infinity)
|
3616
|
+
];
|
3617
|
+
|
3618
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3619
|
+
out.push(
|
3620
|
+
...generate(scope, decl.arguments[i]),
|
3621
|
+
[ Opcodes.f64_min ]
|
3622
|
+
);
|
3623
|
+
}
|
3624
|
+
|
3625
|
+
return out;
|
3626
|
+
},
|
3627
|
+
type: TYPES.number,
|
3628
|
+
notConstr: true,
|
3629
|
+
length: 2
|
3630
|
+
},
|
3631
|
+
|
3632
|
+
__console_log: {
|
3633
|
+
generate: (scope, decl) => {
|
3634
|
+
const out = [];
|
3635
|
+
|
3636
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3637
|
+
out.push(
|
3638
|
+
...generateCall(scope, {
|
3639
|
+
callee: {
|
3640
|
+
type: 'Identifier',
|
3641
|
+
name: '__Porffor_print'
|
3642
|
+
},
|
3643
|
+
arguments: [ decl.arguments[i] ]
|
3644
|
+
}),
|
3645
|
+
|
3646
|
+
// print space
|
3647
|
+
...number(32),
|
3648
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3649
|
+
);
|
3650
|
+
}
|
3651
|
+
|
3652
|
+
// print newline
|
3653
|
+
out.push(
|
3654
|
+
...number(10),
|
3655
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3656
|
+
);
|
3657
|
+
|
3658
|
+
return out;
|
3659
|
+
},
|
3660
|
+
type: TYPES.undefined,
|
3661
|
+
notConstr: true,
|
3662
|
+
length: 0
|
3663
|
+
}
|
3664
|
+
};
|
2831
3665
|
|
2832
3666
|
export default program => {
|
2833
3667
|
globals = {};
|
@@ -2837,20 +3671,23 @@ export default program => {
|
|
2837
3671
|
funcs = [];
|
2838
3672
|
funcIndex = {};
|
2839
3673
|
depth = [];
|
2840
|
-
arrays = new Map();
|
2841
3674
|
pages = new Map();
|
2842
3675
|
data = [];
|
2843
3676
|
currentFuncIndex = importedFuncs.length;
|
2844
3677
|
|
2845
3678
|
globalThis.valtype = 'f64';
|
2846
3679
|
|
2847
|
-
const valtypeOpt = process.argv.find(x => x.startsWith('
|
3680
|
+
const valtypeOpt = process.argv.find(x => x.startsWith('--valtype='));
|
2848
3681
|
if (valtypeOpt) valtype = valtypeOpt.split('=')[1];
|
2849
3682
|
|
2850
3683
|
globalThis.valtypeBinary = Valtype[valtype];
|
2851
3684
|
|
2852
3685
|
const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
|
2853
3686
|
|
3687
|
+
globalThis.pageSize = PageSize;
|
3688
|
+
const pageSizeOpt = process.argv.find(x => x.startsWith('--page-size='));
|
3689
|
+
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3690
|
+
|
2854
3691
|
// set generic opcodes for current valtype
|
2855
3692
|
Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
|
2856
3693
|
Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
|
@@ -2859,10 +3696,10 @@ export default program => {
|
|
2859
3696
|
Opcodes.add = [ Opcodes.i32_add, Opcodes.i64_add, Opcodes.f64_add ][valtypeInd];
|
2860
3697
|
Opcodes.sub = [ Opcodes.i32_sub, Opcodes.i64_sub, Opcodes.f64_sub ][valtypeInd];
|
2861
3698
|
|
2862
|
-
Opcodes.i32_to = [ [
|
2863
|
-
Opcodes.i32_to_u = [ [
|
2864
|
-
Opcodes.i32_from = [ [
|
2865
|
-
Opcodes.i32_from_u = [ [
|
3699
|
+
Opcodes.i32_to = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_s ][valtypeInd];
|
3700
|
+
Opcodes.i32_to_u = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_u ][valtypeInd];
|
3701
|
+
Opcodes.i32_from = [ [], [ Opcodes.i64_extend_i32_s ], [ Opcodes.f64_convert_i32_s ] ][valtypeInd];
|
3702
|
+
Opcodes.i32_from_u = [ [], [ Opcodes.i64_extend_i32_u ], [ Opcodes.f64_convert_i32_u ] ][valtypeInd];
|
2866
3703
|
|
2867
3704
|
Opcodes.load = [ Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load ][valtypeInd];
|
2868
3705
|
Opcodes.store = [ Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store ][valtypeInd];
|
@@ -2875,10 +3712,6 @@ export default program => {
|
|
2875
3712
|
|
2876
3713
|
program.id = { name: 'main' };
|
2877
3714
|
|
2878
|
-
globalThis.pageSize = PageSize;
|
2879
|
-
const pageSizeOpt = process.argv.find(x => x.startsWith('-page-size='));
|
2880
|
-
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
2881
|
-
|
2882
3715
|
const scope = {
|
2883
3716
|
locals: {},
|
2884
3717
|
localInd: 0
|
@@ -2889,7 +3722,7 @@ export default program => {
|
|
2889
3722
|
body: program.body
|
2890
3723
|
};
|
2891
3724
|
|
2892
|
-
if (
|
3725
|
+
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
2893
3726
|
|
2894
3727
|
generateFunc(scope, program);
|
2895
3728
|
|
@@ -2906,7 +3739,11 @@ export default program => {
|
|
2906
3739
|
}
|
2907
3740
|
|
2908
3741
|
if (lastInst[0] === Opcodes.end || lastInst[0] === Opcodes.local_set || lastInst[0] === Opcodes.global_set) {
|
2909
|
-
|
3742
|
+
if (lastInst[0] === Opcodes.local_set && lastInst[1] === main.locals['#last_type'].idx) {
|
3743
|
+
main.wasm.splice(main.wasm.length - 1, 1);
|
3744
|
+
} else {
|
3745
|
+
main.returns = [];
|
3746
|
+
}
|
2910
3747
|
}
|
2911
3748
|
|
2912
3749
|
if (lastInst[0] === Opcodes.call) {
|