porffor 0.41.5 → 0.41.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.
@@ -49,19 +49,81 @@ const cacheAst = (decl, wasm) => {
49
49
  };
50
50
 
51
51
  const funcRef = func => {
52
- // generate func
53
52
  func.generate?.();
54
53
 
55
54
  if (globalThis.precompile) return [
56
55
  [ Opcodes.const, 'funcref', func.name ]
57
56
  ];
58
57
 
58
+ const wrapperArgc = Prefs.indirectWrapperArgc ?? 6;
59
+ if (func.returns.length === 2 && countParams(func) < wrapperArgc && !func.wrapperFunc) {
60
+ const locals = {}, params = [];
61
+ for (let i = 0; i < wrapperArgc + (func.constr ? 2 : 0); i++) {
62
+ params.push(valtypeBinary, Valtype.i32);
63
+ locals[i * 2] = { idx: i * 2, type: valtypeBinary };
64
+ locals[i * 2 + 1] = { idx: i * 2 + 1, type: Valtype.i32 };
65
+ }
66
+ let localInd = (wrapperArgc + (func.constr ? 2 : 0)) * 2;
67
+
68
+ const wasm = [];
69
+ for (let i = 0; i < func.params.length; i++) {
70
+ wasm.push(
71
+ [ Opcodes.local_get, i ],
72
+ ...(i % 2 === 0 && func.params[i] === Valtype.i32 ? [ Opcodes.i32_to ]: [])
73
+ );
74
+ }
75
+
76
+ wasm.push([ Opcodes.call, func.index ]);
77
+
78
+ if (func.returns[0] === Valtype.i32) {
79
+ const localIdx = localInd++;
80
+ locals[localIdx] = { idx: localIdx, type: Valtype.i32 };
81
+
82
+ wasm.push(
83
+ [ Opcodes.local_set, localIdx ],
84
+ Opcodes.i32_from,
85
+ [ Opcodes.local_get, localIdx ]
86
+ );
87
+ }
88
+
89
+ const name = '#indirect_' + func.name;
90
+ const wrapperFunc = {
91
+ name,
92
+ params,
93
+ locals, localInd,
94
+ returns: [ valtypeBinary, Valtype.i32 ],
95
+ wasm,
96
+ constr: func.constr,
97
+ internal: true,
98
+ index: currentFuncIndex++
99
+ };
100
+
101
+ funcs.push(wrapperFunc);
102
+ funcIndex[name] = wrapperFunc.index;
103
+
104
+ wrapperFunc.jsLength = countLength(func);
105
+ func.wrapperFunc = wrapperFunc;
106
+ }
107
+
59
108
  return [
60
109
  [ Opcodes.const, func.index - importedFuncs.length ]
61
- // [ Opcodes.const, func.index - importedFuncs.length, 'funcref' ]
62
110
  ];
63
111
  };
64
112
 
113
+ const forceDuoValtype = (scope, wasm, forceValtype) => [
114
+ ...wasm,
115
+ ...(valtypeBinary === Valtype.i32 && forceValtype === Valtype.f64 ? [
116
+ [ Opcodes.local_set, localTmp(scope, '#swap', Valtype.i32) ],
117
+ [ Opcodes.f64_convert_i32_s ],
118
+ [ Opcodes.local_get, localTmp(scope, '#swap', Valtype.i32) ]
119
+ ] : []),
120
+ ...(valtypeBinary === Valtype.f64 && forceValtype === Valtype.i32 ? [
121
+ [ Opcodes.local_set, localTmp(scope, '#swap', Valtype.i32) ],
122
+ Opcodes.i32_trunc_sat_f64_s,
123
+ [ Opcodes.local_get, localTmp(scope, '#swap', Valtype.i32) ]
124
+ ] : [])
125
+ ];
126
+
65
127
  const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
66
128
  if (astCache.has(decl)) return astCache.get(decl);
67
129
 
@@ -245,7 +307,7 @@ const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysV
245
307
  ]
246
308
  }
247
309
  }),
248
- ...(expectsValue ? number(UNDEFINED) : [])
310
+ ...(expectsValue ? number(UNDEFINED, typeof expectsValue === 'number' ? expectsValue : valtypeBinary) : [])
249
311
  ];
250
312
 
