porffor 0.17.0-418ce1445 → 0.17.0-4e4a65afe

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
 
@@ -264,10 +267,12 @@ const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysV
264
267
  argument: {
265
268
  type: 'NewExpression',
266
269
  callee: {
270
+ type: 'Identifier',
267
271
  name: constructor
268
272
  },
269
273
  arguments: [
270
274
  {
275
+ type: 'Literal',
271
276
  value: message
272
277
  }
273
278
  ]
@@ -289,12 +294,13 @@ const generateIdent = (scope, decl) => {
289
294
  return wasm.slice();
290
295
  }
291
296
 
292
- if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
293
- // todo: return an actual something
294
- return number(1);
295
- }
297
+ // todo: enable this by default in future
298
+ // if (!Object.hasOwn(funcIndex, name) && Object.hasOwn(builtinFuncs, name)) {
299
+ // includeBuiltin(scope, name);
300
+ // return number(funcIndex[name] - importedFuncs.length);
301
+ // }
296
302
 
297
- if (isExistingProtoFunc(name)) {
303
+ if (isExistingProtoFunc(name) || Object.hasOwn(internalConstrs, name) || Object.hasOwn(builtinFuncs, name)) {
298
304
  // todo: return an actual something
299
305
  return number(1);
300
306
  }
@@ -613,11 +619,14 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
613
619
  };
614
620
 
615
621
  const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
616
- // if (isIntToFloatOp(wasm[wasm.length - 1])) return [
617
- // ...wasm,
618
- // ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
619
- // ];
620
- // 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
+ ];
621
630
 
622
631
  // todo/perf: use knownType and custom bytecode here instead of typeSwitch
623
632
 
@@ -973,6 +982,33 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
973
982
  };
974
983
 
975
984
  const generateBinaryExp = (scope, decl, _global, _name) => {
985
+ if (decl.operator === 'instanceof') {
986
+ // very hacky basic instanceof
987
+ // todo: support dynamic right-hand side
988
+
989
+ const out = generate(scope, decl.left);
990
+ disposeLeftover(out);
991
+
992
+ const rightName = decl.right.name;
993
+ if (!rightName) return todo(scope, 'instanceof dynamic right-hand side is not supported yet', true);
994
+
995
+ const checkType = TYPES[rightName.toLowerCase()];
996
+ if (checkType == null || rightName !== TYPE_NAMES[checkType] || checkType === TYPES.undefined) return todo(scope, 'instanceof right-hand side type unsupported', true);
997
+
998
+ if ([TYPES.number, TYPES.boolean, TYPES.string, TYPES.symbol, TYPES.object].includes(checkType)) {
999
+ out.push(...number(0));
1000
+ } else {
1001
+ out.push(
1002
+ ...getNodeType(scope, decl.left),
1003
+ ...number(checkType, Valtype.i32),
1004
+ [ Opcodes.i32_eq ],
1005
+ Opcodes.i32_from_u
1006
+ );
1007
+ }
1008
+
1009
+ return out;
1010
+ }
1011
+
976
1012
  const out = performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name);
977
1013
 
978
1014
  if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
@@ -1279,7 +1315,7 @@ const getNodeType = (scope, node) => {
1279
1315
  }
1280
1316
 
