Coverage Report

Created: 2026-11-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/driver/parseTool.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "ast/module.h"
5
#include "common/configure.h"
6
#include "common/errcode.h"
7
#include "common/filesystem.h"
8
#include "common/spdlog.h"
9
#include "driver/tool.h"
10
#include "loader/filemgr.h"
11
#include "loader/loader.h"
12
13
#include <cstdint>
14
#include <cstdlib>
15
#include <map>
16
#include <string>
17
#include <string_view>
18
#include <utility>
19
20
using namespace std::literals;
21
22
namespace WasmEdge {
23
namespace Driver {
24
25
namespace {
26
27
struct NameSection {
28
  std::map<uint32_t, std::string> FuncNames;
29
  std::map<uint32_t, std::string> GlobalNames;
30
};
31
32
// Reads a name map subsection: a count of index and name pairs.
33
0
Expect<std::map<uint32_t, std::string>> readNameMap(Span<const Byte> Data) {
34
0
  FileMgr Mgr;
35
0
  EXPECTED_TRY(Mgr.setCode(Data));
36
0
  std::map<uint32_t, std::string> Map;
37
0
  EXPECTED_TRY(uint32_t Count, Mgr.readU32());
38
0
  for (uint32_t I = 0; I < Count; ++I) {
39
0
    EXPECTED_TRY(uint32_t Idx, Mgr.readU32());
40
0
    EXPECTED_TRY(std::string Name, Mgr.readName());
41
0
    Map[Idx] = std::move(Name);
42
0
  }
43
0
  return Map;
44
0
}
45
46
// Reads the function and global name subsections, each within its own size.
47
0
NameSection parseNameSection(Span<const Byte> Data) {
48
0
  NameSection NS;
49
0
  FileMgr Mgr;
50
0
  if (!Mgr.setCode(Data)) {
51
0
    return NS;
52
0
  }
53
0
  while (Mgr.getRemainSize() > 0) {
54
0
    const auto SubId = Mgr.readByte();
55
0
    const auto SubSize = Mgr.readU32();
56
0
    if (!SubId || !SubSize || *SubSize > Mgr.getRemainSize()) {
57
0
      spdlog::warn(
58
0
          "Malformed name section; the remaining names are ignored."sv);
59
0
      break;
60
0
    }
61
0
    const auto SubData = Data.subspan(Mgr.getOffset(), *SubSize);
62
0
    if (*SubId == 1 || *SubId == 7) {
63
0
      auto Res = readNameMap(SubData);
64
0
      if (!Res) {
65
0
        spdlog::warn("Malformed name subsection {}; its names are ignored."sv,
66
0
                     *SubId);
67
0
      } else if (*SubId == 1) {
68
0
        NS.FuncNames = std::move(*Res);
69
0
      } else {
70
0
        NS.GlobalNames = std::move(*Res);
71
0
      }
72
0
    }
73
0
    Mgr.seek(Mgr.getOffset() + *SubSize);
74
0
  }
75
0
  return NS;
76
0
}
77
78
0
std::string getInitExprStr(const AST::Expression &Expr) {
79
0
  const auto Instrs = Expr.getInstrs();
80
0
  if (Instrs.empty())
81
0
    return {};
82
0
  const auto &Instr = Instrs[0];
83
0
  switch (Instr.getOpCode()) {
84
0
  case OpCode::I32__const:
85
0
    return fmt::format("i32={}"sv, Instr.getNum().get<int32_t>());
86
0
  case OpCode::I64__const:
87
0
    return fmt::format("i64={}"sv, Instr.getNum().get<int64_t>());
88
0
  case OpCode::F32__const:
89
0
    return fmt::format("f32={}"sv, Instr.getNum().get<float>());
90
0
  case OpCode::F64__const:
91
0
    return fmt::format("f64={}"sv, Instr.getNum().get<double>());
92
0
  case OpCode::Global__get:
93
0
    return fmt::format("global.get {}"sv, Instr.getTargetIndex());
94
0
  case OpCode::Ref__func:
95
0
    return fmt::format("ref.func {}"sv, Instr.getTargetIndex());
96
0
  case OpCode::Ref__null:
97
0
    return fmt::format("ref.null {}"sv, Instr.getValType());
98
0
  default:
99
0
    return {};
100
0
  }
101
0
}
102
103
// Renders a single element segment entry, i.e. one of the init expressions
104
// in an element segment's `getInitExprs()` list.
105
0
std::string getElemEntryStr(const AST::Expression &Expr) {
106
0
  const auto Instrs = Expr.getInstrs();
107
0
  if (Instrs.empty())
108
0
    return "ref.null";
109
0
  const auto &Instr = Instrs[0];
110
0
  switch (Instr.getOpCode()) {
111
0
  case OpCode::Ref__func:
112
0
    return fmt::format("func[{}]"sv, Instr.getTargetIndex());
113
0
  case OpCode::Ref__null:
114
0
    return "ref.null";
115
0
  default:
116
0
    return getInitExprStr(Expr);
117
0
  }
118
0
}
119
120
} // namespace
121
122
0
int ParseTool(struct DriverToolOptions &Opt) noexcept {
123
0
  std::ios::sync_with_stdio(false);
124
125
0
  Configure Conf = createConfigure(Opt);
126
0
  const auto InputPath = getInputPath(Opt);
127
0
  if (!InputPath) {
128
0
    return EXIT_FAILURE;
129
0
  }
130
131
0
  Loader::Loader Loader(Conf);
132
0
  auto Res = Loader.parseModule(*InputPath);
133
0
  if (!Res) {
134
0
    spdlog::error("Failed to parse WASM module."sv);
135
0
    return EXIT_FAILURE;
136
0
  }
137
0
  const auto &Mod = **Res;
138
139
0
  const auto &Customs = Mod.getCustomSections();
140
0
  NameSection NS;
141
0
  for (const auto &Custom : Customs) {
142
0
    if (Custom.getName() == "name") {
143
0
      NS = parseNameSection(Custom.getContent());
144
0
      break;
145
0
    }
146
0
  }
147
148
0
  const auto &Imports = Mod.getImportSection().getContent();
149
0
  uint32_t ImportedFuncCount = 0;
150
0
  uint32_t ImportedGlobalCount = 0;
151
0
  uint32_t ImportedTableCount = 0;
152
0
  uint32_t ImportedMemCount = 0;
153
0
  uint32_t ImportedTagCount = 0;
154
0
  for (const auto &Imp : Imports) {
155
0
    if (Imp.getExternalType() == ExternalType::Function)
156
0
      ImportedFuncCount++;
157
0
    else if (Imp.getExternalType() == ExternalType::Global)
158
0
      ImportedGlobalCount++;
159
0
    else if (Imp.getExternalType() == ExternalType::Table)
160
0
      ImportedTableCount++;
161
0
    else if (Imp.getExternalType() == ExternalType::Memory)
162
0
      ImportedMemCount++;
163
0
    else if (Imp.getExternalType() == ExternalType::Tag)
164
0
      ImportedTagCount++;
165
0
  }
166
167
0
  auto FuncName = [&](uint32_t Idx) -> std::string {
168
0
    auto It = NS.FuncNames.find(Idx);
169
0
    if (It != NS.FuncNames.end())
170
0
      return fmt::format(" <{}>"sv, It->second);
171
0
    return {};
172
0
  };
173
174
0
  auto GlobalName = [&](uint32_t Idx) -> std::string {
175
0
    auto It = NS.GlobalNames.find(Idx);
176
0
    if (It != NS.GlobalNames.end())
177
0
      return fmt::format(" <{}>"sv, It->second);
178
0
    return {};
179
0
  };
180
181
0
  fmt::print("{}:  file format wasm 0x1\n\n", InputPath->filename().string());
182
0
  fmt::print("Section Details:\n\n");
183
184
  // Type Section
185
0
  const auto &Types = Mod.getTypeSection().getContent();
186
0
  if (!Types.empty()) {
187
0
    fmt::print("Type[{}]:\n", Types.size());
188
0
    for (uint32_t I = 0; I < Types.size(); I++) {
189
0
      const auto &FuncType = Types[I].getCompositeType().getFuncType();
190
0
      fmt::print(" - type[{}] (", I);
191
0
      const auto &Params = FuncType.getParamTypes();
192
0
      for (uint32_t J = 0; J < Params.size(); J++) {
193
0
        if (J > 0)
194
0
          fmt::print(", ");
195
0
        fmt::print("{}", Params[J]);
196
0
      }
197
0
      fmt::print(") -> ");
198
0
      const auto &Returns = FuncType.getReturnTypes();
199
0
      if (Returns.empty()) {
200
0
        fmt::print("nil");
201
0
      } else {
202
0
        for (uint32_t J = 0; J < Returns.size(); J++) {
203
0
          if (J > 0)
204
0
            fmt::print(", ");
205
0
          fmt::print("{}", Returns[J]);
206
0
        }
207
0
      }
208
0
      fmt::print("\n");
209
0
    }
210
0
  }
211
212
  // Import Section
213
0
  if (!Imports.empty()) {
214
0
    fmt::print("Import[{}]:\n", Imports.size());
215
0
    uint32_t ImpFuncIdx = 0;
216
0
    uint32_t ImpGlobalIdx = 0;
217
0
    uint32_t ImpMemIdx = 0;
218
0
    uint32_t ImpTableIdx = 0;
219
0
    uint32_t ImpTagIdx = 0;
220
0
    for (const auto &Imp : Imports) {
221
0
      switch (Imp.getExternalType()) {
222
0
      case ExternalType::Function:
223
0
        fmt::print(" - func[{}] sig={}{} <- {}.{}\n", ImpFuncIdx,
224
0
                   Imp.getExternalFuncTypeIdx(), FuncName(ImpFuncIdx),
225
0
                   Imp.getModuleName(), Imp.getExternalName());
226
0
        ImpFuncIdx++;
227
0
        break;
228
0
      case ExternalType::Memory: {
229
0
        const auto &Limit = Imp.getExternalMemoryType().getLimit();
230
0
        fmt::print(" - memory[{}] pages: initial={}", ImpMemIdx,
231
0
                   Limit.getMin());
232
0
        if (Limit.hasMax())
233
0
          fmt::print(" max={}", Limit.getMax());
234
0
        fmt::print(" <- {}.{}\n", Imp.getModuleName(), Imp.getExternalName());
235
0
        ImpMemIdx++;
236
0
        break;
237
0
      }
238
0
      case ExternalType::Table:
239
0
        fmt::print(" - table[{}] <- {}.{}\n", ImpTableIdx, Imp.getModuleName(),
240
0
                   Imp.getExternalName());
241
0
        ImpTableIdx++;
242
0
        break;
243
0
      case ExternalType::Global: {
244
0
        const auto &GT = Imp.getExternalGlobalType();
245
0
        fmt::print(" - global[{}] {} mutable={}{} <- {}.{}\n", ImpGlobalIdx,
246
0
                   GT.getValType(), GT.getValMut() == ValMut::Var ? 1 : 0,
247
0
                   GlobalName(ImpGlobalIdx), Imp.getModuleName(),
248
0
                   Imp.getExternalName());
249
0
        ImpGlobalIdx++;
250
0
        break;
251
0
      }
252
0
      case ExternalType::Tag:
253
0
        fmt::print(" - tag[{}] sig={} <- {}.{}\n", ImpTagIdx,
254
0
                   Imp.getExternalTagType().getTypeIdx(), Imp.getModuleName(),
255
0
                   Imp.getExternalName());
256
0
        ImpTagIdx++;
257
0
        break;
258
0
      default:
259
0
        break;
260
0
      }
261
0
    }
262
0
  }
263
264
  // Function Section
265
0
  const auto &Funcs = Mod.getFunctionSection().getContent();
266
0
  if (!Funcs.empty()) {
267
0
    fmt::print("Function[{}]:\n", Funcs.size());
268
0
    for (uint32_t I = 0; I < Funcs.size(); I++) {
269
0
      uint32_t Idx = I + ImportedFuncCount;
270
0
      fmt::print(" - func[{}] sig={}{}\n", Idx, Funcs[I], FuncName(Idx));
271
0
    }
272
0
  }
273
274
  // Table Section
275
0
  const auto &Tables = Mod.getTableSection().getContent();
276
0
  if (!Tables.empty()) {
277
0
    fmt::print("Table[{}]:\n", Tables.size());
278
0
    for (uint32_t I = 0; I < Tables.size(); I++) {
279
0
      uint32_t Idx = I + ImportedTableCount;
280
0
      const auto &TT = Tables[I].getTableType();
281
0
      const auto &Lim = TT.getLimit();
282
0
      fmt::print(" - table[{}] type={} initial={}", Idx, TT.getRefType(),
283
0
                 Lim.getMin());
284
0
      if (Lim.hasMax())
285
0
        fmt::print(" max={}", Lim.getMax());
286
0
      fmt::print("\n");
287
0
    }
288
0
  }
289
290
  // Memory Section
291
0
  const auto &Mems = Mod.getMemorySection().getContent();
292
0
  if (!Mems.empty()) {
293
0
    fmt::print("Memory[{}]:\n", Mems.size());
294
0
    for (uint32_t I = 0; I < Mems.size(); I++) {
295
0
      uint32_t Idx = I + ImportedMemCount;
296
0
      const auto &Lim = Mems[I].getLimit();
297
0
      fmt::print(" - memory[{}] pages: initial={}", Idx, Lim.getMin());
298
0
      if (Lim.hasMax())
299
0
        fmt::print(" max={}", Lim.getMax());
300
0
      fmt::print("\n");
301
0
    }
302
0
  }
303
304
  // Tag Section
305
0
  const auto &Tags = Mod.getTagSection().getContent();
306
0
  if (!Tags.empty()) {
307
0
    fmt::print("Tag[{}]:\n", Tags.size());
308
0
    for (uint32_t I = 0; I < Tags.size(); I++) {
309
0
      uint32_t Idx = I + ImportedTagCount;
310
0
      fmt::print(" - tag[{}] sig={}\n", Idx, Tags[I].getTypeIdx());
311
0
    }
312
0
  }
313
314
  // Global Section
315
0
  const auto &Globals = Mod.getGlobalSection().getContent();
316
0
  if (!Globals.empty()) {
317
0
    fmt::print("Global[{}]:\n", Globals.size());
318
0
    for (uint32_t I = 0; I < Globals.size(); I++) {
319
0
      uint32_t Idx = I + ImportedGlobalCount;
320
0
      const auto &GT = Globals[I].getGlobalType();
321
0
      std::string InitStr = getInitExprStr(Globals[I].getExpr());
322
0
      fmt::print(" - global[{}] {} mutable={}{}", Idx, GT.getValType(),
323
0
                 GT.getValMut() == ValMut::Var ? 1 : 0, GlobalName(Idx));
324
0
      if (!InitStr.empty())
325
0
        fmt::print(" - init {}", InitStr);
326
0
      fmt::print("\n");
327
0
    }
328
0
  }
329
330
  // Export Section
331
0
  const auto &Exports = Mod.getExportSection().getContent();
332
0
  if (!Exports.empty()) {
333
0
    fmt::print("Export[{}]:\n", Exports.size());
334
0
    for (const auto &Exp : Exports) {
335
0
      uint32_t Idx = Exp.getExternalIndex();
336
0
      switch (Exp.getExternalType()) {
337
0
      case ExternalType::Function:
338
0
        fmt::print(" - func[{}]{} -> \"{}\"\n", Idx, FuncName(Idx),
339
0
                   Exp.getExternalName());
340
0
        break;
341
0
      case ExternalType::Global:
342
0
        fmt::print(" - global[{}] -> \"{}\"\n", Idx, Exp.getExternalName());
343
0
        break;
344
0
      case ExternalType::Memory:
345
0
        fmt::print(" - memory[{}] -> \"{}\"\n", Idx, Exp.getExternalName());
346
0
        break;
347
0
      case ExternalType::Table:
348
0
        fmt::print(" - table[{}] -> \"{}\"\n", Idx, Exp.getExternalName());
349
0
        break;
350
0
      default:
351
0
        break;
352
0
      }
353
0
    }
354
0
  }
355
356
  // Start Section
357
0
  if (auto StartIdx = Mod.getStartSection().getContent()) {
358
0
    fmt::print("Start:\n");
359
0
    fmt::print(" - func[{}]{}\n", *StartIdx, FuncName(*StartIdx));
360
0
  }
361
362
  // Element Section
363
0
  const auto &Elems = Mod.getElementSection().getContent();
364
0
  if (!Elems.empty()) {
365
0
    fmt::print("Element[{}]:\n", Elems.size());
366
0
    for (uint32_t I = 0; I < Elems.size(); I++) {
367
0
      const auto &Elem = Elems[I];
368
0
      const auto &Entries = Elem.getInitExprs();
369
0
      switch (Elem.getMode()) {
370
0
      case AST::ElementSegment::ElemMode::Active: {
371
0
        fmt::print(" - segment[{}] flags=0 table={} type={} count={}", I,
372
0
                   Elem.getIdx(), Elem.getRefType(), Entries.size());
373
0
        std::string OffsetStr = getInitExprStr(Elem.getExpr());
374
0
        if (!OffsetStr.empty())
375
0
          fmt::print(" - init {}", OffsetStr);
376
0
        fmt::print("\n");
377
0
        break;
378
0
      }
379
0
      case AST::ElementSegment::ElemMode::Passive:
380
0
        fmt::print(" - segment[{}] flags=1 passive type={} count={}\n", I,
381
0
                   Elem.getRefType(), Entries.size());
382
0
        break;
383
0
      case AST::ElementSegment::ElemMode::Declarative:
384
0
        fmt::print(" - segment[{}] flags=3 declarative type={} count={}\n", I,
385
0
                   Elem.getRefType(), Entries.size());
386
0
        break;
387
0
      }
388
0
      for (uint32_t J = 0; J < Entries.size(); J++) {
389
0
        fmt::print("  - elem[{}] = {}\n", J, getElemEntryStr(Entries[J]));
390
0
      }
391
0
    }
392
0
  }
393
394
  // DataCount Section
395
0
  if (auto Count = Mod.getDataCountSection().getContent()) {
396
0
    fmt::print("DataCount section: {}\n", *Count);
397
0
  }
398
399
  // Code Section
400
0
  const auto &Codes = Mod.getCodeSection().getContent();
401
0
  if (!Codes.empty()) {
402
0
    fmt::print("Code[{}]:\n", Codes.size());
403
0
    for (uint32_t I = 0; I < Codes.size(); I++) {
404
0
      uint32_t Idx = I + ImportedFuncCount;
405
0
      fmt::print(" - func[{}] size={}{}\n", Idx, Codes[I].getSegSize(),
406
0
                 FuncName(Idx));
407
0
    }
408
0
  }
409
410
  // Data Section
411
0
  const auto &Datas = Mod.getDataSection().getContent();
412
0
  if (!Datas.empty()) {
413
0
    fmt::print("Data[{}]:\n", Datas.size());
414
0
    for (uint32_t I = 0; I < Datas.size(); I++) {
415
0
      const auto &Data = Datas[I];
416
0
      switch (Data.getMode()) {
417
0
      case AST::DataSegment::DataMode::Active: {
418
0
        fmt::print(" - segment[{}] memory={} size={}", I, Data.getIdx(),
419
0
                   Data.getData().size());
420
0
        std::string OffsetStr = getInitExprStr(Data.getExpr());
421
0
        if (!OffsetStr.empty())
422
0
          fmt::print(" - init {}", OffsetStr);
423
0
        fmt::print("\n");
424
0
        break;
425
0
      }
426
0
      case AST::DataSegment::DataMode::Passive:
427
0
        fmt::print(" - segment[{}] passive size={}\n", I,
428
0
                   Data.getData().size());
429
0
        break;
430
0
      }
431
0
    }
432
0
  }
433
434
  // Custom Sections
435
0
  for (const auto &Custom : Customs) {
436
0
    fmt::print("Custom:\n");
437
0
    fmt::print(" - name: \"{}\"\n", Custom.getName());
438
0
    if (Custom.getName() == "name") {
439
0
      for (const auto &[Idx, Name] : NS.FuncNames) {
440
0
        fmt::print(" - func[{}] <{}>\n", Idx, Name);
441
0
      }
442
0
      for (const auto &[Idx, Name] : NS.GlobalNames) {
443
0
        fmt::print(" - global[{}] <{}>\n", Idx, Name);
444
0
      }
445
0
    }
446
0
  }
447
448
  return EXIT_SUCCESS;
449
0
}
450
451
} // namespace Driver
452
} // namespace WasmEdge