porffor 0.24.11 → 0.24.13

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.
@@ -91,6 +91,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
91
91
  case 'NewExpression':
92
92
  return cacheAst(decl, generateNew(scope, decl, global, name));
93
93
 
94
+ case 'ThisExpression':
95
+ return cacheAst(decl, generateThis(scope, decl, global, name));
96
+
94
97
  case 'Literal':
95
98
  return cacheAst(decl, generateLiteral(scope, decl, global, name));
96
99
 
@@ -351,22 +354,34 @@ const generateIdent = (scope, decl) => {
351
354
  };
352
355
 
353
356
  const generateReturn = (scope, decl) => {
354
- if (decl.argument === null) {
355
- // just bare "return"
356
- return [
357
- ...number(UNDEFINED), // "undefined" if func returns
358
- ...(scope.returnType != null ? [] : [
359
- ...number(TYPES.undefined, Valtype.i32) // type undefined
360
- ]),
361
- [ Opcodes.return ]
362
- ];
357
+ const arg = decl.argument ?? DEFAULT_VALUE();
358
+
359
+ const out = [];
360
+ if (
361
+ scope.constr && // only do this in constructors
362
+ scope.newTarget && scope.usesThis && // sanity check
363
+ !globalThis.precompile // skip in precompiled built-ins, we should not require this and handle it ourselves
364
+ ) {
365
+ // ignore return value and return this if being constructed
366
+ out.push(
367
+ // ...truthy(scope, [ [ Opcodes.local_get, '#newtarget' ] ], [ [ Opcodes.local_get, '#newtarget#type' ] ], false, true),
368
+ [ Opcodes.local_get, '#newtarget' ],
369
+ Opcodes.i32_to_u,
370
+ [ Opcodes.if, Blocktype.void ],
371
+ [ Opcodes.local_get, '#this' ],
372
+ ...(scope.returnType != null ? [] : [ [ Opcodes.local_get, '#this#type' ] ]),
373
+ [ Opcodes.return ],
374
+ [ Opcodes.end ]
375
+ );
363
376
  }
364
377
 
365
- return [
366
- ...generate(scope, decl.argument),
367
- ...(scope.returnType != null ? [] : getNodeType(scope, decl.argument)),
378
+ out.push(
379
+ ...generate(scope, arg),
380
+ ...(scope.returnType != null ? [] : getNodeType(scope, arg)),
368
381
  [ Opcodes.return ]
369
- ];
382
+ );
383
+
384
+ return out;
370
385
  };
371
386
 
372
387
  const localTmp = (scope, name, type = valtypeBinary) => {
@@ -1227,7 +1242,7 @@ const asmFuncToAsm = (scope, func) => {
1227
1242
  });
1228
1243
  };
1229
1244
 
1230
- const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTypes = [], globals: globalTypes = [], globalInits = [], returns = [], returnType, localNames = [], globalNames = [], data: _data = [], table = false, constr = false, hasRestArgument = false } = {}) => {
1245
+ const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTypes = [], globals: globalTypes = [], globalInits = [], returns = [], returnType, localNames = [], globalNames = [], data: _data = [], table = false, constr = false, newTarget = false, usesThis = false, hasRestArgument = false } = {}) => {
1231
1246
  if (wasm == null) { // called with no builtin
1232
1247
  log.warning('codegen', `${name} has no built-in!`);
1233
1248
  wasm = [];
@@ -1264,6 +1279,8 @@ const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTy
1264
1279
  index: currentFuncIndex++,
1265
1280
  table,
1266
1281
  constr,
1282
+ newTarget,
1283
+ usesThis,
1267
1284
  globalInits
1268
1285
  };
1269
1286
 
@@ -1303,26 +1320,6 @@ const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTy
1303
1320
  funcs.table = true;
1304
1321
  }
1305
1322
 
