porffor 0.1.1 → 0.2.0-c6c8c81

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.
package/compiler/parse.js CHANGED
@@ -1,4 +1,27 @@
1
- const { parse } = (await import(globalThis.document ? 'https://esm.sh/acorn' : 'acorn'));
1
+ import { log } from "./log.js";
2
+
3
+ // deno compat
4
+ if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
5
+ const textEncoder = new TextEncoder();
6
+ globalThis.process = { argv: ['', '', ...Deno.args], stdout: { write: str => Deno.writeAllSync(Deno.stdout, textEncoder.encode(str)) } };
7
+ }
8
+
9
+ // import { parse } from 'acorn';
10
+
11
+ // todo: review which to use by default
12
+ const parser = process.argv.find(x => x.startsWith('-parser='))?.split('=')?.[1] ?? 'acorn';
13
+ const { parse } = (await import((globalThis.document ? 'https://esm.sh/' : '') + parser));
14
+
15
+ // supported parsers:
16
+ // - acorn
17
+ // - meriyah
18
+ // - hermes-parser
19
+ // - @babel/parser
20
+
21
+ // should we try to support types (while parsing)
22
+ const types = process.argv.includes('-types');
23
+
24
+ if (types && !['@babel/parser', 'hermes-parser'].includes(parser)) log.warning('parser', `passed -types with a parser (${parser}) which does not support`);
2
25
 
