porffor 0.2.0-2265539 → 0.2.0-22b3092
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/.vscode/launch.json +18 -0
- package/LICENSE +20 -20
- package/README.md +61 -43
- package/asur/README.md +2 -0
- package/asur/index.js +1262 -0
- package/byg/index.js +237 -0
- package/compiler/2c.js +1 -1
- package/compiler/{sections.js → assemble.js} +57 -10
- package/compiler/builtins/annexb_string.js +72 -0
- package/compiler/builtins/annexb_string.ts +19 -0
- package/compiler/builtins/array.ts +145 -0
- package/compiler/builtins/base64.ts +49 -39
- package/compiler/builtins/crypto.ts +120 -0
- package/compiler/builtins/escape.ts +141 -0
- package/compiler/builtins/int.ts +147 -0
- package/compiler/builtins/number.ts +527 -0
- package/compiler/builtins/porffor.d.ts +26 -19
- package/compiler/builtins/string.ts +1055 -0
- package/compiler/builtins/tostring.ts +45 -0
- package/compiler/builtins.js +412 -105
- package/compiler/{codeGen.js → codegen.js} +674 -287
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +108 -10
- package/compiler/generated_builtins.js +684 -4
- package/compiler/index.js +16 -14
- package/compiler/log.js +6 -3
- package/compiler/opt.js +23 -22
- package/compiler/parse.js +31 -25
- package/compiler/precompile.js +19 -25
- package/compiler/prefs.js +2 -2
- package/compiler/prototype.js +2 -18
- package/compiler/types.js +37 -0
- package/compiler/wasmSpec.js +27 -8
- package/compiler/wrap.js +46 -45
- package/package.json +9 -5
- package/porf +2 -0
- package/rhemyn/compile.js +3 -2
- package/rhemyn/parse.js +323 -320
- package/rhemyn/test/parse.js +58 -58
- package/runner/compare.js +34 -34
- package/runner/debug.js +122 -0
- package/runner/index.js +31 -9
- package/runner/profiler.js +102 -0
- package/runner/repl.js +40 -7
- package/runner/sizes.js +37 -37
- package/test262_changes_from_1afe9b87d2_to_04-09.md +270 -0
- package/demo.js +0 -3
- package/demo.ts +0 -1
- package/filesize.cmd +0 -2
- package/hello +0 -0
- 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 -152
- package/util/enum.js +0 -20
@@ -8,6 +8,7 @@ import { log } from "./log.js";
|
|
8
8
|
import parse from "./parse.js";
|
9
9
|
import * as Rhemyn from "../rhemyn/compile.js";
|
10
10
|
import Prefs from './prefs.js';
|
11
|
+
import { TYPES, TYPE_NAMES } from './types.js';
|
11
12
|
|
12
13
|
let globals = {};
|
13
14
|
let globalInd = 0;
|
@@ -24,35 +25,37 @@ const debug = str => {
|
|
24
25
|
const logChar = n => {
|
25
26
|
code.push(...number(n));
|
26
27
|
|
27
|
-
code.push(Opcodes.call);
|
28
|
-
code.push(...unsignedLEB128(0));
|
28
|
+
code.push([ Opcodes.call, 0 ]);
|
29
29
|
};
|
30
30
|
|
31
31
|
for (let i = 0; i < str.length; i++) {
|
32
32
|
logChar(str.charCodeAt(i));
|
33
33
|
}
|
34
34
|
|
35
|
-
logChar(
|
35
|
+
logChar(10); // new line
|
36
36
|
|
37
37
|
return code;
|
38
38
|
};
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
this.name = 'TodoError';
|
45
|
-
}
|
40
|
+
class TodoError extends Error {
|
41
|
+
constructor(message) {
|
42
|
+
super(message);
|
43
|
+
this.name = 'TodoError';
|
46
44
|
}
|
45
|
+
}
|
46
|
+
const todo = (scope, msg, expectsValue = undefined) => {
|
47
|
+
switch (Prefs.todoTime ?? 'runtime') {
|
48
|
+
case 'compile':
|
49
|
+
throw new TodoError(msg);
|
47
50
|
|
48
|
-
|
49
|
-
|
50
|
-
const code = [];
|
51
|
-
|
52
|
-
code.push(...debug(`todo! ` + msg));
|
53
|
-
code.push(Opcodes.unreachable);
|
51
|
+
case 'runtime':
|
52
|
+
return internalThrow(scope, 'TodoError', msg, expectsValue);
|
54
53
|
|
55
|
-
|
54
|
+
// return [
|
55
|
+
// ...debug(`todo! ${msg}`),
|
56
|
+
// [ Opcodes.unreachable ]
|
57
|
+
// ];
|
58
|
+
}
|
56
59
|
};
|
57
60
|
|
58
61
|
const isFuncType = type => type === 'FunctionDeclaration' || type === 'FunctionExpression' || type === 'ArrowFunctionExpression';
|
@@ -105,7 +108,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
105
108
|
return generateUnary(scope, decl);
|
106
109
|
|
107
110
|
case 'UpdateExpression':
|
108
|
-
return generateUpdate(scope, decl);
|
111
|
+
return generateUpdate(scope, decl, global, name, valueUnused);
|
109
112
|
|
110
113
|
case 'IfStatement':
|
111
114
|
return generateIf(scope, decl);
|
@@ -116,6 +119,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
116
119
|
case 'WhileStatement':
|
117
120
|
return generateWhile(scope, decl);
|
118
121
|
|
122
|
+
case 'DoWhileStatement':
|
123
|
+
return generateDoWhile(scope, decl);
|
124
|
+
|
119
125
|
case 'ForOfStatement':
|
120
126
|
return generateForOf(scope, decl);
|
121
127
|
|
@@ -125,6 +131,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
125
131
|
case 'ContinueStatement':
|
126
132
|
return generateContinue(scope, decl);
|
127
133
|
|
134
|
+
case 'LabeledStatement':
|
135
|
+
return generateLabel(scope, decl);
|
136
|
+
|
128
137
|
case 'EmptyStatement':
|
129
138
|
return generateEmpty(scope, decl);
|
130
139
|
|
@@ -138,7 +147,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
138
147
|
return generateTry(scope, decl);
|
139
148
|
|
140
149
|
case 'DebuggerStatement':
|
141
|
-
// todo:
|
150
|
+
// todo: hook into terminal debugger
|
142
151
|
return [];
|
143
152
|
|
144
153
|
case 'ArrayExpression':
|
@@ -152,10 +161,16 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
152
161
|
const funcsBefore = funcs.length;
|
153
162
|
generate(scope, decl.declaration);
|
154
163
|
|
155
|
-
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');
|
156
171
|
|
157
|
-
const newFunc = funcs[funcs.length - 1];
|
158
|
-
newFunc.export = true;
|
172
|
+
// const newFunc = funcs[funcs.length - 1];
|
173
|
+
// newFunc.export = true;
|
159
174
|
|
160
175
|
return [];
|
161
176
|
|
@@ -202,35 +217,42 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
202
217
|
},
|
203
218
|
|
204
219
|
__Porffor_bs: str => [
|
205
|
-
...makeString(scope, str,
|
220
|
+
...makeString(scope, str, global, name, true),
|
206
221
|
|
207
|
-
...
|
208
|
-
|
222
|
+
...(name ? setType(scope, name, TYPES._bytestring) : [
|
223
|
+
...number(TYPES._bytestring, Valtype.i32),
|
224
|
+
...setLastType(scope)
|
225
|
+
])
|
209
226
|
],
|
210
227
|
__Porffor_s: str => [
|
211
|
-
...makeString(scope, str,
|
228
|
+
...makeString(scope, str, global, name, false),
|
212
229
|
|
213
|
-
...
|
214
|
-
|
230
|
+
...(name ? setType(scope, name, TYPES.string) : [
|
231
|
+
...number(TYPES.string, Valtype.i32),
|
232
|
+
...setLastType(scope)
|
233
|
+
])
|
215
234
|
],
|
216
235
|
};
|
217
236
|
|
218
|
-
const
|
237
|
+
const func = decl.tag.name;
|
219
238
|
// hack for inline asm
|
220
|
-
if (!funcs[
|
239
|
+
if (!funcs[func]) return todo(scope, 'tagged template expressions not implemented', true);
|
221
240
|
|
222
241
|
const { quasis, expressions } = decl.quasi;
|
223
242
|
let str = quasis[0].value.raw;
|
224
243
|
|
225
244
|
for (let i = 0; i < expressions.length; i++) {
|
226
245
|
const e = expressions[i];
|
227
|
-
|
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
|
+
|
228
252
|
str += quasis[i + 1].value.raw;
|
229
253
|
}
|
230
254
|
|
231
|
-
|
232
|
-
|
233
|
-
return funcs[name](str);
|
255
|
+
return funcs[func](str);
|
234
256
|
}
|
235
257
|
|
236
258
|
default:
|
@@ -240,7 +262,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
240
262
|
return [];
|
241
263
|
}
|
242
264
|
|
243
|
-
return todo(`no generation for ${decl.type}!`);
|
265
|
+
return todo(scope, `no generation for ${decl.type}!`);
|
244
266
|
}
|
245
267
|
};
|
246
268
|
|
@@ -268,7 +290,7 @@ const lookupName = (scope, _name) => {
|
|
268
290
|
return [ undefined, undefined ];
|
269
291
|
};
|
270
292
|
|
271
|
-
const internalThrow = (scope, constructor, message, expectsValue =
|
293
|
+
const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysValueInternalThrows) => [
|
272
294
|
...generateThrow(scope, {
|
273
295
|
argument: {
|
274
296
|
type: 'NewExpression',
|
@@ -292,7 +314,10 @@ const generateIdent = (scope, decl) => {
|
|
292
314
|
|
293
315
|
if (Object.hasOwn(builtinVars, name)) {
|
294
316
|
if (builtinVars[name].floatOnly && valtype[0] === 'i') throw new Error(`Cannot use ${unhackName(name)} with integer valtype`);
|
295
|
-
|
317
|
+
|
318
|
+
let wasm = builtinVars[name];
|
319
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name });
|
320
|
+
return wasm.slice();
|
296
321
|
}
|
297
322
|
|
298
323
|
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
@@ -300,6 +325,11 @@ const generateIdent = (scope, decl) => {
|
|
300
325
|
return number(1);
|
301
326
|
}
|
302
327
|
|
328
|
+
if (isExistingProtoFunc(name)) {
|
329
|
+
// todo: return an actual something
|
330
|
+
return number(1);
|
331
|
+
}
|
332
|
+
|
303
333
|
if (local?.idx === undefined) {
|
304
334
|
// no local var with name
|
305
335
|
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
@@ -330,14 +360,18 @@ const generateReturn = (scope, decl) => {
|
|
330
360
|
// just bare "return"
|
331
361
|
return [
|
332
362
|
...number(UNDEFINED), // "undefined" if func returns
|
333
|
-
...
|
363
|
+
...(scope.returnType != null ? [] : [
|
364
|
+
...number(TYPES.undefined, Valtype.i32) // type undefined
|
365
|
+
]),
|
334
366
|
[ Opcodes.return ]
|
335
367
|
];
|
336
368
|
}
|
337
369
|
|
338
370
|
return [
|
339
371
|
...generate(scope, decl.argument),
|
340
|
-
...
|
372
|
+
...(scope.returnType != null ? [] : [
|
373
|
+
...getNodeType(scope, decl.argument)
|
374
|
+
]),
|
341
375
|
[ Opcodes.return ]
|
342
376
|
];
|
343
377
|
};
|
@@ -351,7 +385,8 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
351
385
|
return idx;
|
352
386
|
};
|
353
387
|
|
354
|
-
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);
|
355
390
|
|
356
391
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
357
392
|
const checks = {
|
@@ -360,7 +395,7 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
360
395
|
'??': nullish
|
361
396
|
};
|
362
397
|
|
363
|
-
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);
|
364
399
|
|
365
400
|
// generic structure for {a} OP {b}
|
366
401
|
// -->
|
@@ -368,8 +403,8 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
368
403
|
|
369
404
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
370
405
|
// (like if we are in an if condition - very common)
|
371
|
-
const leftIsInt =
|
372
|
-
const rightIsInt =
|
406
|
+
const leftIsInt = isFloatToIntOp(left[left.length - 1]);
|
407
|
+
const rightIsInt = isFloatToIntOp(right[right.length - 1]);
|
373
408
|
|
374
409
|
const canInt = leftIsInt && rightIsInt;
|
375
410
|
|
@@ -386,12 +421,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
386
421
|
...right,
|
387
422
|
// note type
|
388
423
|
...rightType,
|
389
|
-
setLastType(scope),
|
424
|
+
...setLastType(scope),
|
390
425
|
[ Opcodes.else ],
|
391
426
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
392
427
|
// note type
|
393
428
|
...leftType,
|
394
|
-
setLastType(scope),
|
429
|
+
...setLastType(scope),
|
395
430
|
[ Opcodes.end ],
|
396
431
|
Opcodes.i32_from
|
397
432
|
];
|
@@ -405,17 +440,17 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
405
440
|
...right,
|
406
441
|
// note type
|
407
442
|
...rightType,
|
408
|
-
setLastType(scope),
|
443
|
+
...setLastType(scope),
|
409
444
|
[ Opcodes.else ],
|
410
445
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
411
446
|
// note type
|
412
447
|
...leftType,
|
413
|
-
setLastType(scope),
|
448
|
+
...setLastType(scope),
|
414
449
|
[ Opcodes.end ]
|
415
450
|
];
|
416
451
|
};
|
417
452
|
|
418
|
-
const concatStrings = (scope, left, right, global, name, assign) => {
|
453
|
+
const concatStrings = (scope, left, right, global, name, assign = false, bytestrings = false) => {
|
419
454
|
// todo: this should be rewritten into a built-in/func: String.prototype.concat
|
420
455
|
// todo: convert left and right to strings if not
|
421
456
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -426,7 +461,7 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
426
461
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
427
462
|
|
428
463
|
if (assign) {
|
429
|
-
const pointer = arrays
|
464
|
+
const pointer = scope.arrays?.get(name ?? '$undeclared');
|
430
465
|
|
431
466
|
return [
|
432
467
|
// setup right
|
@@ -451,11 +486,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
451
486
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
|
452
487
|
|
453
488
|
// copy right
|
454
|
-
// dst = out pointer + length size + current length *
|
489
|
+
// dst = out pointer + length size + current length * sizeof valtype
|
455
490
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
456
491
|
|
457
492
|
[ Opcodes.local_get, leftLength ],
|
458
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
493
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
459
494
|
[ Opcodes.i32_mul ],
|
460
495
|
[ Opcodes.i32_add ],
|
461
496
|
|
@@ -464,9 +499,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
464
499
|
...number(ValtypeSize.i32, Valtype.i32),
|
465
500
|
[ Opcodes.i32_add ],
|
466
501
|
|
467
|
-
// size = right length *
|
502
|
+
// size = right length * sizeof valtype
|
468
503
|
[ Opcodes.local_get, rightLength ],
|
469
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
504
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
470
505
|
[ Opcodes.i32_mul ],
|
471
506
|
|
472
507
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -524,11 +559,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
524
559
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
525
560
|
|
526
561
|
// copy right
|
527
|
-
// dst = out pointer + length size + left length *
|
562
|
+
// dst = out pointer + length size + left length * sizeof valtype
|
528
563
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
529
564
|
|
530
565
|
[ Opcodes.local_get, leftLength ],
|
531
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
566
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
532
567
|
[ Opcodes.i32_mul ],
|
533
568
|
[ Opcodes.i32_add ],
|
534
569
|
|
@@ -537,9 +572,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
537
572
|
...number(ValtypeSize.i32, Valtype.i32),
|
538
573
|
[ Opcodes.i32_add ],
|
539
574
|
|
540
|
-
// size = right length *
|
575
|
+
// size = right length * sizeof valtype
|
541
576
|
[ Opcodes.local_get, rightLength ],
|
542
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
577
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
543
578
|
[ Opcodes.i32_mul ],
|
544
579
|
|
545
580
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -549,7 +584,7 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
549
584
|
];
|
550
585
|
};
|
551
586
|
|
552
|
-
const compareStrings = (scope, left, right) => {
|
587
|
+
const compareStrings = (scope, left, right, bytestrings = false) => {
|
553
588
|
// todo: this should be rewritten into a func
|
554
589
|
// todo: convert left and right to strings if not
|
555
590
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -558,7 +593,6 @@ const compareStrings = (scope, left, right) => {
|
|
558
593
|
const leftPointer = localTmp(scope, 'compare_left_pointer', Valtype.i32);
|
559
594
|
const leftLength = localTmp(scope, 'compare_left_length', Valtype.i32);
|
560
595
|
const rightPointer = localTmp(scope, 'compare_right_pointer', Valtype.i32);
|
561
|
-
const rightLength = localTmp(scope, 'compare_right_length', Valtype.i32);
|
562
596
|
|
563
597
|
const index = localTmp(scope, 'compare_index', Valtype.i32);
|
564
598
|
const indexEnd = localTmp(scope, 'compare_index_end', Valtype.i32);
|
@@ -586,7 +620,6 @@ const compareStrings = (scope, left, right) => {
|
|
586
620
|
|
587
621
|
[ Opcodes.local_get, rightPointer ],
|
588
622
|
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
589
|
-
[ Opcodes.local_tee, rightLength ],
|
590
623
|
|
591
624
|
// fast path: check leftLength != rightLength
|
592
625
|
[ Opcodes.i32_ne ],
|
@@ -601,11 +634,13 @@ const compareStrings = (scope, left, right) => {
|
|
601
634
|
...number(0, Valtype.i32),
|
602
635
|
[ Opcodes.local_set, index ],
|
603
636
|
|
604
|
-
// setup index end as length * sizeof
|
637
|
+
// setup index end as length * sizeof valtype (1 for bytestring, 2 for string)
|
605
638
|
// we do this instead of having to do mul/div each iter for perf™
|
606
639
|
[ Opcodes.local_get, leftLength ],
|
607
|
-
...
|
608
|
-
|
640
|
+
...(bytestrings ? [] : [
|
641
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
642
|
+
[ Opcodes.i32_mul ],
|
643
|
+
]),
|
609
644
|
[ Opcodes.local_set, indexEnd ],
|
610
645
|
|
611
646
|
// iterate over each char and check if eq
|
@@ -615,13 +650,17 @@ const compareStrings = (scope, left, right) => {
|
|
615
650
|
[ Opcodes.local_get, index ],
|
616
651
|
[ Opcodes.local_get, leftPointer ],
|
617
652
|
[ Opcodes.i32_add ],
|
618
|
-
|
653
|
+
bytestrings ?
|
654
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
655
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
619
656
|
|
620
657
|
// fetch right
|
621
658
|
[ Opcodes.local_get, index ],
|
622
659
|
[ Opcodes.local_get, rightPointer ],
|
623
660
|
[ Opcodes.i32_add ],
|
624
|
-
|
661
|
+
bytestrings ?
|
662
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
663
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
625
664
|
|
626
665
|
// not equal, "return" false
|
627
666
|
[ Opcodes.i32_ne ],
|
@@ -630,13 +669,13 @@ const compareStrings = (scope, left, right) => {
|
|
630
669
|
[ Opcodes.br, 2 ],
|
631
670
|
[ Opcodes.end ],
|
632
671
|
|
633
|
-
// index += sizeof
|
672
|
+
// index += sizeof valtype (1 for bytestring, 2 for string)
|
634
673
|
[ Opcodes.local_get, index ],
|
635
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
674
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
636
675
|
[ Opcodes.i32_add ],
|
637
676
|
[ Opcodes.local_tee, index ],
|
638
677
|
|
639
|
-
// if index != index end (length * sizeof
|
678
|
+
// if index != index end (length * sizeof valtype), loop
|
640
679
|
[ Opcodes.local_get, indexEnd ],
|
641
680
|
[ Opcodes.i32_ne ],
|
642
681
|
[ Opcodes.br_if, 0 ],
|
@@ -657,13 +696,14 @@ const compareStrings = (scope, left, right) => {
|
|
657
696
|
};
|
658
697
|
|
659
698
|
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
660
|
-
if (
|
699
|
+
if (isFloatToIntOp(wasm[wasm.length - 1])) return [
|
661
700
|
...wasm,
|
662
701
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
663
702
|
];
|
703
|
+
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
664
704
|
|
665
705
|
const useTmp = knownType(scope, type) == null;
|
666
|
-
const tmp =
|
706
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
667
707
|
|
668
708
|
const def = [
|
669
709
|
// if value != 0
|
@@ -715,7 +755,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
715
755
|
|
716
756
|
const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
717
757
|
const useTmp = knownType(scope, type) == null;
|
718
|
-
const tmp =
|
758
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
719
759
|
|
720
760
|
return [
|
721
761
|
...wasm,
|
@@ -761,7 +801,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
761
801
|
|
762
802
|
const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
763
803
|
const useTmp = knownType(scope, type) == null;
|
764
|
-
const tmp =
|
804
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
765
805
|
|
766
806
|
return [
|
767
807
|
...wasm,
|
@@ -855,11 +895,11 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
855
895
|
// todo: if equality op and an operand is undefined, return false
|
856
896
|
// todo: niche null hell with 0
|
857
897
|
|
858
|
-
// todo: this should be dynamic but for now only static
|
859
898
|
if (knownLeft === TYPES.string || knownRight === TYPES.string) {
|
860
899
|
if (op === '+') {
|
900
|
+
// todo: this should be dynamic too but for now only static
|
861
901
|
// string concat (a + b)
|
862
|
-
return concatStrings(scope, left, right, _global, _name, assign);
|
902
|
+
return concatStrings(scope, left, right, _global, _name, assign, false);
|
863
903
|
}
|
864
904
|
|
865
905
|
// not an equality op, NaN
|
@@ -882,6 +922,33 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
882
922
|
}
|
883
923
|
}
|
884
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
|
+
}
|
951
|
+
|
885
952
|
let ops = operatorOpcode[valtype][op];
|
886
953
|
|
887
954
|
// some complex ops are implemented as builtin funcs
|
@@ -897,23 +964,62 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
897
964
|
]);
|
898
965
|
}
|
899
966
|
|
900
|
-
if (!ops) return todo(`operator ${op} not implemented yet
|
967
|
+
if (!ops) return todo(scope, `operator ${op} not implemented yet`, true);
|
901
968
|
|
902
969
|
if (!Array.isArray(ops)) ops = [ ops ];
|
903
970
|
ops = [ ops ];
|
904
971
|
|
905
972
|
let tmpLeft, tmpRight;
|
906
973
|
// if equal op, check if strings for compareStrings
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
|
911
|
-
return;
|
912
|
-
}
|
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
|
913
977
|
|
978
|
+
if ((op === '===' || op === '==' || op === '!==' || op === '!=') && (knownLeft == null && knownRight == null)) {
|
914
979
|
tmpLeft = localTmp(scope, '__tmpop_left');
|
915
980
|
tmpRight = localTmp(scope, '__tmpop_right');
|
916
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)
|
917
1023
|
ops.unshift(...stringOnly([
|
918
1024
|
// if left is string
|
919
1025
|
...leftType,
|
@@ -925,30 +1031,28 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
925
1031
|
...number(TYPES.string, Valtype.i32),
|
926
1032
|
[ Opcodes.i32_eq ],
|
927
1033
|
|
928
|
-
// if
|
929
|
-
[ Opcodes.
|
1034
|
+
// if both are true
|
1035
|
+
[ Opcodes.i32_and ],
|
930
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 ],
|
931
1041
|
|
932
|
-
//
|
933
|
-
// if left is not string
|
1042
|
+
// if left is bytestring
|
934
1043
|
...leftType,
|
935
|
-
...number(TYPES.
|
936
|
-
[ Opcodes.
|
1044
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1045
|
+
[ Opcodes.i32_eq ],
|
937
1046
|
|
938
|
-
// if right is
|
1047
|
+
// if right is bytestring
|
939
1048
|
...rightType,
|
940
|
-
...number(TYPES.
|
941
|
-
[ Opcodes.
|
1049
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1050
|
+
[ Opcodes.i32_eq ],
|
942
1051
|
|
943
|
-
// if
|
944
|
-
[ Opcodes.
|
1052
|
+
// if both are true
|
1053
|
+
[ Opcodes.i32_and ],
|
945
1054
|
[ Opcodes.if, Blocktype.void ],
|
946
|
-
...
|
947
|
-
[ Opcodes.br, 2 ],
|
948
|
-
[ Opcodes.end ],
|
949
|
-
|
950
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
951
|
-
// ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1055
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], true),
|
952
1056
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
953
1057
|
[ Opcodes.br, 1 ],
|
954
1058
|
[ Opcodes.end ],
|
@@ -960,7 +1064,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
960
1064
|
// endOut.push(stringOnly([ Opcodes.end ]));
|
961
1065
|
endOut.unshift(stringOnly([ Opcodes.end ]));
|
962
1066
|
// }
|
963
|
-
}
|
1067
|
+
}
|
964
1068
|
|
965
1069
|
return finalize([
|
966
1070
|
...left,
|
@@ -979,6 +1083,21 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
979
1083
|
return out;
|
980
1084
|
};
|
981
1085
|
|
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
|
+
|
982
1101
|
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
983
1102
|
const existing = funcs.find(x => x.name === name);
|
984
1103
|
if (existing) return existing;
|
@@ -997,28 +1116,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
997
1116
|
data.push(copy);
|
998
1117
|
}
|
999
1118
|
|
1000
|
-
if (typeof wasm === 'function') {
|
1001
|
-
const scope = {
|
1002
|
-
name,
|
1003
|
-
params,
|
1004
|
-
locals,
|
1005
|
-
returns,
|
1006
|
-
localInd: allLocals.length,
|
1007
|
-
};
|
1008
|
-
|
1009
|
-
wasm = wasm(scope, {
|
1010
|
-
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage,
|
1011
|
-
builtin: name => {
|
1012
|
-
let idx = funcIndex[name] ?? importedFuncs[name];
|
1013
|
-
if (idx === undefined && builtinFuncs[name]) {
|
1014
|
-
includeBuiltin(scope, name);
|
1015
|
-
idx = funcIndex[name];
|
1016
|
-
}
|
1017
|
-
|
1018
|
-
return idx;
|
1019
|
-
}
|
1020
|
-
});
|
1021
|
-
}
|
1119
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name, params, locals, returns, localInd: allLocals.length });
|
1022
1120
|
|
1023
1121
|
let baseGlobalIdx, i = 0;
|
1024
1122
|
for (const type of globalTypes) {
|
@@ -1042,7 +1140,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1042
1140
|
params,
|
1043
1141
|
locals,
|
1044
1142
|
returns,
|
1045
|
-
returnType:
|
1143
|
+
returnType: returnType ?? TYPES.number,
|
1046
1144
|
wasm,
|
1047
1145
|
internal: true,
|
1048
1146
|
index: currentFuncIndex++
|
@@ -1065,6 +1163,7 @@ const generateLogicExp = (scope, decl) => {
|
|
1065
1163
|
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
1066
1164
|
};
|
1067
1165
|
|
1166
|
+
// potential future ideas for nan boxing (unused):
|
1068
1167
|
// T = JS type, V = value/pointer
|
1069
1168
|
// 0bTTT
|
1070
1169
|
// qNAN: 0 11111111111 1000000000000000000000000000000000000000000000000001
|
@@ -1088,40 +1187,18 @@ const generateLogicExp = (scope, decl) => {
|
|
1088
1187
|
// 4: internal type
|
1089
1188
|
// 5: pointer
|
1090
1189
|
|
1091
|
-
const
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
object: 0x04,
|
1097
|
-
function: 0x05,
|
1098
|
-
symbol: 0x06,
|
1099
|
-
bigint: 0x07,
|
1100
|
-
|
1101
|
-
// these are not "typeof" types but tracked internally
|
1102
|
-
_array: 0x10,
|
1103
|
-
_regexp: 0x11,
|
1104
|
-
_bytestring: 0x12
|
1105
|
-
};
|
1106
|
-
|
1107
|
-
const TYPE_NAMES = {
|
1108
|
-
[TYPES.number]: 'Number',
|
1109
|
-
[TYPES.boolean]: 'Boolean',
|
1110
|
-
[TYPES.string]: 'String',
|
1111
|
-
[TYPES.undefined]: 'undefined',
|
1112
|
-
[TYPES.object]: 'Object',
|
1113
|
-
[TYPES.function]: 'Function',
|
1114
|
-
[TYPES.symbol]: 'Symbol',
|
1115
|
-
[TYPES.bigint]: 'BigInt',
|
1116
|
-
|
1117
|
-
[TYPES._array]: 'Array',
|
1118
|
-
[TYPES._regexp]: 'RegExp',
|
1119
|
-
[TYPES._bytestring]: 'ByteString'
|
1190
|
+
const isExistingProtoFunc = name => {
|
1191
|
+
if (name.startsWith('__Array_prototype')) return !!prototypeFuncs[TYPES._array][name.slice(18)];
|
1192
|
+
if (name.startsWith('__String_prototype_')) return !!prototypeFuncs[TYPES.string][name.slice(19)];
|
1193
|
+
|
1194
|
+
return false;
|
1120
1195
|
};
|
1121
1196
|
|
1122
1197
|
const getType = (scope, _name) => {
|
1123
1198
|
const name = mapName(_name);
|
1124
1199
|
|
1200
|
+
// if (scope.locals[name] && !scope.locals[name + '#type']) console.log(name);
|
1201
|
+
|
1125
1202
|
if (typedInput && scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
|
1126
1203
|
if (scope.locals[name]) return [ [ Opcodes.local_get, scope.locals[name + '#type'].idx ] ];
|
1127
1204
|
|
@@ -1129,11 +1206,10 @@ const getType = (scope, _name) => {
|
|
1129
1206
|
if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
|
1130
1207
|
|
1131
1208
|
let type = TYPES.undefined;
|
1132
|
-
if (builtinVars[name]) type =
|
1209
|
+
if (builtinVars[name]) type = builtinVars[name].type ?? TYPES.number;
|
1133
1210
|
if (builtinFuncs[name] !== undefined || importedFuncs[name] !== undefined || funcIndex[name] !== undefined || internalConstrs[name] !== undefined) type = TYPES.function;
|
1134
1211
|
|
1135
|
-
if (name
|
1136
|
-
name.startsWith('__String_prototype_') && prototypeFuncs[TYPES.string][name.slice(19)]) type = TYPES.function;
|
1212
|
+
if (isExistingProtoFunc(name)) type = TYPES.function;
|
1137
1213
|
|
1138
1214
|
return number(type, Valtype.i32);
|
1139
1215
|
};
|
@@ -1156,15 +1232,16 @@ const setType = (scope, _name, type) => {
|
|
1156
1232
|
];
|
1157
1233
|
|
1158
1234
|
// throw new Error('could not find var');
|
1235
|
+
return [];
|
1159
1236
|
};
|
1160
1237
|
|
1161
1238
|
const getLastType = scope => {
|
1162
1239
|
scope.gotLastType = true;
|
1163
|
-
return [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ];
|
1240
|
+
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1164
1241
|
};
|
1165
1242
|
|
1166
1243
|
const setLastType = scope => {
|
1167
|
-
return [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ];
|
1244
|
+
return [ [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1168
1245
|
};
|
1169
1246
|
|
1170
1247
|
const getNodeType = (scope, node) => {
|
@@ -1189,7 +1266,7 @@ const getNodeType = (scope, node) => {
|
|
1189
1266
|
const name = node.callee.name;
|
1190
1267
|
if (!name) {
|
1191
1268
|
// iife
|
1192
|
-
if (scope.locals['#last_type']) return
|
1269
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1193
1270
|
|
1194
1271
|
// presume
|
1195
1272
|
// todo: warn here?
|
@@ -1203,7 +1280,7 @@ const getNodeType = (scope, node) => {
|
|
1203
1280
|
if (func.returnType) return func.returnType;
|
1204
1281
|
}
|
1205
1282
|
|
1206
|
-
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return
|
1283
|
+
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
|
1207
1284
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1208
1285
|
|
1209
1286
|
// check if this is a prototype function
|
@@ -1223,7 +1300,7 @@ const getNodeType = (scope, node) => {
|
|
1223
1300
|
return TYPES.number;
|
1224
1301
|
}
|
1225
1302
|
|
1226
|
-
if (scope.locals['#last_type']) return
|
1303
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1227
1304
|
|
1228
1305
|
// presume
|
1229
1306
|
// todo: warn here?
|
@@ -1278,6 +1355,7 @@ const getNodeType = (scope, node) => {
|
|
1278
1355
|
|
1279
1356
|
// todo: this should be dynamic but for now only static
|
1280
1357
|
if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
|
1358
|
+
if (knownLeft === TYPES._bytestring || knownRight === TYPES._bytestring) return TYPES._bytestring;
|
1281
1359
|
|
1282
1360
|
return TYPES.number;
|
1283
1361
|
|
@@ -1314,15 +1392,21 @@ const getNodeType = (scope, node) => {
|
|
1314
1392
|
|
1315
1393
|
// ts hack
|
1316
1394
|
if (scope.locals[node.object.name]?.metadata?.type === TYPES.string) return TYPES.string;
|
1395
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES._bytestring) return TYPES._bytestring;
|
1317
1396
|
if (scope.locals[node.object.name]?.metadata?.type === TYPES._array) return TYPES.number;
|
1318
1397
|
|
1319
|
-
if (scope.locals['#last_type']) return
|
1398
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1320
1399
|
|
1321
1400
|
// presume
|
1322
1401
|
return TYPES.number;
|
1323
1402
|
}
|
1324
1403
|
|
1325
|
-
if (
|
1404
|
+
if (node.type === 'TaggedTemplateExpression') {
|
1405
|
+
// hack
|
1406
|
+
if (node.tag.name.startsWith('__Porffor_')) return TYPES.number;
|
1407
|
+
}
|
1408
|
+
|
1409
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1326
1410
|
|
1327
1411
|
// presume
|
1328
1412
|
// todo: warn here?
|
@@ -1355,7 +1439,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1355
1439
|
return makeString(scope, decl.value, global, name);
|
1356
1440
|
|
1357
1441
|
default:
|
1358
|
-
return todo(`cannot generate literal of type ${typeof decl.value}
|
1442
|
+
return todo(scope, `cannot generate literal of type ${typeof decl.value}`, true);
|
1359
1443
|
}
|
1360
1444
|
};
|
1361
1445
|
|
@@ -1364,6 +1448,8 @@ const countLeftover = wasm => {
|
|
1364
1448
|
|
1365
1449
|
for (let i = 0; i < wasm.length; i++) {
|
1366
1450
|
const inst = wasm[i];
|
1451
|
+
if (inst[0] == null) continue;
|
1452
|
+
|
1367
1453
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
1368
1454
|
if (inst[0] === Opcodes.if) count--;
|
1369
1455
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -1372,7 +1458,7 @@ const countLeftover = wasm => {
|
|
1372
1458
|
if (inst[0] === Opcodes.end) depth--;
|
1373
1459
|
|
1374
1460
|
if (depth === 0)
|
1375
|
-
if ([Opcodes.throw,Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1461
|
+
if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1376
1462
|
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)) {}
|
1377
1463
|
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const].includes(inst[0])) count++;
|
1378
1464
|
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
@@ -1475,10 +1561,21 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1475
1561
|
name = func.name;
|
1476
1562
|
}
|
1477
1563
|
|
1478
|
-
if (name === 'eval' && decl.arguments[0]
|
1564
|
+
if (name === 'eval' && decl.arguments[0]?.type === 'Literal') {
|
1479
1565
|
// literal eval hack
|
1480
|
-
const code = decl.arguments[0]
|
1481
|
-
|
1566
|
+
const code = decl.arguments[0]?.value ?? '';
|
1567
|
+
|
1568
|
+
let parsed;
|
1569
|
+
try {
|
1570
|
+
parsed = parse(code, []);
|
1571
|
+
} catch (e) {
|
1572
|
+
if (e.name === 'SyntaxError') {
|
1573
|
+
// throw syntax errors of evals at runtime instead
|
1574
|
+
return internalThrow(scope, 'SyntaxError', e.message, true);
|
1575
|
+
}
|
1576
|
+
|
1577
|
+
throw e;
|
1578
|
+
}
|
1482
1579
|
|
1483
1580
|
const out = generate(scope, {
|
1484
1581
|
type: 'BlockStatement',
|
@@ -1492,13 +1589,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1492
1589
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1493
1590
|
out.push(
|
1494
1591
|
...getNodeType(scope, finalStatement),
|
1495
|
-
setLastType(scope)
|
1592
|
+
...setLastType(scope)
|
1496
1593
|
);
|
1497
1594
|
} else if (countLeftover(out) === 0) {
|
1498
1595
|
out.push(...number(UNDEFINED));
|
1499
1596
|
out.push(
|
1500
1597
|
...number(TYPES.undefined, Valtype.i32),
|
1501
|
-
setLastType(scope)
|
1598
|
+
...setLastType(scope)
|
1502
1599
|
);
|
1503
1600
|
}
|
1504
1601
|
|
@@ -1520,6 +1617,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1520
1617
|
|
1521
1618
|
target = { ...decl.callee };
|
1522
1619
|
target.name = spl.slice(0, -1).join('_');
|
1620
|
+
|
1621
|
+
// failed to lookup name, abort
|
1622
|
+
if (!lookupName(scope, target.name)[0]) protoName = null;
|
1523
1623
|
}
|
1524
1624
|
|
1525
1625
|
// literal.func()
|
@@ -1542,7 +1642,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1542
1642
|
Opcodes.i32_from_u,
|
1543
1643
|
|
1544
1644
|
...number(TYPES.boolean, Valtype.i32),
|
1545
|
-
setLastType(scope)
|
1645
|
+
...setLastType(scope)
|
1546
1646
|
];
|
1547
1647
|
}
|
1548
1648
|
|
@@ -1567,12 +1667,30 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1567
1667
|
// }
|
1568
1668
|
|
1569
1669
|
if (protoName) {
|
1670
|
+
const protoBC = {};
|
1671
|
+
|
1672
|
+
const builtinProtoCands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + protoName));
|
1673
|
+
|
1674
|
+
if (!decl._protoInternalCall && builtinProtoCands.length > 0) {
|
1675
|
+
for (const x of builtinProtoCands) {
|
1676
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
1677
|
+
if (type == null) continue;
|
1678
|
+
|
1679
|
+
protoBC[type] = generateCall(scope, {
|
1680
|
+
callee: {
|
1681
|
+
name: x
|
1682
|
+
},
|
1683
|
+
arguments: [ target, ...decl.arguments ],
|
1684
|
+
_protoInternalCall: true
|
1685
|
+
});
|
1686
|
+
}
|
1687
|
+
}
|
1688
|
+
|
1570
1689
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1571
1690
|
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1572
1691
|
return acc;
|
1573
1692
|
}, {});
|
1574
1693
|
|
1575
|
-
// no prototype function candidates, ignore
|
1576
1694
|
if (Object.keys(protoCands).length > 0) {
|
1577
1695
|
// use local for cached i32 length as commonly used
|
1578
1696
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
@@ -1590,7 +1708,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1590
1708
|
|
1591
1709
|
let allOptUnused = true;
|
1592
1710
|
let lengthI32CacheUsed = false;
|
1593
|
-
const protoBC = {};
|
1594
1711
|
for (const x in protoCands) {
|
1595
1712
|
const protoFunc = protoCands[x];
|
1596
1713
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
@@ -1598,7 +1715,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1598
1715
|
...RTArrayUtil.getLength(getPointer),
|
1599
1716
|
|
1600
1717
|
...number(TYPES.number, Valtype.i32),
|
1601
|
-
setLastType(scope)
|
1718
|
+
...setLastType(scope)
|
1602
1719
|
];
|
1603
1720
|
continue;
|
1604
1721
|
}
|
@@ -1635,7 +1752,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1635
1752
|
...protoOut,
|
1636
1753
|
|
1637
1754
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1638
|
-
setLastType(scope),
|
1755
|
+
...setLastType(scope),
|
1639
1756
|
[ Opcodes.end ]
|
1640
1757
|
];
|
1641
1758
|
}
|
@@ -1661,10 +1778,19 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1661
1778
|
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1662
1779
|
];
|
1663
1780
|
}
|
1781
|
+
|
1782
|
+
if (Object.keys(protoBC).length > 0) {
|
1783
|
+
return typeSwitch(scope, getNodeType(scope, target), {
|
1784
|
+
...protoBC,
|
1785
|
+
|
1786
|
+
// TODO: error better
|
1787
|
+
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1788
|
+
}, valtypeBinary);
|
1789
|
+
}
|
1664
1790
|
}
|
1665
1791
|
|
1666
1792
|
// TODO: only allows callee as literal
|
1667
|
-
if (!name) return todo(`only literal callees (got ${decl.callee.type})`);
|
1793
|
+
if (!name) return todo(scope, `only literal callees (got ${decl.callee.type})`);
|
1668
1794
|
|
1669
1795
|
let idx = funcIndex[name] ?? importedFuncs[name];
|
1670
1796
|
if (idx === undefined && builtinFuncs[name]) {
|
@@ -1700,21 +1826,28 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1700
1826
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1701
1827
|
const wasmOps = {
|
1702
1828
|
// pointer, align, offset
|
1703
|
-
|
1829
|
+
i32_load: { imms: 2, args: 1, returns: 1 },
|
1704
1830
|
// pointer, value, align, offset
|
1705
|
-
|
1831
|
+
i32_store: { imms: 2, args: 2, returns: 0 },
|
1706
1832
|
// pointer, align, offset
|
1707
|
-
|
1708
|
-
//
|
1709
|
-
|
1710
|
-
//
|
1711
|
-
|
1712
|
-
//
|
1713
|
-
|
1833
|
+
i32_load8_u: { imms: 2, args: 1, returns: 1 },
|
1834
|
+
// pointer, value, align, offset
|
1835
|
+
i32_store8: { imms: 2, args: 2, returns: 0 },
|
1836
|
+
// pointer, align, offset
|
1837
|
+
i32_load16_u: { imms: 2, args: 1, returns: 1 },
|
1838
|
+
// pointer, value, align, offset
|
1839
|
+
i32_store16: { imms: 2, args: 2, returns: 0 },
|
1840
|
+
|
1841
|
+
// pointer, align, offset
|
1842
|
+
f64_load: { imms: 2, args: 1, returns: 1 },
|
1843
|
+
// pointer, value, align, offset
|
1844
|
+
f64_store: { imms: 2, args: 2, returns: 0 },
|
1845
|
+
|
1846
|
+
// value
|
1847
|
+
i32_const: { imms: 1, args: 0, returns: 1 },
|
1848
|
+
|
1714
1849
|
// a, b
|
1715
|
-
|
1716
|
-
// val (todo: support >255)
|
1717
|
-
i64_const: { imms: 1, args: 0 },
|
1850
|
+
i32_or: { imms: 0, args: 2, returns: 1 },
|
1718
1851
|
};
|
1719
1852
|
|
1720
1853
|
const opName = name.slice('__Porffor_wasm_'.length);
|
@@ -1723,28 +1856,32 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1723
1856
|
const op = wasmOps[opName];
|
1724
1857
|
|
1725
1858
|
const argOut = [];
|
1726
|
-
for (let i = 0; i < op.args; i++) argOut.push(
|
1859
|
+
for (let i = 0; i < op.args; i++) argOut.push(
|
1860
|
+
...generate(scope, decl.arguments[i]),
|
1861
|
+
Opcodes.i32_to
|
1862
|
+
);
|
1727
1863
|
|
1728
1864
|
// literals only
|
1729
1865
|
const imms = decl.arguments.slice(op.args).map(x => x.value);
|
1730
1866
|
|
1731
1867
|
return [
|
1732
1868
|
...argOut,
|
1733
|
-
[ Opcodes[opName], ...imms ]
|
1869
|
+
[ Opcodes[opName], ...imms ],
|
1870
|
+
...(new Array(op.returns).fill(Opcodes.i32_from))
|
1734
1871
|
];
|
1735
1872
|
}
|
1736
1873
|
}
|
1737
1874
|
|
1738
1875
|
if (idx === undefined) {
|
1739
|
-
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function
|
1740
|
-
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined
|
1876
|
+
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1877
|
+
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1741
1878
|
}
|
1742
1879
|
|
1743
1880
|
const func = funcs.find(x => x.index === idx);
|
1744
1881
|
|
1745
1882
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1746
1883
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1747
|
-
const typedReturns = userFunc || builtinFuncs[name]?.typedReturns;
|
1884
|
+
const typedReturns = (func ? func.returnType == null : userFunc) || builtinFuncs[name]?.typedReturns;
|
1748
1885
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1749
1886
|
|
1750
1887
|
let args = decl.arguments;
|
@@ -1765,7 +1902,11 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1765
1902
|
const arg = args[i];
|
1766
1903
|
out = out.concat(generate(scope, arg));
|
1767
1904
|
|
1768
|
-
if (builtinFuncs[name] && builtinFuncs[name].params[i] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1905
|
+
if (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1906
|
+
out.push(Opcodes.i32_to);
|
1907
|
+
}
|
1908
|
+
|
1909
|
+
if (importedFuncs[name] && name.startsWith('profile')) {
|
1769
1910
|
out.push(Opcodes.i32_to);
|
1770
1911
|
}
|
1771
1912
|
|
@@ -1784,9 +1925,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1784
1925
|
// ...number(type, Valtype.i32),
|
1785
1926
|
// [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1786
1927
|
// );
|
1787
|
-
} else out.push(setLastType(scope));
|
1928
|
+
} else out.push(...setLastType(scope));
|
1788
1929
|
|
1789
|
-
if (builtinFuncs[name] && builtinFuncs[name].returns[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1930
|
+
if (builtinFuncs[name] && builtinFuncs[name].returns?.[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1790
1931
|
out.push(Opcodes.i32_from);
|
1791
1932
|
}
|
1792
1933
|
|
@@ -1797,7 +1938,7 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1797
1938
|
// hack: basically treat this as a normal call for builtins for now
|
1798
1939
|
const name = mapName(decl.callee.name);
|
1799
1940
|
if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1800
|
-
if (!builtinFuncs[name]) return todo(`new statement is not supported yet`); // return todo(`new statement is not supported yet (new ${unhackName(name)})`);
|
1941
|
+
if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
|
1801
1942
|
|
1802
1943
|
return generateCall(scope, decl, _global, _name);
|
1803
1944
|
};
|
@@ -1931,8 +2072,6 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1931
2072
|
[ Opcodes.block, returns ]
|
1932
2073
|
];
|
1933
2074
|
|
1934
|
-
// todo: use br_table?
|
1935
|
-
|
1936
2075
|
for (const x in bc) {
|
1937
2076
|
if (x === 'default') continue;
|
1938
2077
|
|
@@ -1988,12 +2127,14 @@ const addVarMetadata = (scope, name, global = false, metadata = {}) => {
|
|
1988
2127
|
};
|
1989
2128
|
|
1990
2129
|
const typeAnnoToPorfType = x => {
|
1991
|
-
if (
|
1992
|
-
if (TYPES[
|
2130
|
+
if (!x) return null;
|
2131
|
+
if (TYPES[x] != null) return TYPES[x];
|
2132
|
+
if (TYPES['_' + x] != null) return TYPES['_' + x];
|
1993
2133
|
|
1994
2134
|
switch (x) {
|
1995
2135
|
case 'i32':
|
1996
2136
|
case 'i64':
|
2137
|
+
case 'f64':
|
1997
2138
|
return TYPES.number;
|
1998
2139
|
}
|
1999
2140
|
|
@@ -2004,7 +2145,7 @@ const extractTypeAnnotation = decl => {
|
|
2004
2145
|
let a = decl;
|
2005
2146
|
while (a.typeAnnotation) a = a.typeAnnotation;
|
2006
2147
|
|
2007
|
-
let type, elementType;
|
2148
|
+
let type = null, elementType = null;
|
2008
2149
|
if (a.typeName) {
|
2009
2150
|
type = a.typeName.name;
|
2010
2151
|
} else if (a.type.endsWith('Keyword')) {
|
@@ -2035,7 +2176,7 @@ const generateVar = (scope, decl) => {
|
|
2035
2176
|
for (const x of decl.declarations) {
|
2036
2177
|
const name = mapName(x.id.name);
|
2037
2178
|
|
2038
|
-
if (!name) return todo('destructuring is not supported yet');
|
2179
|
+
if (!name) return todo(scope, 'destructuring is not supported yet');
|
2039
2180
|
|
2040
2181
|
if (x.init && isFuncType(x.init.type)) {
|
2041
2182
|
// hack for let a = function () { ... }
|
@@ -2052,9 +2193,10 @@ const generateVar = (scope, decl) => {
|
|
2052
2193
|
continue; // always ignore
|
2053
2194
|
}
|
2054
2195
|
|
2055
|
-
|
2196
|
+
const typed = typedInput && x.id.typeAnnotation;
|
2197
|
+
let idx = allocVar(scope, name, global, !typed);
|
2056
2198
|
|
2057
|
-
if (
|
2199
|
+
if (typed) {
|
2058
2200
|
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
2059
2201
|
}
|
2060
2202
|
|
@@ -2072,7 +2214,8 @@ const generateVar = (scope, decl) => {
|
|
2072
2214
|
return out;
|
2073
2215
|
};
|
2074
2216
|
|
2075
|
-
|
2217
|
+
// todo: optimize this func for valueUnused
|
2218
|
+
const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
2076
2219
|
const { type, name } = decl.left;
|
2077
2220
|
|
2078
2221
|
if (type === 'ObjectPattern') {
|
@@ -2090,9 +2233,9 @@ const generateAssign = (scope, decl) => {
|
|
2090
2233
|
// hack: .length setter
|
2091
2234
|
if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
|
2092
2235
|
const name = decl.left.object.name;
|
2093
|
-
const pointer = arrays
|
2236
|
+
const pointer = scope.arrays?.get(name);
|
2094
2237
|
|
2095
|
-
const aotPointer = pointer != null;
|
2238
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2096
2239
|
|
2097
2240
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
2098
2241
|
|
@@ -2117,9 +2260,9 @@ const generateAssign = (scope, decl) => {
|
|
2117
2260
|
// arr[i]
|
2118
2261
|
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
2119
2262
|
const name = decl.left.object.name;
|
2120
|
-
const pointer = arrays
|
2263
|
+
const pointer = scope.arrays?.get(name);
|
2121
2264
|
|
2122
|
-
const aotPointer = pointer != null;
|
2265
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2123
2266
|
|
2124
2267
|
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
2125
2268
|
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
@@ -2175,7 +2318,7 @@ const generateAssign = (scope, decl) => {
|
|
2175
2318
|
];
|
2176
2319
|
}
|
2177
2320
|
|
2178
|
-
if (!name) return todo('destructuring is not supported yet');
|
2321
|
+
if (!name) return todo(scope, 'destructuring is not supported yet', true);
|
2179
2322
|
|
2180
2323
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2181
2324
|
|
@@ -2223,9 +2366,7 @@ const generateAssign = (scope, decl) => {
|
|
2223
2366
|
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
2224
2367
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
2225
2368
|
|
2226
|
-
getLastType(scope)
|
2227
|
-
// hack: type is idx+1
|
2228
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2369
|
+
...setType(scope, name, getLastType(scope))
|
2229
2370
|
];
|
2230
2371
|
}
|
2231
2372
|
|
@@ -2236,9 +2377,7 @@ const generateAssign = (scope, decl) => {
|
|
2236
2377
|
|
2237
2378
|
// todo: string concat types
|
2238
2379
|
|
2239
|
-
|
2240
|
-
...number(TYPES.number, Valtype.i32),
|
2241
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2380
|
+
...setType(scope, name, TYPES.number)
|
2242
2381
|
];
|
2243
2382
|
};
|
2244
2383
|
|
@@ -2284,7 +2423,7 @@ const generateUnary = (scope, decl) => {
|
|
2284
2423
|
return out;
|
2285
2424
|
}
|
2286
2425
|
|
2287
|
-
case 'delete':
|
2426
|
+
case 'delete': {
|
2288
2427
|
let toReturn = true, toGenerate = true;
|
2289
2428
|
|
2290
2429
|
if (decl.argument.type === 'Identifier') {
|
@@ -2306,9 +2445,26 @@ const generateUnary = (scope, decl) => {
|
|
2306
2445
|
|
2307
2446
|
out.push(...number(toReturn ? 1 : 0));
|
2308
2447
|
return out;
|
2448
|
+
}
|
2449
|
+
|
2450
|
+
case 'typeof': {
|
2451
|
+
let overrideType, toGenerate = true;
|
2452
|
+
|
2453
|
+
if (decl.argument.type === 'Identifier') {
|
2454
|
+
const out = generateIdent(scope, decl.argument);
|
2455
|
+
|
2456
|
+
// if ReferenceError (undeclared var), ignore and return undefined
|
2457
|
+
if (out[1]) {
|
2458
|
+
// does not exist (2 ops from throw)
|
2459
|
+
overrideType = number(TYPES.undefined, Valtype.i32);
|
2460
|
+
toGenerate = false;
|
2461
|
+
}
|
2462
|
+
}
|
2309
2463
|
|
2310
|
-
|
2311
|
-
|
2464
|
+
const out = toGenerate ? generate(scope, decl.argument) : [];
|
2465
|
+
disposeLeftover(out);
|
2466
|
+
|
2467
|
+
out.push(...typeSwitch(scope, overrideType ?? getNodeType(scope, decl.argument), {
|
2312
2468
|
[TYPES.number]: makeString(scope, 'number', false, '#typeof_result'),
|
2313
2469
|
[TYPES.boolean]: makeString(scope, 'boolean', false, '#typeof_result'),
|
2314
2470
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
@@ -2319,27 +2475,30 @@ const generateUnary = (scope, decl) => {
|
|
2319
2475
|
|
2320
2476
|
// object and internal types
|
2321
2477
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
2322
|
-
});
|
2478
|
+
}));
|
2479
|
+
|
2480
|
+
return out;
|
2481
|
+
}
|
2323
2482
|
|
2324
2483
|
default:
|
2325
|
-
return todo(`unary operator ${decl.operator} not implemented yet
|
2484
|
+
return todo(scope, `unary operator ${decl.operator} not implemented yet`, true);
|
2326
2485
|
}
|
2327
2486
|
};
|
2328
2487
|
|
2329
|
-
const generateUpdate = (scope, decl) => {
|
2488
|
+
const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
|
2330
2489
|
const { name } = decl.argument;
|
2331
2490
|
|
2332
2491
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2333
2492
|
|
2334
2493
|
if (local === undefined) {
|
2335
|
-
return todo(`update expression with undefined variable
|
2494
|
+
return todo(scope, `update expression with undefined variable`, true);
|
2336
2495
|
}
|
2337
2496
|
|
2338
2497
|
const idx = local.idx;
|
2339
2498
|
const out = [];
|
2340
2499
|
|
2341
2500
|
out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2342
|
-
if (!decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2501
|
+
if (!decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2343
2502
|
|
2344
2503
|
switch (decl.operator) {
|
2345
2504
|
case '++':
|
@@ -2352,7 +2511,7 @@ const generateUpdate = (scope, decl) => {
|
|
2352
2511
|
}
|
2353
2512
|
|
2354
2513
|
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2355
|
-
if (decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2514
|
+
if (decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2356
2515
|
|
2357
2516
|
return out;
|
2358
2517
|
};
|
@@ -2392,7 +2551,7 @@ const generateConditional = (scope, decl) => {
|
|
2392
2551
|
// note type
|
2393
2552
|
out.push(
|
2394
2553
|
...getNodeType(scope, decl.consequent),
|
2395
|
-
setLastType(scope)
|
2554
|
+
...setLastType(scope)
|
2396
2555
|
);
|
2397
2556
|
|
2398
2557
|
out.push([ Opcodes.else ]);
|
@@ -2401,7 +2560,7 @@ const generateConditional = (scope, decl) => {
|
|
2401
2560
|
// note type
|
2402
2561
|
out.push(
|
2403
2562
|
...getNodeType(scope, decl.alternate),
|
2404
|
-
setLastType(scope)
|
2563
|
+
...setLastType(scope)
|
2405
2564
|
);
|
2406
2565
|
|
2407
2566
|
out.push([ Opcodes.end ]);
|
@@ -2415,7 +2574,7 @@ const generateFor = (scope, decl) => {
|
|
2415
2574
|
const out = [];
|
2416
2575
|
|
2417
2576
|
if (decl.init) {
|
2418
|
-
out.push(...generate(scope, decl.init));
|
2577
|
+
out.push(...generate(scope, decl.init, false, undefined, true));
|
2419
2578
|
disposeLeftover(out);
|
2420
2579
|
}
|
2421
2580
|
|
@@ -2433,7 +2592,7 @@ const generateFor = (scope, decl) => {
|
|
2433
2592
|
out.push(...generate(scope, decl.body));
|
2434
2593
|
out.push([ Opcodes.end ]);
|
2435
2594
|
|
2436
|
-
if (decl.update) out.push(...generate(scope, decl.update));
|
2595
|
+
if (decl.update) out.push(...generate(scope, decl.update, false, undefined, true));
|
2437
2596
|
|
2438
2597
|
out.push([ Opcodes.br, 1 ]);
|
2439
2598
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2461,6 +2620,36 @@ const generateWhile = (scope, decl) => {
|
|
2461
2620
|
return out;
|
2462
2621
|
};
|
2463
2622
|
|
2623
|
+
const generateDoWhile = (scope, decl) => {
|
2624
|
+
const out = [];
|
2625
|
+
|
2626
|
+
out.push([ Opcodes.loop, Blocktype.void ]);
|
2627
|
+
depth.push('dowhile');
|
2628
|
+
|
2629
|
+
// block for break (includes all)
|
2630
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2631
|
+
depth.push('block');
|
2632
|
+
|
2633
|
+
// block for continue
|
2634
|
+
// includes body but not test+loop so we can exit body at anytime
|
2635
|
+
// and still test+loop after
|
2636
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2637
|
+
depth.push('block');
|
2638
|
+
|
2639
|
+
out.push(...generate(scope, decl.body));
|
2640
|
+
|
2641
|
+
out.push([ Opcodes.end ]);
|
2642
|
+
depth.pop();
|
2643
|
+
|
2644
|
+
out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2645
|
+
out.push([ Opcodes.br_if, 1 ]);
|
2646
|
+
|
2647
|
+
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
2648
|
+
depth.pop(); depth.pop();
|
2649
|
+
|
2650
|
+
return out;
|
2651
|
+
};
|
2652
|
+
|
2464
2653
|
const generateForOf = (scope, decl) => {
|
2465
2654
|
const out = [];
|
2466
2655
|
|
@@ -2497,7 +2686,10 @@ const generateForOf = (scope, decl) => {
|
|
2497
2686
|
generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
|
2498
2687
|
}
|
2499
2688
|
|
2689
|
+
// if (!leftName) console.log(decl.left?.declarations?.[0]?.id ?? decl.left);
|
2690
|
+
|
2500
2691
|
const [ local, isGlobal ] = lookupName(scope, leftName);
|
2692
|
+
if (!local) return todo(scope, 'for of failed to get left local (probably destructure)');
|
2501
2693
|
|
2502
2694
|
depth.push('block');
|
2503
2695
|
depth.push('block');
|
@@ -2506,6 +2698,7 @@ const generateForOf = (scope, decl) => {
|
|
2506
2698
|
// hack: this is naughty and will break things!
|
2507
2699
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2508
2700
|
if (pages.hasAnyString) {
|
2701
|
+
// todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
|
2509
2702
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2510
2703
|
rawElements: new Array(1)
|
2511
2704
|
}, isGlobal, leftName, true, 'i16');
|
@@ -2597,6 +2790,56 @@ const generateForOf = (scope, decl) => {
|
|
2597
2790
|
[ Opcodes.end ],
|
2598
2791
|
[ Opcodes.end ]
|
2599
2792
|
],
|
2793
|
+
[TYPES._bytestring]: [
|
2794
|
+
...setType(scope, leftName, TYPES._bytestring),
|
2795
|
+
|
2796
|
+
[ Opcodes.loop, Blocktype.void ],
|
2797
|
+
|
2798
|
+
// setup new/out array
|
2799
|
+
...newOut,
|
2800
|
+
[ Opcodes.drop ],
|
2801
|
+
|
2802
|
+
...number(0, Valtype.i32), // base 0 for store after
|
2803
|
+
|
2804
|
+
// load current string ind {arg}
|
2805
|
+
[ Opcodes.local_get, pointer ],
|
2806
|
+
[ Opcodes.local_get, counter ],
|
2807
|
+
[ Opcodes.i32_add ],
|
2808
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2809
|
+
|
2810
|
+
// store to new string ind 0
|
2811
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2812
|
+
|
2813
|
+
// return new string (page)
|
2814
|
+
...number(newPointer),
|
2815
|
+
|
2816
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2817
|
+
|
2818
|
+
[ Opcodes.block, Blocktype.void ],
|
2819
|
+
[ Opcodes.block, Blocktype.void ],
|
2820
|
+
...generate(scope, decl.body),
|
2821
|
+
[ Opcodes.end ],
|
2822
|
+
|
2823
|
+
// increment iter pointer
|
2824
|
+
// [ Opcodes.local_get, pointer ],
|
2825
|
+
// ...number(1, Valtype.i32),
|
2826
|
+
// [ Opcodes.i32_add ],
|
2827
|
+
// [ Opcodes.local_set, pointer ],
|
2828
|
+
|
2829
|
+
// increment counter by 1
|
2830
|
+
[ Opcodes.local_get, counter ],
|
2831
|
+
...number(1, Valtype.i32),
|
2832
|
+
[ Opcodes.i32_add ],
|
2833
|
+
[ Opcodes.local_tee, counter ],
|
2834
|
+
|
2835
|
+
// loop if counter != length
|
2836
|
+
[ Opcodes.local_get, length ],
|
2837
|
+
[ Opcodes.i32_ne ],
|
2838
|
+
[ Opcodes.br_if, 1 ],
|
2839
|
+
|
2840
|
+
[ Opcodes.end ],
|
2841
|
+
[ Opcodes.end ]
|
2842
|
+
],
|
2600
2843
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2601
2844
|
}, Blocktype.void));
|
2602
2845
|
|
@@ -2607,28 +2850,65 @@ const generateForOf = (scope, decl) => {
|
|
2607
2850
|
return out;
|
2608
2851
|
};
|
2609
2852
|
|
2853
|
+
// find the nearest loop in depth map by type
|
2610
2854
|
const getNearestLoop = () => {
|
2611
2855
|
for (let i = depth.length - 1; i >= 0; i--) {
|
2612
|
-
if (
|
2856
|
+
if (['while', 'dowhile', 'for', 'forof'].includes(depth[i])) return i;
|
2613
2857
|
}
|
2614
2858
|
|
2615
2859
|
return -1;
|
2616
2860
|
};
|
2617
2861
|
|
2618
2862
|
const generateBreak = (scope, decl) => {
|
2619
|
-
const
|
2863
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2864
|
+
const type = depth[target];
|
2865
|
+
|
2866
|
+
// different loop types have different branch offsets
|
2867
|
+
// as they have different wasm block/loop/if structures
|
2868
|
+
// we need to use the right offset by type to branch to the one we want
|
2869
|
+
// for a break: exit the loop without executing anything else inside it
|
2870
|
+
const offset = ({
|
2871
|
+
for: 2, // loop > if (wanted branch) > block (we are here)
|
2872
|
+
while: 2, // loop > if (wanted branch) (we are here)
|
2873
|
+
dowhile: 2, // loop > block (wanted branch) > block (we are here)
|
2874
|
+
forof: 2, // loop > block (wanted branch) > block (we are here)
|
2875
|
+
if: 1 // break inside if, branch 0 to skip the rest of the if
|
2876
|
+
})[type];
|
2877
|
+
|
2620
2878
|
return [
|
2621
|
-
[ Opcodes.br, ...signedLEB128(
|
2879
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2622
2880
|
];
|
2623
2881
|
};
|
2624
2882
|
|
2625
2883
|
const generateContinue = (scope, decl) => {
|
2626
|
-
const
|
2884
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2885
|
+
const type = depth[target];
|
2886
|
+
|
2887
|
+
// different loop types have different branch offsets
|
2888
|
+
// as they have different wasm block/loop/if structures
|
2889
|
+
// we need to use the right offset by type to branch to the one we want
|
2890
|
+
// for a continue: do test for the loop, and then loop depending on that success
|
2891
|
+
const offset = ({
|
2892
|
+
for: 3, // loop (wanted branch) > if > block (we are here)
|
2893
|
+
while: 1, // loop (wanted branch) > if (we are here)
|
2894
|
+
dowhile: 3, // loop > block > block (wanted branch) (we are here)
|
2895
|
+
forof: 3 // loop > block > block (wanted branch) (we are here)
|
2896
|
+
})[type];
|
2897
|
+
|
2627
2898
|
return [
|
2628
|
-
[ Opcodes.br, ...signedLEB128(
|
2899
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2629
2900
|
];
|
2630
2901
|
};
|
2631
2902
|
|
2903
|
+
const generateLabel = (scope, decl) => {
|
2904
|
+
scope.labels ??= new Map();
|
2905
|
+
|
2906
|
+
const name = decl.label.name;
|
2907
|
+
scope.labels.set(name, depth.length);
|
2908
|
+
|
2909
|
+
return generate(scope, decl.body);
|
2910
|
+
};
|
2911
|
+
|
2632
2912
|
const generateThrow = (scope, decl) => {
|
2633
2913
|
scope.throws = true;
|
2634
2914
|
|
@@ -2661,7 +2941,7 @@ const generateThrow = (scope, decl) => {
|
|
2661
2941
|
};
|
2662
2942
|
|
2663
2943
|
const generateTry = (scope, decl) => {
|
2664
|
-
if (decl.finalizer) return todo('try finally not implemented yet');
|
2944
|
+
if (decl.finalizer) return todo(scope, 'try finally not implemented yet');
|
2665
2945
|
|
2666
2946
|
const out = [];
|
2667
2947
|
|
@@ -2692,7 +2972,7 @@ const generateAssignPat = (scope, decl) => {
|
|
2692
2972
|
// TODO
|
2693
2973
|
// if identifier declared, use that
|
2694
2974
|
// else, use default (right)
|
2695
|
-
return todo('assignment pattern (optional arg)');
|
2975
|
+
return todo(scope, 'assignment pattern (optional arg)');
|
2696
2976
|
};
|
2697
2977
|
|
2698
2978
|
let pages = new Map();
|
@@ -2771,16 +3051,20 @@ const getAllocType = itemType => {
|
|
2771
3051
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2772
3052
|
const out = [];
|
2773
3053
|
|
3054
|
+
scope.arrays ??= new Map();
|
3055
|
+
|
2774
3056
|
let firstAssign = false;
|
2775
|
-
if (!arrays.has(name) || name === '$undeclared') {
|
3057
|
+
if (!scope.arrays.has(name) || name === '$undeclared') {
|
2776
3058
|
firstAssign = true;
|
2777
3059
|
|
2778
3060
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2779
3061
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2780
|
-
|
3062
|
+
|
3063
|
+
if (Prefs.scopedPageNames) scope.arrays.set(name, allocPage(scope, `${scope.name} | ${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
3064
|
+
else scope.arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2781
3065
|
}
|
2782
3066
|
|
2783
|
-
const pointer = arrays.get(name);
|
3067
|
+
const pointer = scope.arrays.get(name);
|
2784
3068
|
|
2785
3069
|
const useRawElements = !!decl.rawElements;
|
2786
3070
|
const elements = useRawElements ? decl.rawElements : decl.elements;
|
@@ -2866,20 +3150,29 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
2866
3150
|
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2867
3151
|
};
|
2868
3152
|
|
2869
|
-
let arrays = new Map();
|
2870
3153
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
2871
3154
|
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
2872
3155
|
};
|
2873
3156
|
|
2874
3157
|
export const generateMember = (scope, decl, _global, _name) => {
|
2875
3158
|
const name = decl.object.name;
|
2876
|
-
const pointer = arrays
|
3159
|
+
const pointer = scope.arrays?.get(name);
|
2877
3160
|
|
2878
|
-
const aotPointer = pointer != null;
|
3161
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2879
3162
|
|
2880
3163
|
// hack: .length
|
2881
3164
|
if (decl.property.name === 'length') {
|
2882
|
-
|
3165
|
+
const func = funcs.find(x => x.name === name);
|
3166
|
+
if (func) {
|
3167
|
+
const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
|
3168
|
+
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
3169
|
+
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3170
|
+
}
|
3171
|
+
|
3172
|
+
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3173
|
+
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3174
|
+
if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
|
3175
|
+
|
2883
3176
|
return [
|
2884
3177
|
...(aotPointer ? number(0, Valtype.i32) : [
|
2885
3178
|
...generate(scope, decl.object),
|
@@ -2923,7 +3216,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2923
3216
|
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2924
3217
|
|
2925
3218
|
...number(TYPES.number, Valtype.i32),
|
2926
|
-
setLastType(scope)
|
3219
|
+
...setLastType(scope)
|
2927
3220
|
],
|
2928
3221
|
|
2929
3222
|
[TYPES.string]: [
|
@@ -2955,7 +3248,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2955
3248
|
...number(newPointer),
|
2956
3249
|
|
2957
3250
|
...number(TYPES.string, Valtype.i32),
|
2958
|
-
setLastType(scope)
|
3251
|
+
...setLastType(scope)
|
2959
3252
|
],
|
2960
3253
|
[TYPES._bytestring]: [
|
2961
3254
|
// setup new/out array
|
@@ -2974,19 +3267,19 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2974
3267
|
]),
|
2975
3268
|
|
2976
3269
|
// load current string ind {arg}
|
2977
|
-
[ Opcodes.i32_load8_u,
|
3270
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2978
3271
|
|
2979
3272
|
// store to new string ind 0
|
2980
|
-
[ Opcodes.i32_store8,
|
3273
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2981
3274
|
|
2982
3275
|
// return new string (page)
|
2983
3276
|
...number(newPointer),
|
2984
3277
|
|
2985
3278
|
...number(TYPES._bytestring, Valtype.i32),
|
2986
|
-
setLastType(scope)
|
3279
|
+
...setLastType(scope)
|
2987
3280
|
],
|
2988
3281
|
|
2989
|
-
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet')
|
3282
|
+
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
2990
3283
|
});
|
2991
3284
|
};
|
2992
3285
|
|
@@ -2996,28 +3289,36 @@ const objectHack = node => {
|
|
2996
3289
|
if (!node) return node;
|
2997
3290
|
|
2998
3291
|
if (node.type === 'MemberExpression') {
|
2999
|
-
|
3292
|
+
const out = (() => {
|
3293
|
+
if (node.computed || node.optional) return;
|
3000
3294
|
|
3001
|
-
|
3295
|
+
let objectName = node.object.name;
|
3002
3296
|
|
3003
|
-
|
3004
|
-
|
3297
|
+
// if object is not identifier or another member exp, give up
|
3298
|
+
if (node.object.type !== 'Identifier' && node.object.type !== 'MemberExpression') return;
|
3299
|
+
if (objectName && ['undefined', 'null', 'NaN', 'Infinity'].includes(objectName)) return;
|
3005
3300
|
|
3006
|
-
|
3301
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3007
3302
|
|
3008
|
-
|
3009
|
-
|
3303
|
+
// if .length, give up (hack within a hack!)
|
3304
|
+
if (node.property.name === 'length') {
|
3305
|
+
node.object = objectHack(node.object);
|
3306
|
+
return;
|
3307
|
+
}
|
3010
3308
|
|
3011
|
-
|
3012
|
-
|
3309
|
+
// no object name, give up
|
3310
|
+
if (!objectName) return;
|
3013
3311
|
|
3014
|
-
|
3015
|
-
|
3312
|
+
const name = '__' + objectName + '_' + node.property.name;
|
3313
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
3016
3314
|
|
3017
|
-
|
3018
|
-
|
3019
|
-
|
3020
|
-
|
3315
|
+
return {
|
3316
|
+
type: 'Identifier',
|
3317
|
+
name
|
3318
|
+
};
|
3319
|
+
})();
|
3320
|
+
|
3321
|
+
if (out) return out;
|
3021
3322
|
}
|
3022
3323
|
|
3023
3324
|
for (const x in node) {
|
@@ -3031,8 +3332,8 @@ const objectHack = node => {
|
|
3031
3332
|
};
|
3032
3333
|
|
3033
3334
|
const generateFunc = (scope, decl) => {
|
3034
|
-
if (decl.async) return todo('async functions are not supported');
|
3035
|
-
if (decl.generator) return todo('generator functions are not supported');
|
3335
|
+
if (decl.async) return todo(scope, 'async functions are not supported');
|
3336
|
+
if (decl.generator) return todo(scope, 'generator functions are not supported');
|
3036
3337
|
|
3037
3338
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3038
3339
|
const params = decl.params ?? [];
|
@@ -3048,6 +3349,11 @@ const generateFunc = (scope, decl) => {
|
|
3048
3349
|
name
|
3049
3350
|
};
|
3050
3351
|
|
3352
|
+
if (typedInput && decl.returnType) {
|
3353
|
+
innerScope.returnType = extractTypeAnnotation(decl.returnType).type;
|
3354
|
+
innerScope.returns = [ valtypeBinary ];
|
3355
|
+
}
|
3356
|
+
|
3051
3357
|
for (let i = 0; i < params.length; i++) {
|
3052
3358
|
allocVar(innerScope, params[i].name, false);
|
3053
3359
|
|
@@ -3110,16 +3416,6 @@ const generateCode = (scope, decl) => {
|
|
3110
3416
|
};
|
3111
3417
|
|
3112
3418
|
const internalConstrs = {
|
3113
|
-
Boolean: {
|
3114
|
-
generate: (scope, decl) => {
|
3115
|
-
if (decl.arguments.length === 0) return number(0);
|
3116
|
-
|
3117
|
-
// should generate/run all args
|
3118
|
-
return truthy(scope, generate(scope, decl.arguments[0]), getNodeType(scope, decl.arguments[0]), false, false);
|
3119
|
-
},
|
3120
|
-
type: TYPES.boolean
|
3121
|
-
},
|
3122
|
-
|
3123
3419
|
Array: {
|
3124
3420
|
generate: (scope, decl, global, name) => {
|
3125
3421
|
// new Array(i0, i1, ...)
|
@@ -3137,7 +3433,7 @@ const internalConstrs = {
|
|
3137
3433
|
|
3138
3434
|
// todo: check in wasm instead of here
|
3139
3435
|
const literalValue = arg.value ?? 0;
|
3140
|
-
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length');
|
3436
|
+
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
|
3141
3437
|
|
3142
3438
|
return [
|
3143
3439
|
...number(0, Valtype.i32),
|
@@ -3148,7 +3444,8 @@ const internalConstrs = {
|
|
3148
3444
|
...number(pointer)
|
3149
3445
|
];
|
3150
3446
|
},
|
3151
|
-
type: TYPES._array
|
3447
|
+
type: TYPES._array,
|
3448
|
+
length: 1
|
3152
3449
|
},
|
3153
3450
|
|
3154
3451
|
__Array_of: {
|
@@ -3160,7 +3457,94 @@ const internalConstrs = {
|
|
3160
3457
|
}, global, name);
|
3161
3458
|
},
|
3162
3459
|
type: TYPES._array,
|
3460
|
+
notConstr: true,
|
3461
|
+
length: 0
|
3462
|
+
},
|
3463
|
+
|
3464
|
+
__Porffor_fastOr: {
|
3465
|
+
generate: (scope, decl) => {
|
3466
|
+
const out = [];
|
3467
|
+
|
3468
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3469
|
+
out.push(
|
3470
|
+
...generate(scope, decl.arguments[i]),
|
3471
|
+
Opcodes.i32_to_u,
|
3472
|
+
...(i > 0 ? [ [ Opcodes.i32_or ] ] : [])
|
3473
|
+
);
|
3474
|
+
}
|
3475
|
+
|
3476
|
+
return out;
|
3477
|
+
},
|
3478
|
+
type: TYPES.boolean,
|
3163
3479
|
notConstr: true
|
3480
|
+
},
|
3481
|
+
|
3482
|
+
__Porffor_fastAnd: {
|
3483
|
+
generate: (scope, decl) => {
|
3484
|
+
const out = [];
|
3485
|
+
|
3486
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3487
|
+
out.push(
|
3488
|
+
...generate(scope, decl.arguments[i]),
|
3489
|
+
Opcodes.i32_to_u,
|
3490
|
+
...(i > 0 ? [ [ Opcodes.i32_and ] ] : [])
|
3491
|
+
);
|
3492
|
+
}
|
3493
|
+
|
3494
|
+
return out;
|
3495
|
+
},
|
3496
|
+
type: TYPES.boolean,
|
3497
|
+
notConstr: true
|
3498
|
+
},
|
3499
|
+
|
3500
|
+
Boolean: {
|
3501
|
+
generate: (scope, decl) => {
|
3502
|
+
// todo: boolean object when used as constructor
|
3503
|
+
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3504
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3505
|
+
},
|
3506
|
+
type: TYPES.boolean,
|
3507
|
+
length: 1
|
3508
|
+
},
|
3509
|
+
|
3510
|
+
__Math_max: {
|
3511
|
+
generate: (scope, decl) => {
|
3512
|
+
const out = [
|
3513
|
+
...number(-Infinity)
|
3514
|
+
];
|
3515
|
+
|
3516
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3517
|
+
out.push(
|
3518
|
+
...generate(scope, decl.arguments[i]),
|
3519
|
+
[ Opcodes.f64_max ]
|
3520
|
+
);
|
3521
|
+
}
|
3522
|
+
|
3523
|
+
return out;
|
3524
|
+
},
|
3525
|
+
type: TYPES.number,
|
3526
|
+
notConstr: true,
|
3527
|
+
length: 2
|
3528
|
+
},
|
3529
|
+
|
3530
|
+
__Math_min: {
|
3531
|
+
generate: (scope, decl) => {
|
3532
|
+
const out = [
|
3533
|
+
...number(Infinity)
|
3534
|
+
];
|
3535
|
+
|
3536
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3537
|
+
out.push(
|
3538
|
+
...generate(scope, decl.arguments[i]),
|
3539
|
+
[ Opcodes.f64_min ]
|
3540
|
+
);
|
3541
|
+
}
|
3542
|
+
|
3543
|
+
return out;
|
3544
|
+
},
|
3545
|
+
type: TYPES.number,
|
3546
|
+
notConstr: true,
|
3547
|
+
length: 2
|
3164
3548
|
}
|
3165
3549
|
};
|
3166
3550
|
|
@@ -3189,7 +3573,6 @@ export default program => {
|
|
3189
3573
|
funcs = [];
|
3190
3574
|
funcIndex = {};
|
3191
3575
|
depth = [];
|
3192
|
-
arrays = new Map();
|
3193
3576
|
pages = new Map();
|
3194
3577
|
data = [];
|
3195
3578
|
currentFuncIndex = importedFuncs.length;
|
@@ -3203,6 +3586,10 @@ export default program => {
|
|
3203
3586
|
|
3204
3587
|
const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
|
3205
3588
|
|
3589
|
+
globalThis.pageSize = PageSize;
|
3590
|
+
const pageSizeOpt = process.argv.find(x => x.startsWith('-page-size='));
|
3591
|
+
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3592
|
+
|
3206
3593
|
// set generic opcodes for current valtype
|
3207
3594
|
Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
|
3208
3595
|
Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
|
@@ -3211,10 +3598,10 @@ export default program => {
|
|
3211
3598
|
Opcodes.add = [ Opcodes.i32_add, Opcodes.i64_add, Opcodes.f64_add ][valtypeInd];
|
3212
3599
|
Opcodes.sub = [ Opcodes.i32_sub, Opcodes.i64_sub, Opcodes.f64_sub ][valtypeInd];
|
3213
3600
|
|
3214
|
-
Opcodes.i32_to = [ [
|
3215
|
-
Opcodes.i32_to_u = [ [
|
3216
|
-
Opcodes.i32_from = [ [
|
3217
|
-
Opcodes.i32_from_u = [ [
|
3601
|
+
Opcodes.i32_to = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_s ][valtypeInd];
|
3602
|
+
Opcodes.i32_to_u = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_u ][valtypeInd];
|
3603
|
+
Opcodes.i32_from = [ [], [ Opcodes.i64_extend_i32_s ], [ Opcodes.f64_convert_i32_s ] ][valtypeInd];
|
3604
|
+
Opcodes.i32_from_u = [ [], [ Opcodes.i64_extend_i32_u ], [ Opcodes.f64_convert_i32_u ] ][valtypeInd];
|
3218
3605
|
|
3219
3606
|
Opcodes.load = [ Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load ][valtypeInd];
|
3220
3607
|
Opcodes.store = [ Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store ][valtypeInd];
|
@@ -3227,10 +3614,6 @@ export default program => {
|
|
3227
3614
|
|
3228
3615
|
program.id = { name: 'main' };
|
3229
3616
|
|
3230
|
-
globalThis.pageSize = PageSize;
|
3231
|
-
const pageSizeOpt = process.argv.find(x => x.startsWith('-page-size='));
|
3232
|
-
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3233
|
-
|
3234
3617
|
const scope = {
|
3235
3618
|
locals: {},
|
3236
3619
|
localInd: 0
|
@@ -3241,7 +3624,7 @@ export default program => {
|
|
3241
3624
|
body: program.body
|
3242
3625
|
};
|
3243
3626
|
|
3244
|
-
if (Prefs.astLog) console.log(program.body.body);
|
3627
|
+
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3245
3628
|
|
3246
3629
|
generateFunc(scope, program);
|
3247
3630
|
|
@@ -3258,7 +3641,11 @@ export default program => {
|
|
3258
3641
|
}
|
3259
3642
|
|
3260
3643
|
if (lastInst[0] === Opcodes.end || lastInst[0] === Opcodes.local_set || lastInst[0] === Opcodes.global_set) {
|
3261
|
-
|
3644
|
+
if (lastInst[0] === Opcodes.local_set && lastInst[1] === main.locals['#last_type'].idx) {
|
3645
|
+
main.wasm.splice(main.wasm.length - 1, 1);
|
3646
|
+
} else {
|
3647
|
+
main.returns = [];
|
3648
|
+
}
|
3262
3649
|
}
|
3263
3650
|
|
3264
3651
|
if (lastInst[0] === Opcodes.call) {
|