porffor 0.2.0-5e33105 → 0.2.0-6aff0fa
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 +75 -47
- package/asur/README.md +2 -0
- package/asur/index.js +978 -0
- package/compiler/2c.js +316 -71
- package/compiler/builtins/base64.ts +153 -0
- package/compiler/builtins/porffor.d.ts +23 -0
- package/compiler/builtins.js +230 -205
- package/compiler/codeGen.js +420 -189
- package/compiler/decompile.js +3 -3
- package/compiler/generated_builtins.js +15 -0
- package/compiler/index.js +22 -22
- package/compiler/log.js +4 -1
- package/compiler/opt.js +40 -26
- package/compiler/parse.js +4 -2
- package/compiler/precompile.js +129 -0
- package/compiler/prefs.js +26 -0
- package/compiler/prototype.js +177 -21
- package/compiler/sections.js +7 -6
- package/compiler/wasmSpec.js +16 -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/compiler/builtins/base64.js +0 -92
- package/node_trace.1.log +0 -1
- package/runner/profile.js +0 -46
- package/runner/results.json +0 -1
package/compiler/codeGen.js
CHANGED
@@ -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);
|
@@ -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,31 +201,40 @@ 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:
|
217
|
-
|
218
|
-
|
235
|
+
// ignore typescript nodes
|
236
|
+
if (decl.type.startsWith('TS') ||
|
237
|
+
decl.type === 'ImportDeclaration' && decl.importKind === 'type') {
|
219
238
|
return [];
|
220
239
|
}
|
221
240
|
|
@@ -269,25 +288,28 @@ const generateIdent = (scope, decl) => {
|
|
269
288
|
const name = mapName(rawName);
|
270
289
|
let local = scope.locals[rawName];
|
271
290
|
|
272
|
-
if (builtinVars
|
291
|
+
if (Object.hasOwn(builtinVars, name)) {
|
273
292
|
if (builtinVars[name].floatOnly && valtype[0] === 'i') throw new Error(`Cannot use ${unhackName(name)} with integer valtype`);
|
274
|
-
|
293
|
+
|
294
|
+
let wasm = builtinVars[name];
|
295
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name });
|
296
|
+
return wasm;
|
275
297
|
}
|
276
298
|
|
277
|
-
if (builtinFuncs
|
299
|
+
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
278
300
|
// todo: return an actual something
|
279
301
|
return number(1);
|
280
302
|
}
|
281
303
|
|
282
|
-
if (local === undefined) {
|
304
|
+
if (local?.idx === undefined) {
|
283
305
|
// no local var with name
|
284
|
-
if (
|
285
|
-
if (funcIndex
|
306
|
+
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
307
|
+
if (Object.hasOwn(funcIndex, name)) return number(funcIndex[name]);
|
286
308
|
|
287
|
-
if (globals
|
309
|
+
if (Object.hasOwn(globals, name)) return [ [ Opcodes.global_get, globals[name].idx ] ];
|
288
310
|
}
|
289
311
|
|
290
|
-
if (local === undefined && rawName.startsWith('__')) {
|
312
|
+
if (local?.idx === undefined && rawName.startsWith('__')) {
|
291
313
|
// return undefined if unknown key in already known var
|
292
314
|
let parent = rawName.slice(2).split('_').slice(0, -1).join('_');
|
293
315
|
if (parent.includes('_')) parent = '__' + parent;
|
@@ -296,7 +318,7 @@ const generateIdent = (scope, decl) => {
|
|
296
318
|
if (!parentLookup[1]) return number(UNDEFINED);
|
297
319
|
}
|
298
320
|
|
299
|
-
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);
|
300
322
|
|
301
323
|
return [ [ Opcodes.local_get, local.idx ] ];
|
302
324
|
};
|
@@ -404,9 +426,6 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
404
426
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
405
427
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
406
428
|
|
407
|
-
const aotWFA = process.argv.includes('-aot-well-formed-string-approximation');
|
408
|
-
if (aotWFA) addVarMeta(name, { wellFormed: undefined });
|
409
|
-
|
410
429
|
if (assign) {
|
411
430
|
const pointer = arrays.get(name ?? '$undeclared');
|
412
431
|
|
@@ -644,11 +663,12 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
644
663
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
645
664
|
];
|
646
665
|
|
647
|
-
const
|
666
|
+
const useTmp = knownType(scope, type) == null;
|
667
|
+
const tmp = useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
648
668
|
|
649
669
|
const def = [
|
650
670
|
// if value != 0
|
651
|
-
[ Opcodes.local_get, tmp ],
|
671
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
652
672
|
|
653
673
|
// ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
654
674
|
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
|
@@ -660,7 +680,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
660
680
|
|
661
681
|
return [
|
662
682
|
...wasm,
|
663
|
-
[ Opcodes.local_set, tmp ],
|
683
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
664
684
|
|
665
685
|
...typeSwitch(scope, type, {
|
666
686
|
// [TYPES.number]: def,
|
@@ -669,7 +689,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
669
689
|
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
670
690
|
],
|
671
691
|
[TYPES.string]: [
|
672
|
-
[ Opcodes.local_get, tmp ],
|
692
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
673
693
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
674
694
|
|
675
695
|
// get length
|
@@ -680,16 +700,27 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
680
700
|
[ Opcodes.i32_eqz ], */
|
681
701
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
682
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
|
+
],
|
683
712
|
default: def
|
684
713
|
}, intOut ? Valtype.i32 : valtypeBinary)
|
685
714
|
];
|
686
715
|
};
|
687
716
|
|
688
717
|
const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
689
|
-
const
|
718
|
+
const useTmp = knownType(scope, type) == null;
|
719
|
+
const tmp = useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
720
|
+
|
690
721
|
return [
|
691
722
|
...wasm,
|
692
|
-
[ Opcodes.local_set, tmp ],
|
723
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
693
724
|
|
694
725
|
...typeSwitch(scope, type, {
|
695
726
|
[TYPES._array]: [
|
@@ -697,7 +728,18 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
697
728
|
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
698
729
|
],
|
699
730
|
[TYPES.string]: [
|
700
|
-
[ 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 ] ]),
|
701
743
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
702
744
|
|
703
745
|
// get length
|
@@ -709,7 +751,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
709
751
|
],
|
710
752
|
default: [
|
711
753
|
// if value == 0
|
712
|
-
[ Opcodes.local_get, tmp ],
|
754
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
713
755
|
|
714
756
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
715
757
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -719,10 +761,12 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
719
761
|
};
|
720
762
|
|
721
763
|
const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
722
|
-
const
|
764
|
+
const useTmp = knownType(scope, type) == null;
|
765
|
+
const tmp = useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
766
|
+
|
723
767
|
return [
|
724
768
|
...wasm,
|
725
|
-
[ Opcodes.local_set, tmp ],
|
769
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
726
770
|
|
727
771
|
...typeSwitch(scope, type, {
|
728
772
|
[TYPES.undefined]: [
|
@@ -731,7 +775,7 @@ const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
731
775
|
],
|
732
776
|
[TYPES.object]: [
|
733
777
|
// object, null if == 0
|
734
|
-
[ Opcodes.local_get, tmp ],
|
778
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
735
779
|
|
736
780
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
737
781
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -760,11 +804,14 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
760
804
|
return performLogicOp(scope, op, left, right, leftType, rightType);
|
761
805
|
}
|
762
806
|
|
807
|
+
const knownLeft = knownType(scope, leftType);
|
808
|
+
const knownRight = knownType(scope, rightType);
|
809
|
+
|
763
810
|
const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
|
764
811
|
const strictOp = op === '===' || op === '!==';
|
765
812
|
|
766
813
|
const startOut = [], endOut = [];
|
767
|
-
const
|
814
|
+
const finalize = out => startOut.concat(out, endOut);
|
768
815
|
|
769
816
|
// if strict (in)equal check types match
|
770
817
|
if (strictOp) {
|
@@ -809,31 +856,32 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
809
856
|
// todo: if equality op and an operand is undefined, return false
|
810
857
|
// todo: niche null hell with 0
|
811
858
|
|
812
|
-
//
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
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
|
+
}
|
837
885
|
|
838
886
|
let ops = operatorOpcode[valtype][op];
|
839
887
|
|
@@ -843,7 +891,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
843
891
|
includeBuiltin(scope, builtinName);
|
844
892
|
const idx = funcIndex[builtinName];
|
845
893
|
|
846
|
-
return
|
894
|
+
return finalize([
|
847
895
|
...left,
|
848
896
|
...right,
|
849
897
|
[ Opcodes.call, idx ]
|
@@ -858,9 +906,6 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
858
906
|
let tmpLeft, tmpRight;
|
859
907
|
// if equal op, check if strings for compareStrings
|
860
908
|
if (op === '===' || op === '==' || op === '!==' || op === '!=') (() => {
|
861
|
-
const knownLeft = knownType(scope, leftType);
|
862
|
-
const knownRight = knownType(scope, rightType);
|
863
|
-
|
864
909
|
// todo: intelligent partial skip later
|
865
910
|
// if neither known are string, stop this madness
|
866
911
|
if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
|
@@ -900,7 +945,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
900
945
|
[ Opcodes.i32_or ],
|
901
946
|
[ Opcodes.if, Blocktype.void ],
|
902
947
|
...number(0, Valtype.i32),
|
903
|
-
[ Opcodes.br,
|
948
|
+
[ Opcodes.br, 2 ],
|
904
949
|
[ Opcodes.end ],
|
905
950
|
|
906
951
|
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
@@ -918,7 +963,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
918
963
|
// }
|
919
964
|
})();
|
920
965
|
|
921
|
-
return
|
966
|
+
return finalize([
|
922
967
|
...left,
|
923
968
|
...(tmpLeft != null ? stringOnly([ [ Opcodes.local_tee, tmpLeft ] ]) : []),
|
924
969
|
...right,
|
@@ -935,7 +980,22 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
935
980
|
return out;
|
936
981
|
};
|
937
982
|
|
938
|
-
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 = [] }) => {
|
939
999
|
const existing = funcs.find(x => x.name === name);
|
940
1000
|
if (existing) return existing;
|
941
1001
|
|
@@ -947,6 +1007,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
947
1007
|
locals[nameParam(i)] = { idx: i, type: allLocals[i] };
|
948
1008
|
}
|
949
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
|
+
|
950
1018
|
let baseGlobalIdx, i = 0;
|
951
1019
|
for (const type of globalTypes) {
|
952
1020
|
if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
|
@@ -1027,7 +1095,8 @@ const TYPES = {
|
|
1027
1095
|
|
1028
1096
|
// these are not "typeof" types but tracked internally
|
1029
1097
|
_array: 0x10,
|
1030
|
-
_regexp: 0x11
|
1098
|
+
_regexp: 0x11,
|
1099
|
+
_bytestring: 0x12
|
1031
1100
|
};
|
1032
1101
|
|
1033
1102
|
const TYPE_NAMES = {
|
@@ -1041,13 +1110,17 @@ const TYPE_NAMES = {
|
|
1041
1110
|
[TYPES.bigint]: 'BigInt',
|
1042
1111
|
|
1043
1112
|
[TYPES._array]: 'Array',
|
1044
|
-
[TYPES._regexp]: 'RegExp'
|
1113
|
+
[TYPES._regexp]: 'RegExp',
|
1114
|
+
[TYPES._bytestring]: 'ByteString'
|
1045
1115
|
};
|
1046
1116
|
|
1047
1117
|
const getType = (scope, _name) => {
|
1048
1118
|
const name = mapName(_name);
|
1049
1119
|
|
1120
|
+
if (typedInput && scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
|
1050
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);
|
1051
1124
|
if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
|
1052
1125
|
|
1053
1126
|
let type = TYPES.undefined;
|
@@ -1094,6 +1167,8 @@ const getNodeType = (scope, node) => {
|
|
1094
1167
|
if (node.type === 'Literal') {
|
1095
1168
|
if (node.regex) return TYPES._regexp;
|
1096
1169
|
|
1170
|
+
if (typeof node.value === 'string' && byteStringable(node.value)) return TYPES._bytestring;
|
1171
|
+
|
1097
1172
|
return TYPES[typeof node.value];
|
1098
1173
|
}
|
1099
1174
|
|
@@ -1107,6 +1182,15 @@ const getNodeType = (scope, node) => {
|
|
1107
1182
|
|
1108
1183
|
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
1109
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
|
+
|
1110
1194
|
const func = funcs.find(x => x.name === name);
|
1111
1195
|
|
1112
1196
|
if (func) {
|
@@ -1114,7 +1198,7 @@ const getNodeType = (scope, node) => {
|
|
1114
1198
|
if (func.returnType) return func.returnType;
|
1115
1199
|
}
|
1116
1200
|
|
1117
|
-
if (builtinFuncs[name]) return TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1201
|
+
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1118
1202
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1119
1203
|
|
1120
1204
|
// check if this is a prototype function
|
@@ -1125,10 +1209,15 @@ const getNodeType = (scope, node) => {
|
|
1125
1209
|
const spl = name.slice(2).split('_');
|
1126
1210
|
|
1127
1211
|
const func = spl[spl.length - 1];
|
1128
|
-
const protoFuncs = Object.
|
1212
|
+
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES._bytestring && prototypeFuncs[x][func] != null);
|
1129
1213
|
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1130
1214
|
}
|
1131
1215
|
|
1216
|
+
if (name.startsWith('__Porffor_wasm_')) {
|
1217
|
+
// todo: return undefined for non-returning ops
|
1218
|
+
return TYPES.number;
|
1219
|
+
}
|
1220
|
+
|
1132
1221
|
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1133
1222
|
|
1134
1223
|
// presume
|
@@ -1177,6 +1266,14 @@ const getNodeType = (scope, node) => {
|
|
1177
1266
|
|
1178
1267
|
if (node.type === 'BinaryExpression') {
|
1179
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
|
+
|
1180
1277
|
return TYPES.number;
|
1181
1278
|
|
1182
1279
|
// todo: string concat types
|
@@ -1201,7 +1298,7 @@ const getNodeType = (scope, node) => {
|
|
1201
1298
|
if (node.operator === '!') return TYPES.boolean;
|
1202
1299
|
if (node.operator === 'void') return TYPES.undefined;
|
1203
1300
|
if (node.operator === 'delete') return TYPES.boolean;
|
1204
|
-
if (node.operator === 'typeof') return TYPES.string;
|
1301
|
+
if (node.operator === 'typeof') return Prefs.bytestring ? TYPES._bytestring : TYPES.string;
|
1205
1302
|
|
1206
1303
|
return TYPES.number;
|
1207
1304
|
}
|
@@ -1210,7 +1307,13 @@ const getNodeType = (scope, node) => {
|
|
1210
1307
|
// hack: if something.length, number type
|
1211
1308
|
if (node.property.name === 'length') return TYPES.number;
|
1212
1309
|
|
1213
|
-
//
|
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
|
1214
1317
|
return TYPES.number;
|
1215
1318
|
}
|
1216
1319
|
|
@@ -1227,28 +1330,11 @@ const getNodeType = (scope, node) => {
|
|
1227
1330
|
return ret;
|
1228
1331
|
};
|
1229
1332
|
|
1230
|
-
const toString = (scope, wasm, type) => {
|
1231
|
-
const tmp = localTmp(scope, '#tostring_tmp');
|
1232
|
-
return [
|
1233
|
-
...wasm,
|
1234
|
-
[ Opcodes.local_set, tmp ],
|
1235
|
-
|
1236
|
-
...typeSwitch(scope, type, {
|
1237
|
-
[TYPES.string]: [
|
1238
|
-
[ Opcodes.local_get, tmp ]
|
1239
|
-
],
|
1240
|
-
[TYPES.undefined]: [
|
1241
|
-
// [ Opcodes.]
|
1242
|
-
]
|
1243
|
-
})
|
1244
|
-
]
|
1245
|
-
};
|
1246
|
-
|
1247
1333
|
const generateLiteral = (scope, decl, global, name) => {
|
1248
1334
|
if (decl.value === null) return number(NULL);
|
1249
1335
|
|
1336
|
+
// hack: just return 1 for regex literals
|
1250
1337
|
if (decl.regex) {
|
1251
|
-
scope.regex[name] = decl.regex;
|
1252
1338
|
return number(1);
|
1253
1339
|
}
|
1254
1340
|
|
@@ -1261,16 +1347,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1261
1347
|
return number(decl.value ? 1 : 0);
|
1262
1348
|
|
1263
1349
|
case 'string':
|
1264
|
-
|
1265
|
-
const rawElements = new Array(str.length);
|
1266
|
-
let j = 0;
|
1267
|
-
for (let i = 0; i < str.length; i++) {
|
1268
|
-
rawElements[i] = str.charCodeAt(i);
|
1269
|
-
}
|
1270
|
-
|
1271
|
-
return makeArray(scope, {
|
1272
|
-
rawElements
|
1273
|
-
}, global, name, false, 'i16')[0];
|
1350
|
+
return makeString(scope, decl.value, global, name);
|
1274
1351
|
|
1275
1352
|
default:
|
1276
1353
|
return todo(`cannot generate literal of type ${typeof decl.value}`);
|
@@ -1290,10 +1367,10 @@ const countLeftover = wasm => {
|
|
1290
1367
|
if (inst[0] === Opcodes.end) depth--;
|
1291
1368
|
|
1292
1369
|
if (depth === 0)
|
1293
|
-
if ([Opcodes.throw,Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1294
|
-
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)) {}
|
1295
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++;
|
1296
|
-
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;
|
1297
1374
|
else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
|
1298
1375
|
else if (inst[0] === Opcodes.return) count = 0;
|
1299
1376
|
else if (inst[0] === Opcodes.call) {
|
@@ -1319,7 +1396,7 @@ const disposeLeftover = wasm => {
|
|
1319
1396
|
const generateExp = (scope, decl) => {
|
1320
1397
|
const expression = decl.expression;
|
1321
1398
|
|
1322
|
-
const out = generate(scope, expression);
|
1399
|
+
const out = generate(scope, expression, undefined, undefined, true);
|
1323
1400
|
disposeLeftover(out);
|
1324
1401
|
|
1325
1402
|
return out;
|
@@ -1377,7 +1454,7 @@ const RTArrayUtil = {
|
|
1377
1454
|
]
|
1378
1455
|
};
|
1379
1456
|
|
1380
|
-
const generateCall = (scope, decl, _global, _name) => {
|
1457
|
+
const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
1381
1458
|
/* const callee = decl.callee;
|
1382
1459
|
const args = decl.arguments;
|
1383
1460
|
|
@@ -1443,8 +1520,8 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1443
1520
|
// literal.func()
|
1444
1521
|
if (!name && decl.callee.type === 'MemberExpression') {
|
1445
1522
|
// megahack for /regex/.func()
|
1446
|
-
|
1447
|
-
|
1523
|
+
const funcName = decl.callee.property.name;
|
1524
|
+
if (decl.callee.object.regex && Object.hasOwn(Rhemyn, funcName)) {
|
1448
1525
|
const func = Rhemyn[funcName](decl.callee.object.regex.pattern, currentFuncIndex++);
|
1449
1526
|
|
1450
1527
|
funcIndex[func.name] = func.index;
|
@@ -1486,8 +1563,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1486
1563
|
|
1487
1564
|
if (protoName) {
|
1488
1565
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1489
|
-
|
1490
|
-
if (f) acc[x] = f;
|
1566
|
+
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1491
1567
|
return acc;
|
1492
1568
|
}, {});
|
1493
1569
|
|
@@ -1496,10 +1572,18 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1496
1572
|
// use local for cached i32 length as commonly used
|
1497
1573
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
1498
1574
|
const pointerLocal = localTmp(scope, '__proto_pointer_cache', Valtype.i32);
|
1499
|
-
const getPointer = [ [ Opcodes.local_get, pointerLocal ] ];
|
1500
1575
|
|
1501
1576
|
// TODO: long-term, prototypes should be their individual separate funcs
|
1502
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;
|
1503
1587
|
let lengthI32CacheUsed = false;
|
1504
1588
|
const protoBC = {};
|
1505
1589
|
for (const x in protoCands) {
|
@@ -1519,6 +1603,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1519
1603
|
const protoLocal = protoFunc.local ? localTmp(scope, `__${protoName}_tmp`, protoFunc.local) : -1;
|
1520
1604
|
const protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${protoName}_tmp2`, protoFunc.local2) : -1;
|
1521
1605
|
|
1606
|
+
let optUnused = false;
|
1522
1607
|
const protoOut = protoFunc(getPointer, {
|
1523
1608
|
getCachedI32: () => {
|
1524
1609
|
lengthI32CacheUsed = true;
|
@@ -1533,10 +1618,15 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1533
1618
|
return makeArray(scope, {
|
1534
1619
|
rawElements: new Array(length)
|
1535
1620
|
}, _global, _name, true, itemType);
|
1621
|
+
}, () => {
|
1622
|
+
optUnused = true;
|
1623
|
+
return unusedValue;
|
1536
1624
|
});
|
1537
1625
|
|
1626
|
+
if (!optUnused) allOptUnused = false;
|
1627
|
+
|
1538
1628
|
protoBC[x] = [
|
1539
|
-
[ Opcodes.block, valtypeBinary ],
|
1629
|
+
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1540
1630
|
...protoOut,
|
1541
1631
|
|
1542
1632
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
@@ -1545,11 +1635,13 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1545
1635
|
];
|
1546
1636
|
}
|
1547
1637
|
|
1548
|
-
|
1549
|
-
...generate(scope, target),
|
1638
|
+
// todo: if some cands use optUnused and some don't, we will probably crash
|
1550
1639
|
|
1551
|
-
|
1552
|
-
|
1640
|
+
return [
|
1641
|
+
...(usePointerCache ? [
|
1642
|
+
...rawPointer,
|
1643
|
+
[ Opcodes.local_set, pointerLocal ],
|
1644
|
+
] : []),
|
1553
1645
|
|
1554
1646
|
...(!lengthI32CacheUsed ? [] : [
|
1555
1647
|
...RTArrayUtil.getLengthI32(getPointer),
|
@@ -1561,7 +1653,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1561
1653
|
|
1562
1654
|
// TODO: error better
|
1563
1655
|
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1564
|
-
}, valtypeBinary),
|
1656
|
+
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1565
1657
|
];
|
1566
1658
|
}
|
1567
1659
|
}
|
@@ -1600,6 +1692,32 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1600
1692
|
idx = -1;
|
1601
1693
|
}
|
1602
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
|
+
|
1603
1721
|
if (idx === undefined) {
|
1604
1722
|
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`);
|
1605
1723
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`);
|
@@ -1608,7 +1726,9 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1608
1726
|
const func = funcs.find(x => x.index === idx);
|
1609
1727
|
|
1610
1728
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1611
|
-
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);
|
1612
1732
|
|
1613
1733
|
let args = decl.arguments;
|
1614
1734
|
if (func && args.length < paramCount) {
|
@@ -1624,14 +1744,20 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1624
1744
|
if (func && func.throws) scope.throws = true;
|
1625
1745
|
|
1626
1746
|
let out = [];
|
1627
|
-
for (
|
1747
|
+
for (let i = 0; i < args.length; i++) {
|
1748
|
+
const arg = args[i];
|
1628
1749
|
out = out.concat(generate(scope, arg));
|
1629
|
-
|
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));
|
1630
1756
|
}
|
1631
1757
|
|
1632
1758
|
out.push([ Opcodes.call, idx ]);
|
1633
1759
|
|
1634
|
-
if (!
|
1760
|
+
if (!typedReturns) {
|
1635
1761
|
// let type;
|
1636
1762
|
// if (builtinFuncs[name]) type = TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1637
1763
|
// if (internalConstrs[name]) type = internalConstrs[name].type;
|
@@ -1643,6 +1769,10 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1643
1769
|
// );
|
1644
1770
|
} else out.push(setLastType(scope));
|
1645
1771
|
|
1772
|
+
if (builtinFuncs[name] && builtinFuncs[name].returns[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1773
|
+
out.push(Opcodes.i32_from);
|
1774
|
+
}
|
1775
|
+
|
1646
1776
|
return out;
|
1647
1777
|
};
|
1648
1778
|
|
@@ -1767,12 +1897,14 @@ const brTable = (input, bc, returns) => {
|
|
1767
1897
|
};
|
1768
1898
|
|
1769
1899
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
1900
|
+
if (!Prefs.bytestring) delete bc[TYPES._bytestring];
|
1901
|
+
|
1770
1902
|
const known = knownType(scope, type);
|
1771
1903
|
if (known != null) {
|
1772
1904
|
return bc[known] ?? bc.default;
|
1773
1905
|
}
|
1774
1906
|
|
1775
|
-
if (
|
1907
|
+
if (Prefs.typeswitchUseBrtable)
|
1776
1908
|
return brTable(type, bc, returns);
|
1777
1909
|
|
1778
1910
|
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
@@ -1807,7 +1939,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1807
1939
|
return out;
|
1808
1940
|
};
|
1809
1941
|
|
1810
|
-
const allocVar = (scope, name, global = false) => {
|
1942
|
+
const allocVar = (scope, name, global = false, type = true) => {
|
1811
1943
|
const target = global ? globals : scope.locals;
|
1812
1944
|
|
1813
1945
|
// already declared
|
@@ -1821,8 +1953,10 @@ const allocVar = (scope, name, global = false) => {
|
|
1821
1953
|
let idx = global ? globalInd++ : scope.localInd++;
|
1822
1954
|
target[name] = { idx, type: valtypeBinary };
|
1823
1955
|
|
1824
|
-
|
1825
|
-
|
1956
|
+
if (type) {
|
1957
|
+
let typeIdx = global ? globalInd++ : scope.localInd++;
|
1958
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
1959
|
+
}
|
1826
1960
|
|
1827
1961
|
return idx;
|
1828
1962
|
};
|
@@ -1842,6 +1976,7 @@ const typeAnnoToPorfType = x => {
|
|
1842
1976
|
|
1843
1977
|
switch (x) {
|
1844
1978
|
case 'i32':
|
1979
|
+
case 'i64':
|
1845
1980
|
return TYPES.number;
|
1846
1981
|
}
|
1847
1982
|
|
@@ -1865,6 +2000,8 @@ const extractTypeAnnotation = decl => {
|
|
1865
2000
|
const typeName = type;
|
1866
2001
|
type = typeAnnoToPorfType(type);
|
1867
2002
|
|
2003
|
+
if (type === TYPES._bytestring && !Prefs.bytestring) type = TYPES.string;
|
2004
|
+
|
1868
2005
|
// if (decl.name) console.log(decl.name, { type, elementType });
|
1869
2006
|
|
1870
2007
|
return { type, typeName, elementType };
|
@@ -1881,6 +2018,8 @@ const generateVar = (scope, decl) => {
|
|
1881
2018
|
for (const x of decl.declarations) {
|
1882
2019
|
const name = mapName(x.id.name);
|
1883
2020
|
|
2021
|
+
if (!name) return todo('destructuring is not supported yet');
|
2022
|
+
|
1884
2023
|
if (x.init && isFuncType(x.init.type)) {
|
1885
2024
|
// hack for let a = function () { ... }
|
1886
2025
|
x.init.id = { name };
|
@@ -1896,7 +2035,7 @@ const generateVar = (scope, decl) => {
|
|
1896
2035
|
continue; // always ignore
|
1897
2036
|
}
|
1898
2037
|
|
1899
|
-
let idx = allocVar(scope, name, global);
|
2038
|
+
let idx = allocVar(scope, name, global, !x.id.typeAnnotation);
|
1900
2039
|
|
1901
2040
|
if (typedInput && x.id.typeAnnotation) {
|
1902
2041
|
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
@@ -2019,6 +2158,8 @@ const generateAssign = (scope, decl) => {
|
|
2019
2158
|
];
|
2020
2159
|
}
|
2021
2160
|
|
2161
|
+
if (!name) return todo('destructuring is not supported yet');
|
2162
|
+
|
2022
2163
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2023
2164
|
|
2024
2165
|
if (local === undefined) {
|
@@ -2065,9 +2206,7 @@ const generateAssign = (scope, decl) => {
|
|
2065
2206
|
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
2066
2207
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
2067
2208
|
|
2068
|
-
getLastType(scope)
|
2069
|
-
// hack: type is idx+1
|
2070
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2209
|
+
...setType(scope, name, getLastType(scope))
|
2071
2210
|
];
|
2072
2211
|
}
|
2073
2212
|
|
@@ -2078,9 +2217,7 @@ const generateAssign = (scope, decl) => {
|
|
2078
2217
|
|
2079
2218
|
// todo: string concat types
|
2080
2219
|
|
2081
|
-
|
2082
|
-
...number(TYPES.number, Valtype.i32),
|
2083
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2220
|
+
...setType(scope, name, TYPES.number)
|
2084
2221
|
];
|
2085
2222
|
};
|
2086
2223
|
|
@@ -2157,6 +2294,8 @@ const generateUnary = (scope, decl) => {
|
|
2157
2294
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2158
2295
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2159
2296
|
|
2297
|
+
[TYPES._bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2298
|
+
|
2160
2299
|
// object and internal types
|
2161
2300
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
2162
2301
|
});
|
@@ -2262,8 +2401,10 @@ const generateFor = (scope, decl) => {
|
|
2262
2401
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
2263
2402
|
depth.push('for');
|
2264
2403
|
|
2265
|
-
out.push(...generate(scope, decl.test));
|
2266
|
-
|
2404
|
+
if (decl.test) out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2405
|
+
else out.push(...number(1, Valtype.i32));
|
2406
|
+
|
2407
|
+
out.push([ Opcodes.if, Blocktype.void ]);
|
2267
2408
|
depth.push('if');
|
2268
2409
|
|
2269
2410
|
out.push([ Opcodes.block, Blocktype.void ]);
|
@@ -2271,8 +2412,7 @@ const generateFor = (scope, decl) => {
|
|
2271
2412
|
out.push(...generate(scope, decl.body));
|
2272
2413
|
out.push([ Opcodes.end ]);
|
2273
2414
|
|
2274
|
-
out.push(...generate(scope, decl.update));
|
2275
|
-
depth.pop();
|
2415
|
+
if (decl.update) out.push(...generate(scope, decl.update));
|
2276
2416
|
|
2277
2417
|
out.push([ Opcodes.br, 1 ]);
|
2278
2418
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2329,7 +2469,13 @@ const generateForOf = (scope, decl) => {
|
|
2329
2469
|
// setup local for left
|
2330
2470
|
generate(scope, decl.left);
|
2331
2471
|
|
2332
|
-
|
2472
|
+
let leftName = decl.left.declarations?.[0]?.id?.name;
|
2473
|
+
if (!leftName && decl.left.name) {
|
2474
|
+
leftName = decl.left.name;
|
2475
|
+
|
2476
|
+
generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
|
2477
|
+
}
|
2478
|
+
|
2333
2479
|
const [ local, isGlobal ] = lookupName(scope, leftName);
|
2334
2480
|
|
2335
2481
|
depth.push('block');
|
@@ -2338,13 +2484,14 @@ const generateForOf = (scope, decl) => {
|
|
2338
2484
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2339
2485
|
// hack: this is naughty and will break things!
|
2340
2486
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2341
|
-
if (pages.
|
2487
|
+
if (pages.hasAnyString) {
|
2342
2488
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2343
2489
|
rawElements: new Array(1)
|
2344
2490
|
}, isGlobal, leftName, true, 'i16');
|
2345
2491
|
}
|
2346
2492
|
|
2347
2493
|
// set type for local
|
2494
|
+
// todo: optimize away counter and use end pointer
|
2348
2495
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2349
2496
|
[TYPES._array]: [
|
2350
2497
|
...setType(scope, leftName, TYPES.number),
|
@@ -2469,7 +2616,7 @@ const generateThrow = (scope, decl) => {
|
|
2469
2616
|
// hack: throw new X("...") -> throw "..."
|
2470
2617
|
if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
|
2471
2618
|
constructor = decl.argument.callee.name;
|
2472
|
-
message = decl.argument.arguments[0]
|
2619
|
+
message = decl.argument.arguments[0]?.value ?? '';
|
2473
2620
|
}
|
2474
2621
|
|
2475
2622
|
if (tags.length === 0) tags.push({
|
@@ -2481,6 +2628,9 @@ const generateThrow = (scope, decl) => {
|
|
2481
2628
|
let exceptId = exceptions.push({ constructor, message }) - 1;
|
2482
2629
|
let tagIdx = tags[0].idx;
|
2483
2630
|
|
2631
|
+
scope.exceptions ??= [];
|
2632
|
+
scope.exceptions.push(exceptId);
|
2633
|
+
|
2484
2634
|
// todo: write a description of how this works lol
|
2485
2635
|
|
2486
2636
|
return [
|
@@ -2525,25 +2675,31 @@ const generateAssignPat = (scope, decl) => {
|
|
2525
2675
|
};
|
2526
2676
|
|
2527
2677
|
let pages = new Map();
|
2528
|
-
const allocPage = (reason, type) => {
|
2678
|
+
const allocPage = (scope, reason, type) => {
|
2529
2679
|
if (pages.has(reason)) return pages.get(reason).ind;
|
2530
2680
|
|
2531
2681
|
if (reason.startsWith('array:')) pages.hasArray = true;
|
2532
2682
|
if (reason.startsWith('string:')) pages.hasString = true;
|
2683
|
+
if (reason.startsWith('bytestring:')) pages.hasByteString = true;
|
2684
|
+
if (reason.includes('string:')) pages.hasAnyString = true;
|
2533
2685
|
|
2534
2686
|
const ind = pages.size;
|
2535
2687
|
pages.set(reason, { ind, type });
|
2536
2688
|
|
2537
|
-
|
2689
|
+
scope.pages ??= new Map();
|
2690
|
+
scope.pages.set(reason, { ind, type });
|
2691
|
+
|
2692
|
+
if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
2538
2693
|
|
2539
2694
|
return ind;
|
2540
2695
|
};
|
2541
2696
|
|
2697
|
+
// todo: add scope.pages
|
2542
2698
|
const freePage = reason => {
|
2543
2699
|
const { ind } = pages.get(reason);
|
2544
2700
|
pages.delete(reason);
|
2545
2701
|
|
2546
|
-
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2702
|
+
if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2547
2703
|
|
2548
2704
|
return ind;
|
2549
2705
|
};
|
@@ -2563,25 +2719,34 @@ const StoreOps = {
|
|
2563
2719
|
f64: Opcodes.f64_store,
|
2564
2720
|
|
2565
2721
|
// expects i32 input!
|
2566
|
-
|
2722
|
+
i8: Opcodes.i32_store8,
|
2723
|
+
i16: Opcodes.i32_store16,
|
2567
2724
|
};
|
2568
2725
|
|
2569
2726
|
let data = [];
|
2570
2727
|
|
2571
|
-
const compileBytes = (val, itemType
|
2728
|
+
const compileBytes = (val, itemType) => {
|
2572
2729
|
// todo: this is a mess and needs confirming / ????
|
2573
2730
|
switch (itemType) {
|
2574
2731
|
case 'i8': return [ val % 256 ];
|
2575
|
-
case 'i16': return [ val % 256,
|
2576
|
-
|
2577
|
-
case 'i32':
|
2578
|
-
|
2579
|
-
return enforceFourBytes(signedLEB128(val));
|
2732
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
2733
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
2734
|
+
case 'i32': return [...new Uint8Array(new Int32Array([ val ]).buffer)];
|
2735
|
+
// todo: i64
|
2580
2736
|
|
2581
2737
|
case 'f64': return ieee754_binary64(val);
|
2582
2738
|
}
|
2583
2739
|
};
|
2584
2740
|
|
2741
|
+
const getAllocType = itemType => {
|
2742
|
+
switch (itemType) {
|
2743
|
+
case 'i8': return 'bytestring';
|
2744
|
+
case 'i16': return 'string';
|
2745
|
+
|
2746
|
+
default: return 'array';
|
2747
|
+
}
|
2748
|
+
};
|
2749
|
+
|
2585
2750
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2586
2751
|
const out = [];
|
2587
2752
|
|
@@ -2591,7 +2756,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2591
2756
|
|
2592
2757
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2593
2758
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2594
|
-
arrays.set(name, allocPage(`${itemType
|
2759
|
+
arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2595
2760
|
}
|
2596
2761
|
|
2597
2762
|
const pointer = arrays.get(name);
|
@@ -2602,19 +2767,25 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2602
2767
|
const valtype = itemTypeToValtype[itemType];
|
2603
2768
|
const length = elements.length;
|
2604
2769
|
|
2605
|
-
if (firstAssign && useRawElements) {
|
2606
|
-
|
2770
|
+
if (firstAssign && useRawElements && !Prefs.noData) {
|
2771
|
+
// if length is 0 memory/data will just be 0000... anyway
|
2772
|
+
if (length !== 0) {
|
2773
|
+
let bytes = compileBytes(length, 'i32');
|
2607
2774
|
|
2608
|
-
|
2609
|
-
|
2775
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
2776
|
+
if (elements[i] == null) continue;
|
2610
2777
|
|
2611
|
-
|
2612
|
-
|
2778
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
2779
|
+
}
|
2613
2780
|
|
2614
|
-
|
2615
|
-
|
2616
|
-
|
2617
|
-
|
2781
|
+
const ind = data.push({
|
2782
|
+
offset: pointer,
|
2783
|
+
bytes
|
2784
|
+
}) - 1;
|
2785
|
+
|
2786
|
+
scope.data ??= [];
|
2787
|
+
scope.data.push(ind);
|
2788
|
+
}
|
2618
2789
|
|
2619
2790
|
// local value as pointer
|
2620
2791
|
out.push(...number(pointer));
|
@@ -2637,7 +2808,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2637
2808
|
out.push(
|
2638
2809
|
...number(0, Valtype.i32),
|
2639
2810
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
2640
|
-
[ storeOp, Math.log2(ValtypeSize[itemType]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2811
|
+
[ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2641
2812
|
);
|
2642
2813
|
}
|
2643
2814
|
|
@@ -2647,15 +2818,31 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2647
2818
|
return [ out, pointer ];
|
2648
2819
|
};
|
2649
2820
|
|
2650
|
-
const
|
2821
|
+
const byteStringable = str => {
|
2822
|
+
if (!Prefs.bytestring) return false;
|
2823
|
+
|
2824
|
+
for (let i = 0; i < str.length; i++) {
|
2825
|
+
if (str.charCodeAt(i) > 0xFF) return false;
|
2826
|
+
}
|
2827
|
+
|
2828
|
+
return true;
|
2829
|
+
};
|
2830
|
+
|
2831
|
+
const makeString = (scope, str, global = false, name = '$undeclared', forceBytestring = undefined) => {
|
2651
2832
|
const rawElements = new Array(str.length);
|
2833
|
+
let byteStringable = Prefs.bytestring;
|
2652
2834
|
for (let i = 0; i < str.length; i++) {
|
2653
|
-
|
2835
|
+
const c = str.charCodeAt(i);
|
2836
|
+
rawElements[i] = c;
|
2837
|
+
|
2838
|
+
if (byteStringable && c > 0xFF) byteStringable = false;
|
2654
2839
|
}
|
2655
2840
|
|
2841
|
+
if (byteStringable && forceBytestring === false) byteStringable = false;
|
2842
|
+
|
2656
2843
|
return makeArray(scope, {
|
2657
2844
|
rawElements
|
2658
|
-
}, global, name, false, 'i16')[0];
|
2845
|
+
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2659
2846
|
};
|
2660
2847
|
|
2661
2848
|
let arrays = new Map();
|
@@ -2683,10 +2870,13 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2683
2870
|
];
|
2684
2871
|
}
|
2685
2872
|
|
2873
|
+
const object = generate(scope, decl.object);
|
2874
|
+
const property = generate(scope, decl.property);
|
2875
|
+
|
2686
2876
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2687
2877
|
// hack: this is naughty and will break things!
|
2688
2878
|
let newOut = number(0, valtypeBinary), newPointer = -1;
|
2689
|
-
if (pages.
|
2879
|
+
if (pages.hasAnyString) {
|
2690
2880
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2691
2881
|
rawElements: new Array(1)
|
2692
2882
|
}, _global, _name, true, 'i16');
|
@@ -2695,7 +2885,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2695
2885
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
2696
2886
|
[TYPES._array]: [
|
2697
2887
|
// get index as valtype
|
2698
|
-
...
|
2888
|
+
...property,
|
2699
2889
|
|
2700
2890
|
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2701
2891
|
Opcodes.i32_to_u,
|
@@ -2703,7 +2893,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2703
2893
|
[ Opcodes.i32_mul ],
|
2704
2894
|
|
2705
2895
|
...(aotPointer ? [] : [
|
2706
|
-
...
|
2896
|
+
...object,
|
2707
2897
|
Opcodes.i32_to_u,
|
2708
2898
|
[ Opcodes.i32_add ]
|
2709
2899
|
]),
|
@@ -2722,14 +2912,14 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2722
2912
|
|
2723
2913
|
...number(0, Valtype.i32), // base 0 for store later
|
2724
2914
|
|
2725
|
-
...
|
2726
|
-
|
2915
|
+
...property,
|
2727
2916
|
Opcodes.i32_to_u,
|
2917
|
+
|
2728
2918
|
...number(ValtypeSize.i16, Valtype.i32),
|
2729
2919
|
[ Opcodes.i32_mul ],
|
2730
2920
|
|
2731
2921
|
...(aotPointer ? [] : [
|
2732
|
-
...
|
2922
|
+
...object,
|
2733
2923
|
Opcodes.i32_to_u,
|
2734
2924
|
[ Opcodes.i32_add ]
|
2735
2925
|
]),
|
@@ -2746,8 +2936,36 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2746
2936
|
...number(TYPES.string, Valtype.i32),
|
2747
2937
|
setLastType(scope)
|
2748
2938
|
],
|
2939
|
+
[TYPES._bytestring]: [
|
2940
|
+
// setup new/out array
|
2941
|
+
...newOut,
|
2942
|
+
[ Opcodes.drop ],
|
2943
|
+
|
2944
|
+
...number(0, Valtype.i32), // base 0 for store later
|
2945
|
+
|
2946
|
+
...property,
|
2947
|
+
Opcodes.i32_to_u,
|
2948
|
+
|
2949
|
+
...(aotPointer ? [] : [
|
2950
|
+
...object,
|
2951
|
+
Opcodes.i32_to_u,
|
2952
|
+
[ Opcodes.i32_add ]
|
2953
|
+
]),
|
2954
|
+
|
2955
|
+
// load current string ind {arg}
|
2956
|
+
[ Opcodes.i32_load8_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2957
|
+
|
2958
|
+
// store to new string ind 0
|
2959
|
+
[ Opcodes.i32_store8, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2960
|
+
|
2961
|
+
// return new string (page)
|
2962
|
+
...number(newPointer),
|
2963
|
+
|
2964
|
+
...number(TYPES._bytestring, Valtype.i32),
|
2965
|
+
setLastType(scope)
|
2966
|
+
],
|
2749
2967
|
|
2750
|
-
default:
|
2968
|
+
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet')
|
2751
2969
|
});
|
2752
2970
|
};
|
2753
2971
|
|
@@ -2764,13 +2982,16 @@ const objectHack = node => {
|
|
2764
2982
|
// if object is not identifier or another member exp, give up
|
2765
2983
|
if (node.object.type !== 'Identifier' && node.object.type !== 'MemberExpression') return node;
|
2766
2984
|
|
2767
|
-
if (!objectName) objectName = objectHack(node.object)
|
2985
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
2768
2986
|
|
2769
2987
|
// if .length, give up (hack within a hack!)
|
2770
2988
|
if (node.property.name === 'length') return node;
|
2771
2989
|
|
2990
|
+
// no object name, give up
|
2991
|
+
if (!objectName) return node;
|
2992
|
+
|
2772
2993
|
const name = '__' + objectName + '_' + node.property.name;
|
2773
|
-
if (codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2994
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2774
2995
|
|
2775
2996
|
return {
|
2776
2997
|
type: 'Identifier',
|
@@ -2827,13 +3048,13 @@ const generateFunc = (scope, decl) => {
|
|
2827
3048
|
const func = {
|
2828
3049
|
name,
|
2829
3050
|
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
2830
|
-
|
2831
|
-
|
2832
|
-
throws: innerScope.throws,
|
2833
|
-
index: currentFuncIndex++
|
3051
|
+
index: currentFuncIndex++,
|
3052
|
+
...innerScope
|
2834
3053
|
};
|
2835
3054
|
funcIndex[name] = func.index;
|
2836
3055
|
|
3056
|
+
if (name === 'main') func.gotLastType = true;
|
3057
|
+
|
2837
3058
|
// quick hack fixes
|
2838
3059
|
for (const inst of wasm) {
|
2839
3060
|
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
@@ -2868,6 +3089,16 @@ const generateCode = (scope, decl) => {
|
|
2868
3089
|
};
|
2869
3090
|
|
2870
3091
|
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
|
+
|
2871
3102
|
Array: {
|
2872
3103
|
generate: (scope, decl, global, name) => {
|
2873
3104
|
// new Array(i0, i1, ...)
|
@@ -2989,7 +3220,7 @@ export default program => {
|
|
2989
3220
|
body: program.body
|
2990
3221
|
};
|
2991
3222
|
|
2992
|
-
if (
|
3223
|
+
if (Prefs.astLog) console.log(program.body.body);
|
2993
3224
|
|
2994
3225
|
generateFunc(scope, program);
|
2995
3226
|
|