porffor 0.2.0-f2bbe1f → 0.2.0-fbab1de
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 +4 -4
- package/compiler/codeGen.js +164 -61
- package/compiler/opt.js +1 -0
- package/compiler/prototype.js +171 -16
- package/compiler/wasmSpec.js +3 -0
- package/compiler/wrap.js +12 -1
- package/package.json +1 -1
- package/r.js +9 -0
package/README.md
CHANGED
@@ -121,7 +121,7 @@ No particular order and no guarentees, just what could happen soon™
|
|
121
121
|
- *Basic* Wasm engine (interpreter) in JS
|
122
122
|
- More math operators (`**`, etc)
|
123
123
|
- `do { ... } while (...)`
|
124
|
-
-
|
124
|
+
- Typed export inputs (array)
|
125
125
|
- Exceptions
|
126
126
|
- Rewrite to use actual strings (optional?)
|
127
127
|
- `try { } finally { }`
|
@@ -130,7 +130,7 @@ No particular order and no guarentees, just what could happen soon™
|
|
130
130
|
- Rewrite local indexes per func for smallest local header and remove unused idxs
|
131
131
|
- Smarter inline selection (snapshots?)
|
132
132
|
- Remove const ifs (`if (true)`, etc)
|
133
|
-
-
|
133
|
+
- Memory alignment
|
134
134
|
- Runtime
|
135
135
|
- WASI target
|
136
136
|
- Run precompiled Wasm file if given
|
@@ -147,7 +147,7 @@ No particular order and no guarentees, just what could happen soon™
|
|
147
147
|
- Self hosted testing?
|
148
148
|
|
149
149
|
## Performance
|
150
|
-
*For the things it supports most of the time*, Porffor is blazingly fast compared to most interpreters, and common engines running without JIT. For those with JIT, it is
|
150
|
+
*For the things it supports most of the time*, Porffor is *blazingly fast* compared to most interpreters, and common engines running without JIT. For those with JIT, it is usually slower by default, but can catch up with compiler arguments and typed input.
|
151
151
|
|
152
152
|