251
313
  const generateIdent = (scope, decl) => {
@@ -893,7 +955,7 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
893
955
 
894
956
  const asmFuncToAsm = (scope, func) => {
895
957
  return func(scope, {
896
- TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
958
+ Valtype, Opcodes, TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
897
959
  getNodeType, generate, generateIdent,
898
960
  builtin: (n, offset = false) => {
899
961
  let idx = funcIndex[n] ?? importedFuncs[n];
@@ -910,6 +972,7 @@ const asmFuncToAsm = (scope, func) => {
910
972
 
911
973
  return idx;
912
974
  },
975
+ hasFunc: x => funcIndex[x] != null,
913
976
  funcRef: name => {
914
977
  if (funcIndex[name] == null && builtinFuncs[name]) {
915
978
  includeBuiltin(scope, name);
@@ -2166,102 +2229,45 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2166
2229
  } else {
2167
2230
  if (!Prefs.indirectCalls) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
2168
2231
 
2169
- // todo: only works when function uses typedParams and typedReturns
2170
-
2171
- const indirectMode = Prefs.indirectCallMode ?? 'vararg';
2172
- // options: vararg, strict
2173
- // - strict: simpler, smaller size usage, no func lut needed.
2174
- // ONLY works when arg count of call == arg count of function being called
2175
- // - vararg: large size usage, cursed.
2176
- // works when arg count of call != arg count of function being called*
2177
- // * most of the time, some edgecases
2178
-
2179
2232
  funcs.table = true;
2180
2233
  scope.table = true;
2181
2234
 
2182
2235
  let args = decl.arguments;
2183
- let locals = [];
2184
-
2185
- if (indirectMode === 'vararg') {
2186
- const minArgc = Prefs.indirectCallMinArgc ?? 5;
2187
-
2188
- if (args.length < minArgc) {
2189
- args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE()));
2190
- }
2236
+ const wrapperArgc = Prefs.indirectWrapperArgc ?? 6;
2237
+ if (args.length < wrapperArgc) {
2238
+ args = args.concat(new Array(wrapperArgc - args.length).fill(DEFAULT_VALUE()));
2191
2239
  }
2192
2240
 
2193
2241
  for (let i = 0; i < args.length; i++) {
2194
2242
  const arg = args[i];
2195
- out = out.concat(generate(scope, arg));
2196
- out = out.concat(getNodeType(scope, arg));
2197
-
2198
- if (indirectMode === 'vararg') {
2199
- const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
2200
- const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
2201
-
2202
- locals.push([valLocal, typeLocal]);
2203
-
2204
- out.push(
2205
- [ Opcodes.local_set, typeLocal ],
2206
- [ Opcodes.local_set, valLocal ]
2207
- );
2208
- }
2243
+ out = out.concat(generate(scope, arg), valtypeBinary === Valtype.i32 && scope.locals[arg.name]?.type !== Valtype.f64 ? [ [ Opcodes.f64_convert_i32_s ] ] : [], getNodeType(scope, arg));
2209
2244
  }
2210
2245
 
2211
- if (indirectMode === 'strict') {
2212
- return [
2213
- ...generate(scope, decl.callee),
2214
- [ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
2246
+ let knownThis = undefined, getCallee = undefined;
2247
+ const calleeLocal = localTmp(scope, '#indirect_callee');
2215
2248
 
2216
- ...typeSwitch(scope, getNodeType(scope, decl.callee), {
2217
- [TYPES.function]: () => [
2218
- ...out,
2249
+ // hack: this should be more thorough, Function.bind, etc
2250
+ if (decl.callee.type === 'MemberExpression' && !decl._new) {
2251
+ const thisLocal = localTmp(scope, '#indirect_caller');
2252
+ const thisLocalType = localTmp(scope, '#indirect_caller#type', Valtype.i32);
2219
2253
 
2220
- [ Opcodes.local_get, localTmp(scope, '#indirect_callee') ],
2221
- ...generate(scope, decl.callee),
2222
- Opcodes.i32_to_u,
2223
- [ Opcodes.call_indirect, args.length, 0 ],
2224
- ...setLastType(scope)
2225
- ],
2226
- default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
2227
- })
2254
+ knownThis = [
2255
+ [ Opcodes.local_get, thisLocal ],
2256
+ [ Opcodes.local_get, thisLocalType ]
2228
2257
  ];
2229
- }
2230
-
2231
- // hi, I will now explain how vararg mode works:
2232
- // wasm's indirect_call instruction requires you know the func type at compile-time
2233
- // since we have varargs (variable argument count), we do not know it.
2234
- // we could just store args in memory and not use wasm func args,
2235
- // but that is slow (probably) and breaks js exports.
2236
- // instead, we generate every* possibility of argc and use different indirect_call
2237
- // ops for each one, with type depending on argc for that branch.
2238
- // then we load the argc for the wanted function from a memory lut,
2239
- // and call the branch with the matching argc we require.
2240
- // sorry, yes it is very cursed (and size inefficient), but indirect calls
2241
- // are kind of rare anyway (mostly callbacks) so I am not concerned atm.
2242
- // *for argc 0-3, in future (todo:) the max number should be
2243
- // dynamically changed to the max argc of any func in the js file.
2244
-
2245
- const funcLocal = localTmp(scope, '#indirect_func', Valtype.i32);
2246
- const flags = localTmp(scope, '#indirect_flags', Valtype.i32);
2247
-
2248
- let knownThis = undefined;
2249
- let getCalleeObj = undefined;
2250
- let initCalleeObj = undefined;
2251
-
2252
- // hack: this should be more thorough, Function.bind, etc
2253
- if (decl.callee.type == 'MemberExpression' && !decl._new) {
2254
- const callee = localTmp(scope, '#indirect_callee_obj', Valtype.f64);
2255
- initCalleeObj = [
2258
+ getCallee = [
2256
2259
  ...generate(scope, decl.callee.object),
2257
- [ Opcodes.local_set, callee ]
2258
- ];
2259
- getCalleeObj = [
2260
- [ Opcodes.local_get, callee ]
2261
- ];
2262
- knownThis = [
2263
- [ Opcodes.local_get, callee ],
2264
- ...getNodeType(scope, decl.callee.object)
2260
+ [ Opcodes.local_set, thisLocal ],
2261
+ ...getNodeType(scope, decl.callee.object),
2262
+ [ Opcodes.local_set, thisLocalType ],
2263
+
2264
+ ...generate(scope, {
2265
+ type: 'MemberExpression',
2266
+ object: { type: 'Identifier', name: '#indirect_caller' },
2267
+ property: decl.callee.property,
2268
+ computed: decl.callee.computed,
2269
+ optional: decl.callee.optional
2270
+ })
2265
2271
  ];
2266
2272
  }
2267
2273
 
@@ -2277,152 +2283,54 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2277
2283
  }
2278
2284
 
2279
2285
  const newTargetWasm = decl._newTargetWasm ?? createNewTarget(scope, decl, [
2280
- [ Opcodes.local_get, funcLocal ],
2281
- Opcodes.i32_from_u
2286
+ [ Opcodes.local_get, calleeLocal ]
2282
2287
  ], callAsNew);
2283
- const thisWasm = knownThis ?? createThisArg(scope, decl);
2284
-
2285
- const gen = argc => {
2286
- const argsOut = [];
2287
- for (let i = 0; i < argc; i++) {
2288
- argsOut.push(
2289
- [ Opcodes.local_get, locals[i][0] ],
2290
- [ Opcodes.local_get, locals[i][1] ]
2291
- );
2292
- }
2293
-
2294
- const checkFlag = (flag, pass, fail) => [
2295
- [ Opcodes.local_get, flags ],
2296
- ...number(flag, Valtype.i32),
2297
- [ Opcodes.i32_and ],
2298
- [ Opcodes.if, valtypeBinary ],
2299
- ...pass,
2300
- [ Opcodes.else ],
2301
- ...fail,
2302
- [ Opcodes.end ]
2303
- ];
2304
-
2305
- // todo: i'm sure this could be made better somehow, probably only with #96?
2306
- return checkFlag(0b1,
2307
- // no type return
2308
- checkFlag(0b10, [
2309
- // no type return & constr
2310
- ...newTargetWasm,
2311
- ...thisWasm,
2312
- ...argsOut,
2313
- [ Opcodes.local_get, funcLocal ],
2314
- [ Opcodes.call_indirect, argc + 2, 0, 'no_type_return' ],
2315
- ], [
2316
- // no type return & not constr
2317
- ...argsOut,
2318
- [ Opcodes.local_get, funcLocal ],
2319
- [ Opcodes.call_indirect, argc, 0, 'no_type_return' ]
2320
- ]),
2321
-
2322
- // type return
2323
- checkFlag(0b10, [
2324
- // type return & constr
2325
- ...newTargetWasm,
2326
- ...thisWasm,
2327
- ...argsOut,
2328
- [ Opcodes.local_get, funcLocal ],
2329
- [ Opcodes.call_indirect, argc + 2, 0 ],
2330
- ...setLastType(scope),
2331
- ], [
2332
- // type return
2333
- ...argsOut,
2334
- [ Opcodes.local_get, funcLocal ],
2335
- [ Opcodes.call_indirect, argc, 0 ],
2336
- ...setLastType(scope),
2337
- ])
2338
- );
2339
- };
2340
-
2341
- const tableBc = {};
2342
- for (let i = 0; i <= args.length; i++) {
2343
- tableBc[i] = gen(i);
2344
- }
2345
-
2346
- // todo/perf: check if we should use br_table here or just generate our own big if..elses
2288
+ const thisWasm = decl._thisWasm ?? knownThis ?? createThisArg(scope, decl);
2347
2289
 
2348
2290
  return [
2349
- ...(getCalleeObj ? [
2350
- ...initCalleeObj,
2351
- ...generate(scope, callee, false, undefined, getCalleeObj)
2352
- ]: generate(scope, callee)),
2353
- [ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
2291
+ ...(getCallee ? getCallee : generate(scope, callee)),
2292
+ [ Opcodes.local_set, calleeLocal ],
2354
2293
 
2355
2294
  ...typeSwitch(scope, getNodeType(scope, callee), {
2356
2295
  [TYPES.function]: () => [
2357
- ...out,
2358
-
2359
- [ Opcodes.local_get, localTmp(scope, '#indirect_callee') ],
2360
- Opcodes.i32_to_u,
2361
- [ Opcodes.local_set, funcLocal ],
2362
-
2363
2296
  // get if func we are calling is a constructor or not
2364
- [ Opcodes.local_get, funcLocal ],
2365
- ...number(64, Valtype.i32),
2297
+ [ Opcodes.local_get, calleeLocal ],
2298
+ Opcodes.i32_to_u,
2299
+ ...number(48, Valtype.i32),
2366
2300
  [ Opcodes.i32_mul ],
2367
- ...number(4, Valtype.i32),
2301
+ ...number(2, Valtype.i32),
2368
2302
  [ Opcodes.i32_add ],
2369
2303
  [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(allocPage(scope, 'func lut')), 'read func lut' ],
2370
- [ Opcodes.local_set, flags ],
2371
-
2372
- // check if non-constructor was called with new, if so throw
2373
- ...(callAsNew ? [
2374
- [ Opcodes.local_get, flags ],
2375
- ...number(0b10, Valtype.i32),
2376
- [ Opcodes.i32_and ],
2377
- [ Opcodes.i32_eqz ],
2378
- [ Opcodes.if, Blocktype.void ],
2379
- ...internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`),
2380
- [ Opcodes.end ],
2381
- ] : []),
2382
-
2383
- // [ Opcodes.local_get, funcLocal ],
2384
- // Opcodes.i32_from_u,
2385
- // [ Opcodes.call, 0 ],
2386
-
2387
- // ...number(32),
2388
- // [ Opcodes.call, 1 ],
2389
-
2390
- // [ Opcodes.local_get, funcLocal ],
2391
- // ...number(64, Valtype.i32),
2392
- // [ Opcodes.i32_mul ],
2393
- // [ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func lut')), 'read func lut' ],
2394
- // Opcodes.i32_from_u,
2395
- // [ Opcodes.call, 0 ],
2396
-
2397
- // [ Opcodes.local_get, funcLocal ],
2398
- // [ Opcodes.call, includeBuiltin(scope, '__Porffor_funcLut_name').index ],
2399
- // Opcodes.i32_from_u,
2400
- // ...number(TYPES.bytestring, Valtype.i32),
2401
- // [ Opcodes.call, includeBuiltin(scope, '__Porffor_printString').index ],
2402
- // [ Opcodes.drop ],
2403
- // [ Opcodes.drop ],
2404
-
2405
- // ...number(10),
2406
- // [ Opcodes.call, 1 ],
2407
-
2408
- ...brTable([
2409
- // get argc of func we are calling
2410
- [ Opcodes.local_get, funcLocal ],
2411
- ...number(64, Valtype.i32),
2412
- [ Opcodes.i32_mul ],
2413
- [ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func lut')), 'read func lut' ]
2414
- ], tableBc, valtypeBinary)
2415
- ],
2416
2304
 
2417
- ...(decl.optional ? {
2418
- [TYPES.undefined]: [
2419
- ...number(UNDEFINED),
2420
- ...setLastType(scope, TYPES.undefined)
2421
- ]
2422
- } : {}),
2305
+ ...number(0b10, Valtype.i32),
2306
+ [ Opcodes.i32_and ],
2307
+ [ Opcodes.if, Valtype.f64 ],
2308
+ ...forceDuoValtype(scope, newTargetWasm, Valtype.f64),
2309
+ ...forceDuoValtype(scope, thisWasm, Valtype.f64),
2310
+ ...out,
2423
2311
 
2424
- default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
2425
- })
2312
+ [ Opcodes.local_get, calleeLocal ],
2313
+ Opcodes.i32_to_u,
2314
+ [ Opcodes.call_indirect, args.length + 2, 0 ],
2315
+ ...setLastType(scope),
2316
+ [ Opcodes.else ],
2317
+ // throw if non-constructor called with new
2318
+ ...(callAsNew ? [
2319
+ ...internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`, Valtype.f64)
2320
+ ] : [
2321
+ ...out,
2322
+
2323
+ [ Opcodes.local_get, calleeLocal ],
2324
+ Opcodes.i32_to_u,
2325
+ [ Opcodes.call_indirect, args.length, 0 ],
2326
+ ...setLastType(scope)
2327
+ ]),
2328
+ [ Opcodes.end ]
2329
+ ],
2330
+
2331
+ default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, Valtype.f64)
2332
+ }, Valtype.f64),
2333
+ ...(valtypeBinary === Valtype.i32 && scope.returns[0] !== Valtype.f64 ? [ [ Opcodes.f64_convert_i32_s ] ] : [])
2426
2334
  ];
2427
2335
  }
2428
2336
 
@@ -3854,8 +3762,12 @@ const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
3854
3762
  };
3855
3763
 
3856
3764
  const generateIf = (scope, decl) => {
3857
- const out = truthy(scope, generate(scope, decl.test), getNodeType(scope, decl.test), false, true);
3765
+ if (globalThis.precompile && decl.test?.tag?.name === '__Porffor_comptime_flag') {
3766
+ const flag = decl.test.quasi.quasis[0].value.raw;
3767
+ return [ [ null, 'comptime_flag', flag, decl.consequent, '#', Prefs ] ];
3768
+ }
3858
3769
 
3770
+ const out = truthy(scope, generate(scope, decl.test), getNodeType(scope, decl.test), false, true);
3859
3771
 
3860
3772
  out.push([ Opcodes.if, Blocktype.void ]);
3861
3773
  depth.push('if');
@@ -5283,18 +5195,18 @@ const countLength = (func, name = undefined) => {
5283
5195
 
5284
5196
  const generateMember = (scope, decl, _global, _name, _objectWasm = undefined) => {
5285
5197
  let final = [], finalEnd, extraBC = {};
5286
- const name = decl.object.name;
5198
+ let name = decl.object.name;
5287
5199
 
5288
5200
  // todo: handle globalThis.foo
5289
5201
 
5290
5202
  // hack: .name
5291
5203
  if (decl.property.name === 'name' && hasFuncWithName(name) && !scope.noFastFuncMembers) {
5292
- let nameProp = name;
5293
-
5294
5204
  // eg: __String_prototype_toLowerCase -> toLowerCase
5295
- if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
5205
+ if (name.startsWith('__')) name = name.split('_').pop();
5206
+ if (name.startsWith('#indirect_')) name = name.slice(10);
5207
+ if (name.startsWith('#')) name = '';
5296
5208
 
5297
- return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
5209
+ return withType(scope, makeString(scope, name, _global, _name, true), TYPES.bytestring);
5298
5210
  }
5299
5211
 
5300
5212
  const object = decl.object;
@@ -5414,17 +5326,40 @@ const generateMember = (scope, decl, _global, _name, _objectWasm = undefined) =>
5414
5326
 
5415
5327
  const known = knownType(scope, getNodeType(scope, decl.object));
5416
5328
  for (const x of prototypes) {
5417
- const type = TYPES[x.split('_prototype')[0].slice(2).toLowerCase()];
5329
+ let type = TYPES[x.split('_prototype')[0].slice(2).toLowerCase()];
5418
5330
  if (type == null) continue;
5419
5331
 
5420
5332
  // do not __proto__ primitive hack for objects or functions
5421
5333
  if (type === TYPES.object || type === TYPES.function) continue;
5422
5334
 
5335
+ // hack: do not support primitives for Object.prototype.isPrototypeOf
5336
+ if (scope.name === '__Object_prototype_isPrototypeOf') {
5337
+ switch (type) {
5338
+ case TYPES.boolean:
5339
+ type = TYPES.booleanobject;
5340
+ break;
5341
+
5342
+ case TYPES.number:
5343
+ type = TYPES.numberobject;
5344
+ break;
5345
+
5346
+ case TYPES.string:
5347
+ type = TYPES.stringobject;
5348
+ break;
5349
+
5350
+ case TYPES.bytestring:
5351
+ continue;
5352
+ }
5353
+ }
5354
+
5423
5355
  const ident = {
5424
5356
  type: 'Identifier',
5425
5357
  name: x
5426
5358
  };
5427
5359
 
5360
+ // hack: bytestrings should return string prototype
5361
+ if (type === TYPES.bytestring) ident.name = '__String_prototype';
5362
+
5428
5363
  bc[type] = () => [
5429
5364
  ...generate(scope, ident),
5430
5365
  ...setLastType(scope, getNodeType(scope, ident))
@@ -5926,6 +5861,10 @@ let objectHackers = [];
5926
5861
  const objectHack = node => {
5927
5862
  if (!node) return node;
5928
5863
 
5864
+ // delete .end, .loc while here
5865
+ delete node.end;
5866
+ delete node.loc;
5867
+
5929
5868
  if (node.type === 'MemberExpression') {
5930
5869
  const out = (() => {
5931
5870
  const abortOut = { ...node, object: objectHack(node.object) };
@@ -5950,7 +5889,7 @@ const objectHack = node => {
5950
5889
  if (objectName !== 'Object_prototype' && (node.property.name === 'propertyIsEnumerable' || node.property.name === 'hasOwnProperty' || node.property.name === 'isPrototypeOf')) return abortOut;
5951
5890
 
5952
5891
  const name = '__' + objectName + '_' + node.property.name;
5953
- if ((!hasFuncWithName(name) && !Object.hasOwn(builtinVars, name)) && (hasFuncWithName(objectName) || Object.hasOwn(builtinVars, objectName))) return abortOut;
5892
+ if ((!hasFuncWithName(name) && !Object.hasOwn(builtinVars, name) && !isExistingProtoFunc(name) && !hasFuncWithName(name + '$get')) && (hasFuncWithName(objectName) || Object.hasOwn(builtinVars, objectName) || hasFuncWithName('__' + objectName) || Object.hasOwn(builtinVars, '__' + objectName))) return abortOut;
5954
5893
 
5955
5894
  if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
5956
5895
 
@@ -6246,6 +6185,18 @@ const generateFunc = (scope, decl) => {
6246
6185
  func.params = Object.values(func.locals).map(x => x.type);
6247
6186
  func.jsLength = jsLength;
6248
6187
 
6188
+ if (globalThis.valtypeOverrides) {
6189
+ if (globalThis.valtypeOverrides.returns[name]) func.returns = globalThis.valtypeOverrides.returns[name];
6190
+ if (globalThis.valtypeOverrides.params[name]) {
6191
+ func.params = globalThis.valtypeOverrides.params[name];
6192
+
6193
+ const localsVals = Object.values(func.locals);
6194
+ for (let i = 0; i < func.params.length; i++) {
6195
+ localsVals[i].type = func.params[i];
6196
+ }
6197
+ }
6198
+ }
6199
+
6249
6200
  // force generate for main
6250
6201
  if (name === 'main') func.generate();
6251
6202
 
@@ -13,6 +13,26 @@ const __dirname = fileURLToPath(new URL('.', import.meta.url));
13
13
  globalThis.precompileCompilerPath = __dirname;
14
14
  globalThis.precompile = true;
15
15
 
16
+ globalThis.valtypeOverrides = {
17
+ returns: {
18
+ __Porffor_object_get: [ Valtype.f64, Valtype.i32 ],
19
+ __Porffor_object_getExplicit: [ Valtype.f64, Valtype.i32 ],
20
+ __Porffor_object_readValue: [ Valtype.f64, Valtype.i32 ],
21
+ __Porffor_object_set: [ Valtype.f64, Valtype.i32 ],
22
+ __Porffor_object_setStrict: [ Valtype.f64, Valtype.i32 ],
23
+ __Porffor_object_packAccessor: [ Valtype.f64, Valtype.i32 ]
24
+ },
25
+ params: {
26
+ __Porffor_object_set: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
27
+ __Porffor_object_setStrict: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
28
+ __Porffor_object_expr_init: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
29
+ __Porffor_object_expr_initWithFlags: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32, Valtype.i32, Valtype.i32 ],
30
+ __Porffor_object_class_value: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
31
+ __Porffor_object_class_method: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
32
+ __Porffor_object_define: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32, Valtype.i32, Valtype.i32 ],
33
+ }
34
+ };
35
+
16
36
  const argv = process.argv.slice();
17
37
 
18
38
  const timing = {};
@@ -50,23 +70,6 @@ const compile = async (file, _funcs) => {
50
70
  return acc;
51
71
  }, {});
52
72
 
53
- const returnOverrides = {
54
- __Porffor_object_get: [ Valtype.f64, Valtype.i32 ],
55
- __Porffor_object_set: [ Valtype.f64, Valtype.i32 ],
56
- __Porffor_object_setStrict: [ Valtype.f64, Valtype.i32 ],
57
- __Porffor_object_packAccessor: [ Valtype.f64, Valtype.i32 ]
58
- };
59
-
60
- const paramOverrides = {
61
- __Porffor_object_set: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
62
- __Porffor_object_setStrict: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
63
- __Porffor_object_expr_init: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
64
- __Porffor_object_expr_initWithFlags: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32, Valtype.i32, Valtype.i32 ],
65
- __Porffor_object_class_value: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
66
- __Porffor_object_class_method: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32 ],
67
- __Porffor_object_define: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.f64, Valtype.i32, Valtype.i32, Valtype.i32 ],
68
- };
69
-
70
73
  const main = funcs.find(x => x.name === 'main');
71
74
  const exports = funcs.filter(x => x.export && x.name !== 'main');
72
75
  for (const x of exports) {
@@ -82,9 +85,6 @@ const compile = async (file, _funcs) => {
82
85
  }).filter(x => x);
83
86
  }
84
87
 
85
- if (returnOverrides[x.name]) x.returns = returnOverrides[x.name];
86
- if (paramOverrides[x.name]) x.params = paramOverrides[x.name];
87
-
88
88
  const rewriteWasm = (x, wasm, rewriteLocals = false) => {
89
89
  const locals = Object.keys(x.locals).reduce((acc, y) => {
90
90
  acc[x.locals[y].idx] = { ...x.locals[y], name: y };
@@ -201,6 +201,10 @@ const precompile = async () => {
201
201
  const total = performance.now() - t;
202
202
  console.log(`\r${' '.repeat(100)}\r\u001b[90m${`[${total.toFixed(2)}ms]`.padEnd(12, ' ')}\u001b[0m\u001b[92mcompiled ${fileCount} files (${funcs.length} funcs)\u001b[0m \u001b[90m(${['parse', 'codegen', 'opt'].map(x => `${x}: ${((timing[x] / total) * 100).toFixed(0)}%`).join(', ')})\u001b[0m`);
203
203
 
204
+ const comptimeFlagChecks = {
205
+ hasFunc: x => `hasFunc('${x}')`
206
+ };
207
+
204
208
  return `// autogenerated by compiler/precompile.js
205
209
  import { number } from './embedding.js';
206
210
 
@@ -219,9 +223,19 @@ ${funcs.map(x => {
219
223
  .replace(/\["throw","(.*?)","(.*?)"\]/g, (_, constructor, message) => `...internalThrow(_,'${constructor}',\`${message}\`)`)
220
224
  .replace(/\["get object","(.*?)"\]/g, (_, objName) => `...generateIdent(_,{name:'${objName}'})`)
221
225
  .replace(/\[null,"typeswitch case start",\[(.*?)\]\],/g, (_, types) => `...t([${types}],()=>[`)
222
- .replaceAll(',[null,"typeswitch case end"]', '])');
223
-
224
- return `(_,{${str.includes('...t(') ? 't,' : ''}${`${str.includes('allocPage(') ? 'allocPage,' : ''}${str.includes('glbl(') ? 'glbl,' : ''}${str.includes('loc(') ? 'loc,' : ''}${str.includes('builtin(') ? 'builtin,' : ''}${str.includes('funcRef(') ? 'funcRef,' : ''}${str.includes('internalThrow(') ? 'internalThrow,' : ''}${str.includes('generateIdent(') ? 'generateIdent,' : ''}`.slice(0, -1)}})=>`.replace('_,{}', '') + str;
226
+ .replaceAll(',[null,"typeswitch case end"]', '])')
227
+ .replace(/\[null,"comptime_flag","(.*?)",(\{.*?\}),"#",(\{.*?\})\]/g, (_, flag, ast, prefs) => {
228
+ ast = JSON.parse(ast.replaceAll('\n', '\\n'));
229
+ ast = JSON.stringify(ast, (k, v) => {
230
+ if (k === 'loc' || k === 'start' || k === 'end') return undefined;
231
+ return v;
232
+ });
233
+
234
+ const [ id, extra ] = flag.split('.');
235
+ return `[null,()=>{if(${comptimeFlagChecks[id](extra)}){const r=()=>{valtype=Prefs.valtype??'f64';valtypeBinary=Valtype[valtype];const valtypeInd=['i32','i64','f64'].indexOf(valtype);Opcodes.i32_to=[[],[Opcodes.i32_wrap_i64],Opcodes.i32_trunc_sat_f64_s][valtypeInd];Opcodes.i32_to_u=[[],[Opcodes.i32_wrap_i64],Opcodes.i32_trunc_sat_f64_u][valtypeInd];Opcodes.i32_from=[[],[Opcodes.i64_extend_i32_s],[Opcodes.f64_convert_i32_s]][valtypeInd];Opcodes.i32_from_u=[[],[Opcodes.i64_extend_i32_u],[ Opcodes.f64_convert_i32_u]][valtypeInd]};const a=Prefs;Prefs=${prefs};r();const b=generate(_,${ast});Prefs=a;r();return b;}return []}]`;
236
+ });
237
+
238
+ return `(_,{${str.includes('hasFunc(') ? 'hasFunc,' : ''}${str.includes('Valtype[') ? 'Valtype,' : ''}${str.includes('Opcodes.') ? 'Opcodes,' : ''}${str.includes('...t(') ? 't,' : ''}${`${str.includes('allocPage(') ? 'allocPage,' : ''}${str.includes('glbl(') ? 'glbl,' : ''}${str.includes('loc(') ? 'loc,' : ''}${str.includes('builtin(') ? 'builtin,' : ''}${str.includes('funcRef(') ? 'funcRef,' : ''}${str.includes('internalThrow(') ? 'internalThrow,' : ''}${str.includes('generateIdent(') ? 'generateIdent,' : ''}${str.includes('generate(') ? 'generate,' : ''}`.slice(0, -1)}})=>`.replace('_,{}', '') + str;
225
239
  };
226
240
 
227
241
  const locals = Object.entries(x.locals).sort((a,b) => a[1].idx - b[1].idx)
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.41.5+1e2625ec1",
4
+ "version": "0.41.7+89be416c1",
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.41.5+1e2625ec1';
3
+ globalThis.version = '0.41.7+89be416c1';
4
4
 
5
5
  // deno compat
6
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
@@ -167,7 +167,7 @@ try {
167
167
  }
168
168
  } catch (e) {
169
169
  let out = e;
170
- if (!process.argv.includes('-d') && Object.getPrototypeOf(e).message != null) out = `${e.constructor.name}${e.message != null ? `: ${e.message}` : ''}`;
170
+ if (!process.argv.includes('-d') && Object.getPrototypeOf(e).message != null) out = `${e.name}${e.message != null ? `: ${e.message}` : ''}`;
171
171
  console.error(out);
172
172
  }
173
173