porffor 0.2.0-aea77ff → 0.2.0-c597461
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 +5 -2
- package/compiler/codeGen.js +128 -31
- package/compiler/prototype.js +171 -16
- package/compiler/wasmSpec.js +3 -0
- package/compiler/wrap.js +9 -1
- package/package.json +1 -1
package/README.md
CHANGED
@@ -209,10 +209,13 @@ Porffor can run Test262 via some hacks/transforms which remove unsupported featu
|
|
209
209
|
- `test262`: test262 runner and utils
|
210
210
|
|
211
211
|
## Usecases
|
212
|
-
Basically none (other than giving people headaches). Potential ideas
|
212
|
+
Basically none right now (other than giving people headaches). Potential ideas:
|
213
|
+
- Safety. As Porffor is written in JS, a memory-safe language\*, and compiles JS to Wasm, a fully sandboxed environment\*, it is quite safe. (\* These rely on the underlying implementations being secure. You could also run Wasm, or even Porffor itself, with an interpreter instead of a JIT for bonus security points too.)
|
214
|
+
- Compiling JS to native binaries. This is still very early, [`2c`](#2c) is not that good yet :(
|
215
|
+
- More in future probably?
|
213
216
|
|
214
217
|
## Usage
|
215
|
-
Basically nothing will work :). See files in `test` for examples.
|
218
|
+
Basically nothing will work :). See files in `test` and `bench` for examples.
|
216
219
|
|
217
220
|
1. Clone repo
|
218
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) {
|
@@ -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
|
}
|
@@ -1278,16 +1311,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1278
1311
|
return number(decl.value ? 1 : 0);
|
1279
1312
|
|
1280
1313
|
case 'string':
|
1281
|
-
|
1282
|
-
const rawElements = new Array(str.length);
|
1283
|
-
let j = 0;
|
1284
|
-
for (let i = 0; i < str.length; i++) {
|
1285
|
-
rawElements[i] = str.charCodeAt(i);
|
1286
|
-
}
|
1287
|
-
|
1288
|
-
return makeArray(scope, {
|
1289
|
-
rawElements
|
1290
|
-
}, global, name, false, 'i16')[0];
|
1314
|
+
return makeString(scope, decl.value, global, name);
|
1291
1315
|
|
1292
1316
|
default:
|
1293
1317
|
return todo(`cannot generate literal of type ${typeof decl.value}`);
|
@@ -1308,9 +1332,9 @@ const countLeftover = wasm => {
|
|
1308
1332
|
|
1309
1333
|
if (depth === 0)
|
1310
1334
|
if ([Opcodes.throw,Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
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.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x0a)) {}
|
1335
|
+
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)) {}
|
1312
1336
|
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const].includes(inst[0])) count++;
|
1313
|
-
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16].includes(inst[0])) count -= 2;
|
1337
|
+
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1314
1338
|
else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
|
1315
1339
|
else if (inst[0] === Opcodes.return) count = 0;
|
1316
1340
|
else if (inst[0] === Opcodes.call) {
|
@@ -1336,7 +1360,7 @@ const disposeLeftover = wasm => {
|
|
1336
1360
|
const generateExp = (scope, decl) => {
|
1337
1361
|
const expression = decl.expression;
|
1338
1362
|
|
1339
|
-
const out = generate(scope, expression);
|
1363
|
+
const out = generate(scope, expression, undefined, undefined, true);
|
1340
1364
|
disposeLeftover(out);
|
1341
1365
|
|
1342
1366
|
return out;
|
@@ -1394,7 +1418,7 @@ const RTArrayUtil = {
|
|
1394
1418
|
]
|
1395
1419
|
};
|
1396
1420
|
|
1397
|
-
const generateCall = (scope, decl, _global, _name) => {
|
1421
|
+
const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
1398
1422
|
/* const callee = decl.callee;
|
1399
1423
|
const args = decl.arguments;
|
1400
1424
|
|
@@ -1513,10 +1537,18 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1513
1537
|
// use local for cached i32 length as commonly used
|
1514
1538
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
1515
1539
|
const pointerLocal = localTmp(scope, '__proto_pointer_cache', Valtype.i32);
|
1516
|
-
const getPointer = [ [ Opcodes.local_get, pointerLocal ] ];
|
1517
1540
|
|
1518
1541
|
// TODO: long-term, prototypes should be their individual separate funcs
|
1519
1542
|
|
1543
|
+
const rawPointer = [
|
1544
|
+
...generate(scope, target),
|
1545
|
+
Opcodes.i32_to_u
|
1546
|
+
];
|
1547
|
+
|
1548
|
+
const usePointerCache = !Object.values(protoCands).every(x => x.noPointerCache === true);
|
1549
|
+
const getPointer = usePointerCache ? [ [ Opcodes.local_get, pointerLocal ] ] : rawPointer;
|
1550
|
+
|
1551
|
+
let allOptUnused = true;
|
1520
1552
|
let lengthI32CacheUsed = false;
|
1521
1553
|
const protoBC = {};
|
1522
1554
|
for (const x in protoCands) {
|
@@ -1536,6 +1568,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1536
1568
|
const protoLocal = protoFunc.local ? localTmp(scope, `__${protoName}_tmp`, protoFunc.local) : -1;
|
1537
1569
|
const protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${protoName}_tmp2`, protoFunc.local2) : -1;
|
1538
1570
|
|
1571
|
+
let optUnused = false;
|
1539
1572
|
const protoOut = protoFunc(getPointer, {
|
1540
1573
|
getCachedI32: () => {
|
1541
1574
|
lengthI32CacheUsed = true;
|
@@ -1550,10 +1583,15 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1550
1583
|
return makeArray(scope, {
|
1551
1584
|
rawElements: new Array(length)
|
1552
1585
|
}, _global, _name, true, itemType);
|
1586
|
+
}, () => {
|
1587
|
+
optUnused = true;
|
1588
|
+
return unusedValue;
|
1553
1589
|
});
|
1554
1590
|
|
1591
|
+
if (!optUnused) allOptUnused = false;
|
1592
|
+
|
1555
1593
|
protoBC[x] = [
|
1556
|
-
[ Opcodes.block, valtypeBinary ],
|
1594
|
+
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1557
1595
|
...protoOut,
|
1558
1596
|
|
1559
1597
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
@@ -1562,11 +1600,13 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1562
1600
|
];
|
1563
1601
|
}
|
1564
1602
|
|
1565
|
-
|
1566
|
-
...generate(scope, target),
|
1603
|
+
// todo: if some cands use optUnused and some don't, we will probably crash
|
1567
1604
|
|
1568
|
-
|
1569
|
-
|
1605
|
+
return [
|
1606
|
+
...(usePointerCache ? [
|
1607
|
+
...rawPointer,
|
1608
|
+
[ Opcodes.local_set, pointerLocal ],
|
1609
|
+
] : []),
|
1570
1610
|
|
1571
1611
|
...(!lengthI32CacheUsed ? [] : [
|
1572
1612
|
...RTArrayUtil.getLengthI32(getPointer),
|
@@ -1578,7 +1618,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1578
1618
|
|
1579
1619
|
// TODO: error better
|
1580
1620
|
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1581
|
-
}, valtypeBinary),
|
1621
|
+
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1582
1622
|
];
|
1583
1623
|
}
|
1584
1624
|
}
|
@@ -1786,6 +1826,8 @@ const brTable = (input, bc, returns) => {
|
|
1786
1826
|
};
|
1787
1827
|
|
1788
1828
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
1829
|
+
if (!process.argv.includes('-bytestring')) delete bc[TYPES._bytestring];
|
1830
|
+
|
1789
1831
|
const known = knownType(scope, type);
|
1790
1832
|
if (known != null) {
|
1791
1833
|
return bc[known] ?? bc.default;
|
@@ -2176,6 +2218,8 @@ const generateUnary = (scope, decl) => {
|
|
2176
2218
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2177
2219
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2178
2220
|
|
2221
|
+
[TYPES._bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2222
|
+
|
2179
2223
|
// object and internal types
|
2180
2224
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
2181
2225
|
});
|
@@ -2364,6 +2408,7 @@ const generateForOf = (scope, decl) => {
|
|
2364
2408
|
}
|
2365
2409
|
|
2366
2410
|
// set type for local
|
2411
|
+
// todo: optimize away counter and use end pointer
|
2367
2412
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2368
2413
|
[TYPES._array]: [
|
2369
2414
|
...setType(scope, leftName, TYPES.number),
|
@@ -2582,7 +2627,8 @@ const StoreOps = {
|
|
2582
2627
|
f64: Opcodes.f64_store,
|
2583
2628
|
|
2584
2629
|
// expects i32 input!
|
2585
|
-
|
2630
|
+
i8: Opcodes.i32_store8,
|
2631
|
+
i16: Opcodes.i32_store16,
|
2586
2632
|
};
|
2587
2633
|
|
2588
2634
|
let data = [];
|
@@ -2601,6 +2647,15 @@ const compileBytes = (val, itemType, signed = true) => {
|
|
2601
2647
|
}
|
2602
2648
|
};
|
2603
2649
|
|
2650
|
+
const getAllocType = itemType => {
|
2651
|
+
switch (itemType) {
|
2652
|
+
case 'i8': return 'bytestring';
|
2653
|
+
case 'i16': return 'string';
|
2654
|
+
|
2655
|
+
default: return 'array';
|
2656
|
+
}
|
2657
|
+
};
|
2658
|
+
|
2604
2659
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2605
2660
|
const out = [];
|
2606
2661
|
|
@@ -2610,7 +2665,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2610
2665
|
|
2611
2666
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2612
2667
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2613
|
-
arrays.set(name, allocPage(`${itemType
|
2668
|
+
arrays.set(name, allocPage(`${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2614
2669
|
}
|
2615
2670
|
|
2616
2671
|
const pointer = arrays.get(name);
|
@@ -2656,7 +2711,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2656
2711
|
out.push(
|
2657
2712
|
...number(0, Valtype.i32),
|
2658
2713
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
2659
|
-
[ storeOp, Math.log2(ValtypeSize[itemType]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2714
|
+
[ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2660
2715
|
);
|
2661
2716
|
}
|
2662
2717
|
|
@@ -2666,15 +2721,29 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2666
2721
|
return [ out, pointer ];
|
2667
2722
|
};
|
2668
2723
|
|
2724
|
+
const byteStringable = str => {
|
2725
|
+
if (!process.argv.includes('-bytestring')) return false;
|
2726
|
+
|
2727
|
+
for (let i = 0; i < str.length; i++) {
|
2728
|
+
if (str.charCodeAt(i) > 0xFF) return false;
|
2729
|
+
}
|
2730
|
+
|
2731
|
+
return true;
|
2732
|
+
};
|
2733
|
+
|
2669
2734
|
const makeString = (scope, str, global = false, name = '$undeclared') => {
|
2670
2735
|
const rawElements = new Array(str.length);
|
2736
|
+
let byteStringable = process.argv.includes('-bytestring');
|
2671
2737
|
for (let i = 0; i < str.length; i++) {
|
2672
|
-
|
2738
|
+
const c = str.charCodeAt(i);
|
2739
|
+
rawElements[i] = c;
|
2740
|
+
|
2741
|
+
if (byteStringable && c > 0xFF) byteStringable = false;
|
2673
2742
|
}
|
2674
2743
|
|
2675
2744
|
return makeArray(scope, {
|
2676
2745
|
rawElements
|
2677
|
-
}, global, name, false, 'i16')[0];
|
2746
|
+
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2678
2747
|
};
|
2679
2748
|
|
2680
2749
|
let arrays = new Map();
|
@@ -2765,6 +2834,34 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2765
2834
|
...number(TYPES.string, Valtype.i32),
|
2766
2835
|
setLastType(scope)
|
2767
2836
|
],
|
2837
|
+
[TYPES._bytestring]: [
|
2838
|
+
// setup new/out array
|
2839
|
+
...newOut,
|
2840
|
+
[ Opcodes.drop ],
|
2841
|
+
|
2842
|
+
...number(0, Valtype.i32), // base 0 for store later
|
2843
|
+
|
2844
|
+
...generate(scope, decl.property),
|
2845
|
+
Opcodes.i32_to_u,
|
2846
|
+
|
2847
|
+
...(aotPointer ? [] : [
|
2848
|
+
...generate(scope, decl.object),
|
2849
|
+
Opcodes.i32_to_u,
|
2850
|
+
[ Opcodes.i32_add ]
|
2851
|
+
]),
|
2852
|
+
|
2853
|
+
// load current string ind {arg}
|
2854
|
+
[ Opcodes.i32_load8_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2855
|
+
|
2856
|
+
// store to new string ind 0
|
2857
|
+
[ Opcodes.i32_store8, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
2858
|
+
|
2859
|
+
// return new string (page)
|
2860
|
+
...number(newPointer),
|
2861
|
+
|
2862
|
+
...number(TYPES._bytestring, Valtype.i32),
|
2863
|
+
setLastType(scope)
|
2864
|
+
],
|
2768
2865
|
|
2769
2866
|
default: [ [ Opcodes.unreachable ] ]
|
2770
2867
|
});
|
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)) => {
|
@@ -177,6 +178,13 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
177
178
|
return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
178
179
|
}
|
179
180
|
|
181
|
+
case '_bytestring': {
|
182
|
+
const pointer = ret;
|
183
|
+
const length = new Int32Array(memory.buffer, pointer, 1);
|
184
|
+
|
185
|
+
return Array.from(new Uint8Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
186
|
+
}
|
187
|
+
|
180
188
|
case 'function': {
|
181
189
|
// wasm func index, including all imports
|
182
190
|
const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
|
package/package.json
CHANGED