|
153
153
|
|
@@ -215,7 +215,7 @@ Basically none right now (other than giving people headaches). Potential ideas:
|
|
215
215
|
- More in future probably?
|
216
216
|
|
217
217
|
## Usage
|
218
|
-
Basically nothing will work :). See files in `test` for examples.
|
218
|
+
Basically nothing will work :). See files in `test` and `bench` for examples.
|
219
219
|
|
220
220
|
1. Clone repo
|
221
221
|
2. `npm install`
|
package/compiler/codeGen.js
CHANGED
@@ -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);
|
@@ -86,7 +86,7 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
86
86
|
return generateExp(scope, decl);
|
87
87
|
|
88
88
|
case 'CallExpression':
|
89
|
-
return generateCall(scope, decl, global, name);
|
89
|
+
return generateCall(scope, decl, global, name, valueUnused);
|
90
90
|
|
91
91
|
case 'NewExpression':
|
92
92
|
return generateNew(scope, decl, global, name);
|
@@ -685,6 +685,15 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
685
685
|
[ Opcodes.i32_eqz ], */
|
686
686
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
687
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
|
+
],
|
688
697
|
default: def
|
689
698
|
}, intOut ? Valtype.i32 : valtypeBinary)
|
690
699
|
];
|
@@ -712,6 +721,17 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
712
721
|
[ Opcodes.i32_eqz ],
|
713
722
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
714
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
|
+
],
|
715
735
|
default: [
|
716
736
|
// if value == 0
|
717
737
|
[ Opcodes.local_get, tmp ],
|
@@ -1044,7 +1064,8 @@ const TYPES = {
|
|
1044
1064
|
|
1045
1065
|
// these are not "typeof" types but tracked internally
|
1046
1066
|
_array: 0x10,
|
1047
|
-
_regexp: 0x11
|
1067
|
+
_regexp: 0x11,
|
1068
|
+
_bytestring: 0x12
|
1048
1069
|
};
|
1049
1070
|
|
1050
1071
|
const TYPE_NAMES = {
|
@@ -1058,7 +1079,8 @@ const TYPE_NAMES = {
|
|
1058
1079
|
[TYPES.bigint]: 'BigInt',
|
1059
1080
|
|
1060
1081
|
[TYPES._array]: 'Array',
|
1061
|
-
[TYPES._regexp]: 'RegExp'
|
1082
|
+
[TYPES._regexp]: 'RegExp',
|
1083
|
+
[TYPES._bytestring]: 'ByteString'
|
1062
1084
|
};
|
1063
1085
|
|
1064
1086
|
const getType = (scope, _name) => {
|
@@ -1111,6 +1133,8 @@ const getNodeType = (scope, node) => {
|
|
1111
1133
|
if (node.type === 'Literal') {
|
1112
1134
|
if (node.regex) return TYPES._regexp;
|
1113
1135
|
|
1136
|
+
if (typeof node.value === 'string' && byteStringable(node.value)) return TYPES._bytestring;
|
1137
|
+
|
1114
1138
|
return TYPES[typeof node.value];
|
1115
1139
|
}
|
1116
1140
|
|
@@ -1124,6 +1148,15 @@ const getNodeType = (scope, node) => {
|
|
1124
1148
|
|
1125
1149
|
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
1126
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
|
+
|
1127
1160
|
const func = funcs.find(x => x.name === name);
|
1128
1161
|
|
1129
1162
|
if (func) {
|
@@ -1142,7 +1175,7 @@ const getNodeType = (scope, node) => {
|
|
1142
1175
|
const spl = name.slice(2).split('_');
|
1143
1176
|
|
1144
1177
|
const func = spl[spl.length - 1];
|
1145
|
-
const protoFuncs = Object.
|
1178
|
+
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES._bytestring && prototypeFuncs[x][func] != null);
|
1146
1179
|
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1147
1180
|
}
|
1148
1181
|
|
@@ -1218,7 +1251,7 @@ const getNodeType = (scope, node) => {
|
|
1218
1251
|
if (node.operator === '!') return TYPES.boolean;
|
1219
1252
|
if (node.operator === 'void') return TYPES.undefined;
|
1220
1253
|
if (node.operator === 'delete') return TYPES.boolean;
|
1221
|
-
if (node.operator === 'typeof') return TYPES.string;
|
1254
|
+
if (node.operator === 'typeof') return process.argv.includes('-bytestring') ? TYPES._bytestring : TYPES.string;
|
1222
1255
|
|
1223
1256
|
return TYPES.number;
|
1224
1257
|
}
|
@@ -1227,7 +1260,13 @@ const getNodeType = (scope, node) => {
|
|
1227
1260
|
// hack: if something.length, number type
|
1228
1261
|
if (node.property.name === 'length') return TYPES.number;
|
1229
1262
|
|
1230
|
-
//
|
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
|
1231
1270
|
return TYPES.number;
|
1232
1271
|
}
|
1233
1272
|
|
@@ -1244,23 +1283,6 @@ const getNodeType = (scope, node) => {
|
|
1244
1283
|
return ret;
|
1245
1284
|
};
|
1246
1285
|
|
1247
|
-
const toString = (scope, wasm, type) => {
|
1248
|
-
const tmp = localTmp(scope, '#tostring_tmp');
|
1249
|
-
return [
|
1250
|
-
...wasm,
|
1251
|
-
[ Opcodes.local_set, tmp ],
|
1252
|
-
|
1253
|
-
...typeSwitch(scope, type, {
|
1254
|
-
[TYPES.string]: [
|
1255
|
-
[ Opcodes.local_get, tmp ]
|
1256
|
-
],
|
1257
|
-
[TYPES.undefined]: [
|
1258
|
-
// [ Opcodes.]
|
1259
|
-
]
|
1260
|
-
})
|
1261
|
-
]
|
1262
|
-
};
|
1263
|
-
|
1264
1286
|
const generateLiteral = (scope, decl, global, name) => {
|
1265
1287
|
if (decl.value === null) return number(NULL);
|
1266
1288
|
|
@@ -1299,9 +1321,9 @@ const countLeftover = wasm => {
|
|
1299
1321
|
|
1300
1322
|
if (depth === 0)
|
1301
1323
|
if ([Opcodes.throw,Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1302
|
-
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)) {}
|
1303
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++;
|
1304
|
-
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;
|
1305
1327
|
else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
|
1306
1328
|
else if (inst[0] === Opcodes.return) count = 0;
|
1307
1329
|
else if (inst[0] === Opcodes.call) {
|
@@ -1327,7 +1349,7 @@ const disposeLeftover = wasm => {
|
|
1327
1349
|
const generateExp = (scope, decl) => {
|
1328
1350
|
const expression = decl.expression;
|
1329
1351
|
|
1330
|
-
const out = generate(scope, expression);
|
1352
|
+
const out = generate(scope, expression, undefined, undefined, true);
|
1331
1353
|
disposeLeftover(out);
|
1332
1354
|
|
1333
1355
|
return out;
|
@@ -1385,7 +1407,7 @@ const RTArrayUtil = {
|
|
1385
1407
|
]
|
1386
1408
|
};
|
1387
1409
|
|
1388
|
-
const generateCall = (scope, decl, _global, _name) => {
|
1410
|
+
const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
1389
1411
|
/* const callee = decl.callee;
|
1390
1412
|
const args = decl.arguments;
|
1391
1413
|
|
@@ -1451,8 +1473,8 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1451
1473
|
// literal.func()
|
1452
1474
|
if (!name && decl.callee.type === 'MemberExpression') {
|
1453
1475
|
// megahack for /regex/.func()
|
1454
|
-
|
1455
|
-
|
1476
|
+
const funcName = decl.callee.property.name;
|
1477
|
+
if (decl.callee.object.regex && Object.hasOwn(Rhemyn, funcName)) {
|
1456
1478
|
const func = Rhemyn[funcName](decl.callee.object.regex.pattern, currentFuncIndex++);
|
1457
1479
|
|
1458
1480
|
funcIndex[func.name] = func.index;
|
@@ -1494,8 +1516,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1494
1516
|
|
1495
1517
|
if (protoName) {
|
1496
1518
|
const protoCands = Object.keys(prototypeFuncs).reduce((acc, x) => {
|
1497
|
-
|
1498
|
-
if (f) acc[x] = f;
|
1519
|
+
if (Object.hasOwn(prototypeFuncs[x], protoName)) acc[x] = prototypeFuncs[x][protoName];
|
1499
1520
|
return acc;
|
1500
1521
|
}, {});
|
1501
1522
|
|
@@ -1504,10 +1525,18 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1504
1525
|
// use local for cached i32 length as commonly used
|
1505
1526
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
1506
1527
|
const pointerLocal = localTmp(scope, '__proto_pointer_cache', Valtype.i32);
|
1507
|
-
const getPointer = [ [ Opcodes.local_get, pointerLocal ] ];
|
1508
1528
|
|
1509
1529
|
// TODO: long-term, prototypes should be their individual separate funcs
|
1510
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;
|
1511
1540
|
let lengthI32CacheUsed = false;
|
1512
1541
|
const protoBC = {};
|
1513
1542
|
for (const x in protoCands) {
|
@@ -1527,6 +1556,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1527
1556
|
const protoLocal = protoFunc.local ? localTmp(scope, `__${protoName}_tmp`, protoFunc.local) : -1;
|
1528
1557
|
const protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${protoName}_tmp2`, protoFunc.local2) : -1;
|
1529
1558
|
|
1559
|
+
let optUnused = false;
|
1530
1560
|
const protoOut = protoFunc(getPointer, {
|
1531
1561
|
getCachedI32: () => {
|
1532
1562
|
lengthI32CacheUsed = true;
|
@@ -1541,10 +1571,15 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1541
1571
|
return makeArray(scope, {
|
1542
1572
|
rawElements: new Array(length)
|
1543
1573
|
}, _global, _name, true, itemType);
|
1574
|
+
}, () => {
|
1575
|
+
optUnused = true;
|
1576
|
+
return unusedValue;
|
1544
1577
|
});
|
1545
1578
|
|
1579
|
+
if (!optUnused) allOptUnused = false;
|
1580
|
+
|
1546
1581
|
protoBC[x] = [
|
1547
|
-
[ Opcodes.block, valtypeBinary ],
|
1582
|
+
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1548
1583
|
...protoOut,
|
1549
1584
|
|
1550
1585
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
@@ -1553,11 +1588,13 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1553
1588
|
];
|
1554
1589
|
}
|
1555
1590
|
|
1556
|
-
|
1557
|
-
...generate(scope, target),
|
1591
|
+
// todo: if some cands use optUnused and some don't, we will probably crash
|
1558
1592
|
|
1559
|
-
|
1560
|
-
|
1593
|
+
return [
|
1594
|
+
...(usePointerCache ? [
|
1595
|
+
...rawPointer,
|
1596
|
+
[ Opcodes.local_set, pointerLocal ],
|
1597
|
+
] : []),
|
1561
1598
|
|
1562
1599
|
...(!lengthI32CacheUsed ? [] : [
|
1563
1600
|
...RTArrayUtil.getLengthI32(getPointer),
|
@@ -1569,7 +1606,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1569
1606
|
|
1570
1607
|
// TODO: error better
|
1571
1608
|
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1572
|
-
}, valtypeBinary),
|
1609
|
+
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1573
1610
|
];
|
1574
1611
|
}
|
1575
1612
|
}
|
@@ -1777,6 +1814,8 @@ const brTable = (input, bc, returns) => {
|
|
1777
1814
|
};
|
1778
1815
|
|
1779
1816
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
1817
|
+
if (!process.argv.includes('-bytestring')) delete bc[TYPES._bytestring];
|
1818
|
+
|
1780
1819
|
const known = knownType(scope, type);
|
1781
1820
|
if (known != null) {
|
1782
1821
|
return bc[known] ?? bc.default;
|
@@ -1875,6 +1914,8 @@ const extractTypeAnnotation = decl => {
|
|
1875
1914
|
const typeName = type;
|
1876
1915
|
type = typeAnnoToPorfType(type);
|
1877
1916
|
|
1917
|
+
if (type === TYPES._bytestring && !process.argv.includes('-bytestring')) type = TYPES.string;
|
1918
|
+
|
1878
1919
|
// if (decl.name) console.log(decl.name, { type, elementType });
|
1879
1920
|
|
1880
1921
|
return { type, typeName, elementType };
|
@@ -2167,6 +2208,8 @@ const generateUnary = (scope, decl) => {
|
|
2167
2208
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2168
2209
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2169
2210
|
|
2211
|
+
[TYPES._bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2212
|
+
|
2170
2213
|
// object and internal types
|
2171
2214
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
2172
2215
|
});
|
@@ -2272,8 +2315,10 @@ const generateFor = (scope, decl) => {
|
|
2272
2315
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
2273
2316
|
depth.push('for');
|
2274
2317
|
|
2275
|
-
out.push(...generate(scope, decl.test));
|
2276
|
-
|
2318
|
+
if (decl.test) out.push(...generate(scope, decl.test), Opcodes.i32_to);
|
2319
|
+
else out.push(...number(1, Valtype.i32));
|
2320
|
+
|
2321
|
+
out.push([ Opcodes.if, Blocktype.void ]);
|
2277
2322
|
depth.push('if');
|
2278
2323
|
|
2279
2324
|
out.push([ Opcodes.block, Blocktype.void ]);
|
@@ -2281,8 +2326,7 @@ const generateFor = (scope, decl) => {
|
|
2281
2326
|
out.push(...generate(scope, decl.body));
|
2282
2327
|
out.push([ Opcodes.end ]);
|
2283
2328
|
|
2284
|
-
out.push(...generate(scope, decl.update));
|
2285
|
-
depth.pop();
|
2329
|
+
if (decl.update) out.push(...generate(scope, decl.update));
|
2286
2330
|
|
2287
2331
|
out.push([ Opcodes.br, 1 ]);
|
2288
2332
|
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
@@ -2348,13 +2392,14 @@ const generateForOf = (scope, decl) => {
|
|
2348
2392
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2349
2393
|
// hack: this is naughty and will break things!
|
2350
2394
|
let newOut = number(0, Valtype.f64), newPointer = -1;
|
2351
|
-
if (pages.
|
2395
|
+
if (pages.hasAnyString) {
|
2352
2396
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2353
2397
|
rawElements: new Array(1)
|
2354
2398
|
}, isGlobal, leftName, true, 'i16');
|
2355
2399
|
}
|
2356
2400
|
|
2357
2401
|
// set type for local
|
2402
|
+
// todo: optimize away counter and use end pointer
|
2358
2403
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2359
2404
|
[TYPES._array]: [
|
2360
2405
|
...setType(scope, leftName, TYPES.number),
|
@@ -2479,7 +2524,7 @@ const generateThrow = (scope, decl) => {
|
|
2479
2524
|
// hack: throw new X("...") -> throw "..."
|
2480
2525
|
if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
|
2481
2526
|
constructor = decl.argument.callee.name;
|
2482
|
-
message = decl.argument.arguments[0]
|
2527
|
+
message = decl.argument.arguments[0]?.value ?? '';
|
2483
2528
|
}
|
2484
2529
|
|
2485
2530
|
if (tags.length === 0) tags.push({
|
@@ -2540,6 +2585,8 @@ const allocPage = (reason, type) => {
|
|
2540
2585
|
|
2541
2586
|
if (reason.startsWith('array:')) pages.hasArray = true;
|
2542
2587
|
if (reason.startsWith('string:')) pages.hasString = true;
|
2588
|
+
if (reason.startsWith('bytestring:')) pages.hasByteString = true;
|
2589
|
+
if (reason.includes('string:')) pages.hasAnyString = true;
|
2543
2590
|
|
2544
2591
|
const ind = pages.size;
|
2545
2592
|
pages.set(reason, { ind, type });
|
@@ -2573,7 +2620,8 @@ const StoreOps = {
|
|
2573
2620
|
f64: Opcodes.f64_store,
|
2574
2621
|
|
2575
2622
|
// expects i32 input!
|
2576
|
-
|
2623
|
+
i8: Opcodes.i32_store8,
|
2624
|
+
i16: Opcodes.i32_store16,
|
2577
2625
|
};
|
2578
2626
|
|
2579
2627
|
let data = [];
|
@@ -2592,6 +2640,15 @@ const compileBytes = (val, itemType, signed = true) => {
|
|
2592
2640
|
}
|
2593
2641
|
};
|
2594
2642
|
|
2643
|
+
const getAllocType = itemType => {
|
2644
|
+
switch (itemType) {
|
2645
|
+
case 'i8': return 'bytestring';
|
2646
|
+
case 'i16': return 'string';
|
2647
|
+
|
2648
|
+
default: return 'array';
|
2649
|
+
}
|
2650
|
+
};
|
2651
|
+
|
2595
2652
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2596
2653
|
const out = [];
|
2597
2654
|
|
@@ -2601,7 +2658,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2601
2658
|
|
2602
2659
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2603
2660
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2604
|
-
arrays.set(name, allocPage(`${itemType
|
2661
|
+
arrays.set(name, allocPage(`${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2605
2662
|
}
|
2606
2663
|
|
2607
2664
|
const pointer = arrays.get(name);
|
@@ -2647,7 +2704,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2647
2704
|
out.push(
|
2648
2705
|
...number(0, Valtype.i32),
|
2649
2706
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
2650
|
-
[ storeOp, Math.log2(ValtypeSize[itemType]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2707
|
+
[ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2651
2708
|
);
|
2652
2709
|
}
|
2653
2710
|
|
@@ -2657,15 +2714,29 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2657
2714
|
return [ out, pointer ];
|
2658
2715
|
};
|
2659
2716
|
|
2717
|
+
const byteStringable = str => {
|
2718
|
+
if (!process.argv.includes('-bytestring')) return false;
|
2719
|
+
|
2720
|
+
for (let i = 0; i < str.length; i++) {
|
2721
|
+
if (str.charCodeAt(i) > 0xFF) return false;
|
2722
|
+
}
|
2723
|
+
|
2724
|
+
return true;
|
2725
|
+
};
|
2726
|
+
|
2660
2727
|
const makeString = (scope, str, global = false, name = '$undeclared') => {
|
2661
2728
|
const rawElements = new Array(str.length);
|
2729
|
+
let byteStringable = process.argv.includes('-bytestring');
|
2662
2730
|
for (let i = 0; i < str.length; i++) {
|
2663
|
-
|
2731
|
+
const c = str.charCodeAt(i);
|
2732
|
+
rawElements[i] = c;
|
2733
|
+
|
2734
|
+
if (byteStringable && c > 0xFF) byteStringable = false;
|
2664
2735
|
}
|
2665
2736
|
|
2666
2737
|
return makeArray(scope, {
|
2667
2738
|
rawElements
|
2668
|
-
}, global, name, false, 'i16')[0];
|
2739
|
+
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2669
2740
|
};
|
2670
2741
|
|
2671
2742
|
let arrays = new Map();
|
@@ -2693,10 +2764,13 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2693
2764
|
];
|
2694
2765
|
}
|
2695
2766
|
|
2767
|
+
const object = generate(scope, decl.object);
|
2768
|
+
const property = generate(scope, decl.property);
|
2769
|
+
|
2696
2770
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2697
2771
|
// hack: this is naughty and will break things!
|
2698
2772
|
let newOut = number(0, valtypeBinary), newPointer = -1;
|
2699
|
-
if (pages.
|
2773
|
+
if (pages.hasAnyString) {
|
2700
2774
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2701
2775
|
rawElements: new Array(1)
|
2702
2776
|
}, _global, _name, true, 'i16');
|
@@ -2705,7 +2779,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2705
2779
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
2706
2780
|
[TYPES._array]: [
|
2707
2781
|
// get index as valtype
|
2708
|
-
...
|
2782
|
+
...property,
|
2709
2783
|
|
2710
2784
|
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2711
2785
|
Opcodes.i32_to_u,
|
@@ -2713,7 +2787,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2713
2787
|
[ Opcodes.i32_mul ],
|
2714
2788
|
|
2715
2789
|
...(aotPointer ? [] : [
|
2716
|
-
...
|
2790
|
+
...object,
|
2717
2791
|
Opcodes.i32_to_u,
|
2718
2792
|
[ Opcodes.i32_add ]
|
2719
2793
|
]),
|
@@ -2732,14 +2806,14 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2732
2806
|
|
2733
2807
|
...number(0, Valtype.i32), // base 0 for store later
|
2734
2808
|
|
2735
|
-
...
|
2736
|
-
|
2809
|
+
...property,
|
2737
2810
|
Opcodes.i32_to_u,
|
2811
|
+
|
2738
2812
|
...number(ValtypeSize.i16, Valtype.i32),
|
2739
2813
|
[ Opcodes.i32_mul ],
|
2740
2814
|
|
2741
2815
|
...(aotPointer ? [] : [
|
2742
|
-
...
|
2816
|
+
...object,
|
2743
2817
|
Opcodes.i32_to_u,
|
2744
2818
|
[ Opcodes.i32_add ]
|
2745
2819
|
]),
|
@@ -2756,6 +2830,34 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2756
2830
|
...number(TYPES.string, Valtype.i32),
|
2757
2831
|
setLastType(scope)
|
2758
2832
|
],
|
2833
|
+
[TYPES._bytestring]: [
|
2834
|
+
// setup new/out array
|
2835
|
+
...newOut,
|
2836
|
+
[ Opcodes.drop ],
|
2837
|
+
|
2838
|
+
...number(0, Valtype.i32), // base 0 for store later
|
2839
|
+
|
2840
|
+
...property,
|
2841
|
+
Opcodes.i32_to_u,
|
2842
|
+
|
2843
|
+
...(aotPointer ? [] : [
|
2844
|
+
...object,
|
2845
|
+
Opcodes.i32_to_u,
|
2846
|
+
[ Opcodes.i32_add ]
|
2847
|
+
]),
|
2848
|
+
|
2849
|
+
// load current string ind {arg}
|
2850
|
+
[ Opcodes.i32_load8_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2851
|
+
|
2852
|
+
// store to new string ind 0
|
2853
|
+
[ Opcodes.i32_store8, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2854
|
+
|
2855
|
+
// return new string (page)
|
2856
|
+
...number(newPointer),
|
2857
|
+
|
2858
|
+
...number(TYPES._bytestring, Valtype.i32),
|
2859
|
+
setLastType(scope)
|
2860
|
+
],
|
2759
2861
|
|
2760
2862
|
default: [ [ Opcodes.unreachable ] ]
|
2761
2863
|
});
|
@@ -2774,11 +2876,14 @@ const objectHack = node => {
|
|
2774
2876
|
// if object is not identifier or another member exp, give up
|
2775
2877
|
if (node.object.type !== 'Identifier' && node.object.type !== 'MemberExpression') return node;
|
2776
2878
|
|
2777
|
-
if (!objectName) objectName = objectHack(node.object)
|
2879
|
+
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
2778
2880
|
|
2779
2881
|
// if .length, give up (hack within a hack!)
|
2780
2882
|
if (node.property.name === 'length') return node;
|
2781
2883
|
|
2884
|
+
// no object name, give up
|
2885
|
+
if (!objectName) return node;
|
2886
|
+
|
2782
2887
|
const name = '__' + objectName + '_' + node.property.name;
|
2783
2888
|
if (codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2784
2889
|
|
@@ -2837,10 +2942,8 @@ const generateFunc = (scope, decl) => {
|
|
2837
2942
|
const func = {
|
2838
2943
|
name,
|
2839
2944
|
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
2840
|
-
|
2841
|
-
|
2842
|
-
throws: innerScope.throws,
|
2843
|
-
index: currentFuncIndex++
|
2945
|
+
index: currentFuncIndex++,
|
2946
|
+
...innerScope
|
2844
2947
|
};
|
2845
2948
|
funcIndex[name] = func.index;
|
2846
2949
|
|
package/compiler/opt.js
CHANGED
@@ -192,6 +192,7 @@ export default (funcs, globals, pages, tags) => {
|
|
192
192
|
let missing = false;
|
193
193
|
if (type === 'Array') missing = !pages.hasArray;
|
194
194
|
if (type === 'String') missing = !pages.hasString;
|
195
|
+
if (type === 'ByteString') missing = !pages.hasByteString;
|
195
196
|
|
196
197
|
if (missing) {
|
197
198
|
let j = i, depth = 0;
|
package/compiler/prototype.js
CHANGED
@@ -16,7 +16,8 @@ const TYPES = {
|
|
16
16
|
|
17
17
|
// these are not "typeof" types but tracked internally
|
18
18
|
_array: 0x10,
|
19
|
-
_regexp: 0x11
|
19
|
+
_regexp: 0x11,
|
20
|
+
_bytestring: 0x12
|
20
21
|
};
|
21
22
|
|
22
23
|
// todo: turn these into built-ins once arrays and these become less hacky
|
@@ -71,7 +72,7 @@ export const PrototypeFuncs = function() {
|
|
71
72
|
],
|
72
73
|
|
73
74
|
// todo: only for 1 argument
|
74
|
-
push: (pointer, length, wNewMember) => [
|
75
|
+
push: (pointer, length, wNewMember, _1, _2, _3, unusedValue) => [
|
75
76
|
// get memory offset of array at last index (length)
|
76
77
|
...length.getCachedI32(),
|
77
78
|
...number(ValtypeSize[valtype], Valtype.i32),
|
@@ -92,22 +93,28 @@ export const PrototypeFuncs = function() {
|
|
92
93
|
...number(1, Valtype.i32),
|
93
94
|
[ Opcodes.i32_add ],
|
94
95
|
|
95
|
-
...
|
96
|
-
|
96
|
+
...(unusedValue() ? [] : [
|
97
|
+
...length.setCachedI32(),
|
98
|
+
...length.getCachedI32(),
|
99
|
+
])
|
97
100
|
]),
|
98
101
|
|
99
|
-
...
|
100
|
-
|
102
|
+
...(unusedValue() ? [] : [
|
103
|
+
...length.getCachedI32(),
|
104
|
+
Opcodes.i32_from_u
|
105
|
+
])
|
101
106
|
|
102
107
|
// ...length.get()
|
103
108
|
],
|
104
109
|
|
105
|
-
pop: (pointer, length) => [
|
110
|
+
pop: (pointer, length, _1, _2, _3, _4, unusedValue) => [
|
106
111
|
// if length == 0, noop
|
107
112
|
...length.getCachedI32(),
|
108
113
|
[ Opcodes.i32_eqz ],
|
109
114
|
[ Opcodes.if, Blocktype.void ],
|
110
|
-
...
|
115
|
+
...(unusedValue() ? [] : [
|
116
|
+
...number(UNDEFINED),
|
117
|
+
]),
|
111
118
|
[ Opcodes.br, 1 ],
|
112
119
|
[ Opcodes.end ],
|
113
120
|
|
@@ -119,19 +126,23 @@ export const PrototypeFuncs = function() {
|
|
119
126
|
...number(1, Valtype.i32),
|
120
127
|
[ Opcodes.i32_sub ],
|
121
128
|
|
122
|
-
...
|
123
|
-
|
129
|
+
...(unusedValue() ? [] : [
|
130
|
+
...length.setCachedI32(),
|
131
|
+
...length.getCachedI32(),
|
132
|
+
])
|
124
133
|
]),
|
125
134
|
|
126
135
|
// load last element
|
127
|
-
...
|
128
|
-
|
129
|
-
|
136
|
+
...(unusedValue() ? [] : [
|
137
|
+
...length.getCachedI32(),
|
138
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
139
|
+
[ Opcodes.i32_mul ],
|
130
140
|
|
131
|
-
|
132
|
-
|
141
|
+
...pointer,
|
142
|
+
[ Opcodes.i32_add ],
|
133
143
|
|
134
|
-
|
144
|
+
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
|
145
|
+
])
|
135
146
|
],
|
136
147
|
|
137
148
|
shift: (pointer, length) => [
|
@@ -472,8 +483,152 @@ export const PrototypeFuncs = function() {
|
|
472
483
|
this[TYPES.string].at.returnType = TYPES.string;
|
473
484
|
this[TYPES.string].charAt.returnType = TYPES.string;
|
474
485
|
this[TYPES.string].charCodeAt.local = Valtype.i32;
|
486
|
+
this[TYPES.string].charCodeAt.noPointerCache = zeroChecks.charcodeat;
|
475
487
|
|
476
488
|
this[TYPES.string].isWellFormed.local = Valtype.i32;
|
477
489
|
this[TYPES.string].isWellFormed.local2 = Valtype.i32;
|
478
490
|
this[TYPES.string].isWellFormed.returnType = TYPES.boolean;
|
491
|
+
|
492
|
+
if (process.argv.includes('-bytestring')) {
|
493
|
+
this[TYPES._bytestring] = {
|
494
|
+
at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
|
495
|
+
const [ newOut, newPointer ] = arrayShell(1, 'i16');
|
496
|
+
|
497
|
+
return [
|
498
|
+
// setup new/out array
|
499
|
+
...newOut,
|
500
|
+
[ Opcodes.drop ],
|
501
|
+
|
502
|
+
...number(0, Valtype.i32), // base 0 for store later
|
503
|
+
|
504
|
+
...wIndex,
|
505
|
+
Opcodes.i32_to_u,
|
506
|
+
[ Opcodes.local_tee, iTmp ],
|
507
|
+
|
508
|
+
// if index < 0: access index + array length
|
509
|
+
...number(0, Valtype.i32),
|
510
|
+
[ Opcodes.i32_lt_s ],
|
511
|
+
[ Opcodes.if, Blocktype.void ],
|
512
|
+
[ Opcodes.local_get, iTmp ],
|
513
|
+
...length.getCachedI32(),
|
514
|
+
[ Opcodes.i32_add ],
|
515
|
+
[ Opcodes.local_set, iTmp ],
|
516
|
+
[ Opcodes.end ],
|
517
|
+
|
518
|
+
// if still < 0 or >= length: return undefined
|
519
|
+
[ Opcodes.local_get, iTmp ],
|
520
|
+
...number(0, Valtype.i32),
|
521
|
+
[ Opcodes.i32_lt_s ],
|
522
|
+
|
523
|
+
[ Opcodes.local_get, iTmp ],
|
524
|
+
...length.getCachedI32(),
|
525
|
+
[ Opcodes.i32_ge_s ],
|
526
|
+
[ Opcodes.i32_or ],
|
527
|
+
|
528
|
+
[ Opcodes.if, Blocktype.void ],
|
529
|
+
...number(UNDEFINED),
|
530
|
+
[ Opcodes.br, 1 ],
|
531
|
+
[ Opcodes.end ],
|
532
|
+
|
533
|
+
[ Opcodes.local_get, iTmp ],
|
534
|
+
|
535
|
+
...pointer,
|
536
|
+
[ Opcodes.i32_add ],
|
537
|
+
|
538
|
+
// load current string ind {arg}
|
539
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
540
|
+
|
541
|
+
// store to new string ind 0
|
542
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
543
|
+
|
544
|
+
// return new string (pointer)
|
545
|
+
...number(newPointer)
|
546
|
+
];
|
547
|
+
},
|
548
|
+
|
549
|
+
// todo: out of bounds properly
|
550
|
+
charAt: (pointer, length, wIndex, _1, _2, arrayShell) => {
|
551
|
+
const [ newOut, newPointer ] = arrayShell(1, 'i16');
|
552
|
+
|
553
|
+
return [
|
554
|
+
// setup new/out array
|
555
|
+
...newOut,
|
556
|
+
[ Opcodes.drop ],
|
557
|
+
|
558
|
+
...number(0, Valtype.i32), // base 0 for store later
|
559
|
+
|
560
|
+
...wIndex,
|
561
|
+
|
562
|
+
Opcodes.i32_to,
|
563
|
+
|
564
|
+
...pointer,
|
565
|
+
[ Opcodes.i32_add ],
|
566
|
+
|
567
|
+
// load current string ind {arg}
|
568
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
569
|
+
|
570
|
+
// store to new string ind 0
|
571
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
572
|
+
|
573
|
+
// return new string (page)
|
574
|
+
...number(newPointer)
|
575
|
+
];
|
576
|
+
},
|
577
|
+
|
578
|
+
charCodeAt: (pointer, length, wIndex, iTmp) => {
|
579
|
+
return [
|
580
|
+
...wIndex,
|
581
|
+
Opcodes.i32_to,
|
582
|
+
|
583
|
+
...(zeroChecks.charcodeat ? [] : [
|
584
|
+
[ Opcodes.local_set, iTmp ],
|
585
|
+
|
586
|
+
// index < 0
|
587
|
+
...(noUnlikelyChecks ? [] : [
|
588
|
+
[ Opcodes.local_get, iTmp ],
|
589
|
+
...number(0, Valtype.i32),
|
590
|
+
[ Opcodes.i32_lt_s ],
|
591
|
+
]),
|
592
|
+
|
593
|
+
// index >= length
|
594
|
+
[ Opcodes.local_get, iTmp ],
|
595
|
+
...length.getCachedI32(),
|
596
|
+
[ Opcodes.i32_ge_s ],
|
597
|
+
|
598
|
+
...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
|
599
|
+
[ Opcodes.if, Blocktype.void ],
|
600
|
+
...number(NaN),
|
601
|
+
[ Opcodes.br, 1 ],
|
602
|
+
[ Opcodes.end ],
|
603
|
+
|
604
|
+
[ Opcodes.local_get, iTmp ],
|
605
|
+
]),
|
606
|
+
|
607
|
+
...pointer,
|
608
|
+
[ Opcodes.i32_add ],
|
609
|
+
|
610
|
+
// load current string ind {arg}
|
611
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
612
|
+
Opcodes.i32_from_u
|
613
|
+
];
|
614
|
+
},
|
615
|
+
|
616
|
+
isWellFormed: () => {
|
617
|
+
return [
|
618
|
+
// we know it must be true as it is a bytestring lol
|
619
|
+
...number(1)
|
620
|
+
]
|
621
|
+
}
|
622
|
+
};
|
623
|
+
|
624
|
+
this[TYPES._bytestring].at.local = Valtype.i32;
|
625
|
+
this[TYPES._bytestring].at.returnType = TYPES._bytestring;
|
626
|
+
this[TYPES._bytestring].charAt.returnType = TYPES._bytestring;
|
627
|
+
this[TYPES._bytestring].charCodeAt.local = Valtype.i32;
|
628
|
+
this[TYPES._bytestring].charCodeAt.noPointerCache = zeroChecks.charcodeat;
|
629
|
+
|
630
|
+
this[TYPES._bytestring].isWellFormed.local = Valtype.i32;
|
631
|
+
this[TYPES._bytestring].isWellFormed.local2 = Valtype.i32;
|
632
|
+
this[TYPES._bytestring].isWellFormed.returnType = TYPES.boolean;
|
633
|
+
}
|
479
634
|
};
|
package/compiler/wasmSpec.js
CHANGED
package/compiler/wrap.js
CHANGED
@@ -19,7 +19,8 @@ const TYPES = {
|
|
19
19
|
|
20
20
|
// internal
|
21
21
|
[internalTypeBase]: '_array',
|
22
|
-
[internalTypeBase + 1]: '_regexp'
|
22
|
+
[internalTypeBase + 1]: '_regexp',
|
23
|
+
[internalTypeBase + 2]: '_bytestring'
|
23
24
|
};
|
24
25
|
|
25
26
|
export default async (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
|
@@ -48,6 +49,9 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
48
49
|
}
|
49
50
|
});
|
50
51
|
} catch (e) {
|
52
|
+
// only backtrace for runner, not test262/etc
|
53
|
+
if (!process.argv[1].includes('/runner')) throw e;
|
54
|
+
|
51
55
|
const funcInd = parseInt(e.message.match(/function #([0-9]+) /)[1]);
|
52
56
|
const blobOffset = parseInt(e.message.split('@')[1]);
|
53
57
|
|
@@ -177,6 +181,13 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
177
181
|
return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
178
182
|
}
|
179
183
|
|
184
|
+
case '_bytestring': {
|
185
|
+
const pointer = ret;
|
186
|
+
const length = new Int32Array(memory.buffer, pointer, 1);
|
187
|
+
|
188
|
+
return Array.from(new Uint8Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
189
|
+
}
|
190
|
+
|
180
191
|
case 'function': {
|
181
192
|
// wasm func index, including all imports
|
182
193
|
const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
|
package/package.json
CHANGED