Coverage Report

Created: 2026-09-28 06:22

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={}"sv, Instr.getNum().get<int32_t>());
89
0
  case OpCode::I64__const:
90
0
    return fmt::format("i64={}"sv, Instr.getNum().get<int64_t>());
91
0
  case OpCode::F32__const:
92
0
    return fmt::format("f32={}"sv, Instr.getNum().get<float>());
93
0
  case OpCode::F64__const:
94
0
    return fmt::format("f64={}"sv, Instr.getNum().get<double>());
95
0
  case OpCode::Global__get:
96
0
    return fmt::format("global.get {}"sv, Instr.getTargetIndex());
97
0
  case OpCode::Ref__func:
98
0
    return fmt::format("ref.func {}"sv, Instr.getTargetIndex());
99
0
  case OpCode::Ref__null:
100
0
    return fmt::format("ref.null {}"sv, 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[{}]"sv, 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 = std::filesystem::absolute(u8path(Opt.SoName.value()));
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