porffor 0.2.0-6aff0fa → 0.2.0-767de65
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 +54 -41
- package/asur/index.js +624 -340
- 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 +3 -5
- 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 +27 -8
- package/compiler/builtins/string.ts +1055 -0
- package/compiler/builtins/tostring.ts +45 -0
- package/compiler/builtins.js +405 -108
- package/compiler/{codeGen.js → codegen.js} +654 -246
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +108 -10
- package/compiler/generated_builtins.js +682 -2
- package/compiler/index.js +16 -14
- package/compiler/log.js +2 -2
- 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 +13 -1
- package/compiler/wrap.js +36 -42
- package/empty.js +0 -0
- package/hello.js +2 -0
- 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 +26 -10
- package/runner/profiler.js +45 -26
- package/runner/repl.js +40 -7
- package/runner/sizes.js +37 -37
- package/test262_changes_from_1afe9b87d2_to_04-09.md +270 -0
- package/runner/info.js +0 -89
- package/runner/transform.js +0 -15
- 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,33 +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
|
-
return funcs[
|
255
|
+
return funcs[func](str);
|
232
256
|
}
|
233
257
|
|
234
258
|
default:
|
@@ -238,7 +262,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
238
262
|
return [];
|
239
263
|
}
|
240
264
|
|
241
|
-
return todo(`no generation for ${decl.type}!`);
|
265
|
+
return todo(scope, `no generation for ${decl.type}!`);
|
242
266
|
}
|
243
267
|
};
|
244
268
|
|
@@ -266,7 +290,7 @@ const lookupName = (scope, _name) => {
|
|
266
290
|
return [ undefined, undefined ];
|
267
291
|
};
|
268
292
|
|
269
|
-
const internalThrow = (scope, constructor, message, expectsValue =
|
293
|
+
const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysValueInternalThrows) => [
|
270
294
|
...generateThrow(scope, {
|
271
295
|
argument: {
|
272
296
|
type: 'NewExpression',
|
@@ -293,7 +317,7 @@ const generateIdent = (scope, decl) => {
|
|
293
317
|
|
294
318
|
let wasm = builtinVars[name];
|
295
319
|
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name });
|
296
|
-
return wasm;
|
320
|
+
return wasm.slice();
|
297
321
|
}
|
298
322
|
|
299
323
|
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
@@ -301,6 +325,11 @@ const generateIdent = (scope, decl) => {
|
|
301
325
|
return number(1);
|
302
326
|
}
|
303
327
|
|
328
|
+
if (isExistingProtoFunc(name)) {
|
329
|
+
// todo: return an actual something
|
330
|
+
return number(1);
|
331
|
+
}
|
332
|
+
|
304
333
|
if (local?.idx === undefined) {
|
305
334
|
// no local var with name
|
306
335
|
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
@@ -331,14 +360,18 @@ const generateReturn = (scope, decl) => {
|
|
331
360
|
// just bare "return"
|
332
361
|
return [
|
333
362
|
...number(UNDEFINED), // "undefined" if func returns
|
334
|
-
...
|
363
|
+
...(scope.returnType != null ? [] : [
|
364
|
+
...number(TYPES.undefined, Valtype.i32) // type undefined
|
365
|
+
]),
|
335
366
|
[ Opcodes.return ]
|
336
367
|
];
|
337
368
|
}
|
338
369
|
|
339
370
|
return [
|
340
371
|
...generate(scope, decl.argument),
|
341
|
-
...
|
372
|
+
...(scope.returnType != null ? [] : [
|
373
|
+
...getNodeType(scope, decl.argument)
|
374
|
+
]),
|
342
375
|
[ Opcodes.return ]
|
343
376
|
];
|
344
377
|
};
|
@@ -352,7 +385,8 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
352
385
|
return idx;
|
353
386
|
};
|
354
387
|
|
355
|
-
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);
|
356
390
|
|
357
391
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
358
392
|
const checks = {
|
@@ -361,7 +395,7 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
361
395
|
'??': nullish
|
362
396
|
};
|
363
397
|
|
364
|
-
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);
|
365
399
|
|
366
400
|
// generic structure for {a} OP {b}
|
367
401
|
// -->
|
@@ -369,8 +403,8 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
369
403
|
|
370
404
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
371
405
|
// (like if we are in an if condition - very common)
|
372
|
-
const leftIsInt =
|
373
|
-
const rightIsInt =
|
406
|
+
const leftIsInt = isFloatToIntOp(left[left.length - 1]);
|
407
|
+
const rightIsInt = isFloatToIntOp(right[right.length - 1]);
|
374
408
|
|
375
409
|
const canInt = leftIsInt && rightIsInt;
|
376
410
|
|
@@ -387,12 +421,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
387
421
|
...right,
|
388
422
|
// note type
|
389
423
|
...rightType,
|
390
|
-
setLastType(scope),
|
424
|
+
...setLastType(scope),
|
391
425
|
[ Opcodes.else ],
|
392
426
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
393
427
|
// note type
|
394
428
|
...leftType,
|
395
|
-
setLastType(scope),
|
429
|
+
...setLastType(scope),
|
396
430
|
[ Opcodes.end ],
|
397
431
|
Opcodes.i32_from
|
398
432
|
];
|
@@ -406,17 +440,17 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
406
440
|
...right,
|
407
441
|
// note type
|
408
442
|
...rightType,
|
409
|
-
setLastType(scope),
|
443
|
+
...setLastType(scope),
|
410
444
|
[ Opcodes.else ],
|
411
445
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
412
446
|
// note type
|
413
447
|
...leftType,
|
414
|
-
setLastType(scope),
|
448
|
+
...setLastType(scope),
|
415
449
|
[ Opcodes.end ]
|
416
450
|
];
|
417
451
|
};
|
418
452
|
|
419
|
-
const concatStrings = (scope, left, right, global, name, assign) => {
|
453
|
+
const concatStrings = (scope, left, right, global, name, assign = false, bytestrings = false) => {
|
420
454
|
// todo: this should be rewritten into a built-in/func: String.prototype.concat
|
421
455
|
// todo: convert left and right to strings if not
|
422
456
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -427,7 +461,7 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
427
461
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
428
462
|
|
429
463
|
if (assign) {
|
430
|
-
const pointer = arrays
|
464
|
+
const pointer = scope.arrays?.get(name ?? '$undeclared');
|
431
465
|
|
432
466
|
return [
|
433
467
|
// setup right
|
@@ -452,11 +486,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
452
486
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
|
453
487
|
|
454
488
|
// copy right
|
455
|
-
// dst = out pointer + length size + current length *
|
489
|
+
// dst = out pointer + length size + current length * sizeof valtype
|
456
490
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
457
491
|
|
458
492
|
[ Opcodes.local_get, leftLength ],
|
459
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
493
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
460
494
|
[ Opcodes.i32_mul ],
|
461
495
|
[ Opcodes.i32_add ],
|
462
496
|
|
@@ -465,9 +499,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
465
499
|
...number(ValtypeSize.i32, Valtype.i32),
|
466
500
|
[ Opcodes.i32_add ],
|
467
501
|
|
468
|
-
// size = right length *
|
502
|
+
// size = right length * sizeof valtype
|
469
503
|
[ Opcodes.local_get, rightLength ],
|
470
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
504
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
471
505
|
[ Opcodes.i32_mul ],
|
472
506
|
|
473
507
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -525,11 +559,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
525
559
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
526
560
|
|
527
561
|
// copy right
|
528
|
-
// dst = out pointer + length size + left length *
|
562
|
+
// dst = out pointer + length size + left length * sizeof valtype
|
529
563
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
530
564
|
|
531
565
|
[ Opcodes.local_get, leftLength ],
|
532
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
566
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
533
567
|
[ Opcodes.i32_mul ],
|
534
568
|
[ Opcodes.i32_add ],
|
535
569
|
|
@@ -538,9 +572,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
538
572
|
...number(ValtypeSize.i32, Valtype.i32),
|
539
573
|
[ Opcodes.i32_add ],
|
540
574
|
|
541
|
-
// size = right length *
|
575
|
+
// size = right length * sizeof valtype
|
542
576
|
[ Opcodes.local_get, rightLength ],
|
543
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
577
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
544
578
|
[ Opcodes.i32_mul ],
|
545
579
|
|
546
580
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -550,7 +584,7 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
550
584
|
];
|
551
585
|
};
|
552
586
|
|
553
|
-
const compareStrings = (scope, left, right) => {
|
587
|
+
const compareStrings = (scope, left, right, bytestrings = false) => {
|
554
588
|
// todo: this should be rewritten into a func
|
555
589
|
// todo: convert left and right to strings if not
|
556
590
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -559,7 +593,6 @@ const compareStrings = (scope, left, right) => {
|
|
559
593
|
const leftPointer = localTmp(scope, 'compare_left_pointer', Valtype.i32);
|
560
594
|
const leftLength = localTmp(scope, 'compare_left_length', Valtype.i32);
|
561
595
|
const rightPointer = localTmp(scope, 'compare_right_pointer', Valtype.i32);
|
562
|
-
const rightLength = localTmp(scope, 'compare_right_length', Valtype.i32);
|
563
596
|
|
564
597
|
const index = localTmp(scope, 'compare_index', Valtype.i32);
|
565
598
|
const indexEnd = localTmp(scope, 'compare_index_end', Valtype.i32);
|
@@ -587,7 +620,6 @@ const compareStrings = (scope, left, right) => {
|
|
587
620
|
|
588
621
|
[ Opcodes.local_get, rightPointer ],
|
589
622
|
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
590
|
-
[ Opcodes.local_tee, rightLength ],
|
591
623
|
|
592
624
|
// fast path: check leftLength != rightLength
|
593
625
|
[ Opcodes.i32_ne ],
|
@@ -602,11 +634,13 @@ const compareStrings = (scope, left, right) => {
|
|
602
634
|
...number(0, Valtype.i32),
|
603
635
|
[ Opcodes.local_set, index ],
|
604
636
|
|
605
|
-
// setup index end as length * sizeof
|
637
|
+
// setup index end as length * sizeof valtype (1 for bytestring, 2 for string)
|
606
638
|
// we do this instead of having to do mul/div each iter for perf™
|
607
639
|
[ Opcodes.local_get, leftLength ],
|
608
|
-
...
|
609
|
-
|
640
|
+
...(bytestrings ? [] : [
|
641
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
642
|
+
[ Opcodes.i32_mul ],
|
643
|
+
]),
|
610
644
|
[ Opcodes.local_set, indexEnd ],
|
611
645
|
|
612
646
|
// iterate over each char and check if eq
|
@@ -616,13 +650,17 @@ const compareStrings = (scope, left, right) => {
|
|
616
650
|
[ Opcodes.local_get, index ],
|
617
651
|
[ Opcodes.local_get, leftPointer ],
|
618
652
|
[ Opcodes.i32_add ],
|
619
|
-
|
653
|
+
bytestrings ?
|
654
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
655
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
620
656
|
|
621
657
|
// fetch right
|
622
658
|
[ Opcodes.local_get, index ],
|
623
659
|
[ Opcodes.local_get, rightPointer ],
|
624
660
|
[ Opcodes.i32_add ],
|
625
|
-
|
661
|
+
bytestrings ?
|
662
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
663
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
626
664
|
|
627
665
|
// not equal, "return" false
|
628
666
|
[ Opcodes.i32_ne ],
|
@@ -631,13 +669,13 @@ const compareStrings = (scope, left, right) => {
|
|
631
669
|
[ Opcodes.br, 2 ],
|
632
670
|
[ Opcodes.end ],
|
633
671
|
|
634
|
-
// index += sizeof
|
672
|
+
// index += sizeof valtype (1 for bytestring, 2 for string)
|
635
673
|
[ Opcodes.local_get, index ],
|
636
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
674
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
637
675
|
[ Opcodes.i32_add ],
|
638
676
|
[ Opcodes.local_tee, index ],
|
639
677
|
|
640
|
-
// if index != index end (length * sizeof
|
678
|
+
// if index != index end (length * sizeof valtype), loop
|
641
679
|
[ Opcodes.local_get, indexEnd ],
|
642
680
|
[ Opcodes.i32_ne ],
|
643
681
|
[ Opcodes.br_if, 0 ],
|
@@ -658,13 +696,14 @@ const compareStrings = (scope, left, right) => {
|
|
658
696
|
};
|
659
697
|
|
660
698
|
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
661
|
-
if (
|
699
|
+
if (isFloatToIntOp(wasm[wasm.length - 1])) return [
|
662
700
|
...wasm,
|
663
701
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
664
702
|
];
|
703
|
+
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
665
704
|
|
666
705
|
const useTmp = knownType(scope, type) == null;
|
667
|
-
const tmp = useTmp && localTmp(scope,
|
706
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
668
707
|
|
669
708
|
const def = [
|
670
709
|
// if value != 0
|
@@ -716,7 +755,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
716
755
|
|
717
756
|
const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
718
757
|
const useTmp = knownType(scope, type) == null;
|
719
|
-
const tmp = useTmp && localTmp(scope,
|
758
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
720
759
|
|
721
760
|
return [
|
722
761
|
...wasm,
|
@@ -762,7 +801,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
762
801
|
|
763
802
|
const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
764
803
|
const useTmp = knownType(scope, type) == null;
|
765
|
-
const tmp = useTmp && localTmp(scope,
|
804
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
766
805
|
|
767
806
|
return [
|
768
807
|
...wasm,
|
@@ -856,11 +895,11 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
856
895
|
// todo: if equality op and an operand is undefined, return false
|
857
896
|
// todo: niche null hell with 0
|
858
897
|
|
859
|
-
// todo: this should be dynamic but for now only static
|
860
898
|
if (knownLeft === TYPES.string || knownRight === TYPES.string) {
|
861
899
|
if (op === '+') {
|
900
|
+
// todo: this should be dynamic too but for now only static
|
862
901
|
// string concat (a + b)
|
863
|
-
return concatStrings(scope, left, right, _global, _name, assign);
|
902
|
+
return concatStrings(scope, left, right, _global, _name, assign, false);
|
864
903
|
}
|
865
904
|
|
866
905
|
// not an equality op, NaN
|
@@ -883,6 +922,33 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
883
922
|
}
|
884
923
|
}
|
885
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
|
+
|
886
952
|
let ops = operatorOpcode[valtype][op];
|
887
953
|
|
888
954
|
// some complex ops are implemented as builtin funcs
|
@@ -898,23 +964,62 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
898
964
|
]);
|
899
965
|
}
|
900
966
|
|
901
|
-
if (!ops) return todo(`operator ${op} not implemented yet
|
967
|
+
if (!ops) return todo(scope, `operator ${op} not implemented yet`, true);
|
902
968
|
|
903
969
|
if (!Array.isArray(ops)) ops = [ ops ];
|
904
970
|
ops = [ ops ];
|
905
971
|
|
906
972
|
let tmpLeft, tmpRight;
|
907
973
|
// if equal op, check if strings for compareStrings
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
|
912
|
-
return;
|
913
|
-
}
|
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
|
914
977
|
|
978
|
+
if ((op === '===' || op === '==' || op === '!==' || op === '!=') && (knownLeft == null && knownRight == null)) {
|
915
979
|
tmpLeft = localTmp(scope, '__tmpop_left');
|
916
980
|
tmpRight = localTmp(scope, '__tmpop_right');
|
917
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)
|
918
1023
|
ops.unshift(...stringOnly([
|
919
1024
|
// if left is string
|
920
1025
|
...leftType,
|
@@ -926,30 +1031,28 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
926
1031
|
...number(TYPES.string, Valtype.i32),
|
927
1032
|
[ Opcodes.i32_eq ],
|
928
1033
|
|
929
|
-
// if
|
930
|
-
[ Opcodes.
|
1034
|
+
// if both are true
|
1035
|
+
[ Opcodes.i32_and ],
|
931
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 ],
|
932
1041
|
|
933
|
-
//
|
934
|
-
// if left is not string
|
1042
|
+
// if left is bytestring
|
935
1043
|
...leftType,
|
936
|
-
...number(TYPES.
|
937
|
-
[ Opcodes.
|
1044
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1045
|
+
[ Opcodes.i32_eq ],
|
938
1046
|
|
939
|
-
// if right is
|
1047
|
+
// if right is bytestring
|
940
1048
|
...rightType,
|
941
|
-
...number(TYPES.
|
942
|
-
[ Opcodes.
|
1049
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1050
|
+
[ Opcodes.i32_eq ],
|
943
1051
|
|
944
|
-
// if
|
945
|
-
[ Opcodes.
|
1052
|
+
// if both are true
|
1053
|
+
[ Opcodes.i32_and ],
|
946
1054
|
[ Opcodes.if, Blocktype.void ],
|
947
|
-
...
|
948
|
-
[ Opcodes.br, 2 ],
|
949
|
-
[ Opcodes.end ],
|
950
|
-
|
951
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
952
|
-
// ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1055
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], true),
|
953
1056
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
954
1057
|
[ Opcodes.br, 1 ],
|
955
1058
|
[ Opcodes.end ],
|
@@ -961,7 +1064,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
961
1064
|
// endOut.push(stringOnly([ Opcodes.end ]));
|
962
1065
|
endOut.unshift(stringOnly([ Opcodes.end ]));
|
963
1066
|
// }
|
964
|
-
}
|
1067
|
+
}
|
965
1068
|
|
966
1069
|
return finalize([
|
967
1070
|
...left,
|
@@ -1037,7 +1140,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1037
1140
|
params,
|
1038
1141
|
locals,
|
1039
1142
|
returns,
|
1040
|
-
returnType:
|
1143
|
+
returnType: returnType ?? TYPES.number,
|
1041
1144
|
wasm,
|
1042
1145
|
internal: true,
|
1043
1146
|
index: currentFuncIndex++
|
@@ -1060,6 +1163,7 @@ const generateLogicExp = (scope, decl) => {
|
|
1060
1163
|
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
1061
1164
|
};
|
1062
1165
|
|
1166
|
+
// potential future ideas for nan boxing (unused):
|
1063
1167
|
// T = JS type, V = value/pointer
|
1064
1168
|
// 0bTTT
|
1065
1169
|
// qNAN: 0 11111111111 1000000000000000000000000000000000000000000000000001
|
@@ -1083,40 +1187,18 @@ const generateLogicExp = (scope, decl) => {
|
|
1083
1187
|
// 4: internal type
|
1084
1188
|
// 5: pointer
|
1085
1189
|
|
1086
|
-
const
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
object: 0x04,
|
1092
|
-
function: 0x05,
|
1093
|
-
symbol: 0x06,
|
1094
|
-
bigint: 0x07,
|
1095
|
-
|
1096
|
-
// these are not "typeof" types but tracked internally
|
1097
|
-
_array: 0x10,
|
1098
|
-
_regexp: 0x11,
|
1099
|
-
_bytestring: 0x12
|
1100
|
-
};
|
1101
|
-
|
1102
|
-
const TYPE_NAMES = {
|
1103
|
-
[TYPES.number]: 'Number',
|
1104
|
-
[TYPES.boolean]: 'Boolean',
|
1105
|
-
[TYPES.string]: 'String',
|
1106
|
-
[TYPES.undefined]: 'undefined',
|
1107
|
-
[TYPES.object]: 'Object',
|
1108
|
-
[TYPES.function]: 'Function',
|
1109
|
-
[TYPES.symbol]: 'Symbol',
|
1110
|
-
[TYPES.bigint]: 'BigInt',
|
1111
|
-
|
1112
|
-
[TYPES._array]: 'Array',
|
1113
|
-
[TYPES._regexp]: 'RegExp',
|
1114
|
-
[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;
|
1115
1195
|
};
|
1116
1196
|
|
1117
1197
|
const getType = (scope, _name) => {
|
1118
1198
|
const name = mapName(_name);
|
1119
1199
|
|
1200
|
+
// if (scope.locals[name] && !scope.locals[name + '#type']) console.log(name);
|
1201
|
+
|
1120
1202
|
if (typedInput && scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
|
1121
1203
|
if (scope.locals[name]) return [ [ Opcodes.local_get, scope.locals[name + '#type'].idx ] ];
|
1122
1204
|
|
@@ -1124,11 +1206,10 @@ const getType = (scope, _name) => {
|
|
1124
1206
|
if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
|
1125
1207
|
|
1126
1208
|
let type = TYPES.undefined;
|
1127
|
-
if (builtinVars[name]) type =
|
1209
|
+
if (builtinVars[name]) type = builtinVars[name].type ?? TYPES.number;
|
1128
1210
|
if (builtinFuncs[name] !== undefined || importedFuncs[name] !== undefined || funcIndex[name] !== undefined || internalConstrs[name] !== undefined) type = TYPES.function;
|
1129
1211
|
|
1130
|
-
if (name
|
1131
|
-
name.startsWith('__String_prototype_') && prototypeFuncs[TYPES.string][name.slice(19)]) type = TYPES.function;
|
1212
|
+
if (isExistingProtoFunc(name)) type = TYPES.function;
|
1132
1213
|
|
1133
1214
|
return number(type, Valtype.i32);
|
1134
1215
|
};
|
@@ -1151,15 +1232,16 @@ const setType = (scope, _name, type) => {
|
|
1151
1232
|
];
|
1152
1233
|
|
1153
1234
|
// throw new Error('could not find var');
|
1235
|
+
return [];
|
1154
1236
|
};
|
1155
1237
|
|
1156
1238
|
const getLastType = scope => {
|
1157
1239
|
scope.gotLastType = true;
|
1158
|
-
return [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ];
|
1240
|
+
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1159
1241
|
};
|
1160
1242
|
|
1161
1243
|
const setLastType = scope => {
|
1162
|
-
return [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ];
|
1244
|
+
return [ [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1163
1245
|
};
|
1164
1246
|
|
1165
1247
|
const getNodeType = (scope, node) => {
|
@@ -1184,7 +1266,7 @@ const getNodeType = (scope, node) => {
|
|
1184
1266
|
const name = node.callee.name;
|
1185
1267
|
if (!name) {
|
1186
1268
|
// iife
|
1187
|
-
if (scope.locals['#last_type']) return
|
1269
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1188
1270
|
|
1189
1271
|
// presume
|
1190
1272
|
// todo: warn here?
|
@@ -1198,7 +1280,7 @@ const getNodeType = (scope, node) => {
|
|
1198
1280
|
if (func.returnType) return func.returnType;
|
1199
1281
|
}
|
1200
1282
|
|
1201
|
-
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return
|
1283
|
+
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
|
1202
1284
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1203
1285
|
|
1204
1286
|
// check if this is a prototype function
|
@@ -1218,7 +1300,7 @@ const getNodeType = (scope, node) => {
|
|
1218
1300
|
return TYPES.number;
|
1219
1301
|
}
|
1220
1302
|
|
1221
|
-
if (scope.locals['#last_type']) return
|
1303
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1222
1304
|
|
1223
1305
|
// presume
|
1224
1306
|
// todo: warn here?
|
@@ -1273,6 +1355,7 @@ const getNodeType = (scope, node) => {
|
|
1273
1355
|
|
1274
1356
|
// todo: this should be dynamic but for now only static
|
1275
1357
|
if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
|
1358
|
+
if (knownLeft === TYPES._bytestring || knownRight === TYPES._bytestring) return TYPES._bytestring;
|
1276
1359
|
|
1277
1360
|
return TYPES.number;
|
1278
1361
|
|
@@ -1309,15 +1392,21 @@ const getNodeType = (scope, node) => {
|
|
1309
1392
|
|
1310
1393
|
// ts hack
|
1311
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;
|
1312
1396
|
if (scope.locals[node.object.name]?.metadata?.type === TYPES._array) return TYPES.number;
|
1313
1397
|
|
1314
|
-
if (scope.locals['#last_type']) return
|
1398
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1315
1399
|
|
1316
1400
|
// presume
|
1317
1401
|
return TYPES.number;
|
1318
1402
|
}
|
1319
1403
|
|
1320
|
-
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);
|
1321
1410
|
|
1322
1411
|
// presume
|
1323
1412
|
// todo: warn here?
|
@@ -1350,7 +1439,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1350
1439
|
return makeString(scope, decl.value, global, name);
|
1351
1440
|
|
1352
1441
|
default:
|
1353
|
-
return todo(`cannot generate literal of type ${typeof decl.value}
|
1442
|
+
return todo(scope, `cannot generate literal of type ${typeof decl.value}`, true);
|
1354
1443
|
}
|
1355
1444
|
};
|
1356
1445
|
|
@@ -1359,6 +1448,8 @@ const countLeftover = wasm => {
|
|
1359
1448
|
|
1360
1449
|
for (let i = 0; i < wasm.length; i++) {
|
1361
1450
|
const inst = wasm[i];
|
1451
|
+
if (inst[0] == null) continue;
|
1452
|
+
|
1362
1453
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
1363
1454
|
if (inst[0] === Opcodes.if) count--;
|
1364
1455
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -1470,10 +1561,21 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1470
1561
|
name = func.name;
|
1471
1562
|
}
|
1472
1563
|
|
1473
|
-
if (name === 'eval' && decl.arguments[0]
|
1564
|
+
if (name === 'eval' && decl.arguments[0]?.type === 'Literal') {
|
1474
1565
|
// literal eval hack
|
1475
|
-
const code = decl.arguments[0]
|
1476
|
-
|
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
|
+
}
|
1477
1579
|
|
1478
1580
|
const out = generate(scope, {
|
1479
1581
|
type: 'BlockStatement',
|
@@ -1487,13 +1589,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1487
1589
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1488
1590
|
out.push(
|
1489
1591
|
...getNodeType(scope, finalStatement),
|
1490
|
-
setLastType(scope)
|
1592
|
+
...setLastType(scope)
|
1491
1593
|
);
|
1492
1594
|
} else if (countLeftover(out) === 0) {
|
1493
1595
|
out.push(...number(UNDEFINED));
|
1494
1596
|
out.push(
|
1495
1597
|
...number(TYPES.undefined, Valtype.i32),
|
1496
|
-
setLastType(scope)
|
1598
|
+
...setLastType(scope)
|
1497
1599
|
);
|
1498
1600
|
}
|
1499
1601
|
|
@@ -1515,6 +1617,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1515
1617
|
|
1516
1618
|
target = { ...decl.callee };
|
1517
1619
|
target.name = spl.slice(0, -1).join('_');
|
1620
|
+
|
1621
|
+
// failed to lookup name, abort
|
1622
|
+
if (!lookupName(scope, target.name)[0]) protoName = null;
|
1518
1623
|
}
|
1519
1624
|
|
1520
1625
|
// literal.func()
|
@@ -1537,7 +1642,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1537
1642
|
Opcodes.i32_from_u,
|
1538
1643
|
|
1539
1644
|
...number(TYPES.boolean, Valtype.i32),
|
1540
|
-
setLastType(scope)
|
1645
|
+
...setLastType(scope)
|
1541
1646
|
];
|
1542
1647
|
}
|
1543
1648
|
|
@@ -1562,12 +1667,30 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1562
1667
|
// }
|
1563
1668
|
|
1564
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
|
+
|
1565
1689
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1566
1690
|
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1567
1691
|
return acc;
|
1568
1692
|
}, {});
|
1569
1693
|
|
1570
|
-
// no prototype function candidates, ignore
|
1571
1694
|
if (Object.keys(protoCands).length > 0) {
|
1572
1695
|
// use local for cached i32 length as commonly used
|
1573
1696
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
@@ -1585,7 +1708,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1585
1708
|
|
1586
1709
|
let allOptUnused = true;
|
1587
1710
|
let lengthI32CacheUsed = false;
|
1588
|
-
const protoBC = {};
|
1589
1711
|
for (const x in protoCands) {
|
1590
1712
|
const protoFunc = protoCands[x];
|
1591
1713
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
@@ -1593,7 +1715,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1593
1715
|
...RTArrayUtil.getLength(getPointer),
|
1594
1716
|
|
1595
1717
|
...number(TYPES.number, Valtype.i32),
|
1596
|
-
setLastType(scope)
|
1718
|
+
...setLastType(scope)
|
1597
1719
|
];
|
1598
1720
|
continue;
|
1599
1721
|
}
|
@@ -1630,7 +1752,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1630
1752
|
...protoOut,
|
1631
1753
|
|
1632
1754
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1633
|
-
setLastType(scope),
|
1755
|
+
...setLastType(scope),
|
1634
1756
|
[ Opcodes.end ]
|
1635
1757
|
];
|
1636
1758
|
}
|
@@ -1656,10 +1778,19 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1656
1778
|
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1657
1779
|
];
|
1658
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
|
+
}
|
1659
1790
|
}
|
1660
1791
|
|
1661
1792
|
// TODO: only allows callee as literal
|
1662
|
-
if (!name) return todo(`only literal callees (got ${decl.callee.type})`);
|
1793
|
+
if (!name) return todo(scope, `only literal callees (got ${decl.callee.type})`);
|
1663
1794
|
|
1664
1795
|
let idx = funcIndex[name] ?? importedFuncs[name];
|
1665
1796
|
if (idx === undefined && builtinFuncs[name]) {
|
@@ -1695,9 +1826,28 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1695
1826
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1696
1827
|
const wasmOps = {
|
1697
1828
|
// pointer, align, offset
|
1698
|
-
|
1829
|
+
i32_load: { imms: 2, args: 1, returns: 1 },
|
1830
|
+
// pointer, value, align, offset
|
1831
|
+
i32_store: { imms: 2, args: 2, returns: 0 },
|
1832
|
+
// pointer, align, offset
|
1833
|
+
i32_load8_u: { imms: 2, args: 1, returns: 1 },
|
1699
1834
|
// pointer, value, align, offset
|
1700
|
-
i32_store8: { imms: 2, args: 2 },
|
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
|
+
|
1849
|
+
// a, b
|
1850
|
+
i32_or: { imms: 0, args: 2, returns: 1 },
|
1701
1851
|
};
|
1702
1852
|
|
1703
1853
|
const opName = name.slice('__Porffor_wasm_'.length);
|
@@ -1706,28 +1856,32 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1706
1856
|
const op = wasmOps[opName];
|
1707
1857
|
|
1708
1858
|
const argOut = [];
|
1709
|
-
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
|
+
);
|
1710
1863
|
|
1711
1864
|
// literals only
|
1712
1865
|
const imms = decl.arguments.slice(op.args).map(x => x.value);
|
1713
1866
|
|
1714
1867
|
return [
|
1715
1868
|
...argOut,
|
1716
|
-
[ Opcodes[opName], ...imms ]
|
1869
|
+
[ Opcodes[opName], ...imms ],
|
1870
|
+
...(new Array(op.returns).fill(Opcodes.i32_from))
|
1717
1871
|
];
|
1718
1872
|
}
|
1719
1873
|
}
|
1720
1874
|
|
1721
1875
|
if (idx === undefined) {
|
1722
|
-
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function
|
1723
|
-
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);
|
1724
1878
|
}
|
1725
1879
|
|
1726
1880
|
const func = funcs.find(x => x.index === idx);
|
1727
1881
|
|
1728
1882
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1729
1883
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1730
|
-
const typedReturns = userFunc || builtinFuncs[name]?.typedReturns;
|
1884
|
+
const typedReturns = (func ? func.returnType == null : userFunc) || builtinFuncs[name]?.typedReturns;
|
1731
1885
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1732
1886
|
|
1733
1887
|
let args = decl.arguments;
|
@@ -1748,7 +1902,11 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1748
1902
|
const arg = args[i];
|
1749
1903
|
out = out.concat(generate(scope, arg));
|
1750
1904
|
|
1751
|
-
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')) {
|
1752
1910
|
out.push(Opcodes.i32_to);
|
1753
1911
|
}
|
1754
1912
|
|
@@ -1767,9 +1925,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1767
1925
|
// ...number(type, Valtype.i32),
|
1768
1926
|
// [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1769
1927
|
// );
|
1770
|
-
} else out.push(setLastType(scope));
|
1928
|
+
} else out.push(...setLastType(scope));
|
1771
1929
|
|
1772
|
-
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) {
|
1773
1931
|
out.push(Opcodes.i32_from);
|
1774
1932
|
}
|
1775
1933
|
|
@@ -1780,7 +1938,7 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1780
1938
|
// hack: basically treat this as a normal call for builtins for now
|
1781
1939
|
const name = mapName(decl.callee.name);
|
1782
1940
|
if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1783
|
-
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)})`);
|
1784
1942
|
|
1785
1943
|
return generateCall(scope, decl, _global, _name);
|
1786
1944
|
};
|
@@ -1914,8 +2072,6 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1914
2072
|
[ Opcodes.block, returns ]
|
1915
2073
|
];
|
1916
2074
|
|
1917
|
-
// todo: use br_table?
|
1918
|
-
|
1919
2075
|
for (const x in bc) {
|
1920
2076
|
if (x === 'default') continue;
|
1921
2077
|
|
@@ -1971,12 +2127,14 @@ const addVarMetadata = (scope, name, global = false, metadata = {}) => {
|
|
1971
2127
|
};
|
1972
2128
|
|
1973
2129
|
const typeAnnoToPorfType = x => {
|
1974
|
-
if (
|
1975
|
-
if (TYPES[
|
2130
|
+
if (!x) return null;
|
2131
|
+
if (TYPES[x] != null) return TYPES[x];
|
2132
|
+
if (TYPES['_' + x] != null) return TYPES['_' + x];
|
1976
2133
|
|
1977
2134
|
switch (x) {
|
1978
2135
|
case 'i32':
|
1979
2136
|
case 'i64':
|
2137
|
+
case 'f64':
|
1980
2138
|
return TYPES.number;
|
1981
2139
|
}
|
1982
2140
|
|
@@ -1987,7 +2145,7 @@ const extractTypeAnnotation = decl => {
|
|
1987
2145
|
let a = decl;
|
1988
2146
|
while (a.typeAnnotation) a = a.typeAnnotation;
|
1989
2147
|
|
1990
|
-
let type, elementType;
|
2148
|
+
let type = null, elementType = null;
|
1991
2149
|
if (a.typeName) {
|
1992
2150
|
type = a.typeName.name;
|
1993
2151
|
} else if (a.type.endsWith('Keyword')) {
|
@@ -2018,7 +2176,7 @@ const generateVar = (scope, decl) => {
|
|
2018
2176
|
for (const x of decl.declarations) {
|
2019
2177
|
const name = mapName(x.id.name);
|
2020
2178
|
|
2021
|
-
if (!name) return todo('destructuring is not supported yet');
|
2179
|
+
if (!name) return todo(scope, 'destructuring is not supported yet');
|
2022
2180
|
|
2023
2181
|
if (x.init && isFuncType(x.init.type)) {
|
2024
2182
|
// hack for let a = function () { ... }
|
@@ -2035,9 +2193,10 @@ const generateVar = (scope, decl) => {
|
|
2035
2193
|
continue; // always ignore
|
2036
2194
|
}
|
2037
2195
|
|
2038
|
-
|
2196
|
+
const typed = typedInput && x.id.typeAnnotation;
|
2197
|
+
let idx = allocVar(scope, name, global, !typed);
|
2039
2198
|
|
2040
|
-
if (
|
2199
|
+
if (typed) {
|
2041
2200
|
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
2042
2201
|
}
|
2043
2202
|
|
@@ -2055,7 +2214,8 @@ const generateVar = (scope, decl) => {
|
|
2055
2214
|
return out;
|
2056
2215
|
};
|
2057
2216
|
|
2058
|
-
|
2217
|
+
// todo: optimize this func for valueUnused
|
2218
|
+
const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
2059
2219
|
const { type, name } = decl.left;
|
2060
2220
|
|
2061
2221
|
if (type === 'ObjectPattern') {
|
@@ -2073,9 +2233,9 @@ const generateAssign = (scope, decl) => {
|
|
2073
2233
|
// hack: .length setter
|
2074
2234
|
if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
|
2075
2235
|
const name = decl.left.object.name;
|
2076
|
-
const pointer = arrays
|
2236
|
+
const pointer = scope.arrays?.get(name);
|
2077
2237
|
|
2078
|
-
const aotPointer = pointer != null;
|
2238
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2079
2239
|
|
2080
2240
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
2081
2241
|
|
@@ -2100,9 +2260,9 @@ const generateAssign = (scope, decl) => {
|
|
2100
2260
|
// arr[i]
|
2101
2261
|
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
2102
2262
|
const name = decl.left.object.name;
|
2103
|
-
const pointer = arrays
|
2263
|
+
const pointer = scope.arrays?.get(name);
|
2104
2264
|
|
2105
|
-
const aotPointer = pointer != null;
|
2265
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2106
2266
|
|
2107
2267
|
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
2108
2268
|
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
@@ -2158,7 +2318,7 @@ const generateAssign = (scope, decl) => {
|
|
2158
2318
|
];
|
2159
2319
|
}
|
2160
2320
|
|
2161
|
-
if (!name) return todo('destructuring is not supported yet');
|
2321
|
+
if (!name) return todo(scope, 'destructuring is not supported yet', true);
|
2162
2322
|
|
2163
2323
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2164
2324
|
|
@@ -2263,7 +2423,7 @@ const generateUnary = (scope, decl) => {
|
|
2263
2423
|
return out;
|
2264
2424
|
}
|
2265
2425
|
|
2266
|
-
case 'delete':
|
2426
|
+
case 'delete': {
|
2267
2427
|
let toReturn = true, toGenerate = true;
|
2268
2428
|
|
2269
2429
|
if (decl.argument.type === 'Identifier') {
|
@@ -2285,9 +2445,26 @@ const generateUnary = (scope, decl) => {
|
|
2285
2445
|
|
2286
2446
|
out.push(...number(toReturn ? 1 : 0));
|
2287
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);
|
2288
2455
|
|
2289
|
-
|
2290
|
-
|
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
|
+
}
|
2463
|
+
|
2464
|
+
const out = toGenerate ? generate(scope, decl.argument) : [];
|
2465
|
+
disposeLeftover(out);
|
2466
|
+
|
2467
|
+
out.push(...typeSwitch(scope, overrideType ?? getNodeType(scope, decl.argument), {
|
2291
2468
|
[TYPES.number]: makeString(scope, 'number', false, '#typeof_result'),
|
2292
2469
|
[TYPES.boolean]: makeString(scope, 'boolean', false, '#typeof_result'),
|
2293
2470
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
@@ -2298,27 +2475,30 @@ const generateUnary = (scope, decl) => {
|
|
2298
2475
|
|
2299
2476
|
// object and internal types
|
2300
2477
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
2301
|
-
});
|
2478
|
+
}));
|
2479
|
+
|
2480
|
+
return out;
|
2481
|
+
}
|
2302
2482
|
|
2303
2483
|
default:
|
2304
|
-
return todo(`unary operator ${decl.operator} not implemented yet
|
2484
|
+
return todo(scope, `unary operator ${decl.operator} not implemented yet`, true);
|
2305
2485
|
}
|
2306
2486
|
};
|
2307
2487
|
|
2308
|
-
const generateUpdate = (scope, decl) => {
|
2488
|
+
const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
|
2309
2489
|
const { name } = decl.argument;
|
2310
2490
|
|
2311
2491
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2312
2492
|
|
2313
2493
|
if (local === undefined) {
|
2314
|
-
return todo(`update expression with undefined variable
|
2494
|
+
return todo(scope, `update expression with undefined variable`, true);
|
2315
2495
|
}
|
2316
2496
|
|
2317
2497
|
const idx = local.idx;
|
2318
2498
|
const out = [];
|
2319
2499
|
|
2320
2500
|
out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2321
|
-
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 ]);
|
2322
2502
|
|
2323
2503
|
switch (decl.operator) {
|
2324
2504
|
case '++':
|
@@ -2331,7 +2511,7 @@ const generateUpdate = (scope, decl) => {
|
|
2331
2511
|
}
|
2332
2512
|
|
2333
2513
|
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2334
|
-
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 ]);
|
2335
2515
|
|
2336
2516
|
return out;
|
2337
2517
|
};
|
@@ -2371,7 +2551,7 @@ const generateConditional = (scope, decl) => {
|
|
2371
2551
|
// note type
|
2372
2552
|
out.push(
|
2373
2553
|
...getNodeType(scope, decl.consequent),
|
2374
|
-
setLastType(scope)
|
2554
|
+
...setLastType(scope)
|
2375
2555
|
);
|
2376
2556
|
|
2377
2557
|
out.push([ Opcodes.else ]);
|
@@ -2380,7 +2560,7 @@ const generateConditional = (scope, decl) => {
|
|
2380
2560
|
// note type
|
2381
2561
|
out.push(
|
2382
2562
|
...getNodeType(scope, decl.alternate),
|
2383
|
-
setLastType(scope)
|
2563
|
+
...setLastType(scope)
|
2384
2564
|
);
|
2385
2565
|
|
2386
2566
|
out.push([ Opcodes.end ]);
|
@@ -2394,7 +2574,7 @@ const generateFor = (scope, decl) => {
|
|
2394
2574
|
const out = [];
|
2395
2575
|
|
2396
2576
|
if (decl.init) {
|
2397
|
-
out.push(...generate(scope, decl.init));
|
2577
|
+
out.push(...generate(scope, decl.init, false, undefined, true));
|
2398
2578
|
disposeLeftover(out);
|
2399
2579
|
}
|
2400
2580
|
|
@@ -2412,7 +2592,7 @@ const generateFor = (scope, decl) => {
|
|
2412
2592
|
out.push(...generate(scope, decl.body));
|
2413
2593
|
out.push([ Opcodes.end ]);
|
2414
2594
|
|
2415
|
-
if (decl.update) out.push(...generate(scope, decl.update));
|
2595
|
+
if (decl.update) out.push(...generate(scope, decl.update, false, undefined, true));
|
2416
2596
|
|
2417
2597
|
out.push([ Opcodes.br, 1 ]);
|
2418
2598
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2440,6 +2620,36 @@ const generateWhile = (scope, decl) => {
|
|
2440
2620
|
return out;
|
2441
2621
|
};
|
2442
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
|
+
|
2443
2653
|
const generateForOf = (scope, decl) => {
|
2444
2654
|
const out = [];
|
2445
2655
|
|
@@ -2476,7 +2686,10 @@ const generateForOf = (scope, decl) => {
|
|
2476
2686
|
generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
|
2477
2687
|
}
|
2478
2688
|
|
2689
|
+
// if (!leftName) console.log(decl.left?.declarations?.[0]?.id ?? decl.left);
|
2690
|
+
|
2479
2691
|
const [ local, isGlobal ] = lookupName(scope, leftName);
|
2692
|
+
if (!local) return todo(scope, 'for of failed to get left local (probably destructure)');
|
2480
2693
|
|
2481
2694
|
depth.push('block');
|
2482
2695
|
depth.push('block');
|
@@ -2485,6 +2698,7 @@ const generateForOf = (scope, decl) => {
|
|
2485
2698
|
// hack: this is naughty and will break things!
|
2486
2699
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2487
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?
|
2488
2702
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2489
2703
|
rawElements: new Array(1)
|
2490
2704
|
}, isGlobal, leftName, true, 'i16');
|
@@ -2576,6 +2790,56 @@ const generateForOf = (scope, decl) => {
|
|
2576
2790
|
[ Opcodes.end ],
|
2577
2791
|
[ Opcodes.end ]
|
2578
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
|
+
],
|
2579
2843
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2580
2844
|
}, Blocktype.void));
|
2581
2845
|
|
@@ -2586,28 +2850,65 @@ const generateForOf = (scope, decl) => {
|
|
2586
2850
|
return out;
|
2587
2851
|
};
|
2588
2852
|
|
2853
|
+
// find the nearest loop in depth map by type
|
2589
2854
|
const getNearestLoop = () => {
|
2590
2855
|
for (let i = depth.length - 1; i >= 0; i--) {
|
2591
|
-
if (
|
2856
|
+
if (['while', 'dowhile', 'for', 'forof'].includes(depth[i])) return i;
|
2592
2857
|
}
|
2593
2858
|
|
2594
2859
|
return -1;
|
2595
2860
|
};
|
2596
2861
|
|
2597
2862
|
const generateBreak = (scope, decl) => {
|
2598
|
-
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
|
+
|
2599
2878
|
return [
|
2600
|
-
[ Opcodes.br, ...signedLEB128(
|
2879
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2601
2880
|
];
|
2602
2881
|
};
|
2603
2882
|
|
2604
2883
|
const generateContinue = (scope, decl) => {
|
2605
|
-
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
|
+
|
2606
2898
|
return [
|
2607
|
-
[ Opcodes.br, ...signedLEB128(
|
2899
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2608
2900
|
];
|
2609
2901
|
};
|
2610
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
|
+
|
2611
2912
|
const generateThrow = (scope, decl) => {
|
2612
2913
|
scope.throws = true;
|
2613
2914
|
|
@@ -2640,7 +2941,7 @@ const generateThrow = (scope, decl) => {
|
|
2640
2941
|
};
|
2641
2942
|
|
2642
2943
|
const generateTry = (scope, decl) => {
|
2643
|
-
if (decl.finalizer) return todo('try finally not implemented yet');
|
2944
|
+
if (decl.finalizer) return todo(scope, 'try finally not implemented yet');
|
2644
2945
|
|
2645
2946
|
const out = [];
|
2646
2947
|
|
@@ -2671,7 +2972,7 @@ const generateAssignPat = (scope, decl) => {
|
|
2671
2972
|
// TODO
|
2672
2973
|
// if identifier declared, use that
|
2673
2974
|
// else, use default (right)
|
2674
|
-
return todo('assignment pattern (optional arg)');
|
2975
|
+
return todo(scope, 'assignment pattern (optional arg)');
|
2675
2976
|
};
|
2676
2977
|
|
2677
2978
|
let pages = new Map();
|
@@ -2750,16 +3051,20 @@ const getAllocType = itemType => {
|
|
2750
3051
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2751
3052
|
const out = [];
|
2752
3053
|
|
3054
|
+
scope.arrays ??= new Map();
|
3055
|
+
|
2753
3056
|
let firstAssign = false;
|
2754
|
-
if (!arrays.has(name) || name === '$undeclared') {
|
3057
|
+
if (!scope.arrays.has(name) || name === '$undeclared') {
|
2755
3058
|
firstAssign = true;
|
2756
3059
|
|
2757
3060
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2758
3061
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2759
|
-
|
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);
|
2760
3065
|
}
|
2761
3066
|
|
2762
|
-
const pointer = arrays.get(name);
|
3067
|
+
const pointer = scope.arrays.get(name);
|
2763
3068
|
|
2764
3069
|
const useRawElements = !!decl.rawElements;
|
2765
3070
|
const elements = useRawElements ? decl.rawElements : decl.elements;
|
@@ -2845,20 +3150,29 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
2845
3150
|
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2846
3151
|
};
|
2847
3152
|
|
2848
|
-
let arrays = new Map();
|
2849
3153
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
2850
3154
|
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
2851
3155
|
};
|
2852
3156
|
|
2853
3157
|
export const generateMember = (scope, decl, _global, _name) => {
|
2854
3158
|
const name = decl.object.name;
|
2855
|
-
const pointer = arrays
|
3159
|
+
const pointer = scope.arrays?.get(name);
|
2856
3160
|
|
2857
|
-
const aotPointer = pointer != null;
|
3161
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2858
3162
|
|
2859
3163
|
// hack: .length
|
2860
3164
|
if (decl.property.name === 'length') {
|
2861
|
-
|
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
|
+
|
2862
3176
|
return [
|
2863
3177
|
...(aotPointer ? number(0, Valtype.i32) : [
|
2864
3178
|
...generate(scope, decl.object),
|
@@ -2902,7 +3216,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2902
3216
|
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2903
3217
|
|
2904
3218
|
...number(TYPES.number, Valtype.i32),
|
2905
|
-
setLastType(scope)
|
3219
|
+
...setLastType(scope)
|
2906
3220
|
],
|
2907
3221
|
|
2908
3222
|
[TYPES.string]: [
|
@@ -2934,7 +3248,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2934
3248
|
...number(newPointer),
|
2935
3249
|
|
2936
3250
|
...number(TYPES.string, Valtype.i32),
|
2937
|
-
setLastType(scope)
|
3251
|
+
...setLastType(scope)
|
2938
3252
|
],
|
2939
3253
|
[TYPES._bytestring]: [
|
2940
3254
|
// setup new/out array
|
@@ -2953,19 +3267,19 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2953
3267
|
]),
|
2954
3268
|
|
2955
3269
|
// load current string ind {arg}
|
2956
|
-
[ Opcodes.i32_load8_u,
|
3270
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2957
3271
|
|
2958
3272
|
// store to new string ind 0
|
2959
|
-
[ Opcodes.i32_store8,
|
3273
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2960
3274
|
|
2961
3275
|
// return new string (page)
|
2962
3276
|
...number(newPointer),
|
2963
3277
|
|
2964
3278
|
...number(TYPES._bytestring, Valtype.i32),
|
2965
|
-
setLastType(scope)
|
3279
|
+
...setLastType(scope)
|
2966
3280
|
],
|
2967
3281
|
|
2968
|
-
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)
|
2969
3283
|
});
|
2970
3284
|
};
|
2971
3285
|
|
@@ -2975,28 +3289,36 @@ const objectHack = node => {
|
|
2975
3289
|
if (!node) return node;
|
2976
3290
|
|
2977
3291
|
if (node.type === 'MemberExpression') {
|
2978
|
-
|
3292
|
+
const out = (() => {
|
3293
|
+
if (node.computed || node.optional) return;
|
2979
3294
|
|
2980
|
-
|
3295
|
+
let objectName = node.object.name;
|
2981
3296
|
|
2982
|
-
|
2983
|
-
|
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;
|
2984
3300
|
|
2985
|
-
|
3301
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
2986
3302
|
|
2987
|
-
|
2988
|
-
|
3303
|
+
// if .length, give up (hack within a hack!)
|
3304
|
+
if (node.property.name === 'length') {
|
3305
|
+
node.object = objectHack(node.object);
|
3306
|
+
return;
|
3307
|
+
}
|
2989
3308
|
|
2990
|
-
|
2991
|
-
|
3309
|
+
// no object name, give up
|
3310
|
+
if (!objectName) return;
|
2992
3311
|
|
2993
|
-
|
2994
|
-
|
3312
|
+
const name = '__' + objectName + '_' + node.property.name;
|
3313
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2995
3314
|
|
2996
|
-
|
2997
|
-
|
2998
|
-
|
2999
|
-
|
3315
|
+
return {
|
3316
|
+
type: 'Identifier',
|
3317
|
+
name
|
3318
|
+
};
|
3319
|
+
})();
|
3320
|
+
|
3321
|
+
if (out) return out;
|
3000
3322
|
}
|
3001
3323
|
|
3002
3324
|
for (const x in node) {
|
@@ -3010,8 +3332,8 @@ const objectHack = node => {
|
|
3010
3332
|
};
|
3011
3333
|
|
3012
3334
|
const generateFunc = (scope, decl) => {
|
3013
|
-
if (decl.async) return todo('async functions are not supported');
|
3014
|
-
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');
|
3015
3337
|
|
3016
3338
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3017
3339
|
const params = decl.params ?? [];
|
@@ -3027,6 +3349,11 @@ const generateFunc = (scope, decl) => {
|
|
3027
3349
|
name
|
3028
3350
|
};
|
3029
3351
|
|
3352
|
+
if (typedInput && decl.returnType) {
|
3353
|
+
innerScope.returnType = extractTypeAnnotation(decl.returnType).type;
|
3354
|
+
innerScope.returns = [ valtypeBinary ];
|
3355
|
+
}
|
3356
|
+
|
3030
3357
|
for (let i = 0; i < params.length; i++) {
|
3031
3358
|
allocVar(innerScope, params[i].name, false);
|
3032
3359
|
|
@@ -3089,16 +3416,6 @@ const generateCode = (scope, decl) => {
|
|
3089
3416
|
};
|
3090
3417
|
|
3091
3418
|
const internalConstrs = {
|
3092
|
-
Boolean: {
|
3093
|
-
generate: (scope, decl) => {
|
3094
|
-
if (decl.arguments.length === 0) return number(0);
|
3095
|
-
|
3096
|
-
// should generate/run all args
|
3097
|
-
return truthy(scope, generate(scope, decl.arguments[0]), getNodeType(scope, decl.arguments[0]), false, false);
|
3098
|
-
},
|
3099
|
-
type: TYPES.boolean
|
3100
|
-
},
|
3101
|
-
|
3102
3419
|
Array: {
|
3103
3420
|
generate: (scope, decl, global, name) => {
|
3104
3421
|
// new Array(i0, i1, ...)
|
@@ -3116,7 +3433,7 @@ const internalConstrs = {
|
|
3116
3433
|
|
3117
3434
|
// todo: check in wasm instead of here
|
3118
3435
|
const literalValue = arg.value ?? 0;
|
3119
|
-
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);
|
3120
3437
|
|
3121
3438
|
return [
|
3122
3439
|
...number(0, Valtype.i32),
|
@@ -3127,7 +3444,8 @@ const internalConstrs = {
|
|
3127
3444
|
...number(pointer)
|
3128
3445
|
];
|
3129
3446
|
},
|
3130
|
-
type: TYPES._array
|
3447
|
+
type: TYPES._array,
|
3448
|
+
length: 1
|
3131
3449
|
},
|
3132
3450
|
|
3133
3451
|
__Array_of: {
|
@@ -3139,7 +3457,94 @@ const internalConstrs = {
|
|
3139
3457
|
}, global, name);
|
3140
3458
|
},
|
3141
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,
|
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,
|
3142
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
|
3143
3548
|
}
|
3144
3549
|
};
|
3145
3550
|
|
@@ -3168,7 +3573,6 @@ export default program => {
|
|
3168
3573
|
funcs = [];
|
3169
3574
|
funcIndex = {};
|
3170
3575
|
depth = [];
|
3171
|
-
arrays = new Map();
|
3172
3576
|
pages = new Map();
|
3173
3577
|
data = [];
|
3174
3578
|
currentFuncIndex = importedFuncs.length;
|
@@ -3182,6 +3586,10 @@ export default program => {
|
|
3182
3586
|
|
3183
3587
|
const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
|
3184
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
|
+
|
3185
3593
|
// set generic opcodes for current valtype
|
3186
3594
|
Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
|
3187
3595
|
Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
|
@@ -3190,10 +3598,10 @@ export default program => {
|
|
3190
3598
|
Opcodes.add = [ Opcodes.i32_add, Opcodes.i64_add, Opcodes.f64_add ][valtypeInd];
|
3191
3599
|
Opcodes.sub = [ Opcodes.i32_sub, Opcodes.i64_sub, Opcodes.f64_sub ][valtypeInd];
|
3192
3600
|
|
3193
|
-
Opcodes.i32_to = [ [
|
3194
|
-
Opcodes.i32_to_u = [ [
|
3195
|
-
Opcodes.i32_from = [ [
|
3196
|
-
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];
|
3197
3605
|
|
3198
3606
|
Opcodes.load = [ Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load ][valtypeInd];
|
3199
3607
|
Opcodes.store = [ Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store ][valtypeInd];
|
@@ -3206,10 +3614,6 @@ export default program => {
|
|
3206
3614
|
|
3207
3615
|
program.id = { name: 'main' };
|
3208
3616
|
|
3209
|
-
globalThis.pageSize = PageSize;
|
3210
|
-
const pageSizeOpt = process.argv.find(x => x.startsWith('-page-size='));
|
3211
|
-
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3212
|
-
|
3213
3617
|
const scope = {
|
3214
3618
|
locals: {},
|
3215
3619
|
localInd: 0
|
@@ -3220,7 +3624,7 @@ export default program => {
|
|
3220
3624
|
body: program.body
|
3221
3625
|
};
|
3222
3626
|
|
3223
|
-
if (Prefs.astLog) console.log(program.body.body);
|
3627
|
+
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3224
3628
|
|
3225
3629
|
generateFunc(scope, program);
|
3226
3630
|
|
@@ -3237,7 +3641,11 @@ export default program => {
|
|
3237
3641
|
}
|
3238
3642
|
|
3239
3643
|
if (lastInst[0] === Opcodes.end || lastInst[0] === Opcodes.local_set || lastInst[0] === Opcodes.global_set) {
|
3240
|
-
|
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
|
+
}
|
3241
3649
|
}
|
3242
3650
|
|
3243
3651
|
if (lastInst[0] === Opcodes.call) {
|