Coverage Report

Created: 2026-11-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/executor/helper.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "executor/executor.h"
5
6
#include "common/spdlog.h"
7
#include "runtime/storemgr.h"
8
#include "system/fault.h"
9
#include "system/stacktrace.h"
10
11
#include <algorithm>
12
#include <cstdint>
13
#include <shared_mutex>
14
#include <unordered_set>
15
#include <utility>
16
#include <vector>
17
18
#if defined(_MSC_VER) && !defined(__clang__)
19
#include <intrin.h>
20
#endif
21
22
#ifndef __has_builtin
23
#define __has_builtin(x) 0
24
#endif
25
26
namespace WasmEdge {
27
namespace Executor {
28
29
Executor::SavedThreadLocal::SavedThreadLocal(
30
    Executor &Ex, Runtime::StackManager &StackMgr,
31
0
    [[maybe_unused]] const Runtime::Instance::FunctionInstance &Func) noexcept {
32
  // Prepare the execution context.
33
0
  SavedThis = This;
34
0
  This = &Ex;
35
36
0
  SavedExecutionContext = ExecutionContext;
37
0
  ExecutionContext.StopToken = &Ex.StopToken;
38
0
  ExecutionContext.PendingExnTagAddr =
39
0
      reinterpret_cast<void *const *>(&PendingExn.TagInst);
40
0
  if (Ex.Stat) {
41
0
    ExecutionContext.InstrCount = &Ex.Stat->getInstrCountRef();
42
0
    ExecutionContext.CostTable = Ex.Stat->getCostTable().data();
43
0
    ExecutionContext.Gas = &Ex.Stat->getTotalCostRef();
44
0
    ExecutionContext.GasLimit = Ex.Stat->getCostLimit();
45
0
  }
46
  // Native stack limit: this entry's budget, raised to the thread's stack
47
  // bottom plus a margin and to an outer limit on the same stack.
48
0
  {
49
0
    static constexpr const uintptr_t StackMargin = UINT64_C(64) << 10;
50
0
    uint64_t StackBudget = Ex.Conf.getRuntimeConfigure().getMaxStackSize();
51
0
    if (StackBudget == 0) {
52
0
      StackBudget = RuntimeConfigure::DefaultCompiledStackSize;
53
0
    }
54
    // Stack pointer of this entry: from the compiler where it can tell, else
55
    // the address of a local.
56
#if __has_builtin(__builtin_stack_address)
57
    const auto SP = reinterpret_cast<uintptr_t>(__builtin_stack_address());
58
#elif defined(__GNUC__) || defined(__clang__)
59
    const auto SP = reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
60
#elif defined(_MSC_VER)
61
    const auto SP = reinterpret_cast<uintptr_t>(_AddressOfReturnAddress());
62
#else
63
    volatile char StackProbe;
64
    const auto SP = reinterpret_cast<uintptr_t>(&StackProbe);
65
#endif
66
0
    uintptr_t Limit = StackBudget < SP
67
0
                          ? SP - static_cast<uintptr_t>(StackBudget)
68
0
                          : static_cast<uintptr_t>(0);
69
0
    const auto [Low, High] = getThreadStackBounds();
70
0
    const bool OnThreadStack = Low < SP && SP <= High;
71
0
    if (OnThreadStack) {
72
0
      Limit = std::max(Limit, Low + StackMargin);
73
0
    }
74
0
    const auto Outer = reinterpret_cast<uintptr_t>(ExecutionContext.StackLimit);
75
0
    if (OnThreadStack ? Low < Outer && Outer <= High
76
0
                      : Outer <= SP + StackMargin) {
77
0
      Limit = std::max(Limit, Outer);
78
0
    }
79
0
    ExecutionContext.StackLimit = reinterpret_cast<uint8_t *>(Limit);
80
0
  }
81
82
0
  SavedCurrentStack = CurrentStack;
83
0
  CurrentStack = &StackMgr;
84
0
}
85
86
0
Executor::SavedThreadLocal::~SavedThreadLocal() noexcept {
87
0
  CurrentStack = SavedCurrentStack;
88
0
  ExecutionContext = SavedExecutionContext;
89
0
  This = SavedThis;
90
0
}
91
92
Expect<AST::InstrView::iterator> Executor::enterFunction(
93
    Runtime::StackManager &StackMgr,
94
    const Runtime::Instance::FunctionInstance &Func,
95
    const AST::InstrView::iterator RetIt, bool IsTailCall, bool IsNativeEntry,
96
0
    const Runtime::Instance::ModuleInstance *CallerModInst) {
97
  // RetIt: the return position when the entered function returns.
98
99
  // Check whether interruption occurred.
100
0
  if (unlikely(StopToken.exchange(0, std::memory_order_relaxed))) {
101
0
    spdlog::error(ErrCode::Value::Interrupted);
102
0
    return Unexpect(ErrCode::Value::Interrupted);
103
0
  }
104
105
  // Bound the interpreter's heap stack, counting the frame and locals about to
106
  // be pushed; a tail call reuses the frame.
107
0
  if (!IsTailCall) {
108
0
    uint64_t StackSizeLimit = Conf.getRuntimeConfigure().getMaxStackSize();
109
0
    if (StackSizeLimit == 0) {
110
0
      StackSizeLimit = RuntimeConfigure::DefaultInterpreterStackSize;
111
0
    }
112
0
    const uint64_t LocalBytes =
113
0
        Func.isWasmFunction()
114
0
            ? static_cast<uint64_t>(Func.getLocalNum()) * sizeof(ValVariant)
115
0
            : UINT64_C(0);
116
0
    const uint64_t StackSizeNeeded =
117
0
        static_cast<uint64_t>(StackMgr.size()) * sizeof(ValVariant) +
118
0
        static_cast<uint64_t>(StackMgr.getFramesSpan().size() + 1) *
119
0
            sizeof(Runtime::StackManager::Frame) +
120
0
        LocalBytes;
121
0
    if (unlikely(StackSizeNeeded > StackSizeLimit)) {
122
0
      spdlog::error(ErrCode::Value::CallStackExhausted);
123
0
      return Unexpect(ErrCode::Value::CallStackExhausted);
124
0
    }
125
0
  }
126
127
  // Get the function type for the parameter and return counts.
128
0
  const auto &FuncType = Func.getFuncType();
129
0
  const uint32_t ArgsN = static_cast<uint32_t>(FuncType.getParamTypes().size());
130
0
  const uint32_t RetsN =
131
0
      static_cast<uint32_t>(FuncType.getReturnTypes().size());
132
133
  // For the exception handler, remove the inactive handlers caused by the
134
  // branches.
135
0
  const auto Instrs = Func.getInstrs();
136
0
  if (likely(RetIt) && RetIt != Instrs.begin()) {
137
0
    StackMgr.removeInactiveHandler(RetIt - 1);
138
0
  }
139
140
0
  if (Func.isHostFunction()) {
141
    // Host function case: Push args and call function.
142
0
    auto &HostFunc = Func.getHostFunc();
143
144
    // Finalize the host module on its first host-function invocation, after
145
    // which adding host instances to it is rejected.
146
0
    if (const auto *HostModInst = Func.getModule()) {
147
0
      HostModInst->finalizeInstantiation();
148
0
    }
149
150
    // Generate CallingFrame from current frame.
151
    // The module instance will be nullptr if current frame is a dummy frame.
152
    // For this case, use the module instance of this host function.
153
0
    const auto *ModInst = CallerModInst;
154
0
    if (ModInst == nullptr) {
155
0
      ModInst = StackMgr.getModule();
156
0
    }
157
0
    if (ModInst == nullptr) {
158
0
      ModInst = Func.getModule();
159
0
    }
160
0
    Runtime::CallingFrame CallFrame(this, ModInst);
161
162
    // Push frame.
163
0
    StackMgr.pushFrame(Func.getModule(), // Module instance
164
0
                       RetIt,            // Return PC
165
0
                       ArgsN,            // Only args, no locals in stack
166
0
                       RetsN,            // Returns num
167
0
                       IsTailCall,       // For tail-call
168
0
                       IsNativeEntry     // For native entry
169
0
    );
170
171
    // Do the statistics if the statistics turned on.
172
0
    if (Stat) {
173
      // Check host function cost.
174
0
      if (unlikely(!Stat->addCost(HostFunc.getCost()))) {
175
0
        spdlog::error(ErrCode::Value::CostLimitExceeded);
176
0
        return Unexpect(ErrCode::Value::CostLimitExceeded);
177
0
      }
178
      // Start recording time of running host function.
179
0
      Stat->stopRecordWasm();
180
0
      Stat->startRecordHost();
181
0
    }
182
183
    // Call pre-host-function
184
0
    HostFuncHelper.invokePreHostFunc();
185
186
    // Run host function.
187
0
    Span<ValVariant> Args = StackMgr.getTopSpan(ArgsN);
188
0
    for (uint32_t I = 0; I < ArgsN; I++) {
189
      // For the number type cases of the arguments, the unused bits should be
190
      // erased due to the security issue.
191
0
      cleanNumericVal(Args[I], FuncType.getParamTypes()[I]);
192
0
    }
193
0
    std::vector<ValVariant> Rets(RetsN);
194
0
    auto Ret = HostFunc.run(CallFrame, std::move(Args), Rets);
195
196
    // Call post-host-function
197
0
    HostFuncHelper.invokePostHostFunc();
198
199
    // Do the statistics if the statistics turned on.
200
0
    if (Stat) {
201
      // Stop recording time of running host function.
202
0
      Stat->stopRecordHost();
203
0
      Stat->startRecordWasm();
204
0
    }
205
206
    // Check the host function execution status.
207
0
    if (!Ret) {
208
0
      if (Ret.error() == ErrCode::Value::HostFuncError ||
209
0
          Ret.error().getCategory() != ErrCategory::WASM) {
210
0
        spdlog::error(Ret.error());
211
0
      }
212
0
      return Unexpect(Ret);
213
0
    }
214
215
    // Push returns back to the stack.
216
0
    for (auto &R : Rets) {
217
0
      StackMgr.push(std::move(R));
218
0
    }
219
220
    // A tail call pops the replaced caller's frame, whose `From` is one before
221
    // its resume point, so step it forward one instruction for `runCallOp`.
222
0
    const AST::InstrView::iterator Continuation = StackMgr.popFrame();
223
0
    return IsTailCall ? Continuation + 1 : Continuation;
224
0
  } else if (Func.isCompiledFunction()) {
225
    // Compiled function case: Execute the function and jump to the
226
    // continuation.
227
228
    // Push frame.
229
0
    StackMgr.pushFrame(Func.getModule(), // Module instance
230
0
                       RetIt,            // Return PC
231
0
                       ArgsN,            // Only args, no locals in stack
232
0
                       RetsN,            // Returns num
233
0
                       IsTailCall,       // For tail-call
234
0
                       IsNativeEntry     // For native entry
235
0
    );
236
237
    // Prepare arguments.
238
0
    Span<ValVariant> Args = StackMgr.getTopSpan(ArgsN);
239
0
    std::vector<ValVariant> Rets(RetsN);
240
0
    SavedThreadLocal Saved(*this, StackMgr, Func);
241
242
0
    ErrCode Err;
243
0
    try {
244
      // Get symbol and execute the function.
245
0
      Fault FaultHandler;
246
0
      uint32_t Code = PREPARE_FAULT(FaultHandler);
247
0
      if (Code != 0) {
248
0
        auto InnerStackTrace = FaultHandler.stacktrace();
249
0
        {
250
0
          std::array<void *, 256> Buffer;
251
0
          auto OuterStackTrace = stackTrace(Buffer);
252
0
          while (!OuterStackTrace.empty() && !InnerStackTrace.empty() &&
253
0
                 InnerStackTrace[InnerStackTrace.size() - 1] ==
254
0
                     OuterStackTrace[OuterStackTrace.size() - 1]) {
255
0
            InnerStackTrace = InnerStackTrace.first(InnerStackTrace.size() - 1);
256
0
            OuterStackTrace = OuterStackTrace.first(OuterStackTrace.size() - 1);
257
0
          }
258
0
        }
259
0
        auto LiveModules = collectLiveModules(StackMgr);
260
0
        StackTraceSize =
261
0
            compiledStackTrace(LiveModules, InnerStackTrace, StackTrace).size();
262
0
        Err = ErrCode(static_cast<ErrCategory>(Code >> 24), Code);
263
0
      } else {
264
0
        auto &Wrapper = FuncType.getSymbol();
265
0
        Wrapper(
266
0
            &const_cast<Runtime::Instance::ModuleInstance *>(Func.getModule())
267
0
                 ->ModCtx,
268
0
            &ExecutionContext, Func.getSymbol().get(), Args.data(),
269
0
            Rets.data());
270
0
      }
271
0
    } catch (const ErrCode &E) {
272
0
      Err = E;
273
0
    }
274
0
    if (unlikely(Err)) {
275
0
      if (Err != ErrCode::Value::Terminated) {
276
0
        spdlog::error(Err);
277
0
      }
278
0
      StackTraceSize +=
279
0
          interpreterStackTrace(
280
0
              StackMgr,
281
0
              Span<StackTraceEntry>{StackTrace}.subspan(StackTraceSize))
282
0
              .size();
283
0
      return Unexpect(Err);
284
0
    }
285
286
0
    if (unlikely(PendingExn.TagInst != nullptr)) {
287
      // The exception escapes this frame: discard it, then hand off to the
288
      // native caller or continue the handler walk in the interpreter caller.
289
0
      const bool FromNative = StackMgr.isTopFrameNativeEntry();
290
      // Push the dummy results for popping the frame, then drop them because
291
      // the escaping exception produces no results.
292
0
      for (uint32_t I = 0; I < RetsN; ++I) {
293
0
        StackMgr.push(Rets[I]);
294
0
      }
295
0
      AST::InstrView::iterator ResumePC = StackMgr.popFrame();
296
0
      StackMgr.eraseValueStack(RetsN, 0);
297
0
      if (FromNative) {
298
0
        return Unexpect(ErrCode::Value::PendingException);
299
0
      }
300
0
      auto &TagInst = *PendingExn.TagInst;
301
0
      const auto *ExnInst = PendingExn.Inst;
302
0
      StackMgr.pushValVec(PendingExn.getPayload());
303
0
      PendingExn = {};
304
0
      EXPECTED_TRY(throwException(StackMgr, TagInst, ResumePC, ExnInst));
305
0
      return ResumePC + 1;
306
0
    }
307
308
    // Push returns back to the stack.
309
0
    for (uint32_t I = 0; I < Rets.size(); ++I) {
310
0
      StackMgr.push(Rets[I]);
311
0
    }
312
313
    // As in the host case, step a tail-call continuation forward one
314
    // instruction for `runCallOp`.
315
0
    const AST::InstrView::iterator Continuation = StackMgr.popFrame();
316
0
    return IsTailCall ? Continuation + 1 : Continuation;
317
0
  } else {
318
    // WASM interpreter case: Jump to the start of the function body.
319
320
    // Push local variables into the stack.
321
0
    for (auto &Def : Func.getLocals()) {
322
0
      if (Def.second.isRefType()) {
323
        // Type the null refs with the bottom heap type of their hierarchy so
324
        // that ref.test/ref.cast match them against the concrete types.
325
0
        const auto BotTypeCode = toBottomType(Func.getModule(), Def.second);
326
0
        RefVariant InitVal(ValType(TypeCode::RefNull, BotTypeCode));
327
0
        for (uint32_t I = 0; I < Def.first; I++) {
328
0
          StackMgr.push(InitVal);
329
0
        }
330
0
      } else {
331
0
        for (uint32_t I = 0; I < Def.first; I++) {
332
0
          StackMgr.push(ValueFromType(Def.second));
333
0
        }
334
0
      }
335
0
    }
336
337
    // Push frame.
338
    // The PC must -1 here because in the interpreter mode execution, the PC
339
    // will increase after the callee returns.
340
0
    StackMgr.pushFrame(Func.getModule(),           // Module instance
341
0
                       RetIt - 1,                  // Return PC
342
0
                       ArgsN + Func.getLocalNum(), // Arguments num + local num
343
0
                       RetsN,                      // Returns num
344
0
                       IsTailCall,                 // For tail-call
345
0
                       IsNativeEntry               // For native entry
346
0
    );
347
348
    // For the WASM interpreter case, the continuation will be the start of the
349
    // function body.
350
0
    return Instrs.begin();
351
0
  }
352
0
}
353
354
std::vector<const Runtime::Instance::ModuleInstance *>
355
Executor::collectLiveModules(
356
0
    const Runtime::StackManager &StackMgr) const noexcept {
357
0
  std::vector<const Runtime::Instance::ModuleInstance *> Modules;
358
0
  std::unordered_set<const Runtime::Instance::ModuleInstance *> Seen;
359
0
  auto AddModule = [&](const Runtime::Instance::ModuleInstance *M) {
360
0
    if (M != nullptr && Seen.insert(M).second) {
361
0
      Modules.push_back(M);
362
0
    }
363
0
  };
364
365
0
  for (const auto &Frame : StackMgr.getFramesSpan()) {
366
0
    AddModule(Frame.Module);
367
0
  }
368
369
0
  std::unordered_set<Runtime::StoreManager *> Stores;
370
0
  auto GatherStores = [&](const Runtime::Instance::ModuleInstance *M) {
371
0
    if (M == nullptr) {
372
0
      return;
373
0
    }
374
0
    std::shared_lock Lock(M->Mutex);
375
0
    for (const auto &Entry : M->LinkedStore) {
376
0
      Stores.insert(Entry.first.first);
377
0
    }
378
0
  };
379
380
0
  const size_t SeedCount = Modules.size();
381
0
  for (size_t I = 0; I < SeedCount; ++I) {
382
0
    GatherStores(Modules[I]);
383
0
    for (const auto *Func : Modules[I]->getFunctionInstances()) {
384
0
      if (Func != nullptr) {
385
0
        GatherStores(Func->getModule());
386
0
      }
387
0
    }
388
0
  }
389
390
0
  for (auto *Store : Stores) {
391
0
    Store->getModuleList([&AddModule](const auto &NamedMod) {
392
0
      for (const auto &Entry : NamedMod) {
393
0
        AddModule(Entry.second);
394
0
      }
395
0
    });
396
0
  }
397
0
  return Modules;
398
0
}
399
400
Expect<void>
401
Executor::branchToLabel(Runtime::StackManager &StackMgr,
402
                        const AST::Instruction::JumpDescriptor &JumpDesc,
403
0
                        AST::InstrView::iterator &PC) noexcept {
404
  // Check the stop token.
405
0
  if (unlikely(StopToken.exchange(0, std::memory_order_relaxed))) {
406
0
    spdlog::error(ErrCode::Value::Interrupted);
407
0
    return Unexpect(ErrCode::Value::Interrupted);
408
0
  }
409
410
0
  StackMgr.eraseValueStack(JumpDesc.StackEraseBegin, JumpDesc.StackEraseEnd);
411
  // PC needs -1 here because the PC will increase in the next iteration.
412
0
  PC += (JumpDesc.PCOffset - 1);
413
  // A branch leaves the innermost blocks without running their `end`, so the
414
  // handlers it strands are the top of the handler stack right now. Drop them
415
  // here: once a later try_table is pushed on top they are indistinguishable
416
  // from active handlers, and their stale VPos would invert the erase range in
417
  // popTopHandler. PC + 1 is the instruction being branched to.
418
0
  StackMgr.removeInactiveHandler(PC + 1);
419
0
  return {};
420
0
}
421
422
Expect<void> Executor::throwException(
423
    Runtime::StackManager &StackMgr, Runtime::Instance::TagInstance &TagInst,
424
    AST::InstrView::iterator &PC,
425
0
    const Runtime::Instance::ExceptionInstance *ExnInst) noexcept {
426
0
  StackMgr.removeInactiveHandler(PC);
427
0
  auto AssocValSize = TagInst.getTagType().getAssocValSize();
428
0
  while (true) {
429
    // Pop the top handler.
430
0
    auto Handler = StackMgr.popTopHandler(AssocValSize);
431
0
    if (!Handler.has_value()) {
432
0
      break;
433
0
    }
434
    // Checking through the catch clause.
435
0
    for (const auto &C : Handler->CatchClause) {
436
0
      if (!C.IsAll &&
437
0
          getTagInstByIdx(StackMgr.getModule(), C.TagIndex) != &TagInst) {
438
        // Specific-tag clauses require tag-address equivalence; skip the
439
        // ones that do not match.
440
0
        continue;
441
0
      }
442
0
      if (C.IsRef) {
443
        // Allocate the exception instance lazily on the first catch_ref;
444
        // reuse the one passed in by throw_ref to preserve exnref identity.
445
0
        const Runtime::Instance::ExceptionInstance *Inst = ExnInst;
446
0
        if (Inst == nullptr) {
447
0
          auto Payload = StackMgr.getTopSpan(AssocValSize);
448
0
          std::vector<ValVariant> Vec(Payload.begin(), Payload.end());
449
0
          auto *ModInst = const_cast<Runtime::Instance::ModuleInstance *>(
450
0
              StackMgr.getModule());
451
0
          Inst = ModInst->newException(&TagInst, std::move(Vec));
452
0
        }
453
0
        if (C.IsAll) {
454
0
          StackMgr.eraseValueStack(AssocValSize, 0);
455
0
        }
456
0
        StackMgr.push(
457
0
            RefVariant(ValType(TypeCode::Ref, TypeCode::ExnRef), Inst));
458
0
      } else if (C.IsAll) {
459
0
        StackMgr.eraseValueStack(AssocValSize, 0);
460
0
      }
461
      // When an exception is caught, move the PC to the try block and branch to
462
      // the label.
463
464
0
      PC = Handler->Try;
465
0
      return branchToLabel(StackMgr, C.Jump, PC);
466
0
    }
467
0
  }
468
0
  if (StackMgr.isTopFrameNativeEntry()) {
469
    // Stopped at a frame entered from the native code: record the exception
470
    // as pending and restore the stack; the native caller continues it.
471
0
    PendingExn.TagInst = &TagInst;
472
0
    PendingExn.Inst = ExnInst;
473
0
    PendingExn.setPayload(StackMgr.getTopSpan(AssocValSize));
474
    // Push the dummy results for popping the frame, then drop them because
475
    // the escaping exception produces no results.
476
0
    const uint32_t Arity = StackMgr.getFramesSpan().back().Arity;
477
0
    for (uint32_t I = 0; I < Arity; ++I) {
478
0
      StackMgr.push(ValVariant());
479
0
    }
480
0
    StackMgr.popFrame();
481
0
    StackMgr.eraseValueStack(Arity, 0);
482
0
    return Unexpect(ErrCode::Value::PendingException);
483
0
  }
484
0
  spdlog::error(ErrCode::Value::UncaughtException);
485
0
  return Unexpect(ErrCode::Value::UncaughtException);
486
0
}
487
488
Expect<void>
489
Executor::checkOffsetOverflow(const Runtime::Instance::MemoryInstance &MemInst,
490
                              const AST::Instruction &Instr, const uint64_t Val,
491
0
                              const uint64_t Size) const noexcept {
492
  // This function simply checks that the calculated offset fits in 64 bits.
493
0
  uint64_t StartOffset;
494
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
495
  if (std::numeric_limits<uint64_t>::max() - Instr.getMemoryOffset() < Val) {
496
    StartOffset = Instr.getMemoryOffset() + Val;
497
#else
498
0
  if (unlikely(
499
0
          __builtin_add_overflow(Instr.getMemoryOffset(), Val, &StartOffset))) {
500
0
#endif
501
0
    spdlog::error(ErrCode::Value::MemoryOutOfBounds);
502
0
    spdlog::error(
503
0
        ErrInfo::InfoBoundary(StartOffset, Size, MemInst.getSize(), true));
504
0
    spdlog::error(
505
0
        ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset()));
506
0
    return Unexpect(ErrCode::Value::MemoryOutOfBounds);
507
0
  }
508
0
  return {};
509
0
}
510
511
const AST::SubType *
512
Executor::getDefTypeByIdx(const Runtime::Instance::ModuleInstance *ModInst,
513
0
                          const uint32_t Idx) const {
514
  // When the top frame is a dummy frame, the instance cannot be found.
515
0
  if (unlikely(ModInst == nullptr)) {
516
0
    return nullptr;
517
0
  }
518
0
  return ModInst->unsafeGetType(Idx);
519
0
}
520
521
const WasmEdge::AST::CompositeType &Executor::getCompositeTypeByIdx(
522
    const Runtime::Instance::ModuleInstance *ModInst,
523
0
    const uint32_t Idx) const noexcept {
524
0
  auto *DefType = getDefTypeByIdx(ModInst, Idx);
525
0
  assuming(DefType);
526
0
  const auto &CompType = DefType->getCompositeType();
527
0
  assuming(!CompType.isFunc());
528
0
  return CompType;
529
0
}
530
531
const ValType &Executor::getStructStorageTypeByIdx(
532
    const Runtime::Instance::ModuleInstance *ModInst, const uint32_t Idx,
533
0
    const uint32_t Off) const noexcept {
534
0
  const auto &CompType = getCompositeTypeByIdx(ModInst, Idx);
535
0
  assuming(static_cast<uint32_t>(CompType.getFieldTypes().size()) > Off);
536
0
  return CompType.getFieldTypes()[Off].getStorageType();
537
0
}
538
539
const ValType &Executor::getArrayStorageTypeByIdx(
540
    const Runtime::Instance::ModuleInstance *ModInst,
541
0
    const uint32_t Idx) const noexcept {
542
0
  const auto &CompType = getCompositeTypeByIdx(ModInst, Idx);
543
0
  assuming(static_cast<uint32_t>(CompType.getFieldTypes().size()) == 1);
544
0
  return CompType.getFieldTypes()[0].getStorageType();
545
0
}
546
547
Runtime::Instance::FunctionInstance *
548
Executor::getFuncInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
549
0
                           const uint32_t Idx) const {
550
  // When the top frame is a dummy frame, the instance cannot be found.
551
0
  if (unlikely(ModInst == nullptr)) {
552
0
    return nullptr;
553
0
  }
554
0
  return ModInst->unsafeGetFunction(Idx);
555
0
}
556
557
Runtime::Instance::TableInstance *
558
Executor::getTabInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
559
0
                          const uint32_t Idx) const {
560
  // When the top frame is a dummy frame, the instance cannot be found.
561
0
  if (unlikely(ModInst == nullptr)) {
562
0
    return nullptr;
563
0
  }
564
0
  return ModInst->unsafeGetTable(Idx);
565
0
}
566
567
Runtime::Instance::MemoryInstance *
568
Executor::getMemInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
569
0
                          const uint32_t Idx) const {
570
  // When the top frame is a dummy frame, the instance cannot be found.
571
0
  if (unlikely(ModInst == nullptr)) {
572
0
    return nullptr;
573
0
  }
574
0
  return ModInst->unsafeGetMemory(Idx);
575
0
}
576
577
Runtime::Instance::TagInstance *
578
Executor::getTagInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
579
0
                          const uint32_t Idx) const {
580
  // When the top frame is a dummy frame, the instance cannot be found.
581
0
  if (unlikely(ModInst == nullptr)) {
582
0
    return nullptr;
583
0
  }
584
0
  return ModInst->unsafeGetTag(Idx);
585
0
}
586
587
Runtime::Instance::GlobalInstance *
588
Executor::getGlobInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
589
0
                           const uint32_t Idx) const {
590
  // When the top frame is a dummy frame, the instance cannot be found.
591
0
  if (unlikely(ModInst == nullptr)) {
592
0
    return nullptr;
593
0
  }
594
0
  return ModInst->unsafeGetGlobal(Idx);
595
0
}
596
597
Runtime::Instance::ElementInstance *
598
Executor::getElemInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
599
0
                           const uint32_t Idx) const {
600
  // When the top frame is a dummy frame, the instance cannot be found.
601
0
  if (unlikely(ModInst == nullptr)) {
602
0
    return nullptr;
603
0
  }
604
0
  return ModInst->unsafeGetElem(Idx);
605
0
}
606
607
Runtime::Instance::DataInstance *
608
Executor::getDataInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
609
0
                           const uint32_t Idx) const {
610
  // When the top frame is a dummy frame, the instance cannot be found.
611
0
  if (unlikely(ModInst == nullptr)) {
612
0
    return nullptr;
613
0
  }
614
0
  return ModInst->unsafeGetData(Idx);
615
0
}
616
617
TypeCode
618
Executor::toBottomType(const Runtime::Instance::ModuleInstance *ModInst,
619
0
                       const ValType &Type) const {
620
0
  if (!Type.isRefType()) {
621
0
    return Type.getCode();
622
0
  }
623
0
  return AST::TypeMatcher::getBottomHeapType(
624
0
      Type.isAbsHeapType() ? Type.getHeapTypeCode()
625
0
                           : ModInst->unsafeGetType(Type.getTypeIndex())
626
0
                                 ->getCompositeType()
627
0
                                 .expand());
628
0
}
629
630
bool Executor::matchRef(const Runtime::Instance::ModuleInstance *ModInst,
631
                        const ValType &Exp,
632
0
                        const RefVariant &Ref) const noexcept {
633
  // Match the own type of the referenced instance.
634
0
  auto VT = Ref.getType();
635
0
  Span<const AST::SubType *const> GotTypeList = ModInst->getTypeList();
636
0
  const AST::SubType *HostType = nullptr;
637
0
  if (VT.isExternalized()) {
638
0
    VT = ValType(VT.isNullableRefType() ? TypeCode::RefNull : TypeCode::Ref,
639
0
                 TypeCode::ExternRef);
640
0
  } else if (!VT.isAbsHeapType()) {
641
    // Null references have abstract heap types.
642
0
    const auto *Inst = Ref.getPtr<Runtime::Instance::CompositeBase>();
643
0
    assuming(Inst);
644
0
    if (Inst->getModule()) {
645
0
      GotTypeList = Inst->getModule()->getTypeList();
646
0
    }
647
0
  } else if (VT.getHeapTypeCode() == TypeCode::FuncRef && !Ref.isNull()) {
648
0
    const auto *FuncInst = retrieveFuncRef(Ref);
649
0
    if (FuncInst->getModule()) {
650
0
      GotTypeList = FuncInst->getModule()->getTypeList();
651
0
      VT = ValType(TypeCode::Ref, FuncInst->getTypeIndex());
652
0
    } else {
653
      // A host function out of any module is a type list of its own.
654
0
      HostType = &FuncInst->getHostFunc().getDefinedType();
655
0
      GotTypeList = Span<const AST::SubType *const>(&HostType, 1);
656
0
      VT = ValType(TypeCode::Ref, 0U);
657
0
    }
658
0
  }
659
0
  return AST::TypeMatcher::matchType(ModInst->getTypeList(), Exp, GotTypeList,
660
0
                                     VT);
661
0
}
662
663
void Executor::cleanNumericVal(ValVariant &Val,
664
0
                               const ValType &Type) const noexcept {
665
0
  if (Type.isNumType()) {
666
0
    switch (Type.getCode()) {
667
0
    case TypeCode::I32: {
668
0
      uint32_t V = Val.get<uint32_t>();
669
0
      Val.emplace<uint128_t>(static_cast<uint128_t>(0U));
670
0
      Val.emplace<uint32_t>(V);
671
0
      break;
672
0
    }
673
0
    case TypeCode::F32: {
674
0
      float V = Val.get<float>();
675
0
      Val.emplace<uint128_t>(static_cast<uint128_t>(0U));
676
0
      Val.emplace<float>(V);
677
0
      break;
678
0
    }
679
0
    case TypeCode::I64: {
680
0
      uint64_t V = Val.get<uint64_t>();
681
0
      Val.emplace<uint128_t>(static_cast<uint128_t>(0U));
682
0
      Val.emplace<uint64_t>(V);
683
0
      break;
684
0
    }
685
0
    case TypeCode::F64: {
686
0
      double V = Val.get<double>();
687
0
      Val.emplace<uint128_t>(static_cast<uint128_t>(0U));
688
0
      Val.emplace<double>(V);
689
0
      break;
690
0
    }
691
0
    default:
692
0
      break;
693
0
    }
694
0
  }
695
0
}
696
697
ValVariant Executor::packVal(const ValType &Type,
698
0
                             const ValVariant &Val) const noexcept {
699
0
  if (Type.isPackType()) {
700
0
    switch (Type.getCode()) {
701
0
    case TypeCode::I8:
702
0
      if constexpr (Endian::native == Endian::little) {
703
0
        return ValVariant(Val.get<uint32_t>() & 0xFFU);
704
      } else {
705
        return ValVariant(Val.get<uint32_t>() << 24);
706
      }
707
0
    case TypeCode::I16:
708
0
      if constexpr (Endian::native == Endian::little) {
709
0
        return ValVariant(Val.get<uint32_t>() & 0xFFFFU);
710
      } else {
711
        return ValVariant(Val.get<uint32_t>() << 16);
712
      }
713
0
    default:
714
0
      assumingUnreachable();
715
0
    }
716
0
  }
717
0
  return Val;
718
0
}
719
720
std::vector<ValVariant>
721
Executor::packVals(const ValType &Type,
722
0
                   std::vector<ValVariant> &&Vals) const noexcept {
723
0
  for (uint32_t I = 0; I < Vals.size(); I++) {
724
0
    Vals[I] = packVal(Type, Vals[I]);
725
0
  }
726
0
  return std::move(Vals);
727
0
}
728
729
ValVariant Executor::unpackVal(const ValType &Type, const ValVariant &Val,
730
0
                               bool IsSigned) const noexcept {
731
0
  if (Type.isPackType()) {
732
0
    uint32_t Num = Val.get<uint32_t>();
733
0
    switch (Type.getCode()) {
734
0
    case TypeCode::I8:
735
      if constexpr (Endian::native == Endian::big) {
736
        Num >>= 24;
737
      }
738
0
      if (IsSigned) {
739
0
        return static_cast<uint32_t>(static_cast<int8_t>(Num));
740
0
      } else {
741
0
        return static_cast<uint32_t>(static_cast<uint8_t>(Num));
742
0
      }
743
0
    case TypeCode::I16:
744
      if constexpr (Endian::native == Endian::big) {
745
        Num >>= 16;
746
      }
747
0
      if (IsSigned) {
748
0
        return static_cast<uint32_t>(static_cast<int16_t>(Num));
749
0
      } else {
750
0
        return static_cast<uint32_t>(static_cast<uint16_t>(Num));
751
0
      }
752
0
    default:
753
0
      assumingUnreachable();
754
0
    }
755
0
  }
756
0
  return Val;
757
0
}
758
759
} // namespace Executor
760
} // namespace WasmEdge