porffor 0.2.0-6aff0fa → 0.2.0-75bc012
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +256 -0
- package/LICENSE +20 -20
- package/README.md +115 -82
- package/asur/index.js +624 -340
- package/byg/index.js +237 -0
- package/compiler/2c.js +1 -1
- package/compiler/{sections.js → assemble.js} +59 -12
- package/compiler/builtins/annexb_string.js +72 -0
- package/compiler/builtins/annexb_string.ts +18 -0
- package/compiler/builtins/array.ts +145 -0
- package/compiler/builtins/base64.ts +7 -84
- package/compiler/builtins/boolean.ts +20 -0
- package/compiler/builtins/crypto.ts +120 -0
- package/compiler/builtins/date.ts +2070 -0
- package/compiler/builtins/escape.ts +141 -0
- package/compiler/builtins/int.ts +147 -0
- package/compiler/builtins/number.ts +534 -0
- package/compiler/builtins/porffor.d.ts +43 -7
- package/compiler/builtins/string.ts +1080 -0
- package/compiler/builtins/tostring.ts +25 -0
- package/compiler/builtins.js +398 -115
- package/compiler/{codeGen.js → codegen.js} +856 -323
- package/compiler/decompile.js +0 -1
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +108 -10
- package/compiler/generated_builtins.js +1504 -2
- package/compiler/index.js +16 -14
- package/compiler/log.js +2 -2
- package/compiler/opt.js +23 -22
- package/compiler/parse.js +30 -22
- package/compiler/precompile.js +26 -27
- package/compiler/prefs.js +7 -6
- package/compiler/prototype.js +16 -32
- package/compiler/types.js +37 -0
- package/compiler/wasmSpec.js +14 -1
- package/compiler/wrap.js +41 -44
- package/package.json +9 -5
- package/porf +2 -0
- package/rhemyn/compile.js +44 -26
- package/rhemyn/parse.js +322 -320
- package/rhemyn/test/parse.js +58 -58
- package/runner/compare.js +34 -34
- package/runner/debug.js +122 -0
- package/runner/index.js +69 -12
- package/runner/profiler.js +45 -26
- package/runner/repl.js +42 -9
- package/runner/sizes.js +37 -37
- package/runner/info.js +0 -89
- package/runner/transform.js +0 -15
- package/util/enum.js +0 -20
@@ -8,6 +8,7 @@ import { log } from "./log.js";
|
|
8
8
|
import parse from "./parse.js";
|
9
9
|
import * as Rhemyn from "../rhemyn/compile.js";
|
10
10
|
import Prefs from './prefs.js';
|
11
|
+
import { TYPES, TYPE_NAMES } from './types.js';
|
11
12
|
|
12
13
|
let globals = {};
|
13
14
|
let globalInd = 0;
|
@@ -24,38 +25,44 @@ 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';
|
62
|
+
const hasFuncWithName = name => {
|
63
|
+
const func = funcs.find(x => x.name === name);
|
64
|
+
return !!(func || builtinFuncs[name] || importedFuncs[name] || internalConstrs[name]);
|
65
|
+
};
|
59
66
|
const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
|
60
67
|
switch (decl.type) {
|
61
68
|
case 'BinaryExpression':
|
@@ -105,7 +112,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
105
112
|
return generateUnary(scope, decl);
|
106
113
|
|
107
114
|
case 'UpdateExpression':
|
108
|
-
return generateUpdate(scope, decl);
|
115
|
+
return generateUpdate(scope, decl, global, name, valueUnused);
|
109
116
|
|
110
117
|
case 'IfStatement':
|
111
118
|
return generateIf(scope, decl);
|
@@ -116,6 +123,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
116
123
|
case 'WhileStatement':
|
117
124
|
return generateWhile(scope, decl);
|
118
125
|
|
126
|
+
case 'DoWhileStatement':
|
127
|
+
return generateDoWhile(scope, decl);
|
128
|
+
|
119
129
|
case 'ForOfStatement':
|
120
130
|
return generateForOf(scope, decl);
|
121
131
|
|
@@ -125,6 +135,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
125
135
|
case 'ContinueStatement':
|
126
136
|
return generateContinue(scope, decl);
|
127
137
|
|
138
|
+
case 'LabeledStatement':
|
139
|
+
return generateLabel(scope, decl);
|
140
|
+
|
128
141
|
case 'EmptyStatement':
|
129
142
|
return generateEmpty(scope, decl);
|
130
143
|
|
@@ -138,7 +151,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
138
151
|
return generateTry(scope, decl);
|
139
152
|
|
140
153
|
case 'DebuggerStatement':
|
141
|
-
// todo:
|
154
|
+
// todo: hook into terminal debugger
|
142
155
|
return [];
|
143
156
|
|
144
157
|
case 'ArrayExpression':
|
@@ -152,10 +165,16 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
152
165
|
const funcsBefore = funcs.length;
|
153
166
|
generate(scope, decl.declaration);
|
154
167
|
|
155
|
-
if (funcsBefore
|
168
|
+
if (funcsBefore !== funcs.length) {
|
169
|
+
// new func added
|
170
|
+
const newFunc = funcs[funcs.length - 1];
|
171
|
+
newFunc.export = true;
|
172
|
+
}
|
156
173
|
|
157
|
-
|
158
|
-
|
174
|
+
// if (funcsBefore === funcs.length) throw new Error('no new func added in export');
|
175
|
+
|
176
|
+
// const newFunc = funcs[funcs.length - 1];
|
177
|
+
// newFunc.export = true;
|
159
178
|
|
160
179
|
return [];
|
161
180
|
|
@@ -186,7 +205,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
186
205
|
}
|
187
206
|
|
188
207
|
let inst = Opcodes[asm[0].replace('.', '_')];
|
189
|
-
if (
|
208
|
+
if (inst == null) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
190
209
|
|
191
210
|
if (!Array.isArray(inst)) inst = [ inst ];
|
192
211
|
const immediates = asm.slice(1).map(x => {
|
@@ -195,40 +214,49 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
195
214
|
return int;
|
196
215
|
});
|
197
216
|
|
198
|
-
out.push([ ...inst, ...immediates ]);
|
217
|
+
out.push([ ...inst, ...immediates.flatMap(x => signedLEB128(x)) ]);
|
199
218
|
}
|
200
219
|
|
201
220
|
return out;
|
202
221
|
},
|
203
222
|
|
204
223
|
__Porffor_bs: str => [
|
205
|
-
...makeString(scope, str,
|
224
|
+
...makeString(scope, str, global, name, true),
|
206
225
|
|
207
|
-
...
|
208
|
-
|
226
|
+
...(name ? setType(scope, name, TYPES.bytestring) : [
|
227
|
+
...number(TYPES.bytestring, Valtype.i32),
|
228
|
+
...setLastType(scope)
|
229
|
+
])
|
209
230
|
],
|
210
231
|
__Porffor_s: str => [
|
211
|
-
...makeString(scope, str,
|
232
|
+
...makeString(scope, str, global, name, false),
|
212
233
|
|
213
|
-
...
|
214
|
-
|
234
|
+
...(name ? setType(scope, name, TYPES.string) : [
|
235
|
+
...number(TYPES.string, Valtype.i32),
|
236
|
+
...setLastType(scope)
|
237
|
+
])
|
215
238
|
],
|
216
239
|
};
|
217
240
|
|
218
|
-
const
|
241
|
+
const func = decl.tag.name;
|
219
242
|
// hack for inline asm
|
220
|
-
if (!funcs[
|
243
|
+
if (!funcs[func]) return todo(scope, 'tagged template expressions not implemented', true);
|
221
244
|
|
222
245
|
const { quasis, expressions } = decl.quasi;
|
223
246
|
let str = quasis[0].value.raw;
|
224
247
|
|
225
248
|
for (let i = 0; i < expressions.length; i++) {
|
226
249
|
const e = expressions[i];
|
227
|
-
|
250
|
+
if (!e.name) {
|
251
|
+
if (e.type === 'BinaryExpression' && e.operator === '+' && e.left.type === 'Identifier' && e.right.type === 'Literal') {
|
252
|
+
str += lookupName(scope, e.left.name)[0].idx + e.right.value;
|
253
|
+
} else todo(scope, 'unsupported expression in intrinsic');
|
254
|
+
} else str += lookupName(scope, e.name)[0].idx;
|
255
|
+
|
228
256
|
str += quasis[i + 1].value.raw;
|
229
257
|
}
|
230
258
|
|
231
|
-
return funcs[
|
259
|
+
return funcs[func](str);
|
232
260
|
}
|
233
261
|
|
234
262
|
default:
|
@@ -238,7 +266,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
238
266
|
return [];
|
239
267
|
}
|
240
268
|
|
241
|
-
return todo(`no generation for ${decl.type}!`);
|
269
|
+
return todo(scope, `no generation for ${decl.type}!`);
|
242
270
|
}
|
243
271
|
};
|
244
272
|
|
@@ -266,7 +294,7 @@ const lookupName = (scope, _name) => {
|
|
266
294
|
return [ undefined, undefined ];
|
267
295
|
};
|
268
296
|
|
269
|
-
const internalThrow = (scope, constructor, message, expectsValue =
|
297
|
+
const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysValueInternalThrows) => [
|
270
298
|
...generateThrow(scope, {
|
271
299
|
argument: {
|
272
300
|
type: 'NewExpression',
|
@@ -293,7 +321,7 @@ const generateIdent = (scope, decl) => {
|
|
293
321
|
|
294
322
|
let wasm = builtinVars[name];
|
295
323
|
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name });
|
296
|
-
return wasm;
|
324
|
+
return wasm.slice();
|
297
325
|
}
|
298
326
|
|
299
327
|
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
@@ -301,6 +329,11 @@ const generateIdent = (scope, decl) => {
|
|
301
329
|
return number(1);
|
302
330
|
}
|
303
331
|
|
332
|
+
if (isExistingProtoFunc(name)) {
|
333
|
+
// todo: return an actual something
|
334
|
+
return number(1);
|
335
|
+
}
|
336
|
+
|
304
337
|
if (local?.idx === undefined) {
|
305
338
|
// no local var with name
|
306
339
|
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
@@ -331,14 +364,18 @@ const generateReturn = (scope, decl) => {
|
|
331
364
|
// just bare "return"
|
332
365
|
return [
|
333
366
|
...number(UNDEFINED), // "undefined" if func returns
|
334
|
-
...
|
367
|
+
...(scope.returnType != null ? [] : [
|
368
|
+
...number(TYPES.undefined, Valtype.i32) // type undefined
|
369
|
+
]),
|
335
370
|
[ Opcodes.return ]
|
336
371
|
];
|
337
372
|
}
|
338
373
|
|
339
374
|
return [
|
340
375
|
...generate(scope, decl.argument),
|
341
|
-
...
|
376
|
+
...(scope.returnType != null ? [] : [
|
377
|
+
...getNodeType(scope, decl.argument)
|
378
|
+
]),
|
342
379
|
[ Opcodes.return ]
|
343
380
|
];
|
344
381
|
};
|
@@ -352,7 +389,8 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
352
389
|
return idx;
|
353
390
|
};
|
354
391
|
|
355
|
-
const isIntOp = op => op && (op[0] >=
|
392
|
+
const isIntOp = op => op && ((op[0] >= 0x45 && op[0] <= 0x4f) || (op[0] >= 0x67 && op[0] <= 0x78) || op[0] === 0x41);
|
393
|
+
const isFloatToIntOp = op => op && (op[0] >= 0xb7 && op[0] <= 0xba);
|
356
394
|
|
357
395
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
358
396
|
const checks = {
|
@@ -361,7 +399,7 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
361
399
|
'??': nullish
|
362
400
|
};
|
363
401
|
|
364
|
-
if (!checks[op]) return todo(`logic operator ${op} not implemented yet
|
402
|
+
if (!checks[op]) return todo(scope, `logic operator ${op} not implemented yet`, true);
|
365
403
|
|
366
404
|
// generic structure for {a} OP {b}
|
367
405
|
// -->
|
@@ -369,8 +407,8 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
369
407
|
|
370
408
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
371
409
|
// (like if we are in an if condition - very common)
|
372
|
-
const leftIsInt =
|
373
|
-
const rightIsInt =
|
410
|
+
const leftIsInt = isFloatToIntOp(left[left.length - 1]);
|
411
|
+
const rightIsInt = isFloatToIntOp(right[right.length - 1]);
|
374
412
|
|
375
413
|
const canInt = leftIsInt && rightIsInt;
|
376
414
|
|
@@ -387,12 +425,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
387
425
|
...right,
|
388
426
|
// note type
|
389
427
|
...rightType,
|
390
|
-
setLastType(scope),
|
428
|
+
...setLastType(scope),
|
391
429
|
[ Opcodes.else ],
|
392
430
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
393
431
|
// note type
|
394
432
|
...leftType,
|
395
|
-
setLastType(scope),
|
433
|
+
...setLastType(scope),
|
396
434
|
[ Opcodes.end ],
|
397
435
|
Opcodes.i32_from
|
398
436
|
];
|
@@ -406,17 +444,17 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
406
444
|
...right,
|
407
445
|
// note type
|
408
446
|
...rightType,
|
409
|
-
setLastType(scope),
|
447
|
+
...setLastType(scope),
|
410
448
|
[ Opcodes.else ],
|
411
449
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
412
450
|
// note type
|
413
451
|
...leftType,
|
414
|
-
setLastType(scope),
|
452
|
+
...setLastType(scope),
|
415
453
|
[ Opcodes.end ]
|
416
454
|
];
|
417
455
|
};
|
418
456
|
|
419
|
-
const concatStrings = (scope, left, right, global, name, assign) => {
|
457
|
+
const concatStrings = (scope, left, right, global, name, assign = false, bytestrings = false) => {
|
420
458
|
// todo: this should be rewritten into a built-in/func: String.prototype.concat
|
421
459
|
// todo: convert left and right to strings if not
|
422
460
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -426,8 +464,8 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
426
464
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
427
465
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
428
466
|
|
429
|
-
if (assign) {
|
430
|
-
const pointer = arrays
|
467
|
+
if (assign && Prefs.aotPointerOpt) {
|
468
|
+
const pointer = scope.arrays?.get(name ?? '$undeclared');
|
431
469
|
|
432
470
|
return [
|
433
471
|
// setup right
|
@@ -452,11 +490,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
452
490
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
|
453
491
|
|
454
492
|
// copy right
|
455
|
-
// dst = out pointer + length size + current length *
|
493
|
+
// dst = out pointer + length size + current length * sizeof valtype
|
456
494
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
457
495
|
|
458
496
|
[ Opcodes.local_get, leftLength ],
|
459
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
497
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
460
498
|
[ Opcodes.i32_mul ],
|
461
499
|
[ Opcodes.i32_add ],
|
462
500
|
|
@@ -465,9 +503,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
465
503
|
...number(ValtypeSize.i32, Valtype.i32),
|
466
504
|
[ Opcodes.i32_add ],
|
467
505
|
|
468
|
-
// size = right length *
|
506
|
+
// size = right length * sizeof valtype
|
469
507
|
[ Opcodes.local_get, rightLength ],
|
470
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
508
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
471
509
|
[ Opcodes.i32_mul ],
|
472
510
|
|
473
511
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -525,11 +563,11 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
525
563
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
526
564
|
|
527
565
|
// copy right
|
528
|
-
// dst = out pointer + length size + left length *
|
566
|
+
// dst = out pointer + length size + left length * sizeof valtype
|
529
567
|
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
530
568
|
|
531
569
|
[ Opcodes.local_get, leftLength ],
|
532
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
570
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
533
571
|
[ Opcodes.i32_mul ],
|
534
572
|
[ Opcodes.i32_add ],
|
535
573
|
|
@@ -538,9 +576,9 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
538
576
|
...number(ValtypeSize.i32, Valtype.i32),
|
539
577
|
[ Opcodes.i32_add ],
|
540
578
|
|
541
|
-
// size = right length *
|
579
|
+
// size = right length * sizeof valtype
|
542
580
|
[ Opcodes.local_get, rightLength ],
|
543
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
581
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
544
582
|
[ Opcodes.i32_mul ],
|
545
583
|
|
546
584
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
@@ -550,7 +588,7 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
550
588
|
];
|
551
589
|
};
|
552
590
|
|
553
|
-
const compareStrings = (scope, left, right) => {
|
591
|
+
const compareStrings = (scope, left, right, bytestrings = false) => {
|
554
592
|
// todo: this should be rewritten into a func
|
555
593
|
// todo: convert left and right to strings if not
|
556
594
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -559,7 +597,6 @@ const compareStrings = (scope, left, right) => {
|
|
559
597
|
const leftPointer = localTmp(scope, 'compare_left_pointer', Valtype.i32);
|
560
598
|
const leftLength = localTmp(scope, 'compare_left_length', Valtype.i32);
|
561
599
|
const rightPointer = localTmp(scope, 'compare_right_pointer', Valtype.i32);
|
562
|
-
const rightLength = localTmp(scope, 'compare_right_length', Valtype.i32);
|
563
600
|
|
564
601
|
const index = localTmp(scope, 'compare_index', Valtype.i32);
|
565
602
|
const indexEnd = localTmp(scope, 'compare_index_end', Valtype.i32);
|
@@ -587,7 +624,6 @@ const compareStrings = (scope, left, right) => {
|
|
587
624
|
|
588
625
|
[ Opcodes.local_get, rightPointer ],
|
589
626
|
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
590
|
-
[ Opcodes.local_tee, rightLength ],
|
591
627
|
|
592
628
|
// fast path: check leftLength != rightLength
|
593
629
|
[ Opcodes.i32_ne ],
|
@@ -602,11 +638,13 @@ const compareStrings = (scope, left, right) => {
|
|
602
638
|
...number(0, Valtype.i32),
|
603
639
|
[ Opcodes.local_set, index ],
|
604
640
|
|
605
|
-
// setup index end as length * sizeof
|
641
|
+
// setup index end as length * sizeof valtype (1 for bytestring, 2 for string)
|
606
642
|
// we do this instead of having to do mul/div each iter for perf™
|
607
643
|
[ Opcodes.local_get, leftLength ],
|
608
|
-
...
|
609
|
-
|
644
|
+
...(bytestrings ? [] : [
|
645
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
646
|
+
[ Opcodes.i32_mul ],
|
647
|
+
]),
|
610
648
|
[ Opcodes.local_set, indexEnd ],
|
611
649
|
|
612
650
|
// iterate over each char and check if eq
|
@@ -616,13 +654,17 @@ const compareStrings = (scope, left, right) => {
|
|
616
654
|
[ Opcodes.local_get, index ],
|
617
655
|
[ Opcodes.local_get, leftPointer ],
|
618
656
|
[ Opcodes.i32_add ],
|
619
|
-
|
657
|
+
bytestrings ?
|
658
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
659
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
620
660
|
|
621
661
|
// fetch right
|
622
662
|
[ Opcodes.local_get, index ],
|
623
663
|
[ Opcodes.local_get, rightPointer ],
|
624
664
|
[ Opcodes.i32_add ],
|
625
|
-
|
665
|
+
bytestrings ?
|
666
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
667
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
626
668
|
|
627
669
|
// not equal, "return" false
|
628
670
|
[ Opcodes.i32_ne ],
|
@@ -631,13 +673,13 @@ const compareStrings = (scope, left, right) => {
|
|
631
673
|
[ Opcodes.br, 2 ],
|
632
674
|
[ Opcodes.end ],
|
633
675
|
|
634
|
-
// index += sizeof
|
676
|
+
// index += sizeof valtype (1 for bytestring, 2 for string)
|
635
677
|
[ Opcodes.local_get, index ],
|
636
|
-
...number(ValtypeSize.i16, Valtype.i32),
|
678
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
637
679
|
[ Opcodes.i32_add ],
|
638
680
|
[ Opcodes.local_tee, index ],
|
639
681
|
|
640
|
-
// if index != index end (length * sizeof
|
682
|
+
// if index != index end (length * sizeof valtype), loop
|
641
683
|
[ Opcodes.local_get, indexEnd ],
|
642
684
|
[ Opcodes.i32_ne ],
|
643
685
|
[ Opcodes.br_if, 0 ],
|
@@ -658,13 +700,14 @@ const compareStrings = (scope, left, right) => {
|
|
658
700
|
};
|
659
701
|
|
660
702
|
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
661
|
-
if (
|
703
|
+
if (isFloatToIntOp(wasm[wasm.length - 1])) return [
|
662
704
|
...wasm,
|
663
705
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
664
706
|
];
|
707
|
+
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
665
708
|
|
666
709
|
const useTmp = knownType(scope, type) == null;
|
667
|
-
const tmp = useTmp && localTmp(scope,
|
710
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
668
711
|
|
669
712
|
const def = [
|
670
713
|
// if value != 0
|
@@ -684,7 +727,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
684
727
|
|
685
728
|
...typeSwitch(scope, type, {
|
686
729
|
// [TYPES.number]: def,
|
687
|
-
[TYPES.
|
730
|
+
[TYPES.array]: [
|
688
731
|
// arrays are always truthy
|
689
732
|
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
690
733
|
],
|
@@ -700,7 +743,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
700
743
|
[ Opcodes.i32_eqz ], */
|
701
744
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
702
745
|
],
|
703
|
-
[TYPES.
|
746
|
+
[TYPES.bytestring]: [ // duplicate of string
|
704
747
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
705
748
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
706
749
|
|
@@ -716,14 +759,14 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
716
759
|
|
717
760
|
const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
718
761
|
const useTmp = knownType(scope, type) == null;
|
719
|
-
const tmp = useTmp && localTmp(scope,
|
762
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
720
763
|
|
721
764
|
return [
|
722
765
|
...wasm,
|
723
766
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
724
767
|
|
725
768
|
...typeSwitch(scope, type, {
|
726
|
-
[TYPES.
|
769
|
+
[TYPES.array]: [
|
727
770
|
// arrays are always truthy
|
728
771
|
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
729
772
|
],
|
@@ -738,7 +781,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
738
781
|
[ Opcodes.i32_eqz ],
|
739
782
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
740
783
|
],
|
741
|
-
[TYPES.
|
784
|
+
[TYPES.bytestring]: [ // duplicate of string
|
742
785
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
743
786
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
744
787
|
|
@@ -762,7 +805,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
762
805
|
|
763
806
|
const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
764
807
|
const useTmp = knownType(scope, type) == null;
|
765
|
-
const tmp = useTmp && localTmp(scope,
|
808
|
+
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
766
809
|
|
767
810
|
return [
|
768
811
|
...wasm,
|
@@ -856,11 +899,11 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
856
899
|
// todo: if equality op and an operand is undefined, return false
|
857
900
|
// todo: niche null hell with 0
|
858
901
|
|
859
|
-
// todo: this should be dynamic but for now only static
|
860
902
|
if (knownLeft === TYPES.string || knownRight === TYPES.string) {
|
861
903
|
if (op === '+') {
|
904
|
+
// todo: this should be dynamic too but for now only static
|
862
905
|
// string concat (a + b)
|
863
|
-
return concatStrings(scope, left, right, _global, _name, assign);
|
906
|
+
return concatStrings(scope, left, right, _global, _name, assign, false);
|
864
907
|
}
|
865
908
|
|
866
909
|
// not an equality op, NaN
|
@@ -883,6 +926,33 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
883
926
|
}
|
884
927
|
}
|
885
928
|
|
929
|
+
if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) {
|
930
|
+
if (op === '+') {
|
931
|
+
// todo: this should be dynamic too but for now only static
|
932
|
+
// string concat (a + b)
|
933
|
+
return concatStrings(scope, left, right, _global, _name, assign, true);
|
934
|
+
}
|
935
|
+
|
936
|
+
// not an equality op, NaN
|
937
|
+
if (!eqOp) return number(NaN);
|
938
|
+
|
939
|
+
// else leave bool ops
|
940
|
+
// todo: convert string to number if string and number/bool
|
941
|
+
// todo: string (>|>=|<|<=) string
|
942
|
+
|
943
|
+
// string comparison
|
944
|
+
if (op === '===' || op === '==') {
|
945
|
+
return compareStrings(scope, left, right, true);
|
946
|
+
}
|
947
|
+
|
948
|
+
if (op === '!==' || op === '!=') {
|
949
|
+
return [
|
950
|
+
...compareStrings(scope, left, right, true),
|
951
|
+
[ Opcodes.i32_eqz ]
|
952
|
+
];
|
953
|
+
}
|
954
|
+
}
|
955
|
+
|
886
956
|
let ops = operatorOpcode[valtype][op];
|
887
957
|
|
888
958
|
// some complex ops are implemented as builtin funcs
|
@@ -898,23 +968,62 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
898
968
|
]);
|
899
969
|
}
|
900
970
|
|
901
|
-
if (!ops) return todo(`operator ${op} not implemented yet
|
971
|
+
if (!ops) return todo(scope, `operator ${op} not implemented yet`, true);
|
902
972
|
|
903
973
|
if (!Array.isArray(ops)) ops = [ ops ];
|
904
974
|
ops = [ ops ];
|
905
975
|
|
906
976
|
let tmpLeft, tmpRight;
|
907
977
|
// if equal op, check if strings for compareStrings
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
|
912
|
-
return;
|
913
|
-
}
|
978
|
+
// todo: intelligent partial skip later
|
979
|
+
// if neither known are string, stop this madness
|
980
|
+
// we already do known checks earlier, so don't need to recheck
|
914
981
|
|
982
|
+
if ((op === '===' || op === '==' || op === '!==' || op === '!=') && (knownLeft == null && knownRight == null)) {
|
915
983
|
tmpLeft = localTmp(scope, '__tmpop_left');
|
916
984
|
tmpRight = localTmp(scope, '__tmpop_right');
|
917
985
|
|
986
|
+
// returns false for one string, one not - but more ops/slower
|
987
|
+
// ops.unshift(...stringOnly([
|
988
|
+
// // if left is string
|
989
|
+
// ...leftType,
|
990
|
+
// ...number(TYPES.string, Valtype.i32),
|
991
|
+
// [ Opcodes.i32_eq ],
|
992
|
+
|
993
|
+
// // if right is string
|
994
|
+
// ...rightType,
|
995
|
+
// ...number(TYPES.string, Valtype.i32),
|
996
|
+
// [ Opcodes.i32_eq ],
|
997
|
+
|
998
|
+
// // if either are true
|
999
|
+
// [ Opcodes.i32_or ],
|
1000
|
+
// [ Opcodes.if, Blocktype.void ],
|
1001
|
+
|
1002
|
+
// // todo: convert non-strings to strings, for now fail immediately if one is not
|
1003
|
+
// // if left is not string
|
1004
|
+
// ...leftType,
|
1005
|
+
// ...number(TYPES.string, Valtype.i32),
|
1006
|
+
// [ Opcodes.i32_ne ],
|
1007
|
+
|
1008
|
+
// // if right is not string
|
1009
|
+
// ...rightType,
|
1010
|
+
// ...number(TYPES.string, Valtype.i32),
|
1011
|
+
// [ Opcodes.i32_ne ],
|
1012
|
+
|
1013
|
+
// // if either are true
|
1014
|
+
// [ Opcodes.i32_or ],
|
1015
|
+
// [ Opcodes.if, Blocktype.void ],
|
1016
|
+
// ...number(0, Valtype.i32),
|
1017
|
+
// [ Opcodes.br, 2 ],
|
1018
|
+
// [ Opcodes.end ],
|
1019
|
+
|
1020
|
+
// ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1021
|
+
// ...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
1022
|
+
// [ Opcodes.br, 1 ],
|
1023
|
+
// [ Opcodes.end ],
|
1024
|
+
// ]));
|
1025
|
+
|
1026
|
+
// does not handle one string, one not (such cases go past)
|
918
1027
|
ops.unshift(...stringOnly([
|
919
1028
|
// if left is string
|
920
1029
|
...leftType,
|
@@ -926,30 +1035,28 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
926
1035
|
...number(TYPES.string, Valtype.i32),
|
927
1036
|
[ Opcodes.i32_eq ],
|
928
1037
|
|
929
|
-
// if
|
930
|
-
[ Opcodes.
|
1038
|
+
// if both are true
|
1039
|
+
[ Opcodes.i32_and ],
|
931
1040
|
[ Opcodes.if, Blocktype.void ],
|
1041
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1042
|
+
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
1043
|
+
[ Opcodes.br, 1 ],
|
1044
|
+
[ Opcodes.end ],
|
932
1045
|
|
933
|
-
//
|
934
|
-
// if left is not string
|
1046
|
+
// if left is bytestring
|
935
1047
|
...leftType,
|
936
|
-
...number(TYPES.
|
937
|
-
[ Opcodes.
|
1048
|
+
...number(TYPES.bytestring, Valtype.i32),
|
1049
|
+
[ Opcodes.i32_eq ],
|
938
1050
|
|
939
|
-
// if right is
|
1051
|
+
// if right is bytestring
|
940
1052
|
...rightType,
|
941
|
-
...number(TYPES.
|
942
|
-
[ Opcodes.
|
1053
|
+
...number(TYPES.bytestring, Valtype.i32),
|
1054
|
+
[ Opcodes.i32_eq ],
|
943
1055
|
|
944
|
-
// if
|
945
|
-
[ Opcodes.
|
1056
|
+
// if both are true
|
1057
|
+
[ Opcodes.i32_and ],
|
946
1058
|
[ Opcodes.if, Blocktype.void ],
|
947
|
-
...
|
948
|
-
[ Opcodes.br, 2 ],
|
949
|
-
[ Opcodes.end ],
|
950
|
-
|
951
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
952
|
-
// ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1059
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], true),
|
953
1060
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
954
1061
|
[ Opcodes.br, 1 ],
|
955
1062
|
[ Opcodes.end ],
|
@@ -961,7 +1068,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
961
1068
|
// endOut.push(stringOnly([ Opcodes.end ]));
|
962
1069
|
endOut.unshift(stringOnly([ Opcodes.end ]));
|
963
1070
|
// }
|
964
|
-
}
|
1071
|
+
}
|
965
1072
|
|
966
1073
|
return finalize([
|
967
1074
|
...left,
|
@@ -1037,7 +1144,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1037
1144
|
params,
|
1038
1145
|
locals,
|
1039
1146
|
returns,
|
1040
|
-
returnType:
|
1147
|
+
returnType: returnType ?? TYPES.number,
|
1041
1148
|
wasm,
|
1042
1149
|
internal: true,
|
1043
1150
|
index: currentFuncIndex++
|
@@ -1060,6 +1167,7 @@ const generateLogicExp = (scope, decl) => {
|
|
1060
1167
|
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
1061
1168
|
};
|
1062
1169
|
|
1170
|
+
// potential future ideas for nan boxing (unused):
|
1063
1171
|
// T = JS type, V = value/pointer
|
1064
1172
|
// 0bTTT
|
1065
1173
|
// qNAN: 0 11111111111 1000000000000000000000000000000000000000000000000001
|
@@ -1073,7 +1181,6 @@ const generateLogicExp = (scope, decl) => {
|
|
1073
1181
|
// js type: 4 bits
|
1074
1182
|
// internal type: ? bits
|
1075
1183
|
// pointer: 32 bits
|
1076
|
-
|
1077
1184
|
// generic
|
1078
1185
|
// 1 23 4 5
|
1079
1186
|
// 0 11111111111 11TTTTIIII??????????PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
|
@@ -1083,40 +1190,18 @@ const generateLogicExp = (scope, decl) => {
|
|
1083
1190
|
// 4: internal type
|
1084
1191
|
// 5: pointer
|
1085
1192
|
|
1086
|
-
const
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
object: 0x04,
|
1092
|
-
function: 0x05,
|
1093
|
-
symbol: 0x06,
|
1094
|
-
bigint: 0x07,
|
1095
|
-
|
1096
|
-
// these are not "typeof" types but tracked internally
|
1097
|
-
_array: 0x10,
|
1098
|
-
_regexp: 0x11,
|
1099
|
-
_bytestring: 0x12
|
1100
|
-
};
|
1101
|
-
|
1102
|
-
const TYPE_NAMES = {
|
1103
|
-
[TYPES.number]: 'Number',
|
1104
|
-
[TYPES.boolean]: 'Boolean',
|
1105
|
-
[TYPES.string]: 'String',
|
1106
|
-
[TYPES.undefined]: 'undefined',
|
1107
|
-
[TYPES.object]: 'Object',
|
1108
|
-
[TYPES.function]: 'Function',
|
1109
|
-
[TYPES.symbol]: 'Symbol',
|
1110
|
-
[TYPES.bigint]: 'BigInt',
|
1111
|
-
|
1112
|
-
[TYPES._array]: 'Array',
|
1113
|
-
[TYPES._regexp]: 'RegExp',
|
1114
|
-
[TYPES._bytestring]: 'ByteString'
|
1193
|
+
const isExistingProtoFunc = name => {
|
1194
|
+
if (name.startsWith('__Array_prototype')) return !!prototypeFuncs[TYPES.array][name.slice(18)];
|
1195
|
+
if (name.startsWith('__String_prototype_')) return !!prototypeFuncs[TYPES.string][name.slice(19)];
|
1196
|
+
|
1197
|
+
return false;
|
1115
1198
|
};
|
1116
1199
|
|
1117
1200
|
const getType = (scope, _name) => {
|
1118
1201
|
const name = mapName(_name);
|
1119
1202
|
|
1203
|
+
// if (scope.locals[name] && !scope.locals[name + '#type']) console.log(name);
|
1204
|
+
|
1120
1205
|
if (typedInput && scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
|
1121
1206
|
if (scope.locals[name]) return [ [ Opcodes.local_get, scope.locals[name + '#type'].idx ] ];
|
1122
1207
|
|
@@ -1124,11 +1209,10 @@ const getType = (scope, _name) => {
|
|
1124
1209
|
if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
|
1125
1210
|
|
1126
1211
|
let type = TYPES.undefined;
|
1127
|
-
if (builtinVars[name]) type =
|
1212
|
+
if (builtinVars[name]) type = builtinVars[name].type ?? TYPES.number;
|
1128
1213
|
if (builtinFuncs[name] !== undefined || importedFuncs[name] !== undefined || funcIndex[name] !== undefined || internalConstrs[name] !== undefined) type = TYPES.function;
|
1129
1214
|
|
1130
|
-
if (name
|
1131
|
-
name.startsWith('__String_prototype_') && prototypeFuncs[TYPES.string][name.slice(19)]) type = TYPES.function;
|
1215
|
+
if (isExistingProtoFunc(name)) type = TYPES.function;
|
1132
1216
|
|
1133
1217
|
return number(type, Valtype.i32);
|
1134
1218
|
};
|
@@ -1151,23 +1235,24 @@ const setType = (scope, _name, type) => {
|
|
1151
1235
|
];
|
1152
1236
|
|
1153
1237
|
// throw new Error('could not find var');
|
1238
|
+
return [];
|
1154
1239
|
};
|
1155
1240
|
|
1156
1241
|
const getLastType = scope => {
|
1157
1242
|
scope.gotLastType = true;
|
1158
|
-
return [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ];
|
1243
|
+
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1159
1244
|
};
|
1160
1245
|
|
1161
1246
|
const setLastType = scope => {
|
1162
|
-
return [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ];
|
1247
|
+
return [ [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1163
1248
|
};
|
1164
1249
|
|
1165
1250
|
const getNodeType = (scope, node) => {
|
1166
1251
|
const inner = () => {
|
1167
1252
|
if (node.type === 'Literal') {
|
1168
|
-
if (node.regex) return TYPES.
|
1253
|
+
if (node.regex) return TYPES.regexp;
|
1169
1254
|
|
1170
|
-
if (typeof node.value === 'string' && byteStringable(node.value)) return TYPES.
|
1255
|
+
if (typeof node.value === 'string' && byteStringable(node.value)) return TYPES.bytestring;
|
1171
1256
|
|
1172
1257
|
return TYPES[typeof node.value];
|
1173
1258
|
}
|
@@ -1184,13 +1269,25 @@ const getNodeType = (scope, node) => {
|
|
1184
1269
|
const name = node.callee.name;
|
1185
1270
|
if (!name) {
|
1186
1271
|
// iife
|
1187
|
-
if (scope.locals['#last_type']) return
|
1272
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1188
1273
|
|
1189
1274
|
// presume
|
1190
1275
|
// todo: warn here?
|
1191
1276
|
return TYPES.number;
|
1192
1277
|
}
|
1193
1278
|
|
1279
|
+
if (node.type === 'NewExpression' && builtinFuncs[name + '$constructor']) {
|
1280
|
+
if (builtinFuncs[name + '$constructor'].typedReturns) {
|
1281
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1282
|
+
|
1283
|
+
// presume
|
1284
|
+
// todo: warn here?
|
1285
|
+
return TYPES.number;
|
1286
|
+
}
|
1287
|
+
|
1288
|
+
return builtinFuncs[name + '$constructor'].returnType ?? TYPES.number;
|
1289
|
+
}
|
1290
|
+
|
1194
1291
|
const func = funcs.find(x => x.name === name);
|
1195
1292
|
|
1196
1293
|
if (func) {
|
@@ -1198,7 +1295,7 @@ const getNodeType = (scope, node) => {
|
|
1198
1295
|
if (func.returnType) return func.returnType;
|
1199
1296
|
}
|
1200
1297
|
|
1201
|
-
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return
|
1298
|
+
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
|
1202
1299
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1203
1300
|
|
1204
1301
|
// check if this is a prototype function
|
@@ -1209,7 +1306,7 @@ const getNodeType = (scope, node) => {
|
|
1209
1306
|
const spl = name.slice(2).split('_');
|
1210
1307
|
|
1211
1308
|
const func = spl[spl.length - 1];
|
1212
|
-
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.
|
1309
|
+
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1213
1310
|
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1214
1311
|
}
|
1215
1312
|
|
@@ -1218,7 +1315,7 @@ const getNodeType = (scope, node) => {
|
|
1218
1315
|
return TYPES.number;
|
1219
1316
|
}
|
1220
1317
|
|
1221
|
-
if (scope.locals['#last_type']) return
|
1318
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1222
1319
|
|
1223
1320
|
// presume
|
1224
1321
|
// todo: warn here?
|
@@ -1261,7 +1358,7 @@ const getNodeType = (scope, node) => {
|
|
1261
1358
|
}
|
1262
1359
|
|
1263
1360
|
if (node.type === 'ArrayExpression') {
|
1264
|
-
return TYPES.
|
1361
|
+
return TYPES.array;
|
1265
1362
|
}
|
1266
1363
|
|
1267
1364
|
if (node.type === 'BinaryExpression') {
|
@@ -1273,6 +1370,7 @@ const getNodeType = (scope, node) => {
|
|
1273
1370
|
|
1274
1371
|
// todo: this should be dynamic but for now only static
|
1275
1372
|
if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
|
1373
|
+
if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) return TYPES.bytestring;
|
1276
1374
|
|
1277
1375
|
return TYPES.number;
|
1278
1376
|
|
@@ -1298,26 +1396,41 @@ const getNodeType = (scope, node) => {
|
|
1298
1396
|
if (node.operator === '!') return TYPES.boolean;
|
1299
1397
|
if (node.operator === 'void') return TYPES.undefined;
|
1300
1398
|
if (node.operator === 'delete') return TYPES.boolean;
|
1301
|
-
if (node.operator === 'typeof') return Prefs.bytestring ? TYPES.
|
1399
|
+
if (node.operator === 'typeof') return Prefs.bytestring ? TYPES.bytestring : TYPES.string;
|
1302
1400
|
|
1303
1401
|
return TYPES.number;
|
1304
1402
|
}
|
1305
1403
|
|
1306
1404
|
if (node.type === 'MemberExpression') {
|
1405
|
+
// hack: if something.name, string type
|
1406
|
+
if (node.property.name === 'name') {
|
1407
|
+
if (hasFuncWithName(node.object.name)) {
|
1408
|
+
return TYPES.bytestring;
|
1409
|
+
} else {
|
1410
|
+
return TYPES.undefined;
|
1411
|
+
}
|
1412
|
+
}
|
1413
|
+
|
1307
1414
|
// hack: if something.length, number type
|
1308
1415
|
if (node.property.name === 'length') return TYPES.number;
|
1309
1416
|
|
1310
1417
|
// ts hack
|
1311
1418
|
if (scope.locals[node.object.name]?.metadata?.type === TYPES.string) return TYPES.string;
|
1312
|
-
if (scope.locals[node.object.name]?.metadata?.type === TYPES.
|
1419
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES.bytestring) return TYPES.bytestring;
|
1420
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES.array) return TYPES.number;
|
1313
1421
|
|
1314
|
-
if (scope.locals['#last_type']) return
|
1422
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1315
1423
|
|
1316
1424
|
// presume
|
1317
1425
|
return TYPES.number;
|
1318
1426
|
}
|
1319
1427
|
|
1320
|
-
if (
|
1428
|
+
if (node.type === 'TaggedTemplateExpression') {
|
1429
|
+
// hack
|
1430
|
+
if (node.tag.name.startsWith('__Porffor_')) return TYPES.number;
|
1431
|
+
}
|
1432
|
+
|
1433
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1321
1434
|
|
1322
1435
|
// presume
|
1323
1436
|
// todo: warn here?
|
@@ -1350,7 +1463,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1350
1463
|
return makeString(scope, decl.value, global, name);
|
1351
1464
|
|
1352
1465
|
default:
|
1353
|
-
return todo(`cannot generate literal of type ${typeof decl.value}
|
1466
|
+
return todo(scope, `cannot generate literal of type ${typeof decl.value}`, true);
|
1354
1467
|
}
|
1355
1468
|
};
|
1356
1469
|
|
@@ -1359,6 +1472,8 @@ const countLeftover = wasm => {
|
|
1359
1472
|
|
1360
1473
|
for (let i = 0; i < wasm.length; i++) {
|
1361
1474
|
const inst = wasm[i];
|
1475
|
+
if (inst[0] == null) continue;
|
1476
|
+
|
1362
1477
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
1363
1478
|
if (inst[0] === Opcodes.if) count--;
|
1364
1479
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -1369,16 +1484,23 @@ const countLeftover = wasm => {
|
|
1369
1484
|
if (depth === 0)
|
1370
1485
|
if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1371
1486
|
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)) {}
|
1372
|
-
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const].includes(inst[0])) count++;
|
1487
|
+
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++;
|
1373
1488
|
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1374
1489
|
else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
|
1375
1490
|
else if (inst[0] === Opcodes.return) count = 0;
|
1376
1491
|
else if (inst[0] === Opcodes.call) {
|
1377
1492
|
let func = funcs.find(x => x.index === inst[1]);
|
1378
|
-
if (
|
1379
|
-
count
|
1380
|
-
} else
|
1381
|
-
|
1493
|
+
if (inst[1] === -1) {
|
1494
|
+
// todo: count for calling self
|
1495
|
+
} else if (!func && inst[1] < importedFuncs.length) {
|
1496
|
+
count -= importedFuncs[inst[1]].params;
|
1497
|
+
count += importedFuncs[inst[1]].returns;
|
1498
|
+
} else {
|
1499
|
+
if (func) {
|
1500
|
+
count -= func.params.length;
|
1501
|
+
} else count--;
|
1502
|
+
if (func) count += func.returns.length;
|
1503
|
+
}
|
1382
1504
|
} else count--;
|
1383
1505
|
|
1384
1506
|
// console.log(count, decompile([ inst ]).slice(0, -1));
|
@@ -1470,10 +1592,21 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1470
1592
|
name = func.name;
|
1471
1593
|
}
|
1472
1594
|
|
1473
|
-
if (name === 'eval' && decl.arguments[0]
|
1595
|
+
if (name === 'eval' && decl.arguments[0]?.type === 'Literal') {
|
1474
1596
|
// literal eval hack
|
1475
|
-
const code = decl.arguments[0]
|
1476
|
-
|
1597
|
+
const code = decl.arguments[0]?.value ?? '';
|
1598
|
+
|
1599
|
+
let parsed;
|
1600
|
+
try {
|
1601
|
+
parsed = parse(code, []);
|
1602
|
+
} catch (e) {
|
1603
|
+
if (e.name === 'SyntaxError') {
|
1604
|
+
// throw syntax errors of evals at runtime instead
|
1605
|
+
return internalThrow(scope, 'SyntaxError', e.message, true);
|
1606
|
+
}
|
1607
|
+
|
1608
|
+
throw e;
|
1609
|
+
}
|
1477
1610
|
|
1478
1611
|
const out = generate(scope, {
|
1479
1612
|
type: 'BlockStatement',
|
@@ -1487,13 +1620,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1487
1620
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1488
1621
|
out.push(
|
1489
1622
|
...getNodeType(scope, finalStatement),
|
1490
|
-
setLastType(scope)
|
1623
|
+
...setLastType(scope)
|
1491
1624
|
);
|
1492
1625
|
} else if (countLeftover(out) === 0) {
|
1493
1626
|
out.push(...number(UNDEFINED));
|
1494
1627
|
out.push(
|
1495
1628
|
...number(TYPES.undefined, Valtype.i32),
|
1496
|
-
setLastType(scope)
|
1629
|
+
...setLastType(scope)
|
1497
1630
|
);
|
1498
1631
|
}
|
1499
1632
|
|
@@ -1515,6 +1648,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1515
1648
|
|
1516
1649
|
target = { ...decl.callee };
|
1517
1650
|
target.name = spl.slice(0, -1).join('_');
|
1651
|
+
|
1652
|
+
// failed to lookup name, abort
|
1653
|
+
if (!lookupName(scope, target.name)[0]) protoName = null;
|
1518
1654
|
}
|
1519
1655
|
|
1520
1656
|
// literal.func()
|
@@ -1522,22 +1658,29 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1522
1658
|
// megahack for /regex/.func()
|
1523
1659
|
const funcName = decl.callee.property.name;
|
1524
1660
|
if (decl.callee.object.regex && Object.hasOwn(Rhemyn, funcName)) {
|
1525
|
-
const
|
1661
|
+
const regex = decl.callee.object.regex.pattern;
|
1662
|
+
const rhemynName = `regex_${funcName}_${regex}`;
|
1526
1663
|
|
1527
|
-
funcIndex[
|
1528
|
-
|
1664
|
+
if (!funcIndex[rhemynName]) {
|
1665
|
+
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
1666
|
+
|
1667
|
+
funcIndex[func.name] = func.index;
|
1668
|
+
funcs.push(func);
|
1669
|
+
}
|
1529
1670
|
|
1671
|
+
const idx = funcIndex[rhemynName];
|
1530
1672
|
return [
|
1531
1673
|
// make string arg
|
1532
1674
|
...generate(scope, decl.arguments[0]),
|
1675
|
+
Opcodes.i32_to_u,
|
1676
|
+
...getNodeType(scope, decl.arguments[0]),
|
1533
1677
|
|
1534
1678
|
// call regex func
|
1535
|
-
Opcodes.
|
1536
|
-
[ Opcodes.call, func.index ],
|
1679
|
+
[ Opcodes.call, idx ],
|
1537
1680
|
Opcodes.i32_from_u,
|
1538
1681
|
|
1539
1682
|
...number(TYPES.boolean, Valtype.i32),
|
1540
|
-
setLastType(scope)
|
1683
|
+
...setLastType(scope)
|
1541
1684
|
];
|
1542
1685
|
}
|
1543
1686
|
|
@@ -1562,12 +1705,31 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1562
1705
|
// }
|
1563
1706
|
|
1564
1707
|
if (protoName) {
|
1708
|
+
const protoBC = {};
|
1709
|
+
|
1710
|
+
const builtinProtoCands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + protoName));
|
1711
|
+
|
1712
|
+
if (!decl._protoInternalCall && builtinProtoCands.length > 0) {
|
1713
|
+
for (const x of builtinProtoCands) {
|
1714
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
1715
|
+
if (type == null) continue;
|
1716
|
+
|
1717
|
+
protoBC[type] = generateCall(scope, {
|
1718
|
+
callee: {
|
1719
|
+
type: 'Identifier',
|
1720
|
+
name: x
|
1721
|
+
},
|
1722
|
+
arguments: [ target, ...decl.arguments ],
|
1723
|
+
_protoInternalCall: true
|
1724
|
+
});
|
1725
|
+
}
|
1726
|
+
}
|
1727
|
+
|
1565
1728
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1566
1729
|
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1567
1730
|
return acc;
|
1568
1731
|
}, {});
|
1569
1732
|
|
1570
|
-
// no prototype function candidates, ignore
|
1571
1733
|
if (Object.keys(protoCands).length > 0) {
|
1572
1734
|
// use local for cached i32 length as commonly used
|
1573
1735
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
@@ -1585,7 +1747,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1585
1747
|
|
1586
1748
|
let allOptUnused = true;
|
1587
1749
|
let lengthI32CacheUsed = false;
|
1588
|
-
const protoBC = {};
|
1589
1750
|
for (const x in protoCands) {
|
1590
1751
|
const protoFunc = protoCands[x];
|
1591
1752
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
@@ -1593,7 +1754,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1593
1754
|
...RTArrayUtil.getLength(getPointer),
|
1594
1755
|
|
1595
1756
|
...number(TYPES.number, Valtype.i32),
|
1596
|
-
setLastType(scope)
|
1757
|
+
...setLastType(scope)
|
1597
1758
|
];
|
1598
1759
|
continue;
|
1599
1760
|
}
|
@@ -1630,7 +1791,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1630
1791
|
...protoOut,
|
1631
1792
|
|
1632
1793
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1633
|
-
setLastType(scope),
|
1794
|
+
...setLastType(scope),
|
1634
1795
|
[ Opcodes.end ]
|
1635
1796
|
];
|
1636
1797
|
}
|
@@ -1656,10 +1817,19 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1656
1817
|
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1657
1818
|
];
|
1658
1819
|
}
|
1820
|
+
|
1821
|
+
if (Object.keys(protoBC).length > 0) {
|
1822
|
+
return typeSwitch(scope, getNodeType(scope, target), {
|
1823
|
+
...protoBC,
|
1824
|
+
|
1825
|
+
// TODO: error better
|
1826
|
+
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1827
|
+
}, valtypeBinary);
|
1828
|
+
}
|
1659
1829
|
}
|
1660
1830
|
|
1661
1831
|
// TODO: only allows callee as literal
|
1662
|
-
if (!name) return todo(`only literal callees (got ${decl.callee.type})`);
|
1832
|
+
if (!name) return todo(scope, `only literal callees (got ${decl.callee.type})`);
|
1663
1833
|
|
1664
1834
|
let idx = funcIndex[name] ?? importedFuncs[name];
|
1665
1835
|
if (idx === undefined && builtinFuncs[name]) {
|
@@ -1669,20 +1839,20 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1669
1839
|
idx = funcIndex[name];
|
1670
1840
|
|
1671
1841
|
// infer arguments types from builtins params
|
1672
|
-
const func = funcs.find(x => x.name === name);
|
1673
|
-
for (let i = 0; i < decl.arguments.length; i++) {
|
1674
|
-
|
1675
|
-
|
1676
|
-
|
1677
|
-
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
1683
|
-
|
1684
|
-
|
1685
|
-
}
|
1842
|
+
// const func = funcs.find(x => x.name === name);
|
1843
|
+
// for (let i = 0; i < decl.arguments.length; i++) {
|
1844
|
+
// const arg = decl.arguments[i];
|
1845
|
+
// if (!arg.name) continue;
|
1846
|
+
|
1847
|
+
// const local = scope.locals[arg.name];
|
1848
|
+
// if (!local) continue;
|
1849
|
+
|
1850
|
+
// local.type = func.params[i];
|
1851
|
+
// if (local.type === Valtype.v128) {
|
1852
|
+
// // specify vec subtype inferred from last vec type in function name
|
1853
|
+
// local.vecType = name.split('_').reverse().find(x => x.includes('x'));
|
1854
|
+
// }
|
1855
|
+
// }
|
1686
1856
|
}
|
1687
1857
|
|
1688
1858
|
if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
@@ -1695,9 +1865,25 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1695
1865
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1696
1866
|
const wasmOps = {
|
1697
1867
|
// pointer, align, offset
|
1698
|
-
|
1868
|
+
i32_load: { imms: 2, args: [ true ], returns: 1 },
|
1869
|
+
// pointer, value, align, offset
|
1870
|
+
i32_store: { imms: 2, args: [ true, true ], returns: 0 },
|
1871
|
+
// pointer, align, offset
|
1872
|
+
i32_load8_u: { imms: 2, args: [ true ], returns: 1 },
|
1873
|
+
// pointer, value, align, offset
|
1874
|
+
i32_store8: { imms: 2, args: [ true, true ], returns: 0 },
|
1875
|
+
// pointer, align, offset
|
1876
|
+
i32_load16_u: { imms: 2, args: [ true ], returns: 1 },
|
1877
|
+
// pointer, value, align, offset
|
1878
|
+
i32_store16: { imms: 2, args: [ true, true ], returns: 0 },
|
1879
|
+
|
1880
|
+
// pointer, align, offset
|
1881
|
+
f64_load: { imms: 2, args: [ true ], returns: 0 }, // 0 due to not i32
|
1699
1882
|
// pointer, value, align, offset
|
1700
|
-
|
1883
|
+
f64_store: { imms: 2, args: [ true, false ], returns: 0 },
|
1884
|
+
|
1885
|
+
// value
|
1886
|
+
i32_const: { imms: 1, args: [], returns: 1 },
|
1701
1887
|
};
|
1702
1888
|
|
1703
1889
|
const opName = name.slice('__Porffor_wasm_'.length);
|
@@ -1706,28 +1892,32 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1706
1892
|
const op = wasmOps[opName];
|
1707
1893
|
|
1708
1894
|
const argOut = [];
|
1709
|
-
for (let i = 0; i < op.args; i++) argOut.push(
|
1895
|
+
for (let i = 0; i < op.args.length; i++) argOut.push(
|
1896
|
+
...generate(scope, decl.arguments[i]),
|
1897
|
+
...(op.args[i] ? [ Opcodes.i32_to ] : [])
|
1898
|
+
);
|
1710
1899
|
|
1711
1900
|
// literals only
|
1712
|
-
const imms = decl.arguments.slice(op.args).map(x => x.value);
|
1901
|
+
const imms = decl.arguments.slice(op.args.length).map(x => x.value);
|
1713
1902
|
|
1714
1903
|
return [
|
1715
1904
|
...argOut,
|
1716
|
-
[ Opcodes[opName], ...imms ]
|
1905
|
+
[ Opcodes[opName], ...imms ],
|
1906
|
+
...(new Array(op.returns).fill(Opcodes.i32_from))
|
1717
1907
|
];
|
1718
1908
|
}
|
1719
1909
|
}
|
1720
1910
|
|
1721
1911
|
if (idx === undefined) {
|
1722
|
-
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function
|
1723
|
-
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined
|
1912
|
+
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1913
|
+
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1724
1914
|
}
|
1725
1915
|
|
1726
1916
|
const func = funcs.find(x => x.index === idx);
|
1727
1917
|
|
1728
1918
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1729
1919
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1730
|
-
const typedReturns = userFunc || builtinFuncs[name]?.typedReturns;
|
1920
|
+
const typedReturns = (func ? func.returnType == null : userFunc) || builtinFuncs[name]?.typedReturns;
|
1731
1921
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1732
1922
|
|
1733
1923
|
let args = decl.arguments;
|
@@ -1748,7 +1938,11 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1748
1938
|
const arg = args[i];
|
1749
1939
|
out = out.concat(generate(scope, arg));
|
1750
1940
|
|
1751
|
-
if (builtinFuncs[name] && builtinFuncs[name].params[i] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1941
|
+
if (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1942
|
+
out.push(Opcodes.i32_to);
|
1943
|
+
}
|
1944
|
+
|
1945
|
+
if (importedFuncs[name] && name.startsWith('profile')) {
|
1752
1946
|
out.push(Opcodes.i32_to);
|
1753
1947
|
}
|
1754
1948
|
|
@@ -1767,9 +1961,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1767
1961
|
// ...number(type, Valtype.i32),
|
1768
1962
|
// [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1769
1963
|
// );
|
1770
|
-
} else out.push(setLastType(scope));
|
1964
|
+
} else out.push(...setLastType(scope));
|
1771
1965
|
|
1772
|
-
if (builtinFuncs[name] && builtinFuncs[name].returns[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1966
|
+
if (builtinFuncs[name] && builtinFuncs[name].returns?.[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1773
1967
|
out.push(Opcodes.i32_from);
|
1774
1968
|
}
|
1775
1969
|
|
@@ -1779,8 +1973,21 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1779
1973
|
const generateNew = (scope, decl, _global, _name) => {
|
1780
1974
|
// hack: basically treat this as a normal call for builtins for now
|
1781
1975
|
const name = mapName(decl.callee.name);
|
1976
|
+
|
1782
1977
|
if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1783
|
-
|
1978
|
+
|
1979
|
+
if (builtinFuncs[name + '$constructor']) {
|
1980
|
+
// custom ...$constructor override builtin func
|
1981
|
+
return generateCall(scope, {
|
1982
|
+
...decl,
|
1983
|
+
callee: {
|
1984
|
+
type: 'Identifier',
|
1985
|
+
name: name + '$constructor'
|
1986
|
+
}
|
1987
|
+
}, _global, _name);
|
1988
|
+
}
|
1989
|
+
|
1990
|
+
if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
|
1784
1991
|
|
1785
1992
|
return generateCall(scope, decl, _global, _name);
|
1786
1993
|
};
|
@@ -1897,7 +2104,7 @@ const brTable = (input, bc, returns) => {
|
|
1897
2104
|
};
|
1898
2105
|
|
1899
2106
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
1900
|
-
if (!Prefs.bytestring) delete bc[TYPES.
|
2107
|
+
if (!Prefs.bytestring) delete bc[TYPES.bytestring];
|
1901
2108
|
|
1902
2109
|
const known = knownType(scope, type);
|
1903
2110
|
if (known != null) {
|
@@ -1914,8 +2121,6 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1914
2121
|
[ Opcodes.block, returns ]
|
1915
2122
|
];
|
1916
2123
|
|
1917
|
-
// todo: use br_table?
|
1918
|
-
|
1919
2124
|
for (const x in bc) {
|
1920
2125
|
if (x === 'default') continue;
|
1921
2126
|
|
@@ -1971,12 +2176,14 @@ const addVarMetadata = (scope, name, global = false, metadata = {}) => {
|
|
1971
2176
|
};
|
1972
2177
|
|
1973
2178
|
const typeAnnoToPorfType = x => {
|
1974
|
-
if (
|
1975
|
-
if (TYPES[
|
2179
|
+
if (!x) return null;
|
2180
|
+
if (TYPES[x.toLowerCase()] != null) return TYPES[x.toLowerCase()];
|
2181
|
+
if (TYPES['_' + x.toLowerCase()] != null) return TYPES['_' + x.toLowerCase()];
|
1976
2182
|
|
1977
2183
|
switch (x) {
|
1978
2184
|
case 'i32':
|
1979
2185
|
case 'i64':
|
2186
|
+
case 'f64':
|
1980
2187
|
return TYPES.number;
|
1981
2188
|
}
|
1982
2189
|
|
@@ -1987,7 +2194,7 @@ const extractTypeAnnotation = decl => {
|
|
1987
2194
|
let a = decl;
|
1988
2195
|
while (a.typeAnnotation) a = a.typeAnnotation;
|
1989
2196
|
|
1990
|
-
let type, elementType;
|
2197
|
+
let type = null, elementType = null;
|
1991
2198
|
if (a.typeName) {
|
1992
2199
|
type = a.typeName.name;
|
1993
2200
|
} else if (a.type.endsWith('Keyword')) {
|
@@ -2000,7 +2207,7 @@ const extractTypeAnnotation = decl => {
|
|
2000
2207
|
const typeName = type;
|
2001
2208
|
type = typeAnnoToPorfType(type);
|
2002
2209
|
|
2003
|
-
if (type === TYPES.
|
2210
|
+
if (type === TYPES.bytestring && !Prefs.bytestring) type = TYPES.string;
|
2004
2211
|
|
2005
2212
|
// if (decl.name) console.log(decl.name, { type, elementType });
|
2006
2213
|
|
@@ -2014,11 +2221,12 @@ const generateVar = (scope, decl) => {
|
|
2014
2221
|
|
2015
2222
|
// global variable if in top scope (main) and var ..., or if wanted
|
2016
2223
|
const global = topLevel || decl._bare; // decl.kind === 'var';
|
2224
|
+
const target = global ? globals : scope.locals;
|
2017
2225
|
|
2018
2226
|
for (const x of decl.declarations) {
|
2019
2227
|
const name = mapName(x.id.name);
|
2020
2228
|
|
2021
|
-
if (!name) return todo('destructuring is not supported yet');
|
2229
|
+
if (!name) return todo(scope, 'destructuring is not supported yet');
|
2022
2230
|
|
2023
2231
|
if (x.init && isFuncType(x.init.type)) {
|
2024
2232
|
// hack for let a = function () { ... }
|
@@ -2035,16 +2243,29 @@ const generateVar = (scope, decl) => {
|
|
2035
2243
|
continue; // always ignore
|
2036
2244
|
}
|
2037
2245
|
|
2038
|
-
|
2246
|
+
// // generate init before allocating var
|
2247
|
+
// let generated;
|
2248
|
+
// if (x.init) generated = generate(scope, x.init, global, name);
|
2249
|
+
|
2250
|
+
const typed = typedInput && x.id.typeAnnotation;
|
2251
|
+
let idx = allocVar(scope, name, global, !(typed && extractTypeAnnotation(x.id).type != null));
|
2039
2252
|
|
2040
|
-
if (
|
2253
|
+
if (typed) {
|
2041
2254
|
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
2042
2255
|
}
|
2043
2256
|
|
2044
2257
|
if (x.init) {
|
2045
|
-
|
2046
|
-
|
2047
|
-
|
2258
|
+
const generated = generate(scope, x.init, global, name);
|
2259
|
+
if (scope.arrays?.get(name) != null) {
|
2260
|
+
// hack to set local as pointer before
|
2261
|
+
out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2262
|
+
if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
|
2263
|
+
generated.pop();
|
2264
|
+
out = out.concat(generated);
|
2265
|
+
} else {
|
2266
|
+
out = out.concat(generated);
|
2267
|
+
out.push([ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2268
|
+
}
|
2048
2269
|
out.push(...setType(scope, name, getNodeType(scope, x.init)));
|
2049
2270
|
}
|
2050
2271
|
|
@@ -2055,7 +2276,8 @@ const generateVar = (scope, decl) => {
|
|
2055
2276
|
return out;
|
2056
2277
|
};
|
2057
2278
|
|
2058
|
-
|
2279
|
+
// todo: optimize this func for valueUnused
|
2280
|
+
const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
2059
2281
|
const { type, name } = decl.left;
|
2060
2282
|
|
2061
2283
|
if (type === 'ObjectPattern') {
|
@@ -2070,22 +2292,30 @@ const generateAssign = (scope, decl) => {
|
|
2070
2292
|
return [];
|
2071
2293
|
}
|
2072
2294
|
|
2295
|
+
const op = decl.operator.slice(0, -1) || '=';
|
2296
|
+
|
2073
2297
|
// hack: .length setter
|
2074
2298
|
if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
|
2075
2299
|
const name = decl.left.object.name;
|
2076
|
-
const pointer = arrays
|
2300
|
+
const pointer = scope.arrays?.get(name);
|
2077
2301
|
|
2078
|
-
const aotPointer = pointer != null;
|
2302
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2079
2303
|
|
2080
2304
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
2305
|
+
const pointerTmp = op === '=' ? null : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
2081
2306
|
|
2082
2307
|
return [
|
2083
2308
|
...(aotPointer ? number(0, Valtype.i32) : [
|
2084
2309
|
...generate(scope, decl.left.object),
|
2085
2310
|
Opcodes.i32_to_u
|
2086
2311
|
]),
|
2312
|
+
...(!pointerTmp ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2087
2313
|
|
2088
|
-
...generate(scope, decl.right),
|
2314
|
+
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2315
|
+
[ Opcodes.local_get, pointerTmp ],
|
2316
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
2317
|
+
Opcodes.i32_from_u
|
2318
|
+
], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right))),
|
2089
2319
|
[ Opcodes.local_tee, newValueTmp ],
|
2090
2320
|
|
2091
2321
|
Opcodes.i32_to_u,
|
@@ -2095,21 +2325,19 @@ const generateAssign = (scope, decl) => {
|
|
2095
2325
|
];
|
2096
2326
|
}
|
2097
2327
|
|
2098
|
-
const op = decl.operator.slice(0, -1) || '=';
|
2099
|
-
|
2100
2328
|
// arr[i]
|
2101
2329
|
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
2102
2330
|
const name = decl.left.object.name;
|
2103
|
-
const pointer = arrays
|
2331
|
+
const pointer = scope.arrays?.get(name);
|
2104
2332
|
|
2105
|
-
const aotPointer = pointer != null;
|
2333
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2106
2334
|
|
2107
2335
|
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
2108
2336
|
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
2109
2337
|
|
2110
2338
|
return [
|
2111
2339
|
...typeSwitch(scope, getNodeType(scope, decl.left.object), {
|
2112
|
-
[TYPES.
|
2340
|
+
[TYPES.array]: [
|
2113
2341
|
...(aotPointer ? [] : [
|
2114
2342
|
...generate(scope, decl.left.object),
|
2115
2343
|
Opcodes.i32_to_u
|
@@ -2158,7 +2386,7 @@ const generateAssign = (scope, decl) => {
|
|
2158
2386
|
];
|
2159
2387
|
}
|
2160
2388
|
|
2161
|
-
if (!name) return todo('destructuring is not supported yet');
|
2389
|
+
if (!name) return todo(scope, 'destructuring is not supported yet', true);
|
2162
2390
|
|
2163
2391
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2164
2392
|
|
@@ -2263,7 +2491,7 @@ const generateUnary = (scope, decl) => {
|
|
2263
2491
|
return out;
|
2264
2492
|
}
|
2265
2493
|
|
2266
|
-
case 'delete':
|
2494
|
+
case 'delete': {
|
2267
2495
|
let toReturn = true, toGenerate = true;
|
2268
2496
|
|
2269
2497
|
if (decl.argument.type === 'Identifier') {
|
@@ -2285,40 +2513,60 @@ const generateUnary = (scope, decl) => {
|
|
2285
2513
|
|
2286
2514
|
out.push(...number(toReturn ? 1 : 0));
|
2287
2515
|
return out;
|
2516
|
+
}
|
2517
|
+
|
2518
|
+
case 'typeof': {
|
2519
|
+
let overrideType, toGenerate = true;
|
2520
|
+
|
2521
|
+
if (decl.argument.type === 'Identifier') {
|
2522
|
+
const out = generateIdent(scope, decl.argument);
|
2523
|
+
|
2524
|
+
// if ReferenceError (undeclared var), ignore and return undefined
|
2525
|
+
if (out[1]) {
|
2526
|
+
// does not exist (2 ops from throw)
|
2527
|
+
overrideType = number(TYPES.undefined, Valtype.i32);
|
2528
|
+
toGenerate = false;
|
2529
|
+
}
|
2530
|
+
}
|
2288
2531
|
|
2289
|
-
|
2290
|
-
|
2532
|
+
const out = toGenerate ? generate(scope, decl.argument) : [];
|
2533
|
+
disposeLeftover(out);
|
2534
|
+
|
2535
|
+
out.push(...typeSwitch(scope, overrideType ?? getNodeType(scope, decl.argument), {
|
2291
2536
|
[TYPES.number]: makeString(scope, 'number', false, '#typeof_result'),
|
2292
2537
|
[TYPES.boolean]: makeString(scope, 'boolean', false, '#typeof_result'),
|
2293
2538
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
2294
2539
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2295
2540
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2296
2541
|
|
2297
|
-
[TYPES.
|
2542
|
+
[TYPES.bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2298
2543
|
|
2299
2544
|
// object and internal types
|
2300
2545
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
2301
|
-
});
|
2546
|
+
}));
|
2547
|
+
|
2548
|
+
return out;
|
2549
|
+
}
|
2302
2550
|
|
2303
2551
|
default:
|
2304
|
-
return todo(`unary operator ${decl.operator} not implemented yet
|
2552
|
+
return todo(scope, `unary operator ${decl.operator} not implemented yet`, true);
|
2305
2553
|
}
|
2306
2554
|
};
|
2307
2555
|
|
2308
|
-
const generateUpdate = (scope, decl) => {
|
2556
|
+
const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
|
2309
2557
|
const { name } = decl.argument;
|
2310
2558
|
|
2311
2559
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2312
2560
|
|
2313
2561
|
if (local === undefined) {
|
2314
|
-
return todo(`update expression with undefined variable
|
2562
|
+
return todo(scope, `update expression with undefined variable`, true);
|
2315
2563
|
}
|
2316
2564
|
|
2317
2565
|
const idx = local.idx;
|
2318
2566
|
const out = [];
|
2319
2567
|
|
2320
2568
|
out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2321
|
-
if (!decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2569
|
+
if (!decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2322
2570
|
|
2323
2571
|
switch (decl.operator) {
|
2324
2572
|
case '++':
|
@@ -2331,7 +2579,7 @@ const generateUpdate = (scope, decl) => {
|
|
2331
2579
|
}
|
2332
2580
|
|
2333
2581
|
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2334
|
-
if (decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2582
|
+
if (decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2335
2583
|
|
2336
2584
|
return out;
|
2337
2585
|
};
|
@@ -2371,7 +2619,7 @@ const generateConditional = (scope, decl) => {
|
|
2371
2619
|
// note type
|
2372
2620
|
out.push(
|
2373
2621
|
...getNodeType(scope, decl.consequent),
|
2374
|
-
setLastType(scope)
|
2622
|
+
...setLastType(scope)
|
2375
2623
|
);
|
2376
2624
|
|
2377
2625
|
out.push([ Opcodes.else ]);
|
@@ -2380,7 +2628,7 @@ const generateConditional = (scope, decl) => {
|
|
2380
2628
|
// note type
|
2381
2629
|
out.push(
|
2382
2630
|
...getNodeType(scope, decl.alternate),
|
2383
|
-
setLastType(scope)
|
2631
|
+
...setLastType(scope)
|
2384
2632
|
);
|
2385
2633
|
|
2386
2634
|
out.push([ Opcodes.end ]);
|
@@ -2394,7 +2642,7 @@ const generateFor = (scope, decl) => {
|
|
2394
2642
|
const out = [];
|
2395
2643
|
|
2396
2644
|
if (decl.init) {
|
2397
|
-
out.push(...generate(scope, decl.init));
|
2645
|
+
out.push(...generate(scope, decl.init, false, undefined, true));
|
2398
2646
|
disposeLeftover(out);
|
2399
2647
|
}
|
2400
2648
|
|
@@ -2412,7 +2660,7 @@ const generateFor = (scope, decl) => {
|
|
2412
2660
|
out.push(...generate(scope, decl.body));
|
2413
2661
|
out.push([ Opcodes.end ]);
|
2414
2662
|
|
2415
|
-
if (decl.update) out.push(...generate(scope, decl.update));
|
2663
|
+
if (decl.update) out.push(...generate(scope, decl.update, false, undefined, true));
|
2416
2664
|
|
2417
2665
|
out.push([ Opcodes.br, 1 ]);
|
2418
2666
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2440,6 +2688,36 @@ const generateWhile = (scope, decl) => {
|
|
2440
2688
|
return out;
|
2441
2689
|
};
|
2442
2690
|
|
2691
|
+
const generateDoWhile = (scope, decl) => {
|
2692
|
+
const out = [];
|
2693
|
+
|
2694
|
+
out.push([ Opcodes.loop, Blocktype.void ]);
|
2695
|
+
depth.push('dowhile');
|
2696
|
+
|
2697
|
+
// block for break (includes all)
|
2698
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2699
|
+
depth.push('block');
|
2700
|
+
|
2701
|
+
// block for continue
|
2702
|
+
// includes body but not test+loop so we can exit body at anytime
|
2703
|
+
// and still test+loop after
|
2704
|
+
out.push([ Opcodes.block, Blocktype.void ]);
|
2705
|
+
depth.push('block');
|
2706
|
+
|
2707
|
+
out.push(...generate(scope, decl.body));
|
2708
|
+
|
2709
|
+
out.push([ Opcodes.end ]);
|
2710
|
+
depth.pop();
|
2711
|
+
|
2712
|
+
out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2713
|
+
out.push([ Opcodes.br_if, 1 ]);
|
2714
|
+
|
2715
|
+
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
2716
|
+
depth.pop(); depth.pop();
|
2717
|
+
|
2718
|
+
return out;
|
2719
|
+
};
|
2720
|
+
|
2443
2721
|
const generateForOf = (scope, decl) => {
|
2444
2722
|
const out = [];
|
2445
2723
|
|
@@ -2476,7 +2754,10 @@ const generateForOf = (scope, decl) => {
|
|
2476
2754
|
generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
|
2477
2755
|
}
|
2478
2756
|
|
2757
|
+
// if (!leftName) console.log(decl.left?.declarations?.[0]?.id ?? decl.left);
|
2758
|
+
|
2479
2759
|
const [ local, isGlobal ] = lookupName(scope, leftName);
|
2760
|
+
if (!local) return todo(scope, 'for of failed to get left local (probably destructure)');
|
2480
2761
|
|
2481
2762
|
depth.push('block');
|
2482
2763
|
depth.push('block');
|
@@ -2485,6 +2766,7 @@ const generateForOf = (scope, decl) => {
|
|
2485
2766
|
// hack: this is naughty and will break things!
|
2486
2767
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2487
2768
|
if (pages.hasAnyString) {
|
2769
|
+
// todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
|
2488
2770
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2489
2771
|
rawElements: new Array(1)
|
2490
2772
|
}, isGlobal, leftName, true, 'i16');
|
@@ -2493,7 +2775,7 @@ const generateForOf = (scope, decl) => {
|
|
2493
2775
|
// set type for local
|
2494
2776
|
// todo: optimize away counter and use end pointer
|
2495
2777
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2496
|
-
[TYPES.
|
2778
|
+
[TYPES.array]: [
|
2497
2779
|
...setType(scope, leftName, TYPES.number),
|
2498
2780
|
|
2499
2781
|
[ Opcodes.loop, Blocktype.void ],
|
@@ -2576,6 +2858,56 @@ const generateForOf = (scope, decl) => {
|
|
2576
2858
|
[ Opcodes.end ],
|
2577
2859
|
[ Opcodes.end ]
|
2578
2860
|
],
|
2861
|
+
[TYPES.bytestring]: [
|
2862
|
+
...setType(scope, leftName, TYPES.bytestring),
|
2863
|
+
|
2864
|
+
[ Opcodes.loop, Blocktype.void ],
|
2865
|
+
|
2866
|
+
// setup new/out array
|
2867
|
+
...newOut,
|
2868
|
+
[ Opcodes.drop ],
|
2869
|
+
|
2870
|
+
...number(0, Valtype.i32), // base 0 for store after
|
2871
|
+
|
2872
|
+
// load current string ind {arg}
|
2873
|
+
[ Opcodes.local_get, pointer ],
|
2874
|
+
[ Opcodes.local_get, counter ],
|
2875
|
+
[ Opcodes.i32_add ],
|
2876
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2877
|
+
|
2878
|
+
// store to new string ind 0
|
2879
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2880
|
+
|
2881
|
+
// return new string (page)
|
2882
|
+
...number(newPointer),
|
2883
|
+
|
2884
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2885
|
+
|
2886
|
+
[ Opcodes.block, Blocktype.void ],
|
2887
|
+
[ Opcodes.block, Blocktype.void ],
|
2888
|
+
...generate(scope, decl.body),
|
2889
|
+
[ Opcodes.end ],
|
2890
|
+
|
2891
|
+
// increment iter pointer
|
2892
|
+
// [ Opcodes.local_get, pointer ],
|
2893
|
+
// ...number(1, Valtype.i32),
|
2894
|
+
// [ Opcodes.i32_add ],
|
2895
|
+
// [ Opcodes.local_set, pointer ],
|
2896
|
+
|
2897
|
+
// increment counter by 1
|
2898
|
+
[ Opcodes.local_get, counter ],
|
2899
|
+
...number(1, Valtype.i32),
|
2900
|
+
[ Opcodes.i32_add ],
|
2901
|
+
[ Opcodes.local_tee, counter ],
|
2902
|
+
|
2903
|
+
// loop if counter != length
|
2904
|
+
[ Opcodes.local_get, length ],
|
2905
|
+
[ Opcodes.i32_ne ],
|
2906
|
+
[ Opcodes.br_if, 1 ],
|
2907
|
+
|
2908
|
+
[ Opcodes.end ],
|
2909
|
+
[ Opcodes.end ]
|
2910
|
+
],
|
2579
2911
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2580
2912
|
}, Blocktype.void));
|
2581
2913
|
|
@@ -2586,28 +2918,65 @@ const generateForOf = (scope, decl) => {
|
|
2586
2918
|
return out;
|
2587
2919
|
};
|
2588
2920
|
|
2921
|
+
// find the nearest loop in depth map by type
|
2589
2922
|
const getNearestLoop = () => {
|
2590
2923
|
for (let i = depth.length - 1; i >= 0; i--) {
|
2591
|
-
if (
|
2924
|
+
if (['while', 'dowhile', 'for', 'forof'].includes(depth[i])) return i;
|
2592
2925
|
}
|
2593
2926
|
|
2594
2927
|
return -1;
|
2595
2928
|
};
|
2596
2929
|
|
2597
2930
|
const generateBreak = (scope, decl) => {
|
2598
|
-
const
|
2931
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2932
|
+
const type = depth[target];
|
2933
|
+
|
2934
|
+
// different loop types have different branch offsets
|
2935
|
+
// as they have different wasm block/loop/if structures
|
2936
|
+
// we need to use the right offset by type to branch to the one we want
|
2937
|
+
// for a break: exit the loop without executing anything else inside it
|
2938
|
+
const offset = ({
|
2939
|
+
for: 2, // loop > if (wanted branch) > block (we are here)
|
2940
|
+
while: 2, // loop > if (wanted branch) (we are here)
|
2941
|
+
dowhile: 2, // loop > block (wanted branch) > block (we are here)
|
2942
|
+
forof: 2, // loop > block (wanted branch) > block (we are here)
|
2943
|
+
if: 1 // break inside if, branch 0 to skip the rest of the if
|
2944
|
+
})[type];
|
2945
|
+
|
2599
2946
|
return [
|
2600
|
-
[ Opcodes.br, ...signedLEB128(
|
2947
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2601
2948
|
];
|
2602
2949
|
};
|
2603
2950
|
|
2604
2951
|
const generateContinue = (scope, decl) => {
|
2605
|
-
const
|
2952
|
+
const target = decl.label ? scope.labels.get(decl.label.name) : getNearestLoop();
|
2953
|
+
const type = depth[target];
|
2954
|
+
|
2955
|
+
// different loop types have different branch offsets
|
2956
|
+
// as they have different wasm block/loop/if structures
|
2957
|
+
// we need to use the right offset by type to branch to the one we want
|
2958
|
+
// for a continue: do test for the loop, and then loop depending on that success
|
2959
|
+
const offset = ({
|
2960
|
+
for: 3, // loop (wanted branch) > if > block (we are here)
|
2961
|
+
while: 1, // loop (wanted branch) > if (we are here)
|
2962
|
+
dowhile: 3, // loop > block > block (wanted branch) (we are here)
|
2963
|
+
forof: 3 // loop > block > block (wanted branch) (we are here)
|
2964
|
+
})[type];
|
2965
|
+
|
2606
2966
|
return [
|
2607
|
-
[ Opcodes.br, ...signedLEB128(
|
2967
|
+
[ Opcodes.br, ...signedLEB128(depth.length - target - offset) ]
|
2608
2968
|
];
|
2609
2969
|
};
|
2610
2970
|
|
2971
|
+
const generateLabel = (scope, decl) => {
|
2972
|
+
scope.labels ??= new Map();
|
2973
|
+
|
2974
|
+
const name = decl.label.name;
|
2975
|
+
scope.labels.set(name, depth.length);
|
2976
|
+
|
2977
|
+
return generate(scope, decl.body);
|
2978
|
+
};
|
2979
|
+
|
2611
2980
|
const generateThrow = (scope, decl) => {
|
2612
2981
|
scope.throws = true;
|
2613
2982
|
|
@@ -2640,7 +3009,7 @@ const generateThrow = (scope, decl) => {
|
|
2640
3009
|
};
|
2641
3010
|
|
2642
3011
|
const generateTry = (scope, decl) => {
|
2643
|
-
if (decl.finalizer) return todo('try finally not implemented yet');
|
3012
|
+
if (decl.finalizer) return todo(scope, 'try finally not implemented yet');
|
2644
3013
|
|
2645
3014
|
const out = [];
|
2646
3015
|
|
@@ -2671,7 +3040,7 @@ const generateAssignPat = (scope, decl) => {
|
|
2671
3040
|
// TODO
|
2672
3041
|
// if identifier declared, use that
|
2673
3042
|
// else, use default (right)
|
2674
|
-
return todo('assignment pattern (optional arg)');
|
3043
|
+
return todo(scope, 'assignment pattern (optional arg)');
|
2675
3044
|
};
|
2676
3045
|
|
2677
3046
|
let pages = new Map();
|
@@ -2750,16 +3119,22 @@ const getAllocType = itemType => {
|
|
2750
3119
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2751
3120
|
const out = [];
|
2752
3121
|
|
3122
|
+
scope.arrays ??= new Map();
|
3123
|
+
|
2753
3124
|
let firstAssign = false;
|
2754
|
-
if (!arrays.has(name) || name === '$undeclared') {
|
3125
|
+
if (!scope.arrays.has(name) || name === '$undeclared') {
|
2755
3126
|
firstAssign = true;
|
2756
3127
|
|
2757
3128
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2758
3129
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2759
|
-
|
3130
|
+
|
3131
|
+
if (Prefs.scopedPageNames) scope.arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType) * pageSize);
|
3132
|
+
else scope.arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2760
3133
|
}
|
2761
3134
|
|
2762
|
-
const pointer = arrays.get(name);
|
3135
|
+
const pointer = scope.arrays.get(name);
|
3136
|
+
|
3137
|
+
const local = global ? globals[name] : scope.locals[name];
|
2763
3138
|
|
2764
3139
|
const useRawElements = !!decl.rawElements;
|
2765
3140
|
const elements = useRawElements ? decl.rawElements : decl.elements;
|
@@ -2793,11 +3168,22 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2793
3168
|
return [ out, pointer ];
|
2794
3169
|
}
|
2795
3170
|
|
3171
|
+
const pointerTmp = local != null ? localTmp(scope, '#makearray_pointer_tmp', Valtype.i32) : null;
|
3172
|
+
if (pointerTmp != null) {
|
3173
|
+
out.push(
|
3174
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
3175
|
+
Opcodes.i32_to_u,
|
3176
|
+
[ Opcodes.local_set, pointerTmp ]
|
3177
|
+
);
|
3178
|
+
}
|
3179
|
+
|
3180
|
+
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3181
|
+
|
2796
3182
|
// store length as 0th array
|
2797
3183
|
out.push(
|
2798
|
-
...
|
3184
|
+
...pointerWasm,
|
2799
3185
|
...number(length, Valtype.i32),
|
2800
|
-
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1,
|
3186
|
+
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
|
2801
3187
|
);
|
2802
3188
|
|
2803
3189
|
const storeOp = StoreOps[itemType];
|
@@ -2806,14 +3192,14 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2806
3192
|
if (elements[i] == null) continue;
|
2807
3193
|
|
2808
3194
|
out.push(
|
2809
|
-
...
|
3195
|
+
...pointerWasm,
|
2810
3196
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
2811
|
-
[ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(
|
3197
|
+
[ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2812
3198
|
);
|
2813
3199
|
}
|
2814
3200
|
|
2815
3201
|
// local value as pointer
|
2816
|
-
out.push(...
|
3202
|
+
out.push(...pointerWasm, Opcodes.i32_from_u);
|
2817
3203
|
|
2818
3204
|
return [ out, pointer ];
|
2819
3205
|
};
|
@@ -2845,20 +3231,53 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
2845
3231
|
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2846
3232
|
};
|
2847
3233
|
|
2848
|
-
let arrays = new Map();
|
2849
3234
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
2850
3235
|
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
2851
3236
|
};
|
2852
3237
|
|
2853
3238
|
export const generateMember = (scope, decl, _global, _name) => {
|
2854
3239
|
const name = decl.object.name;
|
2855
|
-
const pointer = arrays
|
3240
|
+
const pointer = scope.arrays?.get(name);
|
3241
|
+
|
3242
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2856
3243
|
|
2857
|
-
|
3244
|
+
// hack: .name
|
3245
|
+
if (decl.property.name === 'name') {
|
3246
|
+
if (hasFuncWithName(name)) {
|
3247
|
+
let nameProp = name;
|
3248
|
+
|
3249
|
+
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3250
|
+
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3251
|
+
|
3252
|
+
return makeString(scope, nameProp, _global, _name, true);
|
3253
|
+
} else {
|
3254
|
+
return generate(scope, DEFAULT_VALUE);
|
3255
|
+
}
|
3256
|
+
}
|
2858
3257
|
|
2859
3258
|
// hack: .length
|
2860
3259
|
if (decl.property.name === 'length') {
|
2861
|
-
|
3260
|
+
const func = funcs.find(x => x.name === name);
|
3261
|
+
if (func) {
|
3262
|
+
const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
|
3263
|
+
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
3264
|
+
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3265
|
+
}
|
3266
|
+
|
3267
|
+
if (builtinFuncs[name + '$constructor']) {
|
3268
|
+
const regularFunc = builtinFuncs[name];
|
3269
|
+
const regularParams = regularFunc.typedParams ? (regularFunc.params.length / 2) : regularFunc.params.length;
|
3270
|
+
|
3271
|
+
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3272
|
+
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3273
|
+
|
3274
|
+
return number(Math.max(regularParams, constructorParams));
|
3275
|
+
}
|
3276
|
+
|
3277
|
+
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3278
|
+
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3279
|
+
if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
|
3280
|
+
|
2862
3281
|
return [
|
2863
3282
|
...(aotPointer ? number(0, Valtype.i32) : [
|
2864
3283
|
...generate(scope, decl.object),
|
@@ -2883,7 +3302,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2883
3302
|
}
|
2884
3303
|
|
2885
3304
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
2886
|
-
[TYPES.
|
3305
|
+
[TYPES.array]: [
|
2887
3306
|
// get index as valtype
|
2888
3307
|
...property,
|
2889
3308
|
|
@@ -2902,7 +3321,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2902
3321
|
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2903
3322
|
|
2904
3323
|
...number(TYPES.number, Valtype.i32),
|
2905
|
-
setLastType(scope)
|
3324
|
+
...setLastType(scope)
|
2906
3325
|
],
|
2907
3326
|
|
2908
3327
|
[TYPES.string]: [
|
@@ -2934,9 +3353,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2934
3353
|
...number(newPointer),
|
2935
3354
|
|
2936
3355
|
...number(TYPES.string, Valtype.i32),
|
2937
|
-
setLastType(scope)
|
3356
|
+
...setLastType(scope)
|
2938
3357
|
],
|
2939
|
-
[TYPES.
|
3358
|
+
[TYPES.bytestring]: [
|
2940
3359
|
// setup new/out array
|
2941
3360
|
...newOut,
|
2942
3361
|
[ Opcodes.drop ],
|
@@ -2953,19 +3372,19 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2953
3372
|
]),
|
2954
3373
|
|
2955
3374
|
// load current string ind {arg}
|
2956
|
-
[ Opcodes.i32_load8_u,
|
3375
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2957
3376
|
|
2958
3377
|
// store to new string ind 0
|
2959
|
-
[ Opcodes.i32_store8,
|
3378
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2960
3379
|
|
2961
3380
|
// return new string (page)
|
2962
3381
|
...number(newPointer),
|
2963
3382
|
|
2964
|
-
...number(TYPES.
|
2965
|
-
setLastType(scope)
|
3383
|
+
...number(TYPES.bytestring, Valtype.i32),
|
3384
|
+
...setLastType(scope)
|
2966
3385
|
],
|
2967
3386
|
|
2968
|
-
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet')
|
3387
|
+
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
2969
3388
|
});
|
2970
3389
|
};
|
2971
3390
|
|
@@ -2975,28 +3394,36 @@ const objectHack = node => {
|
|
2975
3394
|
if (!node) return node;
|
2976
3395
|
|
2977
3396
|
if (node.type === 'MemberExpression') {
|
2978
|
-
|
3397
|
+
const out = (() => {
|
3398
|
+
if (node.computed || node.optional) return;
|
2979
3399
|
|
2980
|
-
|
3400
|
+
let objectName = node.object.name;
|
2981
3401
|
|
2982
|
-
|
2983
|
-
|
3402
|
+
// if object is not identifier or another member exp, give up
|
3403
|
+
if (node.object.type !== 'Identifier' && node.object.type !== 'MemberExpression') return;
|
3404
|
+
if (objectName && ['undefined', 'null', 'NaN', 'Infinity'].includes(objectName)) return;
|
2984
3405
|
|
2985
|
-
|
3406
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
2986
3407
|
|
2987
|
-
|
2988
|
-
|
3408
|
+
// if .name or .length, give up (hack within a hack!)
|
3409
|
+
if (['name', 'length'].includes(node.property.name)) {
|
3410
|
+
node.object = objectHack(node.object);
|
3411
|
+
return;
|
3412
|
+
}
|
2989
3413
|
|
2990
|
-
|
2991
|
-
|
3414
|
+
// no object name, give up
|
3415
|
+
if (!objectName) return;
|
2992
3416
|
|
2993
|
-
|
2994
|
-
|
3417
|
+
const name = '__' + objectName + '_' + node.property.name;
|
3418
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2995
3419
|
|
2996
|
-
|
2997
|
-
|
2998
|
-
|
2999
|
-
|
3420
|
+
return {
|
3421
|
+
type: 'Identifier',
|
3422
|
+
name
|
3423
|
+
};
|
3424
|
+
})();
|
3425
|
+
|
3426
|
+
if (out) return out;
|
3000
3427
|
}
|
3001
3428
|
|
3002
3429
|
for (const x in node) {
|
@@ -3010,8 +3437,8 @@ const objectHack = node => {
|
|
3010
3437
|
};
|
3011
3438
|
|
3012
3439
|
const generateFunc = (scope, decl) => {
|
3013
|
-
if (decl.async) return todo('async functions are not supported');
|
3014
|
-
if (decl.generator) return todo('generator functions are not supported');
|
3440
|
+
if (decl.async) return todo(scope, 'async functions are not supported');
|
3441
|
+
if (decl.generator) return todo(scope, 'generator functions are not supported');
|
3015
3442
|
|
3016
3443
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3017
3444
|
const params = decl.params ?? [];
|
@@ -3027,6 +3454,11 @@ const generateFunc = (scope, decl) => {
|
|
3027
3454
|
name
|
3028
3455
|
};
|
3029
3456
|
|
3457
|
+
if (typedInput && decl.returnType) {
|
3458
|
+
innerScope.returnType = extractTypeAnnotation(decl.returnType).type;
|
3459
|
+
innerScope.returns = [ valtypeBinary ];
|
3460
|
+
}
|
3461
|
+
|
3030
3462
|
for (let i = 0; i < params.length; i++) {
|
3031
3463
|
allocVar(innerScope, params[i].name, false);
|
3032
3464
|
|
@@ -3089,16 +3521,6 @@ const generateCode = (scope, decl) => {
|
|
3089
3521
|
};
|
3090
3522
|
|
3091
3523
|
const internalConstrs = {
|
3092
|
-
Boolean: {
|
3093
|
-
generate: (scope, decl) => {
|
3094
|
-
if (decl.arguments.length === 0) return number(0);
|
3095
|
-
|
3096
|
-
// should generate/run all args
|
3097
|
-
return truthy(scope, generate(scope, decl.arguments[0]), getNodeType(scope, decl.arguments[0]), false, false);
|
3098
|
-
},
|
3099
|
-
type: TYPES.boolean
|
3100
|
-
},
|
3101
|
-
|
3102
3524
|
Array: {
|
3103
3525
|
generate: (scope, decl, global, name) => {
|
3104
3526
|
// new Array(i0, i1, ...)
|
@@ -3116,7 +3538,7 @@ const internalConstrs = {
|
|
3116
3538
|
|
3117
3539
|
// todo: check in wasm instead of here
|
3118
3540
|
const literalValue = arg.value ?? 0;
|
3119
|
-
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length');
|
3541
|
+
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
|
3120
3542
|
|
3121
3543
|
return [
|
3122
3544
|
...number(0, Valtype.i32),
|
@@ -3127,7 +3549,8 @@ const internalConstrs = {
|
|
3127
3549
|
...number(pointer)
|
3128
3550
|
];
|
3129
3551
|
},
|
3130
|
-
type: TYPES.
|
3552
|
+
type: TYPES.array,
|
3553
|
+
length: 1
|
3131
3554
|
},
|
3132
3555
|
|
3133
3556
|
__Array_of: {
|
@@ -3138,27 +3561,134 @@ const internalConstrs = {
|
|
3138
3561
|
elements: decl.arguments
|
3139
3562
|
}, global, name);
|
3140
3563
|
},
|
3141
|
-
type: TYPES.
|
3564
|
+
type: TYPES.array,
|
3565
|
+
notConstr: true,
|
3566
|
+
length: 0
|
3567
|
+
},
|
3568
|
+
|
3569
|
+
__Porffor_fastOr: {
|
3570
|
+
generate: (scope, decl) => {
|
3571
|
+
const out = [];
|
3572
|
+
|
3573
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3574
|
+
out.push(
|
3575
|
+
...generate(scope, decl.arguments[i]),
|
3576
|
+
Opcodes.i32_to_u,
|
3577
|
+
...(i > 0 ? [ [ Opcodes.i32_or ] ] : [])
|
3578
|
+
);
|
3579
|
+
}
|
3580
|
+
|
3581
|
+
out.push(Opcodes.i32_from_u);
|
3582
|
+
|
3583
|
+
return out;
|
3584
|
+
},
|
3585
|
+
type: TYPES.boolean,
|
3142
3586
|
notConstr: true
|
3143
|
-
}
|
3144
|
-
};
|
3587
|
+
},
|
3145
3588
|
|
3146
|
-
|
3147
|
-
|
3148
|
-
|
3149
|
-
|
3150
|
-
|
3151
|
-
|
3152
|
-
|
3153
|
-
|
3154
|
-
|
3155
|
-
|
3156
|
-
|
3157
|
-
|
3158
|
-
|
3589
|
+
__Porffor_fastAnd: {
|
3590
|
+
generate: (scope, decl) => {
|
3591
|
+
const out = [];
|
3592
|
+
|
3593
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3594
|
+
out.push(
|
3595
|
+
...generate(scope, decl.arguments[i]),
|
3596
|
+
Opcodes.i32_to_u,
|
3597
|
+
...(i > 0 ? [ [ Opcodes.i32_and ] ] : [])
|
3598
|
+
);
|
3599
|
+
}
|
3600
|
+
|
3601
|
+
out.push(Opcodes.i32_from_u);
|
3159
3602
|
|
3160
|
-
|
3161
|
-
|
3603
|
+
return out;
|
3604
|
+
},
|
3605
|
+
type: TYPES.boolean,
|
3606
|
+
notConstr: true
|
3607
|
+
},
|
3608
|
+
|
3609
|
+
Boolean: {
|
3610
|
+
generate: (scope, decl) => {
|
3611
|
+
// todo: boolean object when used as constructor
|
3612
|
+
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3613
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3614
|
+
},
|
3615
|
+
type: TYPES.boolean,
|
3616
|
+
length: 1
|
3617
|
+
},
|
3618
|
+
|
3619
|
+
__Math_max: {
|
3620
|
+
generate: (scope, decl) => {
|
3621
|
+
const out = [
|
3622
|
+
...number(-Infinity)
|
3623
|
+
];
|
3624
|
+
|
3625
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3626
|
+
out.push(
|
3627
|
+
...generate(scope, decl.arguments[i]),
|
3628
|
+
[ Opcodes.f64_max ]
|
3629
|
+
);
|
3630
|
+
}
|
3631
|
+
|
3632
|
+
return out;
|
3633
|
+
},
|
3634
|
+
type: TYPES.number,
|
3635
|
+
notConstr: true,
|
3636
|
+
length: 2
|
3637
|
+
},
|
3638
|
+
|
3639
|
+
__Math_min: {
|
3640
|
+
generate: (scope, decl) => {
|
3641
|
+
const out = [
|
3642
|
+
...number(Infinity)
|
3643
|
+
];
|
3644
|
+
|
3645
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3646
|
+
out.push(
|
3647
|
+
...generate(scope, decl.arguments[i]),
|
3648
|
+
[ Opcodes.f64_min ]
|
3649
|
+
);
|
3650
|
+
}
|
3651
|
+
|
3652
|
+
return out;
|
3653
|
+
},
|
3654
|
+
type: TYPES.number,
|
3655
|
+
notConstr: true,
|
3656
|
+
length: 2
|
3657
|
+
},
|
3658
|
+
|
3659
|
+
__console_log: {
|
3660
|
+
generate: (scope, decl) => {
|
3661
|
+
const out = [];
|
3662
|
+
|
3663
|
+
for (let i = 0; i < decl.arguments.length; i++) {
|
3664
|
+
out.push(
|
3665
|
+
...generateCall(scope, {
|
3666
|
+
callee: {
|
3667
|
+
type: 'Identifier',
|
3668
|
+
name: '__Porffor_print'
|
3669
|
+
},
|
3670
|
+
arguments: [ decl.arguments[i] ]
|
3671
|
+
}),
|
3672
|
+
|
3673
|
+
// print space
|
3674
|
+
...number(32),
|
3675
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3676
|
+
);
|
3677
|
+
}
|
3678
|
+
|
3679
|
+
// print newline
|
3680
|
+
out.push(
|
3681
|
+
...number(10),
|
3682
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3683
|
+
);
|
3684
|
+
|
3685
|
+
return out;
|
3686
|
+
},
|
3687
|
+
type: TYPES.undefined,
|
3688
|
+
notConstr: true,
|
3689
|
+
length: 0
|
3690
|
+
}
|
3691
|
+
};
|
3162
3692
|
|
3163
3693
|
export default program => {
|
3164
3694
|
globals = {};
|
@@ -3168,20 +3698,23 @@ export default program => {
|
|
3168
3698
|
funcs = [];
|
3169
3699
|
funcIndex = {};
|
3170
3700
|
depth = [];
|
3171
|
-
arrays = new Map();
|
3172
3701
|
pages = new Map();
|
3173
3702
|
data = [];
|
3174
3703
|
currentFuncIndex = importedFuncs.length;
|
3175
3704
|
|
3176
3705
|
globalThis.valtype = 'f64';
|
3177
3706
|
|
3178
|
-
const valtypeOpt = process.argv.find(x => x.startsWith('
|
3707
|
+
const valtypeOpt = process.argv.find(x => x.startsWith('--valtype='));
|
3179
3708
|
if (valtypeOpt) valtype = valtypeOpt.split('=')[1];
|
3180
3709
|
|
3181
3710
|
globalThis.valtypeBinary = Valtype[valtype];
|
3182
3711
|
|
3183
3712
|
const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
|
3184
3713
|
|
3714
|
+
globalThis.pageSize = PageSize;
|
3715
|
+
const pageSizeOpt = process.argv.find(x => x.startsWith('--page-size='));
|
3716
|
+
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3717
|
+
|
3185
3718
|
// set generic opcodes for current valtype
|
3186
3719
|
Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
|
3187
3720
|
Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
|
@@ -3190,10 +3723,10 @@ export default program => {
|
|
3190
3723
|
Opcodes.add = [ Opcodes.i32_add, Opcodes.i64_add, Opcodes.f64_add ][valtypeInd];
|
3191
3724
|
Opcodes.sub = [ Opcodes.i32_sub, Opcodes.i64_sub, Opcodes.f64_sub ][valtypeInd];
|
3192
3725
|
|
3193
|
-
Opcodes.i32_to = [ [
|
3194
|
-
Opcodes.i32_to_u = [ [
|
3195
|
-
Opcodes.i32_from = [ [
|
3196
|
-
Opcodes.i32_from_u = [ [
|
3726
|
+
Opcodes.i32_to = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_s ][valtypeInd];
|
3727
|
+
Opcodes.i32_to_u = [ [], [ Opcodes.i32_wrap_i64 ], Opcodes.i32_trunc_sat_f64_u ][valtypeInd];
|
3728
|
+
Opcodes.i32_from = [ [], [ Opcodes.i64_extend_i32_s ], [ Opcodes.f64_convert_i32_s ] ][valtypeInd];
|
3729
|
+
Opcodes.i32_from_u = [ [], [ Opcodes.i64_extend_i32_u ], [ Opcodes.f64_convert_i32_u ] ][valtypeInd];
|
3197
3730
|
|
3198
3731
|
Opcodes.load = [ Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load ][valtypeInd];
|
3199
3732
|
Opcodes.store = [ Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store ][valtypeInd];
|
@@ -3206,10 +3739,6 @@ export default program => {
|
|
3206
3739
|
|
3207
3740
|
program.id = { name: 'main' };
|
3208
3741
|
|
3209
|
-
globalThis.pageSize = PageSize;
|
3210
|
-
const pageSizeOpt = process.argv.find(x => x.startsWith('-page-size='));
|
3211
|
-
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3212
|
-
|
3213
3742
|
const scope = {
|
3214
3743
|
locals: {},
|
3215
3744
|
localInd: 0
|
@@ -3220,7 +3749,7 @@ export default program => {
|
|
3220
3749
|
body: program.body
|
3221
3750
|
};
|
3222
3751
|
|
3223
|
-
if (Prefs.astLog) console.log(program.body.body);
|
3752
|
+
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3224
3753
|
|
3225
3754
|
generateFunc(scope, program);
|
3226
3755
|
|
@@ -3237,7 +3766,11 @@ export default program => {
|
|
3237
3766
|
}
|
3238
3767
|
|
3239
3768
|
if (lastInst[0] === Opcodes.end || lastInst[0] === Opcodes.local_set || lastInst[0] === Opcodes.global_set) {
|
3240
|
-
|
3769
|
+
if (lastInst[0] === Opcodes.local_set && lastInst[1] === main.locals['#last_type'].idx) {
|
3770
|
+
main.wasm.splice(main.wasm.length - 1, 1);
|
3771
|
+
} else {
|
3772
|
+
main.returns = [];
|
3773
|
+
}
|
3241
3774
|
}
|
3242
3775
|
|
3243
3776
|
if (lastInst[0] === Opcodes.call) {
|