Coverage Report

Created: 2026-06-30 06:10

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