3
26
  export default (input, flags) => {
4
27
  return parse(input, {
@@ -5,27 +5,30 @@ import { UNDEFINED } from "./builtins.js";
5
5
 
6
6
  // todo: do not duplicate this
7
7
  const TYPES = {
8
- number: 0xffffffffffff0,
9
- boolean: 0xffffffffffff1,
10
- string: 0xffffffffffff2,
11
- undefined: 0xffffffffffff3,
12
- object: 0xffffffffffff4,
13
- function: 0xffffffffffff5,
14
- symbol: 0xffffffffffff6,
15
- bigint: 0xffffffffffff7,
8
+ number: 0x00,
9
+ boolean: 0x01,
10
+ string: 0x02,
11
+ undefined: 0x03,
12
+ object: 0x04,
13
+ function: 0x05,
14
+ symbol: 0x06,
15
+ bigint: 0x07,
16
16
 
17
17
  // these are not "typeof" types but tracked internally
18
- _array: 0xffffffffffff8
18
+ _array: 0x10,
19
+ _regexp: 0x11
19
20
  };
20
21
 
21
22
  // todo: turn these into built-ins once arrays and these become less hacky
22
23
 
23
24
  export const PrototypeFuncs = function() {
24
25
  const noUnlikelyChecks = process.argv.includes('-funsafe-no-unlikely-proto-checks');
26
+ let zeroChecks = process.argv.find(x => x.startsWith('-funsafe-zero-proto-checks='));
27
+ if (zeroChecks) zeroChecks = zeroChecks.split('=')[1].split(',').reduce((acc, x) => { acc[x.toLowerCase()] = true; return acc; }, {});
28
+ else zeroChecks = {};
25
29
 
26
30
  this[TYPES._array] = {
27
31
  // lX = local accessor of X ({ get, set }), iX = local index of X, wX = wasm ops of X
28
- // todo: out of bounds (>) properly
29
32
  at: (pointer, length, wIndex, iTmp) => [
30
33
  ...wIndex,
31
34
  Opcodes.i32_to,
@@ -36,7 +39,7 @@ export const PrototypeFuncs = function() {
36
39
  [ Opcodes.i32_lt_s ],
37
40
  [ Opcodes.if, Blocktype.void ],
38
41
  [ Opcodes.local_get, iTmp ],
39
- ...length.cachedI32,
42
+ ...length.getCachedI32(),
40
43
  [ Opcodes.i32_add ],
41
44
  [ Opcodes.local_set, iTmp ],
42
45
  [ Opcodes.end ],
@@ -47,7 +50,7 @@ export const PrototypeFuncs = function() {
47
50
  [ Opcodes.i32_lt_s ],
48
51
 
49
52
  [ Opcodes.local_get, iTmp ],
50
- ...length.cachedI32,
53
+ ...length.getCachedI32(),
51
54
  [ Opcodes.i32_ge_s ],
52
55
  [ Opcodes.i32_or ],
53
56
 
@@ -60,36 +63,48 @@ export const PrototypeFuncs = function() {
60
63
  ...number(ValtypeSize[valtype], Valtype.i32),
61
64
  [ Opcodes.i32_mul ],
62
65
 
66
+ ...pointer,
67
+ [ Opcodes.i32_add ],
68
+
63
69
  // read from memory
64
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32) ]
70
+ [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
65
71
  ],
66
72
 
67
73
  // todo: only for 1 argument
68
74
  push: (pointer, length, wNewMember) => [
69
75
  // get memory offset of array at last index (length)
70
- ...length.cachedI32,
76
+ ...length.getCachedI32(),
71
77
  ...number(ValtypeSize[valtype], Valtype.i32),
72
78
  [ Opcodes.i32_mul ],
73
79
 
80
+ ...pointer,
81
+ [ Opcodes.i32_add ],
82
+
74
83
  // generated wasm for new member
75
84
  ...wNewMember,
76
85
 
77
86
  // store in memory
78
- [ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32) ],
87
+ [ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
79
88
 
80
89
  // bump array length by 1 and return it
81
90
  ...length.setI32([
82
- ...length.cachedI32,
91
+ ...length.getCachedI32(),
83
92
  ...number(1, Valtype.i32),
84
- [ Opcodes.i32_add ]
93
+ [ Opcodes.i32_add ],
94
+
95
+ ...length.setCachedI32(),
96
+ ...length.getCachedI32(),
85
97
  ]),
86
98
 
87
- ...length.get
99
+ ...length.getCachedI32(),
100
+ Opcodes.i32_from_u
101
+
102
+ // ...length.get()
88
103
  ],
89
104
 
90
105
  pop: (pointer, length) => [
91
106
  // if length == 0, noop
92
- ...length.cachedI32,
107
+ ...length.getCachedI32(),
93
108
  [ Opcodes.i32_eqz ],
94
109
  [ Opcodes.if, Blocktype.void ],
95
110
  ...number(UNDEFINED),
@@ -100,22 +115,28 @@ export const PrototypeFuncs = function() {
100
115
 
101
116
  // decrement length by 1
102
117
  ...length.setI32([
103
- ...length.cachedI32,
118
+ ...length.getCachedI32(),
104
119
  ...number(1, Valtype.i32),
105
- [ Opcodes.i32_sub ]
120
+ [ Opcodes.i32_sub ],
121
+
122
+ ...length.setCachedI32(),
123
+ ...length.getCachedI32(),
106
124
  ]),
107
125
 
108
126
  // load last element
109
- ...length.cachedI32,
127
+ ...length.getCachedI32(),
110
128
  ...number(ValtypeSize[valtype], Valtype.i32),
111
129
  [ Opcodes.i32_mul ],
112
130
 
113
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 - ValtypeSize[valtype]) ]
131
+ ...pointer,
132
+ [ Opcodes.i32_add ],
133
+
134
+ [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
114
135
  ],
115
136
 
116
137
  shift: (pointer, length) => [
117
138
  // if length == 0, noop
118
- ...length.cachedI32,
139
+ ...length.getCachedI32(),
119
140
  Opcodes.i32_eqz,
120
141
  [ Opcodes.if, Blocktype.void ],
121
142
  ...number(UNDEFINED),
@@ -126,29 +147,122 @@ export const PrototypeFuncs = function() {
126
147
 
127
148
  // decrement length by 1
128
149
  ...length.setI32([
129
- ...length.cachedI32,
150
+ ...length.getCachedI32(),
130
151
  ...number(1, Valtype.i32),
131
- [ Opcodes.i32_sub ]
152
+ [ Opcodes.i32_sub ],
153
+
154
+ ...length.setCachedI32(),
155
+ ...length.getCachedI32(),
132
156
  ]),
133
157
 
134
158
  // load first element
135
- ...number(0, Valtype.i32),
136
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32) ],
159
+ ...pointer,
160
+ [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
161
+
162
+ // offset page by -1 ind
163
+ // ...number(pointer + ValtypeSize.i32, Valtype.i32), // dst = base array index + length size
164
+ // ...number(pointer + ValtypeSize.i32 + ValtypeSize[valtype], Valtype.i32), // src = base array index + length size + an index
165
+ // ...number(pageSize - ValtypeSize.i32 - ValtypeSize[valtype], Valtype.i32), // size = PageSize - length size - an index
166
+ // [ ...Opcodes.memory_copy, 0x00, 0x00 ]
137
167
 
138
168
  // offset all elements by -1 ind
139
- ...number(pointer + ValtypeSize.i32, Valtype.i32), // dst = base array index + length size
140
- ...number(pointer + ValtypeSize.i32 + ValtypeSize[valtype], Valtype.i32), // src = base array index + length size + an index
141
- ...number(pageSize - ValtypeSize.i32 - ValtypeSize[valtype], Valtype.i32), // size = PageSize - length size - an index
169
+
170
+ // dst = base array index + length size
171
+ ...number(ValtypeSize.i32, Valtype.i32),
172
+ ...pointer,
173
+ [ Opcodes.i32_add ],
174
+
175
+ // src = base array index + length size + an index
176
+ ...number(ValtypeSize.i32 + ValtypeSize[valtype], Valtype.i32),
177
+ ...pointer,
178
+ [ Opcodes.i32_add ],
179
+
180
+ // size = new length * sizeof element
181
+ ...length.getCachedI32(),
182
+ ...number(ValtypeSize[valtype], Valtype.i32),
183
+ [ Opcodes.i32_mul ],
142
184
  [ ...Opcodes.memory_copy, 0x00, 0x00 ]
185
+
186
+ // move pointer + sizeof element
187
+ // ...pointer.get(),
188
+ // ...number(ValtypeSize[valtype], Valtype.i32),
189
+ // [ Opcodes.i32_add ],
190
+ // ...pointer.set(),
191
+
192
+ // // write length - 1 in new address
193
+ // ...length.setI32([
194
+ // ...length.getCachedI32(),
195
+ // ...number(1, Valtype.i32),
196
+ // [ Opcodes.i32_sub ]
197
+ // ]),
198
+ ],
199
+
200
+ fill: (pointer, length, wElement, iTmp) => [
201
+ ...wElement,
202
+ [ Opcodes.local_set, iTmp ],
203
+
204
+ // use cached length i32 as pointer
205
+ ...length.getCachedI32(),
206
+
207
+ // length - 1 for indexes
208
+ ...number(1, Valtype.i32),
209
+ [ Opcodes.i32_sub ],
210
+
211
+ // * sizeof value
212
+ ...number(ValtypeSize[valtype], Valtype.i32),
213
+ [ Opcodes.i32_mul ],
214
+
215
+ ...length.setCachedI32(),
216
+
217
+ ...(noUnlikelyChecks ? [] : [
218
+ ...length.getCachedI32(),
219
+ ...number(0, Valtype.i32),
220
+ [ Opcodes.i32_lt_s ],
221
+ [ Opcodes.if, Blocktype.void ],
222
+ ...pointer,
223
+ Opcodes.i32_from_u,
224
+ [ Opcodes.br, 1 ],
225
+ [ Opcodes.end ]
226
+ ]),
227
+
228
+ [ Opcodes.loop, Blocktype.void ],
229
+
230
+ // set element using pointer
231
+ ...length.getCachedI32(),
232
+ ...pointer,
233
+ [ Opcodes.i32_add ],
234
+
235
+ [ Opcodes.local_get, iTmp ],
236
+ [ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128( ValtypeSize.i32) ],
237
+
238
+ // pointer - sizeof value
239
+ ...length.getCachedI32(),
240
+ ...number(ValtypeSize[valtype], Valtype.i32),
241
+ [ Opcodes.i32_sub ],
242
+
243
+ ...length.setCachedI32(),
244
+
245
+ // if pointer >= 0, loop
246
+ ...length.getCachedI32(),
247
+ ...number(0, Valtype.i32),
248
+ [ Opcodes.i32_ge_s ],
249
+ [ Opcodes.br_if, 0 ],
250
+
251
+ [ Opcodes.end ],
252
+
253
+ // return this array
254
+ ...pointer,
255
+ Opcodes.i32_from_u,
143
256
  ]
144
257
  };
145
258
 
146
259
  this[TYPES._array].at.local = Valtype.i32;
147
260
  this[TYPES._array].push.noArgRetLength = true;
261
+ this[TYPES._array].fill.local = valtypeBinary;
262
+ this[TYPES._array].fill.returnType = TYPES._array;
148
263
 
149
264
  this[TYPES.string] = {
150
- // todo: out of bounds properly
151
- at: (pointer, length, wIndex, iTmp, arrayShell) => {
265
+ at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
152
266
  const [ newOut, newPointer ] = arrayShell(1, 'i16');
153
267
 
154
268
  return [
@@ -157,9 +271,9 @@ export const PrototypeFuncs = function() {
157
271
  [ Opcodes.drop ],
158
272
 
159
273
  ...number(0, Valtype.i32), // base 0 for store later
160
- Opcodes.i32_to_u,
161
274
 
162
275
  ...wIndex,
276
+ Opcodes.i32_to_u,
163
277
  [ Opcodes.local_tee, iTmp ],
164
278
 
165
279
  // if index < 0: access index + array length
@@ -167,7 +281,7 @@ export const PrototypeFuncs = function() {
167
281
  [ Opcodes.i32_lt_s ],
168
282
  [ Opcodes.if, Blocktype.void ],
169
283
  [ Opcodes.local_get, iTmp ],
170
- ...length.cachedI32,
284
+ ...length.getCachedI32(),
171
285
  [ Opcodes.i32_add ],
172
286
  [ Opcodes.local_set, iTmp ],
173
287
  [ Opcodes.end ],
@@ -178,7 +292,7 @@ export const PrototypeFuncs = function() {
178
292
  [ Opcodes.i32_lt_s ],
179
293
 
180
294
  [ Opcodes.local_get, iTmp ],
181
- ...length.cachedI32,
295
+ ...length.getCachedI32(),
182
296
  [ Opcodes.i32_ge_s ],
183
297
  [ Opcodes.i32_or ],
184
298
 
@@ -191,8 +305,11 @@ export const PrototypeFuncs = function() {
191
305
  ...number(ValtypeSize.i16, Valtype.i32),
192
306
  [ Opcodes.i32_mul ],
193
307
 
308
+ ...pointer,
309
+ [ Opcodes.i32_add ],
310
+
194
311
  // load current string ind {arg}
195
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32) ],
312
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
196
313
 
197
314
  // store to new string ind 0
198
315
  [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
@@ -203,7 +320,7 @@ export const PrototypeFuncs = function() {
203
320
  },
204
321
 
205
322
  // todo: out of bounds properly
206
- charAt: (pointer, length, wIndex, _, arrayShell) => {
323
+ charAt: (pointer, length, wIndex, _1, _2, arrayShell) => {
207
324
  const [ newOut, newPointer ] = arrayShell(1, 'i16');
208
325
 
209
326
  return [
@@ -219,8 +336,11 @@ export const PrototypeFuncs = function() {
219
336
  ...number(ValtypeSize.i16, Valtype.i32),
220
337
  [ Opcodes.i32_mul ],
221
338
 
339
+ ...pointer,
340
+ [ Opcodes.i32_add ],
341
+
222
342
  // load current string ind {arg}
223
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32) ],
343
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
224
344
 
225
345
  // store to new string ind 0
226
346
  [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
@@ -234,39 +354,126 @@ export const PrototypeFuncs = function() {
234
354
  return [
235
355
  ...wIndex,
236
356
  Opcodes.i32_to,
237
- [ Opcodes.local_set, iTmp ],
238
357
 
239
- // index < 0
240
- ...(noUnlikelyChecks ? [] : [
358
+ ...(zeroChecks.charcodeat ? [] : [
359
+ [ Opcodes.local_set, iTmp ],
360
+
361
+ // index < 0
362
+ ...(noUnlikelyChecks ? [] : [
363
+ [ Opcodes.local_get, iTmp ],
364
+ ...number(0, Valtype.i32),
365
+ [ Opcodes.i32_lt_s ],
366
+ ]),
367
+
368
+ // index >= length
369
+ [ Opcodes.local_get, iTmp ],
370
+ ...length.getCachedI32(),
371
+ [ Opcodes.i32_ge_s ],
372
+
373
+ ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
374
+ [ Opcodes.if, Blocktype.void ],
375
+ ...number(NaN),
376
+ [ Opcodes.br, 1 ],
377
+ [ Opcodes.end ],
378
+
241
379
  [ Opcodes.local_get, iTmp ],
242
- ...number(0, Valtype.i32),
243
- [ Opcodes.i32_lt_s ],
244
380
  ]),
245
381
 
246
- // index >= length
382
+ ...number(ValtypeSize.i16, Valtype.i32),
383
+ [ Opcodes.i32_mul ],
384
+
385
+ ...pointer,
386
+ [ Opcodes.i32_add ],
387
+
388
+ // load current string ind {arg}
389
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
390
+ Opcodes.i32_from_u
391
+ ];
392
+ },
393
+
394
+ isWellFormed: (pointer, length, wIndex, iTmp, iTmp2) => {
395
+ return [
396
+ // note: we cannot presume it begins as 0 in case it was used previously
397
+ ...pointer,
398
+ [ Opcodes.local_set, iTmp ],
399
+
400
+ // use cached length as end pointer
401
+ ...length.getCachedI32(),
402
+ ...number(ValtypeSize.i16, Valtype.i32),
403
+ [ Opcodes.i32_mul ],
404
+ ...pointer,
405
+ [ Opcodes.i32_add ],
406
+ ...length.setCachedI32(),
407
+
408
+ [ Opcodes.loop, Blocktype.void ],
409
+
410
+ [ Opcodes.block, Blocktype.void ],
411
+
247
412
  [ Opcodes.local_get, iTmp ],
248
- ...length.cachedI32,
413
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
414
+ [ Opcodes.local_set, iTmp2 ],
415
+
416
+ // if not surrogate, continue
417
+ [ Opcodes.local_get, iTmp2 ],
418
+ ...number(0xF800, Valtype.i32),
419
+ [ Opcodes.i32_and ],
420
+ ...number(0xD800, Valtype.i32),
421
+ [ Opcodes.i32_ne ],
422
+ [ Opcodes.br_if, 0 ],
423
+
424
+ // if not leading surrogate, return false
425
+ [ Opcodes.local_get, iTmp2 ],
426
+ ...number(0xDC00, Valtype.i32),
249
427
  [ Opcodes.i32_ge_s ],
428
+ [ Opcodes.if, Blocktype.void ],
429
+ ...number(0),
430
+ [ Opcodes.br, 3 ],
431
+ [ Opcodes.end ],
250
432
 
251
- ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
433
+ // if not followed by trailing surrogate, return false
434
+ [ Opcodes.local_get, iTmp ],
435
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize.i16) ],
436
+ ...number(0xFC00, Valtype.i32),
437
+ [ Opcodes.i32_and ],
438
+ ...number(0xDC00, Valtype.i32),
439
+ [ Opcodes.i32_ne ],
252
440
  [ Opcodes.if, Blocktype.void ],
253
- ...number(NaN),
254
- [ Opcodes.br, 1 ],
441
+ ...number(0),
442
+ [ Opcodes.br, 3 ],
255
443
  [ Opcodes.end ],
256
444
 
445
+ // bump index again since gone through two valid chars
257
446
  [ Opcodes.local_get, iTmp ],
258
447
  ...number(ValtypeSize.i16, Valtype.i32),
259
- [ Opcodes.i32_mul ],
448
+ [ Opcodes.i32_add ],
449
+ [ Opcodes.local_set, iTmp ],
260
450
 
261
- // load current string ind {arg}
262
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32) ],
263
- Opcodes.i32_from_u
264
- ];
265
- },
451
+ [ Opcodes.end ],
452
+
453
+ // bump pointer and loop if not at the end
454
+ [ Opcodes.local_get, iTmp ],
455
+ ...number(ValtypeSize.i16, Valtype.i32),
456
+ [ Opcodes.i32_add ],
457
+ [ Opcodes.local_tee, iTmp ],
458
+
459
+ ...length.getCachedI32(), // end pointer
460
+ [ Opcodes.i32_ne ],
461
+ [ Opcodes.br_if, 0 ],
462
+
463
+ [ Opcodes.end ],
464
+
465
+ // return true
466
+ ...number(1)
467
+ ]
468
+ }
266
469
  };
267
470
 
268
- this[TYPES.string].at.local = valtypeBinary;
471
+ this[TYPES.string].at.local = Valtype.i32;
269
472
  this[TYPES.string].at.returnType = TYPES.string;
270
473
  this[TYPES.string].charAt.returnType = TYPES.string;
271
474
  this[TYPES.string].charCodeAt.local = Valtype.i32;
475
+
476
+ this[TYPES.string].isWellFormed.local = Valtype.i32;
477
+ this[TYPES.string].isWellFormed.local2 = Valtype.i32;
478
+ this[TYPES.string].isWellFormed.returnType = TYPES.boolean;
272
479
  };
@@ -1,18 +1,34 @@
1
1
  import { Valtype, FuncType, Empty, ExportDesc, Section, Magic, ModuleVersion, Opcodes, PageSize } from './wasmSpec.js';
2
- import { encodeVector, encodeString, encodeLocal } from './encoding.js';
2
+ import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128 } from './encoding.js';
3
3
  import { number } from './embedding.js';
4
4
  import { importedFuncs } from './builtins.js';
5
+ import { log } from "./log.js";
5
6
 
6
7
  const createSection = (type, data) => [
7
8
  type,
8
9
  ...encodeVector(data)
9
10
  ];
10
11
 
11
- export default (funcs, globals, tags, pages, flags) => {
12
+ const customSection = (name, data) => [
13
+ Section.custom,
14
+ ...encodeVector([...encodeString(name), ...data])
15
+ ];
16
+
17
+ const chHint = (topTier, baselineTier, strategy) => {
18
+ // 1 byte of 4 2 bit components: spare, top tier, baseline tier, compilation strategy
19
+ // tiers: 0x00 = default, 0x01 = baseline (liftoff), 0x02 = optimized (turbofan)
20
+ // strategy: 0x00 = default, 0x01 = lazy, 0x02 = eager, 0x03 = lazy baseline, eager top tier
21
+ return (strategy | (baselineTier << 2) | (topTier << 4));
22
+ };
23
+
24
+ export default (funcs, globals, tags, pages, data, flags) => {
12
25
  const types = [], typeCache = {};
13
26
 
14
27
  const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
15
28
 
29
+ const compileHints = process.argv.includes('-compile-hints');
30
+ if (compileHints) log.warning('sections', 'compile hints is V8 only w/ experimental arg! (you used -compile-hints)');
31
+
16
32
  const getType = (params, returns) => {
17
33
  const hash = `${params.join(',')}_${returns.join(',')}`;
18
34
  if (optLog) log('sections', `getType(${JSON.stringify(params)}, ${JSON.stringify(returns)}) -> ${hash} | cache: ${typeCache[hash]}`);
@@ -36,7 +52,7 @@ export default (funcs, globals, tags, pages, flags) => {
36
52
  // tree shake imports
37
53
  for (const f of funcs) {
38
54
  for (const inst of f.wasm) {
39
- if (inst[0] === Opcodes.call && inst[1] < importedFuncs.length) {
55
+ if ((inst[0] === Opcodes.call || inst[0] === Opcodes.return_call) && inst[1] < importedFuncs.length) {
40
56
  const idx = inst[1];
41
57
  const func = importedFuncs[idx];
42
58
 
@@ -51,15 +67,17 @@ export default (funcs, globals, tags, pages, flags) => {
51
67
  // fix call indexes for non-imports
52
68
  const delta = importedFuncs.length - importFuncs.length;
53
69
  for (const f of funcs) {
70
+ f.originalIndex = f.index;
54
71
  f.index -= delta;
55
72
 
56
73
  for (const inst of f.wasm) {
57
- if (inst[0] === Opcodes.call && inst[1] >= importedFuncs.length) {
74
+ if ((inst[0] === Opcodes.call || inst[0] === Opcodes.return_call) && inst[1] >= importedFuncs.length) {
58
75
  inst[1] -= delta;
59
76
  }
60
77
  }
61
78
  }
62
79
  }
80
+ globalThis.importFuncs = importFuncs;
63
81
 
64
82
  if (optLog) log('sections', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
65
83
 
@@ -73,6 +91,14 @@ export default (funcs, globals, tags, pages, flags) => {
73
91
  encodeVector(funcs.map(x => getType(x.params, x.returns))) // type indexes
74
92
  );
75
93
 
94
+ // compilation hints section - unspecd, v8 only
95
+ // https://github.com/WebAssembly/design/issues/1473#issuecomment-1431274746
96
+ const chSection = !compileHints ? [] : customSection(
97
+ 'compilationHints',
98
+ // for now just do everything as optimise eager
99
+ encodeVector(funcs.map(_ => chHint(0x02, 0x02, 0x02)))
100
+ );
101
+
76
102
  const globalSection = Object.keys(globals).length === 0 ? [] : createSection(
77
103
  Section.global,
78
104
  encodeVector(Object.keys(globals).map(x => [ globals[x].type, 0x01, ...number(globals[x].init ?? 0, globals[x].type).flat(), Opcodes.end ]))
@@ -80,10 +106,13 @@ export default (funcs, globals, tags, pages, flags) => {
80
106
 
81
107
  const exports = funcs.filter(x => x.export).map((x, i) => [ ...encodeString(x.name === 'main' ? 'm' : x.name), ExportDesc.func, x.index ]);
82
108
 
109
+ if (process.argv.includes('-always-memory') && pages.size === 0) pages.set('-always-memory', 0);
110
+ if (optLevel === 0) pages.set('O0 precaution', 0);
111
+
83
112
  const usesMemory = pages.size > 0;
84
113
  const memorySection = !usesMemory ? [] : createSection(
85
114
  Section.memory,
86
- encodeVector([ [ 0x00, Math.ceil((pages.size * pageSize) / PageSize) ] ])
115
+ encodeVector([ [ 0x00, ...unsignedLEB128(Math.ceil((pages.size * pageSize) / PageSize)) ] ])
87
116
  );
88
117
 
89
118
  // export memory if used
@@ -121,7 +150,7 @@ export default (funcs, globals, tags, pages, flags) => {
121
150
 
122
151
  if (typeCount !== 0) localDecl.push(encodeLocal(typeCount, lastType));
123
152
 
124
- return encodeVector([ ...encodeVector(localDecl), ...x.wasm.flat().filter(x => x !== null), Opcodes.end ]);
153
+ return encodeVector([ ...encodeVector(localDecl), ...x.wasm.flat().filter(x => x <= 0xff), Opcodes.end ]);
125
154
  }))
126
155
  );
127
156
 
@@ -130,13 +159,24 @@ export default (funcs, globals, tags, pages, flags) => {
130
159
  encodeVector(types)
131
160
  );
132
161
 
162
+ const dataSection = data.length === 0 ? [] : createSection(
163
+ Section.data,
164
+ encodeVector(data.map(x => [ 0x00, Opcodes.i32_const, ...signedLEB128(x.offset), Opcodes.end, ...encodeVector(x.bytes) ]))
165
+ );
166
+
167
+ const dataCountSection = data.length === 0 ? [] : createSection(
168
+ Section.data_count,
169
+ unsignedLEB128(data.length)
170
+ );
171
+
133
172
  if (process.argv.includes('-sections')) console.log({
134
173
  typeSection: typeSection.map(x => x.toString(16)),
135
174
  importSection: importSection.map(x => x.toString(16)),
136
175
  funcSection: funcSection.map(x => x.toString(16)),
137
176
  globalSection: globalSection.map(x => x.toString(16)),
138
177
  exportSection: exportSection.map(x => x.toString(16)),
139
- codeSection: codeSection.map(x => x.toString(16))
178
+ codeSection: codeSection.map(x => x.toString(16)),
179
+ dataSection: dataSection.map(x => x.toString(16)),
140
180
  });
141
181
 
142
182
  return Uint8Array.from([
@@ -145,10 +185,13 @@ export default (funcs, globals, tags, pages, flags) => {
145
185
  ...typeSection,
146
186
  ...importSection,
147
187
  ...funcSection,
188
+ ...chSection,
148
189
  ...memorySection,
149
190
  ...tagSection,
150
191
  ...globalSection,
151
192
  ...exportSection,
152
- ...codeSection
193
+ ...dataCountSection,
194
+ ...codeSection,
195
+ ...dataSection
153
196
  ]);
154
197
  };
@@ -118,6 +118,7 @@ export const Opcodes = {
118
118
  i64_shl: 0x86,
119
119
  i64_shr_s: 0x87,
120
120
  i64_shr_u: 0x88,
121
+ i64_rotl: 0x89,
121
122
 
122
123
  f64_eq: 0x61,
123
124
  f64_ne: 0x62,
@@ -156,6 +157,8 @@ export const Opcodes = {
156
157
  f64_convert_i64_s: 0xb9,
157
158
  f64_convert_i64_u: 0xba,
158
159
 
160
+ f64_reinterpret_i64: 0xbf,
161
+
159
162
  i32_trunc_sat_f64_s: [ 0xfc, 0x02 ],
160
163
  i32_trunc_sat_f64_u: [ 0xfc, 0x03 ],
161
164