Coverage Report

Created: 2025-07-01 06:18

/src/WasmEdge/lib/driver/runtimeTool.cpp
Line
Count
Source (jump to first uncovered line)
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
    switch (FuncType.getParamTypes()[I].getCode()) {
38
0
    case TypeCode::I32: {
39
0
      const uint32_t Value =
40
0
          static_cast<uint32_t>(std::stol(Opt.Args.value()[I + 1]));
41
0
      FuncArgs.emplace_back(Value);
42
0
      FuncArgTypes.emplace_back(TypeCode::I32);
43
0
      break;
44
0
    }
45
0
    case TypeCode::I64: {
46
0
      const uint64_t Value =
47
0
          static_cast<uint64_t>(std::stoll(Opt.Args.value()[I + 1]));
48
0
      FuncArgs.emplace_back(Value);
49
0
      FuncArgTypes.emplace_back(TypeCode::I64);
50
0
      break;
51
0
    }
52
0
    case TypeCode::F32: {
53
0
      const float Value = std::stof(Opt.Args.value()[I + 1]);
54
0
      FuncArgs.emplace_back(Value);
55
0
      FuncArgTypes.emplace_back(TypeCode::F32);
56
0
      break;
57
0
    }
58
0
    case TypeCode::F64: {
59
0
      const double Value = std::stod(Opt.Args.value()[I + 1]);
60
0
      FuncArgs.emplace_back(Value);
61
0
      FuncArgTypes.emplace_back(TypeCode::F64);
62
0
      break;
63
0
    }
64
    /// TODO: FuncRef and ExternRef
65
0
    default:
66
0
      break;
67
0
    }
68
0
  }
69
0
  if (FuncType.getParamTypes().size() + 1 < Opt.Args.value().size()) {
70
0
    for (size_t I = FuncType.getParamTypes().size() + 1;
71
0
         I < Opt.Args.value().size(); ++I) {
72
0
      const uint64_t Value =
73
0
          static_cast<uint64_t>(std::stoll(Opt.Args.value()[I]));
74
0
      FuncArgs.emplace_back(Value);
75
0
      FuncArgTypes.emplace_back(TypeCode::I64);
76
0
    }
77
0
  }
78
79
0
  auto AsyncResult = VM.asyncExecute(FuncName, FuncArgs, FuncArgTypes);
80
0
  if (Timeout.has_value()) {
81
0
    if (!AsyncResult.waitUntil(*Timeout)) {
82
0
      AsyncResult.cancel();
83
0
    }
84
0
  }
85
0
  if (auto Result = AsyncResult.get()) {
86
    /// Print results.
87
0
    for (size_t I = 0; I < Result->size(); ++I) {
88
0
      switch ((*Result)[I].second.getCode()) {
89
0
      case TypeCode::I32:
90
0
        fmt::print("{}\n"sv, (*Result)[I].first.get<uint32_t>());
91
0
        break;
92
0
      case TypeCode::I64:
93
0
        fmt::print("{}\n"sv, (*Result)[I].first.get<uint64_t>());
94
0
        break;
95
0
      case TypeCode::F32:
96
0
        fmt::print("{}\n"sv, (*Result)[I].first.get<float>());
97
0
        break;
98
0
      case TypeCode::F64:
99
0
        fmt::print("{}\n"sv, (*Result)[I].first.get<double>());
100
0
        break;
101
0
      case TypeCode::V128:
102
0
        fmt::print("{}\n"sv, uint128((*Result)[I].first.get<uint128_t>()));
103
0
        break;
104
      /// TODO: FuncRef and ExternRef
105
0
      default:
106
0
        break;
107
0
      }
108
0
    }
109
0
    return EXIT_SUCCESS;
110
0
  } else {
111
    // It indicates that the execution of wasm has been aborted
112
0
    return 128 + SIGABRT;
113
0
  }
