porffor 0.2.0-09999e8 → 0.2.0-1afe9b8
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 +28 -14
- package/compiler/builtins.js +151 -25
- package/compiler/codeGen.js +315 -90
- package/compiler/decompile.js +3 -3
- package/compiler/index.js +1 -1
- 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/package.json +1 -1
- package/r.js +4 -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);
|
@@ -189,19 +194,6 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
189
194
|
}
|
190
195
|
|
191
196
|
return out;
|
192
|
-
},
|
193
|
-
|
194
|
-
__internal_print_type: str => {
|
195
|
-
const type = getType(scope, str) - TYPES.number;
|
196
|
-
|
197
|
-
return [
|
198
|
-
...number(type),
|
199
|
-
[ Opcodes.call, importedFuncs.print ],
|
200
|
-
|
201
|
-
// newline
|
202
|
-
...number(10),
|
203
|
-
[ Opcodes.call, importedFuncs.printChar ]
|
204
|
-
];
|
205
197
|
}
|
206
198
|
}
|
207
199
|
|
@@ -269,25 +261,25 @@ const generateIdent = (scope, decl) => {
|
|
269
261
|
const name = mapName(rawName);
|
270
262
|
let local = scope.locals[rawName];
|
271
263
|
|
272
|
-
if (builtinVars
|
264
|
+
if (Object.hasOwn(builtinVars, name)) {
|
273
265
|
if (builtinVars[name].floatOnly && valtype[0] === 'i') throw new Error(`Cannot use ${unhackName(name)} with integer valtype`);
|
274
266
|
return builtinVars[name];
|
275
267
|
}
|
276
268
|
|
277
|
-
if (builtinFuncs
|
269
|
+
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
278
270
|
// todo: return an actual something
|
279
271
|
return number(1);
|
280
272
|
}
|
281
273
|
|
282
|
-
if (local === undefined) {
|
274
|
+
if (local?.idx === undefined) {
|
283
275
|
// no local var with name
|
284
|
-
if (
|
285
|
-
if (funcIndex
|
276
|
+
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
277
|
+
if (Object.hasOwn(funcIndex, name)) return number(funcIndex[name]);
|
286
278
|
|
287
|
-
if (globals
|
279
|
+
if (Object.hasOwn(globals, name)) return [ [ Opcodes.global_get, globals[name].idx ] ];
|
288
280
|
}
|
289
281
|
|
290
|
-
if (local === undefined && rawName.startsWith('__')) {
|
282
|
+
if (local?.idx === undefined && rawName.startsWith('__')) {
|
291
283
|
// return undefined if unknown key in already known var
|
292
284
|
let parent = rawName.slice(2).split('_').slice(0, -1).join('_');
|
293
285
|
if (parent.includes('_')) parent = '__' + parent;
|
@@ -296,7 +288,7 @@ const generateIdent = (scope, decl) => {
|
|
296
288
|
if (!parentLookup[1]) return number(UNDEFINED);
|
297
289
|
}
|
298
290
|
|
299
|
-
if (local === undefined) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
291
|
+
if (local?.idx === undefined) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
300
292
|
|
301
293
|
return [ [ Opcodes.local_get, local.idx ] ];
|
302
294
|
};
|
@@ -680,6 +672,15 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
680
672
|
[ Opcodes.i32_eqz ], */
|
681
673
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
682
674
|
],
|
675
|
+
[TYPES._bytestring]: [ // duplicate of string
|
676
|
+
[ Opcodes.local_get, tmp ],
|
677
|
+
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
678
|
+
|
679
|
+
// get length
|
680
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
681
|
+
|
682
|
+
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
683
|
+
],
|
683
684
|
default: def
|
684
685
|
}, intOut ? Valtype.i32 : valtypeBinary)
|
685
686
|
];
|
@@ -707,6 +708,17 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
707
708
|
[ Opcodes.i32_eqz ],
|
708
709
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
709
710
|
],
|
711
|
+
[TYPES._bytestring]: [ // duplicate of string
|
712
|
+
[ Opcodes.local_get, tmp ],
|
713
|
+
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
714
|
+
|
715
|
+
// get length
|
716
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
717
|
+
|
718
|
+
// if length == 0
|
719
|
+
[ Opcodes.i32_eqz ],
|
720
|
+
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
721
|
+
],
|
710
722
|
default: [
|
711
723
|
// if value == 0
|
712
724
|
[ Opcodes.local_get, tmp ],
|
@@ -947,6 +959,18 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
947
959
|
locals[nameParam(i)] = { idx: i, type: allLocals[i] };
|
948
960
|
}
|
949
961
|
|
962
|
+
if (typeof wasm === 'function') {
|
963
|
+
const scope = {
|
964
|
+
name,
|
965
|
+
params,
|
966
|
+
locals,
|
967
|
+
returns,
|
968
|
+
localInd: allLocals.length,
|
969
|
+
};
|
970
|
+
|
971
|
+
wasm = wasm(scope, { TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString });
|
972
|
+
}
|
973
|
+
|
950
974
|
let baseGlobalIdx, i = 0;
|
951
975
|
for (const type of globalTypes) {
|
952
976
|
if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
|
@@ -1027,7 +1051,8 @@ const TYPES = {
|
|
1027
1051
|
|
1028
1052
|
// these are not "typeof" types but tracked internally
|
1029
1053
|
_array: 0x10,
|
1030
|
-
_regexp: 0x11
|
1054
|
+
_regexp: 0x11,
|
1055
|
+
_bytestring: 0x12
|
1031
1056
|
};
|
1032
1057
|
|
1033
1058
|
const TYPE_NAMES = {
|
@@ -1041,7 +1066,8 @@ const TYPE_NAMES = {
|
|
1041
1066
|
[TYPES.bigint]: 'BigInt',
|
1042
1067
|
|
1043
1068
|
[TYPES._array]: 'Array',
|
1044
|
-
[TYPES._regexp]: 'RegExp'
|
1069
|
+
[TYPES._regexp]: 'RegExp',
|
1070
|
+
[TYPES._bytestring]: 'ByteString'
|
1045
1071
|
};
|
1046
1072
|
|
1047
1073
|
const getType = (scope, _name) => {
|
@@ -1094,6 +1120,8 @@ const getNodeType = (scope, node) => {
|
|
1094
1120
|
if (node.type === 'Literal') {
|
1095
1121
|
if (node.regex) return TYPES._regexp;
|
1096
1122
|
|
1123
|
+
if (typeof node.value === 'string' && byteStringable(node.value)) return TYPES._bytestring;
|
1124
|
+
|
1097
1125
|
return TYPES[typeof node.value];
|
1098
1126
|
}
|
1099
1127
|
|
@@ -1107,6 +1135,15 @@ const getNodeType = (scope, node) => {
|
|
1107
1135
|
|
1108
1136
|
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
1109
1137
|
const name = node.callee.name;
|
1138
|
+
if (!name) {
|
1139
|
+
// iife
|
1140
|
+
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1141
|
+
|
1142
|
+
// presume
|
1143
|
+
// todo: warn here?
|
1144
|
+
return TYPES.number;
|
1145
|
+
}
|
1146
|
+
|
1110
1147
|
const func = funcs.find(x => x.name === name);
|
1111
1148
|
|
1112
1149
|
if (func) {
|
@@ -1125,7 +1162,7 @@ const getNodeType = (scope, node) => {
|
|
1125
1162
|
const spl = name.slice(2).split('_');
|
1126
1163
|
|
1127
1164
|
const func = spl[spl.length - 1];
|
1128
|
-
const protoFuncs = Object.
|
1165
|
+
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES._bytestring && prototypeFuncs[x][func] != null);
|
1129
1166
|
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1130
1167
|
}
|
1131
1168
|
|
@@ -1201,7 +1238,7 @@ const getNodeType = (scope, node) => {
|
|
1201
1238
|
if (node.operator === '!') return TYPES.boolean;
|
1202
1239
|
if (node.operator === 'void') return TYPES.undefined;
|
1203
1240
|
if (node.operator === 'delete') return TYPES.boolean;
|
1204
|
-
if (node.operator === 'typeof') return TYPES.string;
|
1241
|
+
if (node.operator === 'typeof') return process.argv.includes('-bytestring') ? TYPES._bytestring : TYPES.string;
|
1205
1242
|
|
1206
1243
|
return TYPES.number;
|
1207
1244
|
}
|
@@ -1210,7 +1247,13 @@ const getNodeType = (scope, node) => {
|
|
1210
1247
|
// hack: if something.length, number type
|
1211
1248
|
if (node.property.name === 'length') return TYPES.number;
|
1212
1249
|
|
1213
|
-
//
|
1250
|
+
// ts hack
|
1251
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES.string) return TYPES.string;
|
1252
|
+
if (scope.locals[node.object.name]?.metadata?.type === TYPES._array) return TYPES.number;
|
1253
|
+
|
1254
|
+
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1255
|
+
|
1256
|
+
// presume
|
1214
1257
|
return TYPES.number;
|
1215
1258
|
}
|
1216
1259
|
|
@@ -1230,8 +1273,8 @@ const getNodeType = (scope, node) => {
|
|
1230
1273
|
const generateLiteral = (scope, decl, global, name) => {
|
1231
1274
|
if (decl.value === null) return number(NULL);
|
1232
1275
|
|
1276
|
+
// hack: just return 1 for regex literals
|
1233
1277
|
if (decl.regex) {
|
1234
|
-
scope.regex[name] = decl.regex;
|
1235
1278
|
return number(1);
|
1236
1279
|
}
|
1237
1280
|
|
@@ -1244,16 +1287,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1244
1287
|
return number(decl.value ? 1 : 0);
|
1245
1288
|
|
1246
1289
|
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];
|
1290
|
+
return makeString(scope, decl.value, global, name);
|
1257
1291
|
|
1258
1292
|
default:
|
1259
1293
|
return todo(`cannot generate literal of type ${typeof decl.value}`);
|
@@ -1274,9 +1308,9 @@ const countLeftover = wasm => {
|
|
1274
1308
|
|
1275
1309
|
if (depth === 0)
|
1276
1310
|
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)) {}
|
1311
|
+
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
1312
|
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;
|
1313
|
+
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1280
1314
|
else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
|
1281
1315
|
else if (inst[0] === Opcodes.return) count = 0;
|
1282
1316
|
else if (inst[0] === Opcodes.call) {
|
@@ -1302,7 +1336,7 @@ const disposeLeftover = wasm => {
|
|
1302
1336
|
const generateExp = (scope, decl) => {
|
1303
1337
|
const expression = decl.expression;
|
1304
1338
|
|
1305
|
-
const out = generate(scope, expression);
|
1339
|
+
const out = generate(scope, expression, undefined, undefined, true);
|
1306
1340
|
disposeLeftover(out);
|
1307
1341
|
|
1308
1342
|
return out;
|
@@ -1360,7 +1394,7 @@ const RTArrayUtil = {
|
|
1360
1394
|
]
|
1361
1395
|
};
|
1362
1396
|
|
1363
|
-
const generateCall = (scope, decl, _global, _name) => {
|
1397
|
+
const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
1364
1398
|
/* const callee = decl.callee;
|
1365
1399
|
const args = decl.arguments;
|
1366
1400
|
|
@@ -1426,8 +1460,8 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1426
1460
|
// literal.func()
|
1427
1461
|
if (!name && decl.callee.type === 'MemberExpression') {
|
1428
1462
|
// megahack for /regex/.func()
|
1429
|
-
|
1430
|
-
|
1463
|
+
const funcName = decl.callee.property.name;
|
1464
|
+
if (decl.callee.object.regex && Object.hasOwn(Rhemyn, funcName)) {
|
1431
1465
|
const func = Rhemyn[funcName](decl.callee.object.regex.pattern, currentFuncIndex++);
|
1432
1466
|
|
1433
1467
|
funcIndex[func.name] = func.index;
|
@@ -1469,8 +1503,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1469
1503
|
|
1470
1504
|
if (protoName) {
|
1471
1505
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1472
|
-
|
1473
|
-
if (f) acc[x] = f;
|
1506
|
+
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1474
1507
|
return acc;
|
1475
1508
|
}, {});
|
1476
1509
|
|
@@ -1479,10 +1512,18 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1479
1512
|
// use local for cached i32 length as commonly used
|
1480
1513
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
1481
1514
|
const pointerLocal = localTmp(scope, '__proto_pointer_cache', Valtype.i32);
|
1482
|
-
const getPointer = [ [ Opcodes.local_get, pointerLocal ] ];
|
1483
1515
|
|
1484
1516
|
// TODO: long-term, prototypes should be their individual separate funcs
|
1485
1517
|
|
1518
|
+
const rawPointer = [
|
1519
|
+
...generate(scope, target),
|
1520
|
+
Opcodes.i32_to_u
|
1521
|
+
];
|
1522
|
+
|
1523
|
+
const usePointerCache = !Object.values(protoCands).every(x => x.noPointerCache === true);
|
1524
|
+
const getPointer = usePointerCache ? [ [ Opcodes.local_get, pointerLocal ] ] : rawPointer;
|
1525
|
+
|
1526
|
+
let allOptUnused = true;
|
1486
1527
|
let lengthI32CacheUsed = false;
|
1487
1528
|
const protoBC = {};
|
1488
1529
|
for (const x in protoCands) {
|
@@ -1502,6 +1543,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1502
1543
|
const protoLocal = protoFunc.local ? localTmp(scope, `__${protoName}_tmp`, protoFunc.local) : -1;
|
1503
1544
|
const protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${protoName}_tmp2`, protoFunc.local2) : -1;
|
1504
1545
|
|
1546
|
+
let optUnused = false;
|
1505
1547
|
const protoOut = protoFunc(getPointer, {
|
1506
1548
|
getCachedI32: () => {
|
1507
1549
|
lengthI32CacheUsed = true;
|
@@ -1516,10 +1558,15 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1516
1558
|
return makeArray(scope, {
|
1517
1559
|
rawElements: new Array(length)
|
1518
1560
|
}, _global, _name, true, itemType);
|
1561
|
+
}, () => {
|
1562
|
+
optUnused = true;
|
1563
|
+
return unusedValue;
|
1519
1564
|
});
|
1520
1565
|
|
1566
|
+
if (!optUnused) allOptUnused = false;
|
1567
|
+
|
1521
1568
|
protoBC[x] = [
|
1522
|
-
[ Opcodes.block, valtypeBinary ],
|
1569
|
+
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1523
1570
|
...protoOut,
|
1524
1571
|
|
1525
1572
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
@@ -1528,11 +1575,13 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1528
1575
|
];
|
1529
1576
|
}
|
1530
1577
|
|
1531
|
-
|
1532
|
-
...generate(scope, target),
|
1578
|
+
// todo: if some cands use optUnused and some don't, we will probably crash
|
1533
1579
|
|
1534
|
-
|
1535
|
-
|
1580
|
+
return [
|
1581
|
+
...(usePointerCache ? [
|
1582
|
+
...rawPointer,
|
1583
|
+
[ Opcodes.local_set, pointerLocal ],
|
1584
|
+
] : []),
|
1536
1585
|
|
1537
1586
|
...(!lengthI32CacheUsed ? [] : [
|
1538
1587
|
...RTArrayUtil.getLengthI32(getPointer),
|
@@ -1544,7 +1593,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1544
1593
|
|
1545
1594
|
// TODO: error better
|
1546
1595
|
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1547
|
-
}, valtypeBinary),
|
1596
|
+
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1548
1597
|
];
|
1549
1598
|
}
|
1550
1599
|
}
|
@@ -1591,7 +1640,9 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1591
1640
|
const func = funcs.find(x => x.index === idx);
|
1592
1641
|
|
1593
1642
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1594
|
-
const
|
1643
|
+
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1644
|
+
const typedReturn = userFunc || builtinFuncs[name]?.typedReturn;
|
1645
|
+
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1595
1646
|
|
1596
1647
|
let args = decl.arguments;
|
1597
1648
|
if (func && args.length < paramCount) {
|
@@ -1609,12 +1660,12 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1609
1660
|
let out = [];
|
1610
1661
|
for (const arg of args) {
|
1611
1662
|
out = out.concat(generate(scope, arg));
|
1612
|
-
if (
|
1663
|
+
if (typedParams) out = out.concat(getNodeType(scope, arg));
|
1613
1664
|
}
|
1614
1665
|
|
1615
1666
|
out.push([ Opcodes.call, idx ]);
|
1616
1667
|
|
1617
|
-
if (!
|
1668
|
+
if (!typedReturn) {
|
1618
1669
|
// let type;
|
1619
1670
|
// if (builtinFuncs[name]) type = TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1620
1671
|
// if (internalConstrs[name]) type = internalConstrs[name].type;
|
@@ -1665,14 +1716,102 @@ const knownType = (scope, type) => {
|
|
1665
1716
|
return null;
|
1666
1717
|
};
|
1667
1718
|
|
1719
|
+
const brTable = (input, bc, returns) => {
|
1720
|
+
const out = [];
|
1721
|
+
const keys = Object.keys(bc);
|
1722
|
+
const count = keys.length;
|
1723
|
+
|
1724
|
+
if (count === 1) {
|
1725
|
+
// return [
|
1726
|
+
// ...input,
|
1727
|
+
// ...bc[keys[0]]
|
1728
|
+
// ];
|
1729
|
+
return bc[keys[0]];
|
1730
|
+
}
|
1731
|
+
|
1732
|
+
if (count === 2) {
|
1733
|
+
// just use if else
|
1734
|
+
const other = keys.find(x => x !== 'default');
|
1735
|
+
return [
|
1736
|
+
...input,
|
1737
|
+
...number(other, Valtype.i32),
|
1738
|
+
[ Opcodes.i32_eq ],
|
1739
|
+
[ Opcodes.if, returns ],
|
1740
|
+
...bc[other],
|
1741
|
+
[ Opcodes.else ],
|
1742
|
+
...bc.default,
|
1743
|
+
[ Opcodes.end ]
|
1744
|
+
];
|
1745
|
+
}
|
1746
|
+
|
1747
|
+
for (let i = 0; i < count; i++) {
|
1748
|
+
if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
1749
|
+
else out.push([ Opcodes.block, Blocktype.void ]);
|
1750
|
+
}
|
1751
|
+
|
1752
|
+
const nums = keys.filter(x => +x);
|
1753
|
+
const offset = Math.min(...nums);
|
1754
|
+
const max = Math.max(...nums);
|
1755
|
+
|
1756
|
+
const table = [];
|
1757
|
+
let br = 1;
|
1758
|
+
|
1759
|
+
for (let i = offset; i <= max; i++) {
|
1760
|
+
// if branch for this num, go to that block
|
1761
|
+
if (bc[i]) {
|
1762
|
+
table.push(br);
|
1763
|
+
br++;
|
1764
|
+
continue;
|
1765
|
+
}
|
1766
|
+
|
1767
|
+
// else default
|
1768
|
+
table.push(0);
|
1769
|
+
}
|
1770
|
+
|
1771
|
+
out.push(
|
1772
|
+
[ Opcodes.block, Blocktype.void ],
|
1773
|
+
...input,
|
1774
|
+
...(offset > 0 ? [
|
1775
|
+
...number(offset, Valtype.i32),
|
1776
|
+
[ Opcodes.i32_sub ]
|
1777
|
+
] : []),
|
1778
|
+
[ Opcodes.br_table, ...encodeVector(table), 0 ]
|
1779
|
+
);
|
1780
|
+
|
1781
|
+
// if you can guess why we sort the wrong way and then reverse
|
1782
|
+
// (instead of just sorting the correct way)
|
1783
|
+
// dm me and if you are correct and the first person
|
1784
|
+
// I will somehow shout you out or something
|
1785
|
+
const orderedBc = keys.sort((a, b) => b - a).reverse();
|
1786
|
+
|
1787
|
+
br = count - 1;
|
1788
|
+
for (const x of orderedBc) {
|
1789
|
+
out.push(
|
1790
|
+
[ Opcodes.end ],
|
1791
|
+
...bc[x],
|
1792
|
+
...(br === 0 ? [] : [ [ Opcodes.br, br ] ])
|
1793
|
+
);
|
1794
|
+
br--;
|
1795
|
+
}
|
1796
|
+
|
1797
|
+
return [
|
1798
|
+
...out,
|
1799
|
+
[ Opcodes.end, 'br table end' ]
|
1800
|
+
];
|
1801
|
+
};
|
1802
|
+
|
1668
1803
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
1804
|
+
if (!process.argv.includes('-bytestring')) delete bc[TYPES._bytestring];
|
1805
|
+
|
1669
1806
|
const known = knownType(scope, type);
|
1670
1807
|
if (known != null) {
|
1671
1808
|
return bc[known] ?? bc.default;
|
1672
1809
|
}
|
1673
1810
|
|
1674
|
-
|
1811
|
+
if (process.argv.includes('-typeswitch-use-brtable'))
|
1812
|
+
return brTable(type, bc, returns);
|
1675
1813
|
|
1814
|
+
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
1676
1815
|
const out = [
|
1677
1816
|
...type,
|
1678
1817
|
[ Opcodes.local_set, tmp ],
|
@@ -1759,11 +1898,14 @@ const extractTypeAnnotation = decl => {
|
|
1759
1898
|
elementType = extractTypeAnnotation(a.elementType).type;
|
1760
1899
|
}
|
1761
1900
|
|
1901
|
+
const typeName = type;
|
1762
1902
|
type = typeAnnoToPorfType(type);
|
1763
1903
|
|
1904
|
+
if (type === TYPES._bytestring && !process.argv.includes('-bytestring')) type = TYPES.string;
|
1905
|
+
|
1764
1906
|
// if (decl.name) console.log(decl.name, { type, elementType });
|
1765
1907
|
|
1766
|
-
return { type, elementType };
|
1908
|
+
return { type, typeName, elementType };
|
1767
1909
|
};
|
1768
1910
|
|
1769
1911
|
const generateVar = (scope, decl) => {
|
@@ -1777,6 +1919,8 @@ const generateVar = (scope, decl) => {
|
|
1777
1919
|
for (const x of decl.declarations) {
|
1778
1920
|
const name = mapName(x.id.name);
|
1779
1921
|
|
1922
|
+
if (!name) return todo('destructuring is not supported yet');
|
1923
|
+
|
1780
1924
|
if (x.init && isFuncType(x.init.type)) {
|
1781
1925
|
// hack for let a = function () { ... }
|
1782
1926
|
x.init.id = { name };
|
@@ -1793,6 +1937,11 @@ const generateVar = (scope, decl) => {
|
|
1793
1937
|
}
|
1794
1938
|
|
1795
1939
|
let idx = allocVar(scope, name, global);
|
1940
|
+
|
1941
|
+
if (typedInput && x.id.typeAnnotation) {
|
1942
|
+
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
1943
|
+
}
|
1944
|
+
|
1796
1945
|
if (x.init) {
|
1797
1946
|
out = out.concat(generate(scope, x.init, global, name));
|
1798
1947
|
|
@@ -1802,10 +1951,6 @@ const generateVar = (scope, decl) => {
|
|
1802
1951
|
|
1803
1952
|
// hack: this follows spec properly but is mostly unneeded 😅
|
1804
1953
|
// out.push(...setType(scope, name, x.init ? getNodeType(scope, x.init) : TYPES.undefined));
|
1805
|
-
|
1806
|
-
if (typedInput && x.id.typeAnnotation) {
|
1807
|
-
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
1808
|
-
}
|
1809
1954
|
}
|
1810
1955
|
|
1811
1956
|
return out;
|
@@ -1914,6 +2059,8 @@ const generateAssign = (scope, decl) => {
|
|
1914
2059
|
];
|
1915
2060
|
}
|
1916
2061
|
|
2062
|
+
if (!name) return todo('destructuring is not supported yet');
|
2063
|
+
|
1917
2064
|
const [ local, isGlobal ] = lookupName(scope, name);
|
1918
2065
|
|
1919
2066
|
if (local === undefined) {
|
@@ -2052,6 +2199,8 @@ const generateUnary = (scope, decl) => {
|
|
2052
2199
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2053
2200
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2054
2201
|
|
2202
|
+
[TYPES._bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2203
|
+
|
2055
2204
|
// object and internal types
|
2056
2205
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
2057
2206
|
});
|
@@ -2157,8 +2306,10 @@ const generateFor = (scope, decl) => {
|
|
2157
2306
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
2158
2307
|
depth.push('for');
|
2159
2308
|
|
2160
|
-
out.push(...generate(scope, decl.test));
|
2161
|
-
|
2309
|
+
if (decl.test) out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2310
|
+
else out.push(...number(1, Valtype.i32));
|
2311
|
+
|
2312
|
+
out.push([ Opcodes.if, Blocktype.void ]);
|
2162
2313
|
depth.push('if');
|
2163
2314
|
|
2164
2315
|
out.push([ Opcodes.block, Blocktype.void ]);
|
@@ -2166,8 +2317,7 @@ const generateFor = (scope, decl) => {
|
|
2166
2317
|
out.push(...generate(scope, decl.body));
|
2167
2318
|
out.push([ Opcodes.end ]);
|
2168
2319
|
|
2169
|
-
out.push(...generate(scope, decl.update));
|
2170
|
-
depth.pop();
|
2320
|
+
if (decl.update) out.push(...generate(scope, decl.update));
|
2171
2321
|
|
2172
2322
|
out.push([ Opcodes.br, 1 ]);
|
2173
2323
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2224,7 +2374,13 @@ const generateForOf = (scope, decl) => {
|
|
2224
2374
|
// setup local for left
|
2225
2375
|
generate(scope, decl.left);
|
2226
2376
|
|
2227
|
-
|
2377
|
+
let leftName = decl.left.declarations?.[0]?.id?.name;
|
2378
|
+
if (!leftName && decl.left.name) {
|
2379
|
+
leftName = decl.left.name;
|
2380
|
+
|
2381
|
+
generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name: leftName } } ] })
|
2382
|
+
}
|
2383
|
+
|
2228
2384
|
const [ local, isGlobal ] = lookupName(scope, leftName);
|
2229
2385
|
|
2230
2386
|
depth.push('block');
|
@@ -2233,13 +2389,14 @@ const generateForOf = (scope, decl) => {
|
|
2233
2389
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2234
2390
|
// hack: this is naughty and will break things!
|
2235
2391
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2236
|
-
if (pages.
|
2392
|
+
if (pages.hasAnyString) {
|
2237
2393
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2238
2394
|
rawElements: new Array(1)
|
2239
2395
|
}, isGlobal, leftName, true, 'i16');
|
2240
2396
|
}
|
2241
2397
|
|
2242
2398
|
// set type for local
|
2399
|
+
// todo: optimize away counter and use end pointer
|
2243
2400
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2244
2401
|
[TYPES._array]: [
|
2245
2402
|
...setType(scope, leftName, TYPES.number),
|
@@ -2364,7 +2521,7 @@ const generateThrow = (scope, decl) => {
|
|
2364
2521
|
// hack: throw new X("...") -> throw "..."
|
2365
2522
|
if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
|
2366
2523
|
constructor = decl.argument.callee.name;
|
2367
|
-
message = decl.argument.arguments[0]
|
2524
|
+
message = decl.argument.arguments[0]?.value ?? '';
|
2368
2525
|
}
|
2369
2526
|
|
2370
2527
|
if (tags.length === 0) tags.push({
|
@@ -2425,6 +2582,8 @@ const allocPage = (reason, type) => {
|
|
2425
2582
|
|
2426
2583
|
if (reason.startsWith('array:')) pages.hasArray = true;
|
2427
2584
|
if (reason.startsWith('string:')) pages.hasString = true;
|
2585
|
+
if (reason.startsWith('bytestring:')) pages.hasByteString = true;
|
2586
|
+
if (reason.includes('string:')) pages.hasAnyString = true;
|
2428
2587
|
|
2429
2588
|
const ind = pages.size;
|
2430
2589
|
pages.set(reason, { ind, type });
|
@@ -2458,7 +2617,8 @@ const StoreOps = {
|
|
2458
2617
|
f64: Opcodes.f64_store,
|
2459
2618
|
|
2460
2619
|
// expects i32 input!
|
2461
|
-
|
2620
|
+
i8: Opcodes.i32_store8,
|
2621
|
+
i16: Opcodes.i32_store16,
|
2462
2622
|
};
|
2463
2623
|
|
2464
2624
|
let data = [];
|
@@ -2477,6 +2637,15 @@ const compileBytes = (val, itemType, signed = true) => {
|
|
2477
2637
|
}
|
2478
2638
|
};
|
2479
2639
|
|
2640
|
+
const getAllocType = itemType => {
|
2641
|
+
switch (itemType) {
|
2642
|
+
case 'i8': return 'bytestring';
|
2643
|
+
case 'i16': return 'string';
|
2644
|
+
|
2645
|
+
default: return 'array';
|
2646
|
+
}
|
2647
|
+
};
|
2648
|
+
|
2480
2649
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2481
2650
|
const out = [];
|
2482
2651
|
|
@@ -2486,7 +2655,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2486
2655
|
|
2487
2656
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2488
2657
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2489
|
-
arrays.set(name, allocPage(`${itemType
|
2658
|
+
arrays.set(name, allocPage(`${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2490
2659
|
}
|
2491
2660
|
|
2492
2661
|
const pointer = arrays.get(name);
|
@@ -2532,7 +2701,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2532
2701
|
out.push(
|
2533
2702
|
...number(0, Valtype.i32),
|
2534
2703
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
2535
|
-
[ storeOp, Math.log2(ValtypeSize[itemType]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2704
|
+
[ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2536
2705
|
);
|
2537
2706
|
}
|
2538
2707
|
|
@@ -2542,15 +2711,29 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2542
2711
|
return [ out, pointer ];
|
2543
2712
|
};
|
2544
2713
|
|
2714
|
+
const byteStringable = str => {
|
2715
|
+
if (!process.argv.includes('-bytestring')) return false;
|
2716
|
+
|
2717
|
+
for (let i = 0; i < str.length; i++) {
|
2718
|
+
if (str.charCodeAt(i) > 0xFF) return false;
|
2719
|
+
}
|
2720
|
+
|
2721
|
+
return true;
|
2722
|
+
};
|
2723
|
+
|
2545
2724
|
const makeString = (scope, str, global = false, name = '$undeclared') => {
|
2546
2725
|
const rawElements = new Array(str.length);
|
2726
|
+
let byteStringable = process.argv.includes('-bytestring');
|
2547
2727
|
for (let i = 0; i < str.length; i++) {
|
2548
|
-
|
2728
|
+
const c = str.charCodeAt(i);
|
2729
|
+
rawElements[i] = c;
|
2730
|
+
|
2731
|
+
if (byteStringable && c > 0xFF) byteStringable = false;
|
2549
2732
|
}
|
2550
2733
|
|
2551
2734
|
return makeArray(scope, {
|
2552
2735
|
rawElements
|
2553
|
-
}, global, name, false, 'i16')[0];
|
2736
|
+
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2554
2737
|
};
|
2555
2738
|
|
2556
2739
|
let arrays = new Map();
|
@@ -2578,10 +2761,13 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2578
2761
|
];
|
2579
2762
|
}
|
2580
2763
|
|
2764
|
+
const object = generate(scope, decl.object);
|
2765
|
+
const property = generate(scope, decl.property);
|
2766
|
+
|
2581
2767
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2582
2768
|
// hack: this is naughty and will break things!
|
2583
|
-
let newOut = number(0,
|
2584
|
-
if (pages.
|
2769
|
+
let newOut = number(0, valtypeBinary), newPointer = -1;
|
2770
|
+
if (pages.hasAnyString) {
|
2585
2771
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2586
2772
|
rawElements: new Array(1)
|
2587
2773
|
}, _global, _name, true, 'i16');
|
@@ -2590,7 +2776,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2590
2776
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
2591
2777
|
[TYPES._array]: [
|
2592
2778
|
// get index as valtype
|
2593
|
-
...
|
2779
|
+
...property,
|
2594
2780
|
|
2595
2781
|
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2596
2782
|
Opcodes.i32_to_u,
|
@@ -2598,7 +2784,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2598
2784
|
[ Opcodes.i32_mul ],
|
2599
2785
|
|
2600
2786
|
...(aotPointer ? [] : [
|
2601
|
-
...
|
2787
|
+
...object,
|
2602
2788
|
Opcodes.i32_to_u,
|
2603
2789
|
[ Opcodes.i32_add ]
|
2604
2790
|
]),
|
@@ -2617,14 +2803,14 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2617
2803
|
|
2618
2804
|
...number(0, Valtype.i32), // base 0 for store later
|
2619
2805
|
|
2620
|
-
...
|
2621
|
-
|
2806
|
+
...property,
|
2622
2807
|
Opcodes.i32_to_u,
|
2808
|
+
|
2623
2809
|
...number(ValtypeSize.i16, Valtype.i32),
|
2624
2810
|
[ Opcodes.i32_mul ],
|
2625
2811
|
|
2626
2812
|
...(aotPointer ? [] : [
|
2627
|
-
...
|
2813
|
+
...object,
|
2628
2814
|
Opcodes.i32_to_u,
|
2629
2815
|
[ Opcodes.i32_add ]
|
2630
2816
|
]),
|
@@ -2641,8 +2827,36 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2641
2827
|
...number(TYPES.string, Valtype.i32),
|
2642
2828
|
setLastType(scope)
|
2643
2829
|
],
|
2830
|
+
[TYPES._bytestring]: [
|
2831
|
+
// setup new/out array
|
2832
|
+
...newOut,
|
2833
|
+
[ Opcodes.drop ],
|
2834
|
+
|
2835
|
+
...number(0, Valtype.i32), // base 0 for store later
|
2836
|
+
|
2837
|
+
...property,
|
2838
|
+
Opcodes.i32_to_u,
|
2839
|
+
|
2840
|
+
...(aotPointer ? [] : [
|
2841
|
+
...object,
|
2842
|
+
Opcodes.i32_to_u,
|
2843
|
+
[ Opcodes.i32_add ]
|
2844
|
+
]),
|
2845
|
+
|
2846
|
+
// load current string ind {arg}
|
2847
|
+
[ Opcodes.i32_load8_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2848
|
+
|
2849
|
+
// store to new string ind 0
|
2850
|
+
[ Opcodes.i32_store8, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2851
|
+
|
2852
|
+
// return new string (page)
|
2853
|
+
...number(newPointer),
|
2854
|
+
|
2855
|
+
...number(TYPES._bytestring, Valtype.i32),
|
2856
|
+
setLastType(scope)
|
2857
|
+
],
|
2644
2858
|
|
2645
|
-
default:
|
2859
|
+
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet')
|
2646
2860
|
});
|
2647
2861
|
};
|
2648
2862
|
|
@@ -2659,11 +2873,14 @@ const objectHack = node => {
|
|
2659
2873
|
// if object is not identifier or another member exp, give up
|
2660
2874
|
if (node.object.type !== 'Identifier' && node.object.type !== 'MemberExpression') return node;
|
2661
2875
|
|
2662
|
-
if (!objectName) objectName = objectHack(node.object)
|
2876
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
2663
2877
|
|
2664
2878
|
// if .length, give up (hack within a hack!)
|
2665
2879
|
if (node.property.name === 'length') return node;
|
2666
2880
|
|
2881
|
+
// no object name, give up
|
2882
|
+
if (!objectName) return node;
|
2883
|
+
|
2667
2884
|
const name = '__' + objectName + '_' + node.property.name;
|
2668
2885
|
if (codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2669
2886
|
|
@@ -2722,10 +2939,8 @@ const generateFunc = (scope, decl) => {
|
|
2722
2939
|
const func = {
|
2723
2940
|
name,
|
2724
2941
|
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
2725
|
-
|
2726
|
-
|
2727
|
-
throws: innerScope.throws,
|
2728
|
-
index: currentFuncIndex++
|
2942
|
+
index: currentFuncIndex++,
|
2943
|
+
...innerScope
|
2729
2944
|
};
|
2730
2945
|
funcIndex[name] = func.index;
|
2731
2946
|
|
@@ -2763,6 +2978,16 @@ const generateCode = (scope, decl) => {
|
|
2763
2978
|
};
|
2764
2979
|
|
2765
2980
|
const internalConstrs = {
|
2981
|
+
Boolean: {
|
2982
|
+
generate: (scope, decl) => {
|
2983
|
+
if (decl.arguments.length === 0) return number(0);
|
2984
|
+
|
2985
|
+
// should generate/run all args
|
2986
|
+
return truthy(scope, generate(scope, decl.arguments[0]), getNodeType(scope, decl.arguments[0]), false, false);
|
2987
|
+
},
|
2988
|
+
type: TYPES.boolean
|
2989
|
+
},
|
2990
|
+
|
2766
2991
|
Array: {
|
2767
2992
|
generate: (scope, decl, global, name) => {
|
2768
2993
|
// new Array(i0, i1, ...)
|