porffor 0.47.6 → 0.47.7

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.
@@ -8,8 +8,8 @@ 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';
11
- import {} from './prefs.js';
12
- import makeAllocator from './allocators.js';
11
+ import './prefs.js';
12
+ import { allocPage as _allocPage, allocBytes, allocStr, nameToReason } from './allocator.js';
13
13
 
14
14
  let globals = {};
15
15
  let tags = [];
@@ -18,7 +18,6 @@ let exceptions = [];
18
18
  let funcIndex = {};
19
19
  let currentFuncIndex = importedFuncs.length;
20
20
  let builtinFuncs = {}, builtinVars = {}, prototypeFuncs = {};
21
- let allocator;
22
21
 
23
22
  class TodoError extends Error {
24
23
  constructor(message) {
@@ -191,6 +190,7 @@ const forceDuoValtype = (scope, wasm, forceValtype) => [
191
190
  ];
192
191
 
193
192
  const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
193
+ if (valueUnused && !Prefs.optUnused) valueUnused = false;
194
194
  if (astCache.has(decl)) return astCache.get(decl);
195
195
 
196
196
  switch (decl.type) {
@@ -240,7 +240,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
240
240
  return cacheAst(decl, generateVar(scope, decl));
241
241
 
242
242
  case 'AssignmentExpression':
243
- return cacheAst(decl, generateAssign(scope, decl));
243
+ return cacheAst(decl, generateAssign(scope, decl, global, name, valueUnused));
244
244
 
245
245
  case 'UnaryExpression':
246
246
  return cacheAst(decl, generateUnary(scope, decl));
@@ -297,7 +297,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
297
297
  return cacheAst(decl, [[ Opcodes.call, importedFuncs.debugger ]]);
298
298
 
299
299
  case 'ArrayExpression':
300
- return cacheAst(decl, generateArray(scope, decl, global, name));
300
+ return cacheAst(decl, generateArray(scope, decl, global, name, globalThis.precompile));
301
301
 
302
302
  case 'ObjectExpression':
303
303
  return cacheAst(decl, generateObject(scope, decl, global, name));
@@ -348,10 +348,12 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
348
348
  return cacheAst(decl, []);
349
349
  }
350
350
 
351
- return cacheAst(decl, todo(scope, `no generation for ${decl.type}!`));
351
+ return cacheAst(decl, todo(scope, `no generation for ${decl.type}!`, !decl.type.endsWith('Statement') && !decl.type.endsWith('Declaration')));
352
352
  }
353
353
  };
354
354
 
355
+ const optional = (op, clause = op.at(-1)) => clause || clause === 0 ? (Array.isArray(op[0]) ? op : [ op ]) : [];
356
+
355
357
  const lookupName = (scope, name) => {
356
358
  if (Object.hasOwn(scope.locals, name)) return [ scope.locals[name], false ];
357
359
  if (Object.hasOwn(globals, name)) return [ globals[name], true ];
@@ -363,7 +365,7 @@ const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysV
363
365
  ...generate(scope, {
364
366
  type: 'ThrowStatement',
365
367
  argument: {
366
- type: 'NewExpression',
368
+ type: 'CallExpression',
367
369
  callee: {
368
370
  type: 'Identifier',
369
371
  name: constructor
@@ -415,7 +417,7 @@ const generateIdent = (scope, decl) => {
415
417
 
416
418
  return generateArray(scope, {
417
419
  elements: names.map(x => ({ type: 'Identifier', name: x }))
418
- }, false, '#arguments');
420
+ }, false, '#arguments', true);
419
421
  }
420
422
 
421
423
  // no local var with name
@@ -1124,7 +1126,7 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
1124
1126
 
1125
1127
  const asmFuncToAsm = (scope, func) => {
1126
1128
  return func(scope, {
1127
- Valtype, Opcodes, TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
1129
+ Valtype, Opcodes, TYPES, TYPE_NAMES, typeSwitch, makeString, allocPage, internalThrow,
1128
1130
  getNodeType, generate, generateIdent,
1129
1131
  builtin: (n, offset = false) => {
1130
1132
  let idx = funcIndex[n] ?? importedFuncs[n];
@@ -1197,6 +1199,10 @@ const asmFuncToAsm = (scope, func) => {
1197
1199
  return [];
1198
1200
  }, 0 ] ];
1199
1201
  }
1202
+ },
1203
+ i32ify: wasm => {
1204
+ wasm.push(Opcodes.i32_to_u);
1205
+ return wasm;
1200
1206
  }
1201
1207
  });
1202
1208
  };
@@ -1219,6 +1225,7 @@ const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTy
1219
1225
  }
1220
1226
 
1221
1227
  for (const x in _data) {
1228
+ if (data.find(y => y.page === x)) return;
1222
1229
  data.push({ page: x, bytes: _data[x] });
1223
1230
  }
1224
1231
 
