porffor 0.2.0-a759814 → 0.2.0-af678f0
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 +49 -16
- package/compiler/2c.js +316 -71
- package/compiler/builtins.js +177 -25
- package/compiler/codeGen.js +334 -96
- package/compiler/decompile.js +3 -3
- package/compiler/index.js +15 -4
- package/compiler/opt.js +15 -3
- package/compiler/prototype.js +171 -16
- package/compiler/sections.js +1 -1
- package/compiler/wasmSpec.js +6 -2
- package/compiler/wrap.js +101 -8
- package/filesize.cmd +2 -0
- package/package.json +1 -1
- package/porf +2 -0
- package/runner/index.js +15 -2
- package/tmp.c +661 -0
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";
|
@@ -55,7 +55,7 @@ const todo = msg => {
|
|
55
55
|
};
|
56
56
|
|
57
57
|
const isFuncType = type => type === 'FunctionDeclaration' || type === 'FunctionExpression' || type === 'ArrowFunctionExpression';
|
58
|
-
const generate = (scope, decl, global = false, name = undefined) => {
|
58
|
+
const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
|
59
59
|
switch (decl.type) {
|
60
60
|
case 'BinaryExpression':
|
61
61
|
return generateBinaryExp(scope, decl, global, name);
|
@@ -68,7 +68,12 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
68
68
|
|
69
69
|
case 'ArrowFunctionExpression':
|
70
70
|
case 'FunctionDeclaration':
|
71
|
-
generateFunc(scope, decl);
|
71
|
+
const func = generateFunc(scope, decl);
|
72
|
+
|
73
|
+
if (decl.type.endsWith('Expression')) {
|
74
|
+
return number(func.index);
|
75
|
+
}
|
76
|
+
|
72
77
|
return [];
|
73
78
|
|
74
79
|
case 'BlockStatement':
|
@@ -81,7 +86,7 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
81
86
|
return generateExp(scope, decl);
|
82
87
|
|
83
88
|
case 'CallExpression':
|
84
|
-
return generateCall(scope, decl, global, name);
|
89
|
+
return generateCall(scope, decl, global, name, valueUnused);
|
85
90
|
|
86
91
|
case 'NewExpression':
|
87
92
|
return generateNew(scope, decl, global, name);
|
@@ -155,7 +160,7 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
155
160
|
|
156
161
|
case 'TaggedTemplateExpression': {
|
157
162
|
const funcs = {
|
158
|
-
|
163
|
+
__Porffor_asm: str => {
|
159
164
|
let out = [];
|
160
165
|
|
161
166
|
for (const line of str.split('\n')) {
|
@@ -191,19 +196,19 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
191
196
|
return out;
|
192
197
|
},
|
193
198
|
|
194
|
-
|
195
|
-
|
199
|
+
__Porffor_bs: str => [
|
200
|
+
...makeString(scope, str, undefined, undefined, true),
|
196
201
|
|
197
|
-
|
198
|
-
|
199
|
-
|
202
|
+
...number(TYPES._bytestring, Valtype.i32),
|
203
|
+
setLastType(scope)
|
204
|
+
],
|
205
|
+
__Porffor_s: str => [
|
206
|
+
...makeString(scope, str, undefined, undefined, false),
|
200
207
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
}
|
206
|
-
}
|
208
|
+
...number(TYPES.string, Valtype.i32),
|
209
|
+
setLastType(scope)
|
210
|
+
],
|
211
|
+
};
|
207
212
|
|
208
213
|
const name = decl.tag.name;
|
209
214
|
// hack for inline asm
|
@@ -269,25 +274,25 @@ const generateIdent = (scope, decl) => {
|
|
269
274
|
const name = mapName(rawName);
|
270
275
|
let local = scope.locals[rawName];
|
271
276
|
|
272
|
-
if (builtinVars
|
277
|
+
if (Object.hasOwn(builtinVars, name)) {
|
273
278
|
if (builtinVars[name].floatOnly && valtype[0] === 'i') throw new Error(`Cannot use ${unhackName(name)} with integer valtype`);
|
274
279
|
return builtinVars[name];
|
275
280
|
}
|
276
281
|
|
277
|
-
if (builtinFuncs
|
282
|
+
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
278
283
|
// todo: return an actual something
|
279
284
|
return number(1);
|
280
285
|
}
|
281
286
|
|
282
|
-
if (local === undefined) {
|
287
|
+
if (local?.idx === undefined) {
|
283
288
|
// no local var with name
|
284
|
-
if (
|
285
|
-
if (funcIndex
|
289
|
+
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
290
|
+
if (Object.hasOwn(funcIndex, name)) return number(funcIndex[name]);
|
286
291
|
|
287
|
-
if (globals
|
292
|
+
if (Object.hasOwn(globals, name)) return [ [ Opcodes.global_get, globals[name].idx ] ];
|
288
293
|
}
|
289
294
|
|
290
|
-
if (local === undefined && rawName.startsWith('__')) {
|
295
|
+
if (local?.idx === undefined && rawName.startsWith('__')) {
|
291
296
|
// return undefined if unknown key in already known var
|
292
297
|
let parent = rawName.slice(2).split('_').slice(0, -1).join('_');
|
293
298
|
if (parent.includes('_')) parent = '__' + parent;
|
@@ -296,7 +301,7 @@ const generateIdent = (scope, decl) => {
|
|
296
301
|
if (!parentLookup[1]) return number(UNDEFINED);
|
297
302
|
}
|
298
303
|
|
299
|
-
if (local === undefined) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
304
|
+
if (local?.idx === undefined) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
300
305
|
|
301
306
|
return [ [ Opcodes.local_get, local.idx ] ];
|
302
307
|
};
|
@@ -680,6 +685,15 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
680
685
|
[ Opcodes.i32_eqz ], */
|
681
686
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
682
687
|
],
|
688
|
+
[TYPES._bytestring]: [ // duplicate of string
|
689
|
+
[ Opcodes.local_get, tmp ],
|
690
|
+
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
691
|
+
|
692
|
+
// get length
|
693
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
694
|
+
|
695
|
+
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
696
|
+
],
|
683
697
|
default: def
|
684
698
|
}, intOut ? Valtype.i32 : valtypeBinary)
|
685
699
|
];
|
@@ -707,6 +721,17 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
707
721
|
[ Opcodes.i32_eqz ],
|
708
722
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
709
723
|
],
|
724
|
+
[TYPES._bytestring]: [ // duplicate of string
|
725
|
+
[ Opcodes.local_get, tmp ],
|
726
|
+
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
727
|
+
|
728
|
+
// get length
|
729
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
730
|
+
|
731
|
+
// if length == 0
|
732
|
+
[ Opcodes.i32_eqz ],
|
733
|
+
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
734
|
+
],
|
710
735
|
default: [
|
711
736
|
// if value == 0
|
712
737
|
[ Opcodes.local_get, tmp ],
|
@@ -900,7 +925,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
900
925
|
[ Opcodes.i32_or ],
|
901
926
|
[ Opcodes.if, Blocktype.void ],
|
902
927
|
...number(0, Valtype.i32),
|
903
|
-
[ Opcodes.br,
|
928
|
+
[ Opcodes.br, 2 ],
|
904
929
|
[ Opcodes.end ],
|
905
930
|
|
906
931
|
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
@@ -947,6 +972,18 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
947
972
|
locals[nameParam(i)] = { idx: i, type: allLocals[i] };
|
948
973
|
}
|
949
974
|
|
975
|
+
if (typeof wasm === 'function') {
|
976
|
+
const scope = {
|
977
|
+
name,
|
978
|
+
params,
|
979
|
+
locals,
|
980
|
+
returns,
|
981
|
+
localInd: allLocals.length,
|
982
|
+
};
|
983
|
+
|
984
|
+
wasm = wasm(scope, { TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString });
|
985
|
+
}
|
986
|
+
|
950
987
|
let baseGlobalIdx, i = 0;
|
951
988
|
for (const type of globalTypes) {
|
952
989
|
if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
|
@@ -1027,7 +1064,8 @@ const TYPES = {
|
|
1027
1064
|
|
1028
1065
|
// these are not "typeof" types but tracked internally
|
1029
1066
|
_array: 0x10,
|
1030
|
-
_regexp: 0x11
|
1067
|
+
_regexp: 0x11,
|
1068
|
+
_bytestring: 0x12
|
1031
1069
|
};
|
1032
1070
|
|
1033
1071
|
const TYPE_NAMES = {
|
@@ -1041,7 +1079,8 @@ const TYPE_NAMES = {
|
|
1041
1079
|
[TYPES.bigint]: 'BigInt',
|
1042
1080
|
|
1043
1081
|
[TYPES._array]: 'Array',
|
1044
|
-
[TYPES._regexp]: 'RegExp'
|
1082
|
+
[TYPES._regexp]: 'RegExp',
|
1083
|
+
[TYPES._bytestring]: 'ByteString'
|
1045
1084
|
};
|
1046
1085
|
|
1047
1086
|
const getType = (scope, _name) => {
|
@@ -1094,6 +1133,8 @@ const getNodeType = (scope, node) => {
|
|
1094
1133
|
if (node.type === 'Literal') {
|
1095
1134
|
if (node.regex) return TYPES._regexp;
|
1096
1135
|
|
1136
|
+
if (typeof node.value === 'string' && byteStringable(node.value)) return TYPES._bytestring;
|
1137
|
+
|
1097
1138
|
return TYPES[typeof node.value];
|
1098
1139
|
}
|
1099
1140
|
|
@@ -1107,6 +1148,15 @@ const getNodeType = (scope, node) => {
|
|
1107
1148
|
|
1108
1149
|
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
1109
1150
|
const name = node.callee.name;
|
1151
|
+
if (!name) {
|
1152
|
+
// iife
|
1153
|
+
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1154
|
+
|
1155
|
+
// presume
|
1156
|
+
// todo: warn here?
|
1157
|
+
return TYPES.number;
|
1158
|
+
}
|
1159
|
+
|
1110
1160
|
const func = funcs.find(x => x.name === name);
|
1111
1161
|
|
1112
1162
|
if (func) {
|
@@ -1125,7 +1175,7 @@ const getNodeType = (scope, node) => {
|
|
1125
1175
|
const spl = name.slice(2).split('_');
|
1126
1176
|
|
1127
1177
|
const func = spl[spl.length - 1];
|
1128
|
-
const protoFuncs = Object.
|
1178
|
+
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES._bytestring && prototypeFuncs[x][func] != null);
|
1129
1179
|
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1130
1180
|
}
|
1131
1181
|
|
@@ -1201,7 +1251,7 @@ const getNodeType = (scope, node) => {
|
|
1201
1251
|
if (node.operator === '!') return TYPES.boolean;
|
1202
1252
|
if (node.operator === 'void') return TYPES.undefined;
|
1203
1253
|
if (node.operator === 'delete') return TYPES.boolean;
|
1204
|
-
if (node.operator === 'typeof') return TYPES.string;
|
1254
|
+
if (node.operator === 'typeof') return process.argv.includes('-bytestring') ? TYPES._bytestring : TYPES.string;
|
1205
1255
|
|
1206
1256
|
return TYPES.number;
|
1207
1257
|
}
|
@@ -1210,7 +1260,13 @@ const getNodeType = (scope, node) => {
|
|
1210
1260
|
// hack: if something.length, number type
|
1211
1261
|
if (node.property.name === 'length') return TYPES.number;
|
1212
1262
|
|
1213
|
-
//
|
1263
|
+
// ts hack
|
1264
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES.string) return TYPES.string;
|
1265
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES._array) return TYPES.number;
|
1266
|
+
|
1267
|
+
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1268
|
+
|
1269
|
+
// presume
|
1214
1270
|
return TYPES.number;
|
1215
1271
|
}
|
1216
1272
|
|
@@ -1230,8 +1286,8 @@ const getNodeType = (scope, node) => {
|
|
1230
1286
|
const generateLiteral = (scope, decl, global, name) => {
|
1231
1287
|
if (decl.value === null) return number(NULL);
|
1232
1288
|
|
1289
|
+
// hack: just return 1 for regex literals
|
1233
1290
|
if (decl.regex) {
|
1234
|
-
scope.regex[name] = decl.regex;
|
1235
1291
|
return number(1);
|
1236
1292
|
}
|
1237
1293
|
|
@@ -1244,16 +1300,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1244
1300
|
return number(decl.value ? 1 : 0);
|
1245
1301
|
|
1246
1302
|
case 'string':
|
1247
|
-
|
1248
|
-
const rawElements = new Array(str.length);
|
1249
|
-
let j = 0;
|
1250
|
-
for (let i = 0; i < str.length; i++) {
|
1251
|
-
rawElements[i] = str.charCodeAt(i);
|
1252
|
-
}
|
1253
|
-
|
1254
|
-
return makeArray(scope, {
|
1255
|
-
rawElements
|
1256
|
-
}, global, name, false, 'i16')[0];
|
1303
|
+
return makeString(scope, decl.value, global, name);
|
1257
1304
|
|
1258
1305
|
default:
|
1259
1306
|
return todo(`cannot generate literal of type ${typeof decl.value}`);
|
@@ -1274,9 +1321,9 @@ const countLeftover = wasm => {
|
|
1274
1321
|
|
1275
1322
|
if (depth === 0)
|
1276
1323
|
if ([Opcodes.throw,Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1277
|
-
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)) {}
|
1324
|
+
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)) {}
|
1278
1325
|
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const].includes(inst[0])) count++;
|
1279
|
-
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16].includes(inst[0])) count -= 2;
|
1326
|
+
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1280
1327
|
else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
|
1281
1328
|
else if (inst[0] === Opcodes.return) count = 0;
|
1282
1329
|
else if (inst[0] === Opcodes.call) {
|
@@ -1302,7 +1349,7 @@ const disposeLeftover = wasm => {
|
|
1302
1349
|
const generateExp = (scope, decl) => {
|
1303
1350
|
const expression = decl.expression;
|
1304
1351
|
|
1305
|
-
const out = generate(scope, expression);
|
1352
|
+
const out = generate(scope, expression, undefined, undefined, true);
|
1306
1353
|
disposeLeftover(out);
|
1307
1354
|
|
1308
1355
|
return out;
|
@@ -1360,7 +1407,7 @@ const RTArrayUtil = {
|
|
1360
1407
|
]
|
1361
1408
|
};
|
1362
1409
|
|
1363
|
-
const generateCall = (scope, decl, _global, _name) => {
|
1410
|
+
const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
1364
1411
|
/* const callee = decl.callee;
|
1365
1412
|
const args = decl.arguments;
|
1366
1413
|
|
@@ -1426,8 +1473,8 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1426
1473
|
// literal.func()
|
1427
1474
|
if (!name && decl.callee.type === 'MemberExpression') {
|
1428
1475
|
// megahack for /regex/.func()
|
1429
|
-
|
1430
|
-
|
1476
|
+
const funcName = decl.callee.property.name;
|
1477
|
+
if (decl.callee.object.regex && Object.hasOwn(Rhemyn, funcName)) {
|
1431
1478
|
const func = Rhemyn[funcName](decl.callee.object.regex.pattern, currentFuncIndex++);
|
1432
1479
|
|
1433
1480
|
funcIndex[func.name] = func.index;
|
@@ -1469,8 +1516,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1469
1516
|
|
1470
1517
|
if (protoName) {
|
1471
1518
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1472
|
-
|
1473
|
-
if (f) acc[x] = f;
|
1519
|
+
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1474
1520
|
return acc;
|
1475
1521
|
}, {});
|
1476
1522
|
|
@@ -1479,10 +1525,18 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1479
1525
|
// use local for cached i32 length as commonly used
|
1480
1526
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
1481
1527
|
const pointerLocal = localTmp(scope, '__proto_pointer_cache', Valtype.i32);
|
1482
|
-
const getPointer = [ [ Opcodes.local_get, pointerLocal ] ];
|
1483
1528
|
|
1484
1529
|
// TODO: long-term, prototypes should be their individual separate funcs
|
1485
1530
|
|
1531
|
+
const rawPointer = [
|
1532
|
+
...generate(scope, target),
|
1533
|
+
Opcodes.i32_to_u
|
1534
|
+
];
|
1535
|
+
|
1536
|
+
const usePointerCache = !Object.values(protoCands).every(x => x.noPointerCache === true);
|
1537
|
+
const getPointer = usePointerCache ? [ [ Opcodes.local_get, pointerLocal ] ] : rawPointer;
|
1538
|
+
|
1539
|
+
let allOptUnused = true;
|
1486
1540
|
let lengthI32CacheUsed = false;
|
1487
1541
|
const protoBC = {};
|
1488
1542
|
for (const x in protoCands) {
|
@@ -1502,6 +1556,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1502
1556
|
const protoLocal = protoFunc.local ? localTmp(scope, `__${protoName}_tmp`, protoFunc.local) : -1;
|
1503
1557
|
const protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${protoName}_tmp2`, protoFunc.local2) : -1;
|
1504
1558
|
|
1559
|
+
let optUnused = false;
|
1505
1560
|
const protoOut = protoFunc(getPointer, {
|
1506
1561
|
getCachedI32: () => {
|
1507
1562
|
lengthI32CacheUsed = true;
|
@@ -1516,10 +1571,15 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1516
1571
|
return makeArray(scope, {
|
1517
1572
|
rawElements: new Array(length)
|
1518
1573
|
}, _global, _name, true, itemType);
|
1574
|
+
}, () => {
|
1575
|
+
optUnused = true;
|
1576
|
+
return unusedValue;
|
1519
1577
|
});
|
1520
1578
|
|
1579
|
+
if (!optUnused) allOptUnused = false;
|
1580
|
+
|
1521
1581
|
protoBC[x] = [
|
1522
|
-
[ Opcodes.block, valtypeBinary ],
|
1582
|
+
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1523
1583
|
...protoOut,
|
1524
1584
|
|
1525
1585
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
@@ -1528,11 +1588,13 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1528
1588
|
];
|
1529
1589
|
}
|
1530
1590
|
|
1531
|
-
|
1532
|
-
...generate(scope, target),
|
1591
|
+
// todo: if some cands use optUnused and some don't, we will probably crash
|
1533
1592
|
|
1534
|
-
|
1535
|
-
|
1593
|
+
return [
|
1594
|
+
...(usePointerCache ? [
|
1595
|
+
...rawPointer,
|
1596
|
+
[ Opcodes.local_set, pointerLocal ],
|
1597
|
+
] : []),
|
1536
1598
|
|
1537
1599
|
...(!lengthI32CacheUsed ? [] : [
|
1538
1600
|
...RTArrayUtil.getLengthI32(getPointer),
|
@@ -1544,7 +1606,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1544
1606
|
|
1545
1607
|
// TODO: error better
|
1546
1608
|
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1547
|
-
}, valtypeBinary),
|
1609
|
+
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1548
1610
|
];
|
1549
1611
|
}
|
1550
1612
|
}
|
@@ -1591,7 +1653,9 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1591
1653
|
const func = funcs.find(x => x.index === idx);
|
1592
1654
|
|
1593
1655
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1594
|
-
const
|
1656
|
+
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1657
|
+
const typedReturn = userFunc || builtinFuncs[name]?.typedReturn;
|
1658
|
+
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1595
1659
|
|
1596
1660
|
let args = decl.arguments;
|
1597
1661
|
if (func && args.length < paramCount) {
|
@@ -1609,12 +1673,12 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1609
1673
|
let out = [];
|
1610
1674
|
for (const arg of args) {
|
1611
1675
|
out = out.concat(generate(scope, arg));
|
1612
|
-
if (
|
1676
|
+
if (typedParams) out = out.concat(getNodeType(scope, arg));
|
1613
1677
|
}
|
1614
1678
|
|
1615
1679
|
out.push([ Opcodes.call, idx ]);
|
1616
1680
|
|
1617
|
-
if (!
|
1681
|
+
if (!typedReturn) {
|
1618
1682
|
// let type;
|
1619
1683
|
// if (builtinFuncs[name]) type = TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1620
1684
|
// if (internalConstrs[name]) type = internalConstrs[name].type;
|
@@ -1665,14 +1729,102 @@ const knownType = (scope, type) => {
|
|
1665
1729
|
return null;
|
1666
1730
|
};
|
1667
1731
|
|
1732
|
+
const brTable = (input, bc, returns) => {
|
1733
|
+
const out = [];
|
1734
|
+
const keys = Object.keys(bc);
|
1735
|
+
const count = keys.length;
|
1736
|
+
|
1737
|
+
if (count === 1) {
|
1738
|
+
// return [
|
1739
|
+
// ...input,
|
1740
|
+
// ...bc[keys[0]]
|
1741
|
+
// ];
|
1742
|
+
return bc[keys[0]];
|
1743
|
+
}
|
1744
|
+
|
1745
|
+
if (count === 2) {
|
1746
|
+
// just use if else
|
1747
|
+
const other = keys.find(x => x !== 'default');
|
1748
|
+
return [
|
1749
|
+
...input,
|
1750
|
+
...number(other, Valtype.i32),
|
1751
|
+
[ Opcodes.i32_eq ],
|
1752
|
+
[ Opcodes.if, returns ],
|
1753
|
+
...bc[other],
|
1754
|
+
[ Opcodes.else ],
|
1755
|
+
...bc.default,
|
1756
|
+
[ Opcodes.end ]
|
1757
|
+
];
|
1758
|
+
}
|
1759
|
+
|
1760
|
+
for (let i = 0; i < count; i++) {
|
1761
|
+
if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
1762
|
+
else out.push([ Opcodes.block, Blocktype.void ]);
|
1763
|
+
}
|
1764
|
+
|
1765
|
+
const nums = keys.filter(x => +x);
|
1766
|
+
const offset = Math.min(...nums);
|
1767
|
+
const max = Math.max(...nums);
|
1768
|
+
|
1769
|
+
const table = [];
|
1770
|
+
let br = 1;
|
1771
|
+
|
1772
|
+
for (let i = offset; i <= max; i++) {
|
1773
|
+
// if branch for this num, go to that block
|
1774
|
+
if (bc[i]) {
|
1775
|
+
table.push(br);
|
1776
|
+
br++;
|
1777
|
+
continue;
|
1778
|
+
}
|
1779
|
+
|
1780
|
+
// else default
|
1781
|
+
table.push(0);
|
1782
|
+
}
|
1783
|
+
|
1784
|
+
out.push(
|
1785
|
+
[ Opcodes.block, Blocktype.void ],
|
1786
|
+
...input,
|
1787
|
+
...(offset > 0 ? [
|
1788
|
+
...number(offset, Valtype.i32),
|
1789
|
+
[ Opcodes.i32_sub ]
|
1790
|
+
] : []),
|
1791
|
+
[ Opcodes.br_table, ...encodeVector(table), 0 ]
|
1792
|
+
);
|
1793
|
+
|
1794
|
+
// if you can guess why we sort the wrong way and then reverse
|
1795
|
+
// (instead of just sorting the correct way)
|
1796
|
+
// dm me and if you are correct and the first person
|
1797
|
+
// I will somehow shout you out or something
|
1798
|
+
const orderedBc = keys.sort((a, b) => b - a).reverse();
|
1799
|
+
|
1800
|
+
br = count - 1;
|
1801
|
+
for (const x of orderedBc) {
|
1802
|
+
out.push(
|
1803
|
+
[ Opcodes.end ],
|
1804
|
+
...bc[x],
|
1805
|
+
...(br === 0 ? [] : [ [ Opcodes.br, br ] ])
|
1806
|
+
);
|
1807
|
+
br--;
|
1808
|
+
}
|
1809
|
+
|
1810
|
+
return [
|
1811
|
+
...out,
|
1812
|
+
[ Opcodes.end, 'br table end' ]
|
1813
|
+
];
|
1814
|
+
};
|
1815
|
+
|
1668
1816
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
1817
|
+
if (!process.argv.includes('-bytestring')) delete bc[TYPES._bytestring];
|
1818
|
+
|
1669
1819
|
const known = knownType(scope, type);
|
1670
1820
|
if (known != null) {
|
1671
1821
|
return bc[known] ?? bc.default;
|
1672
1822
|
}
|
1673
1823
|
|
1674
|
-
|
1824
|
+
if (process.argv.includes('-typeswitch-use-brtable'))
|
1825
|
+
return brTable(type, bc, returns);
|
1675
1826
|
|
1827
|
+
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
1676
1828
|
const out = [
|
1677
1829
|
...type,
|
1678
1830
|
[ Opcodes.local_set, tmp ],
|
@@ -1762,6 +1914,8 @@ const extractTypeAnnotation = decl => {
|
|
1762
1914
|
const typeName = type;
|
1763
1915
|
type = typeAnnoToPorfType(type);
|
1764
1916
|
|
1917
|
+
if (type === TYPES._bytestring && !process.argv.includes('-bytestring')) type = TYPES.string;
|
1918
|
+
|
1765
1919
|
// if (decl.name) console.log(decl.name, { type, elementType });
|
1766
1920
|
|
1767
1921
|
return { type, typeName, elementType };
|
@@ -1778,6 +1932,8 @@ const generateVar = (scope, decl) => {
|
|
1778
1932
|
for (const x of decl.declarations) {
|
1779
1933
|
const name = mapName(x.id.name);
|
1780
1934
|
|
1935
|
+
if (!name) return todo('destructuring is not supported yet');
|
1936
|
+
|
1781
1937
|
if (x.init && isFuncType(x.init.type)) {
|
1782
1938
|
// hack for let a = function () { ... }
|
1783
1939
|
x.init.id = { name };
|
@@ -1794,6 +1950,11 @@ const generateVar = (scope, decl) => {
|
|
1794
1950
|
}
|
1795
1951
|
|
1796
1952
|
let idx = allocVar(scope, name, global);
|
1953
|
+
|
1954
|
+
if (typedInput && x.id.typeAnnotation) {
|
1955
|
+
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
1956
|
+
}
|
1957
|
+
|
1797
1958
|
if (x.init) {
|
1798
1959
|
out = out.concat(generate(scope, x.init, global, name));
|
1799
1960
|
|
@@ -1803,10 +1964,6 @@ const generateVar = (scope, decl) => {
|
|
1803
1964
|
|
1804
1965
|
// hack: this follows spec properly but is mostly unneeded 😅
|
1805
1966
|
// out.push(...setType(scope, name, x.init ? getNodeType(scope, x.init) : TYPES.undefined));
|
1806
|
-
|
1807
|
-
if (typedInput && x.id.typeAnnotation) {
|
1808
|
-
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
1809
|
-
}
|
1810
1967
|
}
|
1811
1968
|
|
1812
1969
|
return out;
|
@@ -1915,6 +2072,8 @@ const generateAssign = (scope, decl) => {
|
|
1915
2072
|
];
|
1916
2073
|
}
|
1917
2074
|
|
2075
|
+
if (!name) return todo('destructuring is not supported yet');
|
2076
|
+
|
1918
2077
|
const [ local, isGlobal ] = lookupName(scope, name);
|
1919
2078
|
|
1920
2079
|
if (local === undefined) {
|
@@ -2053,6 +2212,8 @@ const generateUnary = (scope, decl) => {
|
|
2053
2212
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2054
2213
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2055
2214
|
|
2215
|
+
[TYPES._bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2216
|
+
|
2056
2217
|
// object and internal types
|
2057
2218
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
2058
2219
|
});
|
@@ -2158,8 +2319,10 @@ const generateFor = (scope, decl) => {
|
|
2158
2319
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
2159
2320
|
depth.push('for');
|
2160
2321
|
|
2161
|
-
out.push(...generate(scope, decl.test));
|
2162
|
-
|
2322
|
+
if (decl.test) out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2323
|
+
else out.push(...number(1, Valtype.i32));
|
2324
|
+
|
2325
|
+
out.push([ Opcodes.if, Blocktype.void ]);
|
2163
2326
|
depth.push('if');
|
2164
2327
|
|
2165
2328
|
out.push([ Opcodes.block, Blocktype.void ]);
|
@@ -2167,8 +2330,7 @@ const generateFor = (scope, decl) => {
|
|
2167
2330
|
out.push(...generate(scope, decl.body));
|
2168
2331
|
out.push([ Opcodes.end ]);
|
2169
2332
|
|
2170
|
-
out.push(...generate(scope, decl.update));
|
2171
|
-
depth.pop();
|
2333
|
+
if (decl.update) out.push(...generate(scope, decl.update));
|
2172
2334
|
|
2173
2335
|
out.push([ Opcodes.br, 1 ]);
|
2174
2336
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2225,7 +2387,13 @@ const generateForOf = (scope, decl) => {
|
|
2225
2387
|
// setup local for left
|
2226
2388
|
generate(scope, decl.left);
|
2227
2389
|
|
2228
|
-
|
2390
|
+
let leftName = decl.left.declarations?.[0]?.id?.name;
|
2391
|
+
if (!leftName && decl.left.name) {
|
2392
|
+
leftName = decl.left.name;
|
2393
|
+
|
2394
|
+
generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
|
2395
|
+
}
|
2396
|
+
|
2229
2397
|
const [ local, isGlobal ] = lookupName(scope, leftName);
|
2230
2398
|
|
2231
2399
|
depth.push('block');
|
@@ -2234,13 +2402,14 @@ const generateForOf = (scope, decl) => {
|
|
2234
2402
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2235
2403
|
// hack: this is naughty and will break things!
|
2236
2404
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2237
|
-
if (pages.
|
2405
|
+
if (pages.hasAnyString) {
|
2238
2406
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2239
2407
|
rawElements: new Array(1)
|
2240
2408
|
}, isGlobal, leftName, true, 'i16');
|
2241
2409
|
}
|
2242
2410
|
|
2243
2411
|
// set type for local
|
2412
|
+
// todo: optimize away counter and use end pointer
|
2244
2413
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2245
2414
|
[TYPES._array]: [
|
2246
2415
|
...setType(scope, leftName, TYPES.number),
|
@@ -2365,7 +2534,7 @@ const generateThrow = (scope, decl) => {
|
|
2365
2534
|
// hack: throw new X("...") -> throw "..."
|
2366
2535
|
if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
|
2367
2536
|
constructor = decl.argument.callee.name;
|
2368
|
-
message = decl.argument.arguments[0]
|
2537
|
+
message = decl.argument.arguments[0]?.value ?? '';
|
2369
2538
|
}
|
2370
2539
|
|
2371
2540
|
if (tags.length === 0) tags.push({
|
@@ -2426,6 +2595,8 @@ const allocPage = (reason, type) => {
|
|
2426
2595
|
|
2427
2596
|
if (reason.startsWith('array:')) pages.hasArray = true;
|
2428
2597
|
if (reason.startsWith('string:')) pages.hasString = true;
|
2598
|
+
if (reason.startsWith('bytestring:')) pages.hasByteString = true;
|
2599
|
+
if (reason.includes('string:')) pages.hasAnyString = true;
|
2429
2600
|
|
2430
2601
|
const ind = pages.size;
|
2431
2602
|
pages.set(reason, { ind, type });
|
@@ -2459,25 +2630,34 @@ const StoreOps = {
|
|
2459
2630
|
f64: Opcodes.f64_store,
|
2460
2631
|
|
2461
2632
|
// expects i32 input!
|
2462
|
-
|
2633
|
+
i8: Opcodes.i32_store8,
|
2634
|
+
i16: Opcodes.i32_store16,
|
2463
2635
|
};
|
2464
2636
|
|
2465
2637
|
let data = [];
|
2466
2638
|
|
2467
|
-
const compileBytes = (val, itemType
|
2639
|
+
const compileBytes = (val, itemType) => {
|
2468
2640
|
// todo: this is a mess and needs confirming / ????
|
2469
2641
|
switch (itemType) {
|
2470
2642
|
case 'i8': return [ val % 256 ];
|
2471
|
-
case 'i16': return [ val % 256,
|
2472
|
-
|
2473
|
-
case 'i32':
|
2474
|
-
|
2475
|
-
return enforceFourBytes(signedLEB128(val));
|
2643
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
2644
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
2645
|
+
case 'i32': return [...new Uint8Array(new Int32Array([ val ]).buffer)];
|
2646
|
+
// todo: i64
|
2476
2647
|
|
2477
2648
|
case 'f64': return ieee754_binary64(val);
|
2478
2649
|
}
|
2479
2650
|
};
|
2480
2651
|
|
2652
|
+
const getAllocType = itemType => {
|
2653
|
+
switch (itemType) {
|
2654
|
+
case 'i8': return 'bytestring';
|
2655
|
+
case 'i16': return 'string';
|
2656
|
+
|
2657
|
+
default: return 'array';
|
2658
|
+
}
|
2659
|
+
};
|
2660
|
+
|
2481
2661
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2482
2662
|
const out = [];
|
2483
2663
|
|
@@ -2487,7 +2667,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2487
2667
|
|
2488
2668
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2489
2669
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2490
|
-
arrays.set(name, allocPage(`${itemType
|
2670
|
+
arrays.set(name, allocPage(`${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2491
2671
|
}
|
2492
2672
|
|
2493
2673
|
const pointer = arrays.get(name);
|
@@ -2533,7 +2713,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2533
2713
|
out.push(
|
2534
2714
|
...number(0, Valtype.i32),
|
2535
2715
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
2536
|
-
[ storeOp, Math.log2(ValtypeSize[itemType]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2716
|
+
[ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2537
2717
|
);
|
2538
2718
|
}
|
2539
2719
|
|
@@ -2543,15 +2723,31 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2543
2723
|
return [ out, pointer ];
|
2544
2724
|
};
|
2545
2725
|
|
2546
|
-
const
|
2726
|
+
const byteStringable = str => {
|
2727
|
+
if (!process.argv.includes('-bytestring')) return false;
|
2728
|
+
|
2729
|
+
for (let i = 0; i < str.length; i++) {
|
2730
|
+
if (str.charCodeAt(i) > 0xFF) return false;
|
2731
|
+
}
|
2732
|
+
|
2733
|
+
return true;
|
2734
|
+
};
|
2735
|
+
|
2736
|
+
const makeString = (scope, str, global = false, name = '$undeclared', forceBytestring = undefined) => {
|
2547
2737
|
const rawElements = new Array(str.length);
|
2738
|
+
let byteStringable = process.argv.includes('-bytestring');
|
2548
2739
|
for (let i = 0; i < str.length; i++) {
|
2549
|
-
|
2740
|
+
const c = str.charCodeAt(i);
|
2741
|
+
rawElements[i] = c;
|
2742
|
+
|
2743
|
+
if (byteStringable && c > 0xFF) byteStringable = false;
|
2550
2744
|
}
|
2551
2745
|
|
2746
|
+
if (byteStringable && forceBytestring === false) byteStringable = false;
|
2747
|
+
|
2552
2748
|
return makeArray(scope, {
|
2553
2749
|
rawElements
|
2554
|
-
}, global, name, false, 'i16')[0];
|
2750
|
+
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2555
2751
|
};
|
2556
2752
|
|
2557
2753
|
let arrays = new Map();
|
@@ -2579,10 +2775,13 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2579
2775
|
];
|
2580
2776
|
}
|
2581
2777
|
|
2778
|
+
const object = generate(scope, decl.object);
|
2779
|
+
const property = generate(scope, decl.property);
|
2780
|
+
|
2582
2781
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2583
2782
|
// hack: this is naughty and will break things!
|
2584
|
-
let newOut = number(0,
|
2585
|
-
if (pages.
|
2783
|
+
let newOut = number(0, valtypeBinary), newPointer = -1;
|
2784
|
+
if (pages.hasAnyString) {
|
2586
2785
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2587
2786
|
rawElements: new Array(1)
|
2588
2787
|
}, _global, _name, true, 'i16');
|
@@ -2591,7 +2790,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2591
2790
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
2592
2791
|
[TYPES._array]: [
|
2593
2792
|
// get index as valtype
|
2594
|
-
...
|
2793
|
+
...property,
|
2595
2794
|
|
2596
2795
|
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2597
2796
|
Opcodes.i32_to_u,
|
@@ -2599,7 +2798,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2599
2798
|
[ Opcodes.i32_mul ],
|
2600
2799
|
|
2601
2800
|
...(aotPointer ? [] : [
|
2602
|
-
...
|
2801
|
+
...object,
|
2603
2802
|
Opcodes.i32_to_u,
|
2604
2803
|
[ Opcodes.i32_add ]
|
2605
2804
|
]),
|
@@ -2618,14 +2817,14 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2618
2817
|
|
2619
2818
|
...number(0, Valtype.i32), // base 0 for store later
|
2620
2819
|
|
2621
|
-
...
|
2622
|
-
|
2820
|
+
...property,
|
2623
2821
|
Opcodes.i32_to_u,
|
2822
|
+
|
2624
2823
|
...number(ValtypeSize.i16, Valtype.i32),
|
2625
2824
|
[ Opcodes.i32_mul ],
|
2626
2825
|
|
2627
2826
|
...(aotPointer ? [] : [
|
2628
|
-
...
|
2827
|
+
...object,
|
2629
2828
|
Opcodes.i32_to_u,
|
2630
2829
|
[ Opcodes.i32_add ]
|
2631
2830
|
]),
|
@@ -2642,8 +2841,36 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2642
2841
|
...number(TYPES.string, Valtype.i32),
|
2643
2842
|
setLastType(scope)
|
2644
2843
|
],
|
2844
|
+
[TYPES._bytestring]: [
|
2845
|
+
// setup new/out array
|
2846
|
+
...newOut,
|
2847
|
+
[ Opcodes.drop ],
|
2848
|
+
|
2849
|
+
...number(0, Valtype.i32), // base 0 for store later
|
2850
|
+
|
2851
|
+
...property,
|
2852
|
+
Opcodes.i32_to_u,
|
2645
2853
|
|
2646
|
-
|
2854
|
+
...(aotPointer ? [] : [
|
2855
|
+
...object,
|
2856
|
+
Opcodes.i32_to_u,
|
2857
|
+
[ Opcodes.i32_add ]
|
2858
|
+
]),
|
2859
|
+
|
2860
|
+
// load current string ind {arg}
|
2861
|
+
[ Opcodes.i32_load8_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2862
|
+
|
2863
|
+
// store to new string ind 0
|
2864
|
+
[ Opcodes.i32_store8, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2865
|
+
|
2866
|
+
// return new string (page)
|
2867
|
+
...number(newPointer),
|
2868
|
+
|
2869
|
+
...number(TYPES._bytestring, Valtype.i32),
|
2870
|
+
setLastType(scope)
|
2871
|
+
],
|
2872
|
+
|
2873
|
+
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet')
|
2647
2874
|
});
|
2648
2875
|
};
|
2649
2876
|
|
@@ -2660,11 +2887,14 @@ const objectHack = node => {
|
|
2660
2887
|
// if object is not identifier or another member exp, give up
|
2661
2888
|
if (node.object.type !== 'Identifier' && node.object.type !== 'MemberExpression') return node;
|
2662
2889
|
|
2663
|
-
if (!objectName) objectName = objectHack(node.object)
|
2890
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
2664
2891
|
|
2665
2892
|
// if .length, give up (hack within a hack!)
|
2666
2893
|
if (node.property.name === 'length') return node;
|
2667
2894
|
|
2895
|
+
// no object name, give up
|
2896
|
+
if (!objectName) return node;
|
2897
|
+
|
2668
2898
|
const name = '__' + objectName + '_' + node.property.name;
|
2669
2899
|
if (codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2670
2900
|
|
@@ -2723,10 +2953,8 @@ const generateFunc = (scope, decl) => {
|
|
2723
2953
|
const func = {
|
2724
2954
|
name,
|
2725
2955
|
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
2726
|
-
|
2727
|
-
|
2728
|
-
throws: innerScope.throws,
|
2729
|
-
index: currentFuncIndex++
|
2956
|
+
index: currentFuncIndex++,
|
2957
|
+
...innerScope
|
2730
2958
|
};
|
2731
2959
|
funcIndex[name] = func.index;
|
2732
2960
|
|
@@ -2764,6 +2992,16 @@ const generateCode = (scope, decl) => {
|
|
2764
2992
|
};
|
2765
2993
|
|
2766
2994
|
const internalConstrs = {
|
2995
|
+
Boolean: {
|
2996
|
+
generate: (scope, decl) => {
|
2997
|
+
if (decl.arguments.length === 0) return number(0);
|
2998
|
+
|
2999
|
+
// should generate/run all args
|
3000
|
+
return truthy(scope, generate(scope, decl.arguments[0]), getNodeType(scope, decl.arguments[0]), false, false);
|
3001
|
+
},
|
3002
|
+
type: TYPES.boolean
|
3003
|
+
},
|
3004
|
+
|
2767
3005
|
Array: {
|
2768
3006
|
generate: (scope, decl, global, name) => {
|
2769
3007
|
// new Array(i0, i1, ...)
|