1306
- if (constr) {
1307
- func.params = [...func.params];
1308
- func.params.unshift(Valtype.i32);
1309
-
1310
- // move all locals +1 idx (sigh)
1311
- func.localInd++;
1312
- const locals = func.locals;
1313
- for (const x in locals) {
1314
- locals[x].idx++;
1315
- }
1316
-
1317
- locals['#newtarget'] = { idx: 0, type: Valtype.i32 };
1318
-
1319
- for (const inst of wasm) {
1320
- if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) {
1321
- inst[1]++;
1322
- }
1323
- }
1324
- }
1325
-
1326
1323
  if (hasRestArgument) func.hasRestArgument = true;
1327
1324
 
1328
1325
  func.wasm = wasm;
@@ -1605,6 +1602,22 @@ const getNodeType = (scope, node) => {
1605
1602
  }
1606
1603
  }
1607
1604
 
1605
+ if (node.type == 'ThisExpression') {
1606
+ if (!scope.constr) return getType(scope, 'globalThis');
1607
+ return [ [ Opcodes.local_get, '#this#type' ] ];
1608
+ }
1609
+
1610
+ if (node.type == 'MetaProperty') {
1611
+ switch (`${node.meta.name}.${node.property.name}`) {
1612
+ case 'new.target': {
1613
+ return [ [ Opcodes.local_get, '#newtarget#type' ] ];
1614
+ }
1615
+
1616
+ default:
1617
+ return todo(scope, `meta property object ${node.meta.name} is not supported yet`, true);
1618
+ }
1619
+ }
1620
+
1608
1621
  if (scope.locals['#last_type']) return getLastType(scope);
1609
1622
 
1610
1623
  // presume
@@ -1762,6 +1775,42 @@ const RTArrayUtil = {
1762
1775
  ]
1763
1776
  };
1764
1777
 
1778
+ const createNewTarget = (scope, decl, idx) => {
1779
+ if (decl._new) {
1780
+ return [
1781
+ ...number(idx),
1782
+ ...number(TYPES.function, Valtype.i32)
1783
+ ];
1784
+ }
1785
+
1786
+ return [
1787
+ ...number(UNDEFINED),
1788
+ ...number(TYPES.undefined, Valtype.i32)
1789
+ ];
1790
+ }
1791
+
1792
+ const createThisArg = (scope, decl, getFunc, knownThis = undefined) => {
1793
+ if (knownThis) {
1794
+ // todo: check compliance
1795
+ return knownThis;
1796
+ }
1797
+
1798
+ if (decl._new) {
1799
+ // todo: created object should have .prototype = func.prototype
1800
+ // todo: created object should have .constructor = func
1801
+ return [
1802
+ // create an empty object
1803
+ ...generateObject(scope, { properties: [] }),
1804
+ ...number(TYPES.object, Valtype.i32)
1805
+ ];
1806
+ } else {
1807
+ return [
1808
+ ...generate(scope, { type: 'Identifier', name: 'globalThis' }),
1809
+ ...getType(scope, 'globalThis')
1810
+ ];
1811
+ }
1812
+ }
1813
+
1765
1814
  const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1766
1815
  let name = mapName(decl.callee.name);
1767
1816
  if (isFuncType(decl.callee.type)) { // iife
@@ -1978,7 +2027,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1978
2027
  getI32: () => RTArrayUtil.getLengthI32(getPointer),
1979
2028
  set: value => RTArrayUtil.setLength(getPointer, value),
1980
2029
  setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
1981
- }, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
2030
+ }, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE()), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE()), protoLocal, protoLocal2, (length, itemType) => {
1982
2031
  return makeArray(scope, {
1983
2032
  rawElements: new Array(length)
1984
2033
  }, _global, _name, true, itemType, true);
@@ -2115,7 +2164,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2115
2164
  const minArgc = Prefs.indirectCallMinArgc ?? 3;
2116
2165
 
2117
2166
  if (args.length < minArgc) {
2118
- args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
2167
+ args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE()));
2119
2168
  }
2120
2169
  }
2121
2170
 
@@ -2182,6 +2231,26 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2182
2231
  const funcLocal = localTmp(scope, '#indirect_func', Valtype.i32);
2183
2232
  const flags = localTmp(scope, '#indirect_flags', Valtype.i32);
2184
2233
 
2234
+ let knownThis = undefined;
2235
+ let getCalleeObj = undefined;
2236
+ let initCalleeObj = undefined;
2237
+
2238
+ // hack: this should be more thorough, Function.bind, etc.
2239
+ if (decl.callee.type == 'MemberExpression') {
2240
+ const callee = localTmp(scope, '#indirect_callee_obj', Valtype.f64);
2241
+ initCalleeObj = [
2242
+ ...generate(scope, decl.callee.object),
2243
+ [ Opcodes.local_set, callee ]
2244
+ ];
2245
+ getCalleeObj = [
2246
+ [ Opcodes.local_get, callee ]
2247
+ ];
2248
+ knownThis = [
2249
+ [ Opcodes.local_get, callee ],
2250
+ ...getNodeType(scope, decl.callee.object)
2251
+ ];
2252
+ }
2253
+
2185
2254
  const gen = argc => {
2186
2255
  const argsOut = [];
2187
2256
  for (let i = 0; i < argc; i++) {
@@ -2202,43 +2271,74 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2202
2271
  [ Opcodes.end ]
2203
2272
  ];
2204
2273
 
2205
- // pain.
2206
- // return checkFlag(0b10, [ // constr
2207
- // [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2208
- // ...argsOut,
2209
- // [ Opcodes.local_get, funcLocal ],
2210
- // [ Opcodes.call_indirect, argc, 0, 'constr' ],
2211
- // ...setLastType(scope),
2212
- // ], [
2213
- // ...argsOut,
2214
- // [ Opcodes.local_get, funcLocal ],
2215
- // [ Opcodes.call_indirect, argc, 0 ],
2216
- // ...setLastType(scope),
2217
- // ]);
2218
-
2219
- return checkFlag(0b1, // no type return
2220
- checkFlag(0b10, [ // no type return & constr
2221
- [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2222
- ...argsOut,
2223
- [ Opcodes.local_get, funcLocal ],
2224
- [ Opcodes.call_indirect, argc, 0, 'no_type_return', 'constr' ],
2225
- ], [
2226
- ...argsOut,
2227
- [ Opcodes.local_get, funcLocal ],
2228
- [ Opcodes.call_indirect, argc, 0, 'no_type_return' ]
2229
- ]),
2230
- checkFlag(0b10, [ // type return & constr
2231
- [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2232
- ...argsOut,
2233
- [ Opcodes.local_get, funcLocal ],
2234
- [ Opcodes.call_indirect, argc, 0, 'constr' ],
2235
- ...setLastType(scope),
2236
- ], [
2237
- ...argsOut,
2238
- [ Opcodes.local_get, funcLocal ],
2239
- [ Opcodes.call_indirect, argc, 0 ],
2240
- ...setLastType(scope),
2241
- ]),
2274
+ // todo: i'm sure this could be made better somehow, probably only with #96?
2275
+ return checkFlag(0b1,
2276
+ // no type return
2277
+ checkFlag(0b1000,
2278
+ // no type return & this
2279
+ checkFlag(0b100, [
2280
+ // no type return & this & new.target
2281
+ ...createNewTarget(scope, decl),
2282
+ ...createThisArg(scope, decl, [], knownThis),
2283
+ ...argsOut,
2284
+ [ Opcodes.local_get, funcLocal ],
2285
+ [ Opcodes.call_indirect, argc + 2, 0, 'no_type_return' ],
2286
+ ], [
2287
+ // no type return & this
2288
+ ...createThisArg(scope, decl),
2289
+ ...argsOut,
2290
+ [ Opcodes.local_get, funcLocal ],
2291
+ [ Opcodes.call_indirect, argc + 1, 0, 'no_type_return' ]
2292
+ ]),
2293
+ // no type return
2294
+ checkFlag(0b100, [
2295
+ // no type return & new.target
2296
+ ...createNewTarget(scope, decl),
2297
+ ...argsOut,
2298
+ [ Opcodes.local_get, funcLocal ],
2299
+ [ Opcodes.call_indirect, argc + 1, 0, 'no_type_return' ],
2300
+ ], [
2301
+ // no type return
2302
+ ...argsOut,
2303
+ [ Opcodes.local_get, funcLocal ],
2304
+ [ Opcodes.call_indirect, argc, 0, 'no_type_return' ]
2305
+ ]),
2306
+ ),
2307
+ // type return
2308
+ checkFlag(0b1000,
2309
+ // type return & this
2310
+ checkFlag(0b100, [
2311
+ // type return & this & new.target
2312
+ ...createNewTarget(scope, decl),
2313
+ ...createThisArg(scope, decl, [], knownThis),
2314
+ ...argsOut,
2315
+ [ Opcodes.local_get, funcLocal ],
2316
+ [ Opcodes.call_indirect, argc + 2, 0 ],
2317
+ ...setLastType(scope),
2318
+ ], [
2319
+ // type return & this
2320
+ ...createThisArg(scope, decl),
2321
+ ...argsOut,
2322
+ [ Opcodes.local_get, funcLocal ],
2323
+ [ Opcodes.call_indirect, argc + 1, 0 ],
2324
+ ...setLastType(scope),
2325
+ ]),
2326
+ // type return
2327
+ checkFlag(0b100, [
2328
+ // type return & new.target
2329
+ ...createNewTarget(scope, decl),
2330
+ ...argsOut,
2331
+ [ Opcodes.local_get, funcLocal ],
2332
+ [ Opcodes.call_indirect, argc + 1, 0 ],
2333
+ ...setLastType(scope),
2334
+ ], [
2335
+ // type return
2336
+ ...argsOut,
2337
+ [ Opcodes.local_get, funcLocal ],
2338
+ [ Opcodes.call_indirect, argc, 0 ],
2339
+ ...setLastType(scope),
2340
+ ]),
2341
+ )
2242
2342
  );
2243
2343
  };
2244
2344
 
@@ -2250,7 +2350,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2250
2350
  // todo/perf: check if we should use br_table here or just generate our own big if..elses
2251
2351
 
2252
2352
  return [
2253
- ...generate(scope, decl.callee),
2353
+ ...(getCalleeObj ? [
2354
+ ...initCalleeObj,
2355
+ ...generateMember(scope, decl.callee, false, undefined, getCalleeObj)
2356
+ ]: generate(scope, decl.callee)),
2254
2357
  [ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
2255
2358
 
2256
2359
  ...typeSwitch(scope, getNodeType(scope, decl.callee), {
@@ -2298,26 +2401,32 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2298
2401
  const userFunc = func && !func.internal;
2299
2402
  const typedParams = userFunc || func?.typedParams;
2300
2403
  const typedReturns = (userFunc && func.returnType == null) || builtinFuncs[name]?.typedReturns;
2301
- let paramCount = func && (typedParams ? Math.floor(func.params.length / 2) : func.params.length);
2404
+ let paramCount = countParams(func, name);
2302
2405
 
2303
2406
  let paramOffset = 0;
2304
- if (func && func.constr) {
2305
- // new.target arg
2306
- if (func.internal) paramOffset = 1;
2307
- if (!typedParams) paramCount--;
2308
- out.push([ Opcodes.i32_const, decl._new ? 1 : 0 ]);
2309
- } else if (decl._new)
2407
+ if (decl._new && func && !func.constr) {
2310
2408
  return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`, true);
2409
+ }
2410
+
2411
+ if (func && func.newTarget) {
2412
+ out.push(...createNewTarget(scope, decl, idx - importedFuncs.length));
2413
+ paramOffset += 2;
2414
+ }
2415
+
2416
+ if (func && func.usesThis) {
2417
+ out.push(...createThisArg(scope, decl, func));
2418
+ paramOffset += 2;
2419
+ }
2311
2420
 
2312
2421
  let args = [...decl.arguments];
2313
2422
  if (func && !func.hasRestArgument && args.length < paramCount) {
2314
2423
  // too little args, push undefineds
2315
- args = args.concat(new Array(paramCount - args.length).fill(DEFAULT_VALUE));
2424
+ args = args.concat(new Array(paramCount - args.length).fill(DEFAULT_VALUE()));
2316
2425
  }
2317
2426
 
2318
2427
  if (func && func.hasRestArgument) {
2319
2428
  if (args.length < paramCount) {
2320
- args = args.concat(new Array(paramCount - 1 - args.length).fill(DEFAULT_VALUE));
2429
+ args = args.concat(new Array(paramCount - 1 - args.length).fill(DEFAULT_VALUE()));
2321
2430
  }
2322
2431
 
2323
2432
  const restArgs = args.slice(paramCount - 1);
@@ -2391,11 +2500,28 @@ const generateNew = (scope, decl, _global, _name) => generateCall(scope, {
2391
2500
  _new: true
2392
2501
  }, _global, _name);
2393
2502
 
2503
+ const generateThis = (scope, decl, _global, _name) => {
2504
+ if (!scope.constr) {
2505
+ // this in a non-constructor context is a reference to globalThis
2506
+ return [
2507
+ ...generate(scope, { type: 'Identifier', name: 'globalThis' }),
2508
+ ...setLastType(scope, getType(scope, 'globalThis'))
2509
+ ];
2510
+ }
2511
+
2512
+ scope.usesThis = true;
2513
+
2514
+ return [
2515
+ [ Opcodes.local_get, '#this' ],
2516
+ ...setLastType(scope, [ [ Opcodes.local_get, '#this#type' ] ])
2517
+ ];
2518
+ }
2519
+
2394
2520
  // bad hack for undefined and null working without additional logic
2395
- const DEFAULT_VALUE = {
2521
+ const DEFAULT_VALUE = () => ({
2396
2522
  type: 'Identifier',
2397
2523
  name: 'undefined'
2398
- };
2524
+ });
2399
2525
 
2400
2526
  const codeToSanitizedStr = code => {
2401
2527
  let out = '';
@@ -4128,7 +4254,7 @@ const generateThrow = (scope, decl) => {
4128
4254
  message = decl.argument.arguments[0];
4129
4255
  }
4130
4256
 
4131
- message ??= DEFAULT_VALUE;
4257
+ message ??= DEFAULT_VALUE();
4132
4258
 
4133
4259
  if (tags.length === 0) tags.push({
4134
4260
  params: [ valtypeBinary, valtypeBinary, Valtype.i32 ],
@@ -4153,7 +4279,7 @@ const generateThrow = (scope, decl) => {
4153
4279
  message = decl.argument.arguments[0];
4154
4280
  }
4155
4281
 
4156
- message ??= DEFAULT_VALUE;
4282
+ message ??= DEFAULT_VALUE();
4157
4283
 
4158
4284
  if (tags.length === 0) tags.push({
4159
4285
  params: [ Valtype.i32, valtypeBinary, Valtype.i32 ],
@@ -4209,18 +4335,17 @@ const generateEmpty = (scope, decl) => {
4209
4335
  };
4210
4336
 
4211
4337
  const generateMeta = (scope, decl) => {
4212
- if (decl.meta.name !== 'new') return todo(scope, `meta property object ${decl.meta.name} is not supported yet`, true);
4213
-
4214
4338
  switch (`${decl.meta.name}.${decl.property.name}`) {
4215
4339
  case 'new.target': {
4216
- scope.constr = true;
4340
+ scope.newTarget = true;
4217
4341
 
4218
4342
  return [
4219
- [ Opcodes.local_get, -1 ],
4220
- Opcodes.i32_from_u,
4221
- ...setLastType(scope, TYPES.boolean)
4343
+ [ Opcodes.local_get, '#newtarget' ],
4222
4344
  ];
4223
4345
  }
4346
+
4347
+ default:
4348
+ return todo(scope, `meta property object ${decl.meta.name} is not supported yet`, true);
4224
4349
  }
4225
4350
  };
4226
4351
 
@@ -4641,7 +4766,26 @@ const wrapBC = (bc, { prelude = [], postlude = [] } = {}) => {
4641
4766
  return out;
4642
4767
  };
4643
4768
 
4644
- const generateMember = (scope, decl, _global, _name) => {
4769
+ const countParams = (func, name = undefined) => {
4770
+ if (!func) {
4771
+ if (Object.hasOwn(importedFuncs, name)) {
4772
+ // reverse lookup then normal lookup
4773
+ func = importedFuncs[importedFuncs[name]];
4774
+ if (func) return func.params?.length ?? func.params;
4775
+ }
4776
+ return;
4777
+ }
4778
+ if (func.argc) return func.argc;
4779
+
4780
+ let params = func.params.length;
4781
+ if (func.newTarget) params -= 2;
4782
+ if (func.usesThis) params -= 2;
4783
+ if (!func.internal || builtinFuncs[func.name]?.typedParams) params = Math.floor(params / 2);
4784
+
4785
+ return params;
4786
+ }
4787
+
4788
+ const generateMember = (scope, decl, _global, _name, _objectWasm = undefined) => {
4645
4789
  const name = decl.object.name;
4646
4790
 
4647
4791
  // hack: .name
@@ -4659,12 +4803,9 @@ const generateMember = (scope, decl, _global, _name) => {
4659
4803
  // todo: support optional
4660
4804
 
4661
4805
  const func = funcs.find(x => x.name === name);
4662
- if (func) {
4663
- const typedParams = !func.internal || func.typedParams;
4664
- return withType(scope, number(typedParams ? Math.floor(func.params.length / 2) : (func.constr ? (func.params.length - 1) : func.params.length)), TYPES.number);
4665
- }
4806
+ if (func) return withType(scope, number(countParams(func)), TYPES.number);
4666
4807
 
4667
- if (Object.hasOwn(builtinFuncs, name)) return withType(scope, number(builtinFuncs[name].typedParams ? Math.floor(builtinFuncs[name].params.length / 2) : (builtinFuncs[name].constr ? (builtinFuncs[name].params.length - 1) : builtinFuncs[name].params.length)), TYPES.number);
4808
+ if (Object.hasOwn(builtinFuncs, name)) return withType(scope, number(countParams(builtinFuncs[name])), TYPES.number);
4668
4809
  if (Object.hasOwn(importedFuncs, name)) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
4669
4810
  if (Object.hasOwn(internalConstrs, name)) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
4670
4811
 
@@ -4954,7 +5095,7 @@ const generateMember = (scope, decl, _global, _name) => {
4954
5095
  if (decl.optional) {
4955
5096
  out.unshift(
4956
5097
  [ Opcodes.block, valtypeBinary ],
4957
- ...generate(scope, object),
5098
+ ...(_objectWasm ? _objectWasm : generate(scope, object)),
4958
5099
  [ Opcodes.local_tee, localTmp(scope, '#member_obj') ],
4959
5100
 
4960
5101
  ...nullish(scope, [], getNodeType(scope, object), false, true),
@@ -4973,7 +5114,7 @@ const generateMember = (scope, decl, _global, _name) => {
4973
5114
  );
4974
5115
  } else {
4975
5116
  out.unshift(
4976
- ...generate(scope, object),
5117
+ ...(_objectWasm ? _objectWasm : generate(scope, object)),
4977
5118
  [ Opcodes.local_set, localTmp(scope, '#member_obj') ],
4978
5119
 
4979
5120
  ...generate(scope, property, false, '#member_prop'),
@@ -4995,7 +5136,7 @@ const objectHack = node => {
4995
5136
  if (node.computed || node.optional) return;
4996
5137
 
4997
5138
  // hack: block these properties as they can be accessed on functions
4998
- if (node.property.name == "length" || node.property.name == "name") return;
5139
+ if (node.property.name == 'length' || node.property.name == 'name') return;
4999
5140
 
5000
5141
  let objectName = node.object.name;
5001
5142
 
@@ -5045,7 +5186,8 @@ const generateFunc = (scope, decl) => {
5045
5186
  returns: [ valtypeBinary, Valtype.i32 ],
5046
5187
  throws: false,
5047
5188
  name,
5048
- index: currentFuncIndex++
5189
+ index: currentFuncIndex++,
5190
+ constr: decl.type && decl.type !== 'ArrowFunctionExpression' && decl.type !== 'Program'
5049
5191
  };
5050
5192
 
5051
5193
  funcIndex[name] = func.index;
@@ -5093,6 +5235,13 @@ const generateFunc = (scope, decl) => {
5093
5235
 
5094
5236
  func.params = Object.values(func.locals).map(x => x.type);
5095
5237
 
5238
+ func.argc = countParams(func, name);
5239
+
5240
+ if (func.constr) {
5241
+ func.newTarget = true;
5242
+ func.usesThis = true;
5243
+ }
5244
+
5096
5245
  let body = objectHack(decl.body);
5097
5246
  if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
5098
5247
  // hack: () => 0 -> () => return 0
@@ -5123,11 +5272,50 @@ const generateFunc = (scope, decl) => {
5123
5272
 
5124
5273
  // add end return if not found
5125
5274
  if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
5126
- wasm.push(
5127
- ...number(0),
5128
- ...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
5129
- [ Opcodes.return ]
5130
- );
5275
+ wasm.push(...generateReturn(func, {}));
5276
+ }
5277
+
5278
+ if (func.newTarget || func.usesThis) {
5279
+ const locals = func.locals;
5280
+ let idxOffset = 0;
5281
+ func.params = [...func.params];
5282
+ if (func.usesThis) {
5283
+ func.params.unshift(valtypeBinary, Valtype.i32);
5284
+ idxOffset += 2;
5285
+ }
5286
+ if (func.newTarget) {
5287
+ func.params.unshift(valtypeBinary, Valtype.i32);
5288
+ idxOffset += 2;
5289
+ }
5290
+
5291
+ // move all local indexes by idxOffset
5292
+ func.localInd += idxOffset;
5293
+ for (const x in locals) {
5294
+ locals[x].idx += idxOffset;
5295
+ }
5296
+
5297
+ let indexes = idxOffset;
5298
+ // note: make sure to add these in reverse order of location
5299
+ if (func.usesThis) {
5300
+ locals['#this#type'] = { idx: --indexes, type: Valtype.i32 };
5301
+ locals['#this'] = { idx: --indexes, type: valtypeBinary };
5302
+ }
5303
+ if (func.newTarget) {
5304
+ locals['#newtarget#type'] = { idx: --indexes, type: Valtype.i32 };
5305
+ locals['#newtarget'] = { idx: --indexes, type: valtypeBinary };
5306
+ }
5307
+
5308
+ for (let i = 0; i < wasm.length; i++) {
5309
+ // note: these needs to be copied, even though they realistically shouldn't
5310
+ const inst = wasm[i];
5311
+ if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) {
5312
+ if (typeof inst[1] == 'string') {
5313
+ wasm[i] = [ inst[0], locals[inst[1]].idx ];
5314
+ } else {
5315
+ wasm[i] = [ inst[0], inst[1] + idxOffset ];
5316
+ }
5317
+ }
5318
+ }
5131
5319
  }
5132
5320
 
5133
5321
  return func;
@@ -5156,7 +5344,7 @@ const internalConstrs = {
5156
5344
  rawElements: new Array(0)
5157
5345
  }, global, name, true, undefined, true, true);
5158
5346
 
5159
- const arg = decl.arguments[0] ?? DEFAULT_VALUE;
5347
+ const arg = decl.arguments[0] ?? DEFAULT_VALUE();
5160
5348
 
5161
5349
  // todo: check in wasm instead of here
5162
5350
  const literalValue = arg.value ?? 0;
@@ -5232,7 +5420,7 @@ const internalConstrs = {
5232
5420
  Boolean: {
5233
5421
  generate: (scope, decl) => {
5234
5422
  // todo: boolean object when used as constructor
5235
- const arg = decl.arguments[0] ?? DEFAULT_VALUE;
5423
+ const arg = decl.arguments[0] ?? DEFAULT_VALUE();
5236
5424
  return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
5237
5425
  },
5238
5426
  type: TYPES.boolean,
@@ -209,13 +209,15 @@ ${funcs.map(x => {
209
209
  return `(scope, {${`${str.includes('allocPage(') ? 'allocPage,' : ''}${str.includes('glbl(') ? 'glbl,' : ''}${str.includes('loc(') ? 'loc,' : ''}${str.includes('builtin(') ? 'builtin,' : ''}${str.includes('internalThrow(') ? 'internalThrow,' : ''}`.slice(0, -1)}}) => ` + str;
210
210
  };
211
211
 
212
+ const locals = Object.entries(x.locals).sort((a,b) => a[1].idx - b[1].idx)
213
+
212
214
  return ` this.${x.name} = {
213
215
  wasm: ${rewriteWasm(x.wasm)},
214
216
  params: ${JSON.stringify(x.params)}, typedParams: 1,
215
217
  returns: ${JSON.stringify(x.returns)}, ${x.returnType != null ? `returnType: ${JSON.stringify(x.returnType)}` : 'typedReturns: 1'},
216
- locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length).map(x => x.type))}, localNames: ${JSON.stringify(Object.keys(x.locals))},
218
+ locals: ${JSON.stringify(locals.slice(x.params.length).map(x => x[1].type))}, localNames: ${JSON.stringify(locals.map(x => x[0]))},
217
219
  ${x.globalInits ? ` globalInits: {${Object.keys(x.globalInits).map(y => `${y}: ${rewriteWasm(x.globalInits[y])}`).join(',')}},\n` : ''}${x.data && x.data.length > 0 ? ` data: [${x.data.map(x => `[${x.offset ?? 'null'},[${x.bytes.join(',')}]]`).join(',')}],` : ''}
218
- ${x.table ? ` table: 1,` : ''}${x.constr ? ` constr: 1,` : ''}${x.hasRestArgument ? ` hasRestArgument: 1,` : ''}
220
+ ${x.table ? ` table: 1,` : ''}${x.constr ? ` constr: 1,` : ''}${x.usesThis ? ` usesThis: 1,` : ''}${x.newTarget ? ` newTarget: 1,` : ''}${x.argc !== undefined ? ` argc: ${x.argc},` : ''}${x.hasRestArgument ? ` hasRestArgument: 1,` : ''}
219
221
  };`.replaceAll('\n\n', '\n').replaceAll('\n\n', '\n').replaceAll('\n\n', '\n');
220
222
  }).join('\n')}
221
223
  };`;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "porffor",
3
3
  "description": "a basic experimental wip aot optimizing js -> wasm engine/compiler/runtime in js",
4
- "version": "0.24.11+a277981f6",
4
+ "version": "0.24.13+4aea75f9a",
5
5
  "author": "CanadaHonk",
6
6
  "license": "MIT",
7
7
  "scripts": {},
package/runner/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from 'node:fs';
3
- globalThis.version = '0.24.11+a277981f6';
3
+ globalThis.version = '0.24.13+4aea75f9a';
4
4
 
5
5
  // deno compat
6
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {