porffor 0.58.4 → 0.58.6

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.
@@ -45,17 +45,23 @@ const funcRef = func => {
45
45
 
46
46
  const wrapperArgc = Prefs.indirectWrapperArgc ?? 10;
47
47
  if (!func.wrapperFunc) {
48
- const locals = {}, params = [];
48
+ const locals = {
49
+ ['#length']: { idx: 0, type: Valtype.i32 }
50
+ }, params = [
51
+ Valtype.i32
52
+ ];
53
+
49
54
  for (let i = 0; i < wrapperArgc + 2; i++) {
50
55
  params.push(valtypeBinary, Valtype.i32);
51
- locals[i * 2] = { idx: i * 2, type: valtypeBinary };
52
- locals[i * 2 + 1] = { idx: i * 2 + 1, type: Valtype.i32 };
56
+ locals[`#${i}`] = { idx: 1 + i * 2, type: valtypeBinary };
57
+ locals[`#${i}#type`] = { idx: 2 + i * 2, type: Valtype.i32 };
53
58
  }
54
- let localInd = (wrapperArgc + 2) * 2;
59
+ let localInd = 1 + (wrapperArgc + 2) * 2;
55
60
 
56
61
  if (indirectFuncs.length === 0) {
57
62
  // add empty indirect func
58
63
  const emptyFunc = {
64
+ constr: true, internal: true, indirect: true,
59
65
  name: '#indirect#empty',
60
66
  params,
61
67
  locals: { ...locals }, localInd,
@@ -64,9 +70,6 @@ const funcRef = func => {
64
70
  number(0),
65
71
  number(0, Valtype.i32)
66
72
  ],
67
- constr: true,
68
- internal: true,
69
- indirect: true,
70
73
  wrapperOf: {
71
74
  name: '',
72
75
  jsLength: 0
@@ -76,7 +79,7 @@ const funcRef = func => {
76
79
 
77
80
  // check not being constructed
78
81
  emptyFunc.wasm.unshift(
79
- [ Opcodes.local_get, 0 ], // new.target value
82
+ [ Opcodes.local_get, 1 ], // new.target value
80
83
  Opcodes.i32_to_u,
81
84
  [ Opcodes.if, Blocktype.void ], // if value is non-zero
82
85
  ...internalThrow(emptyFunc, 'TypeError', `Function is not a constructor`), // throw type error
@@ -88,27 +91,94 @@ const funcRef = func => {
88
91
  }
89
92
 
90
93
  const wasm = [];
91
- const offset = func.constr ? 0 : (func.method ? 2 : 4);
92
- for (let i = 0; i < func.params.length; i++) {
93
- if (func.internal && func.name.includes('_prototype_') && i < 2) {
94
- // special case: use real this for prototype internals
95
- wasm.push(
96
- [ Opcodes.local_get, 2 + i ],
97
- ...(i % 2 === 0 && func.params[i] === Valtype.i32 ? [ Opcodes.i32_to ]: [])
98
- );
99
- } else {
94
+ const name = '#indirect_' + func.name;
95
+ const wrapperFunc = {
96
+ constr: true, internal: true, indirect: true,
97
+ name, params, locals, localInd,
98
+ returns: [ valtypeBinary, Valtype.i32 ],
99
+ wasm,
100
+ wrapperOf: func,
101
+ indirectIndex: indirectFuncs.length
102
+ };
103
+
104
+ indirectFuncs.push(wrapperFunc);
105
+
106
+ wrapperFunc.jsLength = countLength(func);
107
+ func.wrapperFunc = wrapperFunc;
108
+
109
+ const paramCount = countParams(func, name);
110
+ const args = [];
111
+ for (let i = 0; i < paramCount - (func.hasRestArgument ? 1 : 0); i++) {
112
+ args.push({
113
+ type: 'Identifier',
114
+ name: `#${i + 2}`
115
+ });
116
+ }
117
+
118
+ if (func.hasRestArgument) {
119
+ const array = (wrapperFunc.localInd += 2) - 2;
120
+ locals['#array#i32'] = { idx: array, type: Valtype.i32 };
121
+ locals['#array'] = { idx: array + 1, type: valtypeBinary };
122
+
123
+ wasm.push(
124
+ [ Opcodes.call, includeBuiltin(wrapperFunc, '__Porffor_allocate').index ],
125
+ [ Opcodes.local_tee, array ],
126
+ Opcodes.i32_from_u,
127
+ [ Opcodes.local_set, array + 1 ],
128
+
129
+ [ Opcodes.local_get, array ],
130
+ [ Opcodes.local_get, 0 ],
131
+ number(paramCount - 1, Valtype.i32),
132
+ [ Opcodes.i32_sub ],
133
+ [ Opcodes.i32_store, 0, 0 ]
134
+ );
135
+
136
+ let offset = 4;
137
+ for (let i = paramCount - 1; i < wrapperArgc; i++) {
100
138
  wasm.push(
101
- [ Opcodes.local_get, offset + (!func.internal || func.typedParams ? i : i * 2) ],
102
- ...(i % 2 === 0 && func.params[i] === Valtype.i32 ? [ Opcodes.i32_to ]: [])
139
+ [ Opcodes.local_get, array ],
140
+ [ Opcodes.local_get, 5 + i * 2 ],
141
+ [ Opcodes.f64_store, 0, offset ],
142
+
143
+ [ Opcodes.local_get, array ],
144
+ [ Opcodes.local_get, 6 + i * 2 ],
145
+ [ Opcodes.i32_store8, 0, offset + 8 ],
103
146
  );
147
+ offset += 9;
104
148
  }
149
+
150
+ args.push({
151
+ type: 'SpreadElement',
152
+ argument: {
153
+ type: 'Identifier',
154
+ name: '#array',
155
+ _type: TYPES.array
156
+ }
157
+ });
105
158
  }
106
159
 
107
- wasm.push([ Opcodes.call, func.index ]);
160
+ wasm.push(...generate(wrapperFunc, {
161
+ type: 'CallExpression',
162
+ callee: {
163
+ type: 'Identifier',
164
+ name: func.name
165
+ },
166
+ _funcIdx: func.index,
167
+ arguments: args,
168
+ _insideIndirect: true,
169
+ _newTargetWasm: [
170
+ [ Opcodes.local_get, 1 ],
171
+ [ Opcodes.local_get, 2 ]
172
+ ],
173
+ _thisWasm: [
174
+ [ Opcodes.local_get, 3 ],
175
+ [ Opcodes.local_get, 4 ]
176
+ ]
177
+ }));
108
178
 
109
179
  if (func.returns[0] === Valtype.i32) {
110
180
  if (func.returns.length === 2) {
111
- const localIdx = localInd++;
181
+ const localIdx = wrapperFunc.localInd++;
112
182
  locals[localIdx] = { idx: localIdx, type: Valtype.i32 };
113
183
 
114
184
  wasm.push(
@@ -131,29 +201,10 @@ const funcRef = func => {
131
201
  wasm.push(number(func.returnType ?? TYPES.number, Valtype.i32));
132
202
  }
133
203
 
134
- const name = '#indirect_' + func.name;
135
- const wrapperFunc = {
136
- name,
137
- params,
138
- locals, localInd,
139
- returns: [ valtypeBinary, Valtype.i32 ],
140
- wasm,
141
- constr: true,
142
- internal: true,
143
- indirect: true,
144
- wrapperOf: func,
145
- indirectIndex: indirectFuncs.length
146
- };
147
-
148
- indirectFuncs.push(wrapperFunc);
149
-
150
- wrapperFunc.jsLength = countLength(func);
151
- func.wrapperFunc = wrapperFunc;
152
-
153
204
  if (!func.constr) {
154
205
  // check not being constructed
155
206
  wasm.unshift(
156
- [ Opcodes.local_get, 0 ], // new.target value
207
+ [ Opcodes.local_get, 1 ], // new.target value
157
208
  Opcodes.i32_to_u,
158
209
  [ Opcodes.if, Blocktype.void ], // if value is non-zero
159
210
  // ...internalThrow(wrapperFunc, 'TypeError', `${unhackName(func.name)} is not a constructor`), // throw type error
@@ -2053,7 +2104,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2053
2104
  name = func.name;
2054
2105
  }
2055
2106
 
2056
- if (!decl._new && (name === 'eval' || (decl.callee.type === 'SequenceExpression' && decl.callee.expressions.at(-1)?.name === 'eval'))) {
2107
+ if (!decl._funcIdx && !decl._new && (name === 'eval' || (decl.callee.type === 'SequenceExpression' && decl.callee.expressions.at(-1)?.name === 'eval'))) {
2057
2108
  const known = knownValue(scope, decl.arguments[0]);
2058
2109
  if (known !== unknownValue) {
2059
2110
  // eval('with known/literal string')
@@ -2099,7 +2150,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2099
2150
  }
2100
2151
  }
2101
2152
 
2102
- if (name === 'Function') {
2153
+ if (!decl._funcIdx && name === 'Function') {
2103
2154
  const knowns = decl.arguments.map(x => knownValue(scope, x));
2104
2155
  if (knowns.every(x => x !== unknownValue)) {
2105
2156
  // new Function('with known/literal strings')
@@ -2235,19 +2286,24 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2235
2286
  const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
2236
2287
  const pointerLocal = localTmp(scope, '__proto_pointer_cache', Valtype.i32);
2237
2288
 
2238
- // TODO: long-term, prototypes should be their individual separate funcs
2239
-
2240
- const rawPointer = [
2241
- ...generate(scope, target),
2242
- Opcodes.i32_to_u
2243
- ];
2244
-
2245
- const usePointerCache = !Object.values(protoCands).every(x => x.noPointerCache === true);
2246
- const getPointer = usePointerCache ? [ [ Opcodes.local_get, pointerLocal ] ] : rawPointer;
2289
+ if (out.length === 0) {
2290
+ out.push(
2291
+ ...generate(scope, target),
2292
+ Opcodes.i32_to_u,
2293
+ [ Opcodes.local_set, pointerLocal ]
2294
+ );
2295
+ } else {
2296
+ out.push(
2297
+ [ Opcodes.local_get, localTmp(scope, '#proto_target') ],
2298
+ Opcodes.i32_to_u,
2299
+ [ Opcodes.local_set, pointerLocal ]
2300
+ );
2301
+ }
2247
2302
 
2248
- const useLengthCache = true; // basically every prototype uses it
2249
2303
  for (const x in protoCands) {
2250
2304
  const protoFunc = protoCands[x];
2305
+ const getPointer = [ [ Opcodes.local_get, pointerLocal ] ];
2306
+
2251
2307
  if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
2252
2308
  protoBC[x] = [
2253
2309
  ...ArrayUtil.getLength(getPointer),
@@ -2287,37 +2343,16 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2287
2343
  });
2288
2344
 
2289
2345
  return [
2290
- [ Opcodes.block, unusedValue ? Blocktype.void : valtypeBinary ],
2346
+ ...ArrayUtil.getLengthI32(getPointer),
2347
+ [ Opcodes.local_set, lengthLocal ],
2348
+
2349
+ [ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
2291
2350
  ...protoOut,
2292
- ...(unusedValue && !optUnused ? [ [ Opcodes.drop ] ] : []),
2293
- [ Opcodes.end ]
2351
+ [ Opcodes.end ],
2352
+ ...(unusedValue && optUnused ? [ number(UNDEFINED) ] : [])
2294
2353
  ];
2295
2354
  };
2296
2355
  }
2297
-
2298
- // alias primitive prototype with primitive object types
2299
- aliasPrimObjsBC(protoBC);
2300
-
2301
- return [
2302
- ...(usePointerCache ? [
2303
- ...rawPointer,
2304
- [ Opcodes.local_set, pointerLocal ],
2305
- ] : []),
2306
-
2307
- ...(useLengthCache ? [
2308
- ...ArrayUtil.getLengthI32(getPointer),
2309
- [ Opcodes.local_set, lengthLocal ],
2310
- ] : []),
2311
-
2312
- ...typeSwitch(scope, getNodeType(scope, target), {
2313
- ...protoBC,
2314
-
2315
- // TODO: error better
2316
- default: decl.optional ? withType(scope, [ number(UNDEFINED) ], TYPES.undefined)
2317
- : internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`, !unusedValue)
2318
- }, unusedValue ? Blocktype.void : valtypeBinary),
2319
- ...(unusedValue ? [ number(UNDEFINED) ] : [])
2320
- ];
2321
2356
  }
2322
2357
 
2323
2358
  if (Object.keys(protoBC).length > 0) {
@@ -2372,7 +2407,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2372
2407
  }
2373
2408
 
2374
2409
  let idx;
2375
- if (Object.hasOwn(funcIndex, name)) {
2410
+ if (decl._funcIdx) {
2411
+ idx = decl._funcIdx;
2412
+ } else if (Object.hasOwn(funcIndex, name)) {
2376
2413
  idx = funcIndex[name];
2377
2414
  } else if (scope.name === name) {
2378
2415
  // fallback for own func but with a different var/id name
@@ -2501,7 +2538,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2501
2538
  }
2502
2539
  }
2503
2540
 
2504
- let callee = decl.callee, callAsNew = decl._new;
2541
+ let callee = decl.callee, callAsNew = decl._new, sup = false;
2505
2542
  if (callee.type === 'Super') {
2506
2543
  // call super constructor with direct super() call
2507
2544
  callee = getObjProp(callee, 'constructor');
@@ -2510,6 +2547,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2510
2547
  ...generate(scope, { type: 'ThisExpression' }),
2511
2548
  ...getNodeType(scope, { type: 'ThisExpression' })
2512
2549
  ];
2550
+ sup = true;
2513
2551
  }
2514
2552
 
2515
2553
  const newTargetWasm = decl._newTargetWasm ?? createNewTarget(scope, decl, [
@@ -2517,32 +2555,35 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2517
2555
  ], callAsNew);
2518
2556
  const thisWasm = decl._thisWasm ?? knownThis ?? createThisArg(scope, decl);
2519
2557
 
2520
- return [
2558
+ out = [
2521
2559
  ...(getCallee ? getCallee : generate(scope, callee)),
2522
2560
  [ Opcodes.local_set, calleeLocal ],
2523
2561
 
2524
2562
  ...typeSwitch(scope, getNodeType(scope, callee), {
2525
2563
  [TYPES.function]: () => [
2564
+ number(10 - underflow, Valtype.i32),
2526
2565
  ...forceDuoValtype(scope, newTargetWasm, Valtype.f64),
2527
2566
  ...forceDuoValtype(scope, thisWasm, Valtype.f64),
2528
2567
  ...out,
2529
2568
 
2530
2569
  [ Opcodes.local_get, calleeLocal ],
2531
2570
  Opcodes.i32_to_u,
2532
- [ Opcodes.call_indirect, args.length + 2, 0 ],
2571
+ [ Opcodes.call_indirect, args.length + 2, 0, ],
2533
2572
  ...setLastType(scope)
2534
2573
  ],
2535
2574
 
2536
2575
  default: decl.optional ? withType(scope, [ number(UNDEFINED, Valtype.f64) ], TYPES.undefined)
2537
2576
  : internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, Valtype.f64)
2538
- }, Valtype.f64),
2539
- ...(valtypeBinary === Valtype.i32 ? [ Opcodes.i32_trunc_sat_f64_s ] : [])
2577
+ }, Valtype.f64)
2540
2578
  ];
2579
+
2580
+ if (valtypeBinary === Valtype.i32) out.push(Opcodes.i32_trunc_sat_f64_s);
2581
+ if (sup) out.push([ null, 'super marker' ]);
2582
+ return out;
2541
2583
  }
2542
2584
 
2543
2585
  const func = funcByIndex(idx);
2544
-
2545
- if (func && !decl._new) func.onlyNew = false;
2586
+ if (func && !decl._new && !decl._insideIndirect) func.onlyNew = false;
2546
2587
 
2547
2588
  // generate func
2548
2589
  if (func) func.generate?.();
@@ -2638,6 +2679,8 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2638
2679
  }
2639
2680
 
2640
2681
  out.push([ Opcodes.call, idx ]);
2682
+ if (decl._insideIndirect) return out;
2683
+
2641
2684
  if (typedReturns) out.push(...setLastType(scope));
2642
2685
 
2643
2686
  if (
@@ -6205,14 +6248,33 @@ const generateClass = (scope, decl) => {
6205
6248
  const proto = getObjProp(root, 'prototype');
6206
6249
 
6207
6250
  const [ func, out ] = generateFunc(scope, {
6208
- ...(body.find(x => x.kind === 'constructor')?.value ?? {
6251
+ ...(body.find(x => x.kind === 'constructor')?.value ?? (decl.superClass ? {
6252
+ type: 'FunctionExpression',
6253
+ params: [
6254
+ {
6255
+ type: 'RestElement',
6256
+ argument: { type: 'Identifier', name: 'args' }
6257
+ }
6258
+ ],
6259
+ body: {
6260
+ type: 'ExpressionStatement',
6261
+ expression: {
6262
+ type: 'CallExpression',
6263
+ callee: { type: 'Super' },
6264
+ arguments: [ {
6265
+ type: 'SpreadElement',
6266
+ argument: { type: 'Identifier', name: 'args' }
6267
+ } ]
6268
+ }
6269
+ }
6270
+ }: {
6209
6271
  type: 'FunctionExpression',
6210
6272
  params: [],
6211
6273
  body: {
6212
6274
  type: 'BlockStatement',
6213
6275
  body: []
6214
6276
  }
6215
- }),
6277
+ })),
6216
6278
  id: root,
6217
6279
  strict: true,
6218
6280
  type: expr ? 'FunctionExpression' : 'FunctionDeclaration',
@@ -6223,6 +6285,13 @@ const generateClass = (scope, decl) => {
6223
6285
  // always generate class constructor funcs
6224
6286
  func.generate();
6225
6287
 
6288
+ let constrInsertIndex = func.wasm.findIndex(x => x.at(-1) === 'super marker');
6289
+ if (constrInsertIndex != -1) {
6290
+ func.wasm.splice(constrInsertIndex, 1);
6291
+ } else {
6292
+ constrInsertIndex = 0;
6293
+ }
6294
+
6226
6295
  if (decl.superClass) {
6227
6296
  const superTmp = localTmp(scope, '#superclass');
6228
6297
  const superTypeTmp = localTmp(scope, '#superclass#type', Valtype.i32);
@@ -6285,6 +6354,7 @@ const generateClass = (scope, decl) => {
6285
6354
  scope.overrideThis = generate(scope, root);
6286
6355
  scope.overrideThisType = TYPES.function;
6287
6356
 
6357
+ let constrAdd = [];
6288
6358
  for (const x of body) {
6289
6359
  let { type, value, kind, static: _static, computed } = x;
6290
6360
  if (kind === 'constructor') continue;
@@ -6349,7 +6419,7 @@ const generateClass = (scope, decl) => {
6349
6419
  );
6350
6420
  }
6351
6421
 
6352
- func.wasm.unshift(
6422
+ const constrWasm = [
6353
6423
  ...generate(func, object),
6354
6424
  Opcodes.i32_to_u,
6355
6425
  ...getNodeType(func, object),
@@ -6368,7 +6438,10 @@ const generateClass = (scope, decl) => {
6368
6438
  ...getNodeType(func, value),
6369
6439
 
6370
6440
  [ Opcodes.call, includeBuiltin(func, `__Porffor_object_class_${initKind}`).index ]
6371
- );
6441
+ ];
6442
+
6443
+ func.wasm.splice(constrInsertIndex, 0, ...constrWasm);
6444
+ constrInsertIndex += constrWasm.length;
6372
6445
  } else {
6373
6446
  out.push(
6374
6447
  ...generate(scope, object),
@@ -320,7 +320,6 @@ export const PrototypeFuncs = function() {
320
320
  this[TYPES.string].at.local2 = Valtype.i32;
321
321
  this[TYPES.string].charAt.local = Valtype.i32;
322
322
  this[TYPES.string].charCodeAt.local = Valtype.i32;
323
- this[TYPES.string].charCodeAt.noPointerCache = zeroChecks.charcodeat;
324
323
 
325
324
  this[TYPES.bytestring] = {
326
325
  at: ({ pointer, length, arg, iTmp, iTmp2, alloc, setType }) => [
@@ -454,5 +453,4 @@ export const PrototypeFuncs = function() {
454
453
  this[TYPES.bytestring].at.local2 = Valtype.i32;
455
454
  this[TYPES.bytestring].charAt.local = Valtype.i32;
456
455
  this[TYPES.bytestring].charCodeAt.local = Valtype.i32;
457
- this[TYPES.bytestring].charCodeAt.noPointerCache = zeroChecks.charcodeat;
458
456
  };
package/foo.js ADDED
@@ -0,0 +1,5 @@
1
+ let bcPtr = 16;
2
+ let classNegate = false;
3
+ if (true) {
4
+ Porffor.wasm.i32.store8(bcPtr, classNegate ? 0x03 : 0x02, 0, 0);
5
+ }
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@honk/porffor",
3
- "version": "0.58.4",
3
+ "version": "0.58.6",
4
4
  "exports": "./compiler/wrap.js",
5
5
  "publish": {
6
6
  "exclude": [
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "porffor",
3
3
  "description": "An ahead-of-time JavaScript compiler",
4
- "version": "0.58.4",
4
+ "version": "0.58.6",
5
5
  "author": "Oliver Medhurst <honk@goose.icu>",
6
6
  "license": "MIT",
7
7
  "scripts": {},
package/runtime/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from 'node:fs';
3
- globalThis.version = '0.58.4';
3
+ globalThis.version = '0.58.6';
4
4
 
5
5
  // deno compat
6
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {