porffor 0.14.0-86ac87fa4 → 0.14.0-af9ac5ad4

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.
@@ -203,19 +203,11 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
203
203
 
204
204
  __Porffor_bs: str => [
205
205
  ...makeString(scope, str, global, name, true),
206
-
207
- ...(name ? setType(scope, name, TYPES.bytestring) : [
208
- ...number(TYPES.bytestring, Valtype.i32),
209
- ...setLastType(scope)
210
- ])
206
+ ...(name ? setType(scope, name, TYPES.bytestring) : setLastType(scope, TYPES.bytestring))
211
207
  ],
212
208
  __Porffor_s: str => [
213
209
  ...makeString(scope, str, global, name, false),
214
-
215
- ...(name ? setType(scope, name, TYPES.string) : [
216
- ...number(TYPES.string, Valtype.i32),
217
- ...setLastType(scope)
218
- ])
210
+ ...(name ? setType(scope, name, TYPES.string) : setLastType(scope, TYPES.string))
219
211
  ],
220
212
  };
221
213
 
@@ -403,13 +395,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
403
395
  [ Opcodes.if, Valtype.i32 ],
404
396
  ...right,
405
397
  // note type
406
- ...rightType,
407
- ...setLastType(scope),
398
+ ...setLastType(scope, rightType),
408
399
  [ Opcodes.else ],
409
400
  [ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
410
401
  // note type
411
- ...leftType,
412
- ...setLastType(scope),
402
+ ...setLastType(scope, leftType),
413
403
  [ Opcodes.end ],
414
404
  Opcodes.i32_from
415
405
  ];
@@ -422,13 +412,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
422
412
  [ Opcodes.if, valtypeBinary ],
423
413
  ...right,
424
414
  // note type
425
- ...rightType,
426
- ...setLastType(scope),
415
+ ...setLastType(scope, rightType),
427
416
  [ Opcodes.else ],
428
417
  [ Opcodes.local_get, localTmp(scope, 'logictmp') ],
429
418
  // note type
430
- ...leftType,
431
- ...setLastType(scope),
419
+ ...setLastType(scope, leftType),
432
420
  [ Opcodes.end ]
433
421
  ];
434
422
  };
@@ -456,11 +444,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
456
444
  ...number(0, Valtype.i32), // base 0 for store later
457
445
 
458
446
  ...number(pointer, Valtype.i32),
459
- [ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
447
+ [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
460
448
  [ Opcodes.local_tee, leftLength ],
461
449
 
462
450
  [ Opcodes.local_get, rightPointer ],
463
- [ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
451
+ [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
464
452
  [ Opcodes.local_tee, rightLength ],
465
453
 
466
454
  [ Opcodes.i32_add ],
@@ -516,11 +504,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
516
504
  ...number(0, Valtype.i32), // base 0 for store later
517
505
 
518
506
  [ Opcodes.local_get, leftPointer ],
519
- [ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
507
+ [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
520
508
  [ Opcodes.local_tee, leftLength ],
521
509
 
522
510
  [ Opcodes.local_get, rightPointer ],
523
- [ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
511
+ [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
524
512
  [ Opcodes.local_tee, rightLength ],
525
513
 
526
514
  [ Opcodes.i32_add ],
@@ -598,11 +586,11 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
598
586
 
599
587
  // get lengths
600
588
  [ Opcodes.local_get, leftPointer ],
601
- [ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
589
+ [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
602
590
  [ Opcodes.local_tee, leftLength ],
603
591
 
604
592
  [ Opcodes.local_get, rightPointer ],
605
- [ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
593
+ [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
606
594
 
607
595
  // fast path: check leftLength != rightLength
608
596
  [ Opcodes.i32_ne ],
@@ -693,6 +681,25 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
693
681
  ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
694
682
 
695
683
  // ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
684
+ // [ Opcodes.i32_eqz ],
685
+
686
+ // ...(intIn ? [
687
+ // ...number(0, Valtype.i32),
688
+ // [ Opcodes.i32_gt_s ]
689
+ // ] : [
690
+ // ...number(0),
691
+ // [ Opcodes.f64_gt ]
692
+ // ]),
693
+
694
+ // ...(intOut ? [] : [ Opcodes.i32_from ]),
695
+
696
+ // ...(intIn ? [] : [ Opcodes.i32_to ]),
697
+ // [ Opcodes.if, intOut ? Valtype.i32 : valtypeBinary ],
698
+ // ...number(1, intOut ? Valtype.i32 : valtypeBinary),
699
+ // [ Opcodes.else ],
700
+ // ...number(0, intOut ? Valtype.i32 : valtypeBinary),
701
+ // [ Opcodes.end ],
702
+
696
703
  ...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
697
704
 
698
705
  /* Opcodes.eqz,
@@ -1056,7 +1063,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
1056
1063
  });
1057
1064
  };
1058
1065
 
1059
- const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
1066
+ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], callsSelf = false }) => {
1060
1067
  const existing = funcs.find(x => x.name === name);
1061
1068
  if (existing) return existing;
1062
1069
 
@@ -1104,6 +1111,12 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1104
1111
  index: currentFuncIndex++
1105
1112
  };
1106
1113
 
1114
+ if (callsSelf) for (const inst of wasm) {
1115
+ if (inst[0] === Opcodes.call && inst[1] === -1) {
1116
+ inst[1] = func.index;
1117
+ }
1118
+ }
1119
+
1107
1120
  funcs.push(func);
1108
1121
  funcIndex[name] = func.index;
1109
1122
 
@@ -1197,9 +1210,10 @@ const getLastType = scope => {
1197
1210
  return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
1198
1211
  };
1199
1212
 
1200
- const setLastType = scope => {
1201
- return [ [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ] ];
1202
- };
1213
+ const setLastType = (scope, type = []) => [
1214
+ ...(typeof type === 'number' ? number(type, Valtype.i32) : type),
1215
+ [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
1216
+ ];
1203
1217
 
1204
1218
  const getNodeType = (scope, node) => {
1205
1219
  const ret = (() => {
@@ -1260,7 +1274,17 @@ const getNodeType = (scope, node) => {
1260
1274
 
1261
1275
  const func = spl[spl.length - 1];
1262
1276
  const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
1263
- if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
1277
+ if (protoFuncs.length === 1) {
1278
+ if (protoFuncs[0].returnType) return protoFuncs[0].returnType;
1279
+ }
1280
+
1281
+ if (protoFuncs.length > 0) {
1282
+ if (scope.locals['#last_type']) return getLastType(scope);
1283
+
1284
+ // presume
1285
+ // todo: warn here?
1286
+ return TYPES.number;
1287
+ }
1264
1288
  }
1265
1289
 
1266
1290
  if (name.startsWith('__Porffor_wasm_')) {
@@ -1355,22 +1379,27 @@ const getNodeType = (scope, node) => {
1355
1379
  }
1356
1380
 
1357
1381
  if (node.type === 'MemberExpression') {
1358
- // hack: if something.name, string type
1359
- if (node.property.name === 'name') {
1360
- if (hasFuncWithName(node.object.name)) {
1361
- return TYPES.bytestring;
1362
- } else {
1363
- return TYPES.undefined;
1364
- }
1382
+ const name = node.property.name;
1383
+
1384
+ if (name === 'length') {
1385
+ if (hasFuncWithName(node.object.name)) return TYPES.number;
1386
+ if (Prefs.fastLength) return TYPES.number;
1365
1387
  }
1366
1388
 
1367
- // hack: if something.length, number type
1368
- if (node.property.name === 'length') return TYPES.number;
1369
1389
 
1370
- // ts hack
1371
- if (scope.locals[node.object.name]?.metadata?.type === TYPES.string) return TYPES.string;
1372
- if (scope.locals[node.object.name]?.metadata?.type === TYPES.bytestring) return TYPES.bytestring;
1373
- if (scope.locals[node.object.name]?.metadata?.type === TYPES.array) return TYPES.number;
1390
+ const objectKnownType = knownType(scope, getNodeType(scope, node.object));
1391
+ if (objectKnownType != null) {
1392
+ if (name === 'length') {
1393
+ if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
1394
+ else return TYPES.undefined;
1395
+ }
1396
+
1397
+ if (node.computed) {
1398
+ if (objectKnownType === TYPES.string) return TYPES.string;
1399
+ if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
1400
+ if (objectKnownType === TYPES.array) return TYPES.number;
1401
+ }
1402
+ }
1374
1403
 
1375
1404
  if (scope.locals['#last_type']) return getLastType(scope);
1376
1405
 
@@ -1473,7 +1502,7 @@ const disposeLeftover = wasm => {
1473
1502
  const generateExp = (scope, decl) => {
1474
1503
  const expression = decl.expression;
1475
1504
 
1476
- const out = generate(scope, expression, undefined, undefined, true);
1505
+ const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
1477
1506
  disposeLeftover(out);
1478
1507
 
1479
1508
  return out;
@@ -1564,16 +1593,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1564
1593
  out.splice(out.length - 1, 1);
1565
1594
 
1566
1595
  const finalStatement = parsed.body[parsed.body.length - 1];
1567
- out.push(
1568
- ...getNodeType(scope, finalStatement),
1569
- ...setLastType(scope)
1570
- );
1596
+ out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
1571
1597
  } else if (countLeftover(out) === 0) {
1572
1598
  out.push(...number(UNDEFINED));
1573
- out.push(
1574
- ...number(TYPES.undefined, Valtype.i32),
1575
- ...setLastType(scope)
1576
- );
1599
+ out.push(...setLastType(scope, TYPES.undefined));
1577
1600
  }
1578
1601
 
1579
1602
  // if (lastInst && lastInst[0] === Opcodes.drop) {
@@ -1625,8 +1648,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1625
1648
  [ Opcodes.call, idx ],
1626
1649
  Opcodes.i32_from_u,
1627
1650
 
1628
- ...number(TYPES.boolean, Valtype.i32),
1629
- ...setLastType(scope)
1651
+ ...setLastType(scope, TYPES.boolean)
1630
1652
  ];
1631
1653
  }
1632
1654
 
@@ -1698,9 +1720,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1698
1720
  if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
1699
1721
  protoBC[x] = [
1700
1722
  ...RTArrayUtil.getLength(getPointer),
1701
-
1702
- ...number(TYPES.number, Valtype.i32),
1703
- ...setLastType(scope)
1723
+ ...setLastType(scope, TYPES.number)
1704
1724
  ];
1705
1725
  continue;
1706
1726
  }
@@ -1719,7 +1739,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1719
1739
  getI32: () => RTArrayUtil.getLengthI32(getPointer),
1720
1740
  set: value => RTArrayUtil.setLength(getPointer, value),
1721
1741
  setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
1722
- }, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
1742
+ }, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
1723
1743
  return makeArray(scope, {
1724
1744
  rawElements: new Array(length)
1725
1745
  }, _global, _name, true, itemType);
@@ -1733,9 +1753,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1733
1753
  protoBC[x] = [
1734
1754
  [ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
1735
1755
  ...protoOut,
1736
-
1737
- ...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
1738
- ...setLastType(scope),
1756
+ ...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
1739
1757
  [ Opcodes.end ]
1740
1758
  ];
1741
1759
  }
@@ -1904,6 +1922,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1904
1922
  const arg = args[i];
1905
1923
  out = out.concat(generate(scope, arg));
1906
1924
 
1925
+ // todo: this should be used instead of the too many args thing above (by removing that)
1907
1926
  if (i >= paramCount) {
1908
1927
  // over param count of func, drop arg
1909
1928
  out.push([ Opcodes.drop ]);
@@ -1988,8 +2007,11 @@ const knownType = (scope, type) => {
1988
2007
  const idx = type[0][1];
1989
2008
 
1990
2009
  // type idx = var idx + 1
1991
- const v = Object.values(scope.locals).find(x => x.idx === idx - 1);
1992
- if (v.metadata?.type != null) return v.metadata.type;
2010
+ const name = Object.values(scope.locals).find(x => x.idx === idx)?.name;
2011
+ if (name) {
2012
+ const local = scope.locals[name];
2013
+ if (local.metadata?.type != null) return v.metadata.type;
2014
+ }
1993
2015
  }
1994
2016
 
1995
2017
  return null;
@@ -2120,6 +2142,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
2120
2142
  return out;
2121
2143
  };
2122
2144
 
2145
+ const typeIsOneOf = (type, types, valtype = Valtype.i32) => {
2146
+ const out = [];
2147
+
2148
+ for (let i = 0; i < types.length; i++) {
2149
+ out.push(...type, ...number(types[i], valtype), valtype === Valtype.f64 ? [ Opcodes.f64_eq ] : [ Opcodes.i32_eq ]);
2150
+ if (i !== 0) out.push([ Opcodes.i32_or ]);
2151
+ }
2152
+
2153
+ return out;
2154
+ };
2155
+
2123
2156
  const allocVar = (scope, name, global = false, type = true) => {
2124
2157
  const target = global ? globals : scope.locals;
2125
2158
 
@@ -2136,7 +2169,7 @@ const allocVar = (scope, name, global = false, type = true) => {
2136
2169
 
2137
2170
  if (type) {
2138
2171
  let typeIdx = global ? globalInd++ : scope.localInd++;
2139
- target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
2172
+ target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
2140
2173
  }
2141
2174
 
2142
2175
  return idx;
@@ -2349,18 +2382,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2349
2382
  Opcodes.i32_to_u,
2350
2383
 
2351
2384
  // turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
2352
- ...number(ValtypeSize[valtype], Valtype.i32),
2385
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
2353
2386
  [ Opcodes.i32_mul ],
2354
2387
  ...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
2355
2388
  ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2356
2389
 
2357
2390
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2358
2391
  [ Opcodes.local_get, pointerTmp ],
2359
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2360
- ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2392
+ [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2393
+ ], generate(scope, decl.right), [
2394
+ [ Opcodes.local_get, pointerTmp ],
2395
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
2396
+ ], getNodeType(scope, decl.right), false, name, true)),
2361
2397
  [ Opcodes.local_tee, newValueTmp ],
2362
2398
 
2363
- [ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2399
+ [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2364
2400
  ],
2365
2401
 
2366
2402
  default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
@@ -2614,21 +2650,16 @@ const generateConditional = (scope, decl) => {
2614
2650
  out.push([ Opcodes.if, valtypeBinary ]);
2615
2651
  depth.push('if');
2616
2652
 
2617
- out.push(...generate(scope, decl.consequent));
2618
-
2619
- // note type
2620
2653
  out.push(
2621
- ...getNodeType(scope, decl.consequent),
2622
- ...setLastType(scope)
2654
+ ...generate(scope, decl.consequent),
2655
+ ...setLastType(scope, getNodeType(scope, decl.consequent))
2623
2656
  );
2624
2657
 
2625
2658
  out.push([ Opcodes.else ]);
2626
- out.push(...generate(scope, decl.alternate));
2627
2659
 
2628
- // note type
2629
2660
  out.push(
2630
- ...getNodeType(scope, decl.alternate),
2631
- ...setLastType(scope)
2661
+ ...generate(scope, decl.alternate),
2662
+ ...setLastType(scope, getNodeType(scope, decl.alternate))
2632
2663
  );
2633
2664
 
2634
2665
  out.push([ Opcodes.end ]);
@@ -2776,12 +2807,15 @@ const generateForOf = (scope, decl) => {
2776
2807
  // todo: optimize away counter and use end pointer
2777
2808
  out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
2778
2809
  [TYPES.array]: [
2779
- ...setType(scope, leftName, TYPES.number),
2780
-
2781
2810
  [ Opcodes.loop, Blocktype.void ],
2782
2811
 
2783
2812
  [ Opcodes.local_get, pointer ],
2784
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
2813
+ [ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
2814
+
2815
+ ...setType(scope, leftName, [
2816
+ [ Opcodes.local_get, pointer ],
2817
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
2818
+ ]),
2785
2819
 
2786
2820
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2787
2821
 
@@ -2790,9 +2824,9 @@ const generateForOf = (scope, decl) => {
2790
2824
  ...generate(scope, decl.body),
2791
2825
  [ Opcodes.end ],
2792
2826
 
2793
- // increment iter pointer by valtype size
2827
+ // increment iter pointer by valtype size + 1
2794
2828
  [ Opcodes.local_get, pointer ],
2795
- ...number(ValtypeSize[valtype], Valtype.i32),
2829
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
2796
2830
  [ Opcodes.i32_add ],
2797
2831
  [ Opcodes.local_set, pointer ],
2798
2832
 
@@ -3109,7 +3143,7 @@ const getAllocType = itemType => {
3109
3143
  }
3110
3144
  };
3111
3145
 
3112
- const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
3146
+ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
3113
3147
  const out = [];
3114
3148
 
3115
3149
  scope.arrays ??= new Map();
@@ -3172,7 +3206,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3172
3206
 
3173
3207
  const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
3174
3208
 
3175
- // store length as 0th array
3209
+ // store length
3176
3210
  out.push(
3177
3211
  ...pointerWasm,
3178
3212
  ...number(length, Valtype.i32),
@@ -3180,14 +3214,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3180
3214
  );
3181
3215
 
3182
3216
  const storeOp = StoreOps[itemType];
3183
-
3217
+ const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
3184
3218
  if (!initEmpty) for (let i = 0; i < length; i++) {
3185
3219
  if (elements[i] == null) continue;
3186
3220
 
3221
+ const offset = ValtypeSize.i32 + i * sizePerEl;
3187
3222
  out.push(
3188
3223
  ...pointerWasm,
3189
3224
  ...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
3190
- [ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
3225
+ [ storeOp, 0, ...unsignedLEB128(offset) ],
3226
+ ...(!typed ? [] : [ // typed presumes !useRawElements
3227
+ ...pointerWasm,
3228
+ ...getNodeType(scope, elements[i]),
3229
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
3230
+ ])
3191
3231
  );
3192
3232
  }
3193
3233
 
@@ -3197,6 +3237,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3197
3237
  return [ out, pointer ];
3198
3238
  };
3199
3239
 
3240
+ const storeArray = (scope, array, index, element, aotPointer = null) => {
3241
+ if (!Array.isArray(element)) element = generate(scope, element);
3242
+ if (typeof index === 'number') index = number(index);
3243
+
3244
+ const offset = localTmp(scope, '#storeArray_offset', Valtype.i32);
3245
+
3246
+ return [
3247
+ // calculate offset
3248
+ ...index,
3249
+ Opcodes.i32_to_u,
3250
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3251
+ [ Opcodes.i32_mul ],
3252
+ ...(aotPointer ? [] : [
3253
+ ...array,
3254
+ Opcodes.i32_to_u,
3255
+ [ Opcodes.i32_add ],
3256
+ ]),
3257
+ [ Opcodes.local_set, offset ],
3258
+
3259
+ // store value
3260
+ [ Opcodes.local_get, offset ],
3261
+ ...generate(scope, element),
3262
+ [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3263
+
3264
+ // store type
3265
+ [ Opcodes.local_get, offset ],
3266
+ ...getNodeType(scope, element),
3267
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3268
+ ];
3269
+ };
3270
+
3271
+ const loadArray = (scope, array, index, aotPointer = null) => {
3272
+ if (typeof index === 'number') index = number(index);
3273
+
3274
+ const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
3275
+
3276
+ return [
3277
+ // calculate offset
3278
+ ...index,
3279
+ Opcodes.i32_to_u,
3280
+ ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3281
+ [ Opcodes.i32_mul ],
3282
+ ...(aotPointer ? [] : [
3283
+ ...array,
3284
+ Opcodes.i32_to_u,
3285
+ [ Opcodes.i32_add ],
3286
+ ]),
3287
+ [ Opcodes.local_set, offset ],
3288
+
3289
+ // load value
3290
+ [ Opcodes.local_get, offset ],
3291
+ [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3292
+
3293
+ // load type
3294
+ [ Opcodes.local_get, offset ],
3295
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3296
+ ];
3297
+ };
3298
+
3200
3299
  const byteStringable = str => {
3201
3300
  if (!Prefs.bytestring) return false;
3202
3301
 
@@ -3225,7 +3324,7 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
3225
3324
  };
3226
3325
 
3227
3326
  const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
3228
- return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
3327
+ return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
3229
3328
  };
3230
3329
 
3231
3330
  const generateObject = (scope, decl, global = false, name = '$undeclared') => {
@@ -3233,17 +3332,20 @@ const generateObject = (scope, decl, global = false, name = '$undeclared') => {
3233
3332
 
3234
3333
  return [
3235
3334
  ...number(1),
3236
-
3237
- ...number(TYPES.object, Valtype.i32),
3238
- ...setLastType(scope)
3335
+ ...setLastType(scope, TYPES.object)
3239
3336
  ];
3240
3337
  };
3241
3338
 
3339
+ const withType = (scope, wasm, type) => [
3340
+ ...wasm,
3341
+ ...setLastType(scope, type)
3342
+ ];
3343
+
3242
3344
  const generateMember = (scope, decl, _global, _name) => {
3243
3345
  const name = decl.object.name;
3244
3346
  const pointer = scope.arrays?.get(name);
3245
3347
 
3246
- const aotPointer = Prefs.aotPointerOpt && pointer != null;
3348
+ const aotPointer = Prefs.aotPointerOpt && pointer;
3247
3349
 
3248
3350
  // hack: .name
3249
3351
  if (decl.property.name === 'name') {
@@ -3253,9 +3355,9 @@ const generateMember = (scope, decl, _global, _name) => {
3253
3355
  // eg: __String_prototype_toLowerCase -> toLowerCase
3254
3356
  if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
3255
3357
 
3256
- return makeString(scope, nameProp, _global, _name, true);
3358
+ return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
3257
3359
  } else {
3258
- return generate(scope, DEFAULT_VALUE);
3360
+ return withType(scope, number(0), TYPES.undefined);
3259
3361
  }
3260
3362
  }
3261
3363
 
@@ -3265,7 +3367,7 @@ const generateMember = (scope, decl, _global, _name) => {
3265
3367
  if (func) {
3266
3368
  const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
3267
3369
  const typedParams = userFunc || builtinFuncs[name]?.typedParams;
3268
- return number(typedParams ? func.params.length / 2 : func.params.length);
3370
+ return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
3269
3371
  }
3270
3372
 
3271
3373
  if (builtinFuncs[name + '$constructor']) {
@@ -3275,24 +3377,88 @@ const generateMember = (scope, decl, _global, _name) => {
3275
3377
  const constructorFunc = builtinFuncs[name + '$constructor'];
3276
3378
  const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
3277
3379
 
3278
- return number(Math.max(regularParams, constructorParams));
3380
+ return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
3381
+ }
3382
+
3383
+ if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
3384
+ if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
3385
+ if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
3386
+
3387
+ if (Prefs.fastLength) {
3388
+ // presume valid length object
3389
+ return [
3390
+ ...(aotPointer ? number(0, Valtype.i32) : [
3391
+ ...generate(scope, decl.object),
3392
+ Opcodes.i32_to_u
3393
+ ]),
3394
+
3395
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3396
+ Opcodes.i32_from_u
3397
+ ];
3279
3398
  }
3280
3399
 
3281
- if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
3282
- if (importedFuncs[name]) return number(importedFuncs[name].params);
3283
- if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
3400
+ const type = getNodeType(scope, decl.object);
3401
+ const known = knownType(scope, type);
3402
+ if (known != null) {
3403
+ if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
3404
+ ...(aotPointer ? number(0, Valtype.i32) : [
3405
+ ...generate(scope, decl.object),
3406
+ Opcodes.i32_to_u
3407
+ ]),
3408
+
3409
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3410
+ Opcodes.i32_from_u
3411
+ ];
3412
+
3413
+ return number(0);
3414
+ }
3284
3415
 
3285
3416
  return [
3286
- ...(aotPointer ? number(0, Valtype.i32) : [
3287
- ...generate(scope, decl.object),
3288
- Opcodes.i32_to_u
3289
- ]),
3417
+ ...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
3418
+ [ Opcodes.if, valtypeBinary ],
3419
+ ...(aotPointer ? number(0, Valtype.i32) : [
3420
+ ...generate(scope, decl.object),
3421
+ Opcodes.i32_to_u
3422
+ ]),
3423
+
3424
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3425
+ Opcodes.i32_from_u,
3290
3426
 
3291
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128((aotPointer ? pointer : 0)) ],
3292
- Opcodes.i32_from_u
3427
+ ...setLastType(scope, TYPES.number),
3428
+ [ Opcodes.else ],
3429
+ ...number(0),
3430
+ ...setLastType(scope, TYPES.undefined),
3431
+ [ Opcodes.end ]
3293
3432
  ];
3294
3433
  }
3295
3434
 
3435
+ // todo: generate this array procedurally during builtinFuncs creation
3436
+ if (['size', 'description'].includes(decl.property.name)) {
3437
+ const bc = {};
3438
+ const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
3439
+
3440
+ if (cands.length > 0) {
3441
+ for (const x of cands) {
3442
+ const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
3443
+ if (type == null) continue;
3444
+
3445
+ bc[type] = generateCall(scope, {
3446
+ callee: {
3447
+ type: 'Identifier',
3448
+ name: x
3449
+ },
3450
+ arguments: [ decl.object ],
3451
+ _protoInternalCall: true
3452
+ });
3453
+ }
3454
+ }
3455
+
3456
+ return typeSwitch(scope, getNodeType(scope, decl.object), {
3457
+ ...bc,
3458
+ default: withType(scope, number(0), TYPES.undefined)
3459
+ }, valtypeBinary);
3460
+ }
3461
+
3296
3462
  const object = generate(scope, decl.object);
3297
3463
  const property = generate(scope, decl.property);
3298
3464
 
@@ -3307,24 +3473,7 @@ const generateMember = (scope, decl, _global, _name) => {
3307
3473
 
3308
3474
  return typeSwitch(scope, getNodeType(scope, decl.object), {
3309
3475
  [TYPES.array]: [
3310
- // get index as valtype
3311
- ...property,
3312
-
3313
- // convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
3314
- Opcodes.i32_to_u,
3315
- ...number(ValtypeSize[valtype], Valtype.i32),
3316
- [ Opcodes.i32_mul ],
3317
-
3318
- ...(aotPointer ? [] : [
3319
- ...object,
3320
- Opcodes.i32_to_u,
3321
- [ Opcodes.i32_add ]
3322
- ]),
3323
-
3324
- // read from memory
3325
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3326
-
3327
- ...number(TYPES.number, Valtype.i32),
3476
+ ...loadArray(scope, object, property, aotPointer),
3328
3477
  ...setLastType(scope)
3329
3478
  ],
3330
3479
 
@@ -3355,9 +3504,7 @@ const generateMember = (scope, decl, _global, _name) => {
3355
3504
 
3356
3505
  // return new string (page)
3357
3506
  ...number(newPointer),
3358
-
3359
- ...number(TYPES.string, Valtype.i32),
3360
- ...setLastType(scope)
3507
+ ...setLastType(scope, TYPES.string)
3361
3508
  ],
3362
3509
  [TYPES.bytestring]: [
3363
3510
  // setup new/out array
@@ -3383,9 +3530,7 @@ const generateMember = (scope, decl, _global, _name) => {
3383
3530
 
3384
3531
  // return new string (page)
3385
3532
  ...number(newPointer),
3386
-
3387
- ...number(TYPES.bytestring, Valtype.i32),
3388
- ...setLastType(scope)
3533
+ ...setLastType(scope, TYPES.bytestring)
3389
3534
  ],
3390
3535
 
3391
3536
  default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
@@ -3410,7 +3555,7 @@ const objectHack = node => {
3410
3555
  if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
3411
3556
 
3412
3557
  // if .name or .length, give up (hack within a hack!)
3413
- if (['name', 'length'].includes(node.property.name)) {
3558
+ if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
3414
3559
  node.object = objectHack(node.object);
3415
3560
  return;
3416
3561
  }