porffor 0.17.0-94400a2b8 → 0.17.0-99a1a1851

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.
@@ -4,7 +4,7 @@ import { operatorOpcode } from './expression.js';
4
4
  import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from './builtins.js';
5
5
  import { PrototypeFuncs } from './prototype.js';
6
6
  import { number } from './embedding.js';
7
- import { TYPES, TYPE_NAMES } from './types.js';
7
+ import { TYPES, TYPE_FLAGS, TYPE_NAMES, typeHasFlag } from './types.js';
8
8
  import * as Rhemyn from '../rhemyn/compile.js';
9
9
  import parse from './parse.js';
10
10
  import { log } from './log.js';
@@ -72,6 +72,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
72
72
  case 'ExpressionStatement':
73
73
  return generateExp(scope, decl);
74
74
 
75
+ case 'SequenceExpression':
76
+ return generateSequence(scope, decl);
77
+
75
78
  case 'CallExpression':
76
79
  return generateCall(scope, decl, global, name, valueUnused);
77
80
 
@@ -616,11 +619,14 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
616
619
  };
617
620
 
618
621
  const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
619
- // if (isIntToFloatOp(wasm[wasm.length - 1])) return [
620
- // ...wasm,
621
- // ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
622
- // ];
623
- // if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
622
+ if (isIntToFloatOp(wasm[wasm.length - 1])) return [
623
+ ...wasm,
624
+ ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
625
+ ];
626
+ if (isIntOp(wasm[wasm.length - 1])) return [
627
+ ...wasm,
628
+ ...(intOut ? [] : [ Opcodes.i32_from ]),
629
+ ];
624
630
 
625
631
  // todo/perf: use knownType and custom bytecode here instead of typeSwitch
626
632
 
@@ -1020,6 +1026,7 @@ const asmFuncToAsm = (func, scope) => {
1020
1026
  idx = funcIndex[n];
1021
1027
  }
1022
1028
 
1029
+ if (idx == null) throw new Error(`builtin('${n}') failed to find a func (inside ${scope.name})`);
1023
1030
  return idx;
1024
1031
  }
1025
1032
  });
@@ -1058,7 +1065,10 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1058
1065
  funcs.push(func);
1059
1066
  funcIndex[name] = func.index;
1060
1067
 
1061
- if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, func);
1068
+ if (typeof wasm === 'function') {
1069
+ if (globalThis.precompile) wasm = [];
1070
+ else wasm = asmFuncToAsm(wasm, func);
1071
+ }
1062
1072
 
1063
1073
  let baseGlobalIdx, i = 0;
1064
1074
  for (const type of globalTypes) {
@@ -1342,7 +1352,7 @@ const getNodeType = (scope, node) => {
1342
1352
  const objectKnownType = knownType(scope, getNodeType(scope, node.object));
1343
1353
  if (objectKnownType != null) {
1344
1354
  if (name === 'length') {
1345
- if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
1355
+ if (typeHasFlag(objectKnownType, TYPE_FLAGS.length)) return TYPES.number;
1346
1356
  else return TYPES.undefined;
1347
1357
  }
1348
1358
 
@@ -1414,9 +1424,9 @@ const countLeftover = wasm => {
1414
1424
 
1415
1425
  if (depth === 0)
1416
1426
  if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1417
- 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] < 0x04)) {}
1427
+ 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.f32_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] < 0x04)) {}
1418
1428
  else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const, Opcodes.memory_size].includes(inst[0])) count++;
1419
- else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
1429
+ else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.f32_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
1420
1430
  else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
1421
1431
  else if (inst[0] === Opcodes.return) count = 0;
