porffor 0.57.18 → 0.57.20

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.
@@ -1,61 +1,10 @@
1
1
  import { Valtype, FuncType, ExportDesc, Section, Magic, Opcodes, PageSize, Reftype } from './wasmSpec.js';
2
- import { encodeVector, encodeString, unsignedLEB128, signedLEB128, unsignedLEB128_into, signedLEB128_into, ieee754_binary64, ieee754_binary64_into, unsignedLEB128_length } from './encoding.js';
2
+ import { unsignedLEB128_length, signedLEB128_length } from './encoding.js';
3
3
  import { importedFuncs } from './builtins.js';
4
4
  import { log } from './log.js';
5
5
  import './prefs.js';
6
6
 
7
- const createSection = (type, data) => [
8
- type,
9
- ...encodeVector(data)
10
- ];
11
-
12
- const customSection = (name, data) => createSection(
13
- Section.custom,
14
- [ ...encodeString(name), ...data ]
15
- );
16
-
17
- const encodeNames = funcs => {
18
- const encodeSection = (id, section) => [
19
- id,
20
- ...unsignedLEB128(section.length),
21
- ...section
22
- ];
23
-
24
- const moduleSection = encodeString('js'); // TODO: filename?
25
- const functionsSection = encodeVector(
26
- funcs.map(x => unsignedLEB128(x.asmIndex).concat(encodeString(x.name))),
27
- );
28
- const localsSection = encodeVector(
29
- funcs.map(x => unsignedLEB128(x.asmIndex).concat(encodeVector(
30
- Object.entries(x.locals).map(([name, local]) =>
31
- unsignedLEB128(local.idx).concat(encodeString(name))
32
- )
33
- )))
34
- );
35
-
36
- return [
37
- ...encodeSection(0, moduleSection),
38
- ...encodeSection(1, functionsSection),
39
- ...encodeSection(2, localsSection),
40
- ];
41
- };
42
-
43
7
  export default (funcs, globals, tags, pages, data, noTreeshake = false) => {
44
- const types = [], typeCache = {};
45
-
46
- const getType = (params, returns) => {
47
- const hash = `${params.join(',')}_${returns.join(',')}`;
48
- if (Prefs.optLog) log('assemble', `getType(${JSON.stringify(params)}, ${JSON.stringify(returns)}) -> ${hash} | cache: ${typeCache[hash]}`);
49
- if (typeCache[hash] !== undefined) return typeCache[hash];
50
-
51
- const type = [ FuncType, ...encodeVector(params), ...encodeVector(returns) ];
52
- const idx = types.length;
53
-
54
- types.push(type);
55
-
56
- return typeCache[hash] = idx;
57
- };
58
-
59
8
  let t = performance.now();
60
9
  const time = msg => {
61
10
  if (!Prefs.profileAssemble) return;
@@ -64,71 +13,189 @@ export default (funcs, globals, tags, pages, data, noTreeshake = false) => {
64
13
  t = performance.now();
65
14
  };
66
15
 
67
- let importFuncs = [], importDelta = 0;
16
+ let importFuncs = [];
17
+ globalThis.importDelta = 0;
68
18
  if (!Prefs.treeshakeWasmImports || noTreeshake) {
69
19
  importFuncs = Array.from(importedFuncs);
70
20
  } else {
71
- let imports = new Map();
72
-
73
21
  // tree shake imports
74
- for (const f of funcs) {
75
- if (f.usesImports) for (const inst of f.wasm) {
76
- if (inst[0] === Opcodes.call && inst[1] < importedFuncs.length) {
77
- const idx = inst[1];
22
+ const remap = new WeakMap();
23
+ for (let i = 0; i < funcs.length; i++) {
24
+ const f = funcs[i];
25
+ if (f.usesImports) for (let j = 0; j < f.wasm.length; j++) {
26
+ const x = f.wasm[j];
27
+ if (x[0] === Opcodes.call && x[1] < importedFuncs.length) {
28
+ const idx = x[1];
78
29
  const func = importedFuncs[idx];
79
30
 
80
- if (!imports.has(func.name)) imports.set(func.name, { ...func, idx: imports.size });
81
- inst[1] = imports.get(func.name).idx;
31
+ if (!remap.has(func)) {
32
+ remap.set(func, importFuncs.length);
33
+ importFuncs.push(func);
34
+ }
35
+ x[1] = remap.get(func);
82
36
  }
83
37
  }
84
38
  }
85
39
 
86
- importFuncs = globalThis.importFuncs = [...imports.values()];
87
- importDelta = importedFuncs.length - importFuncs.length;
40
+ globalThis.importDelta = importedFuncs.length - importFuncs.length;
41
+ }
42
+
43
+ if (Prefs.optLog) log('assemble', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
44
+ time('import treeshake');
45
+
46
+ // todo: this will just break if it is too small
47
+ let bufferSize = 1024 * 1024; // 1MB
48
+ let buffer = new Uint8Array(bufferSize);
49
+ let offset = 0;
50
+
51
+ const ensureBufferSize = added => {
52
+ if (offset + added >= bufferSize - 64) {
53
+ const newBuffer = new Uint8Array(bufferSize *= 2);
54
+ newBuffer.set(buffer);
55
+ buffer = null; // help gc
56
+ buffer = newBuffer;
57
+ }
58
+ };
59
+
60
+ const byte = byte => {
61
+ ensureBufferSize(1);
62
+ buffer[offset++] = byte;
63
+ };
64
+
65
+ const array = array => {
66
+ ensureBufferSize(array.length);
67
+ buffer.set(array, offset);
68
+ offset += array.length;
69
+ };
70
+
71
+ const unsigned = n => {
72
+ n |= 0;
73
+ if (n >= 0 && n <= 127) return byte(n);
74
+
75
+ do {
76
+ let x = n & 0x7f;
77
+ n >>>= 7;
78
+ if (n !== 0) {
79
+ x |= 0x80;
80
+ }
81
+
82
+ byte(x);
83
+ } while (n !== 0);
84
+ };
85
+
86
+ const signed = n => {
87
+ n |= 0;
88
+ if (n >= 0 && n <= 63) return byte(n);
89
+
90
+ while (true) {
91
+ let x = n & 0x7f;
92
+ n >>= 7;
93
+
94
+ if ((n === 0 && (x & 0x40) === 0) || (n === -1 && (x & 0x40) !== 0)) {
95
+ byte(x);
96
+ break;
97
+ } else {
98
+ x |= 0x80;
99
+ }
100
+
101
+ byte(x);
102
+ }
103
+ };
104
+
105
+ const ieee754 = n => {
106
+ ensureBufferSize(8);
107
+ array(new Uint8Array(new Float64Array([ n ]).buffer));
108
+ };
109
+
110
+ const string = str => {
111
+ unsigned(str.length);
112
+ for (let i = 0; i < str.length; i++) {
113
+ byte(str.charCodeAt(i));
114
+ }
115
+ };
116
+
117
+ const section = (id, bytes) => {
118
+ byte(id);
119
+ unsigned(bytes);
120
+ };
121
+
122
+ const unsignedPost = () => {
123
+ const o = offset;
124
+ offset += 5;
125
+ return n => {
126
+ const o2 = offset;
127
+ offset = o;
128
+ unsigned(n);
129
+
130
+ buffer.set(buffer.subarray(o + 5, o2), offset);
131
+ offset = o2 - (5 - (offset - o));
132
+ };
133
+ };
134
+
135
+ array(Magic, Magic.length);
136
+ time('setup');
137
+
138
+ const types = [], typeCache = new Map();
139
+ const getType = (params, returns) => {
140
+ const hash = `${params.join()}_${returns.join()}`;
141
+ if (typeCache.has(hash)) return typeCache.get(hash);
142
+
143
+ const type = [ FuncType, params.length, ...params, returns.length, ...returns ];
144
+ const idx = types.length;
145
+
146
+ types.push(type);
147
+ typeCache.set(hash, idx);
148
+ return idx;
149
+ };
150
+
151
+ // precache all types to be used
152
+ for (let i = 0; i < funcs.length; i++) {
153
+ const func = funcs[i];
154
+ getType(func.params, func.returns);
88
155
  }
89
156
 
90
- for (const f of funcs) {
91
- f.asmIndex = f.index - importDelta;
157
+ for (let i = 0; i < importFuncs.length; i++) {
158
+ const func = importFuncs[i];
159
+ getType(func.params, func.returns);
92
160
  }
93
161
 
94
- if (Prefs.optLog) log('assemble', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
162
+ for (let i = 0; i < tags.length; i++) {
163
+ const tag = tags[i];
164
+ getType(tag.params, tag.results);
165
+ }
95
166
 
96
- const importSection = importFuncs.length === 0 ? [] : createSection(
97
- Section.import,
98
- encodeVector(importFuncs.map(x => {
99
- return [
100
- 0, 1, x.import.charCodeAt(0),
101
- ExportDesc.func,
102
- getType(x.params, x.returns)
103
- ];
104
- }))
105
- );
167
+ section(Section.type, unsignedLEB128_length(types.length) + types.reduce((acc, x) => acc + x.length, 0));
168
+ unsigned(types.length);
169
+ for (let i = 0; i < types.length; i++) {
170
+ array(types[i]);
171
+ }
172
+ time('type section');
173
+
174
+ if (importFuncs.length > 0) {
175
+ section(Section.import, unsignedLEB128_length(importFuncs.length) + importFuncs.length * 5);
176
+ unsigned(importFuncs.length);
177
+ for (let i = 0; i < importFuncs.length; i++) {
178
+ const x = importFuncs[i];
179
+ byte(0); byte(1);
180
+ byte(x.import.charCodeAt(0));
181
+ byte(ExportDesc.func);
182
+ byte(getType(x.params, x.returns));
183
+ }
184
+ }
106
185
  time('import section');
107
186
 
108
- const funcSection = createSection(
109
- Section.func,
110
- encodeVector(funcs.map(x => getType(x.params, x.returns))) // type indexes
111
- );
112
- time('func section');
187
+ const indirectFuncs = [], exportFuncs = [];
113
188
 
114
- const nameSection = Prefs.d ? customSection('name', encodeNames(funcs)) : [];
115
-
116
- const indirectFuncs = funcs.filter(x => x.indirect);
117
- const tableSection = !funcs.table ? [] : createSection(
118
- Section.table,
119
- encodeVector([ [ Reftype.funcref, 0x00, ...unsignedLEB128(indirectFuncs.length) ] ])
120
- );
121
- time('table section');
122
-
123
- const elementSection = !funcs.table ? [] : createSection(
124
- Section.element,
125
- encodeVector([ [
126
- 0x00,
127
- Opcodes.i32_const, 0, Opcodes.end,
128
- ...encodeVector(indirectFuncs.map(x => unsignedLEB128(x.asmIndex)))
129
- ] ])
130
- );
131
- time('element section');
189
+ section(Section.func, unsignedLEB128_length(funcs.length) + funcs.length);
190
+ unsigned(funcs.length);
191
+ for (let i = 0; i < funcs.length; i++) {
192
+ const x = funcs[i];
193
+ byte(getType(x.params, x.returns));
194
+
195
+ if (x.indirect) indirectFuncs.push(x);
196
+ if (x.export) exportFuncs.push(x);
197
+ }
198
+ time('func section');
132
199
 
133
200
  if (pages.has('func lut')) {
134
201
  if (data.addedFuncArgcLut) {
@@ -182,285 +249,276 @@ export default (funcs, globals, tags, pages, data, noTreeshake = false) => {
182
249
  }
183
250
  time('func lut');
184
251
 
185
- // specially optimized assembly for globals as this version is much (>5x) faster than traditional createSection()
186
- const globalsValues = Object.values(globals);
252
+ if (funcs.table) {
253
+ section(Section.table, unsignedLEB128_length(indirectFuncs.length) + 3);
254
+ byte(1); // table count
255
+ byte(Reftype.funcref); byte(0); // table type
256
+ unsigned(indirectFuncs.length); // table size
257
+ time('table section');
258
+ }
187
259
 
188
- let globalSection = [];
260
+ if (Prefs.alwaysMemory && pages.size === 0) pages.set('--always-memory', 0);
261
+ const usesMemory = pages.size > 0;
262
+ if (usesMemory) {
263
+ const pageCount = Math.ceil((pages.size * pageSize) / PageSize);
264
+ section(Section.memory, unsignedLEB128_length(pageCount) + 2);
265
+ byte(1); // memory count
266
+ byte(0); // memory type
267
+ unsigned(pageCount); // memory size
268
+ time('memory section');
269
+ }
270
+
271
+ const usesTags = tags.length > 0;
272
+ if (usesTags) {
273
+ section(Section.tag, unsignedLEB128_length(tags.length) + tags.length * 2);
274
+ unsigned(tags.length);
275
+ for (let i = 0; i < tags.length; i++) {
276
+ const x = tags[i];
277
+ byte(0); // tag type
278
+ byte(getType(x.params, x.results)); // tag signature
279
+ }
280
+ time('tag section');
281
+ }
282
+
283
+ const globalsValues = Object.values(globals);
189
284
  if (globalsValues.length > 0) {
190
- let data = unsignedLEB128(globalsValues.length);
285
+ section(Section.global, unsignedLEB128_length(globalsValues.length) + globalsValues.length * 4
286
+ + globalsValues.reduce((acc, x) => acc + (x.type === Valtype.f64 ? 8 : signedLEB128_length(x.init ?? 0)), 0));
287
+ unsigned(globalsValues.length);
191
288
  for (let i = 0; i < globalsValues.length; i++) {
192
- const global = globalsValues[i];
193
-
194
- switch (global.type) {
289
+ const x = globalsValues[i];
290
+ switch (x.type) {
195
291
  case Valtype.i32:
196
- if (i > 0) data.push(Opcodes.end, Valtype.i32, 0x01, Opcodes.i32_const);
197
- else data.push(Valtype.i32, 0x01, Opcodes.i32_const);
198
-
199
- signedLEB128_into(global.init ?? 0, data);
292
+ byte(Valtype.i32);
293
+ byte(0x01);
294
+ byte(Opcodes.i32_const);
295
+ signed(x.init ?? 0);
200
296
  break;
201
297
 
202
298
  case Valtype.i64:
203
- if (i > 0) data.push(Opcodes.end, Valtype.i64, 0x01, Opcodes.i64_const);
204
- else data.push(Valtype.i64, 0x01, Opcodes.i64_const);
205
-
206
- signedLEB128_into(global.init ?? 0, data);
299
+ byte(Valtype.i64);
300
+ byte(0x01);
301
+ byte(Opcodes.i64_const);
302
+ signed(x.init ?? 0);
207
303
  break;
208
304
 
209
305
  case Valtype.f64:
210
- if (i > 0) data.push(Opcodes.end, Valtype.f64, 0x01, Opcodes.f64_const);
211
- else data.push(Valtype.f64, 0x01, Opcodes.f64_const);
212
-
213
- ieee754_binary64_into(global.init ?? 0, data);
306
+ byte(Valtype.f64);
307
+ byte(0x01);
308
+ byte(Opcodes.f64_const);
309
+ ieee754(x.init ?? 0);
214
310
  break;
215
311
  }
312
+
313
+ byte(Opcodes.end);
216
314
  }
315
+ time('global section');
316
+ }
217
317
 
218
- data.push(Opcodes.end);
318
+ if (exportFuncs.length > 0 || usesMemory || usesTags) {
319
+ byte(Section.export);
320
+ const sizeOffset = offset, setSize = unsignedPost();
219
321
 
220
- globalSection.push(Section.global);
322
+ unsigned(exportFuncs.length + usesMemory + usesTags);
323
+ if (usesMemory) {
324
+ string('$');
325
+ byte(ExportDesc.mem); byte(0);
326
+ }
327
+ if (usesTags) {
328
+ string('0');
329
+ byte(ExportDesc.tag); byte(0);
330
+ }
221
331
 
222
- unsignedLEB128_into(data.length, globalSection);
223
- globalSection = globalSection.concat(data);
332
+ for (let i = 0; i < exportFuncs.length; i++) {
333
+ const x = exportFuncs[i];
334
+ string(x.name === '#main' ? 'm' : x.name);
335
+ byte(ExportDesc.func);
336
+ unsigned(x.index - importDelta);
337
+ }
338
+
339
+ setSize(offset - sizeOffset - 5);
340
+ time('export section');
224
341
  }
225
- time('global section');
226
342
 
227
- if (Prefs.alwaysMemory && pages.size === 0) pages.set('--always-memory', 0);
343
+ if (funcs.table) {
344
+ section(Section.element, unsignedLEB128_length(indirectFuncs.length) + 5 + indirectFuncs.reduce((acc, x) => acc + unsignedLEB128_length(x.index - importDelta), 0));
345
+ byte(1); // table index
346
+ byte(0); // element type
347
+ byte(Opcodes.i32_const); byte(0); byte(Opcodes.end); // offset
228
348
 
229
- const usesMemory = pages.size > 0;
230
- const memorySection = !usesMemory ? [] : createSection(
231
- Section.memory,
232
- encodeVector([ [ 0x00, ...unsignedLEB128(Math.ceil((pages.size * pageSize) / PageSize)) ] ])
233
- );
234
- time('memory section');
235
-
236
- const exports = funcs.filter(x => x.export).map((x, i) => [ ...encodeString(x.name === '#main' ? 'm' : x.name), ExportDesc.func, ...unsignedLEB128(x.asmIndex) ]);
237
-
238
- // export memory if used
239
- if (usesMemory) exports.unshift([ ...encodeString('$'), ExportDesc.mem, 0x00 ]);
240
- time('gen exports');
241
-
242
- const tagSection = tags.length === 0 ? [] : createSection(
243
- Section.tag,
244
- encodeVector(tags.map(x => [ 0x00, getType(x.params, x.results) ]))
245
- );
246
-
247
- // export first tag if used
248
- if (tags.length !== 0) exports.unshift([ ...encodeString('0'), ExportDesc.tag, 0x00 ]);
249
- time('tag section');
250
-
251
- const exportSection = createSection(
252
- Section.export,
253
- encodeVector(exports)
254
- );
255
- time('export section');
256
-
257
- let codeSection = [];
349
+ unsigned(indirectFuncs.length);
350
+ for (let i = 0; i < indirectFuncs.length; i++) {
351
+ const x = indirectFuncs[i];
352
+ unsigned(x.index - importDelta);
353
+ }
354
+ time('element section');
355
+ }
356
+
357
+ if (data.length > 0) {
358
+ section(Section.data_count, unsignedLEB128_length(data.length));
359
+ unsigned(data.length);
360
+ time('data count section');
361
+ }
362
+
363
+ byte(Section.code);
364
+ const codeSectionSizeOffset = offset, setCodeSectionSize = unsignedPost();
365
+
366
+ unsigned(funcs.length);
258
367
  for (let i = 0; i < funcs.length; i++) {
368
+ const funcSizeOffset = offset, setFuncSize = unsignedPost();
369
+
259
370
  const x = funcs[i];
260
371
  const locals = Object.values(x.locals).sort((a, b) => a.idx - b.idx);
261
372
 
262
373
  const paramCount = x.params.length;
263
- let localDecl = [], typeCount = 0, lastType, declCount = 0;
374
+ let declCount = 0, lastType, typeCount = 0;
264
375
  for (let i = paramCount; i <= locals.length; i++) {
265
376
  const local = locals[i];
266
- if (i !== paramCount && local?.type !== lastType) {
267
- unsignedLEB128_into(typeCount, localDecl);
268
- localDecl.push(lastType);
269
- typeCount = 0;
377
+ if (lastType && local?.type !== lastType) {
270
378
  declCount++;
271
379
  }
272
380
 
273
- typeCount++;
274
381
  lastType = local?.type;
275
382
  }
383
+ unsigned(declCount);
276
384
 
277
- let wasm = [], wasmNonFlat = [];
278
- if (Prefs.d) {
279
- for (let i = 0; i < x.wasm.length; i++) {
280
- let o = x.wasm[i];
281
-
282
- // encode local/global ops as unsigned leb128 from raw number
283
- if (
284
- (o[0] >= Opcodes.local_get && o[0] <= Opcodes.global_set) &&
285
- o[1] > 127
286
- ) {
287
- const n = o[1];
288
- o = [ o[0] ];
289
- unsignedLEB128_into(n, o);
290
- }
291
-
292
- // encode f64.const ops as ieee754 from raw number
293
- if (o[0] === Opcodes.f64_const) {
294
- const n = o[1];
295
- o = ieee754_binary64(n);
296
- if (o.length === 8) o.unshift(Opcodes.f64_const);
297
- }
298
-
299
- // encode call ops as unsigned leb128 from raw number
300
- if ((o[0] === Opcodes.call /* || o[0] === Opcodes.return_call */) && o[1] >= importedFuncs.length) {
301
- const n = o[1] - importDelta;
302
- o = [ Opcodes.call ];
303
- unsignedLEB128_into(n, o);
304
- }
305
-
306
- // encode call indirect ops as types from info
307
- if (o[0] === Opcodes.call_indirect) {
308
- o = [...o];
309
- const params = [];
310
- for (let i = 0; i < o[1]; i++) {
311
- params.push(valtypeBinary, Valtype.i32);
312
- }
313
-
314
- let returns = [ valtypeBinary, Valtype.i32 ];
315
- if (o.at(-1) === 'no_type_return') {
316
- o.pop();
317
- returns = [ valtypeBinary ];
318
- }
319
-
320
- o[1] = getType(params, returns);
321
- }
385
+ lastType = undefined;
386
+ for (let i = paramCount; i <= locals.length; i++) {
387
+ const local = locals[i];
388
+ if (lastType && local?.type !== lastType) {
389
+ unsigned(typeCount);
390
+ byte(lastType);
391
+ typeCount = 0;
392
+ }
322
393
 
323
- for (let j = 0; j < o.length; j++) {
324
- const x = o[j];
325
- if (x == null || !(x <= 0xff)) continue;
326
- wasm.push(x);
327
- }
394
+ typeCount++;
395
+ lastType = local?.type;
396
+ }
328
397
 
329
- wasmNonFlat.push(o);
398
+ for (let i = 0; i < x.wasm.length; i++) {
399
+ let o = x.wasm[i];
400
+ const op = o[0];
401
+
402
+ // encode local/global ops as unsigned leb128 from raw number
403
+ if (
404
+ (op >= Opcodes.local_get && op <= Opcodes.global_set) &&
405
+ o[1] > 127
406
+ ) {
407
+ byte(op);
408
+ unsigned(o[1]);
409
+ continue;
330
410
  }
331
411
 
332
- x.assembled = { localDecl, wasm, wasmNonFlat };
333
- } else {
334
- for (let i = 0; i < x.wasm.length; i++) {
335
- let o = x.wasm[i];
336
- const op = o[0];
337
-
338
- // encode local/global ops as unsigned leb128 from raw number
339
- if (
340
- (op >= Opcodes.local_get && op <= Opcodes.global_set) &&
341
- o[1] > 127
342
- ) {
343
- wasm.push(op);
344
- unsignedLEB128_into(o[1], wasm);
345
- continue;
346
- }
412
+ // encode f64.const ops as ieee754 from raw number
413
+ if (op === Opcodes.f64_const) {
414
+ byte(op);
415
+ ieee754(o[1]);
416
+ continue;
417
+ }
347
418
 
348
- // encode f64.const ops as ieee754 from raw number
349
- if (op === Opcodes.f64_const) {
350
- wasm.push(op);
351
- ieee754_binary64_into(o[1], wasm);
352
- continue;
353
- }
419
+ // encode call ops as unsigned leb128 from raw number
420
+ if ((op === Opcodes.call /* || o[0] === Opcodes.return_call */) && o[1] >= importedFuncs.length) {
421
+ byte(op);
422
+ unsigned(o[1] - importDelta);
423
+ continue;
424
+ }
354
425
 
355
- // encode call ops as unsigned leb128 from raw number
356
- if ((op === Opcodes.call /* || o[0] === Opcodes.return_call */) && o[1] >= importedFuncs.length) {
357
- wasm.push(op);
358
- unsignedLEB128_into(o[1] - importDelta, wasm);
359
- continue;
426
+ // encode call indirect ops as types from info
427
+ if (op === Opcodes.call_indirect) {
428
+ const params = [];
429
+ for (let i = 0; i < o[1]; i++) {
430
+ params.push(valtypeBinary, Valtype.i32);
360
431
  }
361
432
 
362
- // encode call indirect ops as types from info
363
- if (op === Opcodes.call_indirect) {
364
- const params = [];
365
- for (let i = 0; i < o[1]; i++) {
366
- params.push(valtypeBinary, Valtype.i32);
367
- }
368
-
369
- let returns = [ valtypeBinary, Valtype.i32 ];
370
- if (o.at(-1) === 'no_type_return') {
371
- returns = [ valtypeBinary ];
372
- }
373
-
374
- wasm.push(op, getType(params, returns), o[2]);
375
- continue;
376
- }
433
+ byte(op);
434
+ byte(getType(params, [ valtypeBinary, Valtype.i32 ]));
435
+ byte(o[2]);
436
+ continue;
437
+ }
377
438
 
378
- for (let j = 0; j < o.length; j++) {
379
- const x = o[j];
380
- if (x == null || !(x <= 0xff)) continue;
381
- wasm.push(x);
382
- }
439
+ for (let j = 0; j < o.length; j++) {
440
+ const x = o[j];
441
+ if (x == null || !(x <= 0xff)) continue;
442
+ buffer[offset++] = x;
383
443
  }
384
444
  }
385
445
 
386
- if (wasm.length > 100000) {
387
- // slow path for handling large arrays which break v8 due to running out of stack size
388
- const out = unsignedLEB128(declCount)
389
- .concat(localDecl, wasm, Opcodes.end);
390
- codeSection = codeSection.concat(unsignedLEB128(out.length), out);
391
- } else {
392
- codeSection.push(
393
- ...unsignedLEB128(unsignedLEB128_length(declCount) + localDecl.length + wasm.length + 1),
394
- ...unsignedLEB128(declCount),
395
- ...localDecl,
396
- ...wasm,
397
- Opcodes.end
398
- );
446
+ byte(Opcodes.end);
447
+ setFuncSize(offset - funcSizeOffset - 5);
448
+ }
449
+
450
+ setCodeSectionSize(offset - codeSectionSizeOffset - 5);
451
+ time('code section');
452
+
453
+ section(Section.data, unsignedLEB128_length(data.length) + data.reduce((acc, x) =>
454
+ acc + (x.page != null ? (3 + signedLEB128_length(pages.allocs.get(x.page) ?? (pages.get(x.page) * pageSize))) : 1)
455
+ + unsignedLEB128_length(x.bytes.length) + x.bytes.length, 0));
456
+
457
+ unsigned(data.length);
458
+ for (let i = 0; i < data.length; i++) {
459
+ const x = data[i];
460
+ if (x.page != null) { // active
461
+ let offset = pages.allocs.get(x.page) ?? (pages.get(x.page) * pageSize);
462
+ if (offset === 0) offset = 16;
463
+
464
+ byte(0x00);
465
+ byte(Opcodes.i32_const);
466
+ signed(offset);
467
+ byte(Opcodes.end);
468
+ } else { // passive
469
+ byte(0x01);
399
470
  }
400
471
 
401
- // globalThis.progress?.(`${i}/${funcs.length}`);
472
+ unsigned(x.bytes.length);
473
+ array(x.bytes);
402
474
  }
475
+ time('data section');
403
476
 
404
- codeSection.unshift(...unsignedLEB128(funcs.length, codeSection));
405
- codeSection.unshift(Section.code, ...unsignedLEB128(codeSection.length));
406
- time('code section');
477
+ if (Prefs.d) {
478
+ byte(Section.custom);
479
+ const totalSizeOffset = offset, setTotalSize = unsignedPost();
480
+ string('name');
407
481
 
408
- const typeSection = createSection(
409
- Section.type,
410
- encodeVector(types)
411
- );
412
- time('type section');
482
+ const section = (id, cb) => {
483
+ byte(id);
484
+ const sizeOffset = offset, setSize = unsignedPost();
485
+ cb();
486
+ setSize(offset - sizeOffset - 5);
487
+ };
413
488
 
414
- let dataSection = [];
415
- if (data.length > 0) {
416
- for (let i = 0; i < data.length; i++) {
417
- const x = data[i];
418
- if (Prefs.d && x.bytes.length > PageSize) log.warning('assemble', `data (${x.page}) has more bytes than Wasm page size! (${x.bytes.length})`);
419
-
420
- if (x.page != null) {
421
- // type: active
422
- let offset = pages.allocs.get(x.page) ?? (pages.get(x.page) * pageSize);
423
- if (offset === 0) offset = 16;
424
- dataSection.push(0x00, Opcodes.i32_const, ...signedLEB128(offset), Opcodes.end);
425
- } else {
426
- // type: passive
427
- dataSection.push(0x01);
489
+ section(0, () => { // module
490
+ string('js'); // todo: filename?
491
+ });
492
+
493
+ section(1, () => { // funcs
494
+ unsigned(funcs.length);
495
+ for (let i = 0; i < funcs.length; i++) {
496
+ const x = funcs[i];
497
+ unsigned(x.index - importDelta);
498
+ string(x.name);
428
499
  }
500
+ });
429
501
 
430
- if (x.bytes.length > 100000) {
431
- codeSection = codeSection.concat(unsignedLEB128(x.bytes.length), x.bytes);
432
- } else {
433
- dataSection.push(
434
- ...unsignedLEB128(x.bytes.length),
435
- ...x.bytes
436
- );
502
+ section(2, () => { // locals
503
+ unsigned(funcs.length);
504
+ for (let i = 0; i < funcs.length; i++) {
505
+ const x = funcs[i];
506
+ unsigned(x.index - importDelta);
507
+
508
+ const locals = Object.keys(x.locals);
509
+ unsigned(locals.length);
510
+ for (let j = 0; j < locals.length; j++) {
511
+ const name = locals[j];
512
+ unsigned(x.locals[name]);
513
+ string(name);
514
+ }
437
515
  }
438
- }
516
+ });
439
517
 
440
- dataSection.unshift(...unsignedLEB128(data.length, dataSection));
441
- dataSection.unshift(Section.data, ...unsignedLEB128(dataSection.length));
518
+ setTotalSize(offset - totalSizeOffset - 5);
519
+ time('name section');
442
520
  }
443
521
 
444
- const dataCountSection = data.length === 0 ? [] : createSection(
445
- Section.data_count,
446
- unsignedLEB128(data.length)
447
- );
448
- time('data section');
449
-
450
- return Uint8Array.from([
451
- ...Magic,
452
- ...typeSection,
453
- ...importSection,
454
- ...funcSection,
455
- ...tableSection,
456
- ...memorySection,
457
- ...tagSection,
458
- ...globalSection,
459
- ...exportSection,
460
- ...elementSection,
461
- ...dataCountSection,
462
- ...codeSection,
463
- ...dataSection,
464
- ...nameSection
465
- ]);
522
+ buffer = buffer.subarray(0, offset);
523
+ return buffer;
466
524
  };