porffor 0.2.0-536e463 → 0.2.0-5ac7ea0
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/README.md +96 -49
- package/asur/README.md +2 -0
- package/asur/index.js +978 -0
- package/compiler/2c.js +321 -71
- package/compiler/builtins/base64.ts +153 -0
- package/compiler/builtins/crypto.ts +132 -0
- package/compiler/builtins/porffor.d.ts +26 -0
- package/compiler/builtins.js +590 -255
- package/compiler/codeGen.js +641 -309
- package/compiler/decompile.js +4 -4
- package/compiler/encoding.js +2 -116
- package/compiler/generated_builtins.js +25 -0
- package/compiler/index.js +22 -22
- package/compiler/log.js +4 -1
- package/compiler/opt.js +61 -26
- package/compiler/parse.js +13 -12
- package/compiler/precompile.js +129 -0
- package/compiler/prefs.js +26 -0
- package/compiler/prototype.js +177 -21
- package/compiler/sections.js +8 -7
- package/compiler/wasmSpec.js +20 -6
- package/compiler/wrap.js +112 -11
- package/package.json +1 -1
- package/porf +2 -0
- package/rhemyn/compile.js +2 -1
- package/runner/index.js +26 -3
- package/runner/profiler.js +83 -0
- package/runner/repl.js +2 -2
- package/compiler/builtins/base64.js +0 -92
- package/runner/profile.js +0 -46
- package/runner/results.json +0 -1
- package/tmp.c +0 -69
package/compiler/codeGen.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from "./wasmSpec.js";
|
2
|
-
import { ieee754_binary64, signedLEB128, unsignedLEB128 } from "./encoding.js";
|
2
|
+
import { ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector } from "./encoding.js";
|
3
3
|
import { operatorOpcode } from "./expression.js";
|
4
4
|
import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from "./builtins.js";
|
5
5
|
import { PrototypeFuncs } from "./prototype.js";
|
@@ -7,6 +7,7 @@ import { number, i32x4, enforceOneByte, enforceTwoBytes, enforceFourBytes, enfor
|
|
7
7
|
import { log } from "./log.js";
|
8
8
|
import parse from "./parse.js";
|
9
9
|
import * as Rhemyn from "../rhemyn/compile.js";
|
10
|
+
import Prefs from './prefs.js';
|
10
11
|
|
11
12
|
let globals = {};
|
12
13
|
let globalInd = 0;
|
@@ -55,7 +56,7 @@ const todo = msg => {
|
|
55
56
|
};
|
56
57
|
|
57
58
|
const isFuncType = type => type === 'FunctionDeclaration' || type === 'FunctionExpression' || type === 'ArrowFunctionExpression';
|
58
|
-
const generate = (scope, decl, global = false, name = undefined) => {
|
59
|
+
const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
|
59
60
|
switch (decl.type) {
|
60
61
|
case 'BinaryExpression':
|
61
62
|
return generateBinaryExp(scope, decl, global, name);
|
@@ -68,7 +69,12 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
68
69
|
|
69
70
|
case 'ArrowFunctionExpression':
|
70
71
|
case 'FunctionDeclaration':
|
71
|
-
generateFunc(scope, decl);
|
72
|
+
const func = generateFunc(scope, decl);
|
73
|
+
|
74
|
+
if (decl.type.endsWith('Expression')) {
|
75
|
+
return number(func.index);
|
76
|
+
}
|
77
|
+
|
72
78
|
return [];
|
73
79
|
|
74
80
|
case 'BlockStatement':
|
@@ -81,7 +87,7 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
81
87
|
return generateExp(scope, decl);
|
82
88
|
|
83
89
|
case 'CallExpression':
|
84
|
-
return generateCall(scope, decl, global, name);
|
90
|
+
return generateCall(scope, decl, global, name, valueUnused);
|
85
91
|
|
86
92
|
case 'NewExpression':
|
87
93
|
return generateNew(scope, decl, global, name);
|
@@ -99,7 +105,7 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
99
105
|
return generateUnary(scope, decl);
|
100
106
|
|
101
107
|
case 'UpdateExpression':
|
102
|
-
return generateUpdate(scope, decl);
|
108
|
+
return generateUpdate(scope, decl, global, name, valueUnused);
|
103
109
|
|
104
110
|
case 'IfStatement':
|
105
111
|
return generateIf(scope, decl);
|
@@ -155,7 +161,7 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
155
161
|
|
156
162
|
case 'TaggedTemplateExpression': {
|
157
163
|
const funcs = {
|
158
|
-
|
164
|
+
__Porffor_wasm: str => {
|
159
165
|
let out = [];
|
160
166
|
|
161
167
|
for (const line of str.split('\n')) {
|
@@ -163,8 +169,8 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
163
169
|
if (asm[0] === '') continue; // blank
|
164
170
|
|
165
171
|
if (asm[0] === 'local') {
|
166
|
-
const [ name,
|
167
|
-
scope.locals[name] = { idx:
|
172
|
+
const [ name, type ] = asm.slice(1);
|
173
|
+
scope.locals[name] = { idx: scope.localInd++, type: Valtype[type] };
|
168
174
|
continue;
|
169
175
|
}
|
170
176
|
|
@@ -174,7 +180,7 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
174
180
|
}
|
175
181
|
|
176
182
|
if (asm[0] === 'memory') {
|
177
|
-
allocPage('asm instrinsic');
|
183
|
+
allocPage(scope, 'asm instrinsic');
|
178
184
|
// todo: add to store/load offset insts
|
179
185
|
continue;
|
180
186
|
}
|
@@ -183,7 +189,11 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
183
189
|
if (!inst) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
184
190
|
|
185
191
|
if (!Array.isArray(inst)) inst = [ inst ];
|
186
|
-
const immediates = asm.slice(1).map(x =>
|
192
|
+
const immediates = asm.slice(1).map(x => {
|
193
|
+
const int = parseInt(x);
|
194
|
+
if (Number.isNaN(int)) return scope.locals[x]?.idx;
|
195
|
+
return int;
|
196
|
+
});
|
187
197
|
|
188
198
|
out.push([ ...inst, ...immediates ]);
|
189
199
|
}
|
@@ -191,29 +201,43 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
191
201
|
return out;
|
192
202
|
},
|
193
203
|
|
194
|
-
|
195
|
-
|
204
|
+
__Porffor_bs: str => [
|
205
|
+
...makeString(scope, str, undefined, undefined, true),
|
196
206
|
|
197
|
-
|
198
|
-
|
199
|
-
|
207
|
+
...number(TYPES._bytestring, Valtype.i32),
|
208
|
+
setLastType(scope)
|
209
|
+
],
|
210
|
+
__Porffor_s: str => [
|
211
|
+
...makeString(scope, str, undefined, undefined, false),
|
200
212
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
}
|
206
|
-
}
|
213
|
+
...number(TYPES.string, Valtype.i32),
|
214
|
+
setLastType(scope)
|
215
|
+
],
|
216
|
+
};
|
207
217
|
|
208
218
|
const name = decl.tag.name;
|
209
219
|
// hack for inline asm
|
210
220
|
if (!funcs[name]) return todo('tagged template expressions not implemented');
|
211
221
|
|
212
|
-
const
|
222
|
+
const { quasis, expressions } = decl.quasi;
|
223
|
+
let str = quasis[0].value.raw;
|
224
|
+
|
225
|
+
for (let i = 0; i < expressions.length; i++) {
|
226
|
+
const e = expressions[i];
|
227
|
+
str += lookupName(scope, e.name)[0].idx;
|
228
|
+
str += quasis[i + 1].value.raw;
|
229
|
+
}
|
230
|
+
|
213
231
|
return funcs[name](str);
|
214
232
|
}
|
215
233
|
|
216
234
|
default:
|
235
|
+
// ignore typescript nodes
|
236
|
+
if (decl.type.startsWith('TS') ||
|
237
|
+
decl.type === 'ImportDeclaration' && decl.importKind === 'type') {
|
238
|
+
return [];
|
239
|
+
}
|
240
|
+
|
217
241
|
return todo(`no generation for ${decl.type}!`);
|
218
242
|
}
|
219
243
|
};
|
@@ -264,25 +288,28 @@ const generateIdent = (scope, decl) => {
|
|
264
288
|
const name = mapName(rawName);
|
265
289
|
let local = scope.locals[rawName];
|
266
290
|
|
267
|
-
if (builtinVars
|
291
|
+
if (Object.hasOwn(builtinVars, name)) {
|
268
292
|
if (builtinVars[name].floatOnly && valtype[0] === 'i') throw new Error(`Cannot use ${unhackName(name)} with integer valtype`);
|
269
|
-
|
293
|
+
|
294
|
+
let wasm = builtinVars[name];
|
295
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name });
|
296
|
+
return wasm;
|
270
297
|
}
|
271
298
|
|
272
|
-
if (builtinFuncs
|
299
|
+
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
273
300
|
// todo: return an actual something
|
274
301
|
return number(1);
|
275
302
|
}
|
276
303
|
|
277
|
-
if (local === undefined) {
|
304
|
+
if (local?.idx === undefined) {
|
278
305
|
// no local var with name
|
279
|
-
if (
|
280
|
-
if (funcIndex
|
306
|
+
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
307
|
+
if (Object.hasOwn(funcIndex, name)) return number(funcIndex[name]);
|
281
308
|
|
282
|
-
if (globals
|
309
|
+
if (Object.hasOwn(globals, name)) return [ [ Opcodes.global_get, globals[name].idx ] ];
|
283
310
|
}
|
284
311
|
|
285
|
-
if (local === undefined && rawName.startsWith('__')) {
|
312
|
+
if (local?.idx === undefined && rawName.startsWith('__')) {
|
286
313
|
// return undefined if unknown key in already known var
|
287
314
|
let parent = rawName.slice(2).split('_').slice(0, -1).join('_');
|
288
315
|
if (parent.includes('_')) parent = '__' + parent;
|
@@ -291,7 +318,7 @@ const generateIdent = (scope, decl) => {
|
|
291
318
|
if (!parentLookup[1]) return number(UNDEFINED);
|
292
319
|
}
|
293
320
|
|
294
|
-
if (local === undefined) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
321
|
+
if (local?.idx === undefined) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
295
322
|
|
296
323
|
return [ [ Opcodes.local_get, local.idx ] ];
|
297
324
|
};
|
@@ -360,12 +387,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
360
387
|
...right,
|
361
388
|
// note type
|
362
389
|
...rightType,
|
363
|
-
|
390
|
+
setLastType(scope),
|
364
391
|
[ Opcodes.else ],
|
365
392
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
366
393
|
// note type
|
367
394
|
...leftType,
|
368
|
-
|
395
|
+
setLastType(scope),
|
369
396
|
[ Opcodes.end ],
|
370
397
|
Opcodes.i32_from
|
371
398
|
];
|
@@ -379,12 +406,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
379
406
|
...right,
|
380
407
|
// note type
|
381
408
|
...rightType,
|
382
|
-
|
409
|
+
setLastType(scope),
|
383
410
|
[ Opcodes.else ],
|
384
411
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
385
412
|
// note type
|
386
413
|
...leftType,
|
387
|
-
|
414
|
+
setLastType(scope),
|
388
415
|
[ Opcodes.end ]
|
389
416
|
];
|
390
417
|
};
|
@@ -399,9 +426,6 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
399
426
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
400
427
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
401
428
|
|
402
|
-
const aotWFA = process.argv.includes('-aot-well-formed-string-approximation');
|
403
|
-
if (aotWFA) addVarMeta(name, { wellFormed: undefined });
|
404
|
-
|
405
429
|
if (assign) {
|
406
430
|
const pointer = arrays.get(name ?? '$undeclared');
|
407
431
|
|
@@ -639,11 +663,12 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
639
663
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
640
664
|
];
|
641
665
|
|
642
|
-
const
|
666
|
+
const useTmp = knownType(scope, type) == null;
|
667
|
+
const tmp = useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
643
668
|
|
644
669
|
const def = [
|
645
670
|
// if value != 0
|
646
|
-
[ Opcodes.local_get, tmp ],
|
671
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
647
672
|
|
648
673
|
// ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
649
674
|
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
|
@@ -655,7 +680,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
655
680
|
|
656
681
|
return [
|
657
682
|
...wasm,
|
658
|
-
[ Opcodes.local_set, tmp ],
|
683
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
659
684
|
|
660
685
|
...typeSwitch(scope, type, {
|
661
686
|
// [TYPES.number]: def,
|
@@ -664,7 +689,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
664
689
|
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
665
690
|
],
|
666
691
|
[TYPES.string]: [
|
667
|
-
[ Opcodes.local_get, tmp ],
|
692
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
668
693
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
669
694
|
|
670
695
|
// get length
|
@@ -675,16 +700,27 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
675
700
|
[ Opcodes.i32_eqz ], */
|
676
701
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
677
702
|
],
|
703
|
+
[TYPES._bytestring]: [ // duplicate of string
|
704
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
705
|
+
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
706
|
+
|
707
|
+
// get length
|
708
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
709
|
+
|
710
|
+
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
711
|
+
],
|
678
712
|
default: def
|
679
713
|
}, intOut ? Valtype.i32 : valtypeBinary)
|
680
714
|
];
|
681
715
|
};
|
682
716
|
|
683
717
|
const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
684
|
-
const
|
718
|
+
const useTmp = knownType(scope, type) == null;
|
719
|
+
const tmp = useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
720
|
+
|
685
721
|
return [
|
686
722
|
...wasm,
|
687
|
-
[ Opcodes.local_set, tmp ],
|
723
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
688
724
|
|
689
725
|
...typeSwitch(scope, type, {
|
690
726
|
[TYPES._array]: [
|
@@ -692,7 +728,18 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
692
728
|
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
693
729
|
],
|
694
730
|
[TYPES.string]: [
|
695
|
-
[ Opcodes.local_get, tmp ],
|
731
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
732
|
+
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
733
|
+
|
734
|
+
// get length
|
735
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
736
|
+
|
737
|
+
// if length == 0
|
738
|
+
[ Opcodes.i32_eqz ],
|
739
|
+
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
740
|
+
],
|
741
|
+
[TYPES._bytestring]: [ // duplicate of string
|
742
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
696
743
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
697
744
|
|
698
745
|
// get length
|
@@ -704,7 +751,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
704
751
|
],
|
705
752
|
default: [
|
706
753
|
// if value == 0
|
707
|
-
[ Opcodes.local_get, tmp ],
|
754
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
708
755
|
|
709
756
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
710
757
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -714,10 +761,12 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
714
761
|
};
|
715
762
|
|
716
763
|
const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
717
|
-
const
|
764
|
+
const useTmp = knownType(scope, type) == null;
|
765
|
+
const tmp = useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
766
|
+
|
718
767
|
return [
|
719
768
|
...wasm,
|
720
|
-
[ Opcodes.local_set, tmp ],
|
769
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
721
770
|
|
722
771
|
...typeSwitch(scope, type, {
|
723
772
|
[TYPES.undefined]: [
|
@@ -726,7 +775,7 @@ const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
726
775
|
],
|
727
776
|
[TYPES.object]: [
|
728
777
|
// object, null if == 0
|
729
|
-
[ Opcodes.local_get, tmp ],
|
778
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
730
779
|
|
731
780
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
732
781
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -755,11 +804,14 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
755
804
|
return performLogicOp(scope, op, left, right, leftType, rightType);
|
756
805
|
}
|
757
806
|
|
807
|
+
const knownLeft = knownType(scope, leftType);
|
808
|
+
const knownRight = knownType(scope, rightType);
|
809
|
+
|
758
810
|
const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
|
759
811
|
const strictOp = op === '===' || op === '!==';
|
760
812
|
|
761
813
|
const startOut = [], endOut = [];
|
762
|
-
const
|
814
|
+
const finalize = out => startOut.concat(out, endOut);
|
763
815
|
|
764
816
|
// if strict (in)equal check types match
|
765
817
|
if (strictOp) {
|
@@ -804,31 +856,32 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
804
856
|
// todo: if equality op and an operand is undefined, return false
|
805
857
|
// todo: niche null hell with 0
|
806
858
|
|
807
|
-
//
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
859
|
+
// todo: this should be dynamic but for now only static
|
860
|
+
if (knownLeft === TYPES.string || knownRight === TYPES.string) {
|
861
|
+
if (op === '+') {
|
862
|
+
// string concat (a + b)
|
863
|
+
return concatStrings(scope, left, right, _global, _name, assign);
|
864
|
+
}
|
865
|
+
|
866
|
+
// not an equality op, NaN
|
867
|
+
if (!eqOp) return number(NaN);
|
868
|
+
|
869
|
+
// else leave bool ops
|
870
|
+
// todo: convert string to number if string and number/bool
|
871
|
+
// todo: string (>|>=|<|<=) string
|
872
|
+
|
873
|
+
// string comparison
|
874
|
+
if (op === '===' || op === '==') {
|
875
|
+
return compareStrings(scope, left, right);
|
876
|
+
}
|
877
|
+
|
878
|
+
if (op === '!==' || op === '!=') {
|
879
|
+
return [
|
880
|
+
...compareStrings(scope, left, right),
|
881
|
+
[ Opcodes.i32_eqz ]
|
882
|
+
];
|
883
|
+
}
|
884
|
+
}
|
832
885
|
|
833
886
|
let ops = operatorOpcode[valtype][op];
|
834
887
|
|
@@ -838,7 +891,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
838
891
|
includeBuiltin(scope, builtinName);
|
839
892
|
const idx = funcIndex[builtinName];
|
840
893
|
|
841
|
-
return
|
894
|
+
return finalize([
|
842
895
|
...left,
|
843
896
|
...right,
|
844
897
|
[ Opcodes.call, idx ]
|
@@ -852,7 +905,13 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
852
905
|
|
853
906
|
let tmpLeft, tmpRight;
|
854
907
|
// if equal op, check if strings for compareStrings
|
855
|
-
if (op === '===' || op === '==' || op === '!==' || op === '!=') {
|
908
|
+
if (op === '===' || op === '==' || op === '!==' || op === '!=') (() => {
|
909
|
+
// todo: intelligent partial skip later
|
910
|
+
// if neither known are string, stop this madness
|
911
|
+
if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
|
912
|
+
return;
|
913
|
+
}
|
914
|
+
|
856
915
|
tmpLeft = localTmp(scope, '__tmpop_left');
|
857
916
|
tmpRight = localTmp(scope, '__tmpop_right');
|
858
917
|
|
@@ -886,7 +945,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
886
945
|
[ Opcodes.i32_or ],
|
887
946
|
[ Opcodes.if, Blocktype.void ],
|
888
947
|
...number(0, Valtype.i32),
|
889
|
-
[ Opcodes.br,
|
948
|
+
[ Opcodes.br, 2 ],
|
890
949
|
[ Opcodes.end ],
|
891
950
|
|
892
951
|
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
@@ -902,9 +961,9 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
902
961
|
// endOut.push(stringOnly([ Opcodes.end ]));
|
903
962
|
endOut.unshift(stringOnly([ Opcodes.end ]));
|
904
963
|
// }
|
905
|
-
}
|
964
|
+
})();
|
906
965
|
|
907
|
-
return
|
966
|
+
return finalize([
|
908
967
|
...left,
|
909
968
|
...(tmpLeft != null ? stringOnly([ [ Opcodes.local_tee, tmpLeft ] ]) : []),
|
910
969
|
...right,
|
@@ -921,7 +980,22 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
921
980
|
return out;
|
922
981
|
};
|
923
982
|
|
924
|
-
const
|
983
|
+
const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals = [], returns = [], localInd = 0 }) => {
|
984
|
+
return func({ name, params, locals, returns, localInd }, {
|
985
|
+
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage,
|
986
|
+
builtin: name => {
|
987
|
+
let idx = funcIndex[name] ?? importedFuncs[name];
|
988
|
+
if (idx === undefined && builtinFuncs[name]) {
|
989
|
+
includeBuiltin(null, name);
|
990
|
+
idx = funcIndex[name];
|
991
|
+
}
|
992
|
+
|
993
|
+
return idx;
|
994
|
+
}
|
995
|
+
});
|
996
|
+
};
|
997
|
+
|
998
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
925
999
|
const existing = funcs.find(x => x.name === name);
|
926
1000
|
if (existing) return existing;
|
927
1001
|
|
@@ -933,6 +1007,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
933
1007
|
locals[nameParam(i)] = { idx: i, type: allLocals[i] };
|
934
1008
|
}
|
935
1009
|
|
1010
|
+
for (const x of _data) {
|
1011
|
+
const copy = { ...x };
|
1012
|
+
copy.offset += pages.size * pageSize;
|
1013
|
+
data.push(copy);
|
1014
|
+
}
|
1015
|
+
|
1016
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name, params, locals, returns, localInd: allLocals.length });
|
1017
|
+
|
936
1018
|
let baseGlobalIdx, i = 0;
|
937
1019
|
for (const type of globalTypes) {
|
938
1020
|
if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
|
@@ -1013,7 +1095,8 @@ const TYPES = {
|
|
1013
1095
|
|
1014
1096
|
// these are not "typeof" types but tracked internally
|
1015
1097
|
_array: 0x10,
|
1016
|
-
_regexp: 0x11
|
1098
|
+
_regexp: 0x11,
|
1099
|
+
_bytestring: 0x12
|
1017
1100
|
};
|
1018
1101
|
|
1019
1102
|
const TYPE_NAMES = {
|
@@ -1027,13 +1110,17 @@ const TYPE_NAMES = {
|
|
1027
1110
|
[TYPES.bigint]: 'BigInt',
|
1028
1111
|
|
1029
1112
|
[TYPES._array]: 'Array',
|
1030
|
-
[TYPES._regexp]: 'RegExp'
|
1113
|
+
[TYPES._regexp]: 'RegExp',
|
1114
|
+
[TYPES._bytestring]: 'ByteString'
|
1031
1115
|
};
|
1032
1116
|
|
1033
1117
|
const getType = (scope, _name) => {
|
1034
1118
|
const name = mapName(_name);
|
1035
1119
|
|
1120
|
+
if (typedInput && scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
|
1036
1121
|
if (scope.locals[name]) return [ [ Opcodes.local_get, scope.locals[name + '#type'].idx ] ];
|
1122
|
+
|
1123
|
+
if (typedInput && globals[name]?.metadata?.type != null) return number(globals[name].metadata.type, Valtype.i32);
|
1037
1124
|
if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
|
1038
1125
|
|
1039
1126
|
let type = TYPES.undefined;
|
@@ -1051,11 +1138,13 @@ const setType = (scope, _name, type) => {
|
|
1051
1138
|
|
1052
1139
|
const out = typeof type === 'number' ? number(type, Valtype.i32) : type;
|
1053
1140
|
|
1141
|
+
if (typedInput && scope.locals[name]?.metadata?.type != null) return [];
|
1054
1142
|
if (scope.locals[name]) return [
|
1055
1143
|
...out,
|
1056
1144
|
[ Opcodes.local_set, scope.locals[name + '#type'].idx ]
|
1057
1145
|
];
|
1058
1146
|
|
1147
|
+
if (typedInput && globals[name]?.metadata?.type != null) return [];
|
1059
1148
|
if (globals[name]) return [
|
1060
1149
|
...out,
|
1061
1150
|
[ Opcodes.global_set, globals[name + '#type'].idx ]
|
@@ -1064,11 +1153,22 @@ const setType = (scope, _name, type) => {
|
|
1064
1153
|
// throw new Error('could not find var');
|
1065
1154
|
};
|
1066
1155
|
|
1156
|
+
const getLastType = scope => {
|
1157
|
+
scope.gotLastType = true;
|
1158
|
+
return [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ];
|
1159
|
+
};
|
1160
|
+
|
1161
|
+
const setLastType = scope => {
|
1162
|
+
return [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ];
|
1163
|
+
};
|
1164
|
+
|
1067
1165
|
const getNodeType = (scope, node) => {
|
1068
1166
|
const inner = () => {
|
1069
1167
|
if (node.type === 'Literal') {
|
1070
1168
|
if (node.regex) return TYPES._regexp;
|
1071
1169
|
|
1170
|
+
if (typeof node.value === 'string' && byteStringable(node.value)) return TYPES._bytestring;
|
1171
|
+
|
1072
1172
|
return TYPES[typeof node.value];
|
1073
1173
|
}
|
1074
1174
|
|
@@ -1082,6 +1182,15 @@ const getNodeType = (scope, node) => {
|
|
1082
1182
|
|
1083
1183
|
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
1084
1184
|
const name = node.callee.name;
|
1185
|
+
if (!name) {
|
1186
|
+
// iife
|
1187
|
+
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1188
|
+
|
1189
|
+
// presume
|
1190
|
+
// todo: warn here?
|
1191
|
+
return TYPES.number;
|
1192
|
+
}
|
1193
|
+
|
1085
1194
|
const func = funcs.find(x => x.name === name);
|
1086
1195
|
|
1087
1196
|
if (func) {
|
@@ -1089,10 +1198,27 @@ const getNodeType = (scope, node) => {
|
|
1089
1198
|
if (func.returnType) return func.returnType;
|
1090
1199
|
}
|
1091
1200
|
|
1092
|
-
if (builtinFuncs[name]) return TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1201
|
+
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1093
1202
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1094
1203
|
|
1095
|
-
|
1204
|
+
// check if this is a prototype function
|
1205
|
+
// if so and there is only one impl (eg charCodeAt)
|
1206
|
+
// use that return type as that is the only possibility
|
1207
|
+
// (if non-matching type it would error out)
|
1208
|
+
if (name.startsWith('__')) {
|
1209
|
+
const spl = name.slice(2).split('_');
|
1210
|
+
|
1211
|
+
const func = spl[spl.length - 1];
|
1212
|
+
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES._bytestring && prototypeFuncs[x][func] != null);
|
1213
|
+
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1214
|
+
}
|
1215
|
+
|
1216
|
+
if (name.startsWith('__Porffor_wasm_')) {
|
1217
|
+
// todo: return undefined for non-returning ops
|
1218
|
+
return TYPES.number;
|
1219
|
+
}
|
1220
|
+
|
1221
|
+
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1096
1222
|
|
1097
1223
|
// presume
|
1098
1224
|
// todo: warn here?
|
@@ -1140,6 +1266,14 @@ const getNodeType = (scope, node) => {
|
|
1140
1266
|
|
1141
1267
|
if (node.type === 'BinaryExpression') {
|
1142
1268
|
if (['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(node.operator)) return TYPES.boolean;
|
1269
|
+
if (node.operator !== '+') return TYPES.number;
|
1270
|
+
|
1271
|
+
const knownLeft = knownType(scope, getNodeType(scope, node.left));
|
1272
|
+
const knownRight = knownType(scope, getNodeType(scope, node.right));
|
1273
|
+
|
1274
|
+
// todo: this should be dynamic but for now only static
|
1275
|
+
if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
|
1276
|
+
|
1143
1277
|
return TYPES.number;
|
1144
1278
|
|
1145
1279
|
// todo: string concat types
|
@@ -1164,7 +1298,7 @@ const getNodeType = (scope, node) => {
|
|
1164
1298
|
if (node.operator === '!') return TYPES.boolean;
|
1165
1299
|
if (node.operator === 'void') return TYPES.undefined;
|
1166
1300
|
if (node.operator === 'delete') return TYPES.boolean;
|
1167
|
-
if (node.operator === 'typeof') return TYPES.string;
|
1301
|
+
if (node.operator === 'typeof') return Prefs.bytestring ? TYPES._bytestring : TYPES.string;
|
1168
1302
|
|
1169
1303
|
return TYPES.number;
|
1170
1304
|
}
|
@@ -1173,11 +1307,17 @@ const getNodeType = (scope, node) => {
|
|
1173
1307
|
// hack: if something.length, number type
|
1174
1308
|
if (node.property.name === 'length') return TYPES.number;
|
1175
1309
|
|
1176
|
-
//
|
1310
|
+
// ts hack
|
1311
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES.string) return TYPES.string;
|
1312
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES._array) return TYPES.number;
|
1313
|
+
|
1314
|
+
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1315
|
+
|
1316
|
+
// presume
|
1177
1317
|
return TYPES.number;
|
1178
1318
|
}
|
1179
1319
|
|
1180
|
-
if (scope.locals['#last_type']) return [
|
1320
|
+
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1181
1321
|
|
1182
1322
|
// presume
|
1183
1323
|
// todo: warn here?
|
@@ -1193,8 +1333,8 @@ const getNodeType = (scope, node) => {
|
|
1193
1333
|
const generateLiteral = (scope, decl, global, name) => {
|
1194
1334
|
if (decl.value === null) return number(NULL);
|
1195
1335
|
|
1336
|
+
// hack: just return 1 for regex literals
|
1196
1337
|
if (decl.regex) {
|
1197
|
-
scope.regex[name] = decl.regex;
|
1198
1338
|
return number(1);
|
1199
1339
|
}
|
1200
1340
|
|
@@ -1207,16 +1347,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1207
1347
|
return number(decl.value ? 1 : 0);
|
1208
1348
|
|
1209
1349
|
case 'string':
|
1210
|
-
|
1211
|
-
const rawElements = new Array(str.length);
|
1212
|
-
let j = 0;
|
1213
|
-
for (let i = 0; i < str.length; i++) {
|
1214
|
-
rawElements[i] = str.charCodeAt(i);
|
1215
|
-
}
|
1216
|
-
|
1217
|
-
return makeArray(scope, {
|
1218
|
-
rawElements
|
1219
|
-
}, global, name, false, 'i16')[0];
|
1350
|
+
return makeString(scope, decl.value, global, name);
|
1220
1351
|
|
1221
1352
|
default:
|
1222
1353
|
return todo(`cannot generate literal of type ${typeof decl.value}`);
|
@@ -1236,10 +1367,10 @@ const countLeftover = wasm => {
|
|
1236
1367
|
if (inst[0] === Opcodes.end) depth--;
|
1237
1368
|
|
1238
1369
|
if (depth === 0)
|
1239
|
-
if ([Opcodes.throw,Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1240
|
-
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.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x0a)) {}
|
1370
|
+
if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1371
|
+
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)) {}
|
1241
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++;
|
1242
|
-
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16].includes(inst[0])) count -= 2;
|
1373
|
+
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1243
1374
|
else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
|
1244
1375
|
else if (inst[0] === Opcodes.return) count = 0;
|
1245
1376
|
else if (inst[0] === Opcodes.call) {
|
@@ -1265,7 +1396,7 @@ const disposeLeftover = wasm => {
|
|
1265
1396
|
const generateExp = (scope, decl) => {
|
1266
1397
|
const expression = decl.expression;
|
1267
1398
|
|
1268
|
-
const out = generate(scope, expression);
|
1399
|
+
const out = generate(scope, expression, undefined, undefined, true);
|
1269
1400
|
disposeLeftover(out);
|
1270
1401
|
|
1271
1402
|
return out;
|
@@ -1323,7 +1454,7 @@ const RTArrayUtil = {
|
|
1323
1454
|
]
|
1324
1455
|
};
|
1325
1456
|
|
1326
|
-
const generateCall = (scope, decl, _global, _name) => {
|
1457
|
+
const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
1327
1458
|
/* const callee = decl.callee;
|
1328
1459
|
const args = decl.arguments;
|
1329
1460
|
|
@@ -1356,13 +1487,13 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1356
1487
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1357
1488
|
out.push(
|
1358
1489
|
...getNodeType(scope, finalStatement),
|
1359
|
-
|
1490
|
+
setLastType(scope)
|
1360
1491
|
);
|
1361
1492
|
} else if (countLeftover(out) === 0) {
|
1362
1493
|
out.push(...number(UNDEFINED));
|
1363
1494
|
out.push(
|
1364
1495
|
...number(TYPES.undefined, Valtype.i32),
|
1365
|
-
|
1496
|
+
setLastType(scope)
|
1366
1497
|
);
|
1367
1498
|
}
|
1368
1499
|
|
@@ -1380,8 +1511,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1380
1511
|
if (name && name.startsWith('__')) {
|
1381
1512
|
const spl = name.slice(2).split('_');
|
1382
1513
|
|
1383
|
-
|
1384
|
-
protoName = func;
|
1514
|
+
protoName = spl[spl.length - 1];
|
1385
1515
|
|
1386
1516
|
target = { ...decl.callee };
|
1387
1517
|
target.name = spl.slice(0, -1).join('_');
|
@@ -1390,8 +1520,8 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1390
1520
|
// literal.func()
|
1391
1521
|
if (!name && decl.callee.type === 'MemberExpression') {
|
1392
1522
|
// megahack for /regex/.func()
|
1393
|
-
|
1394
|
-
|
1523
|
+
const funcName = decl.callee.property.name;
|
1524
|
+
if (decl.callee.object.regex && Object.hasOwn(Rhemyn, funcName)) {
|
1395
1525
|
const func = Rhemyn[funcName](decl.callee.object.regex.pattern, currentFuncIndex++);
|
1396
1526
|
|
1397
1527
|
funcIndex[func.name] = func.index;
|
@@ -1407,12 +1537,11 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1407
1537
|
Opcodes.i32_from_u,
|
1408
1538
|
|
1409
1539
|
...number(TYPES.boolean, Valtype.i32),
|
1410
|
-
|
1540
|
+
setLastType(scope)
|
1411
1541
|
];
|
1412
1542
|
}
|
1413
1543
|
|
1414
|
-
|
1415
|
-
protoName = func;
|
1544
|
+
protoName = decl.callee.property.name;
|
1416
1545
|
|
1417
1546
|
target = decl.callee.object;
|
1418
1547
|
}
|
@@ -1434,8 +1563,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1434
1563
|
|
1435
1564
|
if (protoName) {
|
1436
1565
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1437
|
-
|
1438
|
-
if (f) acc[x] = f;
|
1566
|
+
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1439
1567
|
return acc;
|
1440
1568
|
}, {});
|
1441
1569
|
|
@@ -1444,10 +1572,18 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1444
1572
|
// use local for cached i32 length as commonly used
|
1445
1573
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
1446
1574
|
const pointerLocal = localTmp(scope, '__proto_pointer_cache', Valtype.i32);
|
1447
|
-
const getPointer = [ [ Opcodes.local_get, pointerLocal ] ];
|
1448
1575
|
|
1449
1576
|
// TODO: long-term, prototypes should be their individual separate funcs
|
1450
1577
|
|
1578
|
+
const rawPointer = [
|
1579
|
+
...generate(scope, target),
|
1580
|
+
Opcodes.i32_to_u
|
1581
|
+
];
|
1582
|
+
|
1583
|
+
const usePointerCache = !Object.values(protoCands).every(x => x.noPointerCache === true);
|
1584
|
+
const getPointer = usePointerCache ? [ [ Opcodes.local_get, pointerLocal ] ] : rawPointer;
|
1585
|
+
|
1586
|
+
let allOptUnused = true;
|
1451
1587
|
let lengthI32CacheUsed = false;
|
1452
1588
|
const protoBC = {};
|
1453
1589
|
for (const x in protoCands) {
|
@@ -1457,7 +1593,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1457
1593
|
...RTArrayUtil.getLength(getPointer),
|
1458
1594
|
|
1459
1595
|
...number(TYPES.number, Valtype.i32),
|
1460
|
-
|
1596
|
+
setLastType(scope)
|
1461
1597
|
];
|
1462
1598
|
continue;
|
1463
1599
|
}
|
@@ -1467,6 +1603,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1467
1603
|
const protoLocal = protoFunc.local ? localTmp(scope, `__${protoName}_tmp`, protoFunc.local) : -1;
|
1468
1604
|
const protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${protoName}_tmp2`, protoFunc.local2) : -1;
|
1469
1605
|
|
1606
|
+
let optUnused = false;
|
1470
1607
|
const protoOut = protoFunc(getPointer, {
|
1471
1608
|
getCachedI32: () => {
|
1472
1609
|
lengthI32CacheUsed = true;
|
@@ -1481,23 +1618,30 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1481
1618
|
return makeArray(scope, {
|
1482
1619
|
rawElements: new Array(length)
|
1483
1620
|
}, _global, _name, true, itemType);
|
1621
|
+
}, () => {
|
1622
|
+
optUnused = true;
|
1623
|
+
return unusedValue;
|
1484
1624
|
});
|
1485
1625
|
|
1626
|
+
if (!optUnused) allOptUnused = false;
|
1627
|
+
|
1486
1628
|
protoBC[x] = [
|
1487
|
-
[ Opcodes.block, valtypeBinary ],
|
1629
|
+
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1488
1630
|
...protoOut,
|
1489
1631
|
|
1490
1632
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1491
|
-
|
1633
|
+
setLastType(scope),
|
1492
1634
|
[ Opcodes.end ]
|
1493
1635
|
];
|
1494
1636
|
}
|
1495
1637
|
|
1496
|
-
|
1497
|
-
...generate(scope, target),
|
1638
|
+
// todo: if some cands use optUnused and some don't, we will probably crash
|
1498
1639
|
|
1499
|
-
|
1500
|
-
|
1640
|
+
return [
|
1641
|
+
...(usePointerCache ? [
|
1642
|
+
...rawPointer,
|
1643
|
+
[ Opcodes.local_set, pointerLocal ],
|
1644
|
+
] : []),
|
1501
1645
|
|
1502
1646
|
...(!lengthI32CacheUsed ? [] : [
|
1503
1647
|
...RTArrayUtil.getLengthI32(getPointer),
|
@@ -1509,7 +1653,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1509
1653
|
|
1510
1654
|
// TODO: error better
|
1511
1655
|
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1512
|
-
}, valtypeBinary),
|
1656
|
+
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1513
1657
|
];
|
1514
1658
|
}
|
1515
1659
|
}
|
@@ -1548,6 +1692,32 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1548
1692
|
idx = -1;
|
1549
1693
|
}
|
1550
1694
|
|
1695
|
+
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1696
|
+
const wasmOps = {
|
1697
|
+
// pointer, align, offset
|
1698
|
+
i32_load8_u: { imms: 2, args: 1 },
|
1699
|
+
// pointer, value, align, offset
|
1700
|
+
i32_store8: { imms: 2, args: 2 },
|
1701
|
+
};
|
1702
|
+
|
1703
|
+
const opName = name.slice('__Porffor_wasm_'.length);
|
1704
|
+
|
1705
|
+
if (wasmOps[opName]) {
|
1706
|
+
const op = wasmOps[opName];
|
1707
|
+
|
1708
|
+
const argOut = [];
|
1709
|
+
for (let i = 0; i < op.args; i++) argOut.push(...generate(scope, decl.arguments[i]));
|
1710
|
+
|
1711
|
+
// literals only
|
1712
|
+
const imms = decl.arguments.slice(op.args).map(x => x.value);
|
1713
|
+
|
1714
|
+
return [
|
1715
|
+
...argOut,
|
1716
|
+
[ Opcodes[opName], ...imms ]
|
1717
|
+
];
|
1718
|
+
}
|
1719
|
+
}
|
1720
|
+
|
1551
1721
|
if (idx === undefined) {
|
1552
1722
|
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`);
|
1553
1723
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`);
|
@@ -1556,7 +1726,9 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1556
1726
|
const func = funcs.find(x => x.index === idx);
|
1557
1727
|
|
1558
1728
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1559
|
-
const
|
1729
|
+
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1730
|
+
const typedReturns = userFunc || builtinFuncs[name]?.typedReturns;
|
1731
|
+
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1560
1732
|
|
1561
1733
|
let args = decl.arguments;
|
1562
1734
|
if (func && args.length < paramCount) {
|
@@ -1572,14 +1744,20 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1572
1744
|
if (func && func.throws) scope.throws = true;
|
1573
1745
|
|
1574
1746
|
let out = [];
|
1575
|
-
for (
|
1747
|
+
for (let i = 0; i < args.length; i++) {
|
1748
|
+
const arg = args[i];
|
1576
1749
|
out = out.concat(generate(scope, arg));
|
1577
|
-
|
1750
|
+
|
1751
|
+
if (builtinFuncs[name] && builtinFuncs[name].params[i] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1752
|
+
out.push(Opcodes.i32_to);
|
1753
|
+
}
|
1754
|
+
|
1755
|
+
if (typedParams) out = out.concat(getNodeType(scope, arg));
|
1578
1756
|
}
|
1579
1757
|
|
1580
1758
|
out.push([ Opcodes.call, idx ]);
|
1581
1759
|
|
1582
|
-
if (!
|
1760
|
+
if (!typedReturns) {
|
1583
1761
|
// let type;
|
1584
1762
|
// if (builtinFuncs[name]) type = TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1585
1763
|
// if (internalConstrs[name]) type = internalConstrs[name].type;
|
@@ -1589,7 +1767,11 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1589
1767
|
// ...number(type, Valtype.i32),
|
1590
1768
|
// [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1591
1769
|
// );
|
1592
|
-
} else out.push(
|
1770
|
+
} else out.push(setLastType(scope));
|
1771
|
+
|
1772
|
+
if (builtinFuncs[name] && builtinFuncs[name].returns[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1773
|
+
out.push(Opcodes.i32_from);
|
1774
|
+
}
|
1593
1775
|
|
1594
1776
|
return out;
|
1595
1777
|
};
|
@@ -1614,9 +1796,118 @@ const unhackName = name => {
|
|
1614
1796
|
return name;
|
1615
1797
|
};
|
1616
1798
|
|
1799
|
+
const knownType = (scope, type) => {
|
1800
|
+
if (type.length === 1 && type[0][0] === Opcodes.i32_const) {
|
1801
|
+
return type[0][1];
|
1802
|
+
}
|
1803
|
+
|
1804
|
+
if (typedInput && type.length === 1 && type[0][0] === Opcodes.local_get) {
|
1805
|
+
const idx = type[0][1];
|
1806
|
+
|
1807
|
+
// type idx = var idx + 1
|
1808
|
+
const v = Object.values(scope.locals).find(x => x.idx === idx - 1);
|
1809
|
+
if (v.metadata?.type != null) return v.metadata.type;
|
1810
|
+
}
|
1811
|
+
|
1812
|
+
return null;
|
1813
|
+
};
|
1814
|
+
|
1815
|
+
const brTable = (input, bc, returns) => {
|
1816
|
+
const out = [];
|
1817
|
+
const keys = Object.keys(bc);
|
1818
|
+
const count = keys.length;
|
1819
|
+
|
1820
|
+
if (count === 1) {
|
1821
|
+
// return [
|
1822
|
+
// ...input,
|
1823
|
+
// ...bc[keys[0]]
|
1824
|
+
// ];
|
1825
|
+
return bc[keys[0]];
|
1826
|
+
}
|
1827
|
+
|
1828
|
+
if (count === 2) {
|
1829
|
+
// just use if else
|
1830
|
+
const other = keys.find(x => x !== 'default');
|
1831
|
+
return [
|
1832
|
+
...input,
|
1833
|
+
...number(other, Valtype.i32),
|
1834
|
+
[ Opcodes.i32_eq ],
|
1835
|
+
[ Opcodes.if, returns ],
|
1836
|
+
...bc[other],
|
1837
|
+
[ Opcodes.else ],
|
1838
|
+
...bc.default,
|
1839
|
+
[ Opcodes.end ]
|
1840
|
+
];
|
1841
|
+
}
|
1842
|
+
|
1843
|
+
for (let i = 0; i < count; i++) {
|
1844
|
+
if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
1845
|
+
else out.push([ Opcodes.block, Blocktype.void ]);
|
1846
|
+
}
|
1847
|
+
|
1848
|
+
const nums = keys.filter(x => +x);
|
1849
|
+
const offset = Math.min(...nums);
|
1850
|
+
const max = Math.max(...nums);
|
1851
|
+
|
1852
|
+
const table = [];
|
1853
|
+
let br = 1;
|
1854
|
+
|
1855
|
+
for (let i = offset; i <= max; i++) {
|
1856
|
+
// if branch for this num, go to that block
|
1857
|
+
if (bc[i]) {
|
1858
|
+
table.push(br);
|
1859
|
+
br++;
|
1860
|
+
continue;
|
1861
|
+
}
|
1862
|
+
|
1863
|
+
// else default
|
1864
|
+
table.push(0);
|
1865
|
+
}
|
1866
|
+
|
1867
|
+
out.push(
|
1868
|
+
[ Opcodes.block, Blocktype.void ],
|
1869
|
+
...input,
|
1870
|
+
...(offset > 0 ? [
|
1871
|
+
...number(offset, Valtype.i32),
|
1872
|
+
[ Opcodes.i32_sub ]
|
1873
|
+
] : []),
|
1874
|
+
[ Opcodes.br_table, ...encodeVector(table), 0 ]
|
1875
|
+
);
|
1876
|
+
|
1877
|
+
// if you can guess why we sort the wrong way and then reverse
|
1878
|
+
// (instead of just sorting the correct way)
|
1879
|
+
// dm me and if you are correct and the first person
|
1880
|
+
// I will somehow shout you out or something
|
1881
|
+
const orderedBc = keys.sort((a, b) => b - a).reverse();
|
1882
|
+
|
1883
|
+
br = count - 1;
|
1884
|
+
for (const x of orderedBc) {
|
1885
|
+
out.push(
|
1886
|
+
[ Opcodes.end ],
|
1887
|
+
...bc[x],
|
1888
|
+
...(br === 0 ? [] : [ [ Opcodes.br, br ] ])
|
1889
|
+
);
|
1890
|
+
br--;
|
1891
|
+
}
|
1892
|
+
|
1893
|
+
return [
|
1894
|
+
...out,
|
1895
|
+
[ Opcodes.end, 'br table end' ]
|
1896
|
+
];
|
1897
|
+
};
|
1898
|
+
|
1617
1899
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
1618
|
-
|
1900
|
+
if (!Prefs.bytestring) delete bc[TYPES._bytestring];
|
1619
1901
|
|
1902
|
+
const known = knownType(scope, type);
|
1903
|
+
if (known != null) {
|
1904
|
+
return bc[known] ?? bc.default;
|
1905
|
+
}
|
1906
|
+
|
1907
|
+
if (Prefs.typeswitchUseBrtable)
|
1908
|
+
return brTable(type, bc, returns);
|
1909
|
+
|
1910
|
+
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
1620
1911
|
const out = [
|
1621
1912
|
...type,
|
1622
1913
|
[ Opcodes.local_set, tmp ],
|
@@ -1648,7 +1939,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1648
1939
|
return out;
|
1649
1940
|
};
|
1650
1941
|
|
1651
|
-
const allocVar = (scope, name, global = false) => {
|
1942
|
+
const allocVar = (scope, name, global = false, type = true) => {
|
1652
1943
|
const target = global ? globals : scope.locals;
|
1653
1944
|
|
1654
1945
|
// already declared
|
@@ -1662,12 +1953,60 @@ const allocVar = (scope, name, global = false) => {
|
|
1662
1953
|
let idx = global ? globalInd++ : scope.localInd++;
|
1663
1954
|
target[name] = { idx, type: valtypeBinary };
|
1664
1955
|
|
1665
|
-
|
1666
|
-
|
1956
|
+
if (type) {
|
1957
|
+
let typeIdx = global ? globalInd++ : scope.localInd++;
|
1958
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
1959
|
+
}
|
1667
1960
|
|
1668
1961
|
return idx;
|
1669
1962
|
};
|
1670
1963
|
|
1964
|
+
const addVarMetadata = (scope, name, global = false, metadata = {}) => {
|
1965
|
+
const target = global ? globals : scope.locals;
|
1966
|
+
|
1967
|
+
target[name].metadata ??= {};
|
1968
|
+
for (const x in metadata) {
|
1969
|
+
if (metadata[x] != null) target[name].metadata[x] = metadata[x];
|
1970
|
+
}
|
1971
|
+
};
|
1972
|
+
|
1973
|
+
const typeAnnoToPorfType = x => {
|
1974
|
+
if (TYPES[x]) return TYPES[x];
|
1975
|
+
if (TYPES['_' + x]) return TYPES['_' + x];
|
1976
|
+
|
1977
|
+
switch (x) {
|
1978
|
+
case 'i32':
|
1979
|
+
case 'i64':
|
1980
|
+
return TYPES.number;
|
1981
|
+
}
|
1982
|
+
|
1983
|
+
return null;
|
1984
|
+
};
|
1985
|
+
|
1986
|
+
const extractTypeAnnotation = decl => {
|
1987
|
+
let a = decl;
|
1988
|
+
while (a.typeAnnotation) a = a.typeAnnotation;
|
1989
|
+
|
1990
|
+
let type, elementType;
|
1991
|
+
if (a.typeName) {
|
1992
|
+
type = a.typeName.name;
|
1993
|
+
} else if (a.type.endsWith('Keyword')) {
|
1994
|
+
type = a.type.slice(2, -7).toLowerCase();
|
1995
|
+
} else if (a.type === 'TSArrayType') {
|
1996
|
+
type = 'array';
|
1997
|
+
elementType = extractTypeAnnotation(a.elementType).type;
|
1998
|
+
}
|
1999
|
+
|
2000
|
+
const typeName = type;
|
2001
|
+
type = typeAnnoToPorfType(type);
|
2002
|
+
|
2003
|
+
if (type === TYPES._bytestring && !Prefs.bytestring) type = TYPES.string;
|
2004
|
+
|
2005
|
+
// if (decl.name) console.log(decl.name, { type, elementType });
|
2006
|
+
|
2007
|
+
return { type, typeName, elementType };
|
2008
|
+
};
|
2009
|
+
|
1671
2010
|
const generateVar = (scope, decl) => {
|
1672
2011
|
let out = [];
|
1673
2012
|
|
@@ -1679,6 +2018,8 @@ const generateVar = (scope, decl) => {
|
|
1679
2018
|
for (const x of decl.declarations) {
|
1680
2019
|
const name = mapName(x.id.name);
|
1681
2020
|
|
2021
|
+
if (!name) return todo('destructuring is not supported yet');
|
2022
|
+
|
1682
2023
|
if (x.init && isFuncType(x.init.type)) {
|
1683
2024
|
// hack for let a = function () { ... }
|
1684
2025
|
x.init.id = { name };
|
@@ -1694,7 +2035,12 @@ const generateVar = (scope, decl) => {
|
|
1694
2035
|
continue; // always ignore
|
1695
2036
|
}
|
1696
2037
|
|
1697
|
-
let idx = allocVar(scope, name, global);
|
2038
|
+
let idx = allocVar(scope, name, global, !x.id.typeAnnotation);
|
2039
|
+
|
2040
|
+
if (typedInput && x.id.typeAnnotation) {
|
2041
|
+
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
2042
|
+
}
|
2043
|
+
|
1698
2044
|
if (x.init) {
|
1699
2045
|
out = out.concat(generate(scope, x.init, global, name));
|
1700
2046
|
|
@@ -1709,7 +2055,8 @@ const generateVar = (scope, decl) => {
|
|
1709
2055
|
return out;
|
1710
2056
|
};
|
1711
2057
|
|
1712
|
-
|
2058
|
+
// todo: optimize this func for valueUnused
|
2059
|
+
const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
1713
2060
|
const { type, name } = decl.left;
|
1714
2061
|
|
1715
2062
|
if (type === 'ObjectPattern') {
|
@@ -1812,6 +2159,8 @@ const generateAssign = (scope, decl) => {
|
|
1812
2159
|
];
|
1813
2160
|
}
|
1814
2161
|
|
2162
|
+
if (!name) return todo('destructuring is not supported yet');
|
2163
|
+
|
1815
2164
|
const [ local, isGlobal ] = lookupName(scope, name);
|
1816
2165
|
|
1817
2166
|
if (local === undefined) {
|
@@ -1858,9 +2207,7 @@ const generateAssign = (scope, decl) => {
|
|
1858
2207
|
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
1859
2208
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1860
2209
|
|
1861
|
-
|
1862
|
-
// hack: type is idx+1
|
1863
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2210
|
+
...setType(scope, name, getLastType(scope))
|
1864
2211
|
];
|
1865
2212
|
}
|
1866
2213
|
|
@@ -1871,9 +2218,7 @@ const generateAssign = (scope, decl) => {
|
|
1871
2218
|
|
1872
2219
|
// todo: string concat types
|
1873
2220
|
|
1874
|
-
|
1875
|
-
...number(TYPES.number, Valtype.i32),
|
1876
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2221
|
+
...setType(scope, name, TYPES.number)
|
1877
2222
|
];
|
1878
2223
|
};
|
1879
2224
|
|
@@ -1950,6 +2295,8 @@ const generateUnary = (scope, decl) => {
|
|
1950
2295
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
1951
2296
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
1952
2297
|
|
2298
|
+
[TYPES._bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2299
|
+
|
1953
2300
|
// object and internal types
|
1954
2301
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
1955
2302
|
});
|
@@ -1959,7 +2306,7 @@ const generateUnary = (scope, decl) => {
|
|
1959
2306
|
}
|
1960
2307
|
};
|
1961
2308
|
|
1962
|
-
const generateUpdate = (scope, decl) => {
|
2309
|
+
const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
|
1963
2310
|
const { name } = decl.argument;
|
1964
2311
|
|
1965
2312
|
const [ local, isGlobal ] = lookupName(scope, name);
|
@@ -1972,7 +2319,7 @@ const generateUpdate = (scope, decl) => {
|
|
1972
2319
|
const out = [];
|
1973
2320
|
|
1974
2321
|
out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
1975
|
-
if (!decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2322
|
+
if (!decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
1976
2323
|
|
1977
2324
|
switch (decl.operator) {
|
1978
2325
|
case '++':
|
@@ -1985,7 +2332,7 @@ const generateUpdate = (scope, decl) => {
|
|
1985
2332
|
}
|
1986
2333
|
|
1987
2334
|
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
1988
|
-
if (decl.prefix) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
2335
|
+
if (decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
|
1989
2336
|
|
1990
2337
|
return out;
|
1991
2338
|
};
|
@@ -2025,7 +2372,7 @@ const generateConditional = (scope, decl) => {
|
|
2025
2372
|
// note type
|
2026
2373
|
out.push(
|
2027
2374
|
...getNodeType(scope, decl.consequent),
|
2028
|
-
|
2375
|
+
setLastType(scope)
|
2029
2376
|
);
|
2030
2377
|
|
2031
2378
|
out.push([ Opcodes.else ]);
|
@@ -2034,7 +2381,7 @@ const generateConditional = (scope, decl) => {
|
|
2034
2381
|
// note type
|
2035
2382
|
out.push(
|
2036
2383
|
...getNodeType(scope, decl.alternate),
|
2037
|
-
|
2384
|
+
setLastType(scope)
|
2038
2385
|
);
|
2039
2386
|
|
2040
2387
|
out.push([ Opcodes.end ]);
|
@@ -2048,15 +2395,17 @@ const generateFor = (scope, decl) => {
|
|
2048
2395
|
const out = [];
|
2049
2396
|
|
2050
2397
|
if (decl.init) {
|
2051
|
-
out.push(...generate(scope, decl.init));
|
2398
|
+
out.push(...generate(scope, decl.init, false, undefined, true));
|
2052
2399
|
disposeLeftover(out);
|
2053
2400
|
}
|
2054
2401
|
|
2055
2402
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
2056
2403
|
depth.push('for');
|
2057
2404
|
|
2058
|
-
out.push(...generate(scope, decl.test));
|
2059
|
-
|
2405
|
+
if (decl.test) out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2406
|
+
else out.push(...number(1, Valtype.i32));
|
2407
|
+
|
2408
|
+
out.push([ Opcodes.if, Blocktype.void ]);
|
2060
2409
|
depth.push('if');
|
2061
2410
|
|
2062
2411
|
out.push([ Opcodes.block, Blocktype.void ]);
|
@@ -2064,8 +2413,7 @@ const generateFor = (scope, decl) => {
|
|
2064
2413
|
out.push(...generate(scope, decl.body));
|
2065
2414
|
out.push([ Opcodes.end ]);
|
2066
2415
|
|
2067
|
-
out.push(...generate(scope, decl.update));
|
2068
|
-
depth.pop();
|
2416
|
+
if (decl.update) out.push(...generate(scope, decl.update, false, undefined, true));
|
2069
2417
|
|
2070
2418
|
out.push([ Opcodes.br, 1 ]);
|
2071
2419
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2122,7 +2470,13 @@ const generateForOf = (scope, decl) => {
|
|
2122
2470
|
// setup local for left
|
2123
2471
|
generate(scope, decl.left);
|
2124
2472
|
|
2125
|
-
|
2473
|
+
let leftName = decl.left.declarations?.[0]?.id?.name;
|
2474
|
+
if (!leftName && decl.left.name) {
|
2475
|
+
leftName = decl.left.name;
|
2476
|
+
|
2477
|
+
generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
|
2478
|
+
}
|
2479
|
+
|
2126
2480
|
const [ local, isGlobal ] = lookupName(scope, leftName);
|
2127
2481
|
|
2128
2482
|
depth.push('block');
|
@@ -2131,13 +2485,14 @@ const generateForOf = (scope, decl) => {
|
|
2131
2485
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2132
2486
|
// hack: this is naughty and will break things!
|
2133
2487
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2134
|
-
if (pages.
|
2488
|
+
if (pages.hasAnyString) {
|
2135
2489
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2136
2490
|
rawElements: new Array(1)
|
2137
2491
|
}, isGlobal, leftName, true, 'i16');
|
2138
2492
|
}
|
2139
2493
|
|
2140
2494
|
// set type for local
|
2495
|
+
// todo: optimize away counter and use end pointer
|
2141
2496
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2142
2497
|
[TYPES._array]: [
|
2143
2498
|
...setType(scope, leftName, TYPES.number),
|
@@ -2262,7 +2617,7 @@ const generateThrow = (scope, decl) => {
|
|
2262
2617
|
// hack: throw new X("...") -> throw "..."
|
2263
2618
|
if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
|
2264
2619
|
constructor = decl.argument.callee.name;
|
2265
|
-
message = decl.argument.arguments[0]
|
2620
|
+
message = decl.argument.arguments[0]?.value ?? '';
|
2266
2621
|
}
|
2267
2622
|
|
2268
2623
|
if (tags.length === 0) tags.push({
|
@@ -2274,6 +2629,9 @@ const generateThrow = (scope, decl) => {
|
|
2274
2629
|
let exceptId = exceptions.push({ constructor, message }) - 1;
|
2275
2630
|
let tagIdx = tags[0].idx;
|
2276
2631
|
|
2632
|
+
scope.exceptions ??= [];
|
2633
|
+
scope.exceptions.push(exceptId);
|
2634
|
+
|
2277
2635
|
// todo: write a description of how this works lol
|
2278
2636
|
|
2279
2637
|
return [
|
@@ -2318,25 +2676,31 @@ const generateAssignPat = (scope, decl) => {
|
|
2318
2676
|
};
|
2319
2677
|
|
2320
2678
|
let pages = new Map();
|
2321
|
-
const allocPage = (reason, type) => {
|
2679
|
+
const allocPage = (scope, reason, type) => {
|
2322
2680
|
if (pages.has(reason)) return pages.get(reason).ind;
|
2323
2681
|
|
2324
2682
|
if (reason.startsWith('array:')) pages.hasArray = true;
|
2325
2683
|
if (reason.startsWith('string:')) pages.hasString = true;
|
2684
|
+
if (reason.startsWith('bytestring:')) pages.hasByteString = true;
|
2685
|
+
if (reason.includes('string:')) pages.hasAnyString = true;
|
2326
2686
|
|
2327
2687
|
const ind = pages.size;
|
2328
2688
|
pages.set(reason, { ind, type });
|
2329
2689
|
|
2330
|
-
|
2690
|
+
scope.pages ??= new Map();
|
2691
|
+
scope.pages.set(reason, { ind, type });
|
2692
|
+
|
2693
|
+
if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
2331
2694
|
|
2332
2695
|
return ind;
|
2333
2696
|
};
|
2334
2697
|
|
2698
|
+
// todo: add scope.pages
|
2335
2699
|
const freePage = reason => {
|
2336
2700
|
const { ind } = pages.get(reason);
|
2337
2701
|
pages.delete(reason);
|
2338
2702
|
|
2339
|
-
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2703
|
+
if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2340
2704
|
|
2341
2705
|
return ind;
|
2342
2706
|
};
|
@@ -2356,25 +2720,34 @@ const StoreOps = {
|
|
2356
2720
|
f64: Opcodes.f64_store,
|
2357
2721
|
|
2358
2722
|
// expects i32 input!
|
2359
|
-
|
2723
|
+
i8: Opcodes.i32_store8,
|
2724
|
+
i16: Opcodes.i32_store16,
|
2360
2725
|
};
|
2361
2726
|
|
2362
2727
|
let data = [];
|
2363
2728
|
|
2364
|
-
const compileBytes = (val, itemType
|
2729
|
+
const compileBytes = (val, itemType) => {
|
2365
2730
|
// todo: this is a mess and needs confirming / ????
|
2366
2731
|
switch (itemType) {
|
2367
2732
|
case 'i8': return [ val % 256 ];
|
2368
|
-
case 'i16': return [ val % 256,
|
2369
|
-
|
2370
|
-
case 'i32':
|
2371
|
-
|
2372
|
-
return enforceFourBytes(signedLEB128(val));
|
2733
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
2734
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
2735
|
+
case 'i32': return [...new Uint8Array(new Int32Array([ val ]).buffer)];
|
2736
|
+
// todo: i64
|
2373
2737
|
|
2374
2738
|
case 'f64': return ieee754_binary64(val);
|
2375
2739
|
}
|
2376
2740
|
};
|
2377
2741
|
|
2742
|
+
const getAllocType = itemType => {
|
2743
|
+
switch (itemType) {
|
2744
|
+
case 'i8': return 'bytestring';
|
2745
|
+
case 'i16': return 'string';
|
2746
|
+
|
2747
|
+
default: return 'array';
|
2748
|
+
}
|
2749
|
+
};
|
2750
|
+
|
2378
2751
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2379
2752
|
const out = [];
|
2380
2753
|
|
@@ -2384,7 +2757,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2384
2757
|
|
2385
2758
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2386
2759
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2387
|
-
arrays.set(name, allocPage(`${itemType
|
2760
|
+
arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2388
2761
|
}
|
2389
2762
|
|
2390
2763
|
const pointer = arrays.get(name);
|
@@ -2395,19 +2768,25 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2395
2768
|
const valtype = itemTypeToValtype[itemType];
|
2396
2769
|
const length = elements.length;
|
2397
2770
|
|
2398
|
-
if (firstAssign && useRawElements) {
|
2399
|
-
|
2771
|
+
if (firstAssign && useRawElements && !Prefs.noData) {
|
2772
|
+
// if length is 0 memory/data will just be 0000... anyway
|
2773
|
+
if (length !== 0) {
|
2774
|
+
let bytes = compileBytes(length, 'i32');
|
2400
2775
|
|
2401
|
-
|
2402
|
-
|
2776
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
2777
|
+
if (elements[i] == null) continue;
|
2403
2778
|
|
2404
|
-
|
2405
|
-
|
2779
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
2780
|
+
}
|
2406
2781
|
|
2407
|
-
|
2408
|
-
|
2409
|
-
|
2410
|
-
|
2782
|
+
const ind = data.push({
|
2783
|
+
offset: pointer,
|
2784
|
+
bytes
|
2785
|
+
}) - 1;
|
2786
|
+
|
2787
|
+
scope.data ??= [];
|
2788
|
+
scope.data.push(ind);
|
2789
|
+
}
|
2411
2790
|
|
2412
2791
|
// local value as pointer
|
2413
2792
|
out.push(...number(pointer));
|
@@ -2430,7 +2809,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2430
2809
|
out.push(
|
2431
2810
|
...number(0, Valtype.i32),
|
2432
2811
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
2433
|
-
[ storeOp, Math.log2(ValtypeSize[itemType]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2812
|
+
[ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2434
2813
|
);
|
2435
2814
|
}
|
2436
2815
|
|
@@ -2440,15 +2819,31 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2440
2819
|
return [ out, pointer ];
|
2441
2820
|
};
|
2442
2821
|
|
2443
|
-
const
|
2822
|
+
const byteStringable = str => {
|
2823
|
+
if (!Prefs.bytestring) return false;
|
2824
|
+
|
2825
|
+
for (let i = 0; i < str.length; i++) {
|
2826
|
+
if (str.charCodeAt(i) > 0xFF) return false;
|
2827
|
+
}
|
2828
|
+
|
2829
|
+
return true;
|
2830
|
+
};
|
2831
|
+
|
2832
|
+
const makeString = (scope, str, global = false, name = '$undeclared', forceBytestring = undefined) => {
|
2444
2833
|
const rawElements = new Array(str.length);
|
2834
|
+
let byteStringable = Prefs.bytestring;
|
2445
2835
|
for (let i = 0; i < str.length; i++) {
|
2446
|
-
|
2836
|
+
const c = str.charCodeAt(i);
|
2837
|
+
rawElements[i] = c;
|
2838
|
+
|
2839
|
+
if (byteStringable && c > 0xFF) byteStringable = false;
|
2447
2840
|
}
|
2448
2841
|
|
2842
|
+
if (byteStringable && forceBytestring === false) byteStringable = false;
|
2843
|
+
|
2449
2844
|
return makeArray(scope, {
|
2450
2845
|
rawElements
|
2451
|
-
}, global, name, false, 'i16')[0];
|
2846
|
+
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2452
2847
|
};
|
2453
2848
|
|
2454
2849
|
let arrays = new Map();
|
@@ -2476,10 +2871,13 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2476
2871
|
];
|
2477
2872
|
}
|
2478
2873
|
|
2874
|
+
const object = generate(scope, decl.object);
|
2875
|
+
const property = generate(scope, decl.property);
|
2876
|
+
|
2479
2877
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2480
2878
|
// hack: this is naughty and will break things!
|
2481
|
-
let newOut = number(0,
|
2482
|
-
if (pages.
|
2879
|
+
let newOut = number(0, valtypeBinary), newPointer = -1;
|
2880
|
+
if (pages.hasAnyString) {
|
2483
2881
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2484
2882
|
rawElements: new Array(1)
|
2485
2883
|
}, _global, _name, true, 'i16');
|
@@ -2488,7 +2886,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2488
2886
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
2489
2887
|
[TYPES._array]: [
|
2490
2888
|
// get index as valtype
|
2491
|
-
...
|
2889
|
+
...property,
|
2492
2890
|
|
2493
2891
|
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2494
2892
|
Opcodes.i32_to_u,
|
@@ -2496,7 +2894,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2496
2894
|
[ Opcodes.i32_mul ],
|
2497
2895
|
|
2498
2896
|
...(aotPointer ? [] : [
|
2499
|
-
...
|
2897
|
+
...object,
|
2500
2898
|
Opcodes.i32_to_u,
|
2501
2899
|
[ Opcodes.i32_add ]
|
2502
2900
|
]),
|
@@ -2505,7 +2903,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2505
2903
|
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2506
2904
|
|
2507
2905
|
...number(TYPES.number, Valtype.i32),
|
2508
|
-
|
2906
|
+
setLastType(scope)
|
2509
2907
|
],
|
2510
2908
|
|
2511
2909
|
[TYPES.string]: [
|
@@ -2515,14 +2913,14 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2515
2913
|
|
2516
2914
|
...number(0, Valtype.i32), // base 0 for store later
|
2517
2915
|
|
2518
|
-
...
|
2519
|
-
|
2916
|
+
...property,
|
2520
2917
|
Opcodes.i32_to_u,
|
2918
|
+
|
2521
2919
|
...number(ValtypeSize.i16, Valtype.i32),
|
2522
2920
|
[ Opcodes.i32_mul ],
|
2523
2921
|
|
2524
2922
|
...(aotPointer ? [] : [
|
2525
|
-
...
|
2923
|
+
...object,
|
2526
2924
|
Opcodes.i32_to_u,
|
2527
2925
|
[ Opcodes.i32_add ]
|
2528
2926
|
]),
|
@@ -2537,10 +2935,38 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2537
2935
|
...number(newPointer),
|
2538
2936
|
|
2539
2937
|
...number(TYPES.string, Valtype.i32),
|
2540
|
-
|
2938
|
+
setLastType(scope)
|
2939
|
+
],
|
2940
|
+
[TYPES._bytestring]: [
|
2941
|
+
// setup new/out array
|
2942
|
+
...newOut,
|
2943
|
+
[ Opcodes.drop ],
|
2944
|
+
|
2945
|
+
...number(0, Valtype.i32), // base 0 for store later
|
2946
|
+
|
2947
|
+
...property,
|
2948
|
+
Opcodes.i32_to_u,
|
2949
|
+
|
2950
|
+
...(aotPointer ? [] : [
|
2951
|
+
...object,
|
2952
|
+
Opcodes.i32_to_u,
|
2953
|
+
[ Opcodes.i32_add ]
|
2954
|
+
]),
|
2955
|
+
|
2956
|
+
// load current string ind {arg}
|
2957
|
+
[ Opcodes.i32_load8_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2958
|
+
|
2959
|
+
// store to new string ind 0
|
2960
|
+
[ Opcodes.i32_store8, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2961
|
+
|
2962
|
+
// return new string (page)
|
2963
|
+
...number(newPointer),
|
2964
|
+
|
2965
|
+
...number(TYPES._bytestring, Valtype.i32),
|
2966
|
+
setLastType(scope)
|
2541
2967
|
],
|
2542
2968
|
|
2543
|
-
default:
|
2969
|
+
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet')
|
2544
2970
|
});
|
2545
2971
|
};
|
2546
2972
|
|
@@ -2557,13 +2983,16 @@ const objectHack = node => {
|
|
2557
2983
|
// if object is not identifier or another member exp, give up
|
2558
2984
|
if (node.object.type !== 'Identifier' && node.object.type !== 'MemberExpression') return node;
|
2559
2985
|
|
2560
|
-
if (!objectName) objectName = objectHack(node.object)
|
2986
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
2561
2987
|
|
2562
2988
|
// if .length, give up (hack within a hack!)
|
2563
2989
|
if (node.property.name === 'length') return node;
|
2564
2990
|
|
2991
|
+
// no object name, give up
|
2992
|
+
if (!objectName) return node;
|
2993
|
+
|
2565
2994
|
const name = '__' + objectName + '_' + node.property.name;
|
2566
|
-
if (codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2995
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2567
2996
|
|
2568
2997
|
return {
|
2569
2998
|
type: 'Identifier',
|
@@ -2586,7 +3015,7 @@ const generateFunc = (scope, decl) => {
|
|
2586
3015
|
if (decl.generator) return todo('generator functions are not supported');
|
2587
3016
|
|
2588
3017
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
2589
|
-
const params = decl.params
|
3018
|
+
const params = decl.params ?? [];
|
2590
3019
|
|
2591
3020
|
// const innerScope = { ...scope };
|
2592
3021
|
// TODO: share scope/locals between !!!
|
@@ -2600,7 +3029,11 @@ const generateFunc = (scope, decl) => {
|
|
2600
3029
|
};
|
2601
3030
|
|
2602
3031
|
for (let i = 0; i < params.length; i++) {
|
2603
|
-
allocVar(innerScope, params[i], false);
|
3032
|
+
allocVar(innerScope, params[i].name, false);
|
3033
|
+
|
3034
|
+
if (typedInput && params[i].typeAnnotation) {
|
3035
|
+
addVarMetadata(innerScope, params[i].name, false, extractTypeAnnotation(params[i]));
|
3036
|
+
}
|
2604
3037
|
}
|
2605
3038
|
|
2606
3039
|
let body = objectHack(decl.body);
|
@@ -2616,13 +3049,13 @@ const generateFunc = (scope, decl) => {
|
|
2616
3049
|
const func = {
|
2617
3050
|
name,
|
2618
3051
|
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
2619
|
-
|
2620
|
-
|
2621
|
-
throws: innerScope.throws,
|
2622
|
-
index: currentFuncIndex++
|
3052
|
+
index: currentFuncIndex++,
|
3053
|
+
...innerScope
|
2623
3054
|
};
|
2624
3055
|
funcIndex[name] = func.index;
|
2625
3056
|
|
3057
|
+
if (name === 'main') func.gotLastType = true;
|
3058
|
+
|
2626
3059
|
// quick hack fixes
|
2627
3060
|
for (const inst of wasm) {
|
2628
3061
|
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
@@ -2639,117 +3072,6 @@ const generateFunc = (scope, decl) => {
|
|
2639
3072
|
);
|
2640
3073
|
}
|
2641
3074
|
|
2642
|
-
// change v128 params into many <type> (i32x4 -> i32/etc) instead as unsupported param valtype
|
2643
|
-
let offset = 0, vecParams = 0;
|
2644
|
-
for (let i = 0; i < params.length; i++) {
|
2645
|
-
const name = params[i];
|
2646
|
-
const local = func.locals[name];
|
2647
|
-
if (local.type === Valtype.v128) {
|
2648
|
-
vecParams++;
|
2649
|
-
|
2650
|
-
/* wasm.unshift( // add v128 load for param
|
2651
|
-
[ Opcodes.i32_const, 0 ],
|
2652
|
-
[ ...Opcodes.v128_load, 0, i * 16 ],
|
2653
|
-
[ Opcodes.local_set, local.idx ]
|
2654
|
-
); */
|
2655
|
-
|
2656
|
-
// using params and replace_lane is noticably faster than just loading from memory (above) somehow
|
2657
|
-
|
2658
|
-
// extract valtype and lane count from vec type (i32x4 = i32 4, i8x16 = i8 16, etc)
|
2659
|
-
const { vecType } = local;
|
2660
|
-
let [ type, lanes ] = vecType.split('x');
|
2661
|
-
if (!type || !lanes) throw new Error('bad metadata from vec params'); // sanity check
|
2662
|
-
|
2663
|
-
lanes = parseInt(lanes);
|
2664
|
-
type = Valtype[type];
|
2665
|
-
|
2666
|
-
const name = params[i]; // get original param name
|
2667
|
-
|
2668
|
-
func.params.splice(offset, 1, ...new Array(lanes).fill(type)); // add new params of {type}, {lanes} times
|
2669
|
-
|
2670
|
-
// update index of original local
|
2671
|
-
// delete func.locals[name];
|
2672
|
-
|
2673
|
-
// add new locals for params
|
2674
|
-
for (let j = 0; j < lanes; j++) {
|
2675
|
-
func.locals[name + j] = { idx: offset + j, type, vecParamAutogen: true };
|
2676
|
-
}
|
2677
|
-
|
2678
|
-
// prepend wasm to generate expected v128 locals
|
2679
|
-
wasm.splice(i * 2 + offset * 2, 0,
|
2680
|
-
...i32x4(0, 0, 0, 0),
|
2681
|
-
...new Array(lanes).fill(0).flatMap((_, j) => [
|
2682
|
-
[ Opcodes.local_get, offset + j ],
|
2683
|
-
[ ...Opcodes[vecType + '_replace_lane'], j ]
|
2684
|
-
]),
|
2685
|
-
[ Opcodes.local_set, i ]
|
2686
|
-
);
|
2687
|
-
|
2688
|
-
offset += lanes;
|
2689
|
-
|
2690
|
-
// note: wrapping is disabled for now due to perf/dx concerns (so this will never run)
|
2691
|
-
/* if (!func.name.startsWith('#')) func.name = '##' + func.name;
|
2692
|
-
|
2693
|
-
// add vec type index to hash name prefix for wrapper to know how to wrap
|
2694
|
-
const vecTypeIdx = [ 'i8x16', 'i16x8', 'i32x4', 'i64x2', 'f32x4', 'f64x2' ].indexOf(local.vecType);
|
2695
|
-
const secondHash = func.name.slice(1).indexOf('#');
|
2696
|
-
func.name = '#' + func.name.slice(1, secondHash) + vecTypeIdx + func.name.slice(secondHash); */
|
2697
|
-
}
|
2698
|
-
}
|
2699
|
-
|
2700
|
-
if (offset !== 0) {
|
2701
|
-
// bump local indexes for all other locals after
|
2702
|
-
for (const x in func.locals) {
|
2703
|
-
const local = func.locals[x];
|
2704
|
-
if (!local.vecParamAutogen) local.idx += offset;
|
2705
|
-
}
|
2706
|
-
|
2707
|
-
// bump local indexes in wasm local.get/set
|
2708
|
-
for (let j = 0; j < wasm.length; j++) {
|
2709
|
-
const inst = wasm[j];
|
2710
|
-
if (j < offset * 2 + vecParams * 2) {
|
2711
|
-
if (inst[0] === Opcodes.local_set) inst[1] += offset;
|
2712
|
-
continue;
|
2713
|
-
}
|
2714
|
-
|
2715
|
-
if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set) inst[1] += offset;
|
2716
|
-
}
|
2717
|
-
}
|
2718
|
-
|
2719
|
-
// change v128 return into many <type> instead as unsupported return valtype
|
2720
|
-
const lastReturnLocal = wasm.length > 2 && wasm[wasm.length - 1][0] === Opcodes.return && Object.values(func.locals).find(x => x.idx === wasm[wasm.length - 2][1]);
|
2721
|
-
if (lastReturnLocal && lastReturnLocal.type === Valtype.v128) {
|
2722
|
-
const name = Object.keys(func.locals)[Object.values(func.locals).indexOf(lastReturnLocal)];
|
2723
|
-
// extract valtype and lane count from vec type (i32x4 = i32 4, i8x16 = i8 16, etc)
|
2724
|
-
const { vecType } = lastReturnLocal;
|
2725
|
-
let [ type, lanes ] = vecType.split('x');
|
2726
|
-
if (!type || !lanes) throw new Error('bad metadata from vec params'); // sanity check
|
2727
|
-
|
2728
|
-
lanes = parseInt(lanes);
|
2729
|
-
type = Valtype[type];
|
2730
|
-
|
2731
|
-
const vecIdx = lastReturnLocal.idx;
|
2732
|
-
|
2733
|
-
const lastIdx = Math.max(0, ...Object.values(func.locals).map(x => x.idx));
|
2734
|
-
const tmpIdx = [];
|
2735
|
-
for (let i = 0; i < lanes; i++) {
|
2736
|
-
const idx = lastIdx + i + 1;
|
2737
|
-
tmpIdx.push(idx);
|
2738
|
-
func.locals[name + i] = { idx, type, vecReturnAutogen: true };
|
2739
|
-
}
|
2740
|
-
|
2741
|
-
wasm.splice(wasm.length - 1, 1,
|
2742
|
-
...new Array(lanes).fill(0).flatMap((_, i) => [
|
2743
|
-
i === 0 ? null : [ Opcodes.local_get, vecIdx ],
|
2744
|
-
[ ...Opcodes[vecType + '_extract_lane'], i ],
|
2745
|
-
[ Opcodes.local_set, tmpIdx[i] ],
|
2746
|
-
].filter(x => x !== null)),
|
2747
|
-
...new Array(lanes).fill(0).map((_, i) => [ Opcodes.local_get, tmpIdx[i]])
|
2748
|
-
);
|
2749
|
-
|
2750
|
-
func.returns = new Array(lanes).fill(type);
|
2751
|
-
}
|
2752
|
-
|
2753
3075
|
func.wasm = wasm;
|
2754
3076
|
|
2755
3077
|
funcs.push(func);
|
@@ -2768,6 +3090,16 @@ const generateCode = (scope, decl) => {
|
|
2768
3090
|
};
|
2769
3091
|
|
2770
3092
|
const internalConstrs = {
|
3093
|
+
Boolean: {
|
3094
|
+
generate: (scope, decl) => {
|
3095
|
+
if (decl.arguments.length === 0) return number(0);
|
3096
|
+
|
3097
|
+
// should generate/run all args
|
3098
|
+
return truthy(scope, generate(scope, decl.arguments[0]), getNodeType(scope, decl.arguments[0]), false, false);
|
3099
|
+
},
|
3100
|
+
type: TYPES.boolean
|
3101
|
+
},
|
3102
|
+
|
2771
3103
|
Array: {
|
2772
3104
|
generate: (scope, decl, global, name) => {
|
2773
3105
|
// new Array(i0, i1, ...)
|
@@ -2889,7 +3221,7 @@ export default program => {
|
|
2889
3221
|
body: program.body
|
2890
3222
|
};
|
2891
3223
|
|
2892
|
-
if (
|
3224
|
+
if (Prefs.astLog) console.log(program.body.body);
|
2893
3225
|
|
2894
3226
|
generateFunc(scope, program);
|
2895
3227
|
|