@@ -1628,7 +1635,7 @@ const generateLiteral = (scope, decl, global, name) => {
1628
1635
  return number(decl.value ? 1 : 0);
1629
1636
 
1630
1637
  case 'string':
1631
- return makeString(scope, decl.value, global, name);
1638
+ return makeString(scope, decl.value);
1632
1639
 
1633
1640
  default:
1634
1641
  return todo(scope, `cannot generate literal of type ${typeof decl.value}`, true);
@@ -1653,14 +1660,17 @@ const countLeftover = wasm => {
1653
1660
  if (inst[0] === Opcodes.end) depth--;
1654
1661
 
1655
1662
  if (depth === 0)
1656
- if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1663
+ if ([Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1657
1664
  else if ([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)) {}
1658
1665
  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++;
1659
1666
  else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.f32_store, Opcodes.i32_store16, Opcodes.i32_store8, Opcodes.select].includes(inst[0])) count -= 2;
1660
1667
  else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
1661
1668
  else if (inst[0] === Opcodes.return) count = 0;
1662
1669
  else if (inst[0] === Opcodes.catch) count += 2;
1663
- else if (inst[0] === Opcodes.call) {
1670
+ else if (inst[0] === Opcodes.throw) {
1671
+ count--;
1672
+ if ((Prefs.exceptionMode ?? 'stack') === 'stack' || (globalThis.precompile && inst[1] === 1)) count--;
1673
+ } else if (inst[0] === Opcodes.call) {
1664
1674
  if (inst[1] < importedFuncs.length) {
1665
1675
  const func = importedFuncs[inst[1]];
1666
1676
  count = count - func.params + func.returns;
@@ -1674,7 +1684,7 @@ const countLeftover = wasm => {
1674
1684
  count += 2; // fixed return (value, type)
1675
1685
  } else count--;
1676
1686
 
1677
- // console.log(count, depth, decompile([ inst ]).slice(0, -1));
1687
+ // console.log(count, depth, decompile([ inst ], undefined, undefined, undefined, undefined, undefined, funcs).slice(0, -1));
1678
1688
  }
1679
1689
 
1680
1690
  return count;
@@ -1694,7 +1704,7 @@ const generateExp = (scope, decl) => {
1694
1704
  }
1695
1705
  }
1696
1706
 
1697
- const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
1707
+ const out = generate(scope, expression, undefined, undefined, !scope.inEval);
1698
1708
  disposeLeftover(out);
1699
1709
 
1700
1710
  return out;
@@ -1716,33 +1726,7 @@ const generateChain = (scope, decl) => {
1716
1726
  return generate(scope, decl.expression);
1717
1727
  };
1718
1728
 
1719
- const CTArrayUtil = {
1720
- getLengthI32: pointer => [
1721
- ...number(0, Valtype.i32),
1722
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ]
1723
- ],
1724
-
1725
- getLength: pointer => [
1726
- ...number(0, Valtype.i32),
1727
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
1728
- Opcodes.i32_from_u
1729
- ],
1730
-
1731
- setLengthI32: (pointer, value) => [
1732
- ...number(0, Valtype.i32),
1733
- ...value,
1734
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ]
1735
- ],
1736
-
1737
- setLength: (pointer, value) => [
1738
- ...number(0, Valtype.i32),
1739
- ...value,
1740
- Opcodes.i32_to_u,
1741
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ]
1742
- ]
1743
- };
1744
-
1745
- const RTArrayUtil = {
1729
+ const ArrayUtil = {
1746
1730
  getLengthI32: pointer => [
1747
1731
  ...pointer,
1748
1732
  [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ]
@@ -1969,10 +1953,12 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1969
1953
  throw e;
1970
1954
  }
1971
1955
 
1956
+ scope.inEval = true;
1972
1957
  const out = generate(scope, {
1973
1958
  type: 'BlockStatement',
1974
1959
  body: parsed.body
1975
1960
  });
1961
+ scope.inEval = false;
1976
1962
 
1977
1963
  const lastInst = out[out.length - 1];
1978
1964
  if (lastInst && lastInst[0] === Opcodes.drop) {
@@ -1985,12 +1971,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1985
1971
  out.push(...setLastType(scope, TYPES.undefined));
1986
1972
  }
1987
1973
 
1988
- // if (lastInst && lastInst[0] === Opcodes.drop) {
1989
- // out.splice(out.length - 1, 1);
1990
- // } else if (countLeftover(out) === 0) {
1991
- // out.push(...number(UNDEFINED));
1992
- // }
1993
-
1994
1974
  return out;
1995
1975
  }
1996
1976
 
@@ -2200,7 +2180,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2200
2180
  const protoFunc = protoCands[x];
2201
2181
  if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
2202
2182
  protoBC[x] = [
2203
- ...RTArrayUtil.getLength(getPointer),
2183
+ ...ArrayUtil.getLength(getPointer),
2204
2184
  ...setLastType(scope, TYPES.number)
2205
2185
  ];
2206
2186
  continue;
@@ -2214,19 +2194,15 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2214
2194
  const protoOut = protoFunc(getPointer, {
2215
2195
  getCachedI32: () => [ [ Opcodes.local_get, lengthLocal ] ],
2216
2196
  setCachedI32: () => [ [ Opcodes.local_set, lengthLocal ] ],
2217
- get: () => RTArrayUtil.getLength(getPointer),
2218
- getI32: () => RTArrayUtil.getLengthI32(getPointer),
2219
- set: value => RTArrayUtil.setLength(getPointer, value),
2220
- setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
2197
+ get: () => ArrayUtil.getLength(getPointer),
2198
+ getI32: () => ArrayUtil.getLengthI32(getPointer),
2199
+ set: value => ArrayUtil.setLength(getPointer, value),
2200
+ setI32: value => ArrayUtil.setLengthI32(getPointer, value)
2221
2201
  },
2222
2202
  generate(scope, decl.arguments[0] ?? DEFAULT_VALUE()),
2223
2203
  getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE()),
2224
2204
  protoLocal, protoLocal2,
2225
- (length, itemType) => {
2226
- return makeArray(scope, {
2227
- rawElements: new Array(length)
2228
- }, _global, _name, true, itemType, true);
2229
- },
2205
+ () => [ [ Opcodes.call, includeBuiltin(scope, '__Porffor_allocate').index ] ],
2230
2206
  () => {
2231
2207
  optUnused = true;
2232
2208
  return unusedValue;
@@ -2252,7 +2228,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2252
2228
  ] : []),