1422
1432
  else if (inst[0] === Opcodes.call) {
@@ -1454,6 +1464,18 @@ const generateExp = (scope, decl) => {
1454
1464
  return out;
1455
1465
  };
1456
1466
 
1467
+ const generateSequence = (scope, decl) => {
1468
+ let out = [];
1469
+
1470
+ const exprs = decl.expressions;
1471
+ for (let i = 0; i < exprs.length; i++) {
1472
+ if (i > 0) disposeLeftover(out);
1473
+ out.push(...generate(scope, exprs[i]));
1474
+ }
1475
+
1476
+ return out;
1477
+ };
1478
+
1457
1479
  const CTArrayUtil = {
1458
1480
  getLengthI32: pointer => [
1459
1481
  ...number(0, Valtype.i32),
@@ -1572,9 +1594,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1572
1594
  if (!name && decl.callee.type === 'MemberExpression') {
1573
1595
  // megahack for /regex/.func()
1574
1596
  const funcName = decl.callee.property.name;
1575
- if (decl.callee.object.regex && Object.hasOwn(Rhemyn, funcName)) {
1597
+ if (decl.callee.object.regex && ['test'].includes(funcName)) {
1576
1598
  const regex = decl.callee.object.regex.pattern;
1577
- const rhemynName = `regex_${funcName}_${regex}`;
1599
+ const rhemynName = `regex_${funcName}_${sanitize(regex)}`;
1578
1600
 
1579
1601
  if (!funcIndex[rhemynName]) {
1580
1602
  const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
@@ -1595,7 +1617,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1595
1617
  [ Opcodes.call, idx ],
1596
1618
  Opcodes.i32_from_u,
1597
1619
 
1598
- ...setLastType(scope, TYPES.boolean)
1620
+ ...setLastType(scope, Rhemyn.types[funcName])
1599
1621
  ];
1600
1622
  }
1601
1623
 
@@ -1604,24 +1626,44 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1604
1626
  target = decl.callee.object;
1605
1627
  }
1606
1628
 
1607
- // if (protoName && baseType === TYPES.string && Rhemyn[protoName]) {
1608
- // const func = Rhemyn[protoName](decl.arguments[0].regex.pattern, currentFuncIndex++);
1629
+ if (protoName) {
1630
+ if (['search'].includes(protoName)) {
1631
+ const regex = decl.arguments[0]?.regex?.pattern;
1632
+ if (!regex) return [
1633
+ // no/bad regex arg, return -1/0 for now
1634
+ ...generate(scope, target),
1635
+ [ Opcodes.drop ],
1609
1636
 
1610
- // funcIndex[func.name] = func.index;
1611
- // funcs.push(func);
1637
+ ...number(Rhemyn.types[protoName] === TYPES.number ? -1 : 0),
1638
+ ...setLastType(scope, Rhemyn.types[protoName])
1639
+ ];
1612
1640
 
1613
- // return [
1614
- // generate(scope, decl.callee.object)
1641
+ const rhemynName = `regex_${protoName}_${sanitize(regex)}`;
1615
1642
 
1616
- // // call regex func
1617
- // [ Opcodes.call, func.index ],
1618
- // Opcodes.i32_from_u
1619
- // ];
1620
- // }
1643
+ if (!funcIndex[rhemynName]) {
1644
+ const func = Rhemyn[protoName](regex, currentFuncIndex++, rhemynName);
1645
+ func.internal = true;
1621
1646
 
1622
- if (protoName) {
1623
- const protoBC = {};
1647
+ funcIndex[func.name] = func.index;
1648
+ funcs.push(func);
1649
+ }
1650
+
1651
+ const idx = funcIndex[rhemynName];
1652
+ return [
1653
+ // make string arg
1654
+ ...generate(scope, target),
1655
+ Opcodes.i32_to_u,
1656
+ ...getNodeType(scope, target),
1624
1657
 
1658
+ // call regex func
1659
+ [ Opcodes.call, idx ],
1660
+ Opcodes.i32_from,
1661
+
1662
+ ...setLastType(scope, Rhemyn.types[protoName])
1663
+ ];
1664
+ }
1665
+
1666
+ const protoBC = {};
1625
1667
  const builtinProtoCands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + protoName));
1626
1668
 
1627
1669
  if (!decl._protoInternalCall && builtinProtoCands.length > 0) {
@@ -2035,6 +2077,16 @@ const DEFAULT_VALUE = {
2035
2077
  name: 'undefined'
2036
2078
  };
2037
2079
 
2080
+ const codeToSanitizedStr = code => {
2081
+ let out = '';
2082
+ while (code > 0) {
2083
+ out += String.fromCharCode(97 + code % 26);
2084
+ code -= 26;
2085
+ }
2086
+ return out;
2087
+ };
2088
+ const sanitize = str => str.replace(/[^0-9a-zA-Z_]/g, _ => codeToSanitizedStr(_.charCodeAt(0)));
2089
+
2038
2090
  const unhackName = name => {
2039
2091
  if (name.startsWith('__')) return name.slice(2).replaceAll('_', '.');
2040
2092
  return name;
@@ -2042,7 +2094,7 @@ const unhackName = name => {
2042
2094
 
2043
2095
  const knownType = (scope, type) => {
2044
2096
  if (type.length === 1 && type[0][0] === Opcodes.i32_const) {
2045
- return type[0][1];
2097
+ return read_signedLEB128(type[0].slice(1));
2046
2098
  }
2047
2099
 
2048
2100
  if (typedInput && type.length === 1 && type[0][0] === Opcodes.local_get) {
@@ -2329,11 +2381,6 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2329
2381
  const { type, name } = decl.left;
2330
2382
  const [ local, isGlobal ] = lookupName(scope, name);
2331
2383
 
2332
- if (type === 'ObjectPattern') {
2333
- // hack: ignore object parts of `var a = {} = 2`
2334
- return generate(scope, decl.right);
2335
- }
2336
-
2337
2384
  if (isFuncType(decl.right.type)) {
2338
2385
  // hack for a = function () { ... }
2339
2386
  decl.right.id = { name };
@@ -2406,10 +2453,160 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2406
2453
  [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
2407
2454
  ], getNodeType(scope, decl.right), false, name, true)),
2408
2455
  [ Opcodes.local_tee, newValueTmp ],
2409
-
2410
2456
  [ Opcodes.store, 0, ValtypeSize.i32 ]
2411
2457
  ],
2412
2458
 
2459
+ ...wrapBC({
2460
+ [TYPES.uint8array]: [
2461
+ [ Opcodes.i32_add ],
2462
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2463
+
2464
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2465
+ [ Opcodes.local_get, pointerTmp ],
2466
+ [ Opcodes.i32_load8_u, 0, 4 ],
2467
+ Opcodes.i32_from_u
2468
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2469
+ [ Opcodes.local_tee, newValueTmp ],
2470
+
2471
+ Opcodes.i32_to_u,
2472
+ [ Opcodes.i32_store8, 0, 4 ]
2473
+ ],
2474
+ [TYPES.uint8clampedarray]: [
2475
+ [ Opcodes.i32_add ],
2476
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2477
+
2478
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2479
+ [ Opcodes.local_get, pointerTmp ],
2480
+ [ Opcodes.i32_load8_u, 0, 4 ],
2481
+ Opcodes.i32_from_u
2482
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2483
+ [ Opcodes.local_tee, newValueTmp ],
2484
+
2485
+ ...number(0),
2486
+ [ Opcodes.f64_max ],
2487
+ ...number(255),
2488
+ [ Opcodes.f64_min ],
2489
+ Opcodes.i32_to_u,
2490
+ [ Opcodes.i32_store8, 0, 4 ]
2491
+ ],
2492
+ [TYPES.int8array]: [
2493
+ [ Opcodes.i32_add ],
2494
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2495
+
2496
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2497
+ [ Opcodes.local_get, pointerTmp ],
2498
+ [ Opcodes.i32_load8_s, 0, 4 ],
2499
+ Opcodes.i32_from
2500
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2501
+ [ Opcodes.local_tee, newValueTmp ],
2502
+
2503
+ Opcodes.i32_to,
2504
+ [ Opcodes.i32_store8, 0, 4 ]
2505
+ ],
2506
+ [TYPES.uint16array]: [
2507
+ ...number(2, Valtype.i32),
2508
+ [ Opcodes.i32_mul ],
2509
+ [ Opcodes.i32_add ],
2510
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2511
+
2512
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2513
+ [ Opcodes.local_get, pointerTmp ],
2514
+ [ Opcodes.i32_load16_u, 0, 4 ],
2515
+ Opcodes.i32_from_u
2516
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2517
+ [ Opcodes.local_tee, newValueTmp ],
2518
+
2519
+ Opcodes.i32_to_u,
2520
+ [ Opcodes.i32_store16, 0, 4 ]
2521
+ ],
2522
+ [TYPES.int16array]: [
2523
+ ...number(2, Valtype.i32),
2524
+ [ Opcodes.i32_mul ],
2525
+ [ Opcodes.i32_add ],
2526
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2527
+
2528
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2529
+ [ Opcodes.local_get, pointerTmp ],
2530
+ [ Opcodes.i32_load16_s, 0, 4 ],
2531
+ Opcodes.i32_from
2532
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2533
+ [ Opcodes.local_tee, newValueTmp ],
2534
+
2535
+ Opcodes.i32_to,
2536
+ [ Opcodes.i32_store16, 0, 4 ]
2537
+ ],
2538
+ [TYPES.uint32array]: [
2539
+ ...number(4, Valtype.i32),
2540
+ [ Opcodes.i32_mul ],
2541
+ [ Opcodes.i32_add ],
2542
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2543
+
2544
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2545
+ [ Opcodes.local_get, pointerTmp ],
2546
+ [ Opcodes.i32_load, 0, 4 ],
2547
+ Opcodes.i32_from_u
2548
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2549
+ [ Opcodes.local_tee, newValueTmp ],
2550
+
2551
+ Opcodes.i32_to_u,
2552
+ [ Opcodes.i32_store, 0, 4 ]
2553
+ ],
2554
+ [TYPES.int32array]: [
2555
+ ...number(4, Valtype.i32),
2556
+ [ Opcodes.i32_mul ],
2557
+ [ Opcodes.i32_add ],
2558
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2559
+
2560
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2561
+ [ Opcodes.local_get, pointerTmp ],
2562
+ [ Opcodes.i32_load, 0, 4 ],
2563
+ Opcodes.i32_from
2564
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2565
+ [ Opcodes.local_tee, newValueTmp ],
2566
+
2567
+ Opcodes.i32_to,
2568
+ [ Opcodes.i32_store, 0, 4 ]
2569
+ ],
2570
+ [TYPES.float32array]: [
2571
+ ...number(4, Valtype.i32),
2572
+ [ Opcodes.i32_mul ],
2573
+ [ Opcodes.i32_add ],
2574
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2575
+
2576
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2577
+ [ Opcodes.local_get, pointerTmp ],
2578
+ [ Opcodes.f32_load, 0, 4 ],
2579
+ [ Opcodes.f64_promote_f32 ]
2580
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2581
+ [ Opcodes.local_tee, newValueTmp ],
2582
+
2583
+ [ Opcodes.f32_demote_f64 ],
2584
+ [ Opcodes.f32_store, 0, 4 ]
2585
+ ],
2586
+ [TYPES.float64array]: [
2587
+ ...number(8, Valtype.i32),
2588
+ [ Opcodes.i32_mul ],
2589
+ [ Opcodes.i32_add ],
2590
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2591
+
2592
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2593
+ [ Opcodes.local_get, pointerTmp ],
2594
+ [ Opcodes.f64_load, 0, 4 ]
2595
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2596
+ [ Opcodes.local_tee, newValueTmp ],
2597
+
2598
+ [ Opcodes.f64_store, 0, 4 ]
2599
+ ],
2600
+ }, {
2601
+ prelude: [
2602
+ ...generate(scope, decl.left.object),
2603
+ Opcodes.i32_to_u,
2604
+ ...generate(scope, decl.left.property),
2605
+ Opcodes.i32_to_u,
2606
+ ],
2607
+ postlude: setLastType(scope, TYPES.number)
2608
+ }),
2609
+
2413
2610
  default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
2414
2611
  }, Blocktype.void),
2415
2612
 
@@ -2793,7 +2990,9 @@ const generateForOf = (scope, decl) => {
2793
2990
  // // todo: we should only do this for strings but we don't know at compile-time :(
2794
2991
  // hack: this is naughty and will break things!
2795
2992
  let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
2796
- if (pages.hasAnyString) {
2993
+
2994
+ const known = knownType(scope, getNodeType(scope, decl.right));
2995
+ if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
2797
2996
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
2798
2997
  0, [ newOut, newPointer ] = makeArray(scope, {
2799
2998
  rawElements: new Array(0)
@@ -2841,6 +3040,7 @@ const generateForOf = (scope, decl) => {
2841
3040
  [ Opcodes.end ],
2842
3041
  [ Opcodes.end ]
2843
3042
  ],
3043
+
2844
3044
  [TYPES.string]: [
2845
3045
  ...setType(scope, leftName, TYPES.string),
2846
3046
 
@@ -2949,6 +3149,7 @@ const generateForOf = (scope, decl) => {
2949
3149
  [ Opcodes.end ],
2950
3150
  [ Opcodes.end ]
2951
3151
  ],
3152
+
2952
3153
  [TYPES.set]: [
2953
3154
  [ Opcodes.loop, Blocktype.void ],
2954
3155
 
@@ -2987,6 +3188,106 @@ const generateForOf = (scope, decl) => {
2987
3188
  [ Opcodes.end ],
2988
3189
  [ Opcodes.end ]
2989
3190
  ],
3191
+
3192
+ ...wrapBC({
3193
+ [TYPES.uint8array]: [
3194
+ [ Opcodes.i32_add ],
3195
+
3196
+ [ Opcodes.i32_load8_u, 0, 4 ],
3197
+ Opcodes.i32_from_u
3198
+ ],
3199
+ [TYPES.uint8clampedarray]: [
3200
+ [ Opcodes.i32_add ],
3201
+
3202
+ [ Opcodes.i32_load8_u, 0, 4 ],
3203
+ Opcodes.i32_from_u
3204
+ ],
3205
+ [TYPES.int8array]: [
3206
+ [ Opcodes.i32_add ],
3207
+
3208
+ [ Opcodes.i32_load8_s, 0, 4 ],
3209
+ Opcodes.i32_from
3210
+ ],
3211
+ [TYPES.uint16array]: [
3212
+ ...number(2, Valtype.i32),
3213
+ [ Opcodes.i32_mul ],
3214
+ [ Opcodes.i32_add ],
3215
+
3216
+ [ Opcodes.i32_load16_u, 0, 4 ],
3217
+ Opcodes.i32_from_u
3218
+ ],
3219
+ [TYPES.int16array]: [
3220
+ ...number(2, Valtype.i32),
3221
+ [ Opcodes.i32_mul ],
3222
+ [ Opcodes.i32_add ],
3223
+
3224
+ [ Opcodes.i32_load16_s, 0, 4 ],
3225
+ Opcodes.i32_from
3226
+ ],
3227
+ [TYPES.uint32array]: [
3228
+ ...number(4, Valtype.i32),
3229
+ [ Opcodes.i32_mul ],
3230
+ [ Opcodes.i32_add ],
3231
+
3232
+ [ Opcodes.i32_load, 0, 4 ],
3233
+ Opcodes.i32_from_u
3234
+ ],
3235
+ [TYPES.int32array]: [
3236
+ ...number(4, Valtype.i32),
3237
+ [ Opcodes.i32_mul ],
3238
+ [ Opcodes.i32_add ],
3239
+
3240
+ [ Opcodes.i32_load, 0, 4 ],
3241
+ Opcodes.i32_from
3242
+ ],
3243
+ [TYPES.float32array]: [
3244
+ ...number(4, Valtype.i32),
3245
+ [ Opcodes.i32_mul ],
3246
+ [ Opcodes.i32_add ],
3247
+
3248
+ [ Opcodes.f32_load, 0, 4 ],
3249
+ [ Opcodes.f64_promote_f32 ]
3250
+ ],
3251
+ [TYPES.float64array]: [
3252
+ ...number(8, Valtype.i32),
3253
+ [ Opcodes.i32_mul ],
3254
+ [ Opcodes.i32_add ],
3255
+
3256
+ [ Opcodes.f64_load, 0, 4 ]
3257
+ ],
3258
+ }, {
3259
+ prelude: [
3260
+ ...setType(scope, leftName, TYPES.number),
3261
+
3262
+ [ Opcodes.loop, Blocktype.void ],
3263
+
3264
+ [ Opcodes.local_get, pointer ],
3265
+ [ Opcodes.local_get, counter ]
3266
+ ],
3267
+ postlude: [
3268
+ [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
3269
+
3270
+ [ Opcodes.block, Blocktype.void ],
3271
+ [ Opcodes.block, Blocktype.void ],
3272
+ ...generate(scope, decl.body),
3273
+ [ Opcodes.end ],
3274
+
3275
+ // increment counter by 1
3276
+ [ Opcodes.local_get, counter ],
3277
+ ...number(1, Valtype.i32),
3278
+ [ Opcodes.i32_add ],
3279
+ [ Opcodes.local_tee, counter ],
3280
+
3281
+ // loop if counter != length
3282
+ [ Opcodes.local_get, length ],
3283
+ [ Opcodes.i32_ne ],
3284
+ [ Opcodes.br_if, 1 ],
3285
+
3286
+ [ Opcodes.end ],
3287
+ [ Opcodes.end ]
3288
+ ]
3289
+ }),
3290
+
2990
3291
  default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
2991
3292
  }, Blocktype.void));
2992
3293
 
@@ -3075,13 +3376,14 @@ const generateThrow = (scope, decl) => {
3075
3376
  idx: tags.length
3076
3377
  });
3077
3378
 
3078
- let exceptId = exceptions.push({ constructor, message }) - 1;
3379
+ let exceptId = exceptions.findIndex(x => x.constructor === constructor && x.message === message);
3380
+ if (exceptId === -1) exceptId = exceptions.push({ constructor, message }) - 1;
3079
3381
 
3080
3382
  scope.exceptions ??= [];
3081
3383
  scope.exceptions.push(exceptId);
3082
3384
 
3083
3385
  return [
3084
- [ Opcodes.i32_const, signedLEB128(exceptId) ],
3386
+ ...number(exceptId, Valtype.i32),
3085
3387
  [ Opcodes.throw, tags[0].idx ]
3086
3388
  ];
3087
3389
  }
@@ -3148,7 +3450,7 @@ const generateThrow = (scope, decl) => {
3148
3450
  scope.exceptions.push(exceptId);
3149
3451
 
3150
3452
  return [
3151
- [ Opcodes.i32_const, signedLEB128(exceptId) ],
3453
+ ...number(exceptId, Valtype.i32),
3152
3454
  ...generate(scope, message),
3153
3455
  ...getNodeType(scope, message),
3154
3456
  [ Opcodes.throw, tags[0].idx ]
@@ -3506,6 +3808,19 @@ const withType = (scope, wasm, type) => [
3506
3808
  ...setLastType(scope, type)
3507
3809
  ];
3508
3810
 
3811
+ const wrapBC = (bc, { prelude = [], postlude = [] } = {}) => {
3812
+ const out = {};
3813
+ for (const x in bc) {
3814
+ out[x] = [
3815
+ ...prelude,
3816
+ ...bc[x],
3817
+ ...postlude
3818
+ ];
3819
+ }
3820
+
3821
+ return out;
3822
+ };
3823
+
3509
3824
  const generateMember = (scope, decl, _global, _name) => {
3510
3825
  const name = decl.object.name;
3511
3826
 
@@ -3559,7 +3874,7 @@ const generateMember = (scope, decl, _global, _name) => {
3559
3874
  const type = getNodeType(scope, decl.object);
3560
3875
  const known = knownType(scope, type);
3561
3876
  if (known != null) {
3562
- if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
3877
+ if (typeHasFlag(known, TYPE_FLAGS.length)) return [
3563
3878
  ...generate(scope, decl.object),
3564
3879
  Opcodes.i32_to_u,
3565
3880
 
@@ -3571,7 +3886,9 @@ const generateMember = (scope, decl, _global, _name) => {
3571
3886
  }
3572
3887
 
3573
3888
  return [
3574
- ...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
3889
+ ...getNodeType(scope, decl.object),
3890
+ ...number(TYPE_FLAGS.length, Valtype.i32),
3891
+ [ Opcodes.i32_and ],
3575
3892
  [ Opcodes.if, valtypeBinary ],
3576
3893
  ...generate(scope, decl.object),
3577
3894
  Opcodes.i32_to_u,
@@ -3588,7 +3905,7 @@ const generateMember = (scope, decl, _global, _name) => {
3588
3905
  }
3589
3906
 
3590
3907
  // todo: generate this array procedurally during builtinFuncs creation
3591
- if (['size', 'description'].includes(decl.property.name)) {
3908
+ if (['size', 'description', 'byteLength'].includes(decl.property.name)) {
3592
3909
  const bc = {};
3593
3910
  const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
3594
3911
 
@@ -3620,7 +3937,9 @@ const generateMember = (scope, decl, _global, _name) => {
3620
3937
  // // todo: we should only do this for strings but we don't know at compile-time :(
3621
3938
  // hack: this is naughty and will break things!
3622
3939
  let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
3623
- if (pages.hasAnyString && knownType(scope, getNodeType(scope, decl.object)) !== TYPES.array) {
3940
+
3941
+ const known = knownType(scope, getNodeType(scope, decl.object));
3942
+ if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
3624
3943
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
3625
3944
  0, [ newOut, newPointer ] = makeArray(scope, {
3626
3945
  rawElements: new Array(0)
@@ -3694,6 +4013,82 @@ const generateMember = (scope, decl, _global, _name) => {
3694
4013
  ...setLastType(scope, TYPES.bytestring)
3695
4014
  ],
3696
4015
 
4016
+ ...wrapBC({
4017
+ [TYPES.uint8array]: [
4018
+ [ Opcodes.i32_add ],
4019
+
4020
+ [ Opcodes.i32_load8_u, 0, 4 ],
4021
+ Opcodes.i32_from_u
4022
+ ],
4023
+ [TYPES.uint8clampedarray]: [
4024
+ [ Opcodes.i32_add ],
4025
+
4026
+ [ Opcodes.i32_load8_u, 0, 4 ],
4027
+ Opcodes.i32_from_u
4028
+ ],
4029
+ [TYPES.int8array]: [
4030
+ [ Opcodes.i32_add ],
4031
+
4032
+ [ Opcodes.i32_load8_s, 0, 4 ],
4033
+ Opcodes.i32_from
4034
+ ],
4035
+ [TYPES.uint16array]: [
4036
+ ...number(2, Valtype.i32),
4037
+ [ Opcodes.i32_mul ],
4038
+ [ Opcodes.i32_add ],
4039
+
4040
+ [ Opcodes.i32_load16_u, 0, 4 ],
4041
+ Opcodes.i32_from_u
4042
+ ],
4043
+ [TYPES.int16array]: [
4044
+ ...number(2, Valtype.i32),
4045
+ [ Opcodes.i32_mul ],
4046
+ [ Opcodes.i32_add ],
4047
+
4048
+ [ Opcodes.i32_load16_s, 0, 4 ],
4049
+ Opcodes.i32_from
4050
+ ],
4051
+ [TYPES.uint32array]: [
4052
+ ...number(4, Valtype.i32),
4053
+ [ Opcodes.i32_mul ],
4054
+ [ Opcodes.i32_add ],
4055
+
4056
+ [ Opcodes.i32_load, 0, 4 ],
4057
+ Opcodes.i32_from_u
4058
+ ],
4059
+ [TYPES.int32array]: [
4060
+ ...number(4, Valtype.i32),
4061
+ [ Opcodes.i32_mul ],
4062
+ [ Opcodes.i32_add ],
4063
+
4064
+ [ Opcodes.i32_load, 0, 4 ],
4065
+ Opcodes.i32_from
4066
+ ],
4067
+ [TYPES.float32array]: [
4068
+ ...number(4, Valtype.i32),
4069
+ [ Opcodes.i32_mul ],
4070
+ [ Opcodes.i32_add ],
4071
+
4072
+ [ Opcodes.f32_load, 0, 4 ],
4073
+ [ Opcodes.f64_promote_f32 ]
4074
+ ],
4075
+ [TYPES.float64array]: [
4076
+ ...number(8, Valtype.i32),
4077
+ [ Opcodes.i32_mul ],
4078
+ [ Opcodes.i32_add ],
4079
+
4080
+ [ Opcodes.f64_load, 0, 4 ]
4081
+ ],
4082
+ }, {
4083
+ prelude: [
4084
+ ...object,
4085
+ Opcodes.i32_to_u,
4086
+ ...property,
4087
+ Opcodes.i32_to_u
4088
+ ],
4089
+ postlude: setLastType(scope, TYPES.number)
4090
+ }),
4091
+
3697
4092
  default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
3698
4093
  });
3699
4094
  };
@@ -3716,7 +4111,7 @@ const objectHack = node => {
3716
4111
  if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
3717
4112
 
3718
4113
  // if .name or .length, give up (hack within a hack!)
3719
- if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
4114
+ if (['name', 'length', 'size', 'description', 'byteLength'].includes(node.property.name)) {
3720
4115
  node.object = objectHack(node.object);
3721
4116
  return;
3722
4117
  }