porffor 0.2.0-4b72c49 → 0.2.0-4d189b5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +248 -0
- package/LICENSE +20 -20
- package/README.md +129 -84
- 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} +59 -12
- package/compiler/builtins/annexb_string.js +72 -0
- package/compiler/builtins/annexb_string.ts +18 -0
- package/compiler/builtins/array.ts +145 -0
- package/compiler/builtins/base64.ts +7 -84
- package/compiler/builtins/crypto.ts +120 -0
- package/compiler/builtins/date.ts +2071 -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 +43 -7
- package/compiler/builtins/string.ts +1055 -0
- package/compiler/builtins/tostring.ts +45 -0
- package/compiler/builtins.js +403 -110
- package/compiler/{codeGen.js → codegen.js} +820 -309
- package/compiler/decompile.js +0 -1
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +108 -10
- package/compiler/generated_builtins.js +1470 -4
- package/compiler/index.js +16 -14
- package/compiler/log.js +6 -3
- package/compiler/opt.js +23 -22
- package/compiler/parse.js +30 -22
- package/compiler/precompile.js +25 -26
- package/compiler/prefs.js +7 -6
- package/compiler/prototype.js +2 -18
- package/compiler/types.js +37 -0
- package/compiler/wasmSpec.js +18 -6
- package/compiler/wrap.js +51 -47
- package/package.json +9 -5
- package/porf +2 -0
- package/rhemyn/compile.js +44 -26
- package/rhemyn/parse.js +322 -320
- package/rhemyn/test/parse.js +58 -58
- package/runner/compare.js +34 -34
- package/runner/debug.js +122 -0
- package/runner/index.js +74 -11
- package/runner/profiler.js +102 -0
- package/runner/repl.js +42 -9
- package/runner/sizes.js +37 -37
- package/demo.js +0 -15
- 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/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
|
|
@@ -186,7 +201,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
186
201
|
}
|
187
202
|
|
188
203
|
let inst = Opcodes[asm[0].replace('.', '_')];
|
189
|
-
if (
|
204
|
+
if (inst == null) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
190
205
|
|
191
206
|
if (!Array.isArray(inst)) inst = [ inst ];
|
192
207
|
const immediates = asm.slice(1).map(x => {
|
@@ -195,40 +210,49 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
195
210
|
return int;
|
196
211
|
});
|
197
212
|
|
198
|
-
out.push([ ...inst, ...immediates ]);
|
213
|
+
out.push([ ...inst, ...immediates.flatMap(x => signedLEB128(x)) ]);
|
199
214
|
}
|
200
215
|
|
201
216
|
return out;
|
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',
|
@@ -290,7 +314,10 @@ const generateIdent = (scope, decl) => {
|
|
290
314
|
|
291
315
|
if (Object.hasOwn(builtinVars, name)) {
|
292
316
|
if (builtinVars[name].floatOnly && valtype[0] === 'i') throw new Error(`Cannot use ${unhackName(name)} with integer valtype`);
|
293
|
-
|
317
|
+
|
318
|
+
let wasm = builtinVars[name];
|
319
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name });
|
320
|
+
return wasm.slice();
|
294
321
|
}
|
295
322
|
|
296
323
|
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
@@ -298,6 +325,11 @@ const generateIdent = (scope, decl) => {
|
|
298
325
|
return number(1);
|
299
326
|
}
|
300
327
|
|
328
|
+
if (isExistingProtoFunc(name)) {
|
329
|
+
// todo: return an actual something
|
330
|
+
return number(1);
|
331
|
+
}
|
332
|
+
|
301
333
|
if (local?.idx === undefined) {
|
302
334
|
// no local var with name
|
303
335
|
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
@@ -328,14 +360,18 @@ const generateReturn = (scope, decl) => {
|
|
328
360
|
// just bare "return"
|
329
361
|
return [
|
330
362
|
...number(UNDEFINED), // "undefined" if func returns
|
331
|
-
...
|
363
|
+
...(scope.returnType != null ? [] : [
|
364
|
+
...number(TYPES.undefined, Valtype.i32) // type undefined
|
365
|
+
]),
|
332
366
|
[ Opcodes.return ]
|
333
367
|
];
|
334
368
|
}
|
335
369
|
|
336
370
|
return [
|
337
371
|
...generate(scope, decl.argument),
|
338
|
-
...
|
372
|
+
...(scope.returnType != null ? [] : [
|
373
|
+
...getNodeType(scope, decl.argument)
|
374
|
+
]),
|
339
375
|
[ Opcodes.return ]
|
340
376
|
];
|
341
377
|
};
|
@@ -349,7 +385,8 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
349
385
|
return idx;
|
350
386
|
};
|
351
387
|
|
352
|
-
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);
|
353
390
|
|
354
391
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
355
392
|
const checks = {
|
@@ -358,7 +395,7 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
358
395
|
'??': nullish
|
359
396
|
};
|
360
397
|
|
361
|
-
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);
|
362
399
|
|
363
400
|
// generic structure for {a} OP {b}
|
364
401
|
// -->
|
@@ -366,8 +403,8 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
366
403
|
|
367
404
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
368
405
|
// (like if we are in an if condition - very common)
|
369
|
-
const leftIsInt =
|
370
|
-
const rightIsInt =
|
406
|
+
const leftIsInt = isFloatToIntOp(left[left.length - 1]);
|
407
|
+
const rightIsInt = isFloatToIntOp(right[right.length - 1]);
|
371
408
|
|
372
409
|
const canInt = leftIsInt && rightIsInt;
|
373
410
|
|
@@ -384,12 +421,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
384
421
|
...right,
|
385
422
|
// note type
|
386
423
|
...rightType,
|
387
|
-
setLastType(scope),
|
424
|
+
...setLastType(scope),
|
388
425
|
[ Opcodes.else ],
|
389
426
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
390
427
|
// note type
|
391
428
|
...leftType,
|
392
|
-
setLastType(scope),
|
429
|
+
...setLastType(scope),
|
393
430
|
[ Opcodes.end ],
|
394
431
|
Opcodes.i32_from
|
395
432
|
];
|
@@ -403,17 +440,17 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
403
440
|
...right,
|
404
441
|
// note type
|
405
442
|
...rightType,
|
406
|
-
setLastType(scope),
|
443
|
+
...setLastType(scope),
|
407
444
|
[ Opcodes.else ],
|
408
445
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
409
446
|
// note type
|
410
447
|
...leftType,
|
411
|
-
setLastType(scope),
|
448
|
+
...setLastType(scope),
|
412
449
|
[ Opcodes.end ]
|
413
450
|
];
|
414
451
|
};
|
415
452
|
|
416
|
-
const concatStrings = (scope, left, right, global, name, assign) => {
|
453
|
+
const concatStrings = (scope, left, right, global, name, assign = false, bytestrings = false) => {
|
417
454
|
// todo: this should be rewritten into a built-in/func: String.prototype.concat
|
418
455
|
// todo: convert left and right to strings if not
|
419
456
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -423,8 +460,8 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
423
460
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
424
461
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
425
462
|
|
426
|
-
if (assign) {
|
427
|
-
const pointer = arrays
|
463
|
+
if (assign && Prefs.aotPointerOpt) {
|
464
|
+
const pointer = scope.arrays?.get(name ?? '$undeclared');
|
428
465
|
|
429
466
|
return [
|
430
467
|
// setup right
|
@@ -449,11 +486,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
449
486
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
|
450
487
|
|
451
488
|
// copy right
|
452
|
-
// dst = out pointer + length size + current length *
|
489
|
+
// dst = out pointer + length size + current length * sizeof valtype
|
453
490
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
454
491
|
|
455
492
|
[ Opcodes.local_get, leftLength ],
|
456
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
493
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
457
494
|
[ Opcodes.i32_mul ],
|
458
495
|
[ Opcodes.i32_add ],
|
459
496
|
|
@@ -462,9 +499,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
462
499
|
...number(ValtypeSize.i32, Valtype.i32),
|
463
500
|
[ Opcodes.i32_add ],
|
464
501
|
|
465
|
-
// size = right length *
|
502
|
+
// size = right length * sizeof valtype
|
466
503
|
[ Opcodes.local_get, rightLength ],
|
467
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
504
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
468
505
|
[ Opcodes.i32_mul ],
|
469
506
|
|
470
507
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -522,11 +559,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
522
559
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
523
560
|
|
524
561
|
// copy right
|
525
|
-
// dst = out pointer + length size + left length *
|
562
|
+
// dst = out pointer + length size + left length * sizeof valtype
|
526
563
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
527
564
|
|
528
565
|
[ Opcodes.local_get, leftLength ],
|
529
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
566
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
530
567
|
[ Opcodes.i32_mul ],
|
531
568
|
[ Opcodes.i32_add ],
|
532
569
|
|
@@ -535,9 +572,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
535
572
|
...number(ValtypeSize.i32, Valtype.i32),
|
536
573
|
[ Opcodes.i32_add ],
|
537
574
|
|
538
|
-
// size = right length *
|
575
|
+
// size = right length * sizeof valtype
|
539
576
|
[ Opcodes.local_get, rightLength ],
|
540
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
577
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
541
578
|
[ Opcodes.i32_mul ],
|
542
579
|
|
543
580
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -547,7 +584,7 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
547
584
|
];
|
548
585
|
};
|
549
586
|
|
550
|
-
const compareStrings = (scope, left, right) => {
|
587
|
+
const compareStrings = (scope, left, right, bytestrings = false) => {
|
551
588
|
// todo: this should be rewritten into a func
|
552
589
|
// todo: convert left and right to strings if not
|
553
590
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -556,7 +593,6 @@ const compareStrings = (scope, left, right) => {
|
|
556
593
|
const leftPointer = localTmp(scope, 'compare_left_pointer', Valtype.i32);
|
557
594
|
const leftLength = localTmp(scope, 'compare_left_length', Valtype.i32);
|
558
595
|
const rightPointer = localTmp(scope, 'compare_right_pointer', Valtype.i32);
|
559
|
-
const rightLength = localTmp(scope, 'compare_right_length', Valtype.i32);
|
560
596
|
|
561
597
|
const index = localTmp(scope, 'compare_index', Valtype.i32);
|
562
598
|
const indexEnd = localTmp(scope, 'compare_index_end', Valtype.i32);
|
@@ -584,7 +620,6 @@ const compareStrings = (scope, left, right) => {
|
|
584
620
|
|
585
621
|
[ Opcodes.local_get, rightPointer ],
|
586
622
|
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
587
|
-
[ Opcodes.local_tee, rightLength ],
|
588
623
|
|
589
624
|
// fast path: check leftLength != rightLength
|
590
625
|
[ Opcodes.i32_ne ],
|
@@ -599,11 +634,13 @@ const compareStrings = (scope, left, right) => {
|
|
599
634
|
...number(0, Valtype.i32),
|
600
635
|
[ Opcodes.local_set, index ],
|
601
636
|
|
602
|
-
// setup index end as length * sizeof
|
637
|
+
// setup index end as length * sizeof valtype (1 for bytestring, 2 for string)
|
603
638
|
// we do this instead of having to do mul/div each iter for perf™
|
604
639
|
[ Opcodes.local_get, leftLength ],
|
605
|
-
...
|
606
|
-
|
640
|
+
...(bytestrings ? [] : [
|
641
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
642
|
+
[ Opcodes.i32_mul ],
|
643
|
+
]),
|
607
644
|
[ Opcodes.local_set, indexEnd ],
|
608
645
|
|
609
646
|
// iterate over each char and check if eq
|
@@ -613,13 +650,17 @@ const compareStrings = (scope, left, right) => {
|
|
613
650
|
[ Opcodes.local_get, index ],
|
614
651
|
[ Opcodes.local_get, leftPointer ],
|
615
652
|
[ Opcodes.i32_add ],
|
616
|
-
|
653
|
+
bytestrings ?
|
654
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
655
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
617
656
|
|
618
657
|
// fetch right
|
619
658
|
[ Opcodes.local_get, index ],
|
620
659
|
[ Opcodes.local_get, rightPointer ],
|
621
660
|
[ Opcodes.i32_add ],
|
622
|
-
|
661
|
+
bytestrings ?
|
662
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
663
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
623
664
|
|
624
665
|
// not equal, "return" false
|
625
666
|
[ Opcodes.i32_ne ],
|
@@ -628,13 +669,13 @@ const compareStrings = (scope, left, right) => {
|
|
628
669
|
[ Opcodes.br, 2 ],
|
629
670
|
[ Opcodes.end ],
|
630
671
|
|
631
|
-
// index += sizeof
|
672
|
+
// index += sizeof valtype (1 for bytestring, 2 for string)
|
632
673
|
[ Opcodes.local_get, index ],
|
633
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
674
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
634
675
|
[ Opcodes.i32_add ],
|
635
676
|
[ Opcodes.local_tee, index ],
|
636
677
|
|
637
|
-
// if index != index end (length * sizeof
|
678
|
+
// if index != index end (length * sizeof valtype), loop
|
638
679
|
[ Opcodes.local_get, indexEnd ],
|
639
680
|
[ Opcodes.i32_ne ],
|
640
681
|
[ Opcodes.br_if, 0 ],
|
@@ -655,13 +696,14 @@ const compareStrings = (scope, left, right) => {
|
|
655
696
|
};
|
656
697
|
|
657
698
|
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
658
|
-
if (
|
699
|
+
if (isFloatToIntOp(wasm[wasm.length - 1])) return [
|
659
700
|
...wasm,
|
660
701
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
661
702
|
];
|
703
|
+
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
662
704
|
|
663
705
|
const useTmp = knownType(scope, type) == null;
|
664
|
-
const tmp = useTmp && localTmp(scope,
|
706
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
665
707
|
|
666
708
|
const def = [
|
667
709
|
// if value != 0
|
@@ -713,7 +755,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
713
755
|
|
714
756
|
const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
715
757
|
const useTmp = knownType(scope, type) == null;
|
716
|
-
const tmp = useTmp && localTmp(scope,
|
758
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
717
759
|
|
718
760
|
return [
|
719
761
|
...wasm,
|
@@ -759,7 +801,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
759
801
|
|
760
802
|
const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
761
803
|
const useTmp = knownType(scope, type) == null;
|
762
|
-
const tmp = useTmp && localTmp(scope,
|
804
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
763
805
|
|
764
806
|
return [
|
765
807
|
...wasm,
|
@@ -853,11 +895,11 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
853
895
|
// todo: if equality op and an operand is undefined, return false
|
854
896
|
// todo: niche null hell with 0
|
855
897
|
|
856
|
-
// todo: this should be dynamic but for now only static
|
857
898
|
if (knownLeft === TYPES.string || knownRight === TYPES.string) {
|
858
899
|
if (op === '+') {
|
900
|
+
// todo: this should be dynamic too but for now only static
|
859
901
|
// string concat (a + b)
|
860
|
-
return concatStrings(scope, left, right, _global, _name, assign);
|
902
|
+
return concatStrings(scope, left, right, _global, _name, assign, false);
|
861
903
|
}
|
862
904
|
|
863
905
|
// not an equality op, NaN
|
@@ -880,6 +922,33 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
880
922
|
}
|
881
923
|
}
|
882
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
|
+
|
883
952
|
let ops = operatorOpcode[valtype][op];
|
884
953
|
|
885
954
|
// some complex ops are implemented as builtin funcs
|
@@ -895,23 +964,62 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
895
964
|
]);
|
896
965
|
}
|
897
966
|
|
898
|
-
if (!ops) return todo(`operator ${op} not implemented yet
|
967
|
+
if (!ops) return todo(scope, `operator ${op} not implemented yet`, true);
|
899
968
|
|
900
969
|
if (!Array.isArray(ops)) ops = [ ops ];
|
901
970
|
ops = [ ops ];
|
902
971
|
|
903
972
|
let tmpLeft, tmpRight;
|
904
973
|
// if equal op, check if strings for compareStrings
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
|
909
|
-
return;
|
910
|
-
}
|
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
|
911
977
|
|
978
|
+
if ((op === '===' || op === '==' || op === '!==' || op === '!=') && (knownLeft == null && knownRight == null)) {
|
912
979
|
tmpLeft = localTmp(scope, '__tmpop_left');
|
913
980
|
tmpRight = localTmp(scope, '__tmpop_right');
|
914
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)
|
915
1023
|
ops.unshift(...stringOnly([
|
916
1024
|
// if left is string
|
917
1025
|
...leftType,
|
@@ -923,30 +1031,28 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
923
1031
|
...number(TYPES.string, Valtype.i32),
|
924
1032
|
[ Opcodes.i32_eq ],
|
925
1033
|
|
926
|
-
// if
|
927
|
-
[ Opcodes.
|
1034
|
+
// if both are true
|
1035
|
+
[ Opcodes.i32_and ],
|
928
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 ],
|
929
1041
|
|
930
|
-
//
|
931
|
-
// if left is not string
|
1042
|
+
// if left is bytestring
|
932
1043
|
...leftType,
|
933
|
-
...number(TYPES.
|
934
|
-
[ Opcodes.
|
1044
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1045
|
+
[ Opcodes.i32_eq ],
|
935
1046
|
|
936
|
-
// if right is
|
1047
|
+
// if right is bytestring
|
937
1048
|
...rightType,
|
938
|
-
...number(TYPES.
|
939
|
-
[ Opcodes.
|
1049
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1050
|
+
[ Opcodes.i32_eq ],
|
940
1051
|
|
941
|
-
// if
|
942
|
-
[ Opcodes.
|
1052
|
+
// if both are true
|
1053
|
+
[ Opcodes.i32_and ],
|
943
1054
|
[ Opcodes.if, Blocktype.void ],
|
944
|
-
...
|
945
|
-
[ Opcodes.br, 2 ],
|
946
|
-
[ Opcodes.end ],
|
947
|
-
|
948
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
949
|
-
// ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1055
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], true),
|
950
1056
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
951
1057
|
[ Opcodes.br, 1 ],
|
952
1058
|
[ Opcodes.end ],
|
@@ -958,7 +1064,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
958
1064
|
// endOut.push(stringOnly([ Opcodes.end ]));
|
959
1065
|
endOut.unshift(stringOnly([ Opcodes.end ]));
|
960
1066
|
// }
|
961
|
-
}
|
1067
|
+
}
|
962
1068
|
|
963
1069
|
return finalize([
|
964
1070
|
...left,
|
@@ -977,6 +1083,21 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
977
1083
|
return out;
|
978
1084
|
};
|
979
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
|
+
|
980
1101
|
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
981
1102
|
const existing = funcs.find(x => x.name === name);
|
982
1103
|
if (existing) return existing;
|
@@ -995,28 +1116,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
995
1116
|
data.push(copy);
|
996
1117
|
}
|
997
1118
|
|
998
|
-
if (typeof wasm === 'function') {
|
999
|
-
const scope = {
|
1000
|
-
name,
|
1001
|
-
params,
|
1002
|
-
locals,
|
1003
|
-
returns,
|
1004
|
-
localInd: allLocals.length,
|
1005
|
-
};
|
1006
|
-
|
1007
|
-
wasm = wasm(scope, {
|
1008
|
-
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage,
|
1009
|
-
builtin: name => {
|
1010
|
-
let idx = funcIndex[name] ?? importedFuncs[name];
|
1011
|
-
if (idx === undefined && builtinFuncs[name]) {
|
1012
|
-
includeBuiltin(scope, name);
|
1013
|
-
idx = funcIndex[name];
|
1014
|
-
}
|
1015
|
-
|
1016
|
-
return idx;
|
1017
|
-
}
|
1018
|
-
});
|
1019
|
-
}
|
1119
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name, params, locals, returns, localInd: allLocals.length });
|
1020
1120
|
|
1021
1121
|
let baseGlobalIdx, i = 0;
|
1022
1122
|
for (const type of globalTypes) {
|
@@ -1040,7 +1140,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1040
1140
|
params,
|
1041
1141
|
locals,
|
1042
1142
|
returns,
|
1043
|
-
returnType:
|
1143
|
+
returnType: returnType ?? TYPES.number,
|
1044
1144
|
wasm,
|
1045
1145
|
internal: true,
|
1046
1146
|
index: currentFuncIndex++
|
@@ -1063,6 +1163,7 @@ const generateLogicExp = (scope, decl) => {
|
|
1063
1163
|
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
1064
1164
|
};
|
1065
1165
|
|
1166
|
+
// potential future ideas for nan boxing (unused):
|
1066
1167
|
// T = JS type, V = value/pointer
|
1067
1168
|
// 0bTTT
|
1068
1169
|
// qNAN: 0 11111111111 1000000000000000000000000000000000000000000000000001
|
@@ -1086,40 +1187,18 @@ const generateLogicExp = (scope, decl) => {
|
|
1086
1187
|
// 4: internal type
|
1087
1188
|
// 5: pointer
|
1088
1189
|
|
1089
|
-
const
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
object: 0x04,
|
1095
|
-
function: 0x05,
|
1096
|
-
symbol: 0x06,
|
1097
|
-
bigint: 0x07,
|
1098
|
-
|
1099
|
-
// these are not "typeof" types but tracked internally
|
1100
|
-
_array: 0x10,
|
1101
|
-
_regexp: 0x11,
|
1102
|
-
_bytestring: 0x12
|
1103
|
-
};
|
1104
|
-
|
1105
|
-
const TYPE_NAMES = {
|
1106
|
-
[TYPES.number]: 'Number',
|
1107
|
-
[TYPES.boolean]: 'Boolean',
|
1108
|
-
[TYPES.string]: 'String',
|
1109
|
-
[TYPES.undefined]: 'undefined',
|
1110
|
-
[TYPES.object]: 'Object',
|
1111
|
-
[TYPES.function]: 'Function',
|
1112
|
-
[TYPES.symbol]: 'Symbol',
|
1113
|
-
[TYPES.bigint]: 'BigInt',
|
1114
|
-
|
1115
|
-
[TYPES._array]: 'Array',
|
1116
|
-
[TYPES._regexp]: 'RegExp',
|
1117
|
-
[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;
|
1118
1195
|
};
|
1119
1196
|
|
1120
1197
|
const getType = (scope, _name) => {
|
1121
1198
|
const name = mapName(_name);
|
1122
1199
|
|
1200
|
+
// if (scope.locals[name] && !scope.locals[name + '#type']) console.log(name);
|
1201
|
+
|
1123
1202
|
if (typedInput && scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
|
1124
1203
|
if (scope.locals[name]) return [ [ Opcodes.local_get, scope.locals[name + '#type'].idx ] ];
|
1125
1204
|
|
@@ -1127,11 +1206,10 @@ const getType = (scope, _name) => {
|
|
1127
1206
|
if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
|
1128
1207
|
|
1129
1208
|
let type = TYPES.undefined;
|
1130
|
-
if (builtinVars[name]) type =
|
1209
|
+
if (builtinVars[name]) type = builtinVars[name].type ?? TYPES.number;
|
1131
1210
|
if (builtinFuncs[name] !== undefined || importedFuncs[name] !== undefined || funcIndex[name] !== undefined || internalConstrs[name] !== undefined) type = TYPES.function;
|
1132
1211
|
|
1133
|
-
if (name
|
1134
|
-
name.startsWith('__String_prototype_') && prototypeFuncs[TYPES.string][name.slice(19)]) type = TYPES.function;
|
1212
|
+
if (isExistingProtoFunc(name)) type = TYPES.function;
|
1135
1213
|
|
1136
1214
|
return number(type, Valtype.i32);
|
1137
1215
|
};
|
@@ -1154,15 +1232,16 @@ const setType = (scope, _name, type) => {
|
|
1154
1232
|
];
|
1155
1233
|
|
1156
1234
|
// throw new Error('could not find var');
|
1235
|
+
return [];
|
1157
1236
|
};
|
1158
1237
|
|
1159
1238
|
const getLastType = scope => {
|
1160
1239
|
scope.gotLastType = true;
|
1161
|
-
return [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ];
|
1240
|
+
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1162
1241
|
};
|
1163
1242
|
|
1164
1243
|
const setLastType = scope => {
|
1165
|
-
return [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ];
|
1244
|
+
return [ [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1166
1245
|
};
|
1167
1246
|
|
1168
1247
|
const getNodeType = (scope, node) => {
|
@@ -1187,13 +1266,25 @@ const getNodeType = (scope, node) => {
|
|
1187
1266
|
const name = node.callee.name;
|
1188
1267
|
if (!name) {
|
1189
1268
|
// iife
|
1190
|
-
if (scope.locals['#last_type']) return
|
1269
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1191
1270
|
|
1192
1271
|
// presume
|
1193
1272
|
// todo: warn here?
|
1194
1273
|
return TYPES.number;
|
1195
1274
|
}
|
1196
1275
|
|
1276
|
+
if (node.type === 'NewExpression' && builtinFuncs[name + '$constructor']) {
|
1277
|
+
if (builtinFuncs[name + '$constructor'].typedReturns) {
|
1278
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1279
|
+
|
1280
|
+
// presume
|
1281
|
+
// todo: warn here?
|
1282
|
+
return TYPES.number;
|
1283
|
+
}
|
1284
|
+
|
1285
|
+
return builtinFuncs[name + '$constructor'].returnType ?? TYPES.number;
|
1286
|
+
}
|
1287
|
+
|
1197
1288
|
const func = funcs.find(x => x.name === name);
|
1198
1289
|
|
1199
1290
|
if (func) {
|
@@ -1201,7 +1292,7 @@ const getNodeType = (scope, node) => {
|
|
1201
1292
|
if (func.returnType) return func.returnType;
|
1202
1293
|
}
|
1203
1294
|
|
1204
|
-
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return
|
1295
|
+
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
|
1205
1296
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1206
1297
|
|
1207
1298
|
// check if this is a prototype function
|
@@ -1221,7 +1312,7 @@ const getNodeType = (scope, node) => {
|
|
1221
1312
|
return TYPES.number;
|
1222
1313
|
}
|
1223
1314
|
|
1224
|
-
if (scope.locals['#last_type']) return
|
1315
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1225
1316
|
|
1226
1317
|
// presume
|
1227
1318
|
// todo: warn here?
|
@@ -1276,6 +1367,7 @@ const getNodeType = (scope, node) => {
|
|
1276
1367
|
|
1277
1368
|
// todo: this should be dynamic but for now only static
|
1278
1369
|
if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
|
1370
|
+
if (knownLeft === TYPES._bytestring || knownRight === TYPES._bytestring) return TYPES._bytestring;
|
1279
1371
|
|
1280
1372
|
return TYPES.number;
|
1281
1373
|
|
@@ -1312,15 +1404,21 @@ const getNodeType = (scope, node) => {
|
|
1312
1404
|
|
1313
1405
|
// ts hack
|
1314
1406
|
if (scope.locals[node.object.name]?.metadata?.type === TYPES.string) return TYPES.string;
|
1407
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES._bytestring) return TYPES._bytestring;
|
1315
1408
|
if (scope.locals[node.object.name]?.metadata?.type === TYPES._array) return TYPES.number;
|
1316
1409
|
|
1317
|
-
if (scope.locals['#last_type']) return
|
1410
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1318
1411
|
|
1319
1412
|
// presume
|
1320
1413
|
return TYPES.number;
|
1321
1414
|
}
|
1322
1415
|
|
1323
|
-
if (
|
1416
|
+
if (node.type === 'TaggedTemplateExpression') {
|
1417
|
+
// hack
|
1418
|
+
if (node.tag.name.startsWith('__Porffor_')) return TYPES.number;
|
1419
|
+
}
|
1420
|
+
|
1421
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1324
1422
|
|
1325
1423
|
// presume
|
1326
1424
|
// todo: warn here?
|
@@ -1353,7 +1451,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1353
1451
|
return makeString(scope, decl.value, global, name);
|
1354
1452
|
|
1355
1453
|
default:
|
1356
|
-
return todo(`cannot generate literal of type ${typeof decl.value}
|
1454
|
+
return todo(scope, `cannot generate literal of type ${typeof decl.value}`, true);
|
1357
1455
|
}
|
1358
1456
|
};
|
1359
1457
|
|
@@ -1362,6 +1460,8 @@ const countLeftover = wasm => {
|
|
1362
1460
|
|
1363
1461
|
for (let i = 0; i < wasm.length; i++) {
|
1364
1462
|
const inst = wasm[i];
|
1463
|
+
if (inst[0] == null) continue;
|
1464
|
+
|
1365
1465
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
1366
1466
|
if (inst[0] === Opcodes.if) count--;
|
1367
1467
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -1370,18 +1470,25 @@ const countLeftover = wasm => {
|
|
1370
1470
|
if (inst[0] === Opcodes.end) depth--;
|
1371
1471
|
|
1372
1472
|
if (depth === 0)
|
1373
|
-
if ([Opcodes.throw,Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1473
|
+
if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1374
1474
|
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)) {}
|
1375
|
-
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const].includes(inst[0])) count++;
|
1475
|
+
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const, Opcodes.memory_size].includes(inst[0])) count++;
|
1376
1476
|
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1377
1477
|
else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
|
1378
1478
|
else if (inst[0] === Opcodes.return) count = 0;
|
1379
1479
|
else if (inst[0] === Opcodes.call) {
|
1380
1480
|
let func = funcs.find(x => x.index === inst[1]);
|
1381
|
-
if (
|
1382
|
-
count
|
1383
|
-
} else
|
1384
|
-
|
1481
|
+
if (inst[1] === -1) {
|
1482
|
+
// todo: count for calling self
|
1483
|
+
} else if (!func && inst[1] < importedFuncs.length) {
|
1484
|
+
count -= importedFuncs[inst[1]].params;
|
1485
|
+
count += importedFuncs[inst[1]].returns;
|
1486
|
+
} else {
|
1487
|
+
if (func) {
|
1488
|
+
count -= func.params.length;
|
1489
|
+
} else count--;
|
1490
|
+
if (func) count += func.returns.length;
|
1491
|
+
}
|
1385
1492
|
} else count--;
|
1386
1493
|
|
1387
1494
|
// console.log(count, decompile([ inst ]).slice(0, -1));
|
@@ -1473,10 +1580,21 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1473
1580
|
name = func.name;
|
1474
1581
|
}
|
1475
1582
|
|
1476
|
-
if (name === 'eval' && decl.arguments[0]
|
1583
|
+
if (name === 'eval' && decl.arguments[0]?.type === 'Literal') {
|
1477
1584
|
// literal eval hack
|
1478
|
-
const code = decl.arguments[0]
|
1479
|
-
|
1585
|
+
const code = decl.arguments[0]?.value ?? '';
|
1586
|
+
|
1587
|
+
let parsed;
|
1588
|
+
try {
|
1589
|
+
parsed = parse(code, []);
|
1590
|
+
} catch (e) {
|
1591
|
+
if (e.name === 'SyntaxError') {
|
1592
|
+
// throw syntax errors of evals at runtime instead
|
1593
|
+
return internalThrow(scope, 'SyntaxError', e.message, true);
|
1594
|
+
}
|
1595
|
+
|
1596
|
+
throw e;
|
1597
|
+
}
|
1480
1598
|
|
1481
1599
|
const out = generate(scope, {
|
1482
1600
|
type: 'BlockStatement',
|
@@ -1490,13 +1608,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1490
1608
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1491
1609
|
out.push(
|
1492
1610
|
...getNodeType(scope, finalStatement),
|
1493
|
-
setLastType(scope)
|
1611
|
+
...setLastType(scope)
|
1494
1612
|
);
|
1495
1613
|
} else if (countLeftover(out) === 0) {
|
1496
1614
|
out.push(...number(UNDEFINED));
|
1497
1615
|
out.push(
|
1498
1616
|
...number(TYPES.undefined, Valtype.i32),
|
1499
|
-
setLastType(scope)
|
1617
|
+
...setLastType(scope)
|
1500
1618
|
);
|
1501
1619
|
}
|
1502
1620
|
|
@@ -1518,6 +1636,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1518
1636
|
|
1519
1637
|
target = { ...decl.callee };
|
1520
1638
|
target.name = spl.slice(0, -1).join('_');
|
1639
|
+
|
1640
|
+
// failed to lookup name, abort
|
1641
|
+
if (!lookupName(scope, target.name)[0]) protoName = null;
|
1521
1642
|
}
|
1522
1643
|
|
1523
1644
|
// literal.func()
|
@@ -1525,22 +1646,29 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1525
1646
|
// megahack for /regex/.func()
|
1526
1647
|
const funcName = decl.callee.property.name;
|
1527
1648
|
if (decl.callee.object.regex && Object.hasOwn(Rhemyn, funcName)) {
|
1528
|
-
const
|
1649
|
+
const regex = decl.callee.object.regex.pattern;
|
1650
|
+
const rhemynName = `regex_${funcName}_${regex}`;
|
1529
1651
|
|
1530
|
-
funcIndex[
|
1531
|
-
|
1652
|
+
if (!funcIndex[rhemynName]) {
|
1653
|
+
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
1532
1654
|
|
1655
|
+
funcIndex[func.name] = func.index;
|
1656
|
+
funcs.push(func);
|
1657
|
+
}
|
1658
|
+
|
1659
|
+
const idx = funcIndex[rhemynName];
|
1533
1660
|
return [
|
1534
1661
|
// make string arg
|
1535
1662
|
...generate(scope, decl.arguments[0]),
|
1663
|
+
Opcodes.i32_to_u,
|
1664
|
+
...getNodeType(scope, decl.arguments[0]),
|
1536
1665
|
|
1537
1666
|
// call regex func
|
1538
|
-
Opcodes.
|
1539
|
-
[ Opcodes.call, func.index ],
|
1667
|
+
[ Opcodes.call, idx ],
|
1540
1668
|
Opcodes.i32_from_u,
|
1541
1669
|
|
1542
1670
|
...number(TYPES.boolean, Valtype.i32),
|
1543
|
-
setLastType(scope)
|
1671
|
+
...setLastType(scope)
|
1544
1672
|
];
|
1545
1673
|
}
|
1546
1674
|
|
@@ -1565,12 +1693,31 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1565
1693
|
// }
|
1566
1694
|
|
1567
1695
|
if (protoName) {
|
1696
|
+
const protoBC = {};
|
1697
|
+
|
1698
|
+
const builtinProtoCands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + protoName));
|
1699
|
+
|
1700
|
+
if (!decl._protoInternalCall && builtinProtoCands.length > 0) {
|
1701
|
+
for (const x of builtinProtoCands) {
|
1702
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
1703
|
+
if (type == null) continue;
|
1704
|
+
|
1705
|
+
protoBC[type] = generateCall(scope, {
|
1706
|
+
callee: {
|
1707
|
+
type: 'Identifier',
|
1708
|
+
name: x
|
1709
|
+
},
|
1710
|
+
arguments: [ target, ...decl.arguments ],
|
1711
|
+
_protoInternalCall: true
|
1712
|
+
});
|
1713
|
+
}
|
1714
|
+
}
|
1715
|
+
|
1568
1716
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1569
1717
|
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1570
1718
|
return acc;
|
1571
1719
|
}, {});
|
1572
1720
|
|
1573
|
-
// no prototype function candidates, ignore
|
1574
1721
|
if (Object.keys(protoCands).length > 0) {
|
1575
1722
|
// use local for cached i32 length as commonly used
|
1576
1723
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
@@ -1588,7 +1735,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1588
1735
|
|
1589
1736
|
let allOptUnused = true;
|
1590
1737
|
let lengthI32CacheUsed = false;
|
1591
|
-
const protoBC = {};
|
1592
1738
|
for (const x in protoCands) {
|
1593
1739
|
const protoFunc = protoCands[x];
|
1594
1740
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
@@ -1596,7 +1742,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1596
1742
|
...RTArrayUtil.getLength(getPointer),
|
1597
1743
|
|
1598
1744
|
...number(TYPES.number, Valtype.i32),
|
1599
|
-
setLastType(scope)
|
1745
|
+
...setLastType(scope)
|
1600
1746
|
];
|
1601
1747
|
continue;
|
1602
1748
|
}
|
@@ -1633,7 +1779,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1633
1779
|
...protoOut,
|
1634
1780
|
|
1635
1781
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1636
|
-
setLastType(scope),
|
1782
|
+
...setLastType(scope),
|
1637
1783
|
[ Opcodes.end ]
|
1638
1784
|
];
|
1639
1785
|
}
|
@@ -1659,10 +1805,19 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1659
1805
|
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1660
1806
|
];
|
1661
1807
|
}
|
1808
|
+
|
1809
|
+
if (Object.keys(protoBC).length > 0) {
|
1810
|
+
return typeSwitch(scope, getNodeType(scope, target), {
|
1811
|
+
...protoBC,
|
1812
|
+
|
1813
|
+
// TODO: error better
|
1814
|
+
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1815
|
+
}, valtypeBinary);
|
1816
|
+
}
|
1662
1817
|
}
|
1663
1818
|
|
1664
1819
|
// TODO: only allows callee as literal
|
1665
|
-
if (!name) return todo(`only literal callees (got ${decl.callee.type})`);
|
1820
|
+
if (!name) return todo(scope, `only literal callees (got ${decl.callee.type})`);
|
1666
1821
|
|
1667
1822
|
let idx = funcIndex[name] ?? importedFuncs[name];
|
1668
1823
|
if (idx === undefined && builtinFuncs[name]) {
|
@@ -1672,20 +1827,20 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1672
1827
|
idx = funcIndex[name];
|
1673
1828
|
|
1674
1829
|
// infer arguments types from builtins params
|
1675
|
-
const func = funcs.find(x => x.name === name);
|
1676
|
-
for (let i = 0; i < decl.arguments.length; i++) {
|
1677
|
-
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
1683
|
-
|
1684
|
-
|
1685
|
-
|
1686
|
-
|
1687
|
-
|
1688
|
-
}
|
1830
|
+
// const func = funcs.find(x => x.name === name);
|
1831
|
+
// for (let i = 0; i < decl.arguments.length; i++) {
|
1832
|
+
// const arg = decl.arguments[i];
|
1833
|
+
// if (!arg.name) continue;
|
1834
|
+
|
1835
|
+
// const local = scope.locals[arg.name];
|
1836
|
+
// if (!local) continue;
|
1837
|
+
|
1838
|
+
// local.type = func.params[i];
|
1839
|
+
// if (local.type === Valtype.v128) {
|
1840
|
+
// // specify vec subtype inferred from last vec type in function name
|
1841
|
+
// local.vecType = name.split('_').reverse().find(x => x.includes('x'));
|
1842
|
+
// }
|
1843
|
+
// }
|
1689
1844
|
}
|
1690
1845
|
|
1691
1846
|
if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
@@ -1698,9 +1853,25 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1698
1853
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1699
1854
|
const wasmOps = {
|
1700
1855
|
// pointer, align, offset
|
1701
|
-
|
1856
|
+
i32_load: { imms: 2, args: [ true ], returns: 1 },
|
1702
1857
|
// pointer, value, align, offset
|
1703
|
-
|
1858
|
+
i32_store: { imms: 2, args: [ true, true ], returns: 0 },
|
1859
|
+
// pointer, align, offset
|
1860
|
+
i32_load8_u: { imms: 2, args: [ true ], returns: 1 },
|
1861
|
+
// pointer, value, align, offset
|
1862
|
+
i32_store8: { imms: 2, args: [ true, true ], returns: 0 },
|
1863
|
+
// pointer, align, offset
|
1864
|
+
i32_load16_u: { imms: 2, args: [ true ], returns: 1 },
|
1865
|
+
// pointer, value, align, offset
|
1866
|
+
i32_store16: { imms: 2, args: [ true, true ], returns: 0 },
|
1867
|
+
|
1868
|
+
// pointer, align, offset
|
1869
|
+
f64_load: { imms: 2, args: [ true ], returns: 0 }, // 0 due to not i32
|
1870
|
+
// pointer, value, align, offset
|
1871
|
+
f64_store: { imms: 2, args: [ true, false ], returns: 0 },
|
1872
|
+
|
1873
|
+
// value
|
1874
|
+
i32_const: { imms: 1, args: [], returns: 1 },
|
1704
1875
|
};
|
1705
1876
|
|
1706
1877
|
const opName = name.slice('__Porffor_wasm_'.length);
|
@@ -1709,28 +1880,32 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1709
1880
|
const op = wasmOps[opName];
|
1710
1881
|
|
1711
1882
|
const argOut = [];
|
1712
|
-
for (let i = 0; i < op.args; i++) argOut.push(
|
1883
|
+
for (let i = 0; i < op.args.length; i++) argOut.push(
|
1884
|
+
...generate(scope, decl.arguments[i]),
|
1885
|
+
...(op.args[i] ? [ Opcodes.i32_to ] : [])
|
1886
|
+
);
|
1713
1887
|
|
1714
1888
|
// literals only
|
1715
|
-
const imms = decl.arguments.slice(op.args).map(x => x.value);
|
1889
|
+
const imms = decl.arguments.slice(op.args.length).map(x => x.value);
|
1716
1890
|
|
1717
1891
|
return [
|
1718
1892
|
...argOut,
|
1719
|
-
[ Opcodes[opName], ...imms ]
|
1893
|
+
[ Opcodes[opName], ...imms ],
|
1894
|
+
...(new Array(op.returns).fill(Opcodes.i32_from))
|
1720
1895
|
];
|
1721
1896
|
}
|
1722
1897
|
}
|
1723
1898
|
|
1724
1899
|
if (idx === undefined) {
|
1725
|
-
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function
|
1726
|
-
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined
|
1900
|
+
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1901
|
+
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1727
1902
|
}
|
1728
1903
|
|
1729
1904
|
const func = funcs.find(x => x.index === idx);
|
1730
1905
|
|
1731
1906
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1732
1907
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1733
|
-
const typedReturns = userFunc || builtinFuncs[name]?.typedReturns;
|
1908
|
+
const typedReturns = (func ? func.returnType == null : userFunc) || builtinFuncs[name]?.typedReturns;
|
1734
1909
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1735
1910
|
|
1736
1911
|
let args = decl.arguments;
|
@@ -1751,7 +1926,11 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1751
1926
|
const arg = args[i];
|
1752
1927
|
out = out.concat(generate(scope, arg));
|
1753
1928
|
|
1754
|
-
if (builtinFuncs[name] && builtinFuncs[name].params[i] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1929
|
+
if (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1930
|
+
out.push(Opcodes.i32_to);
|
1931
|
+
}
|
1932
|
+
|
1933
|
+
if (importedFuncs[name] && name.startsWith('profile')) {
|
1755
1934
|
out.push(Opcodes.i32_to);
|
1756
1935
|
}
|
1757
1936
|
|
@@ -1770,9 +1949,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1770
1949
|
// ...number(type, Valtype.i32),
|
1771
1950
|
// [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1772
1951
|
// );
|
1773
|
-
} else out.push(setLastType(scope));
|
1952
|
+
} else out.push(...setLastType(scope));
|
1774
1953
|
|
1775
|
-
if (builtinFuncs[name] && builtinFuncs[name].returns[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1954
|
+
if (builtinFuncs[name] && builtinFuncs[name].returns?.[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1776
1955
|
out.push(Opcodes.i32_from);
|
1777
1956
|
}
|
1778
1957
|
|
@@ -1782,8 +1961,21 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1782
1961
|
const generateNew = (scope, decl, _global, _name) => {
|
1783
1962
|
// hack: basically treat this as a normal call for builtins for now
|
1784
1963
|
const name = mapName(decl.callee.name);
|
1964
|
+
|
1785
1965
|
if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1786
|
-
|
1966
|
+
|
1967
|
+
if (builtinFuncs[name + '$constructor']) {
|
1968
|
+
// custom ...$constructor override builtin func
|
1969
|
+
return generateCall(scope, {
|
1970
|
+
...decl,
|
1971
|
+
callee: {
|
1972
|
+
type: 'Identifier',
|
1973
|
+
name: name + '$constructor'
|
1974
|
+
}
|
1975
|
+
}, _global, _name);
|
1976
|
+
}
|
1977
|
+
|
1978
|
+
if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
|
1787
1979
|
|
1788
1980
|
return generateCall(scope, decl, _global, _name);
|
1789
1981
|
};
|
@@ -1917,8 +2109,6 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1917
2109
|
[ Opcodes.block, returns ]
|
1918
2110
|
];
|
1919
2111
|
|
1920
|
-
// todo: use br_table?
|
1921
|
-
|
1922
2112
|
for (const x in bc) {
|
1923
2113
|
if (x === 'default') continue;
|
1924
2114
|
|
@@ -1974,12 +2164,14 @@ const addVarMetadata = (scope, name, global = false, metadata = {}) => {
|
|
1974
2164
|
};
|
1975
2165
|
|
1976
2166
|
const typeAnnoToPorfType = x => {
|
1977
|
-
if (
|
1978
|
-
if (TYPES[
|
2167
|
+
if (!x) return null;
|
2168
|
+
if (TYPES[x.toLowerCase()] != null) return TYPES[x.toLowerCase()];
|
2169
|
+
if (TYPES['_' + x.toLowerCase()] != null) return TYPES['_' + x.toLowerCase()];
|
1979
2170
|
|
1980
2171
|
switch (x) {
|
1981
2172
|
case 'i32':
|
1982
2173
|
case 'i64':
|
2174
|
+
case 'f64':
|
1983
2175
|
return TYPES.number;
|
1984
2176
|
}
|
1985
2177
|
|
@@ -1990,7 +2182,7 @@ const extractTypeAnnotation = decl => {
|
|
1990
2182
|
let a = decl;
|
1991
2183
|
while (a.typeAnnotation) a = a.typeAnnotation;
|
1992
2184
|
|
1993
|
-
let type, elementType;
|
2185
|
+
let type = null, elementType = null;
|
1994
2186
|
if (a.typeName) {
|
1995
2187
|
type = a.typeName.name;
|
1996
2188
|
} else if (a.type.endsWith('Keyword')) {
|
@@ -2017,11 +2209,12 @@ const generateVar = (scope, decl) => {
|
|
2017
2209
|
|
2018
2210
|
// global variable if in top scope (main) and var ..., or if wanted
|
2019
2211
|
const global = topLevel || decl._bare; // decl.kind === 'var';
|
2212
|
+
const target = global ? globals : scope.locals;
|
2020
2213
|
|
2021
2214
|
for (const x of decl.declarations) {
|
2022
2215
|
const name = mapName(x.id.name);
|
2023
2216
|
|
2024
|
-
if (!name) return todo('destructuring is not supported yet');
|
2217
|
+
if (!name) return todo(scope, 'destructuring is not supported yet');
|
2025
2218
|
|
2026
2219
|
if (x.init && isFuncType(x.init.type)) {
|
2027
2220
|
// hack for let a = function () { ... }
|
@@ -2038,16 +2231,29 @@ const generateVar = (scope, decl) => {
|
|
2038
2231
|
continue; // always ignore
|
2039
2232
|
}
|
2040
2233
|
|
2041
|
-
|
2234
|
+
// // generate init before allocating var
|
2235
|
+
// let generated;
|
2236
|
+
// if (x.init) generated = generate(scope, x.init, global, name);
|
2237
|
+
|
2238
|
+
const typed = typedInput && x.id.typeAnnotation;
|
2239
|
+
let idx = allocVar(scope, name, global, !(typed && extractTypeAnnotation(x.id).type != null));
|
2042
2240
|
|
2043
|
-
if (
|
2241
|
+
if (typed) {
|
2044
2242
|
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
2045
2243
|
}
|
2046
2244
|
|
2047
2245
|
if (x.init) {
|
2048
|
-
|
2049
|
-
|
2050
|
-
|
2246
|
+
const generated = generate(scope, x.init, global, name);
|
2247
|
+
if (scope.arrays?.get(name) != null) {
|
2248
|
+
// hack to set local as pointer before
|
2249
|
+
out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2250
|
+
if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
|
2251
|
+
generated.pop();
|
2252
|
+
out = out.concat(generated);
|
2253
|
+
} else {
|
2254
|
+
out = out.concat(generated);
|
2255
|
+
out.push([ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2256
|
+
}
|
2051
2257
|
out.push(...setType(scope, name, getNodeType(scope, x.init)));
|
2052
2258
|
}
|
2053
2259
|
|
@@ -2058,7 +2264,8 @@ const generateVar = (scope, decl) => {
|
|
2058
2264
|
return out;
|
2059
2265
|
};
|
2060
2266
|
|
2061
|
-
|
2267
|
+
// todo: optimize this func for valueUnused
|
2268
|
+
const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
2062
2269
|
const { type, name } = decl.left;
|
2063
2270
|
|
2064
2271
|
if (type === 'ObjectPattern') {
|
@@ -2073,22 +2280,30 @@ const generateAssign = (scope, decl) => {
|
|
2073
2280
|
return [];
|
2074
2281
|
}
|
2075
2282
|
|
2283
|
+
const op = decl.operator.slice(0, -1) || '=';
|
2284
|
+
|
2076
2285
|
// hack: .length setter
|
2077
2286
|
if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
|
2078
2287
|
const name = decl.left.object.name;
|
2079
|
-
const pointer = arrays
|
2288
|
+
const pointer = scope.arrays?.get(name);
|
2080
2289
|
|
2081
|
-
const aotPointer = pointer != null;
|
2290
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2082
2291
|
|
2083
2292
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
2293
|
+
const pointerTmp = op === '=' ? null : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
2084
2294
|
|
2085
2295
|
return [
|
2086
2296
|
...(aotPointer ? number(0, Valtype.i32) : [
|
2087
2297
|
...generate(scope, decl.left.object),
|
2088
2298
|
Opcodes.i32_to_u
|
2089
2299
|
]),
|
2300
|
+
...(!pointerTmp ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2090
2301
|
|
2091
|
-
...generate(scope, decl.right),
|
2302
|
+
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2303
|
+
[ Opcodes.local_get, pointerTmp ],
|
2304
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
2305
|
+
Opcodes.i32_from_u
|
2306
|
+
], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right))),
|
2092
2307
|
[ Opcodes.local_tee, newValueTmp ],
|
2093
2308
|
|
2094
2309
|
Opcodes.i32_to_u,
|
@@ -2098,14 +2313,12 @@ const generateAssign = (scope, decl) => {
|
|
2098
2313
|
];
|
2099
2314
|
}
|
2100
2315
|
|
2101
|
-
const op = decl.operator.slice(0, -1) || '=';
|
2102
|
-
|
2103
2316
|
// arr[i]
|
2104
2317
|
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
2105
2318
|
const name = decl.left.object.name;
|
2106
|
-
const pointer = arrays
|
2319
|
+
const pointer = scope.arrays?.get(name);
|
2107
2320
|
|
2108
|
-
const aotPointer = pointer != null;
|
2321
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2109
2322
|
|
2110
2323
|
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
2111
2324
|
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
@@ -2161,7 +2374,7 @@ const generateAssign = (scope, decl) => {
|
|
2161
2374
|
];
|
2162
2375
|
}
|
2163
2376
|
|
2164
|
-
if (!name) return todo('destructuring is not supported yet');
|
2377
|
+
if (!name) return todo(scope, 'destructuring is not supported yet', true);
|
2165
2378
|
|
2166
2379
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2167
2380
|
|
@@ -2266,7 +2479,7 @@ const generateUnary = (scope, decl) => {
|
|
2266
2479
|
return out;
|
2267
2480
|
}
|
2268
2481
|
|
2269
|
-
case 'delete':
|
2482
|
+
case 'delete': {
|
2270
2483
|
let toReturn = true, toGenerate = true;
|
2271
2484
|
|
2272
2485
|
if (decl.argument.type === 'Identifier') {
|
@@ -2288,9 +2501,26 @@ const generateUnary = (scope, decl) => {
|
|
2288
2501
|
|
2289
2502
|
out.push(...number(toReturn ? 1 : 0));
|
2290
2503
|
return out;
|
2504
|
+
}
|
2291
2505
|
|
2292
|
-
case 'typeof':
|
2293
|
-
|
2506
|
+
case 'typeof': {
|
2507
|
+
let overrideType, toGenerate = true;
|
2508
|
+
|
2509
|
+
if (decl.argument.type === 'Identifier') {
|
2510
|
+
const out = generateIdent(scope, decl.argument);
|
2511
|
+
|
2512
|
+
// if ReferenceError (undeclared var), ignore and return undefined
|
2513
|
+
if (out[1]) {
|
2514
|
+
// does not exist (2 ops from throw)
|
2515
|
+
overrideType = number(TYPES.undefined, Valtype.i32);
|
2516
|
+
toGenerate = false;
|
2517
|
+
}
|
2518
|
+
}
|
2519
|
+
|
2520
|
+
const out = toGenerate ? generate(scope, decl.argument) : [];
|
2521
|
+
disposeLeftover(out);
|
2522
|
+
|
2523
|
+
out.push(...typeSwitch(scope, overrideType ?? getNodeType(scope, decl.argument), {
|
2294
2524
|
[TYPES.number]: makeString(scope, 'number', false, '#typeof_result'),
|
2295
2525
|
[TYPES.boolean]: makeString(scope, 'boolean', false, '#typeof_result'),
|
2296
2526
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
@@ -2301,27 +2531,30 @@ const generateUnary = (scope, decl) => {
|
|
2301
2531
|
|
2302
2532
|
// object and internal types
|
2303
2533
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
2304
|
-
});
|
2534
|
+
}));
|
2535
|
+
|
2536
|
+
return out;
|
2537
|
+
}
|
2305
2538
|
|
2306
2539
|
default:
|
2307
|
-
return todo(`unary operator ${decl.operator} not implemented yet
|
2540
|
+
return todo(scope, `unary operator ${decl.operator} not implemented yet`, true);
|
2308
2541
|
}
|
2309
2542
|
};
|
2310
2543
|
|
2311
|
-
const generateUpdate = (scope, decl) => {
|
2544
|
+
const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
|
2312
2545
|
const { name } = decl.argument;
|
2313
2546
|
|
2314
2547
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2315
2548
|
|
2316
2549
|
if (local === undefined) {
|
2317
|
-
return todo(`update expression with undefined variable
|
2550
|
+
return todo(scope, `update expression with undefined variable`, true);
|
2318
2551
|
}
|
2319
2552
|
|
2320
2553
|
const idx = local.idx;
|
2321
2554
|
const out = [];
|
2322
2555
|
|
2323
2556
|
out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2324
|
-
if (!decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2557
|
+
if (!decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2325
2558
|
|
2326
2559
|
switch (decl.operator) {
|
2327
2560
|
case '++':
|
@@ -2334,7 +2567,7 @@ const generateUpdate = (scope, decl) => {
|
|
2334
2567
|
}
|
2335
2568
|
|
2336
2569
|
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2337
|
-
if (decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2570
|
+
if (decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2338
2571
|
|
2339
2572
|
return out;
|
2340
2573
|
};
|
@@ -2374,7 +2607,7 @@ const generateConditional = (scope, decl) => {
|
|
2374
2607
|
// note type
|
2375
2608
|
out.push(
|
2376
2609
|
...getNodeType(scope, decl.consequent),
|
2377
|
-
setLastType(scope)
|
2610
|
+
...setLastType(scope)
|
2378
2611
|
);
|
2379
2612
|
|
2380
2613
|
out.push([ Opcodes.else ]);
|
@@ -2383,7 +2616,7 @@ const generateConditional = (scope, decl) => {
|
|
2383
2616
|
// note type
|
2384
2617
|
out.push(
|
2385
2618
|
...getNodeType(scope, decl.alternate),
|
2386
|
-
setLastType(scope)
|
2619
|
+
...setLastType(scope)
|
2387
2620
|
);
|
2388
2621
|
|
2389
2622
|
out.push([ Opcodes.end ]);
|
@@ -2397,7 +2630,7 @@ const generateFor = (scope, decl) => {
|
|
2397
2630
|
const out = [];
|
2398
2631
|
|
2399
2632
|
if (decl.init) {
|
2400
|
-
out.push(...generate(scope, decl.init));
|
2633
|
+
out.push(...generate(scope, decl.init, false, undefined, true));
|
2401
2634
|
disposeLeftover(out);
|
2402
2635
|
}
|
2403
2636
|
|
@@ -2415,7 +2648,7 @@ const generateFor = (scope, decl) => {
|
|
2415
2648
|
out.push(...generate(scope, decl.body));
|
2416
2649
|
out.push([ Opcodes.end ]);
|
2417
2650
|
|
2418
|
-
if (decl.update) out.push(...generate(scope, decl.update));
|
2651
|
+
if (decl.update) out.push(...generate(scope, decl.update, false, undefined, true));
|
2419
2652
|
|
2420
2653
|
out.push([ Opcodes.br, 1 ]);
|
2421
2654
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2443,6 +2676,36 @@ const generateWhile = (scope, decl) => {
|
|
2443
2676
|
return out;
|
2444
2677
|
};
|
2445
2678
|
|
2679
|
+
const generateDoWhile = (scope, decl) => {
|
2680
|
+
const out = [];
|
2681
|
+
|
2682
|
+
out.push([ Opcodes.loop, Blocktype.void ]);
|
2683
|
+
depth.push('dowhile');
|
2684
|
+
|
2685
|
+
// block for break (includes all)
|
2686
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2687
|
+
depth.push('block');
|
2688
|
+
|
2689
|
+
// block for continue
|
2690
|
+
// includes body but not test+loop so we can exit body at anytime
|
2691
|
+
// and still test+loop after
|
2692
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2693
|
+
depth.push('block');
|
2694
|
+
|
2695
|
+
out.push(...generate(scope, decl.body));
|
2696
|
+
|
2697
|
+
out.push([ Opcodes.end ]);
|
2698
|
+
depth.pop();
|
2699
|
+
|
2700
|
+
out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2701
|
+
out.push([ Opcodes.br_if, 1 ]);
|
2702
|
+
|
2703
|
+
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
2704
|
+
depth.pop(); depth.pop();
|
2705
|
+
|
2706
|
+
return out;
|
2707
|
+
};
|
2708
|
+
|
2446
2709
|
const generateForOf = (scope, decl) => {
|
2447
2710
|
const out = [];
|
2448
2711
|
|
@@ -2479,7 +2742,10 @@ const generateForOf = (scope, decl) => {
|
|
2479
2742
|
generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
|
2480
2743
|
}
|
2481
2744
|
|
2745
|
+
// if (!leftName) console.log(decl.left?.declarations?.[0]?.id ?? decl.left);
|
2746
|
+
|
2482
2747
|
const [ local, isGlobal ] = lookupName(scope, leftName);
|
2748
|
+
if (!local) return todo(scope, 'for of failed to get left local (probably destructure)');
|
2483
2749
|
|
2484
2750
|
depth.push('block');
|
2485
2751
|
depth.push('block');
|
@@ -2488,6 +2754,7 @@ const generateForOf = (scope, decl) => {
|
|
2488
2754
|
// hack: this is naughty and will break things!
|
2489
2755
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2490
2756
|
if (pages.hasAnyString) {
|
2757
|
+
// todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
|
2491
2758
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2492
2759
|
rawElements: new Array(1)
|
2493
2760
|
}, isGlobal, leftName, true, 'i16');
|
@@ -2579,6 +2846,56 @@ const generateForOf = (scope, decl) => {
|
|
2579
2846
|
[ Opcodes.end ],
|
2580
2847
|
[ Opcodes.end ]
|
2581
2848
|
],
|
2849
|
+
[TYPES._bytestring]: [
|
2850
|
+
...setType(scope, leftName, TYPES._bytestring),
|
2851
|
+
|
2852
|
+
[ Opcodes.loop, Blocktype.void ],
|
2853
|
+
|
2854
|
+
// setup new/out array
|
2855
|
+
...newOut,
|
2856
|
+
[ Opcodes.drop ],
|
2857
|
+
|
2858
|
+
...number(0, Valtype.i32), // base 0 for store after
|
2859
|
+
|
2860
|
+
// load current string ind {arg}
|
2861
|
+
[ Opcodes.local_get, pointer ],
|
2862
|
+
[ Opcodes.local_get, counter ],
|
2863
|
+
[ Opcodes.i32_add ],
|
2864
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2865
|
+
|
2866
|
+
// store to new string ind 0
|
2867
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2868
|
+
|
2869
|
+
// return new string (page)
|
2870
|
+
...number(newPointer),
|
2871
|
+
|
2872
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2873
|
+
|
2874
|
+
[ Opcodes.block, Blocktype.void ],
|
2875
|
+
[ Opcodes.block, Blocktype.void ],
|
2876
|
+
...generate(scope, decl.body),
|
2877
|
+
[ Opcodes.end ],
|
2878
|
+
|
2879
|
+
// increment iter pointer
|
2880
|
+
// [ Opcodes.local_get, pointer ],
|
2881
|
+
// ...number(1, Valtype.i32),
|
2882
|
+
// [ Opcodes.i32_add ],
|
2883
|
+
// [ Opcodes.local_set, pointer ],
|
2884
|
+
|
2885
|
+
// increment counter by 1
|
2886
|
+
[ Opcodes.local_get, counter ],
|
2887
|
+
...number(1, Valtype.i32),
|
2888
|
+
[ Opcodes.i32_add ],
|
2889
|
+
[ Opcodes.local_tee, counter ],
|
2890
|
+
|
2891
|
+
// loop if counter != length
|
2892
|
+
[ Opcodes.local_get, length ],
|
2893
|
+
[ Opcodes.i32_ne ],
|
2894
|
+
[ Opcodes.br_if, 1 ],
|
2895
|
+
|
2896
|
+
[ Opcodes.end ],
|
2897
|
+
[ Opcodes.end ]
|
2898
|
+
],
|
2582
2899
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2583
2900
|
}, Blocktype.void));
|
2584
2901
|
|
@@ -2589,28 +2906,65 @@ const generateForOf = (scope, decl) => {
|
|
2589
2906
|
return out;
|
2590
2907
|
};
|
2591
2908
|
|
2909
|
+
// find the nearest loop in depth map by type
|
2592
2910
|
const getNearestLoop = () => {
|
2593
2911
|
for (let i = depth.length - 1; i >= 0; i--) {
|
2594
|
-
if (
|
2912
|
+
if (['while', 'dowhile', 'for', 'forof'].includes(depth[i])) return i;
|
2595
2913
|
}
|
2596
2914
|
|
2597
2915
|
return -1;
|
2598
2916
|
};
|
2599
2917
|
|
2600
2918
|
const generateBreak = (scope, decl) => {
|
2601
|
-
const
|
2919
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2920
|
+
const type = depth[target];
|
2921
|
+
|
2922
|
+
// different loop types have different branch offsets
|
2923
|
+
// as they have different wasm block/loop/if structures
|
2924
|
+
// we need to use the right offset by type to branch to the one we want
|
2925
|
+
// for a break: exit the loop without executing anything else inside it
|
2926
|
+
const offset = ({
|
2927
|
+
for: 2, // loop > if (wanted branch) > block (we are here)
|
2928
|
+
while: 2, // loop > if (wanted branch) (we are here)
|
2929
|
+
dowhile: 2, // loop > block (wanted branch) > block (we are here)
|
2930
|
+
forof: 2, // loop > block (wanted branch) > block (we are here)
|
2931
|
+
if: 1 // break inside if, branch 0 to skip the rest of the if
|
2932
|
+
})[type];
|
2933
|
+
|
2602
2934
|
return [
|
2603
|
-
[ Opcodes.br, ...signedLEB128(
|
2935
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2604
2936
|
];
|
2605
2937
|
};
|
2606
2938
|
|
2607
2939
|
const generateContinue = (scope, decl) => {
|
2608
|
-
const
|
2940
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2941
|
+
const type = depth[target];
|
2942
|
+
|
2943
|
+
// different loop types have different branch offsets
|
2944
|
+
// as they have different wasm block/loop/if structures
|
2945
|
+
// we need to use the right offset by type to branch to the one we want
|
2946
|
+
// for a continue: do test for the loop, and then loop depending on that success
|
2947
|
+
const offset = ({
|
2948
|
+
for: 3, // loop (wanted branch) > if > block (we are here)
|
2949
|
+
while: 1, // loop (wanted branch) > if (we are here)
|
2950
|
+
dowhile: 3, // loop > block > block (wanted branch) (we are here)
|
2951
|
+
forof: 3 // loop > block > block (wanted branch) (we are here)
|
2952
|
+
})[type];
|
2953
|
+
|
2609
2954
|
return [
|
2610
|
-
[ Opcodes.br, ...signedLEB128(
|
2955
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2611
2956
|
];
|
2612
2957
|
};
|
2613
2958
|
|
2959
|
+
const generateLabel = (scope, decl) => {
|
2960
|
+
scope.labels ??= new Map();
|
2961
|
+
|
2962
|
+
const name = decl.label.name;
|
2963
|
+
scope.labels.set(name, depth.length);
|
2964
|
+
|
2965
|
+
return generate(scope, decl.body);
|
2966
|
+
};
|
2967
|
+
|
2614
2968
|
const generateThrow = (scope, decl) => {
|
2615
2969
|
scope.throws = true;
|
2616
2970
|
|
@@ -2643,7 +2997,7 @@ const generateThrow = (scope, decl) => {
|
|
2643
2997
|
};
|
2644
2998
|
|
2645
2999
|
const generateTry = (scope, decl) => {
|
2646
|
-
if (decl.finalizer) return todo('try finally not implemented yet');
|
3000
|
+
if (decl.finalizer) return todo(scope, 'try finally not implemented yet');
|
2647
3001
|
|
2648
3002
|
const out = [];
|
2649
3003
|
|
@@ -2674,7 +3028,7 @@ const generateAssignPat = (scope, decl) => {
|
|
2674
3028
|
// TODO
|
2675
3029
|
// if identifier declared, use that
|
2676
3030
|
// else, use default (right)
|
2677
|
-
return todo('assignment pattern (optional arg)');
|
3031
|
+
return todo(scope, 'assignment pattern (optional arg)');
|
2678
3032
|
};
|
2679
3033
|
|
2680
3034
|
let pages = new Map();
|
@@ -2753,16 +3107,22 @@ const getAllocType = itemType => {
|
|
2753
3107
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2754
3108
|
const out = [];
|
2755
3109
|
|
3110
|
+
scope.arrays ??= new Map();
|
3111
|
+
|
2756
3112
|
let firstAssign = false;
|
2757
|
-
if (!arrays.has(name) || name === '$undeclared') {
|
3113
|
+
if (!scope.arrays.has(name) || name === '$undeclared') {
|
2758
3114
|
firstAssign = true;
|
2759
3115
|
|
2760
3116
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2761
3117
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2762
|
-
|
3118
|
+
|
3119
|
+
if (Prefs.scopedPageNames) scope.arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType) * pageSize);
|
3120
|
+
else scope.arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2763
3121
|
}
|
2764
3122
|
|
2765
|
-
const pointer = arrays.get(name);
|
3123
|
+
const pointer = scope.arrays.get(name);
|
3124
|
+
|
3125
|
+
const local = global ? globals[name] : scope.locals[name];
|
2766
3126
|
|
2767
3127
|
const useRawElements = !!decl.rawElements;
|
2768
3128
|
const elements = useRawElements ? decl.rawElements : decl.elements;
|
@@ -2796,11 +3156,22 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2796
3156
|
return [ out, pointer ];
|
2797
3157
|
}
|
2798
3158
|
|
3159
|
+
const pointerTmp = local != null ? localTmp(scope, '#makearray_pointer_tmp', Valtype.i32) : null;
|
3160
|
+
if (pointerTmp != null) {
|
3161
|
+
out.push(
|
3162
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
3163
|
+
Opcodes.i32_to_u,
|
3164
|
+
[ Opcodes.local_set, pointerTmp ]
|
3165
|
+
);
|
3166
|
+
}
|
3167
|
+
|
3168
|
+
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3169
|
+
|
2799
3170
|
// store length as 0th array
|
2800
3171
|
out.push(
|
2801
|
-
...
|
3172
|
+
...pointerWasm,
|
2802
3173
|
...number(length, Valtype.i32),
|
2803
|
-
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1,
|
3174
|
+
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
|
2804
3175
|
);
|
2805
3176
|
|
2806
3177
|
const storeOp = StoreOps[itemType];
|
@@ -2809,14 +3180,14 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2809
3180
|
if (elements[i] == null) continue;
|
2810
3181
|
|
2811
3182
|
out.push(
|
2812
|
-
...
|
3183
|
+
...pointerWasm,
|
2813
3184
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
2814
|
-
[ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(
|
3185
|
+
[ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2815
3186
|
);
|
2816
3187
|
}
|
2817
3188
|
|
2818
3189
|
// local value as pointer
|
2819
|
-
out.push(...
|
3190
|
+
out.push(...pointerWasm, Opcodes.i32_from_u);
|
2820
3191
|
|
2821
3192
|
return [ out, pointer ];
|
2822
3193
|
};
|
@@ -2848,20 +3219,29 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
2848
3219
|
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2849
3220
|
};
|
2850
3221
|
|
2851
|
-
let arrays = new Map();
|
2852
3222
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
2853
3223
|
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
2854
3224
|
};
|
2855
3225
|
|
2856
3226
|
export const generateMember = (scope, decl, _global, _name) => {
|
2857
3227
|
const name = decl.object.name;
|
2858
|
-
const pointer = arrays
|
3228
|
+
const pointer = scope.arrays?.get(name);
|
2859
3229
|
|
2860
|
-
const aotPointer = pointer != null;
|
3230
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2861
3231
|
|
2862
3232
|
// hack: .length
|
2863
3233
|
if (decl.property.name === 'length') {
|
2864
|
-
|
3234
|
+
const func = funcs.find(x => x.name === name);
|
3235
|
+
if (func) {
|
3236
|
+
const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
|
3237
|
+
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
3238
|
+
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3239
|
+
}
|
3240
|
+
|
3241
|
+
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3242
|
+
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3243
|
+
if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
|
3244
|
+
|
2865
3245
|
return [
|
2866
3246
|
...(aotPointer ? number(0, Valtype.i32) : [
|
2867
3247
|
...generate(scope, decl.object),
|
@@ -2905,7 +3285,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2905
3285
|
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2906
3286
|
|
2907
3287
|
...number(TYPES.number, Valtype.i32),
|
2908
|
-
setLastType(scope)
|
3288
|
+
...setLastType(scope)
|
2909
3289
|
],
|
2910
3290
|
|
2911
3291
|
[TYPES.string]: [
|
@@ -2937,7 +3317,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2937
3317
|
...number(newPointer),
|
2938
3318
|
|
2939
3319
|
...number(TYPES.string, Valtype.i32),
|
2940
|
-
setLastType(scope)
|
3320
|
+
...setLastType(scope)
|
2941
3321
|
],
|
2942
3322
|
[TYPES._bytestring]: [
|
2943
3323
|
// setup new/out array
|
@@ -2956,19 +3336,19 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2956
3336
|
]),
|
2957
3337
|
|
2958
3338
|
// load current string ind {arg}
|
2959
|
-
[ Opcodes.i32_load8_u,
|
3339
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2960
3340
|
|
2961
3341
|
// store to new string ind 0
|
2962
|
-
[ Opcodes.i32_store8,
|
3342
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2963
3343
|
|
2964
3344
|
// return new string (page)
|
2965
3345
|
...number(newPointer),
|
2966
3346
|
|
2967
3347
|
...number(TYPES._bytestring, Valtype.i32),
|
2968
|
-
setLastType(scope)
|
3348
|
+
...setLastType(scope)
|
2969
3349
|
],
|
2970
3350
|
|
2971
|
-
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet')
|
3351
|
+
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
2972
3352
|
});
|
2973
3353
|
};
|
2974
3354
|
|
@@ -2978,28 +3358,36 @@ const objectHack = node => {
|
|
2978
3358
|
if (!node) return node;
|
2979
3359
|
|
2980
3360
|
if (node.type === 'MemberExpression') {
|
2981
|
-
|
3361
|
+
const out = (() => {
|
3362
|
+
if (node.computed || node.optional) return;
|
2982
3363
|
|
2983
|
-
|
3364
|
+
let objectName = node.object.name;
|
2984
3365
|
|
2985
|
-
|
2986
|
-
|
3366
|
+
// if object is not identifier or another member exp, give up
|
3367
|
+
if (node.object.type !== 'Identifier' && node.object.type !== 'MemberExpression') return;
|
3368
|
+
if (objectName && ['undefined', 'null', 'NaN', 'Infinity'].includes(objectName)) return;
|
2987
3369
|
|
2988
|
-
|
3370
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
2989
3371
|
|
2990
|
-
|
2991
|
-
|
3372
|
+
// if .length, give up (hack within a hack!)
|
3373
|
+
if (node.property.name === 'length') {
|
3374
|
+
node.object = objectHack(node.object);
|
3375
|
+
return;
|
3376
|
+
}
|
2992
3377
|
|
2993
|
-
|
2994
|
-
|
3378
|
+
// no object name, give up
|
3379
|
+
if (!objectName) return;
|
2995
3380
|
|
2996
|
-
|
2997
|
-
|
3381
|
+
const name = '__' + objectName + '_' + node.property.name;
|
3382
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2998
3383
|
|
2999
|
-
|
3000
|
-
|
3001
|
-
|
3002
|
-
|
3384
|
+
return {
|
3385
|
+
type: 'Identifier',
|
3386
|
+
name
|
3387
|
+
};
|
3388
|
+
})();
|
3389
|
+
|
3390
|
+
if (out) return out;
|
3003
3391
|
}
|
3004
3392
|
|
3005
3393
|
for (const x in node) {
|
@@ -3013,8 +3401,8 @@ const objectHack = node => {
|
|
3013
3401
|
};
|
3014
3402
|
|
3015
3403
|
const generateFunc = (scope, decl) => {
|
3016
|
-
if (decl.async) return todo('async functions are not supported');
|
3017
|
-
if (decl.generator) return todo('generator functions are not supported');
|
3404
|
+
if (decl.async) return todo(scope, 'async functions are not supported');
|
3405
|
+
if (decl.generator) return todo(scope, 'generator functions are not supported');
|
3018
3406
|
|
3019
3407
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3020
3408
|
const params = decl.params ?? [];
|
@@ -3030,6 +3418,11 @@ const generateFunc = (scope, decl) => {
|
|
3030
3418
|
name
|
3031
3419
|
};
|
3032
3420
|
|
3421
|
+
if (typedInput && decl.returnType) {
|
3422
|
+
innerScope.returnType = extractTypeAnnotation(decl.returnType).type;
|
3423
|
+
innerScope.returns = [ valtypeBinary ];
|
3424
|
+
}
|
3425
|
+
|
3033
3426
|
for (let i = 0; i < params.length; i++) {
|
3034
3427
|
allocVar(innerScope, params[i].name, false);
|
3035
3428
|
|
@@ -3092,16 +3485,6 @@ const generateCode = (scope, decl) => {
|
|
3092
3485
|
};
|
3093
3486
|
|
3094
3487
|
const internalConstrs = {
|
3095
|
-
Boolean: {
|
3096
|
-
generate: (scope, decl) => {
|
3097
|
-
if (decl.arguments.length === 0) return number(0);
|
3098
|
-
|
3099
|
-
// should generate/run all args
|
3100
|
-
return truthy(scope, generate(scope, decl.arguments[0]), getNodeType(scope, decl.arguments[0]), false, false);
|
3101
|
-
},
|
3102
|
-
type: TYPES.boolean
|
3103
|
-
},
|
3104
|
-
|
3105
3488
|
Array: {
|
3106
3489
|
generate: (scope, decl, global, name) => {
|
3107
3490
|
// new Array(i0, i1, ...)
|
@@ -3119,7 +3502,7 @@ const internalConstrs = {
|
|
3119
3502
|
|
3120
3503
|
// todo: check in wasm instead of here
|
3121
3504
|
const literalValue = arg.value ?? 0;
|
3122
|
-
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length');
|
3505
|
+
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
|
3123
3506
|
|
3124
3507
|
return [
|
3125
3508
|
...number(0, Valtype.i32),
|
@@ -3130,7 +3513,8 @@ const internalConstrs = {
|
|
3130
3513
|
...number(pointer)
|
3131
3514
|
];
|
3132
3515
|
},
|
3133
|
-
type: TYPES._array
|
3516
|
+
type: TYPES._array,
|
3517
|
+
length: 1
|
3134
3518
|
},
|
3135
3519
|
|
3136
3520
|
__Array_of: {
|
@@ -3142,7 +3526,131 @@ const internalConstrs = {
|
|
3142
3526
|
}, global, name);
|
3143
3527
|
},
|
3144
3528
|
type: TYPES._array,
|
3529
|
+
notConstr: true,
|
3530
|
+
length: 0
|
3531
|
+
},
|
3532
|
+
|
3533
|
+
__Porffor_fastOr: {
|
3534
|
+
generate: (scope, decl) => {
|
3535
|
+
const out = [];
|
3536
|
+
|
3537
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3538
|
+
out.push(
|
3539
|
+
...generate(scope, decl.arguments[i]),
|
3540
|
+
Opcodes.i32_to_u,
|
3541
|
+
...(i > 0 ? [ [ Opcodes.i32_or ] ] : [])
|
3542
|
+
);
|
3543
|
+
}
|
3544
|
+
|
3545
|
+
out.push(Opcodes.i32_from_u);
|
3546
|
+
|
3547
|
+
return out;
|
3548
|
+
},
|
3549
|
+
type: TYPES.boolean,
|
3550
|
+
notConstr: true
|
3551
|
+
},
|
3552
|
+
|
3553
|
+
__Porffor_fastAnd: {
|
3554
|
+
generate: (scope, decl) => {
|
3555
|
+
const out = [];
|
3556
|
+
|
3557
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3558
|
+
out.push(
|
3559
|
+
...generate(scope, decl.arguments[i]),
|
3560
|
+
Opcodes.i32_to_u,
|
3561
|
+
...(i > 0 ? [ [ Opcodes.i32_and ] ] : [])
|
3562
|
+
);
|
3563
|
+
}
|
3564
|
+
|
3565
|
+
out.push(Opcodes.i32_from_u);
|
3566
|
+
|
3567
|
+
return out;
|
3568
|
+
},
|
3569
|
+
type: TYPES.boolean,
|
3145
3570
|
notConstr: true
|
3571
|
+
},
|
3572
|
+
|
3573
|
+
Boolean: {
|
3574
|
+
generate: (scope, decl) => {
|
3575
|
+
// todo: boolean object when used as constructor
|
3576
|
+
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3577
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3578
|
+
},
|
3579
|
+
type: TYPES.boolean,
|
3580
|
+
length: 1
|
3581
|
+
},
|
3582
|
+
|
3583
|
+
__Math_max: {
|
3584
|
+
generate: (scope, decl) => {
|
3585
|
+
const out = [
|
3586
|
+
...number(-Infinity)
|
3587
|
+
];
|
3588
|
+
|
3589
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3590
|
+
out.push(
|
3591
|
+
...generate(scope, decl.arguments[i]),
|
3592
|
+
[ Opcodes.f64_max ]
|
3593
|
+
);
|
3594
|
+
}
|
3595
|
+
|
3596
|
+
return out;
|
3597
|
+
},
|
3598
|
+
type: TYPES.number,
|
3599
|
+
notConstr: true,
|
3600
|
+
length: 2
|
3601
|
+
},
|
3602
|
+
|
3603
|
+
__Math_min: {
|
3604
|
+
generate: (scope, decl) => {
|
3605
|
+
const out = [
|
3606
|
+
...number(Infinity)
|
3607
|
+
];
|
3608
|
+
|
3609
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3610
|
+
out.push(
|
3611
|
+
...generate(scope, decl.arguments[i]),
|
3612
|
+
[ Opcodes.f64_min ]
|
3613
|
+
);
|
3614
|
+
}
|
3615
|
+
|
3616
|
+
return out;
|
3617
|
+
},
|
3618
|
+
type: TYPES.number,
|
3619
|
+
notConstr: true,
|
3620
|
+
length: 2
|
3621
|
+
},
|
3622
|
+
|
3623
|
+
__console_log: {
|
3624
|
+
generate: (scope, decl) => {
|
3625
|
+
const out = [];
|
3626
|
+
|
3627
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3628
|
+
out.push(
|
3629
|
+
...generateCall(scope, {
|
3630
|
+
callee: {
|
3631
|
+
type: 'Identifier',
|
3632
|
+
name: '__Porffor_print'
|
3633
|
+
},
|
3634
|
+
arguments: [ decl.arguments[i] ]
|
3635
|
+
}),
|
3636
|
+
|
3637
|
+
// print space
|
3638
|
+
...number(32),
|
3639
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3640
|
+
);
|
3641
|
+
}
|
3642
|
+
|
3643
|
+
// print newline
|
3644
|
+
out.push(
|
3645
|
+
...number(10),
|
3646
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3647
|
+
);
|
3648
|
+
|
3649
|
+
return out;
|
3650
|
+
},
|
3651
|
+
type: TYPES.undefined,
|
3652
|
+
notConstr: true,
|
3653
|
+
length: 0
|
3146
3654
|
}
|
3147
3655
|
};
|
3148
3656
|
|
@@ -3171,20 +3679,23 @@ export default program => {
|
|
3171
3679
|
funcs = [];
|
3172
3680
|
funcIndex = {};
|
3173
3681
|
depth = [];
|
3174
|
-
arrays = new Map();
|
3175
3682
|
pages = new Map();
|
3176
3683
|
data = [];
|
3177
3684
|
currentFuncIndex = importedFuncs.length;
|
3178
3685
|
|
3179
3686
|
globalThis.valtype = 'f64';
|
3180
3687
|
|
3181
|
-
const valtypeOpt = process.argv.find(x => x.startsWith('
|
3688
|
+
const valtypeOpt = process.argv.find(x => x.startsWith('--valtype='));
|
3182
3689
|
if (valtypeOpt) valtype = valtypeOpt.split('=')[1];
|
3183
3690
|
|
3184
3691
|
globalThis.valtypeBinary = Valtype[valtype];
|
3185
3692
|
|
3186
3693
|
const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
|
3187
3694
|
|
3695
|
+
globalThis.pageSize = PageSize;
|
3696
|
+
const pageSizeOpt = process.argv.find(x => x.startsWith('--page-size='));
|
3697
|
+
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3698
|
+
|
3188
3699
|
// set generic opcodes for current valtype
|
3189
3700
|
Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
|
3190
3701
|
Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
|
@@ -3193,10 +3704,10 @@ export default program => {
|
|
3193
3704
|
Opcodes.add = [ Opcodes.i32_add, Opcodes.i64_add, Opcodes.f64_add ][valtypeInd];
|
3194
3705
|
Opcodes.sub = [ Opcodes.i32_sub, Opcodes.i64_sub, Opcodes.f64_sub ][valtypeInd];
|
3195
3706
|
|
3196
|
-
Opcodes.i32_to = [ [
|
3197
|
-
Opcodes.i32_to_u = [ [
|
3198
|
-
Opcodes.i32_from = [ [
|
3199
|
-
Opcodes.i32_from_u = [ [
|
3707
|
+
Opcodes.i32_to = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_s ][valtypeInd];
|
3708
|
+
Opcodes.i32_to_u = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_u ][valtypeInd];
|
3709
|
+
Opcodes.i32_from = [ [], [ Opcodes.i64_extend_i32_s ], [ Opcodes.f64_convert_i32_s ] ][valtypeInd];
|
3710
|
+
Opcodes.i32_from_u = [ [], [ Opcodes.i64_extend_i32_u ], [ Opcodes.f64_convert_i32_u ] ][valtypeInd];
|
3200
3711
|
|
3201
3712
|
Opcodes.load = [ Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load ][valtypeInd];
|
3202
3713
|
Opcodes.store = [ Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store ][valtypeInd];
|
@@ -3209,10 +3720,6 @@ export default program => {
|
|
3209
3720
|
|
3210
3721
|
program.id = { name: 'main' };
|
3211
3722
|
|
3212
|
-
globalThis.pageSize = PageSize;
|
3213
|
-
const pageSizeOpt = process.argv.find(x => x.startsWith('-page-size='));
|
3214
|
-
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3215
|
-
|
3216
3723
|
const scope = {
|
3217
3724
|
locals: {},
|
3218
3725
|
localInd: 0
|
@@ -3223,7 +3730,7 @@ export default program => {
|
|
3223
3730
|
body: program.body
|
3224
3731
|
};
|
3225
3732
|
|
3226
|
-
if (Prefs.astLog) console.log(program.body.body);
|
3733
|
+
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3227
3734
|
|
3228
3735
|
generateFunc(scope, program);
|
3229
3736
|
|
@@ -3240,7 +3747,11 @@ export default program => {
|
|
3240
3747
|
}
|
3241
3748
|
|
3242
3749
|
if (lastInst[0] === Opcodes.end || lastInst[0] === Opcodes.local_set || lastInst[0] === Opcodes.global_set) {
|
3243
|
-
|
3750
|
+
if (lastInst[0] === Opcodes.local_set && lastInst[1] === main.locals['#last_type'].idx) {
|
3751
|
+
main.wasm.splice(main.wasm.length - 1, 1);
|
3752
|
+
} else {
|
3753
|
+
main.returns = [];
|
3754
|
+
}
|
3244
3755
|
}
|
3245
3756
|
|
3246
3757
|
if (lastInst[0] === Opcodes.call) {
|