Coverage Report

Created: 2025-12-31 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/driver/runtimeTool.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: 2019-2024 Second State INC
3
4
#include "common/configure.h"
5
#include "common/filesystem.h"
6
#include "common/spdlog.h"
7
#include "common/types.h"
8
#include "common/version.h"
9
#include "driver/tool.h"
10
#include "host/wasi/wasimodule.h"
11
#include "vm/vm.h"
12
13
#include <chrono>
14
#include <cstdint>
15
#include <cstdlib>
16
#include <optional>
17
#include <string>
18
#include <string_view>
19
#include <vector>
20
21
using namespace std::literals;
22
23
namespace WasmEdge {
24
namespace Driver {
25
26
static int
27
ToolOnModule(WasmEdge::VM::VM &VM, const std::string &FuncName,
28
             std::optional<std::chrono::system_clock::time_point> Timeout,
29
             struct DriverToolOptions &Opt,
30
0
             const AST::FunctionType &FuncType) noexcept {
31
0
  std::vector<ValVariant> FuncArgs;
32
0
  std::vector<ValType> FuncArgTypes;
33
34
0
  for (size_t I = 0;
35
0
       I < FuncType.getParamTypes().size() && I + 1 < Opt.Args.value().size();
36
0
       ++I) {
37
0
    const auto TCode = FuncType.getParamTypes()[I].getCode();
38
0
    switch (TCode) {
39
0
    case TypeCode::I32: {
40
0
      const int32_t Value =
41
0
          static_cast<int32_t>(std::stol(Opt.Args.value()[I + 1]));
42
0
      FuncArgs.emplace_back(Value);
43
0
      FuncArgTypes.emplace_back(TCode);
44
0
      break;
45
0
    }
46
0
    case TypeCode::I64: {
47
0
      const int64_t Value =
48
0
          static_cast<int64_t>(std::stoll(Opt.Args.value()[I + 1]));
49
0
      FuncArgs.emplace_back(Value);
50
0
      FuncArgTypes.emplace_back(TCode);
51
0
      break;
52
0
    }
53
0
    case TypeCode::F32: {
54
0
      const float Value = std::stof(Opt.Args.value()[I + 1]);
55
0
      FuncArgs.emplace_back(Value);
56
0
      FuncArgTypes.emplace_back(TCode);
57
0
      break;
58
0
    }
59
0
    case TypeCode::F64: {
60
0
      const double Value = std::stod(Opt.Args.value()[I + 1]);
61
0
      FuncArgs.emplace_back(Value);
62
0
      FuncArgTypes.emplace_back(TCode);
63
0
      break;
64
0
    }
65
    // TODO: FuncRef and ExternRef
66
0
    default:
67
0
      break;
68
0
    }
69
0
  }
70
0
  if (FuncType.getParamTypes().size() + 1 < Opt.Args.value().size()) {
71
0
    for (size_t I = FuncType.getParamTypes().size() + 1;
72
0
         I < Opt.Args.value().size(); ++I) {
73
0
      const uint64_t Value =
74
0
          static_cast<uint64_t>(std::stoll(Opt.Args.value()[I]));
75
0
      FuncArgs.emplace_back(Value);
76
0
      FuncArgTypes.emplace_back(TypeCode::I64);
77
0
    }
78
0
  }
79
80
0
  auto AsyncResult = VM.asyncExecute(FuncName, FuncArgs, FuncArgTypes);
81
0
  if (Timeout.has_value()) {
82
0
    if (!AsyncResult.waitUntil(*Timeout)) {
83
0
      AsyncResult.cancel();
84
0
    }
85
0
  }
86
0
  if (auto Result = AsyncResult.get()) {
87
    // Print results.
88
0
    for (size_t I = 0; I < Result->size(); ++I) {
89
0
      switch ((*Result)[I].second.getCode()) {
90
0
      case TypeCode::I32:
91
0
        fmt::print("{}\n"sv, (*Result)[I].first.get<int32_t>());
92
0
        break;
93
0
      case TypeCode::I64:
94
0
        fmt::print("{}\n"sv, (*Result)[I].first.get<int64_t>());
95
0
        break;
96
0
      case TypeCode::F32:
97
0
        fmt::print("{}\n"sv, (*Result)[I].first.get<float>());
98
0
        break;
99
0
      case TypeCode::F64:
100
0
        fmt::print("{}\n"sv, (*Result)[I].first.get<double>());
101
0
        break;
102
0
      case TypeCode::V128:
103
0
        fmt::print("{}\n"sv, uint128((*Result)[I].first.get<uint128_t>()));
104
0
        break;
105
0
      case TypeCode::Ref: {
106
0
        if ((*Result)[I].second.isFuncRefType()) {
107
0
          fmt::print("<funcref>\n"sv);
108
0
        } else if ((*Result)[I].second.isExternRefType()) {
109
0
          fmt::print("<externref>\n"sv);
110
0
        } else {
111
0
          fmt::print("<anyref>\n"sv);
112
0
        }
113
0
        break;
114
0
      }
115
0
      case TypeCode::RefNull: {
116
0
        if ((*Result)[I].second.isFuncRefType()) {
117
0
          fmt::print("<null funcref>\n"sv);
118
0
        } else if ((*Result)[I].second.isExternRefType()) {
119
0
          fmt::print("<null externref>\n"sv);
120
0
        } else {
121
0
          fmt::print("<null anyref>\n"sv);
122
0
        }
123
0
        break;
124
0
      }
125
0
      default:
126
0
        break;
127
0
      }
128
0
    }
129
0
    return EXIT_SUCCESS;
130
0
  } else {
131
    // It indicates that the execution of wasm has been aborted
132
0
    return 128 + SIGABRT;
133
0
  }
134
0
}
135
136
static int
137
ToolOnComponent(WasmEdge::VM::VM &VM, const std::string &FuncName,
138
                std::optional<std::chrono::system_clock::time_point> Timeout,
139
                struct DriverToolOptions &Opt,
140
0
                const AST::Component::FuncType &FuncType) noexcept {
141
0
  std::vector<ComponentValVariant> FuncArgs;
142
0
  std::vector<ComponentValType> FuncArgTypes;
143
144
0
  for (size_t I = 0;
145
0
       I < FuncType.getParamList().size() && I + 1 < Opt.Args.value().size();
146
0
       ++I) {
147
0
    const auto TCode = FuncType.getParamList()[I].getValType().getCode();
148
0
    switch (TCode) {
149
0
    case ComponentTypeCode::S32:
150
0
    case ComponentTypeCode::U32: {
151
0
      const uint32_t Value =
152
0
          static_cast<uint32_t>(std::stol(Opt.Args.value()[I + 1]));
153
0
      FuncArgs.emplace_back(Value);
154
0
      FuncArgTypes.emplace_back(TCode);
155
0
      break;
156
0
    }
157
0
    case ComponentTypeCode::S64:
158
0
    case ComponentTypeCode::U64: {
159
0
      const uint64_t Value =
160
0
          static_cast<uint64_t>(std::stoll(Opt.Args.value()[I + 1]));
161
0
      FuncArgs.emplace_back(Value);
162
0
      FuncArgTypes.emplace_back(TCode);
163
0
      break;
164
0
    }
165
0
    case ComponentTypeCode::F32: {
166
0
      const float Value = std::stof(Opt.Args.value()[I + 1]);
167
0
      FuncArgs.emplace_back(Value);
168
0
      FuncArgTypes.emplace_back(TCode);
169
0
      break;
170
0
    }
171
0
    case ComponentTypeCode::F64: {
172
0
      const double Value = std::stod(Opt.Args.value()[I + 1]);
173
0
      FuncArgs.emplace_back(Value);
174
0
      FuncArgTypes.emplace_back(TCode);
175
0
      break;
176
0
    }
177
0
    case ComponentTypeCode::String: {
178
0
      const std::string Value = Opt.Args.value()[I + 1];
179
0
      FuncArgs.emplace_back(Value);
180
0
      FuncArgTypes.emplace_back(TCode);
181
0
      break;
182
0
    }
183
    // TODO: COMPONENT - other types.
184
0
    default:
185
0
      break;
186
0
    }
187
0
  }
188
0
  if (FuncType.getParamList().size() + 1 < Opt.Args.value().size()) {
189
0
    for (size_t I = FuncType.getParamList().size() + 1;
190
0
         I < Opt.Args.value().size(); ++I) {
191
0
      const uint64_t Value =
192
0
          static_cast<uint64_t>(std::stoll(Opt.Args.value()[I]));
193
0
      FuncArgs.emplace_back(Value);
194
0
      FuncArgTypes.emplace_back(ComponentTypeCode::U64);
195
0
    }
196
0
  }
197
198
0
  auto AsyncResult = VM.asyncExecuteComponent(FuncName, FuncArgs, FuncArgTypes);
199
0
  if (Timeout.has_value()) {
200
0
    if (!AsyncResult.waitUntil(*Timeout)) {
201
0
      AsyncResult.cancel();
202
0
    }
203
0
  }
204
0
  if (auto Result = AsyncResult.get()) {
205
    // Print results.
206
0
    for (auto &&Val : *Result) {
207
0
      switch (Val.second.getCode()) {
208
0
      case ComponentTypeCode::S32:
209
0
        fmt::print("{}\n"sv, std::get<ValVariant>(Val.first).get<int32_t>());
210
0
        break;
211
0
      case ComponentTypeCode::U32:
212
0
        fmt::print("{}\n"sv, std::get<ValVariant>(Val.first).get<uint32_t>());
213
0
        break;
214
0
      case ComponentTypeCode::S64:
215
0
        fmt::print("{}\n"sv, std::get<ValVariant>(Val.first).get<int64_t>());
216
0
        break;
217
0
      case ComponentTypeCode::U64:
218
0
        fmt::print("{}\n"sv, std::get<ValVariant>(Val.first).get<uint64_t>());
219
0
        break;
220
0
      case ComponentTypeCode::F32:
221
0
        fmt::print("{}\n"sv, std::get<ValVariant>(Val.first).get<float>());
222
0
        break;
223
0
      case ComponentTypeCode::F64:
224
0
        fmt::print("{}\n"sv, std::get<ValVariant>(Val.first).get<double>());
225
0
        break;
226
0
      case ComponentTypeCode::String:
227
0
        fmt::print("{}\n"sv, std::get<std::string>(Val.first));
228
0
        break;
229
0
      default:
230
0
        break;
231
0
      }
232
0
    }
233
234
0
    return EXIT_SUCCESS;
235
0
  } else {
236
    // It indicates that the execution of wasm has been aborted
237
0
    return 128 + SIGABRT;
238
0
  }
239
0
}
240
241
0
int Tool(struct DriverToolOptions &Opt) noexcept {
242
0
  std::ios::sync_with_stdio(false);
243
0
  Log::setInfoLoggingLevel();
244
245
0
  Configure Conf;
246
  // WASM standard configuration has the highest priority.
247
0
  if (Opt.PropWASM1.value()) {
248
0
    Conf.setWASMStandard(Standard::WASM_1);
249
0
  }
250
0
  if (Opt.PropWASM2.value()) {
251
0
    Conf.setWASMStandard(Standard::WASM_2);
252
0
  }
253
0
  if (Opt.PropWASM3.value()) {
254
0
    Conf.setWASMStandard(Standard::WASM_3);
255
0
  }
256
257
  // Proposals adjustment.
258
0
  if (Opt.PropMutGlobals.value()) {
259
0
    Conf.removeProposal(Proposal::ImportExportMutGlobals);
260
0
  }
261
0
  if (Opt.PropNonTrapF2IConvs.value()) {
262
0
    Conf.removeProposal(Proposal::NonTrapFloatToIntConversions);
263
0
  }
264
0
  if (Opt.PropSignExtendOps.value()) {
265
0
    Conf.removeProposal(Proposal::SignExtensionOperators);
266
0
  }
267
0
  if (Opt.PropMultiValue.value()) {
268
0
    Conf.removeProposal(Proposal::MultiValue);
269
0
  }
270
0
  if (Opt.PropBulkMemOps.value()) {
271
0
    Conf.removeProposal(Proposal::BulkMemoryOperations);
272
0
  }
273
0
  if (Opt.PropSIMD.value()) {
274
0
    Conf.removeProposal(Proposal::SIMD);
275
0
  }
276
0
  if (Opt.PropTailCall.value()) {
277
0
    Conf.removeProposal(Proposal::TailCall);
278
0
  }
279
0
  if (Opt.PropExtendConst.value()) {
280
0
    Conf.removeProposal(Proposal::ExtendedConst);
281
0
  }
282
0
  if (Opt.PropMultiMem.value()) {
283
0
    Conf.removeProposal(Proposal::MultiMemories);
284
0
  }
285
0
  if (Opt.PropRelaxedSIMD.value()) {
286
0
    Conf.removeProposal(Proposal::RelaxSIMD);
287
0
  }
288
0
  if (Opt.PropExceptionHandling.value()) {
289
0
    Conf.removeProposal(Proposal::ExceptionHandling);
290
0
  }
291
  // TODO: MEMORY64 - enable the option.
292
  // if (Opt.PropMemory64.value()) {
293
  //   Conf.removeProposal(Proposal::Memory64);
294
  // }
295
296
  // Handle the proposal removal which has dependency.
297
  // The GC proposal depends on the func-ref proposal, and the func-ref proposal
298
  // depends on the ref-types proposal.
299
0
  if (Opt.PropGC.value()) {
300
0
    Conf.removeProposal(Proposal::GC);
301
0
  }
302
0
  if (Opt.PropFunctionReference.value()) {
303
    // This will automatically not work if the GC proposal not disabled.
304
0
    Conf.removeProposal(Proposal::FunctionReferences);
305
0
  }
306
0
  if (Opt.PropRefTypes.value()) {
307
    // This will automatically not work if the GC or func-ref proposal not
308
    // disabled.
309
0
    Conf.removeProposal(Proposal::ReferenceTypes);
310
0
  }
311
312
0
  if (Opt.PropThreads.value()) {
313
0
    Conf.addProposal(Proposal::Threads);
314
0
  }
315
0
  if (Opt.PropComponent.value()) {
316
0
    Conf.addProposal(Proposal::Component);
317
0
    spdlog::warn("component model is enabled, this is experimental."sv);
318
0
  }
319
0
  if (Opt.PropAll.value()) {
320
0
    Conf.setWASMStandard(Standard::WASM_3);
321
0
    Conf.addProposal(Proposal::Threads);
322
0
    spdlog::warn("component model is enabled, this is experimental."sv);
323
0
    Conf.addProposal(Proposal::Component);
324
0
  }
325
326
0
  std::optional<std::chrono::system_clock::time_point> Timeout;
327
0
  if (Opt.TimeLim.value() > 0) {
328
0
    Timeout = std::chrono::system_clock::now() +
329
0
              std::chrono::milliseconds(Opt.TimeLim.value());
330
0
  }
331
0
  if (Opt.GasLim.value().size() > 0) {
332
0
    Conf.getStatisticsConfigure().setCostMeasuring(true);
333
0
    Conf.getStatisticsConfigure().setCostLimit(
334
0
        static_cast<uint32_t>(Opt.GasLim.value().back()));
335
0
  }
336
0
  if (Opt.MemLim.value().size() > 0) {
337
0
    Conf.getRuntimeConfigure().setMaxMemoryPage(
338
0
        static_cast<uint32_t>(Opt.MemLim.value().back()));
339
0
  }
340
0
  if (Opt.ConfEnableAllStatistics.value()) {
341
0
    Conf.getStatisticsConfigure().setInstructionCounting(true);
342
0
    Conf.getStatisticsConfigure().setCostMeasuring(true);
343
0
    Conf.getStatisticsConfigure().setTimeMeasuring(true);
344
0
  } else {
345
0
    if (Opt.ConfEnableInstructionCounting.value()) {
346
0
      Conf.getStatisticsConfigure().setInstructionCounting(true);
347
0
    }
348
0
    if (Opt.ConfEnableGasMeasuring.value()) {
349
0
      Conf.getStatisticsConfigure().setCostMeasuring(true);
350
0
    }
351
0
    if (Opt.ConfEnableTimeMeasuring.value()) {
352
0
      Conf.getStatisticsConfigure().setTimeMeasuring(true);
353
0
    }
354
0
  }
355
0
  if (Opt.ConfEnableJIT.value()) {
356
0
    Conf.getRuntimeConfigure().setEnableJIT(true);
357
0
    Conf.getCompilerConfigure().setOptimizationLevel(
358
0
        WasmEdge::CompilerConfigure::OptimizationLevel::O1);
359
0
  }
360
0
  if (Opt.ConfEnableCoredump.value()) {
361
0
    Conf.getRuntimeConfigure().setEnableCoredump(true);
362
0
  }
363
0
  if (Opt.ConfCoredumpWasmgdb.value()) {
364
0
    Conf.getRuntimeConfigure().setCoredumpWasmgdb(true);
365
0
  }
366
0
  if (Opt.ConfForceInterpreter.value()) {
367
0
    Conf.getRuntimeConfigure().setForceInterpreter(true);
368
0
  }
369
0
  if (Opt.ConfAFUNIX.value()) {
370
0
    Conf.getRuntimeConfigure().setAllowAFUNIX(true);
371
0
  }
372
373
0
  for (const auto &Name : Opt.ForbiddenPlugins.value()) {
374
0
    Conf.addForbiddenPlugins(Name);
375
0
  }
376
377
0
  Conf.addHostRegistration(HostRegistration::Wasi);
378
0
  const auto InputPath =
379
0
      std::filesystem::absolute(std::filesystem::u8path(Opt.SoName.value()));
380
381
  // Create VM and get WASI module instance.
382
0
  VM::VM VM(Conf);
383
0
  Host::WasiModule *WasiMod = dynamic_cast<Host::WasiModule *>(
384
0
      VM.getImportModule(HostRegistration::Wasi));
385
386
  // Load, validate, and instantiate WASM or Component.
387
0
  if (auto Result = VM.loadWasm(InputPath.u8string()); !Result) {
388
0
    return EXIT_FAILURE;
389
0
  }
390
0
  if (auto Result = VM.validate(); !Result) {
391
0
    return EXIT_FAILURE;
392
0
  }
393
0
  if (auto Result = VM.instantiate(); !Result) {
394
0
    return EXIT_FAILURE;
395
0
  }
396
397
0
  auto HasValidCommandModStartFunc = [&]() {
398
0
    bool HasStart = false;
399
0
    bool Valid = false;
400
401
0
    auto Functions = VM.getFunctionList();
402
0
    for (auto &[FuncName, Type] : Functions) {
403
0
      if (FuncName == "_start") {
404
0
        HasStart = true;
405
0
        if (Type.getReturnTypes().size() == 0 &&
406
0
            Type.getParamTypes().size() == 0) {
407
0
          Valid = true;
408
0
          break;
409
0
        }
410
0
      }
411
0
    }
412
413
    // if HasStart but not Valid, insert _start to enter reactor mode
414
0
    if (HasStart && !Valid) {
415
0
      Opt.Args.value().insert(Opt.Args.value().begin(), "_start");
416
0
    }
417
418
0
    return HasStart && Valid;
419
0
  };
420
421
  // TODO: COMPONENT - does component start function named as "_start"?
422
0
  bool EnterCommandMode = !Opt.Reactor.value() && HasValidCommandModStartFunc();
423
424
  // Initialize WASI module.
425
0
  WasiMod->init(Opt.Dir.value(),
426
0
                InputPath.filename()
427
0
                    .replace_extension(std::filesystem::u8path("wasm"sv))
428
0
                    .u8string(),
429
0
                Opt.Args.value(), Opt.Env.value());
430
431
0
  if (EnterCommandMode) {
432
    // command mode
433
434
    // TODO: COMPONENT - currently not supported.
435
0
    auto AsyncResult = VM.asyncExecute("_start"sv);
436
0
    if (Timeout.has_value()) {
437
0
      if (!AsyncResult.waitUntil(*Timeout)) {
438
0
        AsyncResult.cancel();
439
0
      }
440
0
    }
441
0
    if (auto Result = AsyncResult.get();
442
0
        Result || Result.error() == ErrCode::Value::Terminated) {
443
0
      return static_cast<int>(WasiMod->getExitCode());
444
0
    } else {
445
      // It indicates that the execution of wasm has been aborted
446
0
      return 128 + SIGABRT;
447
0
    }
448
0
  } else {
449
    // reactor mode
450
451
    // Get the function name to invoke.
452
0
    if (Opt.Args.value().empty()) {
453
0
      fmt::print(
454
0
          stderr,
455
0
          "A function name is required when reactor mode is enabled.\n"sv);
456
0
      return EXIT_FAILURE;
457
0
    }
458
0
    const auto &FuncName = Opt.Args.value().front();
459
460
0
    if (VM.holdsModule()) {
461
      // WASM case.
462
463
      // Check the exported function name and function type first.
464
0
      const auto InitFunc = "_initialize"s;
465
0
      bool HasInit = false;
466
0
      const AST::FunctionType *FuncType = nullptr;
467
0
      for (const auto &Func : VM.getFunctionList()) {
468
0
        if (Func.first == InitFunc) {
469
          // Found the init function.
470
0
          HasInit = true;
471
0
        } else if (Func.first == FuncName) {
472
          // Found the function to invoke.
473
0
          FuncType = &Func.second;
474
0
        }
475
0
      }
476
477
      // If found initialize function, invoke it first.
478
0
      if (HasInit) {
479
0
        auto AsyncResult = VM.asyncExecute(InitFunc);
480
0
        if (Timeout.has_value()) {
481
0
          if (!AsyncResult.waitUntil(*Timeout)) {
482
0
            AsyncResult.cancel();
483
0
          }
484
0
        }
485
0
        if (auto Result = AsyncResult.get(); unlikely(!Result)) {
486
          // It indicates that the execution of wasm has been aborted.
487
0
          return 128 + SIGABRT;
488
0
        }
489
0
      }
490
0
      return ToolOnModule(VM, FuncName, Timeout, Opt, *FuncType);
491
0
    } else if (VM.holdsComponent()) {
492
      // Component case.
493
494
      // Check the exported function name and function type first.
495
0
      const AST::Component::FuncType *FuncType = nullptr;
496
0
      for (const auto &Func : VM.getComponentFunctionList()) {
497
0
        if (Func.first == FuncName) {
498
          // Found the function to invoke.
499
0
          FuncType = &Func.second;
500
0
        }
501
0
      }
502
      // TODO: COMPONENT - Check the exported function name and function type
503
      // first.
504
0
      return ToolOnComponent(VM, FuncName, Timeout, Opt, *FuncType);
505
0
    } else {
506
      // which means VM has neither instantiated module nor instantiated
507
      // component
508
      return 128 + SIGABRT;
509
0
    }
510
0
  }
511
0
}
512
513
} // namespace Driver
514
} // namespace WasmEdge