2253
2229
 
2254
2230
  ...(useLengthCache ? [
2255
- ...RTArrayUtil.getLengthI32(getPointer),
2231
+ ...ArrayUtil.getLengthI32(getPointer),
2256
2232
  [ Opcodes.local_set, lengthLocal ],
2257
2233
  ] : []),
2258
2234
 
@@ -3335,7 +3311,6 @@ const isIdentAssignable = (scope, name, op = '=') => {
3335
3311
  return false;
3336
3312
  };
3337
3313
 
3338
- // todo: optimize this func for valueUnused
3339
3314
  const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3340
3315
  const { type, name } = decl.left;
3341
3316
  const [ local, isGlobal ] = lookupName(scope, name);
@@ -3361,8 +3336,8 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3361
3336
 
3362
3337
  // hack: .length setter
3363
3338
  if (type === 'MemberExpression' && decl.left.property.name === 'length' && !decl._internalAssign) {
3364
- const newValueTmp = localTmp(scope, '__length_setter_tmp');
3365
- const pointerTmp = localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
3339
+ const newValueTmp = !valueUnused && localTmp(scope, '__length_setter_tmp');
3340
+ let pointerTmp = op !== '=' && localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
3366
3341
 
3367
3342
  const out = [
3368
3343
  ...generate(scope, decl.left.object),
@@ -3375,23 +3350,32 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3375
3350
  [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3376
3351
  Opcodes.i32_from_u
3377
3352
  ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right))),
3378
- [ Opcodes.local_tee, newValueTmp ],
3353
+ ...optional([ Opcodes.local_tee, newValueTmp ]),
3379
3354
 
3380
3355
  Opcodes.i32_to_u,
3381
3356
  [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
3382
3357
 
3383
- [ Opcodes.local_get, newValueTmp ]
3358
+ ...optional([ Opcodes.local_get, newValueTmp ])
3384
3359
  ];
3385
3360
 
3386
3361
  const type = getNodeType(scope, decl.left.object);
3387
3362
  const known = knownType(scope, type);
3388
3363
  if (known != null && typeHasFlag(known, TYPE_FLAGS.length)) return [
3389
3364
  ...out,
3390
- ...(!pointerTmp ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
3365
+ ...optional([ Opcodes.local_tee, pointerTmp ]),
3391
3366
 
3392
- ...lengthTypeWasm
3367
+ ...lengthTypeWasm,
3368
+ ...optional(number(UNDEFINED), valueUnused)
3393
3369
  ];
3394
3370
 
3371
+ pointerTmp ||= localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
3372
+
3373
+ const slow = generate(scope, {
3374
+ ...decl,
3375
+ _internalAssign: true
3376
+ });
3377
+ if (valueUnused) disposeLeftover(slow);
3378
+
3395
3379
  return [
3396
3380
  ...out,
3397
3381
  [ Opcodes.local_set, pointerTmp ],
@@ -3399,33 +3383,31 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3399
3383
  ...type,
3400
3384
  ...number(TYPE_FLAGS.length, Valtype.i32),
3401
3385
  [ Opcodes.i32_and ],
3402
- [ Opcodes.if, valtypeBinary ],
3386
+ [ Opcodes.if, valueUnused ? Blocktype.void : valtypeBinary ],
3403
3387
  [ Opcodes.local_get, pointerTmp ],
3404
3388
  ...lengthTypeWasm,
3405
3389
  [ Opcodes.else ],
3406
- ...generate(scope, {
3407
- ...decl,
3408
- _internalAssign: true
3409
- }),
3410
- [ Opcodes.end ]
3390
+ ...slow,
3391
+ [ Opcodes.end ],
3392
+ ...optional(number(UNDEFINED), valueUnused)
3411
3393
  ];
3412
3394
  }
3413
3395
 
3414
3396
  // arr[i]
3415
3397
  if (type === 'MemberExpression') {
3416
- const newValueTmp = localTmp(scope, '#member_setter_val_tmp');
3398
+ const newValueTmp = !valueUnused && localTmp(scope, '#member_setter_val_tmp');
3417
3399
  const pointerTmp = localTmp(scope, '#member_setter_ptr_tmp', Valtype.i32);
3418
3400
 
3419
3401
  const object = decl.left.object;
3420
3402
  const property = getProperty(decl.left);
3421
3403
 
3422
3404
  // todo/perf: use i32 object (and prop?) locals
3423
- const objectWasm = [ [ Opcodes.local_get, localTmp(scope, '#member_obj') ] ];
3405
+ const objectWasm = [ [ Opcodes.local_get, localTmp(scope, '#member_obj_assign') ] ];
3424
3406
  const propertyWasm = [ [ Opcodes.local_get, localTmp(scope, '#member_prop_assign') ] ];
3425
3407
 
3426
3408
  return [
3427
3409
  ...generate(scope, object),
3428
- [ Opcodes.local_set, localTmp(scope, '#member_obj') ],
3410
+ [ Opcodes.local_set, objectWasm[0][1] ],
3429
3411
 
3430
3412
  ...generate(scope, property, false, '#member_prop_assign'),
3431
3413
  [ Opcodes.local_set, localTmp(scope, '#member_prop_assign') ],
@@ -3454,14 +3436,14 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3454
3436
  [ Opcodes.local_get, pointerTmp ],
3455
3437
  [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
3456
3438
  ], getNodeType(scope, decl.right), false, name, true)),
