porffor 0.2.0-dcc06c8 → 0.2.0-de394c3
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/LICENSE +20 -20
- package/README.md +113 -62
- 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} +58 -11
- package/compiler/builtins/annexb_string.js +72 -0
- package/compiler/builtins/annexb_string.ts +19 -0
- package/compiler/builtins/array.ts +145 -0
- package/compiler/builtins/base64.ts +103 -40
- package/compiler/builtins/crypto.ts +120 -0
- package/compiler/builtins/date.ts +1370 -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 +33 -1
- package/compiler/builtins/string.ts +1055 -0
- package/compiler/builtins/tostring.ts +45 -0
- package/compiler/builtins.js +454 -243
- package/compiler/{codeGen.js → codegen.js} +833 -290
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +108 -10
- package/compiler/generated_builtins.js +1259 -0
- package/compiler/index.js +16 -14
- package/compiler/log.js +6 -3
- package/compiler/opt.js +23 -22
- package/compiler/parse.js +31 -25
- package/compiler/precompile.js +66 -22
- package/compiler/prefs.js +5 -1
- package/compiler/prototype.js +4 -20
- package/compiler/types.js +37 -0
- package/compiler/wasmSpec.js +28 -8
- package/compiler/wrap.js +51 -47
- package/fib.js +7 -0
- package/package.json +9 -5
- package/porf +2 -0
- package/rhemyn/compile.js +3 -2
- package/rhemyn/parse.js +323 -320
- package/rhemyn/test/parse.js +58 -58
- package/runner/compare.js +34 -34
- package/runner/debug.js +122 -0
- package/runner/index.js +48 -9
- package/runner/profiler.js +102 -0
- package/runner/repl.js +40 -7
- package/runner/sizes.js +37 -37
- package/demo.js +0 -3
- package/demo.ts +0 -1
- package/filesize.cmd +0 -2
- package/hello +0 -0
- package/runner/info.js +0 -89
- package/runner/profile.js +0 -46
- package/runner/results.json +0 -1
- package/runner/transform.js +0 -15
- package/tmp.c +0 -152
- package/util/enum.js +0 -20
@@ -8,6 +8,7 @@ import { log } from "./log.js";
|
|
8
8
|
import parse from "./parse.js";
|
9
9
|
import * as Rhemyn from "../rhemyn/compile.js";
|
10
10
|
import Prefs from './prefs.js';
|
11
|
+
import { TYPES, TYPE_NAMES } from './types.js';
|
11
12
|
|
12
13
|
let globals = {};
|
13
14
|
let globalInd = 0;
|
@@ -24,35 +25,37 @@ const debug = str => {
|
|
24
25
|
const logChar = n => {
|
25
26
|
code.push(...number(n));
|
26
27
|
|
27
|
-
code.push(Opcodes.call);
|
28
|
-
code.push(...unsignedLEB128(0));
|
28
|
+
code.push([ Opcodes.call, 0 ]);
|
29
29
|
};
|
30
30
|
|
31
31
|
for (let i = 0; i < str.length; i++) {
|
32
32
|
logChar(str.charCodeAt(i));
|
33
33
|
}
|
34
34
|
|
35
|
-
logChar(
|
35
|
+
logChar(10); // new line
|
36
36
|
|
37
37
|
return code;
|
38
38
|
};
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
this.name = 'TodoError';
|
45
|
-
}
|
40
|
+
class TodoError extends Error {
|
41
|
+
constructor(message) {
|
42
|
+
super(message);
|
43
|
+
this.name = 'TodoError';
|
46
44
|
}
|
45
|
+
}
|
46
|
+
const todo = (scope, msg, expectsValue = undefined) => {
|
47
|
+
switch (Prefs.todoTime ?? 'runtime') {
|
48
|
+
case 'compile':
|
49
|
+
throw new TodoError(msg);
|
47
50
|
|
48
|
-
|
49
|
-
|
50
|
-
const code = [];
|
51
|
-
|
52
|
-
code.push(...debug(`todo! ` + msg));
|
53
|
-
code.push(Opcodes.unreachable);
|
51
|
+
case 'runtime':
|
52
|
+
return internalThrow(scope, 'TodoError', msg, expectsValue);
|
54
53
|
|
55
|
-
|
54
|
+
// return [
|
55
|
+
// ...debug(`todo! ${msg}`),
|
56
|
+
// [ Opcodes.unreachable ]
|
57
|
+
// ];
|
58
|
+
}
|
56
59
|
};
|
57
60
|
|
58
61
|
const isFuncType = type => type === 'FunctionDeclaration' || type === 'FunctionExpression' || type === 'ArrowFunctionExpression';
|
@@ -105,7 +108,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
105
108
|
return generateUnary(scope, decl);
|
106
109
|
|
107
110
|
case 'UpdateExpression':
|
108
|
-
return generateUpdate(scope, decl);
|
111
|
+
return generateUpdate(scope, decl, global, name, valueUnused);
|
109
112
|
|
110
113
|
case 'IfStatement':
|
111
114
|
return generateIf(scope, decl);
|
@@ -116,6 +119,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
116
119
|
case 'WhileStatement':
|
117
120
|
return generateWhile(scope, decl);
|
118
121
|
|
122
|
+
case 'DoWhileStatement':
|
123
|
+
return generateDoWhile(scope, decl);
|
124
|
+
|
119
125
|
case 'ForOfStatement':
|
120
126
|
return generateForOf(scope, decl);
|
121
127
|
|
@@ -125,6 +131,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
125
131
|
case 'ContinueStatement':
|
126
132
|
return generateContinue(scope, decl);
|
127
133
|
|
134
|
+
case 'LabeledStatement':
|
135
|
+
return generateLabel(scope, decl);
|
136
|
+
|
128
137
|
case 'EmptyStatement':
|
129
138
|
return generateEmpty(scope, decl);
|
130
139
|
|
@@ -138,7 +147,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
138
147
|
return generateTry(scope, decl);
|
139
148
|
|
140
149
|
case 'DebuggerStatement':
|
141
|
-
// todo:
|
150
|
+
// todo: hook into terminal debugger
|
142
151
|
return [];
|
143
152
|
|
144
153
|
case 'ArrayExpression':
|
@@ -152,10 +161,16 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
152
161
|
const funcsBefore = funcs.length;
|
153
162
|
generate(scope, decl.declaration);
|
154
163
|
|
155
|
-
if (funcsBefore
|
164
|
+
if (funcsBefore !== funcs.length) {
|
165
|
+
// new func added
|
166
|
+
const newFunc = funcs[funcs.length - 1];
|
167
|
+
newFunc.export = true;
|
168
|
+
}
|
169
|
+
|
170
|
+
// if (funcsBefore === funcs.length) throw new Error('no new func added in export');
|
156
171
|
|
157
|
-
const newFunc = funcs[funcs.length - 1];
|
158
|
-
newFunc.export = true;
|
172
|
+
// const newFunc = funcs[funcs.length - 1];
|
173
|
+
// newFunc.export = true;
|
159
174
|
|
160
175
|
return [];
|
161
176
|
|
@@ -169,8 +184,8 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
169
184
|
if (asm[0] === '') continue; // blank
|
170
185
|
|
171
186
|
if (asm[0] === 'local') {
|
172
|
-
const [ name,
|
173
|
-
scope.locals[name] = { idx:
|
187
|
+
const [ name, type ] = asm.slice(1);
|
188
|
+
scope.locals[name] = { idx: scope.localInd++, type: Valtype[type] };
|
174
189
|
continue;
|
175
190
|
}
|
176
191
|
|
@@ -189,43 +204,55 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
189
204
|
if (!inst) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
190
205
|
|
191
206
|
if (!Array.isArray(inst)) inst = [ inst ];
|
192
|
-
const immediates = asm.slice(1).map(x =>
|
207
|
+
const immediates = asm.slice(1).map(x => {
|
208
|
+
const int = parseInt(x);
|
209
|
+
if (Number.isNaN(int)) return scope.locals[x]?.idx;
|
210
|
+
return int;
|
211
|
+
});
|
193
212
|
|
194
|
-
out.push([ ...inst, ...immediates ]);
|
213
|
+
out.push([ ...inst, ...immediates.flatMap(x => signedLEB128(x)) ]);
|
195
214
|
}
|
196
215
|
|
197
216
|
return out;
|
198
217
|
},
|
199
218
|
|
200
219
|
__Porffor_bs: str => [
|
201
|
-
...makeString(scope, str,
|
220
|
+
...makeString(scope, str, global, name, true),
|
202
221
|
|
203
|
-
...
|
204
|
-
|
222
|
+
...(name ? setType(scope, name, TYPES._bytestring) : [
|
223
|
+
...number(TYPES._bytestring, Valtype.i32),
|
224
|
+
...setLastType(scope)
|
225
|
+
])
|
205
226
|
],
|
206
227
|
__Porffor_s: str => [
|
207
|
-
...makeString(scope, str,
|
228
|
+
...makeString(scope, str, global, name, false),
|
208
229
|
|
209
|
-
...
|
210
|
-
|
230
|
+
...(name ? setType(scope, name, TYPES.string) : [
|
231
|
+
...number(TYPES.string, Valtype.i32),
|
232
|
+
...setLastType(scope)
|
233
|
+
])
|
211
234
|
],
|
212
235
|
};
|
213
236
|
|
214
|
-
const
|
237
|
+
const func = decl.tag.name;
|
215
238
|
// hack for inline asm
|
216
|
-
if (!funcs[
|
239
|
+
if (!funcs[func]) return todo(scope, 'tagged template expressions not implemented', true);
|
217
240
|
|
218
241
|
const { quasis, expressions } = decl.quasi;
|
219
242
|
let str = quasis[0].value.raw;
|
220
243
|
|
221
244
|
for (let i = 0; i < expressions.length; i++) {
|
222
245
|
const e = expressions[i];
|
223
|
-
|
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;
|
224
251
|
|
225
252
|
str += quasis[i + 1].value.raw;
|
226
253
|
}
|
227
254
|
|
228
|
-
return funcs[
|
255
|
+
return funcs[func](str);
|
229
256
|
}
|
230
257
|
|
231
258
|
default:
|
@@ -235,7 +262,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
235
262
|
return [];
|
236
263
|
}
|
237
264
|
|
238
|
-
return todo(`no generation for ${decl.type}!`);
|
265
|
+
return todo(scope, `no generation for ${decl.type}!`);
|
239
266
|
}
|
240
267
|
};
|
241
268
|
|
@@ -263,7 +290,7 @@ const lookupName = (scope, _name) => {
|
|
263
290
|
return [ undefined, undefined ];
|
264
291
|
};
|
265
292
|
|
266
|
-
const internalThrow = (scope, constructor, message, expectsValue =
|
293
|
+
const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysValueInternalThrows) => [
|
267
294
|
...generateThrow(scope, {
|
268
295
|
argument: {
|
269
296
|
type: 'NewExpression',
|
@@ -287,7 +314,10 @@ const generateIdent = (scope, decl) => {
|
|
287
314
|
|
288
315
|
if (Object.hasOwn(builtinVars, name)) {
|
289
316
|
if (builtinVars[name].floatOnly && valtype[0] === 'i') throw new Error(`Cannot use ${unhackName(name)} with integer valtype`);
|
290
|
-
|
317
|
+
|
318
|
+
let wasm = builtinVars[name];
|
319
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name });
|
320
|
+
return wasm.slice();
|
291
321
|
}
|
292
322
|
|
293
323
|
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
@@ -295,6 +325,11 @@ const generateIdent = (scope, decl) => {
|
|
295
325
|
return number(1);
|
296
326
|
}
|
297
327
|
|
328
|
+
if (isExistingProtoFunc(name)) {
|
329
|
+
// todo: return an actual something
|
330
|
+
return number(1);
|
331
|
+
}
|
332
|
+
|
298
333
|
if (local?.idx === undefined) {
|
299
334
|
// no local var with name
|
300
335
|
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
@@ -325,14 +360,18 @@ const generateReturn = (scope, decl) => {
|
|
325
360
|
// just bare "return"
|
326
361
|
return [
|
327
362
|
...number(UNDEFINED), // "undefined" if func returns
|
328
|
-
...
|
363
|
+
...(scope.returnType != null ? [] : [
|
364
|
+
...number(TYPES.undefined, Valtype.i32) // type undefined
|
365
|
+
]),
|
329
366
|
[ Opcodes.return ]
|
330
367
|
];
|
331
368
|
}
|
332
369
|
|
333
370
|
return [
|
334
371
|
...generate(scope, decl.argument),
|
335
|
-
...
|
372
|
+
...(scope.returnType != null ? [] : [
|
373
|
+
...getNodeType(scope, decl.argument)
|
374
|
+
]),
|
336
375
|
[ Opcodes.return ]
|
337
376
|
];
|
338
377
|
};
|
@@ -346,7 +385,8 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
346
385
|
return idx;
|
347
386
|
};
|
348
387
|
|
349
|
-
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);
|
350
390
|
|
351
391
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
352
392
|
const checks = {
|
@@ -355,7 +395,7 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
355
395
|
'??': nullish
|
356
396
|
};
|
357
397
|
|
358
|
-
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);
|
359
399
|
|
360
400
|
// generic structure for {a} OP {b}
|
361
401
|
// -->
|
@@ -363,8 +403,8 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
363
403
|
|
364
404
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
365
405
|
// (like if we are in an if condition - very common)
|
366
|
-
const leftIsInt =
|
367
|
-
const rightIsInt =
|
406
|
+
const leftIsInt = isFloatToIntOp(left[left.length - 1]);
|
407
|
+
const rightIsInt = isFloatToIntOp(right[right.length - 1]);
|
368
408
|
|
369
409
|
const canInt = leftIsInt && rightIsInt;
|
370
410
|
|
@@ -381,12 +421,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
381
421
|
...right,
|
382
422
|
// note type
|
383
423
|
...rightType,
|
384
|
-
setLastType(scope),
|
424
|
+
...setLastType(scope),
|
385
425
|
[ Opcodes.else ],
|
386
426
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
387
427
|
// note type
|
388
428
|
...leftType,
|
389
|
-
setLastType(scope),
|
429
|
+
...setLastType(scope),
|
390
430
|
[ Opcodes.end ],
|
391
431
|
Opcodes.i32_from
|
392
432
|
];
|
@@ -400,17 +440,17 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
400
440
|
...right,
|
401
441
|
// note type
|
402
442
|
...rightType,
|
403
|
-
setLastType(scope),
|
443
|
+
...setLastType(scope),
|
404
444
|
[ Opcodes.else ],
|
405
445
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
406
446
|
// note type
|
407
447
|
...leftType,
|
408
|
-
setLastType(scope),
|
448
|
+
...setLastType(scope),
|
409
449
|
[ Opcodes.end ]
|
410
450
|
];
|
411
451
|
};
|
412
452
|
|
413
|
-
const concatStrings = (scope, left, right, global, name, assign) => {
|
453
|
+
const concatStrings = (scope, left, right, global, name, assign = false, bytestrings = false) => {
|
414
454
|
// todo: this should be rewritten into a built-in/func: String.prototype.concat
|
415
455
|
// todo: convert left and right to strings if not
|
416
456
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -421,7 +461,7 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
421
461
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
422
462
|
|
423
463
|
if (assign) {
|
424
|
-
const pointer = arrays
|
464
|
+
const pointer = scope.arrays?.get(name ?? '$undeclared');
|
425
465
|
|
426
466
|
return [
|
427
467
|
// setup right
|
@@ -446,11 +486,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
446
486
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
|
447
487
|
|
448
488
|
// copy right
|
449
|
-
// dst = out pointer + length size + current length *
|
489
|
+
// dst = out pointer + length size + current length * sizeof valtype
|
450
490
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
451
491
|
|
452
492
|
[ Opcodes.local_get, leftLength ],
|
453
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
493
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
454
494
|
[ Opcodes.i32_mul ],
|
455
495
|
[ Opcodes.i32_add ],
|
456
496
|
|
@@ -459,9 +499,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
459
499
|
...number(ValtypeSize.i32, Valtype.i32),
|
460
500
|
[ Opcodes.i32_add ],
|
461
501
|
|
462
|
-
// size = right length *
|
502
|
+
// size = right length * sizeof valtype
|
463
503
|
[ Opcodes.local_get, rightLength ],
|
464
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
504
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
465
505
|
[ Opcodes.i32_mul ],
|
466
506
|
|
467
507
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -519,11 +559,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
519
559
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
520
560
|
|
521
561
|
// copy right
|
522
|
-
// dst = out pointer + length size + left length *
|
562
|
+
// dst = out pointer + length size + left length * sizeof valtype
|
523
563
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
524
564
|
|
525
565
|
[ Opcodes.local_get, leftLength ],
|
526
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
566
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
527
567
|
[ Opcodes.i32_mul ],
|
528
568
|
[ Opcodes.i32_add ],
|
529
569
|
|
@@ -532,9 +572,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
532
572
|
...number(ValtypeSize.i32, Valtype.i32),
|
533
573
|
[ Opcodes.i32_add ],
|
534
574
|
|
535
|
-
// size = right length *
|
575
|
+
// size = right length * sizeof valtype
|
536
576
|
[ Opcodes.local_get, rightLength ],
|
537
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
577
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
538
578
|
[ Opcodes.i32_mul ],
|
539
579
|
|
540
580
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -544,7 +584,7 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
544
584
|
];
|
545
585
|
};
|
546
586
|
|
547
|
-
const compareStrings = (scope, left, right) => {
|
587
|
+
const compareStrings = (scope, left, right, bytestrings = false) => {
|
548
588
|
// todo: this should be rewritten into a func
|
549
589
|
// todo: convert left and right to strings if not
|
550
590
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -553,7 +593,6 @@ const compareStrings = (scope, left, right) => {
|
|
553
593
|
const leftPointer = localTmp(scope, 'compare_left_pointer', Valtype.i32);
|
554
594
|
const leftLength = localTmp(scope, 'compare_left_length', Valtype.i32);
|
555
595
|
const rightPointer = localTmp(scope, 'compare_right_pointer', Valtype.i32);
|
556
|
-
const rightLength = localTmp(scope, 'compare_right_length', Valtype.i32);
|
557
596
|
|
558
597
|
const index = localTmp(scope, 'compare_index', Valtype.i32);
|
559
598
|
const indexEnd = localTmp(scope, 'compare_index_end', Valtype.i32);
|
@@ -581,7 +620,6 @@ const compareStrings = (scope, left, right) => {
|
|
581
620
|
|
582
621
|
[ Opcodes.local_get, rightPointer ],
|
583
622
|
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
584
|
-
[ Opcodes.local_tee, rightLength ],
|
585
623
|
|
586
624
|
// fast path: check leftLength != rightLength
|
587
625
|
[ Opcodes.i32_ne ],
|
@@ -596,11 +634,13 @@ const compareStrings = (scope, left, right) => {
|
|
596
634
|
...number(0, Valtype.i32),
|
597
635
|
[ Opcodes.local_set, index ],
|
598
636
|
|
599
|
-
// setup index end as length * sizeof
|
637
|
+
// setup index end as length * sizeof valtype (1 for bytestring, 2 for string)
|
600
638
|
// we do this instead of having to do mul/div each iter for perf™
|
601
639
|
[ Opcodes.local_get, leftLength ],
|
602
|
-
...
|
603
|
-
|
640
|
+
...(bytestrings ? [] : [
|
641
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
642
|
+
[ Opcodes.i32_mul ],
|
643
|
+
]),
|
604
644
|
[ Opcodes.local_set, indexEnd ],
|
605
645
|
|
606
646
|
// iterate over each char and check if eq
|
@@ -610,13 +650,17 @@ const compareStrings = (scope, left, right) => {
|
|
610
650
|
[ Opcodes.local_get, index ],
|
611
651
|
[ Opcodes.local_get, leftPointer ],
|
612
652
|
[ Opcodes.i32_add ],
|
613
|
-
|
653
|
+
bytestrings ?
|
654
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
655
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
614
656
|
|
615
657
|
// fetch right
|
616
658
|
[ Opcodes.local_get, index ],
|
617
659
|
[ Opcodes.local_get, rightPointer ],
|
618
660
|
[ Opcodes.i32_add ],
|
619
|
-
|
661
|
+
bytestrings ?
|
662
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
663
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
620
664
|
|
621
665
|
// not equal, "return" false
|
622
666
|
[ Opcodes.i32_ne ],
|
@@ -625,13 +669,13 @@ const compareStrings = (scope, left, right) => {
|
|
625
669
|
[ Opcodes.br, 2 ],
|
626
670
|
[ Opcodes.end ],
|
627
671
|
|
628
|
-
// index += sizeof
|
672
|
+
// index += sizeof valtype (1 for bytestring, 2 for string)
|
629
673
|
[ Opcodes.local_get, index ],
|
630
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
674
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
631
675
|
[ Opcodes.i32_add ],
|
632
676
|
[ Opcodes.local_tee, index ],
|
633
677
|
|
634
|
-
// if index != index end (length * sizeof
|
678
|
+
// if index != index end (length * sizeof valtype), loop
|
635
679
|
[ Opcodes.local_get, indexEnd ],
|
636
680
|
[ Opcodes.i32_ne ],
|
637
681
|
[ Opcodes.br_if, 0 ],
|
@@ -652,16 +696,18 @@ const compareStrings = (scope, left, right) => {
|
|
652
696
|
};
|
653
697
|
|
654
698
|
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
655
|
-
if (
|
699
|
+
if (isFloatToIntOp(wasm[wasm.length - 1])) return [
|
656
700
|
...wasm,
|
657
701
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
658
702
|
];
|
703
|
+
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
659
704
|
|
660
|
-
const
|
705
|
+
const useTmp = knownType(scope, type) == null;
|
706
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
661
707
|
|
662
708
|
const def = [
|
663
709
|
// if value != 0
|
664
|
-
[ Opcodes.local_get, tmp ],
|
710
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
665
711
|
|
666
712
|
// ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
667
713
|
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
|
@@ -673,7 +719,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
673
719
|
|
674
720
|
return [
|
675
721
|
...wasm,
|
676
|
-
[ Opcodes.local_set, tmp ],
|
722
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
677
723
|
|
678
724
|
...typeSwitch(scope, type, {
|
679
725
|
// [TYPES.number]: def,
|
@@ -682,7 +728,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
682
728
|
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
683
729
|
],
|
684
730
|
[TYPES.string]: [
|
685
|
-
[ Opcodes.local_get, tmp ],
|
731
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
686
732
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
687
733
|
|
688
734
|
// get length
|
@@ -694,7 +740,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
694
740
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
695
741
|
],
|
696
742
|
[TYPES._bytestring]: [ // duplicate of string
|
697
|
-
|
743
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
698
744
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
699
745
|
|
700
746
|
// get length
|
@@ -708,10 +754,12 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
708
754
|
};
|
709
755
|
|
710
756
|
const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
711
|
-
const
|
757
|
+
const useTmp = knownType(scope, type) == null;
|
758
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
759
|
+
|
712
760
|
return [
|
713
761
|
...wasm,
|
714
|
-
[ Opcodes.local_set, tmp ],
|
762
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
715
763
|
|
716
764
|
...typeSwitch(scope, type, {
|
717
765
|
[TYPES._array]: [
|
@@ -719,7 +767,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
719
767
|
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
720
768
|
],
|
721
769
|
[TYPES.string]: [
|
722
|
-
[ Opcodes.local_get, tmp ],
|
770
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
723
771
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
724
772
|
|
725
773
|
// get length
|
@@ -730,7 +778,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
730
778
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
731
779
|
],
|
732
780
|
[TYPES._bytestring]: [ // duplicate of string
|
733
|
-
[ Opcodes.local_get, tmp ],
|
781
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
734
782
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
735
783
|
|
736
784
|
// get length
|
@@ -742,7 +790,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
742
790
|
],
|
743
791
|
default: [
|
744
792
|
// if value == 0
|
745
|
-
[ Opcodes.local_get, tmp ],
|
793
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
746
794
|
|
747
795
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
748
796
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -752,10 +800,12 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
752
800
|
};
|
753
801
|
|
754
802
|
const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
755
|
-
const
|
803
|
+
const useTmp = knownType(scope, type) == null;
|
804
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
805
|
+
|
756
806
|
return [
|
757
807
|
...wasm,
|
758
|
-
[ Opcodes.local_set, tmp ],
|
808
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
759
809
|
|
760
810
|
...typeSwitch(scope, type, {
|
761
811
|
[TYPES.undefined]: [
|
@@ -764,7 +814,7 @@ const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
764
814
|
],
|
765
815
|
[TYPES.object]: [
|
766
816
|
// object, null if == 0
|
767
|
-
[ Opcodes.local_get, tmp ],
|
817
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
768
818
|
|
769
819
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
770
820
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -845,11 +895,11 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
845
895
|
// todo: if equality op and an operand is undefined, return false
|
846
896
|
// todo: niche null hell with 0
|
847
897
|
|
848
|
-
// todo: this should be dynamic but for now only static
|
849
898
|
if (knownLeft === TYPES.string || knownRight === TYPES.string) {
|
850
899
|
if (op === '+') {
|
900
|
+
// todo: this should be dynamic too but for now only static
|
851
901
|
// string concat (a + b)
|
852
|
-
return concatStrings(scope, left, right, _global, _name, assign);
|
902
|
+
return concatStrings(scope, left, right, _global, _name, assign, false);
|
853
903
|
}
|
854
904
|
|
855
905
|
// not an equality op, NaN
|
@@ -872,6 +922,33 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
872
922
|
}
|
873
923
|
}
|
874
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
|
+
|
875
952
|
let ops = operatorOpcode[valtype][op];
|
876
953
|
|
877
954
|
// some complex ops are implemented as builtin funcs
|
@@ -887,23 +964,62 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
887
964
|
]);
|
888
965
|
}
|
889
966
|
|
890
|
-
if (!ops) return todo(`operator ${op} not implemented yet
|
967
|
+
if (!ops) return todo(scope, `operator ${op} not implemented yet`, true);
|
891
968
|
|
892
969
|
if (!Array.isArray(ops)) ops = [ ops ];
|
893
970
|
ops = [ ops ];
|
894
971
|
|
895
972
|
let tmpLeft, tmpRight;
|
896
973
|
// if equal op, check if strings for compareStrings
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
|
901
|
-
return;
|
902
|
-
}
|
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
|
903
977
|
|
978
|
+
if ((op === '===' || op === '==' || op === '!==' || op === '!=') && (knownLeft == null && knownRight == null)) {
|
904
979
|
tmpLeft = localTmp(scope, '__tmpop_left');
|
905
980
|
tmpRight = localTmp(scope, '__tmpop_right');
|
906
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)
|
907
1023
|
ops.unshift(...stringOnly([
|
908
1024
|
// if left is string
|
909
1025
|
...leftType,
|
@@ -915,30 +1031,28 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
915
1031
|
...number(TYPES.string, Valtype.i32),
|
916
1032
|
[ Opcodes.i32_eq ],
|
917
1033
|
|
918
|
-
// if
|
919
|
-
[ Opcodes.
|
1034
|
+
// if both are true
|
1035
|
+
[ Opcodes.i32_and ],
|
920
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 ],
|
921
1041
|
|
922
|
-
//
|
923
|
-
// if left is not string
|
1042
|
+
// if left is bytestring
|
924
1043
|
...leftType,
|
925
|
-
...number(TYPES.
|
926
|
-
[ Opcodes.
|
1044
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1045
|
+
[ Opcodes.i32_eq ],
|
927
1046
|
|
928
|
-
// if right is
|
1047
|
+
// if right is bytestring
|
929
1048
|
...rightType,
|
930
|
-
...number(TYPES.
|
931
|
-
[ Opcodes.
|
1049
|
+
...number(TYPES._bytestring, Valtype.i32),
|
1050
|
+
[ Opcodes.i32_eq ],
|
932
1051
|
|
933
|
-
// if
|
934
|
-
[ Opcodes.
|
1052
|
+
// if both are true
|
1053
|
+
[ Opcodes.i32_and ],
|
935
1054
|
[ Opcodes.if, Blocktype.void ],
|
936
|
-
...
|
937
|
-
[ Opcodes.br, 2 ],
|
938
|
-
[ Opcodes.end ],
|
939
|
-
|
940
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
941
|
-
// ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1055
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], true),
|
942
1056
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
943
1057
|
[ Opcodes.br, 1 ],
|
944
1058
|
[ Opcodes.end ],
|
@@ -950,7 +1064,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
950
1064
|
// endOut.push(stringOnly([ Opcodes.end ]));
|
951
1065
|
endOut.unshift(stringOnly([ Opcodes.end ]));
|
952
1066
|
// }
|
953
|
-
}
|
1067
|
+
}
|
954
1068
|
|
955
1069
|
return finalize([
|
956
1070
|
...left,
|
@@ -969,7 +1083,22 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
969
1083
|
return out;
|
970
1084
|
};
|
971
1085
|
|
972
|
-
const
|
1086
|
+
const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals = [], returns = [], localInd = 0 }) => {
|
1087
|
+
return func({ name, params, locals, returns, localInd }, {
|
1088
|
+
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage,
|
1089
|
+
builtin: name => {
|
1090
|
+
let idx = funcIndex[name] ?? importedFuncs[name];
|
1091
|
+
if (idx === undefined && builtinFuncs[name]) {
|
1092
|
+
includeBuiltin(null, name);
|
1093
|
+
idx = funcIndex[name];
|
1094
|
+
}
|
1095
|
+
|
1096
|
+
return idx;
|
1097
|
+
}
|
1098
|
+
});
|
1099
|
+
};
|
1100
|
+
|
1101
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
973
1102
|
const existing = funcs.find(x => x.name === name);
|
974
1103
|
if (existing) return existing;
|
975
1104
|
|
@@ -981,18 +1110,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
981
1110
|
locals[nameParam(i)] = { idx: i, type: allLocals[i] };
|
982
1111
|
}
|
983
1112
|
|
984
|
-
|
985
|
-
const
|
986
|
-
|
987
|
-
|
988
|
-
locals,
|
989
|
-
returns,
|
990
|
-
localInd: allLocals.length,
|
991
|
-
};
|
992
|
-
|
993
|
-
wasm = wasm(scope, { TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString });
|
1113
|
+
for (const x of _data) {
|
1114
|
+
const copy = { ...x };
|
1115
|
+
copy.offset += pages.size * pageSize;
|
1116
|
+
data.push(copy);
|
994
1117
|
}
|
995
1118
|
|
1119
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name, params, locals, returns, localInd: allLocals.length });
|
1120
|
+
|
996
1121
|
let baseGlobalIdx, i = 0;
|
997
1122
|
for (const type of globalTypes) {
|
998
1123
|
if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
|
@@ -1015,7 +1140,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1015
1140
|
params,
|
1016
1141
|
locals,
|
1017
1142
|
returns,
|
1018
|
-
returnType:
|
1143
|
+
returnType: returnType ?? TYPES.number,
|
1019
1144
|
wasm,
|
1020
1145
|
internal: true,
|
1021
1146
|
index: currentFuncIndex++
|
@@ -1038,6 +1163,7 @@ const generateLogicExp = (scope, decl) => {
|
|
1038
1163
|
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
1039
1164
|
};
|
1040
1165
|
|
1166
|
+
// potential future ideas for nan boxing (unused):
|
1041
1167
|
// T = JS type, V = value/pointer
|
1042
1168
|
// 0bTTT
|
1043
1169
|
// qNAN: 0 11111111111 1000000000000000000000000000000000000000000000000001
|
@@ -1061,40 +1187,18 @@ const generateLogicExp = (scope, decl) => {
|
|
1061
1187
|
// 4: internal type
|
1062
1188
|
// 5: pointer
|
1063
1189
|
|
1064
|
-
const
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
object: 0x04,
|
1070
|
-
function: 0x05,
|
1071
|
-
symbol: 0x06,
|
1072
|
-
bigint: 0x07,
|
1073
|
-
|
1074
|
-
// these are not "typeof" types but tracked internally
|
1075
|
-
_array: 0x10,
|
1076
|
-
_regexp: 0x11,
|
1077
|
-
_bytestring: 0x12
|
1078
|
-
};
|
1079
|
-
|
1080
|
-
const TYPE_NAMES = {
|
1081
|
-
[TYPES.number]: 'Number',
|
1082
|
-
[TYPES.boolean]: 'Boolean',
|
1083
|
-
[TYPES.string]: 'String',
|
1084
|
-
[TYPES.undefined]: 'undefined',
|
1085
|
-
[TYPES.object]: 'Object',
|
1086
|
-
[TYPES.function]: 'Function',
|
1087
|
-
[TYPES.symbol]: 'Symbol',
|
1088
|
-
[TYPES.bigint]: 'BigInt',
|
1089
|
-
|
1090
|
-
[TYPES._array]: 'Array',
|
1091
|
-
[TYPES._regexp]: 'RegExp',
|
1092
|
-
[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;
|
1093
1195
|
};
|
1094
1196
|
|
1095
1197
|
const getType = (scope, _name) => {
|
1096
1198
|
const name = mapName(_name);
|
1097
1199
|
|
1200
|
+
// if (scope.locals[name] && !scope.locals[name + '#type']) console.log(name);
|
1201
|
+
|
1098
1202
|
if (typedInput && scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
|
1099
1203
|
if (scope.locals[name]) return [ [ Opcodes.local_get, scope.locals[name + '#type'].idx ] ];
|
1100
1204
|
|
@@ -1102,11 +1206,10 @@ const getType = (scope, _name) => {
|
|
1102
1206
|
if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
|
1103
1207
|
|
1104
1208
|
let type = TYPES.undefined;
|
1105
|
-
if (builtinVars[name]) type =
|
1209
|
+
if (builtinVars[name]) type = builtinVars[name].type ?? TYPES.number;
|
1106
1210
|
if (builtinFuncs[name] !== undefined || importedFuncs[name] !== undefined || funcIndex[name] !== undefined || internalConstrs[name] !== undefined) type = TYPES.function;
|
1107
1211
|
|
1108
|
-
if (name
|
1109
|
-
name.startsWith('__String_prototype_') && prototypeFuncs[TYPES.string][name.slice(19)]) type = TYPES.function;
|
1212
|
+
if (isExistingProtoFunc(name)) type = TYPES.function;
|
1110
1213
|
|
1111
1214
|
return number(type, Valtype.i32);
|
1112
1215
|
};
|
@@ -1129,15 +1232,16 @@ const setType = (scope, _name, type) => {
|
|
1129
1232
|
];
|
1130
1233
|
|
1131
1234
|
// throw new Error('could not find var');
|
1235
|
+
return [];
|
1132
1236
|
};
|
1133
1237
|
|
1134
1238
|
const getLastType = scope => {
|
1135
1239
|
scope.gotLastType = true;
|
1136
|
-
return [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ];
|
1240
|
+
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1137
1241
|
};
|
1138
1242
|
|
1139
1243
|
const setLastType = scope => {
|
1140
|
-
return [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ];
|
1244
|
+
return [ [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1141
1245
|
};
|
1142
1246
|
|
1143
1247
|
const getNodeType = (scope, node) => {
|
@@ -1162,13 +1266,25 @@ const getNodeType = (scope, node) => {
|
|
1162
1266
|
const name = node.callee.name;
|
1163
1267
|
if (!name) {
|
1164
1268
|
// iife
|
1165
|
-
if (scope.locals['#last_type']) return
|
1269
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1166
1270
|
|
1167
1271
|
// presume
|
1168
1272
|
// todo: warn here?
|
1169
1273
|
return TYPES.number;
|
1170
1274
|
}
|
1171
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
|
+
|
1172
1288
|
const func = funcs.find(x => x.name === name);
|
1173
1289
|
|
1174
1290
|
if (func) {
|
@@ -1176,7 +1292,7 @@ const getNodeType = (scope, node) => {
|
|
1176
1292
|
if (func.returnType) return func.returnType;
|
1177
1293
|
}
|
1178
1294
|
|
1179
|
-
if (builtinFuncs[name]) return
|
1295
|
+
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
|
1180
1296
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1181
1297
|
|
1182
1298
|
// check if this is a prototype function
|
@@ -1191,7 +1307,12 @@ const getNodeType = (scope, node) => {
|
|
1191
1307
|
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1192
1308
|
}
|
1193
1309
|
|
1194
|
-
if (
|
1310
|
+
if (name.startsWith('__Porffor_wasm_')) {
|
1311
|
+
// todo: return undefined for non-returning ops
|
1312
|
+
return TYPES.number;
|
1313
|
+
}
|
1314
|
+
|
1315
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1195
1316
|
|
1196
1317
|
// presume
|
1197
1318
|
// todo: warn here?
|
@@ -1246,6 +1367,7 @@ const getNodeType = (scope, node) => {
|
|
1246
1367
|
|
1247
1368
|
// todo: this should be dynamic but for now only static
|
1248
1369
|
if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
|
1370
|
+
if (knownLeft === TYPES._bytestring || knownRight === TYPES._bytestring) return TYPES._bytestring;
|
1249
1371
|
|
1250
1372
|
return TYPES.number;
|
1251
1373
|
|
@@ -1282,15 +1404,21 @@ const getNodeType = (scope, node) => {
|
|
1282
1404
|
|
1283
1405
|
// ts hack
|
1284
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;
|
1285
1408
|
if (scope.locals[node.object.name]?.metadata?.type === TYPES._array) return TYPES.number;
|
1286
1409
|
|
1287
|
-
if (scope.locals['#last_type']) return
|
1410
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1288
1411
|
|
1289
1412
|
// presume
|
1290
1413
|
return TYPES.number;
|
1291
1414
|
}
|
1292
1415
|
|
1293
|
-
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);
|
1294
1422
|
|
1295
1423
|
// presume
|
1296
1424
|
// todo: warn here?
|
@@ -1323,7 +1451,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1323
1451
|
return makeString(scope, decl.value, global, name);
|
1324
1452
|
|
1325
1453
|
default:
|
1326
|
-
return todo(`cannot generate literal of type ${typeof decl.value}
|
1454
|
+
return todo(scope, `cannot generate literal of type ${typeof decl.value}`, true);
|
1327
1455
|
}
|
1328
1456
|
};
|
1329
1457
|
|
@@ -1332,6 +1460,8 @@ const countLeftover = wasm => {
|
|
1332
1460
|
|
1333
1461
|
for (let i = 0; i < wasm.length; i++) {
|
1334
1462
|
const inst = wasm[i];
|
1463
|
+
if (inst[0] == null) continue;
|
1464
|
+
|
1335
1465
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
1336
1466
|
if (inst[0] === Opcodes.if) count--;
|
1337
1467
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -1340,18 +1470,25 @@ const countLeftover = wasm => {
|
|
1340
1470
|
if (inst[0] === Opcodes.end) depth--;
|
1341
1471
|
|
1342
1472
|
if (depth === 0)
|
1343
|
-
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--;
|
1344
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)) {}
|
1345
|
-
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++;
|
1346
1476
|
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1347
1477
|
else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
|
1348
1478
|
else if (inst[0] === Opcodes.return) count = 0;
|
1349
1479
|
else if (inst[0] === Opcodes.call) {
|
1350
1480
|
let func = funcs.find(x => x.index === inst[1]);
|
1351
|
-
if (
|
1352
|
-
count
|
1353
|
-
} else
|
1354
|
-
|
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
|
+
}
|
1355
1492
|
} else count--;
|
1356
1493
|
|
1357
1494
|
// console.log(count, decompile([ inst ]).slice(0, -1));
|
@@ -1443,10 +1580,21 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1443
1580
|
name = func.name;
|
1444
1581
|
}
|
1445
1582
|
|
1446
|
-
if (name === 'eval' && decl.arguments[0]
|
1583
|
+
if (name === 'eval' && decl.arguments[0]?.type === 'Literal') {
|
1447
1584
|
// literal eval hack
|
1448
|
-
const code = decl.arguments[0]
|
1449
|
-
|
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
|
+
}
|
1450
1598
|
|
1451
1599
|
const out = generate(scope, {
|
1452
1600
|
type: 'BlockStatement',
|
@@ -1460,13 +1608,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1460
1608
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1461
1609
|
out.push(
|
1462
1610
|
...getNodeType(scope, finalStatement),
|
1463
|
-
setLastType(scope)
|
1611
|
+
...setLastType(scope)
|
1464
1612
|
);
|
1465
1613
|
} else if (countLeftover(out) === 0) {
|
1466
1614
|
out.push(...number(UNDEFINED));
|
1467
1615
|
out.push(
|
1468
1616
|
...number(TYPES.undefined, Valtype.i32),
|
1469
|
-
setLastType(scope)
|
1617
|
+
...setLastType(scope)
|
1470
1618
|
);
|
1471
1619
|
}
|
1472
1620
|
|
@@ -1488,6 +1636,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1488
1636
|
|
1489
1637
|
target = { ...decl.callee };
|
1490
1638
|
target.name = spl.slice(0, -1).join('_');
|
1639
|
+
|
1640
|
+
// failed to lookup name, abort
|
1641
|
+
if (!lookupName(scope, target.name)[0]) protoName = null;
|
1491
1642
|
}
|
1492
1643
|
|
1493
1644
|
// literal.func()
|
@@ -1510,7 +1661,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1510
1661
|
Opcodes.i32_from_u,
|
1511
1662
|
|
1512
1663
|
...number(TYPES.boolean, Valtype.i32),
|
1513
|
-
setLastType(scope)
|
1664
|
+
...setLastType(scope)
|
1514
1665
|
];
|
1515
1666
|
}
|
1516
1667
|
|
@@ -1535,12 +1686,31 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1535
1686
|
// }
|
1536
1687
|
|
1537
1688
|
if (protoName) {
|
1689
|
+
const protoBC = {};
|
1690
|
+
|
1691
|
+
const builtinProtoCands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + protoName));
|
1692
|
+
|
1693
|
+
if (!decl._protoInternalCall && builtinProtoCands.length > 0) {
|
1694
|
+
for (const x of builtinProtoCands) {
|
1695
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
1696
|
+
if (type == null) continue;
|
1697
|
+
|
1698
|
+
protoBC[type] = generateCall(scope, {
|
1699
|
+
callee: {
|
1700
|
+
type: 'Identifier',
|
1701
|
+
name: x
|
1702
|
+
},
|
1703
|
+
arguments: [ target, ...decl.arguments ],
|
1704
|
+
_protoInternalCall: true
|
1705
|
+
});
|
1706
|
+
}
|
1707
|
+
}
|
1708
|
+
|
1538
1709
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1539
1710
|
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1540
1711
|
return acc;
|
1541
1712
|
}, {});
|
1542
1713
|
|
1543
|
-
// no prototype function candidates, ignore
|
1544
1714
|
if (Object.keys(protoCands).length > 0) {
|
1545
1715
|
// use local for cached i32 length as commonly used
|
1546
1716
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
@@ -1558,7 +1728,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1558
1728
|
|
1559
1729
|
let allOptUnused = true;
|
1560
1730
|
let lengthI32CacheUsed = false;
|
1561
|
-
const protoBC = {};
|
1562
1731
|
for (const x in protoCands) {
|
1563
1732
|
const protoFunc = protoCands[x];
|
1564
1733
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
@@ -1566,7 +1735,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1566
1735
|
...RTArrayUtil.getLength(getPointer),
|
1567
1736
|
|
1568
1737
|
...number(TYPES.number, Valtype.i32),
|
1569
|
-
setLastType(scope)
|
1738
|
+
...setLastType(scope)
|
1570
1739
|
];
|
1571
1740
|
continue;
|
1572
1741
|
}
|
@@ -1603,7 +1772,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1603
1772
|
...protoOut,
|
1604
1773
|
|
1605
1774
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1606
|
-
setLastType(scope),
|
1775
|
+
...setLastType(scope),
|
1607
1776
|
[ Opcodes.end ]
|
1608
1777
|
];
|
1609
1778
|
}
|
@@ -1629,10 +1798,19 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1629
1798
|
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1630
1799
|
];
|
1631
1800
|
}
|
1801
|
+
|
1802
|
+
if (Object.keys(protoBC).length > 0) {
|
1803
|
+
return typeSwitch(scope, getNodeType(scope, target), {
|
1804
|
+
...protoBC,
|
1805
|
+
|
1806
|
+
// TODO: error better
|
1807
|
+
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1808
|
+
}, valtypeBinary);
|
1809
|
+
}
|
1632
1810
|
}
|
1633
1811
|
|
1634
1812
|
// TODO: only allows callee as literal
|
1635
|
-
if (!name) return todo(`only literal callees (got ${decl.callee.type})`);
|
1813
|
+
if (!name) return todo(scope, `only literal callees (got ${decl.callee.type})`);
|
1636
1814
|
|
1637
1815
|
let idx = funcIndex[name] ?? importedFuncs[name];
|
1638
1816
|
if (idx === undefined && builtinFuncs[name]) {
|
@@ -1665,16 +1843,65 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1665
1843
|
idx = -1;
|
1666
1844
|
}
|
1667
1845
|
|
1846
|
+
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1847
|
+
const wasmOps = {
|
1848
|
+
// pointer, align, offset
|
1849
|
+
i32_load: { imms: 2, args: [ true ], returns: 1 },
|
1850
|
+
// pointer, value, align, offset
|
1851
|
+
i32_store: { imms: 2, args: [ true, true ], returns: 0 },
|
1852
|
+
// pointer, align, offset
|
1853
|
+
i32_load8_u: { imms: 2, args: [ true ], returns: 1 },
|
1854
|
+
// pointer, value, align, offset
|
1855
|
+
i32_store8: { imms: 2, args: [ true, true ], returns: 0 },
|
1856
|
+
// pointer, align, offset
|
1857
|
+
i32_load16_u: { imms: 2, args: [ true ], returns: 1 },
|
1858
|
+
// pointer, value, align, offset
|
1859
|
+
i32_store16: { imms: 2, args: [ true, true ], returns: 0 },
|
1860
|
+
|
1861
|
+
// pointer, align, offset
|
1862
|
+
f64_load: { imms: 2, args: [ true ], returns: 0 }, // 0 due to not i32
|
1863
|
+
// pointer, value, align, offset
|
1864
|
+
f64_store: { imms: 2, args: [ true, false ], returns: 0 },
|
1865
|
+
|
1866
|
+
// value
|
1867
|
+
i32_const: { imms: 1, args: [], returns: 1 },
|
1868
|
+
|
1869
|
+
// a, b
|
1870
|
+
i32_or: { imms: 0, args: [ true, true ], returns: 1 },
|
1871
|
+
};
|
1872
|
+
|
1873
|
+
const opName = name.slice('__Porffor_wasm_'.length);
|
1874
|
+
|
1875
|
+
if (wasmOps[opName]) {
|
1876
|
+
const op = wasmOps[opName];
|
1877
|
+
|
1878
|
+
const argOut = [];
|
1879
|
+
for (let i = 0; i < op.args.length; i++) argOut.push(
|
1880
|
+
...generate(scope, decl.arguments[i]),
|
1881
|
+
...(op.args[i] ? [ Opcodes.i32_to ] : [])
|
1882
|
+
);
|
1883
|
+
|
1884
|
+
// literals only
|
1885
|
+
const imms = decl.arguments.slice(op.args.length).map(x => x.value);
|
1886
|
+
|
1887
|
+
return [
|
1888
|
+
...argOut,
|
1889
|
+
[ Opcodes[opName], ...imms ],
|
1890
|
+
...(new Array(op.returns).fill(Opcodes.i32_from))
|
1891
|
+
];
|
1892
|
+
}
|
1893
|
+
}
|
1894
|
+
|
1668
1895
|
if (idx === undefined) {
|
1669
|
-
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function
|
1670
|
-
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined
|
1896
|
+
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1897
|
+
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1671
1898
|
}
|
1672
1899
|
|
1673
1900
|
const func = funcs.find(x => x.index === idx);
|
1674
1901
|
|
1675
1902
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1676
1903
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1677
|
-
const typedReturns = userFunc || builtinFuncs[name]?.typedReturns;
|
1904
|
+
const typedReturns = (func ? func.returnType == null : userFunc) || builtinFuncs[name]?.typedReturns;
|
1678
1905
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1679
1906
|
|
1680
1907
|
let args = decl.arguments;
|
@@ -1691,8 +1918,18 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1691
1918
|
if (func && func.throws) scope.throws = true;
|
1692
1919
|
|
1693
1920
|
let out = [];
|
1694
|
-
for (
|
1921
|
+
for (let i = 0; i < args.length; i++) {
|
1922
|
+
const arg = args[i];
|
1695
1923
|
out = out.concat(generate(scope, arg));
|
1924
|
+
|
1925
|
+
if (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1926
|
+
out.push(Opcodes.i32_to);
|
1927
|
+
}
|
1928
|
+
|
1929
|
+
if (importedFuncs[name] && name.startsWith('profile')) {
|
1930
|
+
out.push(Opcodes.i32_to);
|
1931
|
+
}
|
1932
|
+
|
1696
1933
|
if (typedParams) out = out.concat(getNodeType(scope, arg));
|
1697
1934
|
}
|
1698
1935
|
|
@@ -1708,7 +1945,11 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1708
1945
|
// ...number(type, Valtype.i32),
|
1709
1946
|
// [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1710
1947
|
// );
|
1711
|
-
} else out.push(setLastType(scope));
|
1948
|
+
} else out.push(...setLastType(scope));
|
1949
|
+
|
1950
|
+
if (builtinFuncs[name] && builtinFuncs[name].returns?.[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1951
|
+
out.push(Opcodes.i32_from);
|
1952
|
+
}
|
1712
1953
|
|
1713
1954
|
return out;
|
1714
1955
|
};
|
@@ -1716,8 +1957,21 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1716
1957
|
const generateNew = (scope, decl, _global, _name) => {
|
1717
1958
|
// hack: basically treat this as a normal call for builtins for now
|
1718
1959
|
const name = mapName(decl.callee.name);
|
1960
|
+
|
1719
1961
|
if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1720
|
-
|
1962
|
+
|
1963
|
+
if (builtinFuncs[name + '$constructor']) {
|
1964
|
+
// custom ...$constructor override builtin func
|
1965
|
+
return generateCall(scope, {
|
1966
|
+
...decl,
|
1967
|
+
callee: {
|
1968
|
+
type: 'Identifier',
|
1969
|
+
name: name + '$constructor'
|
1970
|
+
}
|
1971
|
+
}, _global, _name);
|
1972
|
+
}
|
1973
|
+
|
1974
|
+
if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
|
1721
1975
|
|
1722
1976
|
return generateCall(scope, decl, _global, _name);
|
1723
1977
|
};
|
@@ -1851,8 +2105,6 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1851
2105
|
[ Opcodes.block, returns ]
|
1852
2106
|
];
|
1853
2107
|
|
1854
|
-
// todo: use br_table?
|
1855
|
-
|
1856
2108
|
for (const x in bc) {
|
1857
2109
|
if (x === 'default') continue;
|
1858
2110
|
|
@@ -1908,11 +2160,14 @@ const addVarMetadata = (scope, name, global = false, metadata = {}) => {
|
|
1908
2160
|
};
|
1909
2161
|
|
1910
2162
|
const typeAnnoToPorfType = x => {
|
1911
|
-
if (
|
1912
|
-
if (TYPES[
|
2163
|
+
if (!x) return null;
|
2164
|
+
if (TYPES[x.toLowerCase()] != null) return TYPES[x.toLowerCase()];
|
2165
|
+
if (TYPES['_' + x.toLowerCase()] != null) return TYPES['_' + x.toLowerCase()];
|
1913
2166
|
|
1914
2167
|
switch (x) {
|
1915
2168
|
case 'i32':
|
2169
|
+
case 'i64':
|
2170
|
+
case 'f64':
|
1916
2171
|
return TYPES.number;
|
1917
2172
|
}
|
1918
2173
|
|
@@ -1923,7 +2178,7 @@ const extractTypeAnnotation = decl => {
|
|
1923
2178
|
let a = decl;
|
1924
2179
|
while (a.typeAnnotation) a = a.typeAnnotation;
|
1925
2180
|
|
1926
|
-
let type, elementType;
|
2181
|
+
let type = null, elementType = null;
|
1927
2182
|
if (a.typeName) {
|
1928
2183
|
type = a.typeName.name;
|
1929
2184
|
} else if (a.type.endsWith('Keyword')) {
|
@@ -1954,7 +2209,7 @@ const generateVar = (scope, decl) => {
|
|
1954
2209
|
for (const x of decl.declarations) {
|
1955
2210
|
const name = mapName(x.id.name);
|
1956
2211
|
|
1957
|
-
if (!name) return todo('destructuring is not supported yet');
|
2212
|
+
if (!name) return todo(scope, 'destructuring is not supported yet');
|
1958
2213
|
|
1959
2214
|
if (x.init && isFuncType(x.init.type)) {
|
1960
2215
|
// hack for let a = function () { ... }
|
@@ -1971,9 +2226,10 @@ const generateVar = (scope, decl) => {
|
|
1971
2226
|
continue; // always ignore
|
1972
2227
|
}
|
1973
2228
|
|
1974
|
-
|
2229
|
+
const typed = typedInput && x.id.typeAnnotation;
|
2230
|
+
let idx = allocVar(scope, name, global, !(typed && extractTypeAnnotation(x.id).type != null));
|
1975
2231
|
|
1976
|
-
if (
|
2232
|
+
if (typed) {
|
1977
2233
|
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
1978
2234
|
}
|
1979
2235
|
|
@@ -1991,7 +2247,8 @@ const generateVar = (scope, decl) => {
|
|
1991
2247
|
return out;
|
1992
2248
|
};
|
1993
2249
|
|
1994
|
-
|
2250
|
+
// todo: optimize this func for valueUnused
|
2251
|
+
const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
1995
2252
|
const { type, name } = decl.left;
|
1996
2253
|
|
1997
2254
|
if (type === 'ObjectPattern') {
|
@@ -2009,9 +2266,9 @@ const generateAssign = (scope, decl) => {
|
|
2009
2266
|
// hack: .length setter
|
2010
2267
|
if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
|
2011
2268
|
const name = decl.left.object.name;
|
2012
|
-
const pointer = arrays
|
2269
|
+
const pointer = scope.arrays?.get(name);
|
2013
2270
|
|
2014
|
-
const aotPointer = pointer != null;
|
2271
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2015
2272
|
|
2016
2273
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
2017
2274
|
|
@@ -2036,9 +2293,9 @@ const generateAssign = (scope, decl) => {
|
|
2036
2293
|
// arr[i]
|
2037
2294
|
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
2038
2295
|
const name = decl.left.object.name;
|
2039
|
-
const pointer = arrays
|
2296
|
+
const pointer = scope.arrays?.get(name);
|
2040
2297
|
|
2041
|
-
const aotPointer = pointer != null;
|
2298
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2042
2299
|
|
2043
2300
|
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
2044
2301
|
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
@@ -2094,7 +2351,7 @@ const generateAssign = (scope, decl) => {
|
|
2094
2351
|
];
|
2095
2352
|
}
|
2096
2353
|
|
2097
|
-
if (!name) return todo('destructuring is not supported yet');
|
2354
|
+
if (!name) return todo(scope, 'destructuring is not supported yet', true);
|
2098
2355
|
|
2099
2356
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2100
2357
|
|
@@ -2142,9 +2399,7 @@ const generateAssign = (scope, decl) => {
|
|
2142
2399
|
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
2143
2400
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
2144
2401
|
|
2145
|
-
getLastType(scope)
|
2146
|
-
// hack: type is idx+1
|
2147
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2402
|
+
...setType(scope, name, getLastType(scope))
|
2148
2403
|
];
|
2149
2404
|
}
|
2150
2405
|
|
@@ -2155,9 +2410,7 @@ const generateAssign = (scope, decl) => {
|
|
2155
2410
|
|
2156
2411
|
// todo: string concat types
|
2157
2412
|
|
2158
|
-
|
2159
|
-
...number(TYPES.number, Valtype.i32),
|
2160
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2413
|
+
...setType(scope, name, TYPES.number)
|
2161
2414
|
];
|
2162
2415
|
};
|
2163
2416
|
|
@@ -2203,7 +2456,7 @@ const generateUnary = (scope, decl) => {
|
|
2203
2456
|
return out;
|
2204
2457
|
}
|
2205
2458
|
|
2206
|
-
case 'delete':
|
2459
|
+
case 'delete': {
|
2207
2460
|
let toReturn = true, toGenerate = true;
|
2208
2461
|
|
2209
2462
|
if (decl.argument.type === 'Identifier') {
|
@@ -2225,9 +2478,26 @@ const generateUnary = (scope, decl) => {
|
|
2225
2478
|
|
2226
2479
|
out.push(...number(toReturn ? 1 : 0));
|
2227
2480
|
return out;
|
2481
|
+
}
|
2482
|
+
|
2483
|
+
case 'typeof': {
|
2484
|
+
let overrideType, toGenerate = true;
|
2485
|
+
|
2486
|
+
if (decl.argument.type === 'Identifier') {
|
2487
|
+
const out = generateIdent(scope, decl.argument);
|
2488
|
+
|
2489
|
+
// if ReferenceError (undeclared var), ignore and return undefined
|
2490
|
+
if (out[1]) {
|
2491
|
+
// does not exist (2 ops from throw)
|
2492
|
+
overrideType = number(TYPES.undefined, Valtype.i32);
|
2493
|
+
toGenerate = false;
|
2494
|
+
}
|
2495
|
+
}
|
2496
|
+
|
2497
|
+
const out = toGenerate ? generate(scope, decl.argument) : [];
|
2498
|
+
disposeLeftover(out);
|
2228
2499
|
|
2229
|
-
|
2230
|
-
return typeSwitch(scope, getNodeType(scope, decl.argument), {
|
2500
|
+
out.push(...typeSwitch(scope, overrideType ?? getNodeType(scope, decl.argument), {
|
2231
2501
|
[TYPES.number]: makeString(scope, 'number', false, '#typeof_result'),
|
2232
2502
|
[TYPES.boolean]: makeString(scope, 'boolean', false, '#typeof_result'),
|
2233
2503
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
@@ -2238,27 +2508,30 @@ const generateUnary = (scope, decl) => {
|
|
2238
2508
|
|
2239
2509
|
// object and internal types
|
2240
2510
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
2241
|
-
});
|
2511
|
+
}));
|
2512
|
+
|
2513
|
+
return out;
|
2514
|
+
}
|
2242
2515
|
|
2243
2516
|
default:
|
2244
|
-
return todo(`unary operator ${decl.operator} not implemented yet
|
2517
|
+
return todo(scope, `unary operator ${decl.operator} not implemented yet`, true);
|
2245
2518
|
}
|
2246
2519
|
};
|
2247
2520
|
|
2248
|
-
const generateUpdate = (scope, decl) => {
|
2521
|
+
const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
|
2249
2522
|
const { name } = decl.argument;
|
2250
2523
|
|
2251
2524
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2252
2525
|
|
2253
2526
|
if (local === undefined) {
|
2254
|
-
return todo(`update expression with undefined variable
|
2527
|
+
return todo(scope, `update expression with undefined variable`, true);
|
2255
2528
|
}
|
2256
2529
|
|
2257
2530
|
const idx = local.idx;
|
2258
2531
|
const out = [];
|
2259
2532
|
|
2260
2533
|
out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2261
|
-
if (!decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2534
|
+
if (!decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2262
2535
|
|
2263
2536
|
switch (decl.operator) {
|
2264
2537
|
case '++':
|
@@ -2271,7 +2544,7 @@ const generateUpdate = (scope, decl) => {
|
|
2271
2544
|
}
|
2272
2545
|
|
2273
2546
|
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2274
|
-
if (decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2547
|
+
if (decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2275
2548
|
|
2276
2549
|
return out;
|
2277
2550
|
};
|
@@ -2311,7 +2584,7 @@ const generateConditional = (scope, decl) => {
|
|
2311
2584
|
// note type
|
2312
2585
|
out.push(
|
2313
2586
|
...getNodeType(scope, decl.consequent),
|
2314
|
-
setLastType(scope)
|
2587
|
+
...setLastType(scope)
|
2315
2588
|
);
|
2316
2589
|
|
2317
2590
|
out.push([ Opcodes.else ]);
|
@@ -2320,7 +2593,7 @@ const generateConditional = (scope, decl) => {
|
|
2320
2593
|
// note type
|
2321
2594
|
out.push(
|
2322
2595
|
...getNodeType(scope, decl.alternate),
|
2323
|
-
setLastType(scope)
|
2596
|
+
...setLastType(scope)
|
2324
2597
|
);
|
2325
2598
|
|
2326
2599
|
out.push([ Opcodes.end ]);
|
@@ -2334,7 +2607,7 @@ const generateFor = (scope, decl) => {
|
|
2334
2607
|
const out = [];
|
2335
2608
|
|
2336
2609
|
if (decl.init) {
|
2337
|
-
out.push(...generate(scope, decl.init));
|
2610
|
+
out.push(...generate(scope, decl.init, false, undefined, true));
|
2338
2611
|
disposeLeftover(out);
|
2339
2612
|
}
|
2340
2613
|
|
@@ -2352,7 +2625,7 @@ const generateFor = (scope, decl) => {
|
|
2352
2625
|
out.push(...generate(scope, decl.body));
|
2353
2626
|
out.push([ Opcodes.end ]);
|
2354
2627
|
|
2355
|
-
if (decl.update) out.push(...generate(scope, decl.update));
|
2628
|
+
if (decl.update) out.push(...generate(scope, decl.update, false, undefined, true));
|
2356
2629
|
|
2357
2630
|
out.push([ Opcodes.br, 1 ]);
|
2358
2631
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2380,6 +2653,36 @@ const generateWhile = (scope, decl) => {
|
|
2380
2653
|
return out;
|
2381
2654
|
};
|
2382
2655
|
|
2656
|
+
const generateDoWhile = (scope, decl) => {
|
2657
|
+
const out = [];
|
2658
|
+
|
2659
|
+
out.push([ Opcodes.loop, Blocktype.void ]);
|
2660
|
+
depth.push('dowhile');
|
2661
|
+
|
2662
|
+
// block for break (includes all)
|
2663
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2664
|
+
depth.push('block');
|
2665
|
+
|
2666
|
+
// block for continue
|
2667
|
+
// includes body but not test+loop so we can exit body at anytime
|
2668
|
+
// and still test+loop after
|
2669
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2670
|
+
depth.push('block');
|
2671
|
+
|
2672
|
+
out.push(...generate(scope, decl.body));
|
2673
|
+
|
2674
|
+
out.push([ Opcodes.end ]);
|
2675
|
+
depth.pop();
|
2676
|
+
|
2677
|
+
out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2678
|
+
out.push([ Opcodes.br_if, 1 ]);
|
2679
|
+
|
2680
|
+
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
2681
|
+
depth.pop(); depth.pop();
|
2682
|
+
|
2683
|
+
return out;
|
2684
|
+
};
|
2685
|
+
|
2383
2686
|
const generateForOf = (scope, decl) => {
|
2384
2687
|
const out = [];
|
2385
2688
|
|
@@ -2416,7 +2719,10 @@ const generateForOf = (scope, decl) => {
|
|
2416
2719
|
generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
|
2417
2720
|
}
|
2418
2721
|
|
2722
|
+
// if (!leftName) console.log(decl.left?.declarations?.[0]?.id ?? decl.left);
|
2723
|
+
|
2419
2724
|
const [ local, isGlobal ] = lookupName(scope, leftName);
|
2725
|
+
if (!local) return todo(scope, 'for of failed to get left local (probably destructure)');
|
2420
2726
|
|
2421
2727
|
depth.push('block');
|
2422
2728
|
depth.push('block');
|
@@ -2425,6 +2731,7 @@ const generateForOf = (scope, decl) => {
|
|
2425
2731
|
// hack: this is naughty and will break things!
|
2426
2732
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2427
2733
|
if (pages.hasAnyString) {
|
2734
|
+
// todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
|
2428
2735
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2429
2736
|
rawElements: new Array(1)
|
2430
2737
|
}, isGlobal, leftName, true, 'i16');
|
@@ -2516,6 +2823,56 @@ const generateForOf = (scope, decl) => {
|
|
2516
2823
|
[ Opcodes.end ],
|
2517
2824
|
[ Opcodes.end ]
|
2518
2825
|
],
|
2826
|
+
[TYPES._bytestring]: [
|
2827
|
+
...setType(scope, leftName, TYPES._bytestring),
|
2828
|
+
|
2829
|
+
[ Opcodes.loop, Blocktype.void ],
|
2830
|
+
|
2831
|
+
// setup new/out array
|
2832
|
+
...newOut,
|
2833
|
+
[ Opcodes.drop ],
|
2834
|
+
|
2835
|
+
...number(0, Valtype.i32), // base 0 for store after
|
2836
|
+
|
2837
|
+
// load current string ind {arg}
|
2838
|
+
[ Opcodes.local_get, pointer ],
|
2839
|
+
[ Opcodes.local_get, counter ],
|
2840
|
+
[ Opcodes.i32_add ],
|
2841
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2842
|
+
|
2843
|
+
// store to new string ind 0
|
2844
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2845
|
+
|
2846
|
+
// return new string (page)
|
2847
|
+
...number(newPointer),
|
2848
|
+
|
2849
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2850
|
+
|
2851
|
+
[ Opcodes.block, Blocktype.void ],
|
2852
|
+
[ Opcodes.block, Blocktype.void ],
|
2853
|
+
...generate(scope, decl.body),
|
2854
|
+
[ Opcodes.end ],
|
2855
|
+
|
2856
|
+
// increment iter pointer
|
2857
|
+
// [ Opcodes.local_get, pointer ],
|
2858
|
+
// ...number(1, Valtype.i32),
|
2859
|
+
// [ Opcodes.i32_add ],
|
2860
|
+
// [ Opcodes.local_set, pointer ],
|
2861
|
+
|
2862
|
+
// increment counter by 1
|
2863
|
+
[ Opcodes.local_get, counter ],
|
2864
|
+
...number(1, Valtype.i32),
|
2865
|
+
[ Opcodes.i32_add ],
|
2866
|
+
[ Opcodes.local_tee, counter ],
|
2867
|
+
|
2868
|
+
// loop if counter != length
|
2869
|
+
[ Opcodes.local_get, length ],
|
2870
|
+
[ Opcodes.i32_ne ],
|
2871
|
+
[ Opcodes.br_if, 1 ],
|
2872
|
+
|
2873
|
+
[ Opcodes.end ],
|
2874
|
+
[ Opcodes.end ]
|
2875
|
+
],
|
2519
2876
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2520
2877
|
}, Blocktype.void));
|
2521
2878
|
|
@@ -2526,28 +2883,65 @@ const generateForOf = (scope, decl) => {
|
|
2526
2883
|
return out;
|
2527
2884
|
};
|
2528
2885
|
|
2886
|
+
// find the nearest loop in depth map by type
|
2529
2887
|
const getNearestLoop = () => {
|
2530
2888
|
for (let i = depth.length - 1; i >= 0; i--) {
|
2531
|
-
if (
|
2889
|
+
if (['while', 'dowhile', 'for', 'forof'].includes(depth[i])) return i;
|
2532
2890
|
}
|
2533
2891
|
|
2534
2892
|
return -1;
|
2535
2893
|
};
|
2536
2894
|
|
2537
2895
|
const generateBreak = (scope, decl) => {
|
2538
|
-
const
|
2896
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2897
|
+
const type = depth[target];
|
2898
|
+
|
2899
|
+
// different loop types have different branch offsets
|
2900
|
+
// as they have different wasm block/loop/if structures
|
2901
|
+
// we need to use the right offset by type to branch to the one we want
|
2902
|
+
// for a break: exit the loop without executing anything else inside it
|
2903
|
+
const offset = ({
|
2904
|
+
for: 2, // loop > if (wanted branch) > block (we are here)
|
2905
|
+
while: 2, // loop > if (wanted branch) (we are here)
|
2906
|
+
dowhile: 2, // loop > block (wanted branch) > block (we are here)
|
2907
|
+
forof: 2, // loop > block (wanted branch) > block (we are here)
|
2908
|
+
if: 1 // break inside if, branch 0 to skip the rest of the if
|
2909
|
+
})[type];
|
2910
|
+
|
2539
2911
|
return [
|
2540
|
-
[ Opcodes.br, ...signedLEB128(
|
2912
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2541
2913
|
];
|
2542
2914
|
};
|
2543
2915
|
|
2544
2916
|
const generateContinue = (scope, decl) => {
|
2545
|
-
const
|
2917
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2918
|
+
const type = depth[target];
|
2919
|
+
|
2920
|
+
// different loop types have different branch offsets
|
2921
|
+
// as they have different wasm block/loop/if structures
|
2922
|
+
// we need to use the right offset by type to branch to the one we want
|
2923
|
+
// for a continue: do test for the loop, and then loop depending on that success
|
2924
|
+
const offset = ({
|
2925
|
+
for: 3, // loop (wanted branch) > if > block (we are here)
|
2926
|
+
while: 1, // loop (wanted branch) > if (we are here)
|
2927
|
+
dowhile: 3, // loop > block > block (wanted branch) (we are here)
|
2928
|
+
forof: 3 // loop > block > block (wanted branch) (we are here)
|
2929
|
+
})[type];
|
2930
|
+
|
2546
2931
|
return [
|
2547
|
-
[ Opcodes.br, ...signedLEB128(
|
2932
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2548
2933
|
];
|
2549
2934
|
};
|
2550
2935
|
|
2936
|
+
const generateLabel = (scope, decl) => {
|
2937
|
+
scope.labels ??= new Map();
|
2938
|
+
|
2939
|
+
const name = decl.label.name;
|
2940
|
+
scope.labels.set(name, depth.length);
|
2941
|
+
|
2942
|
+
return generate(scope, decl.body);
|
2943
|
+
};
|
2944
|
+
|
2551
2945
|
const generateThrow = (scope, decl) => {
|
2552
2946
|
scope.throws = true;
|
2553
2947
|
|
@@ -2580,7 +2974,7 @@ const generateThrow = (scope, decl) => {
|
|
2580
2974
|
};
|
2581
2975
|
|
2582
2976
|
const generateTry = (scope, decl) => {
|
2583
|
-
if (decl.finalizer) return todo('try finally not implemented yet');
|
2977
|
+
if (decl.finalizer) return todo(scope, 'try finally not implemented yet');
|
2584
2978
|
|
2585
2979
|
const out = [];
|
2586
2980
|
|
@@ -2611,7 +3005,7 @@ const generateAssignPat = (scope, decl) => {
|
|
2611
3005
|
// TODO
|
2612
3006
|
// if identifier declared, use that
|
2613
3007
|
// else, use default (right)
|
2614
|
-
return todo('assignment pattern (optional arg)');
|
3008
|
+
return todo(scope, 'assignment pattern (optional arg)');
|
2615
3009
|
};
|
2616
3010
|
|
2617
3011
|
let pages = new Map();
|
@@ -2690,16 +3084,20 @@ const getAllocType = itemType => {
|
|
2690
3084
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2691
3085
|
const out = [];
|
2692
3086
|
|
3087
|
+
scope.arrays ??= new Map();
|
3088
|
+
|
2693
3089
|
let firstAssign = false;
|
2694
|
-
if (!arrays.has(name) || name === '$undeclared') {
|
3090
|
+
if (!scope.arrays.has(name) || name === '$undeclared') {
|
2695
3091
|
firstAssign = true;
|
2696
3092
|
|
2697
3093
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2698
3094
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2699
|
-
|
3095
|
+
|
3096
|
+
if (Prefs.scopedPageNames) scope.arrays.set(name, allocPage(scope, `${scope.name} | ${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
3097
|
+
else scope.arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2700
3098
|
}
|
2701
3099
|
|
2702
|
-
const pointer = arrays.get(name);
|
3100
|
+
const pointer = scope.arrays.get(name);
|
2703
3101
|
|
2704
3102
|
const useRawElements = !!decl.rawElements;
|
2705
3103
|
const elements = useRawElements ? decl.rawElements : decl.elements;
|
@@ -2707,22 +3105,25 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2707
3105
|
const valtype = itemTypeToValtype[itemType];
|
2708
3106
|
const length = elements.length;
|
2709
3107
|
|
2710
|
-
if (firstAssign && useRawElements) {
|
2711
|
-
|
3108
|
+
if (firstAssign && useRawElements && !Prefs.noData) {
|
3109
|
+
// if length is 0 memory/data will just be 0000... anyway
|
3110
|
+
if (length !== 0) {
|
3111
|
+
let bytes = compileBytes(length, 'i32');
|
2712
3112
|
|
2713
|
-
|
2714
|
-
|
3113
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3114
|
+
if (elements[i] == null) continue;
|
2715
3115
|
|
2716
|
-
|
2717
|
-
|
3116
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
3117
|
+
}
|
2718
3118
|
|
2719
|
-
|
2720
|
-
|
2721
|
-
|
2722
|
-
|
3119
|
+
const ind = data.push({
|
3120
|
+
offset: pointer,
|
3121
|
+
bytes
|
3122
|
+
}) - 1;
|
2723
3123
|
|
2724
|
-
|
2725
|
-
|
3124
|
+
scope.data ??= [];
|
3125
|
+
scope.data.push(ind);
|
3126
|
+
}
|
2726
3127
|
|
2727
3128
|
// local value as pointer
|
2728
3129
|
out.push(...number(pointer));
|
@@ -2782,20 +3183,29 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
2782
3183
|
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2783
3184
|
};
|
2784
3185
|
|
2785
|
-
let arrays = new Map();
|
2786
3186
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
2787
3187
|
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
2788
3188
|
};
|
2789
3189
|
|
2790
3190
|
export const generateMember = (scope, decl, _global, _name) => {
|
2791
3191
|
const name = decl.object.name;
|
2792
|
-
const pointer = arrays
|
3192
|
+
const pointer = scope.arrays?.get(name);
|
2793
3193
|
|
2794
|
-
const aotPointer = pointer != null;
|
3194
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2795
3195
|
|
2796
3196
|
// hack: .length
|
2797
3197
|
if (decl.property.name === 'length') {
|
2798
|
-
|
3198
|
+
const func = funcs.find(x => x.name === name);
|
3199
|
+
if (func) {
|
3200
|
+
const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
|
3201
|
+
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
3202
|
+
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3203
|
+
}
|
3204
|
+
|
3205
|
+
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3206
|
+
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3207
|
+
if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
|
3208
|
+
|
2799
3209
|
return [
|
2800
3210
|
...(aotPointer ? number(0, Valtype.i32) : [
|
2801
3211
|
...generate(scope, decl.object),
|
@@ -2839,7 +3249,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2839
3249
|
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2840
3250
|
|
2841
3251
|
...number(TYPES.number, Valtype.i32),
|
2842
|
-
setLastType(scope)
|
3252
|
+
...setLastType(scope)
|
2843
3253
|
],
|
2844
3254
|
|
2845
3255
|
[TYPES.string]: [
|
@@ -2871,7 +3281,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2871
3281
|
...number(newPointer),
|
2872
3282
|
|
2873
3283
|
...number(TYPES.string, Valtype.i32),
|
2874
|
-
setLastType(scope)
|
3284
|
+
...setLastType(scope)
|
2875
3285
|
],
|
2876
3286
|
[TYPES._bytestring]: [
|
2877
3287
|
// setup new/out array
|
@@ -2890,19 +3300,19 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2890
3300
|
]),
|
2891
3301
|
|
2892
3302
|
// load current string ind {arg}
|
2893
|
-
[ Opcodes.i32_load8_u,
|
3303
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2894
3304
|
|
2895
3305
|
// store to new string ind 0
|
2896
|
-
[ Opcodes.i32_store8,
|
3306
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2897
3307
|
|
2898
3308
|
// return new string (page)
|
2899
3309
|
...number(newPointer),
|
2900
3310
|
|
2901
3311
|
...number(TYPES._bytestring, Valtype.i32),
|
2902
|
-
setLastType(scope)
|
3312
|
+
...setLastType(scope)
|
2903
3313
|
],
|
2904
3314
|
|
2905
|
-
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet')
|
3315
|
+
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
2906
3316
|
});
|
2907
3317
|
};
|
2908
3318
|
|
@@ -2912,28 +3322,36 @@ const objectHack = node => {
|
|
2912
3322
|
if (!node) return node;
|
2913
3323
|
|
2914
3324
|
if (node.type === 'MemberExpression') {
|
2915
|
-
|
3325
|
+
const out = (() => {
|
3326
|
+
if (node.computed || node.optional) return;
|
2916
3327
|
|
2917
|
-
|
3328
|
+
let objectName = node.object.name;
|
2918
3329
|
|
2919
|
-
|
2920
|
-
|
3330
|
+
// if object is not identifier or another member exp, give up
|
3331
|
+
if (node.object.type !== 'Identifier' && node.object.type !== 'MemberExpression') return;
|
3332
|
+
if (objectName && ['undefined', 'null', 'NaN', 'Infinity'].includes(objectName)) return;
|
2921
3333
|
|
2922
|
-
|
3334
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
2923
3335
|
|
2924
|
-
|
2925
|
-
|
3336
|
+
// if .length, give up (hack within a hack!)
|
3337
|
+
if (node.property.name === 'length') {
|
3338
|
+
node.object = objectHack(node.object);
|
3339
|
+
return;
|
3340
|
+
}
|
2926
3341
|
|
2927
|
-
|
2928
|
-
|
3342
|
+
// no object name, give up
|
3343
|
+
if (!objectName) return;
|
2929
3344
|
|
2930
|
-
|
2931
|
-
|
3345
|
+
const name = '__' + objectName + '_' + node.property.name;
|
3346
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2932
3347
|
|
2933
|
-
|
2934
|
-
|
2935
|
-
|
2936
|
-
|
3348
|
+
return {
|
3349
|
+
type: 'Identifier',
|
3350
|
+
name
|
3351
|
+
};
|
3352
|
+
})();
|
3353
|
+
|
3354
|
+
if (out) return out;
|
2937
3355
|
}
|
2938
3356
|
|
2939
3357
|
for (const x in node) {
|
@@ -2947,8 +3365,8 @@ const objectHack = node => {
|
|
2947
3365
|
};
|
2948
3366
|
|
2949
3367
|
const generateFunc = (scope, decl) => {
|
2950
|
-
if (decl.async) return todo('async functions are not supported');
|
2951
|
-
if (decl.generator) return todo('generator functions are not supported');
|
3368
|
+
if (decl.async) return todo(scope, 'async functions are not supported');
|
3369
|
+
if (decl.generator) return todo(scope, 'generator functions are not supported');
|
2952
3370
|
|
2953
3371
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
2954
3372
|
const params = decl.params ?? [];
|
@@ -2964,6 +3382,11 @@ const generateFunc = (scope, decl) => {
|
|
2964
3382
|
name
|
2965
3383
|
};
|
2966
3384
|
|
3385
|
+
if (typedInput && decl.returnType) {
|
3386
|
+
innerScope.returnType = extractTypeAnnotation(decl.returnType).type;
|
3387
|
+
innerScope.returns = [ valtypeBinary ];
|
3388
|
+
}
|
3389
|
+
|
2967
3390
|
for (let i = 0; i < params.length; i++) {
|
2968
3391
|
allocVar(innerScope, params[i].name, false);
|
2969
3392
|
|
@@ -2990,6 +3413,8 @@ const generateFunc = (scope, decl) => {
|
|
2990
3413
|
};
|
2991
3414
|
funcIndex[name] = func.index;
|
2992
3415
|
|
3416
|
+
if (name === 'main') func.gotLastType = true;
|
3417
|
+
|
2993
3418
|
// quick hack fixes
|
2994
3419
|
for (const inst of wasm) {
|
2995
3420
|
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
@@ -3024,16 +3449,6 @@ const generateCode = (scope, decl) => {
|
|
3024
3449
|
};
|
3025
3450
|
|
3026
3451
|
const internalConstrs = {
|
3027
|
-
Boolean: {
|
3028
|
-
generate: (scope, decl) => {
|
3029
|
-
if (decl.arguments.length === 0) return number(0);
|
3030
|
-
|
3031
|
-
// should generate/run all args
|
3032
|
-
return truthy(scope, generate(scope, decl.arguments[0]), getNodeType(scope, decl.arguments[0]), false, false);
|
3033
|
-
},
|
3034
|
-
type: TYPES.boolean
|
3035
|
-
},
|
3036
|
-
|
3037
3452
|
Array: {
|
3038
3453
|
generate: (scope, decl, global, name) => {
|
3039
3454
|
// new Array(i0, i1, ...)
|
@@ -3051,7 +3466,7 @@ const internalConstrs = {
|
|
3051
3466
|
|
3052
3467
|
// todo: check in wasm instead of here
|
3053
3468
|
const literalValue = arg.value ?? 0;
|
3054
|
-
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length');
|
3469
|
+
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
|
3055
3470
|
|
3056
3471
|
return [
|
3057
3472
|
...number(0, Valtype.i32),
|
@@ -3062,7 +3477,8 @@ const internalConstrs = {
|
|
3062
3477
|
...number(pointer)
|
3063
3478
|
];
|
3064
3479
|
},
|
3065
|
-
type: TYPES._array
|
3480
|
+
type: TYPES._array,
|
3481
|
+
length: 1
|
3066
3482
|
},
|
3067
3483
|
|
3068
3484
|
__Array_of: {
|
@@ -3074,7 +3490,131 @@ const internalConstrs = {
|
|
3074
3490
|
}, global, name);
|
3075
3491
|
},
|
3076
3492
|
type: TYPES._array,
|
3493
|
+
notConstr: true,
|
3494
|
+
length: 0
|
3495
|
+
},
|
3496
|
+
|
3497
|
+
__Porffor_fastOr: {
|
3498
|
+
generate: (scope, decl) => {
|
3499
|
+
const out = [];
|
3500
|
+
|
3501
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3502
|
+
out.push(
|
3503
|
+
...generate(scope, decl.arguments[i]),
|
3504
|
+
Opcodes.i32_to_u,
|
3505
|
+
...(i > 0 ? [ [ Opcodes.i32_or ] ] : [])
|
3506
|
+
);
|
3507
|
+
}
|
3508
|
+
|
3509
|
+
out.push(Opcodes.i32_from_u);
|
3510
|
+
|
3511
|
+
return out;
|
3512
|
+
},
|
3513
|
+
type: TYPES.boolean,
|
3514
|
+
notConstr: true
|
3515
|
+
},
|
3516
|
+
|
3517
|
+
__Porffor_fastAnd: {
|
3518
|
+
generate: (scope, decl) => {
|
3519
|
+
const out = [];
|
3520
|
+
|
3521
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3522
|
+
out.push(
|
3523
|
+
...generate(scope, decl.arguments[i]),
|
3524
|
+
Opcodes.i32_to_u,
|
3525
|
+
...(i > 0 ? [ [ Opcodes.i32_and ] ] : [])
|
3526
|
+
);
|
3527
|
+
}
|
3528
|
+
|
3529
|
+
out.push(Opcodes.i32_from_u);
|
3530
|
+
|
3531
|
+
return out;
|
3532
|
+
},
|
3533
|
+
type: TYPES.boolean,
|
3077
3534
|
notConstr: true
|
3535
|
+
},
|
3536
|
+
|
3537
|
+
Boolean: {
|
3538
|
+
generate: (scope, decl) => {
|
3539
|
+
// todo: boolean object when used as constructor
|
3540
|
+
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3541
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3542
|
+
},
|
3543
|
+
type: TYPES.boolean,
|
3544
|
+
length: 1
|
3545
|
+
},
|
3546
|
+
|
3547
|
+
__Math_max: {
|
3548
|
+
generate: (scope, decl) => {
|
3549
|
+
const out = [
|
3550
|
+
...number(-Infinity)
|
3551
|
+
];
|
3552
|
+
|
3553
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3554
|
+
out.push(
|
3555
|
+
...generate(scope, decl.arguments[i]),
|
3556
|
+
[ Opcodes.f64_max ]
|
3557
|
+
);
|
3558
|
+
}
|
3559
|
+
|
3560
|
+
return out;
|
3561
|
+
},
|
3562
|
+
type: TYPES.number,
|
3563
|
+
notConstr: true,
|
3564
|
+
length: 2
|
3565
|
+
},
|
3566
|
+
|
3567
|
+
__Math_min: {
|
3568
|
+
generate: (scope, decl) => {
|
3569
|
+
const out = [
|
3570
|
+
...number(Infinity)
|
3571
|
+
];
|
3572
|
+
|
3573
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3574
|
+
out.push(
|
3575
|
+
...generate(scope, decl.arguments[i]),
|
3576
|
+
[ Opcodes.f64_min ]
|
3577
|
+
);
|
3578
|
+
}
|
3579
|
+
|
3580
|
+
return out;
|
3581
|
+
},
|
3582
|
+
type: TYPES.number,
|
3583
|
+
notConstr: true,
|
3584
|
+
length: 2
|
3585
|
+
},
|
3586
|
+
|
3587
|
+
__console_log: {
|
3588
|
+
generate: (scope, decl) => {
|
3589
|
+
const out = [];
|
3590
|
+
|
3591
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3592
|
+
out.push(
|
3593
|
+
...generateCall(scope, {
|
3594
|
+
callee: {
|
3595
|
+
type: 'Identifier',
|
3596
|
+
name: '__Porffor_print'
|
3597
|
+
},
|
3598
|
+
arguments: [ decl.arguments[i] ]
|
3599
|
+
}),
|
3600
|
+
|
3601
|
+
// print space
|
3602
|
+
...number(32),
|
3603
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3604
|
+
);
|
3605
|
+
}
|
3606
|
+
|
3607
|
+
// print newline
|
3608
|
+
out.push(
|
3609
|
+
...number(10),
|
3610
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3611
|
+
);
|
3612
|
+
|
3613
|
+
return out;
|
3614
|
+
},
|
3615
|
+
type: TYPES.undefined,
|
3616
|
+
notConstr: true,
|
3617
|
+
length: 0
|
3078
3618
|
}
|
3079
3619
|
};
|
3080
3620
|
|
@@ -3103,7 +3643,6 @@ export default program => {
|
|
3103
3643
|
funcs = [];
|
3104
3644
|
funcIndex = {};
|
3105
3645
|
depth = [];
|
3106
|
-
arrays = new Map();
|
3107
3646
|
pages = new Map();
|
3108
3647
|
data = [];
|
3109
3648
|
currentFuncIndex = importedFuncs.length;
|
@@ -3117,6 +3656,10 @@ export default program => {
|
|
3117
3656
|
|
3118
3657
|
const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
|
3119
3658
|
|
3659
|
+
globalThis.pageSize = PageSize;
|
3660
|
+
const pageSizeOpt = process.argv.find(x => x.startsWith('-page-size='));
|
3661
|
+
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3662
|
+
|
3120
3663
|
// set generic opcodes for current valtype
|
3121
3664
|
Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
|
3122
3665
|
Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
|
@@ -3125,10 +3668,10 @@ export default program => {
|
|
3125
3668
|
Opcodes.add = [ Opcodes.i32_add, Opcodes.i64_add, Opcodes.f64_add ][valtypeInd];
|
3126
3669
|
Opcodes.sub = [ Opcodes.i32_sub, Opcodes.i64_sub, Opcodes.f64_sub ][valtypeInd];
|
3127
3670
|
|
3128
|
-
Opcodes.i32_to = [ [
|
3129
|
-
Opcodes.i32_to_u = [ [
|
3130
|
-
Opcodes.i32_from = [ [
|
3131
|
-
Opcodes.i32_from_u = [ [
|
3671
|
+
Opcodes.i32_to = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_s ][valtypeInd];
|
3672
|
+
Opcodes.i32_to_u = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_u ][valtypeInd];
|
3673
|
+
Opcodes.i32_from = [ [], [ Opcodes.i64_extend_i32_s ], [ Opcodes.f64_convert_i32_s ] ][valtypeInd];
|
3674
|
+
Opcodes.i32_from_u = [ [], [ Opcodes.i64_extend_i32_u ], [ Opcodes.f64_convert_i32_u ] ][valtypeInd];
|
3132
3675
|
|
3133
3676
|
Opcodes.load = [ Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load ][valtypeInd];
|
3134
3677
|
Opcodes.store = [ Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store ][valtypeInd];
|
@@ -3141,10 +3684,6 @@ export default program => {
|
|
3141
3684
|
|
3142
3685
|
program.id = { name: 'main' };
|
3143
3686
|
|
3144
|
-
globalThis.pageSize = PageSize;
|
3145
|
-
const pageSizeOpt = process.argv.find(x => x.startsWith('-page-size='));
|
3146
|
-
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3147
|
-
|
3148
3687
|
const scope = {
|
3149
3688
|
locals: {},
|
3150
3689
|
localInd: 0
|
@@ -3155,7 +3694,7 @@ export default program => {
|
|
3155
3694
|
body: program.body
|
3156
3695
|
};
|
3157
3696
|
|
3158
|
-
if (Prefs.astLog) console.log(program.body.body);
|
3697
|
+
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3159
3698
|
|
3160
3699
|
generateFunc(scope, program);
|
3161
3700
|
|
@@ -3172,7 +3711,11 @@ export default program => {
|
|
3172
3711
|
}
|
3173
3712
|
|
3174
3713
|
if (lastInst[0] === Opcodes.end || lastInst[0] === Opcodes.local_set || lastInst[0] === Opcodes.global_set) {
|
3175
|
-
|
3714
|
+
if (lastInst[0] === Opcodes.local_set && lastInst[1] === main.locals['#last_type'].idx) {
|
3715
|
+
main.wasm.splice(main.wasm.length - 1, 1);
|
3716
|
+
} else {
|
3717
|
+
main.returns = [];
|
3718
|
+
}
|
3176
3719
|
}
|
3177
3720
|
|
3178
3721
|
if (lastInst[0] === Opcodes.call) {
|