porffor 0.17.0-048c6f2ee → 0.17.0-05070e1f0

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
 
@@ -291,12 +294,13 @@ const generateIdent = (scope, decl) => {
291
294
  return wasm.slice();
292
295
  }
293
296
 
294
- if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
295
- // todo: return an actual something
296
- return number(1);
297
- }
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
+ // }
298
302
 
299
- if (isExistingProtoFunc(name)) {
303
+ if (isExistingProtoFunc(name) || Object.hasOwn(internalConstrs, name) || Object.hasOwn(builtinFuncs, name)) {
300
304
  // todo: return an actual something
301
305
  return number(1);
302
306
  }
@@ -615,11 +619,14 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
615
619
  };
616
620
 
617
621
  const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
618
- // if (isIntToFloatOp(wasm[wasm.length - 1])) return [
619
- // ...wasm,
620
- // ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
621
- // ];
622
- // 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
+ ];
623
630
 
624
631
  // todo/perf: use knownType and custom bytecode here instead of typeSwitch
625
632
 
@@ -975,6 +982,33 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
975
982
  };
976
983
 
977
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
+
978
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);
979
1013
 
980
1014
  if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
@@ -1281,7 +1315,7 @@ const getNodeType = (scope, node) => {
1281
1315
  }
1282
1316
 