3457
- [ Opcodes.local_tee, newValueTmp ],
3439
+ ...optional([ Opcodes.local_tee, newValueTmp ]),
3458
3440
  [ Opcodes.store, 0, ValtypeSize.i32 ],
3459
3441
 
3460
3442
  [ Opcodes.local_get, pointerTmp ],
3461
3443
  ...getNodeType(scope, decl),
3462
3444
  [ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ],
3463
3445
 
3464
- [ Opcodes.local_get, newValueTmp ]
3446
+ ...optional([ Opcodes.local_get, newValueTmp ])
3465
3447
  ],
3466
3448
 
3467
3449
  ...wrapBC({
@@ -3474,7 +3456,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3474
3456
  [ Opcodes.i32_load8_u, 0, 4 ],
3475
3457
  Opcodes.i32_from_u
3476
3458
  ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
3477
- [ Opcodes.local_tee, newValueTmp ],
3459
+ ...optional([ Opcodes.local_tee, newValueTmp ]),
3478
3460
 
3479
3461
  Opcodes.i32_to_u,
3480
3462
  [ Opcodes.i32_store8, 0, 4 ]
@@ -3488,7 +3470,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3488
3470
  [ Opcodes.i32_load8_u, 0, 4 ],
3489
3471
  Opcodes.i32_from_u
3490
3472
  ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
3491
- [ Opcodes.local_tee, newValueTmp ],
3473
+ ...optional([ Opcodes.local_tee, newValueTmp ]),
3492
3474
 
3493
3475
  ...number(0),
3494
3476
  [ Opcodes.f64_max ],
@@ -3506,7 +3488,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3506
3488
  [ Opcodes.i32_load8_s, 0, 4 ],
3507
3489
  Opcodes.i32_from
3508
3490
  ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
3509
- [ Opcodes.local_tee, newValueTmp ],
3491
+ ...optional([ Opcodes.local_tee, newValueTmp ]),
3510
3492
 
3511
3493
  Opcodes.i32_to,
3512
3494
  [ Opcodes.i32_store8, 0, 4 ]
@@ -3522,7 +3504,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3522
3504
  [ Opcodes.i32_load16_u, 0, 4 ],
3523
3505
  Opcodes.i32_from_u
3524
3506
  ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
3525
- [ Opcodes.local_tee, newValueTmp ],
3507
+ ...optional([ Opcodes.local_tee, newValueTmp ]),
3526
3508
 
3527
3509
  Opcodes.i32_to_u,
3528
3510
  [ Opcodes.i32_store16, 0, 4 ]
@@ -3538,7 +3520,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3538
3520
  [ Opcodes.i32_load16_s, 0, 4 ],
3539
3521
  Opcodes.i32_from
3540
3522
  ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
3541
- [ Opcodes.local_tee, newValueTmp ],
3523
+ ...optional([ Opcodes.local_tee, newValueTmp ]),
3542
3524
 
3543
3525
  Opcodes.i32_to,
3544
3526
  [ Opcodes.i32_store16, 0, 4 ]
@@ -3554,7 +3536,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3554
3536
  [ Opcodes.i32_load, 0, 4 ],
3555
3537
  Opcodes.i32_from_u
3556
3538
  ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
3557
- [ Opcodes.local_tee, newValueTmp ],
3539
+ ...optional([ Opcodes.local_tee, newValueTmp ]),
3558
3540
 
3559
3541
  Opcodes.i32_to_u,
3560
3542
  [ Opcodes.i32_store, 0, 4 ]
@@ -3570,7 +3552,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3570
3552
  [ Opcodes.i32_load, 0, 4 ],
3571
3553
  Opcodes.i32_from
3572
3554
  ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
3573
- [ Opcodes.local_tee, newValueTmp ],
3555
+ ...optional([ Opcodes.local_tee, newValueTmp ]),
3574
3556
 
3575
3557
  Opcodes.i32_to,
3576
3558
  [ Opcodes.i32_store, 0, 4 ]
@@ -3586,7 +3568,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3586
3568
  [ Opcodes.f32_load, 0, 4 ],
3587
3569
  [ Opcodes.f64_promote_f32 ]
3588
3570
  ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
3589
- [ Opcodes.local_tee, newValueTmp ],
3571
+ ...optional([ Opcodes.local_tee, newValueTmp ]),
3590
3572
 
3591
3573
  [ Opcodes.f32_demote_f64 ],
3592
3574
  [ Opcodes.f32_store, 0, 4 ]
@@ -3601,7 +3583,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3601
3583
  [ Opcodes.local_get, pointerTmp ],
3602
3584
  [ Opcodes.f64_load, 0, 4 ]
3603
3585
  ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
3604
- [ Opcodes.local_tee, newValueTmp ],
3586
+ ...optional([ Opcodes.local_tee, newValueTmp ]),
3605
3587
 
3606
3588
  [ Opcodes.f64_store, 0, 4 ]
3607
3589
  ],
@@ -3616,12 +3598,12 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3616
3598
  ],
3617
3599
  postlude: [
3618
3600
  // setLastType(scope, TYPES.number)
3619
- [ Opcodes.local_get, newValueTmp ]
3601
+ ...optional([ Opcodes.local_get, newValueTmp ])
3620
3602
  ]
3621
3603
  }),
3622
3604
  } : {}),
3623
3605
 
3624
- [TYPES.undefined]: internalThrow(scope, 'TypeError', 'Cannot set property of undefined', true),
3606
+ [TYPES.undefined]: internalThrow(scope, 'TypeError', 'Cannot set property of undefined', !valueUnused),
3625
3607
 