114
0
}
115
116
static int
117
ToolOnComponent(WasmEdge::VM::VM &VM, const std::string &FuncName,
118
                std::optional<std::chrono::system_clock::time_point> Timeout,
119
                struct DriverToolOptions &Opt,
120
0
                const AST::FunctionType &FuncType) noexcept {
121
0
  std::vector<ValInterface> FuncArgs;
122
0
  std::vector<ValType> FuncArgTypes;
123
124
0
  for (size_t I = 0;
125
0
       I < FuncType.getParamTypes().size() && I + 1 < Opt.Args.value().size();
126
0
       ++I) {
127
0
    switch (FuncType.getParamTypes()[I].getCode()) {
128
0
    case TypeCode::I32: {
129
0
      const uint32_t Value =
130
0
          static_cast<uint32_t>(std::stol(Opt.Args.value()[I + 1]));
131
0
      FuncArgs.emplace_back(Value);
132
0
      FuncArgTypes.emplace_back(TypeCode::I32);
133
0
      break;
134
0
    }
135
0
    case TypeCode::I64: {
136
0
      const uint64_t Value =
137
0
          static_cast<uint64_t>(std::stoll(Opt.Args.value()[I + 1]));
138
0
      FuncArgs.emplace_back(Value);
139
0
      FuncArgTypes.emplace_back(TypeCode::I64);
140
0
      break;
141
0
    }
142
0
    case TypeCode::F32: {
143
0
      const float Value = std::stof(Opt.Args.value()[I + 1]);
144
0
      FuncArgs.emplace_back(Value);
145
0
      FuncArgTypes.emplace_back(TypeCode::F32);
146
0
      break;
147
0
    }
148
0
    case TypeCode::F64: {
149
0
      const double Value = std::stod(Opt.Args.value()[I + 1]);
150
0
      FuncArgs.emplace_back(Value);
151
0
      FuncArgTypes.emplace_back(TypeCode::F64);
152
0
      break;
153
0
    }
154
0
    case TypeCode::String: {
155
      // FIXME: Alpine still unhappy with this
156
0
      ValInterface V{};
157
0
      V.emplace<std::string>(Opt.Args.value()[I + 1]);
158
0
      FuncArgs.emplace_back(V);
159
0
      FuncArgTypes.emplace_back(TypeCode::String);
160
0
      break;
161
0
    }
162
    /// TODO: FuncRef and ExternRef
163
0
    default:
164
0
      break;
165
0
    }
166
0
  }
167
0
  if (FuncType.getParamTypes().size() + 1 < Opt.Args.value().size()) {
168
0
    for (size_t I = FuncType.getParamTypes().size() + 1;
169
0
         I < Opt.Args.value().size(); ++I) {
170
0
      const uint64_t Value =
171
0
          static_cast<uint64_t>(std::stoll(Opt.Args.value()[I]));
172
0
      FuncArgs.emplace_back(Value);
173
0
      FuncArgTypes.emplace_back(TypeCode::I64);
174
0
    }
175
0
  }
176
177
0
  auto AsyncResult = VM.asyncExecute(FuncName, FuncArgs, FuncArgTypes);
178
0
  if (Timeout.has_value()) {
179
0
    if (!AsyncResult.waitUntil(*Timeout)) {
180
0
      AsyncResult.cancel();
181
0
    }
182
0
  }
183
0
  if (auto Result = AsyncResult.get()) {
184
    /// Print results.
185
0
    for (size_t I = 0; I < Result->size(); ++I) {
186
0
      switch ((*Result)[I].second.getCode()) {
187
0
      case TypeCode::I32:
188
0
        fmt::print("{}\n"sv,
189
0
                   std::get<ValVariant>((*Result)[I].first).get<uint32_t>());
190
0
        break;
191
0
      case TypeCode::I64:
192
0
        fmt::print("{}\n"sv,
193
0
                   std::get<ValVariant>((*Result)[I].first).get<uint64_t>());
194
0
        break;
195
0
      case TypeCode::F32:
196
0
        fmt::print("{}\n"sv,
197
0
                   std::get<ValVariant>((*Result)[I].first).get<float>());
198
0
        break;
199
0
      case TypeCode::F64:
200
0
        fmt::print("{}\n"sv,
201
0
                   std::get<ValVariant>((*Result)[I].first).get<double>());
202
0
        break;
203
0
      case TypeCode::V128:
204
0
        fmt::print(
205
0
            "{}\n"sv,
206
0
            uint128(std::get<ValVariant>((*Result)[I].first).get<uint128_t>()));
207
0
        break;
208
      /// TODO: FuncRef and ExternRef
209
0
      default:
210
0
        break;
211
0
      }
212
0
    }
213
0
    return EXIT_SUCCESS;
214
0
  } else {
215
    // It indicates that the execution of wasm has been aborted
216
0
    return 128 + SIGABRT;
217
0
  }
218
0
}
219
220
0
int Tool(struct DriverToolOptions &Opt) noexcept {
221
222
0
  std::ios::sync_with_stdio(false);
223
0
  Log::setInfoLoggingLevel();
224
225
0
  Configure Conf;
226
0
  if (Opt.PropAFUNIX.value()) {
227
0
    Conf.getRuntimeConfigure().setAllowAFUNIX(true);
228
0
  }
229
0
  if (Opt.PropMutGlobals.value()) {
230
0
    Conf.removeProposal(Proposal::ImportExportMutGlobals);
231
0
  }
232
0
  if (Opt.PropNonTrapF2IConvs.value()) {
233
0
    Conf.removeProposal(Proposal::NonTrapFloatToIntConversions);
234
0
  }
235
0
  if (Opt.PropSignExtendOps.value()) {
236
0
    Conf.removeProposal(Proposal::SignExtensionOperators);
237
0
  }
238
0
  if (Opt.PropMultiValue.value()) {
239
0
    Conf.removeProposal(Proposal::MultiValue);
240
0
  }
241
0
  if (Opt.PropBulkMemOps.value()) {
242
0
    Conf.removeProposal(Proposal::BulkMemoryOperations);
243
0
  }
244
0
  if (Opt.PropRefTypes.value()) {
245
0
    Conf.removeProposal(Proposal::ReferenceTypes);
246
0
  }
247
0
  if (Opt.PropSIMD.value()) {
248
0
    Conf.removeProposal(Proposal::SIMD);
249
0
  }
250
0
  if (Opt.PropRelaxedSIMD.value()) {
251
0
    Conf.addProposal(Proposal::RelaxSIMD);
252
0
  }
253
0
  if (Opt.PropMultiMem.value()) {
254
0
    Conf.addProposal(Proposal::MultiMemories);
255
0
  }
256
0
  if (Opt.PropTailCall.value()) {
257
0
    Conf.addProposal(Proposal::TailCall);
258
0
  }
259
0
  if (Opt.PropExtendConst.value()) {
260
0
    Conf.addProposal(Proposal::ExtendedConst);
261
0
  }
262
0
  if (Opt.PropThreads.value()) {
263
0
    Conf.addProposal(Proposal::Threads);
264
0
  }
265
0
  if (Opt.PropFunctionReference.value()) {
266
0
    Conf.addProposal(Proposal::FunctionReferences);
267
0
  }
268
0
  if (Opt.PropGC.value()) {
269
0
    Conf.addProposal(Proposal::GC);
270
0
    spdlog::warn("GC proposal is enabled, this is experimental."sv);
271
0
  }
272
0
  if (Opt.PropComponent.value()) {
273
0
    Conf.addProposal(Proposal::Component);
274
0
    spdlog::warn("component model is enabled, this is experimental."sv);
275
0
  }
276
0
  if (Opt.PropExceptionHandling.value()) {
277
0
    Conf.addProposal(Proposal::ExceptionHandling);
278
0
  }
279
0
  if (Opt.PropAll.value()) {
280
0
    Conf.addProposal(Proposal::MultiMemories);
281
0
    Conf.addProposal(Proposal::TailCall);
282
0
    Conf.addProposal(Proposal::ExtendedConst);
283
0
    Conf.addProposal(Proposal::Threads);
284
0
    Conf.addProposal(Proposal::GC);
285
0
    Conf.addProposal(Proposal::Component);
286
0
    spdlog::warn("GC proposal is enabled, this is experimental."sv);
287
0
    spdlog::warn("component model is enabled, this is experimental."sv);
288
0
    Conf.addProposal(Proposal::ExceptionHandling);
289
0
  }
290
291
0
  std::optional<std::chrono::system_clock::time_point> Timeout;
292
0
  if (Opt.TimeLim.value() > 0) {
293
0
    Timeout = std::chrono::system_clock::now() +
294
0
              std::chrono::milliseconds(Opt.TimeLim.value());
295
0
  }
296
0
  if (Opt.GasLim.value().size() > 0) {
297
0
    Conf.getStatisticsConfigure().setCostMeasuring(true);
298
0
    Conf.getStatisticsConfigure().setCostLimit(
299
0
        static_cast<uint32_t>(Opt.GasLim.value().back()));
300
0
  }
301
0
  if (Opt.MemLim.value().size() > 0) {
302
0
    Conf.getRuntimeConfigure().setMaxMemoryPage(
303
0
        static_cast<uint32_t>(Opt.MemLim.value().back()));
304
0
  }
305
0
  if (Opt.ConfEnableAllStatistics.value()) {
306
0
    Conf.getStatisticsConfigure().setInstructionCounting(true);
307
0
    Conf.getStatisticsConfigure().setCostMeasuring(true);
308
0
    Conf.getStatisticsConfigure().setTimeMeasuring(true);
309
0
  } else {
310
0
    if (Opt.ConfEnableInstructionCounting.value()) {
311
0
      Conf.getStatisticsConfigure().setInstructionCounting(true);
312
0
    }
313
0
    if (Opt.ConfEnableGasMeasuring.value()) {
314
0
      Conf.getStatisticsConfigure().setCostMeasuring(true);
315
0
    }
316
0
    if (Opt.ConfEnableTimeMeasuring.value()) {
317
0
      Conf.getStatisticsConfigure().setTimeMeasuring(true);
318
0
    }
319
0
  }
320
0
  if (Opt.ConfEnableJIT.value()) {
321
0
    Conf.getRuntimeConfigure().setEnableJIT(true);
322
0
    Conf.getCompilerConfigure().setOptimizationLevel(
323
0
        WasmEdge::CompilerConfigure::OptimizationLevel::O1);
324
0
  }
325
0
  if (Opt.ConfEnableCoredump.value()) {
326
0
    Conf.getRuntimeConfigure().setEnableCoredump(true);
327
0
  }
328
0
  if (Opt.ConfCoredumpWasmgdb.value()) {
329
0
    Conf.getRuntimeConfigure().setCoredumpWasmgdb(true);
330
0
  }
331
0
  if (Opt.ConfForceInterpreter.value()) {
332
0
    Conf.getRuntimeConfigure().setForceInterpreter(true);
333
0
  }
334
335
0
  for (const auto &Name : Opt.ForbiddenPlugins.value()) {
336
0
    Conf.addForbiddenPlugins(Name);
337
0
  }
338
339
0
  Conf.addHostRegistration(HostRegistration::Wasi);
340
0
  const auto InputPath =
341
0
      std::filesystem::absolute(std::filesystem::u8path(Opt.SoName.value()));
342
0
  VM::VM VM(Conf);
343
344
0
  Host::WasiModule *WasiMod = dynamic_cast<Host::WasiModule *>(
345
0
      VM.getImportModule(HostRegistration::Wasi));
346
347
0
  if (auto Result = VM.loadWasm(InputPath.u8string()); !Result) {
348
0
    return EXIT_FAILURE;
349
0
  }
350
0
  if (auto Result = VM.validate(); !Result) {
351
0
    return EXIT_FAILURE;
352
0
  }
353
0
  if (auto Result = VM.instantiate(); !Result) {
354
0
    return EXIT_FAILURE;
355
0
  }
356
357
0
  auto HasValidCommandModStartFunc = [&]() {
358
0
    bool HasStart = false;
359
0
    bool Valid = false;
360
361
0
    auto Functions = VM.getFunctionList();
362
0
    for (auto &[FuncName, Type] : Functions) {
363
0
      if (FuncName == "_start") {
364
0
        HasStart = true;
365
0
        if (Type.getReturnTypes().size() == 0 &&
366
0
            Type.getParamTypes().size() == 0) {
367
0
          Valid = true;
368
0
          break;
369
0
        }
370
0
      }
371
0
    }
372
373
    // if HasStart but not Valid, insert _start to enter reactor mode
374
0
    if (HasStart && !Valid) {
375
0
      Opt.Args.value().insert(Opt.Args.value().begin(), "_start");
376
0
    }
377
378
0
    return HasStart && Valid;
379
0
  };
380
381
0
  bool EnterCommandMode = !Opt.Reactor.value() && HasValidCommandModStartFunc();
382
383
0
  WasiMod->getEnv().init(
384
0
      Opt.Dir.value(),
385
0
      InputPath.filename()
386
0
          .replace_extension(std::filesystem::u8path("wasm"sv))
387
0
          .u8string(),
388
0
      Opt.Args.value(), Opt.Env.value());
389
390
0
  if (EnterCommandMode) {
391
    // command mode
392
0
    auto AsyncResult = VM.asyncExecute("_start"sv);
393
0
    if (Timeout.has_value()) {
394
0
      if (!AsyncResult.waitUntil(*Timeout)) {
395
0
        AsyncResult.cancel();
396
0
      }
397
0
    }
398
0
    if (auto Result = AsyncResult.get();
399
0
        Result || Result.error() == ErrCode::Value::Terminated) {
400
0
      return static_cast<int>(WasiMod->getEnv().getExitCode());
401
0
    } else {
402
      // It indicates that the execution of wasm has been aborted
403
0
      return 128 + SIGABRT;
404
0
    }
405
0
  } else {
406
    // reactor mode
407
0
    if (Opt.Args.value().empty()) {
408
0
      fmt::print(
409
0
          stderr,
410
0
          "A function name is required when reactor mode is enabled.\n"sv);
411
0
      return EXIT_FAILURE;
412
0
    }
413
0
    const auto &FuncName = Opt.Args.value().front();
414
415
0
    const auto InitFunc = "_initialize"s;
416
417
0
    bool HasInit = false;
418
0
    AST::FunctionType FuncType;
419
420
0
    for (const auto &Func : VM.getFunctionList()) {
421
0
      if (Func.first == InitFunc) {
422
0
        HasInit = true;
423
0
      } else if (Func.first == FuncName) {
424
0
        FuncType = Func.second;
425
0
      }
426
0
    }
427
428
0
    if (HasInit) {
429
0
      auto AsyncResult = VM.asyncExecute(InitFunc);
430
0
      if (Timeout.has_value()) {
431
0
        if (!AsyncResult.waitUntil(*Timeout)) {
432
0
          AsyncResult.cancel();
433
0
        }
434
0
      }
435
0
      if (auto Result = AsyncResult.get(); unlikely(!Result)) {
436
        // It indicates that the execution of wasm has been aborted
437
0
        return 128 + SIGABRT;
438
0
      }
439
0
    }
440
441
0
    if (VM.holdsModule()) {
442
0
      return ToolOnModule(VM, FuncName, Timeout, Opt, FuncType);
443
0
    } else if (VM.holdsComponent()) {
444
0
      return ToolOnComponent(VM, FuncName, Timeout, Opt, FuncType);
445
0
    } else {
446
      // which means VM has neither instantiated module nor instantiated
447
      // component
448
0
      return 128 + SIGABRT;
449
0
    }
450
0
  }
451
0
}
452
453
} // namespace Driver
454
} // namespace WasmEdge