1283
1317
  if (node.type === 'BinaryExpression') {
1284
- if (['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(node.operator)) return TYPES.boolean;
1318
+ if (['==', '===', '!=', '!==', '>', '>=', '<', '<=', 'instanceof'].includes(node.operator)) return TYPES.boolean;
1285
1319
  if (node.operator !== '+') return TYPES.number;
1286
1320
 
1287
1321
  const knownLeft = knownType(scope, getNodeType(scope, node.left));
@@ -1314,7 +1348,7 @@ const getNodeType = (scope, node) => {
1314
1348
  const objectKnownType = knownType(scope, getNodeType(scope, node.object));
1315
1349
  if (objectKnownType != null) {
1316
1350
  if (name === 'length') {
1317
- if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
1351
+ if (typeHasFlag(objectKnownType, TYPE_FLAGS.length)) return TYPES.number;
1318
1352
  else return TYPES.undefined;
1319
1353
  }
1320
1354
 
@@ -1386,9 +1420,9 @@ const countLeftover = wasm => {
1386
1420
 
1387
1421
  if (depth === 0)
1388
1422
  if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1389
- 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)) {}
1390
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++;
1391
- 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;
1392
1426
  else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
1393
1427
  else if (inst[0] === Opcodes.return) count = 0;
1394
1428
  else if (inst[0] === Opcodes.call) {
@@ -1426,6 +1460,18 @@ const generateExp = (scope, decl) => {
1426
1460
  return out;
1427
1461
  };
1428
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
+
1429
1475
  const CTArrayUtil = {
1430
1476
  getLengthI32: pointer => [
1431
1477
  ...number(0, Valtype.i32),
@@ -2014,7 +2060,7 @@ const unhackName = name => {
2014
2060
 
2015
2061
  const knownType = (scope, type) => {
2016
2062
  if (type.length === 1 && type[0][0] === Opcodes.i32_const) {
2017
- return type[0][1];
2063
+ return read_signedLEB128(type[0].slice(1));
2018
2064
  }
2019
2065
 
2020
2066
  if (typedInput && type.length === 1 && type[0][0] === Opcodes.local_get) {
@@ -2301,11 +2347,6 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2301
2347
  const { type, name } = decl.left;
2302
2348
  const [ local, isGlobal ] = lookupName(scope, name);
2303
2349
 
2304
- if (type === 'ObjectPattern') {
2305
- // hack: ignore object parts of `var a = {} = 2`
2306
- return generate(scope, decl.right);
2307
- }
2308
-
2309
2350
  if (isFuncType(decl.right.type)) {
2310
2351
  // hack for a = function () { ... }
2311
2352
  decl.right.id = { name };
@@ -2378,10 +2419,160 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2378
2419
  [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
2379
2420
  ], getNodeType(scope, decl.right), false, name, true)),
2380
2421
  [ Opcodes.local_tee, newValueTmp ],
2381
-
2382
2422
  [ Opcodes.store, 0, ValtypeSize.i32 ]
2383
2423
  ],
2384
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
+
2385
2576
  default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
2386
2577
  }, Blocktype.void),
2387
2578
 
@@ -2765,7 +2956,9 @@ const generateForOf = (scope, decl) => {
2765
2956
  // // todo: we should only do this for strings but we don't know at compile-time :(
2766
2957
  // hack: this is naughty and will break things!
2767
2958
  let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
2768
- 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)) {
2769
2962
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
2770
2963
  0, [ newOut, newPointer ] = makeArray(scope, {
2771
2964
  rawElements: new Array(0)
@@ -2813,6 +3006,7 @@ const generateForOf = (scope, decl) => {
2813
3006
  [ Opcodes.end ],
2814
3007
  [ Opcodes.end ]
2815
3008
  ],
3009
+
2816
3010
  [TYPES.string]: [
2817
3011
  ...setType(scope, leftName, TYPES.string),
2818
3012
 
@@ -2921,6 +3115,7 @@ const generateForOf = (scope, decl) => {
2921
3115
  [ Opcodes.end ],
2922
3116
  [ Opcodes.end ]
2923
3117
  ],
3118
+
2924
3119
  [TYPES.set]: [
2925
3120
  [ Opcodes.loop, Blocktype.void ],
2926
3121
 
@@ -2959,6 +3154,106 @@ const generateForOf = (scope, decl) => {
2959
3154
  [ Opcodes.end ],
2960
3155
  [ Opcodes.end ]
2961
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
+
2962
3257
  default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
2963
3258
  }, Blocktype.void));
2964
3259
 
@@ -3047,13 +3342,14 @@ const generateThrow = (scope, decl) => {
3047
3342
  idx: tags.length
3048
3343
  });
3049
3344
 
3050
- let exceptId = exceptions.push({ constructor, message }) - 1;
3345
+ let exceptId = exceptions.findIndex(x => x.constructor === constructor && x.message === message);
3346
+ if (exceptId === -1) exceptId = exceptions.push({ constructor, message }) - 1;
3051
3347
 
3052
3348
  scope.exceptions ??= [];
3053
3349
  scope.exceptions.push(exceptId);
3054
3350
 
3055
3351
  return [
3056
- [ Opcodes.i32_const, signedLEB128(exceptId) ],
3352
+ ...number(exceptId, Valtype.i32),
3057
3353
  [ Opcodes.throw, tags[0].idx ]
3058
3354
  ];
3059
3355
  }
@@ -3071,6 +3367,61 @@ const generateThrow = (scope, decl) => {
3071
3367
  [ Opcodes.throw, tags[0].idx ]
3072
3368
  ];
3073
3369
  }
3370
+
3371
+ if (exceptionMode === 'stackest') {
3372
+ let message = decl.argument, constructor = null;
3373
+
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
+ }
3379
+
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
+ }
3074
3425
  };
3075
3426
 
3076
3427
  const generateTry = (scope, decl) => {
@@ -3423,6 +3774,19 @@ const withType = (scope, wasm, type) => [
3423
3774
  ...setLastType(scope, type)
3424
3775
  ];
3425
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
+
3426
3790
  const generateMember = (scope, decl, _global, _name) => {
3427
3791
  const name = decl.object.name;
3428
3792
 
@@ -3476,7 +3840,7 @@ const generateMember = (scope, decl, _global, _name) => {
3476
3840
  const type = getNodeType(scope, decl.object);
3477
3841
  const known = knownType(scope, type);
3478
3842
  if (known != null) {
3479
- if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
3843
+ if (typeHasFlag(known, TYPE_FLAGS.length)) return [
3480
3844
  ...generate(scope, decl.object),
3481
3845
  Opcodes.i32_to_u,
3482
3846
 
@@ -3488,7 +3852,9 @@ const generateMember = (scope, decl, _global, _name) => {
3488
3852
  }
3489
3853
 
3490
3854
  return [
3491
- ...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 ],
3492
3858
  [ Opcodes.if, valtypeBinary ],
3493
3859
  ...generate(scope, decl.object),
3494
3860
  Opcodes.i32_to_u,
@@ -3537,7 +3903,9 @@ const generateMember = (scope, decl, _global, _name) => {
3537
3903
  // // todo: we should only do this for strings but we don't know at compile-time :(
3538
3904
  // hack: this is naughty and will break things!
3539
3905
  let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
3540
- 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)) {
3541
3909
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
3542
3910
  0, [ newOut, newPointer ] = makeArray(scope, {
3543
3911
  rawElements: new Array(0)
@@ -3611,6 +3979,82 @@ const generateMember = (scope, decl, _global, _name) => {
3611
3979
  ...setLastType(scope, TYPES.bytestring)
3612
3980
  ],
3613
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
+
3614
4058
  default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
3615
4059
  });
3616
4060
  };
@@ -3788,7 +4232,7 @@ const internalConstrs = {
3788
4232
 
3789
4233
  // todo: check in wasm instead of here
3790
4234
  const literalValue = arg.value ?? 0;
3791
- 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);
3792
4236
 
3793
4237
  return [
3794
4238
  ...out,