UNPKG

80.9 kBPlain TextView Raw
1/* Copyright 2016 Mozilla Foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15import {
16 BinaryReader,
17 BinaryReaderState,
18 bytesToString,
19 ElementMode,
20 ExternalKind,
21 IDataSegmentBody,
22 IElementSegment,
23 IElementSegmentBody,
24 IEventNameEntry,
25 IEventType,
26 IExportEntry,
27 IFieldNameEntry,
28 IFunctionEntry,
29 IFunctionInformation,
30 IFunctionNameEntry,
31 IGlobalNameEntry,
32 IGlobalType,
33 IGlobalVariable,
34 IImportEntry,
35 ILocalNameEntry,
36 IMemoryAddress,
37 IMemoryNameEntry,
38 IMemoryType,
39 INameEntry,
40 Int64,
41 IOperatorInformation,
42 IResizableLimits,
43 ISectionInformation,
44 IStartEntry,
45 ITableNameEntry,
46 ITableType,
47 ITypeEntry,
48 ITypeNameEntry,
49 NameType,
50 OperatorCode,
51 OperatorCodeNames,
52 SectionCode,
53 Type,
54 TypeKind,
55} from "./WasmParser.js";
56
57const NAME_SECTION_NAME = "name";
58const INVALID_NAME_SYMBOLS_REGEX = /[^0-9A-Za-z!#$%&'*+.:<=>?@^_`|~\/\-]/;
59const INVALID_NAME_SYMBOLS_REGEX_GLOBAL = new RegExp(
60 INVALID_NAME_SYMBOLS_REGEX.source,
61 "g"
62);
63
64function formatFloat32(n: number): string {
65 if (n === 0) return 1 / n < 0 ? "-0.0" : "0.0";
66 if (isFinite(n)) return n.toString();
67 if (!isNaN(n)) return n < 0 ? "-inf" : "inf";
68 var view = new DataView(new ArrayBuffer(8));
69 view.setFloat32(0, n, true);
70 var data = view.getInt32(0, true);
71 var payload = data & 0x7fffff;
72 const canonicalBits = 4194304; // 0x800..0
73 if (data > 0 && payload === canonicalBits) return "nan";
74 // canonical NaN;
75 else if (payload === canonicalBits) return "-nan";
76 return (data < 0 ? "-" : "+") + "nan:0x" + payload.toString(16);
77}
78
79function formatFloat64(n: number): string {
80 if (n === 0) return 1 / n < 0 ? "-0.0" : "0.0";
81 if (isFinite(n)) return n.toString();
82 if (!isNaN(n)) return n < 0 ? "-inf" : "inf";
83 var view = new DataView(new ArrayBuffer(8));
84 view.setFloat64(0, n, true);
85 var data1 = view.getUint32(0, true);
86 var data2 = view.getInt32(4, true);
87 var payload = data1 + (data2 & 0xfffff) * 4294967296;
88 const canonicalBits = 524288 * 4294967296; // 0x800..0
89 if (data2 > 0 && payload === canonicalBits) return "nan";
90 // canonical NaN;
91 else if (payload === canonicalBits) return "-nan";
92 return (data2 < 0 ? "-" : "+") + "nan:0x" + payload.toString(16);
93}
94
95function formatI32Array(bytes, count) {
96 var dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
97 var result = [];
98 for (var i = 0; i < count; i++)
99 result.push(`0x${formatHex(dv.getInt32(i << 2, true), 8)}`);
100 return result.join(" ");
101}
102
103function formatI8Array(bytes, count) {
104 var dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
105 var result = [];
106 for (var i = 0; i < count; i++) result.push(`${dv.getInt8(i)}`);
107 return result.join(" ");
108}
109
110function memoryAddressToString(
111 address: IMemoryAddress,
112 code: OperatorCode
113): string {
114 var defaultAlignFlags;
115 switch (code) {
116 case OperatorCode.v128_load:
117 case OperatorCode.i16x8_load8x8_s:
118 case OperatorCode.i16x8_load8x8_u:
119 case OperatorCode.i32x4_load16x4_s:
120 case OperatorCode.i32x4_load16x4_u:
121 case OperatorCode.i64x2_load32x2_s:
122 case OperatorCode.i64x2_load32x2_u:
123 case OperatorCode.v8x16_load_splat:
124 case OperatorCode.v16x8_load_splat:
125 case OperatorCode.v32x4_load_splat:
126 case OperatorCode.v64x2_load_splat:
127 case OperatorCode.v128_store:
128 defaultAlignFlags = 4;
129 break;
130 case OperatorCode.i64_load:
131 case OperatorCode.i64_store:
132 case OperatorCode.f64_load:
133 case OperatorCode.f64_store:
134 case OperatorCode.i64_atomic_wait:
135 case OperatorCode.i64_atomic_load:
136 case OperatorCode.i64_atomic_store:
137 case OperatorCode.i64_atomic_rmw_add:
138 case OperatorCode.i64_atomic_rmw_sub:
139 case OperatorCode.i64_atomic_rmw_and:
140 case OperatorCode.i64_atomic_rmw_or:
141 case OperatorCode.i64_atomic_rmw_xor:
142 case OperatorCode.i64_atomic_rmw_xchg:
143 case OperatorCode.i64_atomic_rmw_cmpxchg:
144 case OperatorCode.v128_load64_zero:
145 defaultAlignFlags = 3;
146 break;
147 case OperatorCode.i32_load:
148 case OperatorCode.i64_load32_s:
149 case OperatorCode.i64_load32_u:
150 case OperatorCode.i32_store:
151 case OperatorCode.i64_store32:
152 case OperatorCode.f32_load:
153 case OperatorCode.f32_store:
154 case OperatorCode.atomic_notify:
155 case OperatorCode.i32_atomic_wait:
156 case OperatorCode.i32_atomic_load:
157 case OperatorCode.i64_atomic_load32_u:
158 case OperatorCode.i32_atomic_store:
159 case OperatorCode.i64_atomic_store32:
160 case OperatorCode.i32_atomic_rmw_add:
161 case OperatorCode.i64_atomic_rmw32_add_u:
162 case OperatorCode.i32_atomic_rmw_sub:
163 case OperatorCode.i64_atomic_rmw32_sub_u:
164 case OperatorCode.i32_atomic_rmw_and:
165 case OperatorCode.i64_atomic_rmw32_and_u:
166 case OperatorCode.i32_atomic_rmw_or:
167 case OperatorCode.i64_atomic_rmw32_or_u:
168 case OperatorCode.i32_atomic_rmw_xor:
169 case OperatorCode.i64_atomic_rmw32_xor_u:
170 case OperatorCode.i32_atomic_rmw_xchg:
171 case OperatorCode.i64_atomic_rmw32_xchg_u:
172 case OperatorCode.i32_atomic_rmw_cmpxchg:
173 case OperatorCode.i64_atomic_rmw32_cmpxchg_u:
174 case OperatorCode.v128_load32_zero:
175 defaultAlignFlags = 2;
176 break;
177 case OperatorCode.i32_load16_s:
178 case OperatorCode.i32_load16_u:
179 case OperatorCode.i64_load16_s:
180 case OperatorCode.i64_load16_u:
181 case OperatorCode.i32_store16:
182 case OperatorCode.i64_store16:
183 case OperatorCode.i32_atomic_load16_u:
184 case OperatorCode.i64_atomic_load16_u:
185 case OperatorCode.i32_atomic_store16:
186 case OperatorCode.i64_atomic_store16:
187 case OperatorCode.i32_atomic_rmw16_add_u:
188 case OperatorCode.i64_atomic_rmw16_add_u:
189 case OperatorCode.i32_atomic_rmw16_sub_u:
190 case OperatorCode.i64_atomic_rmw16_sub_u:
191 case OperatorCode.i32_atomic_rmw16_and_u:
192 case OperatorCode.i64_atomic_rmw16_and_u:
193 case OperatorCode.i32_atomic_rmw16_or_u:
194 case OperatorCode.i64_atomic_rmw16_or_u:
195 case OperatorCode.i32_atomic_rmw16_xor_u:
196 case OperatorCode.i64_atomic_rmw16_xor_u:
197 case OperatorCode.i32_atomic_rmw16_xchg_u:
198 case OperatorCode.i64_atomic_rmw16_xchg_u:
199 case OperatorCode.i32_atomic_rmw16_cmpxchg_u:
200 case OperatorCode.i64_atomic_rmw16_cmpxchg_u:
201 defaultAlignFlags = 1;
202 break;
203 case OperatorCode.i32_load8_s:
204 case OperatorCode.i32_load8_u:
205 case OperatorCode.i64_load8_s:
206 case OperatorCode.i64_load8_u:
207 case OperatorCode.i32_store8:
208 case OperatorCode.i64_store8:
209 case OperatorCode.i32_atomic_load8_u:
210 case OperatorCode.i64_atomic_load8_u:
211 case OperatorCode.i32_atomic_store8:
212 case OperatorCode.i64_atomic_store8:
213 case OperatorCode.i32_atomic_rmw8_add_u:
214 case OperatorCode.i64_atomic_rmw8_add_u:
215 case OperatorCode.i32_atomic_rmw8_sub_u:
216 case OperatorCode.i64_atomic_rmw8_sub_u:
217 case OperatorCode.i32_atomic_rmw8_and_u:
218 case OperatorCode.i64_atomic_rmw8_and_u:
219 case OperatorCode.i32_atomic_rmw8_or_u:
220 case OperatorCode.i64_atomic_rmw8_or_u:
221 case OperatorCode.i32_atomic_rmw8_xor_u:
222 case OperatorCode.i64_atomic_rmw8_xor_u:
223 case OperatorCode.i32_atomic_rmw8_xchg_u:
224 case OperatorCode.i64_atomic_rmw8_xchg_u:
225 case OperatorCode.i32_atomic_rmw8_cmpxchg_u:
226 case OperatorCode.i64_atomic_rmw8_cmpxchg_u:
227 defaultAlignFlags = 0;
228 break;
229 }
230 if (address.flags == defaultAlignFlags)
231 // hide default flags
232 return !address.offset ? null : `offset=${address.offset}`;
233 if (!address.offset)
234 // hide default offset
235 return `align=${1 << address.flags}`;
236 return `offset=${address.offset | 0} align=${1 << address.flags}`;
237}
238function limitsToString(limits: IResizableLimits): string {
239 return (
240 limits.initial + (limits.maximum !== undefined ? " " + limits.maximum : "")
241 );
242}
243var paddingCache = ["0", "00", "000"];
244function formatHex(n: number, width?: number): string {
245 var s = (n >>> 0).toString(16).toUpperCase();
246 if (width === undefined || s.length >= width) return s;
247 var paddingIndex = width - s.length - 1;
248 while (paddingIndex >= paddingCache.length)
249 paddingCache.push(paddingCache[paddingCache.length - 1] + "0");
250 return paddingCache[paddingIndex] + s;
251}
252const IndentIncrement = " ";
253
254function isValidName(name: string) {
255 return !INVALID_NAME_SYMBOLS_REGEX.test(name);
256}
257
258export interface IExportMetadata {
259 getFunctionExportNames(index: number): string[];
260 getGlobalExportNames(index: number): string[];
261 getMemoryExportNames(index: number): string[];
262 getTableExportNames(index: number): string[];
263 getEventExportNames(index: number): string[];
264}
265
266export interface INameResolver {
267 getTypeName(index: number, isRef: boolean): string;
268 getTableName(index: number, isRef: boolean): string;
269 getMemoryName(index: number, isRef: boolean): string;
270 getGlobalName(index: number, isRef: boolean): string;
271 getElementName(index: number, isRef: boolean): string;
272 getEventName(index: number, isRef: boolean): string;
273 getFunctionName(index: number, isImport: boolean, isRef: boolean): string;
274 getVariableName(funcIndex: number, index: number, isRef: boolean): string;
275 getFieldName(typeIndex: number, index: number, isRef: boolean): string;
276 getLabel(index: number): string;
277}
278export class DefaultNameResolver implements INameResolver {
279 public getTypeName(index: number, isRef: boolean): string {
280 return "$type" + index;
281 }
282 public getTableName(index: number, isRef: boolean): string {
283 return "$table" + index;
284 }
285 public getMemoryName(index: number, isRef: boolean): string {
286 return "$memory" + index;
287 }
288 public getGlobalName(index: number, isRef: boolean): string {
289 return "$global" + index;
290 }
291 public getElementName(index: number, isRef: boolean): string {
292 return `$elem${index}`;
293 }
294 public getEventName(index: number, isRef: boolean): string {
295 return `$event${index}`;
296 }
297 public getFunctionName(
298 index: number,
299 isImport: boolean,
300 isRef: boolean
301 ): string {
302 return (isImport ? "$import" : "$func") + index;
303 }
304 public getVariableName(
305 funcIndex: number,
306 index: number,
307 isRef: boolean
308 ): string {
309 return "$var" + index;
310 }
311 public getFieldName(
312 typeIndex: number,
313 index: number,
314 isRef: boolean
315 ): string {
316 return "$field" + index;
317 }
318 public getLabel(index: number): string {
319 return "$label" + index;
320 }
321}
322
323const EMPTY_STRING_ARRAY: string[] = [];
324
325class DevToolsExportMetadata implements IExportMetadata {
326 private readonly _functionExportNames: string[][];
327 private readonly _globalExportNames: string[][];
328 private readonly _memoryExportNames: string[][];
329 private readonly _tableExportNames: string[][];
330 private readonly _eventExportNames: string[][];
331
332 constructor(
333 functionExportNames: string[][],
334 globalExportNames: string[][],
335 memoryExportNames: string[][],
336 tableExportNames: string[][],
337 eventExportNames: string[][]
338 ) {
339 this._functionExportNames = functionExportNames;
340 this._globalExportNames = globalExportNames;
341 this._memoryExportNames = memoryExportNames;
342 this._tableExportNames = tableExportNames;
343 this._eventExportNames = eventExportNames;
344 }
345
346 public getFunctionExportNames(index: number) {
347 return this._functionExportNames[index] ?? EMPTY_STRING_ARRAY;
348 }
349
350 public getGlobalExportNames(index: number) {
351 return this._globalExportNames[index] ?? EMPTY_STRING_ARRAY;
352 }
353
354 public getMemoryExportNames(index: number) {
355 return this._memoryExportNames[index] ?? EMPTY_STRING_ARRAY;
356 }
357
358 public getTableExportNames(index: number) {
359 return this._tableExportNames[index] ?? EMPTY_STRING_ARRAY;
360 }
361
362 public getEventExportNames(index: number) {
363 return this._eventExportNames[index] ?? EMPTY_STRING_ARRAY;
364 }
365}
366
367export class NumericNameResolver implements INameResolver {
368 public getTypeName(index: number, isRef: boolean): string {
369 return isRef ? "" + index : `(;${index};)`;
370 }
371 public getTableName(index: number, isRef: boolean): string {
372 return isRef ? "" + index : `(;${index};)`;
373 }
374 public getMemoryName(index: number, isRef: boolean): string {
375 return isRef ? "" + index : `(;${index};)`;
376 }
377 public getGlobalName(index: number, isRef: boolean): string {
378 return isRef ? "" + index : `(;${index};)`;
379 }
380 public getElementName(index: number, isRef: boolean): string {
381 return isRef ? "" + index : `(;${index};)`;
382 }
383 public getEventName(index: number, isRef: boolean): string {
384 return isRef ? "" + index : `(;${index};)`;
385 }
386 public getFunctionName(
387 index: number,
388 isImport: boolean,
389 isRef: boolean
390 ): string {
391 return isRef ? "" + index : `(;${index};)`;
392 }
393 public getVariableName(
394 funcIndex: number,
395 index: number,
396 isRef: boolean
397 ): string {
398 return isRef ? "" + index : `(;${index};)`;
399 }
400 public getFieldName(
401 typeIndex: number,
402 index: number,
403 isRef: boolean
404 ): string {
405 return isRef ? "" : index + `(;${index};)`;
406 }
407 public getLabel(index: number): string {
408 return null;
409 }
410}
411
412export enum LabelMode {
413 Depth,
414 WhenUsed,
415 Always,
416}
417
418// The breakable range is [start, end).
419export interface IFunctionBodyOffset {
420 start: number;
421 end: number;
422}
423
424export interface IDisassemblerResult {
425 lines: Array<string>;
426 offsets?: Array<number>;
427 done: boolean;
428 functionBodyOffsets?: Array<IFunctionBodyOffset>;
429}
430
431export class WasmDisassembler {
432 private _lines: Array<string>;
433 private _offsets: Array<number>;
434 private _buffer: string;
435 private _types: Array<ITypeEntry>;
436 private _funcIndex: number;
437 private _funcTypes: Array<number>;
438 private _importCount: number;
439 private _globalCount: number;
440 private _memoryCount: number;
441 private _eventCount: number;
442 private _tableCount: number;
443 private _elementCount: number;
444 private _expression: Array<IOperatorInformation>;
445 private _backrefLabels: Array<{
446 line: number;
447 position: number;
448 useLabel: boolean;
449 label: string;
450 }>;
451 private _labelIndex: number;
452 private _indent: string;
453 private _indentLevel: number;
454 private _addOffsets: boolean;
455 private _skipTypes = true;
456 private _done: boolean;
457 private _currentPosition: number;
458 private _nameResolver: INameResolver;
459 private _exportMetadata: IExportMetadata = null;
460 private _labelMode: LabelMode;
461 private _functionBodyOffsets: Array<IFunctionBodyOffset>;
462 private _currentFunctionBodyOffset: number;
463 private _currentSectionId: SectionCode;
464 private _logFirstInstruction: boolean;
465 constructor() {
466 this._lines = [];
467 this._offsets = [];
468 this._buffer = "";
469 this._indent = null;
470 this._indentLevel = 0;
471 this._addOffsets = false;
472 this._done = false;
473 this._currentPosition = 0;
474 this._nameResolver = new DefaultNameResolver();
475 this._labelMode = LabelMode.WhenUsed;
476 this._functionBodyOffsets = [];
477 this._currentFunctionBodyOffset = 0;
478 this._currentSectionId = SectionCode.Unknown;
479 this._logFirstInstruction = false;
480
481 this._reset();
482 }
483 private _reset(): void {
484 this._types = [];
485 this._funcIndex = 0;
486 this._funcTypes = [];
487 this._importCount = 0;
488 this._globalCount = 0;
489 this._memoryCount = 0;
490 this._eventCount = 0;
491 this._tableCount = 0;
492 this._elementCount = 0;
493 this._expression = [];
494 this._backrefLabels = null;
495 this._labelIndex = 0;
496 }
497 public get addOffsets(): boolean {
498 return this._addOffsets;
499 }
500 public set addOffsets(value: boolean) {
501 if (this._currentPosition)
502 throw new Error("Cannot switch addOffsets during processing.");
503 this._addOffsets = value;
504 }
505 public get skipTypes(): boolean {
506 return this._skipTypes;
507 }
508 public set skipTypes(skipTypes: boolean) {
509 if (this._currentPosition)
510 throw new Error("Cannot switch skipTypes during processing.");
511 this._skipTypes = skipTypes;
512 }
513 public get labelMode(): LabelMode {
514 return this._labelMode;
515 }
516 public set labelMode(value: LabelMode) {
517 if (this._currentPosition)
518 throw new Error("Cannot switch labelMode during processing.");
519 this._labelMode = value;
520 }
521 public get exportMetadata(): IExportMetadata {
522 return this._exportMetadata;
523 }
524 public set exportMetadata(exportMetadata: IExportMetadata) {
525 if (this._currentPosition)
526 throw new Error("Cannot switch exportMetadata during processing.");
527 this._exportMetadata = exportMetadata;
528 }
529 public get nameResolver(): INameResolver {
530 return this._nameResolver;
531 }
532 public set nameResolver(resolver: INameResolver) {
533 if (this._currentPosition)
534 throw new Error("Cannot switch nameResolver during processing.");
535 this._nameResolver = resolver;
536 }
537 private appendBuffer(s: string) {
538 this._buffer += s;
539 }
540 private newLine() {
541 if (this.addOffsets) this._offsets.push(this._currentPosition);
542 this._lines.push(this._buffer);
543 this._buffer = "";
544 }
545 private logStartOfFunctionBodyOffset() {
546 if (this.addOffsets) {
547 this._currentFunctionBodyOffset = this._currentPosition;
548 }
549 }
550 private logEndOfFunctionBodyOffset() {
551 if (this.addOffsets) {
552 this._functionBodyOffsets.push({
553 start: this._currentFunctionBodyOffset,
554 end: this._currentPosition,
555 });
556 }
557 }
558 private typeIndexToString(typeIndex: number): string {
559 if (typeIndex >= 0) return this._nameResolver.getTypeName(typeIndex, true);
560 switch (typeIndex) {
561 case TypeKind.funcref:
562 return "func";
563 case TypeKind.externref:
564 return "extern";
565 case TypeKind.anyref:
566 return "any";
567 case TypeKind.eqref:
568 return "eq";
569 case TypeKind.i31ref:
570 return "i31";
571 case TypeKind.dataref:
572 return "data";
573 }
574 }
575 private typeToString(type: Type): string {
576 switch (type.kind) {
577 case TypeKind.i32:
578 return "i32";
579 case TypeKind.i64:
580 return "i64";
581 case TypeKind.f32:
582 return "f32";
583 case TypeKind.f64:
584 return "f64";
585 case TypeKind.v128:
586 return "v128";
587 case TypeKind.i8:
588 return "i8";
589 case TypeKind.i16:
590 return "i16";
591 case TypeKind.funcref:
592 return "funcref";
593 case TypeKind.externref:
594 return "externref";
595 case TypeKind.anyref:
596 return "anyref";
597 case TypeKind.eqref:
598 return "eqref";
599 case TypeKind.i31ref:
600 return "i31ref";
601 case TypeKind.dataref:
602 return "dataref";
603 case TypeKind.ref:
604 return `(ref ${this.typeIndexToString(type.index)})`;
605 case TypeKind.optref:
606 return `(ref null ${this.typeIndexToString(type.index)})`;
607 case TypeKind.rtt:
608 return `(rtt ${this.typeIndexToString(type.index)})`;
609 case TypeKind.rtt_d:
610 return `(rtt ${type.depth} ${this.typeIndexToString(type.index)})`;
611 default:
612 throw new Error(`Unexpected type ${JSON.stringify(type)}`);
613 }
614 }
615 private maybeMut(type: string, mutability: boolean): string {
616 return mutability ? `(mut ${type})` : type;
617 }
618 private globalTypeToString(type: IGlobalType): string {
619 const typeStr = this.typeToString(type.contentType);
620 return this.maybeMut(typeStr, !!type.mutability);
621 }
622 private printFuncType(typeIndex: number): void {
623 var type = this._types[typeIndex];
624 if (type.params.length > 0) {
625 this.appendBuffer(" (param");
626 for (var i = 0; i < type.params.length; i++) {
627 this.appendBuffer(" ");
628 this.appendBuffer(this.typeToString(type.params[i]));
629 }
630 this.appendBuffer(")");
631 }
632 if (type.returns.length > 0) {
633 this.appendBuffer(" (result");
634 for (var i = 0; i < type.returns.length; i++) {
635 this.appendBuffer(" ");
636 this.appendBuffer(this.typeToString(type.returns[i]));
637 }
638 this.appendBuffer(")");
639 }
640 }
641 private printStructType(typeIndex: number): void {
642 var type = this._types[typeIndex];
643 if (type.fields.length === 0) return;
644 for (var i = 0; i < type.fields.length; i++) {
645 const fieldType = this.maybeMut(
646 this.typeToString(type.fields[i]),
647 type.mutabilities[i]
648 );
649 const fieldName = this._nameResolver.getFieldName(typeIndex, i, false);
650 this.appendBuffer(` (field ${fieldName} ${fieldType})`);
651 }
652 }
653 private printArrayType(typeIndex: number): void {
654 var type = this._types[typeIndex];
655 this.appendBuffer(" (field ");
656 this.appendBuffer(
657 this.maybeMut(this.typeToString(type.elementType), type.mutability)
658 );
659 }
660 private printBlockType(type: Type): void {
661 if (type.kind === TypeKind.empty_block_type) {
662 return;
663 }
664 if (type.kind === TypeKind.unspecified) {
665 return this.printFuncType(type.index);
666 }
667 this.appendBuffer(" (result ");
668 this.appendBuffer(this.typeToString(type));
669 this.appendBuffer(")");
670 }
671 private printString(b: Uint8Array): void {
672 this.appendBuffer('"');
673 for (var i = 0; i < b.length; i++) {
674 var byte = b[i];
675 if (
676 byte < 0x20 ||
677 byte >= 0x7f ||
678 byte == /* " */ 0x22 ||
679 byte == /* \ */ 0x5c
680 ) {
681 this.appendBuffer(
682 "\\" + (byte >> 4).toString(16) + (byte & 15).toString(16)
683 );
684 } else {
685 this.appendBuffer(String.fromCharCode(byte));
686 }
687 }
688 this.appendBuffer('"');
689 }
690 private printExpression(expression: IOperatorInformation[]): void {
691 for (const operator of expression) {
692 this.appendBuffer("(");
693 this.printOperator(operator);
694 this.appendBuffer(")");
695 }
696 }
697 // extraDepthOffset is used by "delegate" instructions.
698 private useLabel(depth: number, extraDepthOffset = 0): string {
699 if (!this._backrefLabels) {
700 return "" + depth;
701 }
702 var i = this._backrefLabels.length - depth - 1 - extraDepthOffset;
703 if (i < 0) {
704 return "" + depth;
705 }
706 var backrefLabel = this._backrefLabels[i];
707 if (!backrefLabel.useLabel) {
708 backrefLabel.useLabel = true;
709 backrefLabel.label = this._nameResolver.getLabel(this._labelIndex);
710 var line = this._lines[backrefLabel.line];
711 this._lines[backrefLabel.line] =
712 line.substring(0, backrefLabel.position) +
713 " " +
714 backrefLabel.label +
715 line.substring(backrefLabel.position);
716 this._labelIndex++;
717 }
718 return backrefLabel.label || "" + depth;
719 }
720 private printOperator(operator: IOperatorInformation): void {
721 var code = operator.code;
722 this.appendBuffer(OperatorCodeNames[code]);
723 switch (code) {
724 case OperatorCode.block:
725 case OperatorCode.loop:
726 case OperatorCode.if:
727 case OperatorCode.try:
728 if (this._labelMode !== LabelMode.Depth) {
729 const backrefLabel = {
730 line: this._lines.length,
731 position: this._buffer.length,
732 useLabel: false,
733 label: null,
734 };
735 if (this._labelMode === LabelMode.Always) {
736 backrefLabel.useLabel = true;
737 backrefLabel.label = this._nameResolver.getLabel(
738 this._labelIndex++
739 );
740 if (backrefLabel.label) {
741 this.appendBuffer(" ");
742 this.appendBuffer(backrefLabel.label);
743 }
744 }
745 this._backrefLabels.push(backrefLabel);
746 }
747 this.printBlockType(operator.blockType);
748 break;
749 case OperatorCode.end:
750 if (this._labelMode === LabelMode.Depth) {
751 break;
752 }
753 const backrefLabel = this._backrefLabels.pop();
754 if (backrefLabel.label) {
755 this.appendBuffer(" ");
756 this.appendBuffer(backrefLabel.label);
757 }
758 break;
759 case OperatorCode.br:
760 case OperatorCode.br_if:
761 case OperatorCode.br_on_null:
762 case OperatorCode.br_on_non_null:
763 case OperatorCode.br_on_cast:
764 case OperatorCode.br_on_cast_fail:
765 case OperatorCode.br_on_func:
766 case OperatorCode.br_on_non_func:
767 case OperatorCode.br_on_data:
768 case OperatorCode.br_on_non_data:
769 case OperatorCode.br_on_i31:
770 case OperatorCode.br_on_non_i31:
771 this.appendBuffer(" ");
772 this.appendBuffer(this.useLabel(operator.brDepth));
773 break;
774 case OperatorCode.br_on_cast_static:
775 case OperatorCode.br_on_cast_static_fail: {
776 const label = this.useLabel(operator.brDepth);
777 const refType = this._nameResolver.getTypeName(operator.refType, true);
778 this.appendBuffer(` ${label} ${refType}`);
779 break;
780 }
781 case OperatorCode.br_table:
782 for (var i = 0; i < operator.brTable.length; i++) {
783 this.appendBuffer(" ");
784 this.appendBuffer(this.useLabel(operator.brTable[i]));
785 }
786 break;
787 case OperatorCode.rethrow:
788 this.appendBuffer(" ");
789 this.appendBuffer(this.useLabel(operator.relativeDepth));
790 break;
791 case OperatorCode.delegate:
792 this.appendBuffer(" ");
793 this.appendBuffer(this.useLabel(operator.relativeDepth, 1));
794 break;
795 case OperatorCode.catch:
796 case OperatorCode.throw:
797 var eventName = this._nameResolver.getEventName(
798 operator.eventIndex,
799 true
800 );
801 this.appendBuffer(` ${eventName}`);
802 break;
803 case OperatorCode.ref_null:
804 this.appendBuffer(" ");
805 this.appendBuffer(this.typeIndexToString(operator.refType));
806 break;
807 case OperatorCode.call:
808 case OperatorCode.return_call:
809 case OperatorCode.ref_func:
810 var funcName = this._nameResolver.getFunctionName(
811 operator.funcIndex,
812 operator.funcIndex < this._importCount,
813 true
814 );
815 this.appendBuffer(` ${funcName}`);
816 break;
817 case OperatorCode.call_indirect:
818 case OperatorCode.return_call_indirect:
819 this.printFuncType(operator.typeIndex);
820 break;
821 case OperatorCode.select_with_type: {
822 const selectType = this.typeToString(operator.selectType);
823 this.appendBuffer(` ${selectType}`);
824 break;
825 }
826 case OperatorCode.local_get:
827 case OperatorCode.local_set:
828 case OperatorCode.local_tee:
829 var paramName = this._nameResolver.getVariableName(
830 this._funcIndex,
831 operator.localIndex,
832 true
833 );
834 this.appendBuffer(` ${paramName}`);
835 break;
836 case OperatorCode.global_get:
837 case OperatorCode.global_set:
838 var globalName = this._nameResolver.getGlobalName(
839 operator.globalIndex,
840 true
841 );
842 this.appendBuffer(` ${globalName}`);
843 break;
844 case OperatorCode.i32_load:
845 case OperatorCode.i64_load:
846 case OperatorCode.f32_load:
847 case OperatorCode.f64_load:
848 case OperatorCode.i32_load8_s:
849 case OperatorCode.i32_load8_u:
850 case OperatorCode.i32_load16_s:
851 case OperatorCode.i32_load16_u:
852 case OperatorCode.i64_load8_s:
853 case OperatorCode.i64_load8_u:
854 case OperatorCode.i64_load16_s:
855 case OperatorCode.i64_load16_u:
856 case OperatorCode.i64_load32_s:
857 case OperatorCode.i64_load32_u:
858 case OperatorCode.i32_store:
859 case OperatorCode.i64_store:
860 case OperatorCode.f32_store:
861 case OperatorCode.f64_store:
862 case OperatorCode.i32_store8:
863 case OperatorCode.i32_store16:
864 case OperatorCode.i64_store8:
865 case OperatorCode.i64_store16:
866 case OperatorCode.i64_store32:
867 case OperatorCode.atomic_notify:
868 case OperatorCode.i32_atomic_wait:
869 case OperatorCode.i64_atomic_wait:
870 case OperatorCode.i32_atomic_load:
871 case OperatorCode.i64_atomic_load:
872 case OperatorCode.i32_atomic_load8_u:
873 case OperatorCode.i32_atomic_load16_u:
874 case OperatorCode.i64_atomic_load8_u:
875 case OperatorCode.i64_atomic_load16_u:
876 case OperatorCode.i64_atomic_load32_u:
877 case OperatorCode.i32_atomic_store:
878 case OperatorCode.i64_atomic_store:
879 case OperatorCode.i32_atomic_store8:
880 case OperatorCode.i32_atomic_store16:
881 case OperatorCode.i64_atomic_store8:
882 case OperatorCode.i64_atomic_store16:
883 case OperatorCode.i64_atomic_store32:
884 case OperatorCode.i32_atomic_rmw_add:
885 case OperatorCode.i64_atomic_rmw_add:
886 case OperatorCode.i32_atomic_rmw8_add_u:
887 case OperatorCode.i32_atomic_rmw16_add_u:
888 case OperatorCode.i64_atomic_rmw8_add_u:
889 case OperatorCode.i64_atomic_rmw16_add_u:
890 case OperatorCode.i64_atomic_rmw32_add_u:
891 case OperatorCode.i32_atomic_rmw_sub:
892 case OperatorCode.i64_atomic_rmw_sub:
893 case OperatorCode.i32_atomic_rmw8_sub_u:
894 case OperatorCode.i32_atomic_rmw16_sub_u:
895 case OperatorCode.i64_atomic_rmw8_sub_u:
896 case OperatorCode.i64_atomic_rmw16_sub_u:
897 case OperatorCode.i64_atomic_rmw32_sub_u:
898 case OperatorCode.i32_atomic_rmw_and:
899 case OperatorCode.i64_atomic_rmw_and:
900 case OperatorCode.i32_atomic_rmw8_and_u:
901 case OperatorCode.i32_atomic_rmw16_and_u:
902 case OperatorCode.i64_atomic_rmw8_and_u:
903 case OperatorCode.i64_atomic_rmw16_and_u:
904 case OperatorCode.i64_atomic_rmw32_and_u:
905 case OperatorCode.i32_atomic_rmw_or:
906 case OperatorCode.i64_atomic_rmw_or:
907 case OperatorCode.i32_atomic_rmw8_or_u:
908 case OperatorCode.i32_atomic_rmw16_or_u:
909 case OperatorCode.i64_atomic_rmw8_or_u:
910 case OperatorCode.i64_atomic_rmw16_or_u:
911 case OperatorCode.i64_atomic_rmw32_or_u:
912 case OperatorCode.i32_atomic_rmw_xor:
913 case OperatorCode.i64_atomic_rmw_xor:
914 case OperatorCode.i32_atomic_rmw8_xor_u:
915 case OperatorCode.i32_atomic_rmw16_xor_u:
916 case OperatorCode.i64_atomic_rmw8_xor_u:
917 case OperatorCode.i64_atomic_rmw16_xor_u:
918 case OperatorCode.i64_atomic_rmw32_xor_u:
919 case OperatorCode.i32_atomic_rmw_xchg:
920 case OperatorCode.i64_atomic_rmw_xchg:
921 case OperatorCode.i32_atomic_rmw8_xchg_u:
922 case OperatorCode.i32_atomic_rmw16_xchg_u:
923 case OperatorCode.i64_atomic_rmw8_xchg_u:
924 case OperatorCode.i64_atomic_rmw16_xchg_u:
925 case OperatorCode.i64_atomic_rmw32_xchg_u:
926 case OperatorCode.i32_atomic_rmw_cmpxchg:
927 case OperatorCode.i64_atomic_rmw_cmpxchg:
928 case OperatorCode.i32_atomic_rmw8_cmpxchg_u:
929 case OperatorCode.i32_atomic_rmw16_cmpxchg_u:
930 case OperatorCode.i64_atomic_rmw8_cmpxchg_u:
931 case OperatorCode.i64_atomic_rmw16_cmpxchg_u:
932 case OperatorCode.i64_atomic_rmw32_cmpxchg_u:
933 case OperatorCode.v128_load:
934 case OperatorCode.i16x8_load8x8_s:
935 case OperatorCode.i16x8_load8x8_u:
936 case OperatorCode.i32x4_load16x4_s:
937 case OperatorCode.i32x4_load16x4_u:
938 case OperatorCode.i64x2_load32x2_s:
939 case OperatorCode.i64x2_load32x2_u:
940 case OperatorCode.v8x16_load_splat:
941 case OperatorCode.v16x8_load_splat:
942 case OperatorCode.v32x4_load_splat:
943 case OperatorCode.v64x2_load_splat:
944 case OperatorCode.v128_store:
945 case OperatorCode.v128_load32_zero:
946 case OperatorCode.v128_load64_zero:
947 var memoryAddress = memoryAddressToString(
948 operator.memoryAddress,
949 operator.code
950 );
951 if (memoryAddress !== null) {
952 this.appendBuffer(" ");
953 this.appendBuffer(memoryAddress);
954 }
955 break;
956 case OperatorCode.current_memory:
957 case OperatorCode.grow_memory:
958 break;
959 case OperatorCode.i32_const:
960 this.appendBuffer(` ${(<number>operator.literal).toString()}`);
961 break;
962 case OperatorCode.i64_const:
963 this.appendBuffer(` ${(<Int64>operator.literal).toString()}`);
964 break;
965 case OperatorCode.f32_const:
966 this.appendBuffer(` ${formatFloat32(<number>operator.literal)}`);
967 break;
968 case OperatorCode.f64_const:
969 this.appendBuffer(` ${formatFloat64(<number>operator.literal)}`);
970 break;
971 case OperatorCode.v128_const:
972 this.appendBuffer(` i32x4 ${formatI32Array(operator.literal, 4)}`);
973 break;
974 case OperatorCode.i8x16_shuffle:
975 this.appendBuffer(` ${formatI8Array(operator.lines, 16)}`);
976 break;
977 case OperatorCode.i8x16_extract_lane_s:
978 case OperatorCode.i8x16_extract_lane_u:
979 case OperatorCode.i8x16_replace_lane:
980 case OperatorCode.i16x8_extract_lane_s:
981 case OperatorCode.i16x8_extract_lane_u:
982 case OperatorCode.i16x8_replace_lane:
983 case OperatorCode.i32x4_extract_lane:
984 case OperatorCode.i32x4_replace_lane:
985 case OperatorCode.f32x4_extract_lane:
986 case OperatorCode.f32x4_replace_lane:
987 case OperatorCode.i64x2_extract_lane:
988 case OperatorCode.i64x2_replace_lane:
989 case OperatorCode.f64x2_extract_lane:
990 case OperatorCode.f64x2_replace_lane:
991 this.appendBuffer(` ${operator.lineIndex}`);
992 break;
993 case OperatorCode.memory_init:
994 case OperatorCode.data_drop:
995 this.appendBuffer(` ${operator.segmentIndex}`);
996 break;
997 case OperatorCode.elem_drop:
998 const elementName = this._nameResolver.getElementName(
999 operator.segmentIndex,
1000 true
1001 );
1002 this.appendBuffer(` ${elementName}`);
1003 break;
1004 case OperatorCode.table_set:
1005 case OperatorCode.table_get:
1006 case OperatorCode.table_fill: {
1007 const tableName = this._nameResolver.getTableName(
1008 operator.tableIndex,
1009 true
1010 );
1011 this.appendBuffer(` ${tableName}`);
1012 break;
1013 }
1014 case OperatorCode.table_copy: {
1015 // Table index might be omitted and defaults to 0.
1016 if (operator.tableIndex !== 0 || operator.destinationIndex !== 0) {
1017 const tableName = this._nameResolver.getTableName(
1018 operator.tableIndex,
1019 true
1020 );
1021 const destinationName = this._nameResolver.getTableName(
1022 operator.destinationIndex,
1023 true
1024 );
1025 this.appendBuffer(` ${destinationName} ${tableName}`);
1026 }
1027 break;
1028 }
1029 case OperatorCode.table_init: {
1030 // Table index might be omitted and defaults to 0.
1031 if (operator.tableIndex !== 0) {
1032 const tableName = this._nameResolver.getTableName(
1033 operator.tableIndex,
1034 true
1035 );
1036 this.appendBuffer(` ${tableName}`);
1037 }
1038 const elementName = this._nameResolver.getElementName(
1039 operator.segmentIndex,
1040 true
1041 );
1042 this.appendBuffer(` ${elementName}`);
1043 break;
1044 }
1045 case OperatorCode.struct_get:
1046 case OperatorCode.struct_get_s:
1047 case OperatorCode.struct_get_u:
1048 case OperatorCode.struct_set: {
1049 const refType = this._nameResolver.getTypeName(operator.refType, true);
1050 const fieldName = this._nameResolver.getFieldName(
1051 operator.refType,
1052 operator.fieldIndex,
1053 true
1054 );
1055 this.appendBuffer(` ${refType} ${fieldName}`);
1056 break;
1057 }
1058 case OperatorCode.rtt_canon:
1059 case OperatorCode.rtt_sub:
1060 case OperatorCode.rtt_fresh_sub:
1061 case OperatorCode.ref_test_static:
1062 case OperatorCode.ref_cast_static:
1063 case OperatorCode.struct_new_default:
1064 case OperatorCode.struct_new_default_with_rtt:
1065 case OperatorCode.struct_new:
1066 case OperatorCode.struct_new_with_rtt:
1067 case OperatorCode.array_new_default:
1068 case OperatorCode.array_new_default_with_rtt:
1069 case OperatorCode.array_new:
1070 case OperatorCode.array_new_with_rtt:
1071 case OperatorCode.array_get:
1072 case OperatorCode.array_get_s:
1073 case OperatorCode.array_get_u:
1074 case OperatorCode.array_set:
1075 case OperatorCode.array_len: {
1076 const refType = this._nameResolver.getTypeName(operator.refType, true);
1077 this.appendBuffer(` ${refType}`);
1078 break;
1079 }
1080 case OperatorCode.array_copy: {
1081 const dstType = this._nameResolver.getTypeName(operator.refType, true);
1082 const srcType = this._nameResolver.getTypeName(operator.srcType, true);
1083 this.appendBuffer(` ${dstType} ${srcType}`);
1084 break;
1085 }
1086 case OperatorCode.array_init:
1087 case OperatorCode.array_init_static: {
1088 const refType = this._nameResolver.getTypeName(operator.refType, true);
1089 const length = operator.brDepth; // Overloaded field.
1090 this.appendBuffer(` ${refType} ${length}`);
1091 break;
1092 }
1093 }
1094 }
1095 private printImportSource(info: IImportEntry): void {
1096 this.printString(info.module);
1097 this.appendBuffer(" ");
1098 this.printString(info.field);
1099 }
1100 private increaseIndent(): void {
1101 this._indent += IndentIncrement;
1102 this._indentLevel++;
1103 }
1104 private decreaseIndent(): void {
1105 this._indent = this._indent.slice(0, -IndentIncrement.length);
1106 this._indentLevel--;
1107 }
1108 public disassemble(reader: BinaryReader): string {
1109 const done = this.disassembleChunk(reader);
1110 if (!done) return null;
1111 let lines = this._lines;
1112 if (this._addOffsets) {
1113 lines = lines.map((line, index) => {
1114 var position = formatHex(this._offsets[index], 4);
1115 return line + " ;; @" + position;
1116 });
1117 }
1118 lines.push(""); // we need '\n' after last line
1119 const result = lines.join("\n");
1120 this._lines.length = 0;
1121 this._offsets.length = 0;
1122 this._functionBodyOffsets.length = 0;
1123 return result;
1124 }
1125 public getResult(): IDisassemblerResult {
1126 let linesReady = this._lines.length;
1127 if (this._backrefLabels && this._labelMode === LabelMode.WhenUsed) {
1128 this._backrefLabels.some((backrefLabel) => {
1129 if (backrefLabel.useLabel) return false;
1130 linesReady = backrefLabel.line;
1131 return true;
1132 });
1133 }
1134 if (linesReady === 0) {
1135 return {
1136 lines: [],
1137 offsets: this._addOffsets ? [] : undefined,
1138 done: this._done,
1139 functionBodyOffsets: this._addOffsets ? [] : undefined,
1140 };
1141 }
1142 if (linesReady === this._lines.length) {
1143 const result = {
1144 lines: this._lines,
1145 offsets: this._addOffsets ? this._offsets : undefined,
1146 done: this._done,
1147 functionBodyOffsets: this._addOffsets
1148 ? this._functionBodyOffsets
1149 : undefined,
1150 };
1151 this._lines = [];
1152 if (this._addOffsets) {
1153 this._offsets = [];
1154 this._functionBodyOffsets = [];
1155 }
1156 return result;
1157 }
1158 const result = {
1159 lines: this._lines.splice(0, linesReady),
1160 offsets: this._addOffsets
1161 ? this._offsets.splice(0, linesReady)
1162 : undefined,
1163 done: false,
1164 functionBodyOffsets: this._addOffsets
1165 ? this._functionBodyOffsets
1166 : undefined,
1167 };
1168 if (this._backrefLabels) {
1169 this._backrefLabels.forEach((backrefLabel) => {
1170 backrefLabel.line -= linesReady;
1171 });
1172 }
1173 return result;
1174 }
1175 public disassembleChunk(reader: BinaryReader, offsetInModule = 0): boolean {
1176 if (this._done)
1177 throw new Error(
1178 "Invalid state: disassembly process was already finished."
1179 );
1180 while (true) {
1181 this._currentPosition = reader.position + offsetInModule;
1182 if (!reader.read()) return false;
1183 switch (reader.state) {
1184 case BinaryReaderState.END_WASM:
1185 this.appendBuffer(")");
1186 this.newLine();
1187 this._reset();
1188 if (!reader.hasMoreBytes()) {
1189 this._done = true;
1190 return true;
1191 }
1192 break;
1193 case BinaryReaderState.ERROR:
1194 throw reader.error;
1195 case BinaryReaderState.BEGIN_WASM:
1196 this.appendBuffer("(module");
1197 this.newLine();
1198 break;
1199 case BinaryReaderState.END_SECTION:
1200 this._currentSectionId = SectionCode.Unknown;
1201 break;
1202 case BinaryReaderState.BEGIN_SECTION:
1203 var sectionInfo = <ISectionInformation>reader.result;
1204 switch (sectionInfo.id) {
1205 case SectionCode.Type:
1206 case SectionCode.Import:
1207 case SectionCode.Export:
1208 case SectionCode.Global:
1209 case SectionCode.Function:
1210 case SectionCode.Start:
1211 case SectionCode.Code:
1212 case SectionCode.Memory:
1213 case SectionCode.Data:
1214 case SectionCode.Table:
1215 case SectionCode.Element:
1216 case SectionCode.Event:
1217 this._currentSectionId = sectionInfo.id;
1218 break; // reading known section;
1219 default:
1220 reader.skipSection();
1221 break;
1222 }
1223 break;
1224 case BinaryReaderState.MEMORY_SECTION_ENTRY:
1225 var memoryInfo = <IMemoryType>reader.result;
1226 var memoryIndex = this._memoryCount++;
1227 var memoryName = this._nameResolver.getMemoryName(memoryIndex, false);
1228 this.appendBuffer(` (memory ${memoryName}`);
1229 if (this._exportMetadata !== null) {
1230 for (const exportName of this._exportMetadata.getMemoryExportNames(
1231 memoryIndex
1232 )) {
1233 this.appendBuffer(` (export ${JSON.stringify(exportName)})`);
1234 }
1235 }
1236 this.appendBuffer(` ${limitsToString(memoryInfo.limits)}`);
1237 if (memoryInfo.shared) {
1238 this.appendBuffer(` shared`);
1239 }
1240 this.appendBuffer(")");
1241 this.newLine();
1242 break;
1243 case BinaryReaderState.EVENT_SECTION_ENTRY:
1244 var eventInfo = <IEventType>reader.result;
1245 var eventIndex = this._eventCount++;
1246 var eventName = this._nameResolver.getEventName(eventIndex, false);
1247 this.appendBuffer(` (event ${eventName}`);
1248 if (this._exportMetadata !== null) {
1249 for (const exportName of this._exportMetadata.getEventExportNames(
1250 eventIndex
1251 )) {
1252 this.appendBuffer(` (export ${JSON.stringify(exportName)})`);
1253 }
1254 }
1255 this.printFuncType(eventInfo.typeIndex);
1256 this.appendBuffer(")");
1257 this.newLine();
1258 break;
1259 case BinaryReaderState.TABLE_SECTION_ENTRY:
1260 var tableInfo = <ITableType>reader.result;
1261 var tableIndex = this._tableCount++;
1262 var tableName = this._nameResolver.getTableName(tableIndex, false);
1263 this.appendBuffer(` (table ${tableName}`);
1264 if (this._exportMetadata !== null) {
1265 for (const exportName of this._exportMetadata.getTableExportNames(
1266 tableIndex
1267 )) {
1268 this.appendBuffer(` (export ${JSON.stringify(exportName)})`);
1269 }
1270 }
1271 this.appendBuffer(
1272 ` ${limitsToString(tableInfo.limits)} ${this.typeToString(
1273 tableInfo.elementType
1274 )})`
1275 );
1276 this.newLine();
1277 break;
1278 case BinaryReaderState.EXPORT_SECTION_ENTRY:
1279 // Skip printing exports here when we have export metadata
1280 // which we can use to print export information inline.
1281 if (this._exportMetadata === null) {
1282 var exportInfo = <IExportEntry>reader.result;
1283 this.appendBuffer(" (export ");
1284 this.printString(exportInfo.field);
1285 this.appendBuffer(" ");
1286 switch (exportInfo.kind) {
1287 case ExternalKind.Function:
1288 var funcName = this._nameResolver.getFunctionName(
1289 exportInfo.index,
1290 exportInfo.index < this._importCount,
1291 true
1292 );
1293 this.appendBuffer(`(func ${funcName})`);
1294 break;
1295 case ExternalKind.Table:
1296 var tableName = this._nameResolver.getTableName(
1297 exportInfo.index,
1298 true
1299 );
1300 this.appendBuffer(`(table ${tableName})`);
1301 break;
1302 case ExternalKind.Memory:
1303 var memoryName = this._nameResolver.getMemoryName(
1304 exportInfo.index,
1305 true
1306 );
1307 this.appendBuffer(`(memory ${memoryName})`);
1308 break;
1309 case ExternalKind.Global:
1310 var globalName = this._nameResolver.getGlobalName(
1311 exportInfo.index,
1312 true
1313 );
1314 this.appendBuffer(`(global ${globalName})`);
1315 break;
1316 case ExternalKind.Event:
1317 var eventName = this._nameResolver.getEventName(
1318 exportInfo.index,
1319 true
1320 );
1321 this.appendBuffer(`(event ${eventName})`);
1322 break;
1323 default:
1324 throw new Error(`Unsupported export ${exportInfo.kind}`);
1325 }
1326 this.appendBuffer(")");
1327 this.newLine();
1328 }
1329 break;
1330 case BinaryReaderState.IMPORT_SECTION_ENTRY:
1331 var importInfo = <IImportEntry>reader.result;
1332 switch (importInfo.kind) {
1333 case ExternalKind.Function:
1334 this._importCount++;
1335 var funcIndex = this._funcIndex++;
1336 var funcName = this._nameResolver.getFunctionName(
1337 funcIndex,
1338 true,
1339 false
1340 );
1341 this.appendBuffer(` (func ${funcName}`);
1342 if (this._exportMetadata !== null) {
1343 for (const exportName of this._exportMetadata.getFunctionExportNames(
1344 funcIndex
1345 )) {
1346 this.appendBuffer(` (export ${JSON.stringify(exportName)})`);
1347 }
1348 }
1349 this.appendBuffer(` (import `);
1350 this.printImportSource(importInfo);
1351 this.appendBuffer(")");
1352 this.printFuncType(importInfo.funcTypeIndex);
1353 this.appendBuffer(")");
1354 break;
1355 case ExternalKind.Global:
1356 var globalImportInfo = <IGlobalType>importInfo.type;
1357 var globalIndex = this._globalCount++;
1358 var globalName = this._nameResolver.getGlobalName(
1359 globalIndex,
1360 false
1361 );
1362 this.appendBuffer(` (global ${globalName}`);
1363 if (this._exportMetadata !== null) {
1364 for (const exportName of this._exportMetadata.getGlobalExportNames(
1365 globalIndex
1366 )) {
1367 this.appendBuffer(` (export ${JSON.stringify(exportName)})`);
1368 }
1369 }
1370 this.appendBuffer(` (import `);
1371 this.printImportSource(importInfo);
1372 this.appendBuffer(
1373 `) ${this.globalTypeToString(globalImportInfo)})`
1374 );
1375 break;
1376 case ExternalKind.Memory:
1377 var memoryImportInfo = <IMemoryType>importInfo.type;
1378 var memoryIndex = this._memoryCount++;
1379 var memoryName = this._nameResolver.getMemoryName(
1380 memoryIndex,
1381 false
1382 );
1383 this.appendBuffer(` (memory ${memoryName}`);
1384 if (this._exportMetadata !== null) {
1385 for (const exportName of this._exportMetadata.getMemoryExportNames(
1386 memoryIndex
1387 )) {
1388 this.appendBuffer(` (export ${JSON.stringify(exportName)})`);
1389 }
1390 }
1391 this.appendBuffer(` (import `);
1392 this.printImportSource(importInfo);
1393 this.appendBuffer(`) ${limitsToString(memoryImportInfo.limits)}`);
1394 if (memoryImportInfo.shared) {
1395 this.appendBuffer(` shared`);
1396 }
1397 this.appendBuffer(")");
1398 break;
1399 case ExternalKind.Table:
1400 var tableImportInfo = <ITableType>importInfo.type;
1401 var tableIndex = this._tableCount++;
1402 var tableName = this._nameResolver.getTableName(
1403 tableIndex,
1404 false
1405 );
1406 this.appendBuffer(` (table ${tableName}`);
1407 if (this._exportMetadata !== null) {
1408 for (const exportName of this._exportMetadata.getTableExportNames(
1409 tableIndex
1410 )) {
1411 this.appendBuffer(` (export ${JSON.stringify(exportName)})`);
1412 }
1413 }
1414 this.appendBuffer(` (import `);
1415 this.printImportSource(importInfo);
1416 this.appendBuffer(
1417 `) ${limitsToString(
1418 tableImportInfo.limits
1419 )} ${this.typeToString(tableImportInfo.elementType)})`
1420 );
1421 break;
1422 case ExternalKind.Event:
1423 var eventImportInfo = <IEventType>importInfo.type;
1424 var eventIndex = this._eventCount++;
1425 var eventName = this._nameResolver.getEventName(
1426 eventIndex,
1427 false
1428 );
1429 this.appendBuffer(` (event ${eventName}`);
1430 if (this._exportMetadata !== null) {
1431 for (const exportName of this._exportMetadata.getEventExportNames(
1432 eventIndex
1433 )) {
1434 this.appendBuffer(` (export ${JSON.stringify(exportName)})`);
1435 }
1436 }
1437 this.appendBuffer(` (import `);
1438 this.printImportSource(importInfo);
1439 this.appendBuffer(")");
1440 this.printFuncType(eventImportInfo.typeIndex);
1441 this.appendBuffer(")");
1442 break;
1443 default:
1444 throw new Error(`NYI other import types: ${importInfo.kind}`);
1445 }
1446 this.newLine();
1447 break;
1448 case BinaryReaderState.BEGIN_ELEMENT_SECTION_ENTRY:
1449 var elementSegment = <IElementSegment>reader.result;
1450 var elementIndex = this._elementCount++;
1451 var elementName = this._nameResolver.getElementName(
1452 elementIndex,
1453 false
1454 );
1455 this.appendBuffer(` (elem ${elementName}`);
1456 switch (elementSegment.mode) {
1457 case ElementMode.Active:
1458 if (elementSegment.tableIndex !== 0) {
1459 const tableName = this._nameResolver.getTableName(
1460 elementSegment.tableIndex,
1461 false
1462 );
1463 this.appendBuffer(` (table ${tableName})`);
1464 }
1465 break;
1466 case ElementMode.Passive:
1467 break;
1468 case ElementMode.Declarative:
1469 this.appendBuffer(" declare");
1470 break;
1471 }
1472 break;
1473 case BinaryReaderState.END_ELEMENT_SECTION_ENTRY:
1474 this.appendBuffer(")");
1475 this.newLine();
1476 break;
1477 case BinaryReaderState.ELEMENT_SECTION_ENTRY_BODY:
1478 const elementSegmentBody = <IElementSegmentBody>reader.result;
1479 this.appendBuffer(
1480 ` ${this.typeToString(elementSegmentBody.elementType)}`
1481 );
1482 break;
1483 case BinaryReaderState.BEGIN_GLOBAL_SECTION_ENTRY:
1484 var globalInfo = <IGlobalVariable>reader.result;
1485 var globalIndex = this._globalCount++;
1486 var globalName = this._nameResolver.getGlobalName(globalIndex, false);
1487 this.appendBuffer(` (global ${globalName}`);
1488 if (this._exportMetadata !== null) {
1489 for (const exportName of this._exportMetadata.getGlobalExportNames(
1490 globalIndex
1491 )) {
1492 this.appendBuffer(` (export ${JSON.stringify(exportName)})`);
1493 }
1494 }
1495 this.appendBuffer(` ${this.globalTypeToString(globalInfo.type)}`);
1496 break;
1497 case BinaryReaderState.END_GLOBAL_SECTION_ENTRY:
1498 this.appendBuffer(")");
1499 this.newLine();
1500 break;
1501 case BinaryReaderState.TYPE_SECTION_ENTRY:
1502 var typeEntry = <ITypeEntry>reader.result;
1503 var typeIndex = this._types.length;
1504 this._types.push(typeEntry);
1505 if (!this._skipTypes) {
1506 var typeName = this._nameResolver.getTypeName(typeIndex, false);
1507 var superTypeName = undefined;
1508 if (typeEntry.supertype !== undefined) {
1509 superTypeName = this.typeIndexToString(typeEntry.supertype);
1510 }
1511 if (typeEntry.form === TypeKind.func) {
1512 this.appendBuffer(` (type ${typeName} (func`);
1513 this.printFuncType(typeIndex);
1514 this.appendBuffer("))");
1515 } else if (typeEntry.form === TypeKind.func_subtype) {
1516 this.appendBuffer(` (type ${typeName} (func_subtype`);
1517 this.printFuncType(typeIndex);
1518 this.appendBuffer(` (supertype ${superTypeName})))`);
1519 } else if (typeEntry.form === TypeKind.struct) {
1520 this.appendBuffer(` (type ${typeName} (struct`);
1521 this.printStructType(typeIndex);
1522 this.appendBuffer("))");
1523 } else if (typeEntry.form === TypeKind.struct_subtype) {
1524 this.appendBuffer(` (type ${typeName} (struct_subtype`);
1525 this.printStructType(typeIndex);
1526 this.appendBuffer(` (supertype ${superTypeName})))`);
1527 } else if (typeEntry.form === TypeKind.array) {
1528 this.appendBuffer(` (type ${typeName} (array`);
1529 this.printArrayType(typeIndex);
1530 this.appendBuffer("))");
1531 } else if (typeEntry.form === TypeKind.array_subtype) {
1532 this.appendBuffer(` (type ${typeName} (array_subtype`);
1533 this.printArrayType(typeIndex);
1534 this.appendBuffer(`) (supertype ${superTypeName})))`);
1535 } else {
1536 throw new Error(`Unknown type form: ${typeEntry.form}`);
1537 }
1538 this.newLine();
1539 }
1540 break;
1541 case BinaryReaderState.START_SECTION_ENTRY:
1542 var startEntry = <IStartEntry>reader.result;
1543 var funcName = this._nameResolver.getFunctionName(
1544 startEntry.index,
1545 startEntry.index < this._importCount,
1546 true
1547 );
1548 this.appendBuffer(` (start ${funcName})`);
1549 this.newLine();
1550 break;
1551 case BinaryReaderState.BEGIN_DATA_SECTION_ENTRY:
1552 this.appendBuffer(" (data");
1553 break;
1554 case BinaryReaderState.DATA_SECTION_ENTRY_BODY:
1555 var body = <IDataSegmentBody>reader.result;
1556 this.appendBuffer(" ");
1557 this.printString(body.data);
1558 break;
1559 case BinaryReaderState.END_DATA_SECTION_ENTRY:
1560 this.appendBuffer(")");
1561 this.newLine();
1562 break;
1563 case BinaryReaderState.BEGIN_INIT_EXPRESSION_BODY:
1564 case BinaryReaderState.BEGIN_OFFSET_EXPRESSION_BODY:
1565 this._expression = [];
1566 break;
1567 case BinaryReaderState.INIT_EXPRESSION_OPERATOR:
1568 case BinaryReaderState.OFFSET_EXPRESSION_OPERATOR:
1569 var operator = <IOperatorInformation>reader.result;
1570 if (operator.code !== OperatorCode.end) {
1571 this._expression.push(operator);
1572 }
1573 break;
1574 case BinaryReaderState.END_OFFSET_EXPRESSION_BODY:
1575 if (this._expression.length > 1) {
1576 this.appendBuffer(" (offset ");
1577 this.printExpression(this._expression);
1578 this.appendBuffer(")");
1579 } else {
1580 this.appendBuffer(" ");
1581 this.printExpression(this._expression);
1582 }
1583 this._expression = [];
1584 break;
1585 case BinaryReaderState.END_INIT_EXPRESSION_BODY:
1586 if (
1587 this._expression.length > 1 &&
1588 this._currentSectionId === SectionCode.Element
1589 ) {
1590 this.appendBuffer(" (item ");
1591 this.printExpression(this._expression);
1592 this.appendBuffer(")");
1593 } else {
1594 this.appendBuffer(" ");
1595 this.printExpression(this._expression);
1596 }
1597 this._expression = [];
1598 break;
1599 case BinaryReaderState.FUNCTION_SECTION_ENTRY:
1600 this._funcTypes.push((<IFunctionEntry>reader.result).typeIndex);
1601 break;
1602 case BinaryReaderState.BEGIN_FUNCTION_BODY:
1603 var func = <IFunctionInformation>reader.result;
1604 var type = this._types[
1605 this._funcTypes[this._funcIndex - this._importCount]
1606 ];
1607 this.appendBuffer(" (func ");
1608 this.appendBuffer(
1609 this._nameResolver.getFunctionName(this._funcIndex, false, false)
1610 );
1611 if (this._exportMetadata !== null) {
1612 for (const exportName of this._exportMetadata.getFunctionExportNames(
1613 this._funcIndex
1614 )) {
1615 this.appendBuffer(` (export ${JSON.stringify(exportName)})`);
1616 }
1617 }
1618 for (var i = 0; i < type.params.length; i++) {
1619 var paramName = this._nameResolver.getVariableName(
1620 this._funcIndex,
1621 i,
1622 false
1623 );
1624 this.appendBuffer(
1625 ` (param ${paramName} ${this.typeToString(type.params[i])})`
1626 );
1627 }
1628 for (var i = 0; i < type.returns.length; i++) {
1629 this.appendBuffer(
1630 ` (result ${this.typeToString(type.returns[i])})`
1631 );
1632 }
1633 this.newLine();
1634 var localIndex = type.params.length;
1635 if (func.locals.length > 0) {
1636 this.appendBuffer(" ");
1637 for (var l of func.locals) {
1638 for (var i = 0; i < l.count; i++) {
1639 var paramName = this._nameResolver.getVariableName(
1640 this._funcIndex,
1641 localIndex++,
1642 false
1643 );
1644 this.appendBuffer(
1645 ` (local ${paramName} ${this.typeToString(l.type)})`
1646 );
1647 }
1648 }
1649 this.newLine();
1650 }
1651 this._indent = " ";
1652 this._indentLevel = 0;
1653 this._labelIndex = 0;
1654 this._backrefLabels = this._labelMode === LabelMode.Depth ? null : [];
1655 this._logFirstInstruction = true;
1656 break;
1657 case BinaryReaderState.CODE_OPERATOR:
1658 if (this._logFirstInstruction) {
1659 this.logStartOfFunctionBodyOffset();
1660 this._logFirstInstruction = false;
1661 }
1662 var operator = <IOperatorInformation>reader.result;
1663 if (operator.code == OperatorCode.end && this._indentLevel == 0) {
1664 // reached of the function, closing function body
1665 this.appendBuffer(` )`);
1666 this.newLine();
1667 break;
1668 }
1669 switch (operator.code) {
1670 case OperatorCode.end:
1671 case OperatorCode.else:
1672 case OperatorCode.catch:
1673 case OperatorCode.catch_all:
1674 case OperatorCode.unwind:
1675 case OperatorCode.delegate:
1676 this.decreaseIndent();
1677 break;
1678 }
1679 this.appendBuffer(this._indent);
1680 this.printOperator(operator);
1681 this.newLine();
1682 switch (operator.code) {
1683 case OperatorCode.if:
1684 case OperatorCode.block:
1685 case OperatorCode.loop:
1686 case OperatorCode.else:
1687 case OperatorCode.try:
1688 case OperatorCode.catch:
1689 case OperatorCode.catch_all:
1690 case OperatorCode.unwind:
1691 this.increaseIndent();
1692 break;
1693 }
1694 break;
1695 case BinaryReaderState.END_FUNCTION_BODY:
1696 this._funcIndex++;
1697 this._backrefLabels = null;
1698 this.logEndOfFunctionBodyOffset();
1699 // See case BinaryReaderState.CODE_OPERATOR for closing of body
1700 break;
1701 default:
1702 throw new Error(`Expectected state: ${reader.state}`);
1703 }
1704 }
1705 }
1706}
1707
1708const UNKNOWN_FUNCTION_PREFIX = "unknown";
1709
1710class NameSectionNameResolver extends DefaultNameResolver {
1711 protected readonly _functionNames: string[];
1712 protected readonly _localNames: string[][];
1713 protected readonly _eventNames: string[];
1714 protected readonly _typeNames: string[];
1715 protected readonly _tableNames: string[];
1716 protected readonly _memoryNames: string[];
1717 protected readonly _globalNames: string[];
1718 protected readonly _fieldNames: string[][];
1719
1720 constructor(
1721 functionNames: string[],
1722 localNames: string[][],
1723 eventNames: string[],
1724 typeNames: string[],
1725 tableNames: string[],
1726 memoryNames: string[],
1727 globalNames: string[],
1728 fieldNames: string[][]
1729 ) {
1730 super();
1731 this._functionNames = functionNames;
1732 this._localNames = localNames;
1733 this._eventNames = eventNames;
1734 this._typeNames = typeNames;
1735 this._tableNames = tableNames;
1736 this._memoryNames = memoryNames;
1737 this._globalNames = globalNames;
1738 this._fieldNames = fieldNames;
1739 }
1740
1741 public override getTypeName(index: number, isRef: boolean): string {
1742 const name = this._typeNames[index];
1743 if (!name) return super.getTypeName(index, isRef);
1744 return isRef ? `$${name}` : `$${name} (;${index};)`;
1745 }
1746
1747 public override getTableName(index: number, isRef: boolean): string {
1748 const name = this._tableNames[index];
1749 if (!name) return super.getTableName(index, isRef);
1750 return isRef ? `$${name}` : `$${name} (;${index};)`;
1751 }
1752
1753 public override getMemoryName(index: number, isRef: boolean): string {
1754 const name = this._memoryNames[index];
1755 if (!name) return super.getMemoryName(index, isRef);
1756 return isRef ? `$${name}` : `$${name} (;${index};)`;
1757 }
1758
1759 public override getGlobalName(index: number, isRef: boolean): string {
1760 const name = this._globalNames[index];
1761 if (!name) return super.getGlobalName(index, isRef);
1762 return isRef ? `$${name}` : `$${name} (;${index};)`;
1763 }
1764
1765 public override getEventName(index: number, isRef: boolean): string {
1766 const name = this._eventNames[index];
1767 if (!name) return super.getEventName(index, isRef);
1768 return isRef ? `$${name}` : `$${name} (;${index};)`;
1769 }
1770
1771 public override getFunctionName(
1772 index: number,
1773 isImport: boolean,
1774 isRef: boolean
1775 ): string {
1776 const name = this._functionNames[index];
1777 if (!name) return `$${UNKNOWN_FUNCTION_PREFIX}${index}`;
1778 return isRef ? `$${name}` : `$${name} (;${index};)`;
1779 }
1780
1781 public override getVariableName(
1782 funcIndex: number,
1783 index: number,
1784 isRef: boolean
1785 ): string {
1786 const name =
1787 this._localNames[funcIndex] && this._localNames[funcIndex][index];
1788 if (!name) return super.getVariableName(funcIndex, index, isRef);
1789 return isRef ? `$${name}` : `$${name} (;${index};)`;
1790 }
1791
1792 public override getFieldName(
1793 typeIndex: number,
1794 index: number,
1795 isRef: boolean
1796 ): string {
1797 const name =
1798 this._fieldNames[typeIndex] && this._fieldNames[typeIndex][index];
1799 if (!name) return super.getFieldName(typeIndex, index, isRef);
1800 return isRef ? `$${name}` : `$${name} (;${index};)`;
1801 }
1802}
1803
1804export class NameSectionReader {
1805 private _done = false;
1806 private _functionsCount = 0;
1807 private _functionImportsCount = 0;
1808 private _functionNames: string[] = null;
1809 private _functionLocalNames: string[][] = null;
1810 private _eventNames: string[] = null;
1811 private _typeNames: string[] = null;
1812 private _tableNames: string[] = null;
1813 private _memoryNames: string[] = null;
1814 private _globalNames: string[] = null;
1815 private _fieldNames: string[][] = null;
1816 private _hasNames = false;
1817
1818 public read(reader: BinaryReader): boolean {
1819 if (this._done)
1820 throw new Error(
1821 "Invalid state: disassembly process was already finished."
1822 );
1823 while (true) {
1824 if (!reader.read()) return false;
1825 switch (reader.state) {
1826 case BinaryReaderState.END_WASM:
1827 if (!reader.hasMoreBytes()) {
1828 this._done = true;
1829 return true;
1830 }
1831 break;
1832 case BinaryReaderState.ERROR:
1833 throw reader.error;
1834 case BinaryReaderState.BEGIN_WASM:
1835 this._functionsCount = 0;
1836 this._functionImportsCount = 0;
1837 this._functionNames = [];
1838 this._functionLocalNames = [];
1839 this._eventNames = [];
1840 this._typeNames = [];
1841 this._tableNames = [];
1842 this._memoryNames = [];
1843 this._globalNames = [];
1844 this._fieldNames = [];
1845 this._hasNames = false;
1846 break;
1847 case BinaryReaderState.END_SECTION:
1848 break;
1849 case BinaryReaderState.BEGIN_SECTION:
1850 var sectionInfo = <ISectionInformation>reader.result;
1851 if (
1852 sectionInfo.id === SectionCode.Custom &&
1853 bytesToString(sectionInfo.name) === NAME_SECTION_NAME
1854 ) {
1855 break;
1856 }
1857 if (
1858 sectionInfo.id === SectionCode.Function ||
1859 sectionInfo.id === SectionCode.Import
1860 ) {
1861 break;
1862 }
1863 reader.skipSection();
1864 break;
1865 case BinaryReaderState.IMPORT_SECTION_ENTRY:
1866 var importInfo = <IImportEntry>reader.result;
1867 if (importInfo.kind === ExternalKind.Function)
1868 this._functionImportsCount++;
1869 break;
1870 case BinaryReaderState.FUNCTION_SECTION_ENTRY:
1871 this._functionsCount++;
1872 break;
1873 case BinaryReaderState.NAME_SECTION_ENTRY:
1874 const nameInfo = <INameEntry>reader.result;
1875 if (nameInfo.type === NameType.Function) {
1876 const { names } = <IFunctionNameEntry>nameInfo;
1877 names.forEach(({ index, name }) => {
1878 this._functionNames[index] = bytesToString(name);
1879 });
1880 this._hasNames = true;
1881 } else if (nameInfo.type === NameType.Local) {
1882 const { funcs } = <ILocalNameEntry>nameInfo;
1883 funcs.forEach(({ index, locals }) => {
1884 const localNames = (this._functionLocalNames[index] = []);
1885 locals.forEach(({ index, name }) => {
1886 localNames[index] = bytesToString(name);
1887 });
1888 });
1889 this._hasNames = true;
1890 } else if (nameInfo.type === NameType.Event) {
1891 const { names } = <IEventNameEntry>nameInfo;
1892 names.forEach(({ index, name }) => {
1893 this._eventNames[index] = bytesToString(name);
1894 });
1895 this._hasNames = true;
1896 } else if (nameInfo.type === NameType.Type) {
1897 const { names } = <ITypeNameEntry>nameInfo;
1898 names.forEach(({ index, name }) => {
1899 this._typeNames[index] = bytesToString(name);
1900 });
1901 this._hasNames = true;
1902 } else if (nameInfo.type === NameType.Table) {
1903 const { names } = <ITableNameEntry>nameInfo;
1904 names.forEach(({ index, name }) => {
1905 this._tableNames[index] = bytesToString(name);
1906 });
1907 this._hasNames = true;
1908 } else if (nameInfo.type === NameType.Memory) {
1909 const { names } = <IMemoryNameEntry>nameInfo;
1910 names.forEach(({ index, name }) => {
1911 this._memoryNames[index] = bytesToString(name);
1912 });
1913 this._hasNames = true;
1914 } else if (nameInfo.type === NameType.Global) {
1915 const { names } = <IGlobalNameEntry>nameInfo;
1916 names.forEach(({ index, name }) => {
1917 this._globalNames[index] = bytesToString(name);
1918 });
1919 this._hasNames = true;
1920 } else if (nameInfo.type === NameType.Field) {
1921 const { types } = <IFieldNameEntry>nameInfo;
1922 types.forEach(({ index, fields }) => {
1923 const fieldNames = (this._fieldNames[index] = []);
1924 fields.forEach(({ index, name }) => {
1925 fieldNames[index] = bytesToString(name);
1926 });
1927 });
1928 }
1929 break;
1930 default:
1931 throw new Error(`Expectected state: ${reader.state}`);
1932 }
1933 }
1934 }
1935
1936 public hasValidNames(): boolean {
1937 return this._hasNames;
1938 }
1939
1940 public getNameResolver(): INameResolver {
1941 if (!this.hasValidNames()) throw new Error("Has no valid name section");
1942
1943 // Fix bad names.
1944 const functionNamesLength =
1945 this._functionImportsCount + this._functionsCount;
1946 const functionNames = this._functionNames.slice(0, functionNamesLength);
1947 const usedNameAt = Object.create(null);
1948 for (let i = 0; i < functionNames.length; i++) {
1949 const name = functionNames[i];
1950 if (!name) continue;
1951 const goodName =
1952 !(name in usedNameAt) &&
1953 isValidName(name) &&
1954 name.indexOf(UNKNOWN_FUNCTION_PREFIX) !== 0;
1955 if (!goodName) {
1956 if (usedNameAt[name] >= 0) {
1957 // Remove all non-unique names.
1958 functionNames[usedNameAt[name]] = null;
1959 usedNameAt[name] = -1;
1960 }
1961 functionNames[i] = null;
1962 continue;
1963 }
1964 usedNameAt[name] = i;
1965 }
1966
1967 return new NameSectionNameResolver(
1968 functionNames,
1969 this._functionLocalNames,
1970 this._eventNames,
1971 this._typeNames,
1972 this._tableNames,
1973 this._memoryNames,
1974 this._globalNames,
1975 this._fieldNames
1976 );
1977 }
1978}
1979
1980export class DevToolsNameResolver extends NameSectionNameResolver {
1981 constructor(
1982 functionNames: string[],
1983 localNames: string[][],
1984 eventNames: string[],
1985 typeNames: string[],
1986 tableNames: string[],
1987 memoryNames: string[],
1988 globalNames: string[],
1989 fieldNames: string[][]
1990 ) {
1991 super(
1992 functionNames,
1993 localNames,
1994 eventNames,
1995 typeNames,
1996 tableNames,
1997 memoryNames,
1998 globalNames,
1999 fieldNames
2000 );
2001 }
2002
2003 public override getFunctionName(
2004 index: number,
2005 isImport: boolean,
2006 isRef: boolean
2007 ): string {
2008 const name = this._functionNames[index];
2009 if (!name) return isImport ? `$import${index}` : `$func${index}`;
2010 return isRef ? `$${name}` : `$${name} (;${index};)`;
2011 }
2012}
2013
2014export class DevToolsNameGenerator {
2015 private _done = false;
2016 private _functionImportsCount = 0;
2017 private _memoryImportsCount = 0;
2018 private _tableImportsCount = 0;
2019 private _globalImportsCount = 0;
2020 private _eventImportsCount = 0;
2021
2022 private _functionNames: string[] = null;
2023 private _functionLocalNames: string[][] = null;
2024 private _eventNames: string[] = null;
2025 private _memoryNames: string[] = null;
2026 private _typeNames: string[] = null;
2027 private _tableNames: string[] = null;
2028 private _globalNames: string[] = null;
2029 private _fieldNames: string[][] = null;
2030
2031 private _functionExportNames: string[][] = null;
2032 private _globalExportNames: string[][] = null;
2033 private _memoryExportNames: string[][] = null;
2034 private _tableExportNames: string[][] = null;
2035 private _eventExportNames: string[][] = null;
2036
2037 private _addExportName(exportNames: string[][], index: number, name: string) {
2038 const names = exportNames[index];
2039 if (names) {
2040 names.push(name);
2041 } else {
2042 exportNames[index] = [name];
2043 }
2044 }
2045
2046 private _setName(
2047 names: string[],
2048 index: number,
2049 name: string,
2050 isNameSectionName: boolean
2051 ) {
2052 if (!name) return;
2053 if (isNameSectionName) {
2054 if (!isValidName(name)) return;
2055 names[index] = name;
2056 } else if (!names[index]) {
2057 names[index] = name.replace(INVALID_NAME_SYMBOLS_REGEX_GLOBAL, "_");
2058 }
2059 }
2060
2061 public read(reader: BinaryReader): boolean {
2062 if (this._done)
2063 throw new Error(
2064 "Invalid state: disassembly process was already finished."
2065 );
2066 while (true) {
2067 if (!reader.read()) return false;
2068 switch (reader.state) {
2069 case BinaryReaderState.END_WASM:
2070 if (!reader.hasMoreBytes()) {
2071 this._done = true;
2072 return true;
2073 }
2074 break;
2075 case BinaryReaderState.ERROR:
2076 throw reader.error;
2077 case BinaryReaderState.BEGIN_WASM:
2078 this._functionImportsCount = 0;
2079 this._memoryImportsCount = 0;
2080 this._tableImportsCount = 0;
2081 this._globalImportsCount = 0;
2082 this._eventImportsCount = 0;
2083
2084 this._functionNames = [];
2085 this._functionLocalNames = [];
2086 this._eventNames = [];
2087 this._memoryNames = [];
2088 this._typeNames = [];
2089 this._tableNames = [];
2090 this._globalNames = [];
2091 this._fieldNames = [];
2092 this._functionExportNames = [];
2093 this._globalExportNames = [];
2094 this._memoryExportNames = [];
2095 this._tableExportNames = [];
2096 this._eventExportNames = [];
2097 break;
2098 case BinaryReaderState.END_SECTION:
2099 break;
2100 case BinaryReaderState.BEGIN_SECTION:
2101 var sectionInfo = <ISectionInformation>reader.result;
2102
2103 if (
2104 sectionInfo.id === SectionCode.Custom &&
2105 bytesToString(sectionInfo.name) === NAME_SECTION_NAME
2106 ) {
2107 break;
2108 }
2109 switch (sectionInfo.id) {
2110 case SectionCode.Import:
2111 case SectionCode.Export:
2112 break; // reading known section;
2113 default:
2114 reader.skipSection();
2115 break;
2116 }
2117 break;
2118 case BinaryReaderState.IMPORT_SECTION_ENTRY:
2119 var importInfo = <IImportEntry>reader.result;
2120 const importName = `${bytesToString(
2121 importInfo.module
2122 )}.${bytesToString(importInfo.field)}`;
2123 switch (importInfo.kind) {
2124 case ExternalKind.Function:
2125 this._setName(
2126 this._functionNames,
2127 this._functionImportsCount++,
2128 importName,
2129 false
2130 );
2131 break;
2132 case ExternalKind.Table:
2133 this._setName(
2134 this._tableNames,
2135 this._tableImportsCount++,
2136 importName,
2137 false
2138 );
2139 break;
2140 case ExternalKind.Memory:
2141 this._setName(
2142 this._memoryNames,
2143 this._memoryImportsCount++,
2144 importName,
2145 false
2146 );
2147 break;
2148 case ExternalKind.Global:
2149 this._setName(
2150 this._globalNames,
2151 this._globalImportsCount++,
2152 importName,
2153 false
2154 );
2155 break;
2156 case ExternalKind.Event:
2157 this._setName(
2158 this._eventNames,
2159 this._eventImportsCount++,
2160 importName,
2161 false
2162 );
2163 default:
2164 throw new Error(`Unsupported export ${importInfo.kind}`);
2165 }
2166 break;
2167 case BinaryReaderState.NAME_SECTION_ENTRY:
2168 const nameInfo = <INameEntry>reader.result;
2169 if (nameInfo.type === NameType.Function) {
2170 const { names } = <IFunctionNameEntry>nameInfo;
2171 names.forEach(({ index, name }) => {
2172 this._setName(
2173 this._functionNames,
2174 index,
2175 bytesToString(name),
2176 true
2177 );
2178 });
2179 } else if (nameInfo.type === NameType.Local) {
2180 const { funcs } = <ILocalNameEntry>nameInfo;
2181 funcs.forEach(({ index, locals }) => {
2182 const localNames = (this._functionLocalNames[index] = []);
2183 locals.forEach(({ index, name }) => {
2184 localNames[index] = bytesToString(name);
2185 });
2186 });
2187 } else if (nameInfo.type === NameType.Event) {
2188 const { names } = <IEventNameEntry>nameInfo;
2189 names.forEach(({ index, name }) => {
2190 this._setName(this._eventNames, index, bytesToString(name), true);
2191 });
2192 } else if (nameInfo.type === NameType.Type) {
2193 const { names } = <ITypeNameEntry>nameInfo;
2194 names.forEach(({ index, name }) => {
2195 this._setName(this._typeNames, index, bytesToString(name), true);
2196 });
2197 } else if (nameInfo.type === NameType.Table) {
2198 const { names } = <ITableNameEntry>nameInfo;
2199 names.forEach(({ index, name }) => {
2200 this._setName(this._tableNames, index, bytesToString(name), true);
2201 });
2202 } else if (nameInfo.type === NameType.Memory) {
2203 const { names } = <IMemoryNameEntry>nameInfo;
2204 names.forEach(({ index, name }) => {
2205 this._setName(
2206 this._memoryNames,
2207 index,
2208 bytesToString(name),
2209 true
2210 );
2211 });
2212 } else if (nameInfo.type === NameType.Global) {
2213 const { names } = <IGlobalNameEntry>nameInfo;
2214 names.forEach(({ index, name }) => {
2215 this._setName(
2216 this._globalNames,
2217 index,
2218 bytesToString(name),
2219 true
2220 );
2221 });
2222 } else if (nameInfo.type === NameType.Field) {
2223 const { types } = <IFieldNameEntry>nameInfo;
2224 types.forEach(({ index, fields }) => {
2225 const fieldNames = (this._fieldNames[index] = []);
2226 fields.forEach(({ index, name }) => {
2227 fieldNames[index] = bytesToString(name);
2228 });
2229 });
2230 }
2231 break;
2232 case BinaryReaderState.EXPORT_SECTION_ENTRY:
2233 var exportInfo = <IExportEntry>reader.result;
2234 const exportName = bytesToString(exportInfo.field);
2235 switch (exportInfo.kind) {
2236 case ExternalKind.Function:
2237 this._addExportName(
2238 this._functionExportNames,
2239 exportInfo.index,
2240 exportName
2241 );
2242 this._setName(
2243 this._functionNames,
2244 exportInfo.index,
2245 exportName,
2246 false
2247 );
2248 break;
2249 case ExternalKind.Global:
2250 this._addExportName(
2251 this._globalExportNames,
2252 exportInfo.index,
2253 exportName
2254 );
2255 this._setName(
2256 this._globalNames,
2257 exportInfo.index,
2258 exportName,
2259 false
2260 );
2261 break;
2262 case ExternalKind.Memory:
2263 this._addExportName(
2264 this._memoryExportNames,
2265 exportInfo.index,
2266 exportName
2267 );
2268 this._setName(
2269 this._memoryNames,
2270 exportInfo.index,
2271 exportName,
2272 false
2273 );
2274 break;
2275 case ExternalKind.Table:
2276 this._addExportName(
2277 this._tableExportNames,
2278 exportInfo.index,
2279 exportName
2280 );
2281 this._setName(
2282 this._tableNames,
2283 exportInfo.index,
2284 exportName,
2285 false
2286 );
2287 break;
2288 case ExternalKind.Event:
2289 this._addExportName(
2290 this._eventExportNames,
2291 exportInfo.index,
2292 exportName
2293 );
2294 this._setName(
2295 this._eventNames,
2296 exportInfo.index,
2297 exportName,
2298 false
2299 );
2300 break;
2301 default:
2302 throw new Error(`Unsupported export ${exportInfo.kind}`);
2303 }
2304 break;
2305 default:
2306 throw new Error(`Expectected state: ${reader.state}`);
2307 }
2308 }
2309 }
2310
2311 public getExportMetadata(): IExportMetadata {
2312 return new DevToolsExportMetadata(
2313 this._functionExportNames,
2314 this._globalExportNames,
2315 this._memoryExportNames,
2316 this._tableExportNames,
2317 this._eventExportNames
2318 );
2319 }
2320
2321 public getNameResolver(): INameResolver {
2322 return new DevToolsNameResolver(
2323 this._functionNames,
2324 this._functionLocalNames,
2325 this._eventNames,
2326 this._typeNames,
2327 this._tableNames,
2328 this._memoryNames,
2329 this._globalNames,
2330 this._fieldNames
2331 );
2332 }
2333}