3626
3608
  // default: internalThrow(scope, 'TypeError', `Cannot assign member with this type`)
3627
3609
  default: () => [
@@ -3652,9 +3634,11 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3652
3634
 
3653
3635
  [ Opcodes.call, includeBuiltin(scope, scope.strict ? '__Porffor_object_setStrict' : '__Porffor_object_set').index ],
3654
3636
  [ Opcodes.drop ],
3637
+ ...(valueUnused ? [ [ Opcodes.drop ] ] : [])
3655
3638
  // ...setLastType(scope, getNodeType(scope, decl)),
3656
3639
  ]
3657
- }, valtypeBinary)
3640
+ }, valueUnused ? Blocktype.void : valtypeBinary),
3641
+ ...optional(number(UNDEFINED), valueUnused)
3658
3642
  ];
3659
3643
  }
3660
3644
 
@@ -3682,7 +3666,8 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3682
3666
  // set global and return (eg a = 2)
3683
3667
  return [
3684
3668
  ...generateVarDstr(scope, 'var', name, decl.right, undefined, true),
3685
- ...generate(scope, decl.left)
3669
+ ...optional(generate(scope, decl.left), !valueUnused),
3670
+ ...optional(number(UNDEFINED), valueUnused)
3686
3671
  ];
3687
3672
  }
3688
3673
 
@@ -3690,7 +3675,10 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3690
3675
  if (local.metadata?.kind === 'const') return internalThrow(scope, 'TypeError', `Cannot assign to constant variable ${name}`, true);
3691
3676
 
3692
3677
  if (op === '=') {
3693
- return setLocalWithType(scope, name, isGlobal, decl.right, true);
3678
+ const out = setLocalWithType(scope, name, isGlobal, decl.right, !valueUnused);
3679
+
3680
+ if (valueUnused) out.push(...number(UNDEFINED));
3681
+ return out;
3694
3682
  }
3695
3683
 
3696
3684
  if (op === '||' || op === '&&' || op === '??') {
@@ -3713,12 +3701,15 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3713
3701
  ];
3714
3702
  }
3715
3703
 
3716
- return setLocalWithType(
3704
+ const out = setLocalWithType(
3717
3705
  scope, name, isGlobal,
3718
3706
  performOp(scope, op, [ [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ] ], generate(scope, decl.right), getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
3719
- true,
3707
+ !valueUnused,
3720
3708
  getNodeType(scope, decl)
3721
3709
  );
3710
+
3711
+ if (valueUnused) out.push(...number(UNDEFINED));
3712
+ return out;
3722
3713
  };
3723
3714
 
3724
3715
  const ifIdentifierErrors = (scope, decl) => {
@@ -3859,15 +3850,15 @@ const generateUnary = (scope, decl) => {
3859
3850
  disposeLeftover(out);
3860
3851
 
3861
3852
  out.push(...typeSwitch(scope, overrideType ?? getNodeType(scope, decl.argument), [
3862
- [ TYPES.number, () => makeString(scope, 'number', false, '#typeof_result') ],
3863
- [ TYPES.boolean, () => makeString(scope, 'boolean', false, '#typeof_result') ],
3864
- [ [ TYPES.string, TYPES.bytestring ], () => makeString(scope, 'string', false, '#typeof_result') ],
3865
- [ [ TYPES.undefined, TYPES.empty ], () => makeString(scope, 'undefined', false, '#typeof_result') ],
3866
- [ TYPES.function, () => makeString(scope, 'function', false, '#typeof_result') ],
3867
- [ TYPES.symbol, () => makeString(scope, 'symbol', false, '#typeof_result') ],
3853
+ [ TYPES.number, () => makeString(scope, 'number') ],
3854
+ [ TYPES.boolean, () => makeString(scope, 'boolean') ],
3855
+ [ [ TYPES.string, TYPES.bytestring ], () => makeString(scope, 'string') ],
3856
+ [ [ TYPES.undefined, TYPES.empty ], () => makeString(scope, 'undefined') ],
3857
+ [ TYPES.function, () => makeString(scope, 'function') ],
3858
+ [ TYPES.symbol, () => makeString(scope, 'symbol') ],
3868
3859
 
3869
3860
  // object and internal types
3870
- [ 'default', () => makeString(scope, 'object', false, '#typeof_result') ],
3861
+ [ 'default', () => makeString(scope, 'object') ],
3871
3862
  ]));
3872
3863
 
3873
3864
  return out;
@@ -3903,6 +3894,7 @@ const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
3903
3894
  out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, idx ]);
3904
3895
  if (decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
3905
3896
 
3897
+ if (valueUnused) out.push(...number(UNDEFINED));
3906
3898
  return out;
3907
3899
  }
3908
3900
 
@@ -3920,7 +3912,7 @@ const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
3920
3912
  prefix: true,
3921
3913
  argument: decl.argument
3922
3914
  }),
3923
- [ decl.prefix ? Opcodes.local_set : Opcodes.local_tee, tmp ],
3915
+ [ decl.prefix || valueUnused ? Opcodes.local_set : Opcodes.local_tee, tmp ],
3924
3916
 
3925
3917
  // x = tmp + 1
3926
3918
  ...generate(scope, {
@@ -3933,8 +3925,9 @@ const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
3933
3925
  left: { type: 'Identifier', name: '#updatetmp' },
3934
3926
  right: { type: 'Literal', value: 1 }
3935
3927
  }
3936
- }),
3937
- ...(decl.prefix ? [] : [ [ Opcodes.drop ] ])
3928
+ }, _global, _name, valueUnused),
3929
+ ...(decl.prefix || valueUnused ? [] : [ [ Opcodes.drop ] ]),
3930
+ ...optional(number(UNDEFINED), valueUnused)
3938
3931
  ];