1281
1317
  if (node.type === 'BinaryExpression') {
1282
- if (['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(node.operator)) return TYPES.boolean;
1318
+ if (['==', '===', '!=', '!==', '>', '>=', '<', '<=', 'instanceof'].includes(node.operator)) return TYPES.boolean;
1283
1319
  if (node.operator !== '+') return TYPES.number;
1284
1320
 
1285
1321
  const knownLeft = knownType(scope, getNodeType(scope, node.left));
@@ -1312,7 +1348,7 @@ const getNodeType = (scope, node) => {
1312
1348
  const objectKnownType = knownType(scope, getNodeType(scope, node.object));
1313
1349
  if (objectKnownType != null) {
1314
1350
  if (name === 'length') {
1315
- if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
1351
+ if (typeHasFlag(objectKnownType, TYPE_FLAGS.length)) return TYPES.number;
1316
1352
  else return TYPES.undefined;
1317
1353
  }
1318
1354
 
@@ -1384,9 +1420,9 @@ const countLeftover = wasm => {
1384
1420
 
1385
1421
  if (depth === 0)
1386
1422
  if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1387
- 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)) {}
1423
+ 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)) {}
1388
1424
  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++;
1389
- else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
1425
+ 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;
1390
1426
  else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
1391
1427
  else if (inst[0] === Opcodes.return) count = 0;
1392
1428
  else if (inst[0] === Opcodes.call) {
@@ -1424,6 +1460,18 @@ const generateExp = (scope, decl) => {
1424
1460
  return out;
1425
1461
  };
1426
1462
 
1463
+ const generateSequence = (scope, decl) => {
1464
+ let out = [];
1465
+
1466
+ const exprs = decl.expressions;
1467
+ for (let i = 0; i < exprs.length; i++) {
1468
+ if (i > 0) disposeLeftover(out);
1469
+ out.push(...generate(scope, exprs[i]));
1470
+ }
1471
+
1472
+ return out;
1473
+ };
1474
+
1427
1475
  const CTArrayUtil = {
1428
1476
  getLengthI32: pointer => [
1429
1477
  ...number(0, Valtype.i32),
@@ -2012,7 +2060,7 @@ const unhackName = name => {
2012
2060
 
2013
2061
  const knownType = (scope, type) => {
2014
2062
  if (type.length === 1 && type[0][0] === Opcodes.i32_const) {
2015
- return type[0][1];
2063
+ return read_signedLEB128(type[0].slice(1));
2016
2064
  }
2017
2065
 
2018
2066
  if (typedInput && type.length === 1 && type[0][0] === Opcodes.local_get) {
@@ -2299,11 +2347,6 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2299
2347
  const { type, name } = decl.left;
2300
2348
  const [ local, isGlobal ] = lookupName(scope, name);
2301
2349
 
2302
- if (type === 'ObjectPattern') {
2303
- // hack: ignore object parts of `var a = {} = 2`
2304
- return generate(scope, decl.right);
2305
- }
2306
-
2307
2350
  if (isFuncType(decl.right.type)) {
2308
2351
  // hack for a = function () { ... }
2309
2352
  decl.right.id = { name };
@@ -2376,10 +2419,160 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2376
2419
  [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
2377
2420
  ], getNodeType(scope, decl.right), false, name, true)),
2378
2421
  [ Opcodes.local_tee, newValueTmp ],
2379
-
2380
2422
  [ Opcodes.store, 0, ValtypeSize.i32 ]
2381
2423
  ],
2382
2424
 
2425
+ ...wrapBC({
2426
+ [TYPES.uint8array]: [
2427
+ [ Opcodes.i32_add ],
2428
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2429
+
2430
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2431
+ [ Opcodes.local_get, pointerTmp ],
2432
+ [ Opcodes.i32_load8_u, 0, 4 ],
2433
+ Opcodes.i32_from_u
2434
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2435
+ [ Opcodes.local_tee, newValueTmp ],
2436
+
2437
+ Opcodes.i32_to_u,
2438
+ [ Opcodes.i32_store8, 0, 4 ]
2439
+ ],
2440
+ [TYPES.uint8clampedarray]: [
2441
+ [ Opcodes.i32_add ],
2442
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2443
+
2444
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2445
+ [ Opcodes.local_get, pointerTmp ],
2446
+ [ Opcodes.i32_load8_u, 0, 4 ],
2447
+ Opcodes.i32_from_u
2448
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2449
+ [ Opcodes.local_tee, newValueTmp ],
2450
+
2451
+ ...number(0),
2452
+ [ Opcodes.f64_max ],
2453
+ ...number(255),
2454
+ [ Opcodes.f64_min ],
2455
+ Opcodes.i32_to_u,
2456
+ [ Opcodes.i32_store8, 0, 4 ]
2457
+ ],
2458
+ [TYPES.int8array]: [
2459
+ [ Opcodes.i32_add ],
2460
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2461
+
2462
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2463
+ [ Opcodes.local_get, pointerTmp ],
2464
+ [ Opcodes.i32_load8_s, 0, 4 ],
2465
+ Opcodes.i32_from
2466
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2467
+ [ Opcodes.local_tee, newValueTmp ],
2468
+
2469
+ Opcodes.i32_to,
2470
+ [ Opcodes.i32_store8, 0, 4 ]
2471
+ ],
2472
+ [TYPES.uint16array]: [
2473
+ ...number(2, Valtype.i32),
2474
+ [ Opcodes.i32_mul ],
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_load16_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
+ Opcodes.i32_to_u,
2486
+ [ Opcodes.i32_store16, 0, 4 ]
2487
+ ],
2488
+ [TYPES.int16array]: [
2489
+ ...number(2, Valtype.i32),
2490
+ [ Opcodes.i32_mul ],
2491
+ [ Opcodes.i32_add ],
2492
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2493
+
2494
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2495
+ [ Opcodes.local_get, pointerTmp ],
2496
+ [ Opcodes.i32_load16_s, 0, 4 ],
2497
+ Opcodes.i32_from
2498
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2499
+ [ Opcodes.local_tee, newValueTmp ],
2500
+
2501
+ Opcodes.i32_to,
2502
+ [ Opcodes.i32_store16, 0, 4 ]
2503
+ ],
2504
+ [TYPES.uint32array]: [
2505
+ ...number(4, Valtype.i32),
2506
+ [ Opcodes.i32_mul ],
2507
+ [ Opcodes.i32_add ],
2508
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2509
+
2510
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2511
+ [ Opcodes.local_get, pointerTmp ],
2512
+ [ Opcodes.i32_load, 0, 4 ],
2513
+ Opcodes.i32_from_u
2514
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2515
+ [ Opcodes.local_tee, newValueTmp ],
2516
+
2517
+ Opcodes.i32_to_u,
2518
+ [ Opcodes.i32_store, 0, 4 ]
2519
+ ],
2520
+ [TYPES.int32array]: [
2521
+ ...number(4, Valtype.i32),
2522
+ [ Opcodes.i32_mul ],
2523
+ [ Opcodes.i32_add ],
2524
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2525
+
2526
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2527
+ [ Opcodes.local_get, pointerTmp ],
2528
+ [ Opcodes.i32_load, 0, 4 ],
2529
+ Opcodes.i32_from
2530
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2531
+ [ Opcodes.local_tee, newValueTmp ],
2532
+
2533
+ Opcodes.i32_to,
2534
+ [ Opcodes.i32_store, 0, 4 ]
2535
+ ],
2536
+ [TYPES.float32array]: [
2537
+ ...number(4, Valtype.i32),
2538
+ [ Opcodes.i32_mul ],
2539
+ [ Opcodes.i32_add ],
2540
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2541
+
2542
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2543
+ [ Opcodes.local_get, pointerTmp ],
2544
+ [ Opcodes.f32_load, 0, 4 ],
2545
+ [ Opcodes.f64_promote_f32 ]
2546
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2547
+ [ Opcodes.local_tee, newValueTmp ],
2548
+
2549
+ [ Opcodes.f32_demote_f64 ],
2550
+ [ Opcodes.f32_store, 0, 4 ]
2551
+ ],
2552
+ [TYPES.float64array]: [
2553
+ ...number(8, Valtype.i32),
2554
+ [ Opcodes.i32_mul ],
2555
+ [ Opcodes.i32_add ],
2556
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2557
+
2558
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2559
+ [ Opcodes.local_get, pointerTmp ],
2560
+ [ Opcodes.f64_load, 0, 4 ]
2561
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2562
+ [ Opcodes.local_tee, newValueTmp ],
2563
+
2564
+ [ Opcodes.f64_store, 0, 4 ]
2565
+ ],
2566
+ }, {
2567
+ prelude: [
2568
+ ...generate(scope, decl.left.object),
2569
+ Opcodes.i32_to_u,
2570
+ ...generate(scope, decl.left.property),
2571
+ Opcodes.i32_to_u,
2572
+ ],
2573
+ postlude: setLastType(scope, TYPES.number)
2574
+ }),
2575
+
2383
2576
  default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
2384
2577
  }, Blocktype.void),
2385
2578
 
@@ -2763,7 +2956,9 @@ const generateForOf = (scope, decl) => {
2763
2956
  // // todo: we should only do this for strings but we don't know at compile-time :(
2764
2957
  // hack: this is naughty and will break things!
2765
2958
  let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
2766
- if (pages.hasAnyString) {
2959
+
2960
+ const known = knownType(scope, getNodeType(scope, decl.right));
2961
+ if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
2767
2962
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
2768
2963
  0, [ newOut, newPointer ] = makeArray(scope, {
2769
2964
  rawElements: new Array(0)
@@ -2811,6 +3006,7 @@ const generateForOf = (scope, decl) => {
2811
3006
  [ Opcodes.end ],
2812
3007
  [ Opcodes.end ]
2813
3008
  ],
3009
+
2814
3010
  [TYPES.string]: [
2815
3011
  ...setType(scope, leftName, TYPES.string),
2816
3012
 
@@ -2919,6 +3115,7 @@ const generateForOf = (scope, decl) => {
2919
3115
  [ Opcodes.end ],
2920
3116
  [ Opcodes.end ]
2921
3117
  ],
3118
+
2922
3119
  [TYPES.set]: [
2923
3120
  [ Opcodes.loop, Blocktype.void ],
2924
3121
 
@@ -2957,6 +3154,106 @@ const generateForOf = (scope, decl) => {
2957
3154
  [ Opcodes.end ],
2958
3155
  [ Opcodes.end ]
2959
3156
  ],
3157
+
3158
+ ...wrapBC({
3159
+ [TYPES.uint8array]: [
3160
+ [ Opcodes.i32_add ],
3161
+
3162
+ [ Opcodes.i32_load8_u, 0, 4 ],
3163
+ Opcodes.i32_from_u
3164
+ ],
3165
+ [TYPES.uint8clampedarray]: [
3166
+ [ Opcodes.i32_add ],
3167
+
3168
+ [ Opcodes.i32_load8_u, 0, 4 ],
3169
+ Opcodes.i32_from_u
3170
+ ],
3171
+ [TYPES.int8array]: [
3172
+ [ Opcodes.i32_add ],
3173
+
3174
+ [ Opcodes.i32_load8_s, 0, 4 ],
3175
+ Opcodes.i32_from
3176
+ ],
3177
+ [TYPES.uint16array]: [
3178
+ ...number(2, Valtype.i32),
3179
+ [ Opcodes.i32_mul ],
3180
+ [ Opcodes.i32_add ],
3181
+
3182
+ [ Opcodes.i32_load16_u, 0, 4 ],
3183
+ Opcodes.i32_from_u
3184
+ ],
3185
+ [TYPES.int16array]: [
3186
+ ...number(2, Valtype.i32),
3187
+ [ Opcodes.i32_mul ],
3188
+ [ Opcodes.i32_add ],
3189
+
3190
+ [ Opcodes.i32_load16_s, 0, 4 ],
3191
+ Opcodes.i32_from
3192
+ ],
3193
+ [TYPES.uint32array]: [
3194
+ ...number(4, Valtype.i32),
3195
+ [ Opcodes.i32_mul ],
3196
+ [ Opcodes.i32_add ],
3197
+
3198
+ [ Opcodes.i32_load, 0, 4 ],
3199
+ Opcodes.i32_from_u
3200
+ ],
3201
+ [TYPES.int32array]: [
3202
+ ...number(4, Valtype.i32),
3203
+ [ Opcodes.i32_mul ],
3204
+ [ Opcodes.i32_add ],
3205
+
3206
+ [ Opcodes.i32_load, 0, 4 ],
3207
+ Opcodes.i32_from
3208
+ ],
3209
+ [TYPES.float32array]: [
3210
+ ...number(4, Valtype.i32),
3211
+ [ Opcodes.i32_mul ],
3212
+ [ Opcodes.i32_add ],
3213
+
3214
+ [ Opcodes.f32_load, 0, 4 ],
3215
+ [ Opcodes.f64_promote_f32 ]
3216
+ ],
3217
+ [TYPES.float64array]: [
3218
+ ...number(8, Valtype.i32),
3219
+ [ Opcodes.i32_mul ],
3220
+ [ Opcodes.i32_add ],
3221
+
3222
+ [ Opcodes.f64_load, 0, 4 ]
3223
+ ],
3224
+ }, {
3225
+ prelude: [
3226
+ ...setType(scope, leftName, TYPES.number),
3227
+
3228
+ [ Opcodes.loop, Blocktype.void ],
3229
+
3230
+ [ Opcodes.local_get, pointer ],
3231
+ [ Opcodes.local_get, counter ]
3232
+ ],
3233
+ postlude: [
3234
+ [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
3235
+
3236
+ [ Opcodes.block, Blocktype.void ],
3237
+ [ Opcodes.block, Blocktype.void ],
3238
+ ...generate(scope, decl.body),
3239
+ [ Opcodes.end ],
3240
+
3241
+ // increment counter by 1
3242
+ [ Opcodes.local_get, counter ],
3243
+ ...number(1, Valtype.i32),
3244
+ [ Opcodes.i32_add ],
3245
+ [ Opcodes.local_tee, counter ],
3246
+
3247
+ // loop if counter != length
3248
+ [ Opcodes.local_get, length ],
3249
+ [ Opcodes.i32_ne ],
3250
+ [ Opcodes.br_if, 1 ],
3251
+
3252
+ [ Opcodes.end ],
3253
+ [ Opcodes.end ]
3254
+ ]
3255
+ }),
3256
+
2960
3257
  default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
2961
3258
  }, Blocktype.void));
2962
3259
 
@@ -3029,32 +3326,102 @@ const generateLabel = (scope, decl) => {
3029
3326
  const generateThrow = (scope, decl) => {
3030
3327
  scope.throws = true;
3031
3328
 
3032
- let message = decl.argument.value, constructor = null;
3329
+ const exceptionMode = Prefs.exceptionMode ?? 'lut';
3330
+ if (exceptionMode === 'lut') {
3331
+ let message = decl.argument.value, constructor = null;
3332
+
3333
+ // support `throw (new)? Error(...)`
3334
+ if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
3335
+ constructor = decl.argument.callee.name;
3336
+ message = decl.argument.arguments[0]?.value ?? '';
3337
+ }
3338
+
3339
+ if (tags.length === 0) tags.push({
3340
+ params: [ Valtype.i32 ],
3341
+ results: [],
3342
+ idx: tags.length
3343
+ });
3344
+
3345
+ let exceptId = exceptions.findIndex(x => x.constructor === constructor && x.message === message);
3346
+ if (exceptId === -1) exceptId = exceptions.push({ constructor, message }) - 1;
3347
+
3348
+ scope.exceptions ??= [];
3349
+ scope.exceptions.push(exceptId);
3033
3350
 
3034
- // hack: throw new X("...") -> throw "..."
3035
- if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
3036
- constructor = decl.argument.callee.name;
3037
- message = decl.argument.arguments[0]?.value ?? '';
3351
+ return [
3352
+ ...number(exceptId, Valtype.i32),
3353
+ [ Opcodes.throw, tags[0].idx ]
3354
+ ];
3038
3355
  }
3039
3356
 
3040
- if (tags.length === 0) tags.push({
3041
- params: [ Valtype.i32 ],
3042
- results: [],
3043
- idx: tags.length
3044
- });
3357
+ if (exceptionMode === 'stack') {
3358
+ if (tags.length === 0) tags.push({
3359
+ params: [ valtypeBinary, Valtype.i32 ],
3360
+ results: [],
3361
+ idx: tags.length
3362
+ });
3045
3363
 
3046
- let exceptId = exceptions.push({ constructor, message }) - 1;
3047
- let tagIdx = tags[0].idx;
3364
+ return [
3365
+ ...generate(scope, decl.argument),
3366
+ ...getNodeType(scope, decl.argument),
3367
+ [ Opcodes.throw, tags[0].idx ]
3368
+ ];
3369
+ }
3048
3370
 
3049
- scope.exceptions ??= [];
3050
- scope.exceptions.push(exceptId);
3371
+ if (exceptionMode === 'stackest') {
3372
+ let message = decl.argument, constructor = null;
3051
3373
 
3052
- // todo: write a description of how this works lol
3374
+ // support `throw (new)? Error(...)`
3375
+ if (message.type === 'NewExpression' || message.type === 'CallExpression') {
3376
+ constructor = decl.argument.callee;
3377
+ message = decl.argument.arguments[0];
3378
+ }
3053
3379
 
3054
- return [
3055
- [ Opcodes.i32_const, signedLEB128(exceptId) ],
3056
- [ Opcodes.throw, tagIdx ]
3057
- ];
3380
+ message ??= DEFAULT_VALUE;
3381
+
3382
+ if (tags.length === 0) tags.push({
3383
+ params: [ valtypeBinary, valtypeBinary, Valtype.i32 ],
3384
+ results: [],
3385
+ idx: tags.length
3386
+ });
3387
+
3388
+ return [
3389
+ ...(constructor == null ? number(-1) : generate(scope, constructor)),
3390
+ ...generate(scope, message),
3391
+ ...getNodeType(scope, message),
3392
+ [ Opcodes.throw, tags[0].idx ]
3393
+ ];
3394
+ }
3395
+
3396
+ if (exceptionMode === 'partial') {
3397
+ let message = decl.argument, constructor = null;
3398
+
3399
+ // support `throw (new)? Error(...)`
3400
+ if (message.type === 'NewExpression' || message.type === 'CallExpression') {
3401
+ constructor = decl.argument.callee.name;
3402
+ message = decl.argument.arguments[0];
3403
+ }
3404
+
3405
+ message ??= DEFAULT_VALUE;
3406
+
3407
+ if (tags.length === 0) tags.push({
3408
+ params: [ Valtype.i32, valtypeBinary, Valtype.i32 ],
3409
+ results: [],
3410
+ idx: tags.length
3411
+ });
3412
+
3413
+ let exceptId = exceptions.push({ constructor }) - 1;
3414
+
3415
+ scope.exceptions ??= [];
3416
+ scope.exceptions.push(exceptId);
3417
+
3418
+ return [
3419
+ ...number(exceptId, Valtype.i32),
3420
+ ...generate(scope, message),
3421
+ ...getNodeType(scope, message),
3422
+ [ Opcodes.throw, tags[0].idx ]
3423
+ ];
3424
+ }
3058
3425
  };
3059
3426
 
3060
3427
  const generateTry = (scope, decl) => {
@@ -3407,6 +3774,19 @@ const withType = (scope, wasm, type) => [
3407
3774
  ...setLastType(scope, type)
3408
3775
  ];
3409
3776
 
3777
+ const wrapBC = (bc, { prelude = [], postlude = [] } = {}) => {
3778
+ const out = {};
3779
+ for (const x in bc) {
3780
+ out[x] = [
3781
+ ...prelude,
3782
+ ...bc[x],
3783
+ ...postlude
3784
+ ];
3785
+ }
3786
+
3787
+ return out;
3788
+ };
3789
+
3410
3790
  const generateMember = (scope, decl, _global, _name) => {
3411
3791
  const name = decl.object.name;
3412
3792
 
@@ -3460,7 +3840,7 @@ const generateMember = (scope, decl, _global, _name) => {
3460
3840
  const type = getNodeType(scope, decl.object);
3461
3841
  const known = knownType(scope, type);
3462
3842
  if (known != null) {
3463
- if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
3843
+ if (typeHasFlag(known, TYPE_FLAGS.length)) return [
3464
3844
  ...generate(scope, decl.object),
3465
3845
  Opcodes.i32_to_u,
3466
3846
 
@@ -3472,7 +3852,9 @@ const generateMember = (scope, decl, _global, _name) => {
3472
3852
  }
3473
3853
 
3474
3854
  return [
3475
- ...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
3855
+ ...getNodeType(scope, decl.object),
3856
+ ...number(TYPE_FLAGS.length, Valtype.i32),
3857
+ [ Opcodes.i32_and ],
3476
3858
  [ Opcodes.if, valtypeBinary ],
3477
3859
  ...generate(scope, decl.object),
3478
3860
  Opcodes.i32_to_u,
@@ -3521,7 +3903,9 @@ const generateMember = (scope, decl, _global, _name) => {
3521
3903
  // // todo: we should only do this for strings but we don't know at compile-time :(
3522
3904
  // hack: this is naughty and will break things!
3523
3905
  let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
3524
- if (pages.hasAnyString && knownType(scope, getNodeType(scope, decl.object)) !== TYPES.array) {
3906
+
3907
+ const known = knownType(scope, getNodeType(scope, decl.object));
3908
+ if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
3525
3909
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
3526
3910
  0, [ newOut, newPointer ] = makeArray(scope, {
3527
3911
  rawElements: new Array(0)
@@ -3595,6 +3979,82 @@ const generateMember = (scope, decl, _global, _name) => {
3595
3979
  ...setLastType(scope, TYPES.bytestring)
3596
3980
  ],
3597
3981
 
3982
+ ...wrapBC({
3983
+ [TYPES.uint8array]: [
3984
+ [ Opcodes.i32_add ],
3985
+
3986
+ [ Opcodes.i32_load8_u, 0, 4 ],
3987
+ Opcodes.i32_from_u
3988
+ ],
3989
+ [TYPES.uint8clampedarray]: [
3990
+ [ Opcodes.i32_add ],
3991
+
3992
+ [ Opcodes.i32_load8_u, 0, 4 ],
3993
+ Opcodes.i32_from_u
3994
+ ],
3995
+ [TYPES.int8array]: [
3996
+ [ Opcodes.i32_add ],
3997
+
3998
+ [ Opcodes.i32_load8_s, 0, 4 ],
3999
+ Opcodes.i32_from
4000
+ ],
4001
+ [TYPES.uint16array]: [
4002
+ ...number(2, Valtype.i32),
4003
+ [ Opcodes.i32_mul ],
4004
+ [ Opcodes.i32_add ],
4005
+
4006
+ [ Opcodes.i32_load16_u, 0, 4 ],
4007
+ Opcodes.i32_from_u
4008
+ ],
4009
+ [TYPES.int16array]: [
4010
+ ...number(2, Valtype.i32),
4011
+ [ Opcodes.i32_mul ],
4012
+ [ Opcodes.i32_add ],
4013
+
4014
+ [ Opcodes.i32_load16_s, 0, 4 ],
4015
+ Opcodes.i32_from
4016
+ ],
4017
+ [TYPES.uint32array]: [
4018
+ ...number(4, Valtype.i32),
4019
+ [ Opcodes.i32_mul ],
4020
+ [ Opcodes.i32_add ],
4021
+
4022
+ [ Opcodes.i32_load, 0, 4 ],
4023
+ Opcodes.i32_from_u
4024
+ ],
4025
+ [TYPES.int32array]: [
4026
+ ...number(4, Valtype.i32),
4027
+ [ Opcodes.i32_mul ],
4028
+ [ Opcodes.i32_add ],
4029
+
4030
+ [ Opcodes.i32_load, 0, 4 ],
4031
+ Opcodes.i32_from
4032
+ ],
4033
+ [TYPES.float32array]: [
4034
+ ...number(4, Valtype.i32),
4035
+ [ Opcodes.i32_mul ],
4036
+ [ Opcodes.i32_add ],
4037
+
4038
+ [ Opcodes.f32_load, 0, 4 ],
4039
+ [ Opcodes.f64_promote_f32 ]
4040
+ ],
4041
+ [TYPES.float64array]: [
4042
+ ...number(8, Valtype.i32),
4043
+ [ Opcodes.i32_mul ],
4044
+ [ Opcodes.i32_add ],
4045
+
4046
+ [ Opcodes.f64_load, 0, 4 ]
4047
+ ],
4048
+ }, {
4049
+ prelude: [
4050
+ ...object,
4051
+ Opcodes.i32_to_u,
4052
+ ...property,
4053
+ Opcodes.i32_to_u
4054
+ ],
4055
+ postlude: setLastType(scope, TYPES.number)
4056
+ }),
4057
+
3598
4058
  default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
3599
4059
  });
3600
4060
  };
@@ -3772,7 +4232,7 @@ const internalConstrs = {
3772
4232
 
3773
4233
  // todo: check in wasm instead of here
3774
4234
  const literalValue = arg.value ?? 0;
3775
- if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
4235
+ if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeError', 'Invalid array length', true);
3776
4236
 
3777
4237
  return [
3778
4238
  ...out,