Coverage Report

Created: 2025-08-29 06:29

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