3939
3932
  };
3940
3933
 
@@ -4897,18 +4890,7 @@ const generateMeta = (scope, decl) => {
4897
4890
  };
4898
4891
 
4899
4892
  let pages = new Map();
4900
- const allocPage = (scope, reason, type) => {
4901
- const ptr = i => i === 0 ? 16 : (i * pageSize);
4902
- if (pages.has(reason)) return ptr(pages.get(reason).ind);
4903
-
4904
- const ind = pages.size;
4905
- pages.set(reason, { ind, type });
4906
-
4907
- scope.pages ??= new Map();
4908
- scope.pages.set(reason, { ind, type });
4909
-
4910
- return ptr(ind);
4911
- };
4893
+ const allocPage = (scope, name) => _allocPage({ scope, pages }, name);
4912
4894
 
4913
4895
  const itemTypeToValtype = {
4914
4896
  i32: 'i32',
@@ -4942,18 +4924,23 @@ const compileBytes = (val, itemType) => {
4942
4924
  }
4943
4925
  };
4944
4926
 
4945
- const makeData = (scope, elements, page = null, itemType, initEmpty) => {
4927
+ const makeData = (scope, elements, page = null, itemType = 'i8') => {
4928
+ // if data for page already exists, abort
4929
+ if (page && data.find(x => x.page === page)) return;
4930
+
4946
4931
  const length = elements.length;
4947
4932
 
4948
4933
  // if length is 0 memory/data will just be 0000... anyway
4949
4934
  if (length === 0) return false;
4950
4935
 
4951
4936
  let bytes = compileBytes(length, 'i32');
4952
-
4953
- if (!initEmpty) for (let i = 0; i < length; i++) {
4954
- if (elements[i] == null) continue;
4955
-
4956
- bytes.push(...compileBytes(elements[i], itemType));
4937
+ if (itemType === 'i8') {
4938
+ bytes = bytes.concat(elements);
4939
+ } else {
4940
+ for (let i = 0; i < length; i++) {
4941
+ if (elements[i] == null) continue;
4942
+ bytes.push(...compileBytes(elements[i], itemType));
4943
+ }
4957
4944
  }
4958
4945
 
4959
4946
  const obj = { bytes, page };
@@ -4981,169 +4968,6 @@ const printStaticStr = str => {
4981
4968
  return out;
4982
4969
  };
4983
4970
 
4984
- const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, intOut = false, typed = false) => {
4985
- const out = [];
4986
-
4987
- const uniqueName = name === '$undeclared' ? name + uniqId() : name;
4988
-
4989
- const useRawElements = !!decl.rawElements;
4990
- const elements = useRawElements ? decl.rawElements : decl.elements;
4991
-
4992
- const valtype = itemTypeToValtype[itemType];
4993
- const length = elements.length;
4994
-
4995
- const allocated = allocator.alloc({ scope, pages, globals, asmFunc, funcIndex }, uniqueName, { itemType });
4996
-
4997
- let pointer = allocated;
4998
- if (allocator.constructor.name !== 'StaticAllocator') {
4999
- // const tmp = localTmp(scope, '#makearray_pointer' + uniqueName, Valtype.i32);
5000
- const tmp = localTmp(scope, '#makearray_pointer' + name, Valtype.i32);
5001
- out.push(
5002
- ...allocated,
5003
- [ Opcodes.local_set, tmp ]
5004
- );
5005
-
5006
- if (Prefs.runtimeAllocLog) out.push(
5007
- ...printStaticStr(`${name}: `),
5008
-
5009
- [ Opcodes.local_get, tmp ],
5010
- Opcodes.i32_from_u,
5011
- [ Opcodes.call, 0 ],
5012
-
5013
- ...number(10),
5014
- [ Opcodes.call, 1 ]
5015
- );
5016
-
5017
- pointer = [ [ Opcodes.local_get, tmp ] ];
5018
-
5019
- if (Prefs.data && useRawElements) {
5020
- const data = makeData(scope, elements, null, itemType, initEmpty);
5021
- if (data) {
5022
- // init data
5023
- out.push(
5024
- ...pointer,
5025
- ...number(0, Valtype.i32),
5026
- ...number(data.size, Valtype.i32),
5027
- [ ...Opcodes.memory_init, ...unsignedLEB128(data.idx), 0 ]
5028
- );
5029
- }
5030
-
5031
- // return pointer in out
5032
- out.push(
5033
- ...pointer,
5034
- ...(!intOut ? [ Opcodes.i32_from_u ] : [])
5035
- );
5036
-
5037
- return [ out, pointer ];
5038
- }
5039
- } else {
5040
- const rawPtr = allocator.lastPtr;
5041
-
5042
- scope.arrays ??= new Map();
5043
- const firstAssign = !scope.arrays.has(uniqueName);
5044
- if (firstAssign) scope.arrays.set(uniqueName, rawPtr);
5045
-
5046
- const local = global ? globals[name] : scope.locals?.[name];
5047
- if (
5048
- Prefs.data && useRawElements &&
5049
- name !== '#member_prop' && name !== '#member_prop_assign' &&
5050
- (!globalThis.precompile || !global)
5051
- ) {
5052
- if (Prefs.activeData && firstAssign) {
5053
- makeData(scope, elements, allocator.lastName, itemType, initEmpty);
5054
-
5055
- // local value as pointer
5056
- return [ number(rawPtr, intOut ? Valtype.i32 : valtypeBinary), pointer ];
5057
- }
5058
-
5059
- if (Prefs.passiveData) {
5060
- const data = makeData(scope, elements, null, itemType, initEmpty);
5061
- if (data) {
5062
- // init data
5063
- out.push(
5064
- ...pointer,
5065
- ...number(0, Valtype.i32),
5066
- ...number(data.size, Valtype.i32),
5067
- [ ...Opcodes.memory_init, ...unsignedLEB128(data.idx), 0 ]
5068
- );
5069
- }
5070
-
5071
- // return pointer in out
5072
- out.push(...number(rawPtr, intOut ? Valtype.i32 : valtypeBinary));
5073
-
5074
- return [ out, pointer ];
5075
- }
5076
- }
5077
-
5078
- if (local != null) {
5079
- // hack: handle allocation for #member_prop's here instead of in several places /shrug
5080
- let shouldGet = true;
5081
- if (name === '#member_prop') {
5082
- out.push(...number(rawPtr));
5083
- shouldGet = false;
5084
- }
5085
-
5086
- if (name === '#member_prop_assign') {
5087
- out.push(
5088
- [ Opcodes.call, includeBuiltin(scope, '__Porffor_allocate').index ],
5089
- Opcodes.i32_from_u
5090
- );
5091
- shouldGet = false;
5092
- }
5093
-
5094
- const pointerTmp = localTmp(scope, '#makearray_pointer_tmp', Valtype.i32);
5095
- out.push(
5096
- ...(shouldGet ? [
5097
- [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
5098
- Opcodes.i32_to_u
5099
- ] : [
5100
- // hack: make precompile realise we are allocating
5101
- ...(globalThis.precompile ? [
5102
- [ global ? Opcodes.global_set : Opcodes.local_set, local.idx ],
5103
- [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
5104
- ] : []),
5105
- Opcodes.i32_to_u
5106
- ]),
5107
- [ Opcodes.local_set, pointerTmp ]
5108
- );
5109
-
5110
- pointer = [ [ Opcodes.local_get, pointerTmp ] ];
5111
- }
5112
- }
5113
-
5114
-
5115
- // store length
5116
- out.push(
5117
- ...pointer,
5118
- ...number(length, Valtype.i32),
5119
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
5120
- );
5121
-
5122
- const storeOp = StoreOps[itemType];
5123
- const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
5124
- if (!initEmpty) for (let i = 0; i < length; i++) {
5125
- if (elements[i] == null) continue;
5126
-
5127
- const offset = ValtypeSize.i32 + i * sizePerEl;
5128
- out.push(
5129
- ...pointer,
5130
- ...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
5131
- [ storeOp, 0, ...unsignedLEB128(offset) ],
5132
- ...(!typed ? [] : [ // typed presumes !useRawElements
5133
- ...pointer,
5134
- ...getNodeType(scope, elements[i]),
5135
- [ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
5136
- ])
5137
- );
5138
- }
5139
-
5140
- // local value as pointer
5141
- out.push(...pointer);
5142
- if (!intOut) out.push(Opcodes.i32_from_u);
5143
-
5144
- return [ out, pointer ];
5145
- };
5146
-
5147
4971
  const storeArray = (scope, array, index, element) => {
5148
4972
  if (!Array.isArray(element)) element = generate(scope, element);
5149
4973
  if (typeof index === 'number') index = number(index);
@@ -5209,25 +5033,83 @@ const byteStringable = str => {
5209
5033
  return true;
5210
5034
  };
5211
5035
 
5212
- const makeString = (scope, str, global = false, name = '$undeclared', forceBytestring = undefined) => {
5213
- const rawElements = new Array(str.length);
5214
- let byteStringable = true;
5036
+ const makeString = (scope, str, forceBytestring = undefined) => {
5037
+ if (str.length === 0) return number(0);
5038
+
5039
+ const elements = new Array(str.length);
5040
+ let bytestring = forceBytestring !== false;
5215
5041
  for (let i = 0; i < str.length; i++) {
5216
5042
  const c = str.charCodeAt(i);
5217
- rawElements[i] = c;
5043
+ elements[i] = c;
5218
5044
 
5219
- if (byteStringable && c > 0xFF) byteStringable = false;
5045
+ if (bytestring && c > 0xFF) bytestring = false;
5220
5046
  }
5221
5047
 
5222
- if (byteStringable && forceBytestring === false) byteStringable = false;
5048
+ if (globalThis.precompile) return [
5049
+ [ Opcodes.const, 'str', str, forceBytestring ]
5050
+ ];
5051
+
5052
+ const ptr = allocStr({ scope, pages }, str, bytestring);
5053
+ makeData(scope, elements, str, bytestring ? 'i8' : 'i16');
5223
5054
 
5224
- return makeArray(scope, {
5225
- rawElements
5226
- }, global, name, false, byteStringable ? 'i8' : 'i16')[0];
5055
+ return number(ptr);
5227
5056
  };
5228
5057
 
5229
- const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
5230
- return makeArray(scope, decl, global, name, initEmpty, valtype, false, true)[0];
5058
+ const generateArray = (scope, decl, global = false, name = '$undeclared', staticAlloc = false) => {
5059
+ const elements = decl.elements;
5060
+ const length = elements.length;
5061
+
5062
+ const out = [];
5063
+ let pointer;
5064
+
5065
+ if (staticAlloc) {
5066
+ const uniqueName = name === '$undeclared' ? name + uniqId() : name;
5067
+
5068
+ const ptr = allocPage(scope, uniqueName);
5069
+ pointer = number(ptr, Valtype.i32)[0];
5070
+
5071
+ scope.arrays ??= new Map();
5072
+ scope.arrays.set(uniqueName, ptr);
5073
+ } else {
5074
+ const tmp = localTmp(scope, '#create_array' + uniqId(), Valtype.i32);
5075
+ out.push(
5076
+ [ Opcodes.call, includeBuiltin(scope, '__Porffor_allocate').index ],
5077
+ [ Opcodes.local_set, tmp ]
5078
+ );
5079
+
5080
+ pointer = [ Opcodes.local_get, tmp ];
5081
+ }
5082
+
5083
+ // store length
5084
+ if (length !== 0) out.push(
5085
+ pointer,
5086
+ ...number(length, Valtype.i32),
5087
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
5088
+ );
5089
+
5090
+ // todo: rewrite below to support RestElement (holdover from makeArray)
5091
+ for (let i = 0; i < length; i++) {
5092
+ if (elements[i] == null) continue;
5093
+
5094
+ const offset = ValtypeSize.i32 + i * (ValtypeSize[valtype] + 1);
5095
+ out.push(
5096
+ pointer,
5097
+ ...generate(scope, elements[i]),
5098
+ [ Opcodes.store, 0, ...unsignedLEB128(offset) ],
5099
+
5100
+ pointer,
5101
+ ...getNodeType(scope, elements[i]),
5102
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[valtype]) ]
5103
+ );
5104
+ }
5105
+
5106
+ // return array pointer
5107
+ out.push(
5108
+ pointer,
5109
+ Opcodes.i32_from_u
5110
+ );
5111
+
5112
+ return out;
5231
5113
  };
5232
5114
 
5233
5115
  // opt: do not call ToPropertyKey for non-computed properties as unneeded
@@ -5380,7 +5262,7 @@ const generateMember = (scope, decl, _global, _name, _objectWasm = undefined) =>
5380
5262
  if (name.startsWith('__')) name = name.split('_').pop();
5381
5263
  if (name.startsWith('#')) name = '';
5382
5264
 
5383
- return withType(scope, makeString(scope, name, _global, _name, true), TYPES.bytestring);
5265
+ return withType(scope, makeString(scope, name, true), TYPES.bytestring);
5384
5266
  }
5385
5267
 
5386
5268
  const object = decl.object;
@@ -5982,8 +5864,8 @@ const generateTaggedTemplate = (scope, decl, global = false, name = undefined) =
5982
5864
  return out;
5983
5865
  },
5984
5866
 
5985
- __Porffor_bs: str => makeString(scope, str, global, name, true),
5986
- __Porffor_s: str => makeString(scope, str, global, name, false)
5867
+ __Porffor_bs: str => makeString(scope, str, true),
5868
+ __Porffor_s: str => makeString(scope, str, false)
5987
5869
  };
5988
5870
 
5989
5871
  const { quasis, expressions } = decl.quasi;
@@ -6171,7 +6053,7 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
6171
6053
  }
6172
6054
 
6173
6055
  if ([
6174
- TYPES.date, TYPES.number, TYPES.promise, TYPES.symbol,
6056
+ TYPES.date, TYPES.number, TYPES.promise, TYPES.symbol, TYPES.function,
6175
6057
  TYPES.set, TYPES.map,
6176
6058
  TYPES.weakref, TYPES.weakset, TYPES.weakmap,
6177
6059
  TYPES.arraybuffer, TYPES.sharedarraybuffer, TYPES.dataview
@@ -6288,13 +6170,12 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
6288
6170
  if (name === 'main') {
6289
6171
  func.gotLastType = true;
6290
6172
  func.export = true;
6291
- func.returns = [ valtypeBinary, Valtype.i32 ];
6292
6173
 
6293
6174
  let finalStatement = decl.body.body[decl.body.body.length - 1];
6294
6175
  if (finalStatement?.type === 'EmptyStatement') finalStatement = decl.body.body[decl.body.body.length - 2];
6295
6176
 
6296
6177
  const lastInst = wasm[wasm.length - 1] ?? [ Opcodes.end ];
6297
- if (lastInst[0] === Opcodes.drop) {
6178
+ if (lastInst[0] === Opcodes.drop || lastInst[0] === Opcodes.f64_const) {
6298
6179
  if (finalStatement.type.endsWith('Declaration')) {
6299
6180
  // final statement is decl, force undefined
6300
6181
  disposeLeftover(wasm);
@@ -6332,7 +6213,7 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
6332
6213
  }
6333
6214
  } else {
6334
6215
  // add end return if not found
6335
- if (wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
6216
+ if (wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) < func.returns.length) {
6336
6217
  wasm.push(...generateReturn(func, {}));
6337
6218
  }
6338
6219
  }
@@ -6572,7 +6453,6 @@ export default program => {
6572
6453
  builtinFuncs = new BuiltinFuncs();
6573
6454
  builtinVars = new BuiltinVars({ builtinFuncs });
6574
6455
  prototypeFuncs = new PrototypeFuncs();
6575
- allocator = makeAllocator(Prefs.allocator ?? 'static');
6576
6456
 
6577
6457
  const getObjectName = x => x.startsWith('__') && x.slice(2, x.indexOf('_', 2));
6578
6458
  objectHackers = ['assert', 'compareArray', 'Test262Error', ...new Set(Object.keys(builtinFuncs).map(getObjectName).concat(Object.keys(builtinVars).map(getObjectName)).filter(x => x))];