Coverage Report

Created: 2026-08-14 06:41

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 "system/fault.h"
8
#include "system/stacktrace.h"
9
10
#include <cstdint>
11
#include <utility>
12
#include <vector>
13
14
namespace WasmEdge {
15
namespace Executor {
16
17
Executor::SavedThreadLocal::SavedThreadLocal(
18
    Executor &Ex, Runtime::StackManager &StackMgr,
19
0
    const Runtime::Instance::FunctionInstance &Func) noexcept {
20
  // Prepare the execution context.
21
0
  auto *ModInst =
22
0
      const_cast<Runtime::Instance::ModuleInstance *>(Func.getModule());
23
0
  SavedThis = This;
24
0
  This = &Ex;
25
26
0
  SavedExecutionContext = ExecutionContext;
27
0
  ExecutionContext.Memories = ModInst->MemoryPtrs.data();
28
0
  ExecutionContext.MemorySizes = ModInst->MemorySizePtrs.data();
29
0
  ExecutionContext.TableRefs = ModInst->TableRefPtrs.data();
30
0
  ExecutionContext.TableSizes = ModInst->TableSizePtrs.data();
31
0
  ExecutionContext.Globals = ModInst->GlobalPtrs.data();
32
0
  ExecutionContext.Tags =
33
0
      reinterpret_cast<void *const *>(ModInst->TagInsts.data());
34
0
  ExecutionContext.PendingExnTagAddr =
35
0
      reinterpret_cast<void *const *>(&PendingExn.TagInst);
36
0
  if (Ex.Stat) {
37
0
    ExecutionContext.InstrCount = &Ex.Stat->getInstrCountRef();
38
0
    ExecutionContext.CostTable = Ex.Stat->getCostTable().data();
39
0
    ExecutionContext.Gas = &Ex.Stat->getTotalCostRef();
40
0
    ExecutionContext.GasLimit = Ex.Stat->getCostLimit();
41
0
  }
42
0
  ExecutionContext.StopToken = &Ex.StopToken;
43
0
  ExecutionContext.ModuleInst = ModInst;
44
45
0
  SavedCurrentStack = CurrentStack;
46
0
  CurrentStack = &StackMgr;
47
0
}
48
49
0
Executor::SavedThreadLocal::~SavedThreadLocal() noexcept {
50
0
  CurrentStack = SavedCurrentStack;
51
0
  ExecutionContext = SavedExecutionContext;
52
0
  This = SavedThis;
53
0
}
54
55
Expect<AST::InstrView::iterator>
56
Executor::enterFunction(Runtime::StackManager &StackMgr,
57
                        const Runtime::Instance::FunctionInstance &Func,
58
                        const AST::InstrView::iterator RetIt, bool IsTailCall,
59
0
                        bool IsNativeEntry) {
60
  // RetIt: the return position when the entered function returns.
61
62
  // Check whether interruption occurred.
63
0
  if (unlikely(StopToken.exchange(0, std::memory_order_relaxed))) {
64
0
    spdlog::error(ErrCode::Value::Interrupted);
65
0
    return Unexpect(ErrCode::Value::Interrupted);
66
0
  }
67
68
  // Get the function type for the parameter and return counts.
69
0
  const auto &FuncType = Func.getFuncType();
70
0
  const uint32_t ArgsN = static_cast<uint32_t>(FuncType.getParamTypes().size());
71
0
  const uint32_t RetsN =
72
0
      static_cast<uint32_t>(FuncType.getReturnTypes().size());
73
74
  // For the exception handler, remove the inactive handlers caused by the
75
  // branches.
76
0
  const auto Instrs = Func.getInstrs();
77
0
  if (likely(RetIt) && RetIt != Instrs.begin()) {
78
0
    StackMgr.removeInactiveHandler(RetIt - 1);
79
0
  }
80
81
0
  if (Func.isHostFunction()) {
82
    // Host function case: Push args and call function.
83
0
    auto &HostFunc = Func.getHostFunc();
84
85
    // Finalize the host module on its first host-function invocation, after
86
    // which adding host instances to it is rejected.
87
0
    if (const auto *HostModInst = Func.getModule()) {
88
0
      HostModInst->finalizeInstantiation();
89
0
    }
90
91
    // Generate CallingFrame from current frame.
92
    // The module instance will be nullptr if current frame is a dummy frame.
93
    // For this case, use the module instance of this host function.
94
0
    const auto *ModInst = StackMgr.getModule();
95
0
    if (ModInst == nullptr) {
96
0
      ModInst = Func.getModule();
97
0
    }
98
0
    Runtime::CallingFrame CallFrame(this, ModInst);
99
100
    // Push frame.
101
0
    StackMgr.pushFrame(Func.getModule(), // Module instance
102
0
                       RetIt,            // Return PC
103
0
                       ArgsN,            // Only args, no locals in stack
104
0
                       RetsN,            // Returns num
105
0
                       IsTailCall,       // For tail-call
106
0
                       IsNativeEntry     // For native entry
107
0
    );
108
109
    // Do the statistics if the statistics turned on.
110
0
    if (Stat) {
111
      // Check host function cost.
112
0
      if (unlikely(!Stat->addCost(HostFunc.getCost()))) {
113
0
        spdlog::error(ErrCode::Value::CostLimitExceeded);
114
0
        return Unexpect(ErrCode::Value::CostLimitExceeded);
115
0
      }
116
      // Start recording time of running host function.
117
0
      Stat->stopRecordWasm();
118
0
      Stat->startRecordHost();
119
0
    }
120
121
    // Call pre-host-function
122
0
    HostFuncHelper.invokePreHostFunc();
123
124
    // Run host function.
125
0
    Span<ValVariant> Args = StackMgr.getTopSpan(ArgsN);
126
0
    for (uint32_t I = 0; I < ArgsN; I++) {
127
      // For the number type cases of the arguments, the unused bits should be
128
      // erased due to the security issue.
129
0
      cleanNumericVal(Args[I], FuncType.getParamTypes()[I]);
130
0
    }
131
0
    std::vector<ValVariant> Rets(RetsN);
132
0
    auto Ret = HostFunc.run(CallFrame, std::move(Args), Rets);
133
134
    // Call post-host-function
135
0
    HostFuncHelper.invokePostHostFunc();
136
137
    // Do the statistics if the statistics turned on.
138
0
    if (Stat) {
139
      // Stop recording time of running host function.
140
0
      Stat->stopRecordHost();
141
0
      Stat->startRecordWasm();
142
0
    }
143
144
    // Check the host function execution status.
145
0
    if (!Ret) {
146
0
      if (Ret.error() == ErrCode::Value::HostFuncError ||
147
0
          Ret.error().getCategory() != ErrCategory::WASM) {
148
0
        spdlog::error(Ret.error());
149
0
      }
150
0
      return Unexpect(Ret);
151
0
    }
152
153
    // Push returns back to the stack.
154
0
    for (auto &R : Rets) {
155
0
      StackMgr.push(std::move(R));
156
0
    }
157
158
    // For host function case, the continuation will be the continuation from
159
    // the popped frame.
160
0
    return StackMgr.popFrame();
161
0
  } else if (Func.isCompiledFunction()) {
162
    // Compiled function case: Execute the function and jump to the
163
    // continuation.
164
165
    // Push frame.
166
0
    StackMgr.pushFrame(Func.getModule(), // Module instance
167
0
                       RetIt,            // Return PC
168
0
                       ArgsN,            // Only args, no locals in stack
169
0
                       RetsN,            // Returns num
170
0
                       IsTailCall,       // For tail-call
171
0
                       IsNativeEntry     // For native entry
172
0
    );
173
174
    // Prepare arguments.
175
0
    Span<ValVariant> Args = StackMgr.getTopSpan(ArgsN);
176
0
    std::vector<ValVariant> Rets(RetsN);
177
0
    SavedThreadLocal Saved(*this, StackMgr, Func);
178
179
0
    ErrCode Err;
180
0
    try {
181
      // Get symbol and execute the function.
182
0
      Fault FaultHandler;
183
0
      uint32_t Code = PREPARE_FAULT(FaultHandler);
184
0
      if (Code != 0) {
185
0
        auto InnerStackTrace = FaultHandler.stacktrace();
186
0
        {
187
0
          std::array<void *, 256> Buffer;
188
0
          auto OuterStackTrace = stackTrace(Buffer);
189
0
          while (!OuterStackTrace.empty() && !InnerStackTrace.empty() &&
190
0
                 InnerStackTrace[InnerStackTrace.size() - 1] ==
191
0
                     OuterStackTrace[OuterStackTrace.size() - 1]) {
192
0
            InnerStackTrace = InnerStackTrace.first(InnerStackTrace.size() - 1);
193
0
            OuterStackTrace = OuterStackTrace.first(OuterStackTrace.size() - 1);
194
0
          }
195
0
        }
196
0
        StackTraceSize =
197
0
            compiledStackTrace(StackMgr, InnerStackTrace, StackTrace).size();
198
0
        Err = ErrCode(static_cast<ErrCategory>(Code >> 24), Code);
199
0
      } else {
200
0
        auto &Wrapper = FuncType.getSymbol();
201
0
        Wrapper(&ExecutionContext, Func.getSymbol().get(), Args.data(),
202
0
                Rets.data());
203
0
      }
204
0
    } catch (const ErrCode &E) {
205
0
      Err = E;
206
0
    }
207
0
    if (unlikely(Err)) {
208
0
      if (Err != ErrCode::Value::Terminated) {
209
0
        spdlog::error(Err);
210
0
      }
211
0
      StackTraceSize +=
212
0
          interpreterStackTrace(
213
0
              StackMgr, Span<uint32_t>{StackTrace}.subspan(StackTraceSize))
214
0
              .size();
215
0
      return Unexpect(Err);
216
0
    }
217
218
0
    if (unlikely(PendingExn.TagInst != nullptr)) {
219
      // The exception escapes this frame: discard it, then hand off to the
220
      // native caller or continue the handler walk in the interpreter caller.
221
0
      const bool FromNative = StackMgr.isTopFrameNativeEntry();
222
      // Push the dummy results for popping the frame, then drop them because
223
      // the escaping exception produces no results.
224
0
      for (uint32_t I = 0; I < RetsN; ++I) {
225
0
        StackMgr.push(Rets[I]);
226
0
      }
227
0
      AST::InstrView::iterator ResumePC = StackMgr.popFrame();
228
0
      StackMgr.eraseValueStack(RetsN, 0);
229
0
      if (FromNative) {
230
0
        return Unexpect(ErrCode::Value::PendingException);
231
0
      }
232
0
      auto &TagInst = *PendingExn.TagInst;
233
0
      const auto *ExnInst = PendingExn.Inst;
234
0
      StackMgr.pushValVec(PendingExn.getPayload());
235
0
      PendingExn = {};
236
0
      EXPECTED_TRY(throwException(StackMgr, TagInst, ResumePC, ExnInst));
237
0
      return ResumePC + 1;
238
0
    }
239
240
    // Push returns back to the stack.
241
0
    for (uint32_t I = 0; I < Rets.size(); ++I) {
242
0
      StackMgr.push(Rets[I]);
243
0
    }
244
245
    // For compiled function case, the continuation will be the continuation
246
    // from the popped frame.
247
0
    return StackMgr.popFrame();
248
0
  } else {
249
    // WASM interpreter case: Jump to the start of the function body.
250
251
    // Push local variables into the stack.
252
0
    for (auto &Def : Func.getLocals()) {
253
0
      if (Def.second.isRefType() && !Def.second.isAbsHeapType()) {
254
        // For non-abstract heap types (concrete type indices), convert the
255
        // null ref to the abstract heap type so that ref.cast/ref.test won't
256
        // dereference a null pointer when checking the type.
257
0
        const auto &CompType = Func.getModule()
258
0
                                   ->unsafeGetType(Def.second.getTypeIndex())
259
0
                                   ->getCompositeType();
260
0
        auto BotTypeCode =
261
0
            CompType.isFunc() ? TypeCode::NullFuncRef : TypeCode::NullRef;
262
0
        RefVariant InitVal(ValType(TypeCode::RefNull, BotTypeCode));
263
0
        for (uint32_t I = 0; I < Def.first; I++) {
264
0
          StackMgr.push(InitVal);
265
0
        }
266
0
      } else {
267
0
        for (uint32_t I = 0; I < Def.first; I++) {
268
0
          StackMgr.push(ValueFromType(Def.second));
269
0
        }
270
0
      }
271
0
    }
272
273
    // Push frame.
274
    // The PC must -1 here because in the interpreter mode execution, the PC
275
    // will increase after the callee returns.
276
0
    StackMgr.pushFrame(Func.getModule(),           // Module instance
277
0
                       RetIt - 1,                  // Return PC
278
0
                       ArgsN + Func.getLocalNum(), // Arguments num + local num
279
0
                       RetsN,                      // Returns num
280
0
                       IsTailCall,                 // For tail-call
281
0
                       IsNativeEntry               // For native entry
282
0
    );
283
284
    // For the WASM interpreter case, the continuation will be the start of the
285
    // function body.
286
0
    return Instrs.begin();
287
0
  }
288
0
}
289
290
Expect<void>
291
Executor::branchToLabel(Runtime::StackManager &StackMgr,
292
                        const AST::Instruction::JumpDescriptor &JumpDesc,
293
0
                        AST::InstrView::iterator &PC) noexcept {
294
  // Check the stop token.
295
0
  if (unlikely(StopToken.exchange(0, std::memory_order_relaxed))) {
296
0
    spdlog::error(ErrCode::Value::Interrupted);
297
0
    return Unexpect(ErrCode::Value::Interrupted);
298
0
  }
299
300
0
  StackMgr.eraseValueStack(JumpDesc.StackEraseBegin, JumpDesc.StackEraseEnd);
301
  // PC needs -1 here because the PC will increase in the next iteration.
302
0
  PC += (JumpDesc.PCOffset - 1);
303
  // A branch leaves the innermost blocks without running their `end`, so the
304
  // handlers it strands are the top of the handler stack right now. Drop them
305
  // here: once a later try_table is pushed on top they are indistinguishable
306
  // from active handlers, and their stale VPos would invert the erase range in
307
  // popTopHandler. PC + 1 is the instruction being branched to.
308
0
  StackMgr.removeInactiveHandler(PC + 1);
309
0
  return {};
310
0
}
311
312
Expect<void> Executor::throwException(
313
    Runtime::StackManager &StackMgr, Runtime::Instance::TagInstance &TagInst,
314
    AST::InstrView::iterator &PC,
315
0
    const Runtime::Instance::ExceptionInstance *ExnInst) noexcept {
316
0
  StackMgr.removeInactiveHandler(PC);
317
0
  auto AssocValSize = TagInst.getTagType().getAssocValSize();
318
0
  while (true) {
319
    // Pop the top handler.
320
0
    auto Handler = StackMgr.popTopHandler(AssocValSize);
321
0
    if (!Handler.has_value()) {
322
0
      break;
323
0
    }
324
    // Checking through the catch clause.
325
0
    for (const auto &C : Handler->CatchClause) {
326
0
      if (!C.IsAll && getTagInstByIdx(StackMgr, C.TagIndex) != &TagInst) {
327
        // Specific-tag clauses require tag-address equivalence; skip the
328
        // ones that do not match.
329
0
        continue;
330
0
      }
331
0
      if (C.IsRef) {
332
        // Allocate the exception instance lazily on the first catch_ref;
333
        // reuse the one passed in by throw_ref to preserve exnref identity.
334
0
        const Runtime::Instance::ExceptionInstance *Inst = ExnInst;
335
0
        if (Inst == nullptr) {
336
0
          auto Payload = StackMgr.getTopSpan(AssocValSize);
337
0
          std::vector<ValVariant> Vec(Payload.begin(), Payload.end());
338
0
          auto *ModInst = const_cast<Runtime::Instance::ModuleInstance *>(
339
0
              StackMgr.getModule());
340
0
          Inst = ModInst->newException(&TagInst, std::move(Vec));
341
0
        }
342
0
        if (C.IsAll) {
343
0
          StackMgr.eraseValueStack(AssocValSize, 0);
344
0
        }
345
0
        StackMgr.push(
346
0
            RefVariant(ValType(TypeCode::Ref, TypeCode::ExnRef), Inst));
347
0
      } else if (C.IsAll) {
348
0
        StackMgr.eraseValueStack(AssocValSize, 0);
349
0
      }
350
      // When an exception is caught, move the PC to the try block and branch to
351
      // the label.
352
353
0
      PC = Handler->Try;
354
0
      return branchToLabel(StackMgr, C.Jump, PC);
355
0
    }
356
0
  }
357
0
  if (StackMgr.isTopFrameNativeEntry()) {
358
    // Stopped at a frame entered from the native code: record the exception
359
    // as pending and restore the stack; the native caller continues it.
360
0
    PendingExn.TagInst = &TagInst;
361
0
    PendingExn.Inst = ExnInst;
362
0
    PendingExn.setPayload(StackMgr.getTopSpan(AssocValSize));
363
    // Push the dummy results for popping the frame, then drop them because
364
    // the escaping exception produces no results.
365
0
    const uint32_t Arity = StackMgr.getFramesSpan().back().Arity;
366
0
    for (uint32_t I = 0; I < Arity; ++I) {
367
0
      StackMgr.push(ValVariant());
368
0
    }
369
0
    StackMgr.popFrame();
370
0
    StackMgr.eraseValueStack(Arity, 0);
371
0
    return Unexpect(ErrCode::Value::PendingException);
372
0
  }
373
0
  spdlog::error(ErrCode::Value::UncaughtException);
374
0
  return Unexpect(ErrCode::Value::UncaughtException);
375
0
}
376
377
Expect<void>
378
Executor::checkOffsetOverflow(const Runtime::Instance::MemoryInstance &MemInst,
379
                              const AST::Instruction &Instr, const uint64_t Val,
380
0
                              const uint64_t Size) const noexcept {
381
  // This function simply checks that the calculated offset fits in 64 bits.
382
0
  uint64_t StartOffset;
383
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
384
  if (std::numeric_limits<uint64_t>::max() - Instr.getMemoryOffset() < Val) {
385
    StartOffset = Instr.getMemoryOffset() + Val;
386
#else
387
0
  if (unlikely(
388
0
          __builtin_add_overflow(Instr.getMemoryOffset(), Val, &StartOffset))) {
389
0
#endif
390
0
    spdlog::error(ErrCode::Value::MemoryOutOfBounds);
391
0
    spdlog::error(
392
0
        ErrInfo::InfoBoundary(StartOffset, Size, MemInst.getSize(), true));
393
0
    spdlog::error(
394
0
        ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset()));
395
0
    return Unexpect(ErrCode::Value::MemoryOutOfBounds);
396
0
  }
397
0
  return {};
398
0
}
399
400
const AST::SubType *Executor::getDefTypeByIdx(Runtime::StackManager &StackMgr,
401
0
                                              const uint32_t Idx) const {
402
0
  const auto *ModInst = StackMgr.getModule();
403
  // When the top frame is a dummy frame, the instance cannot be found.
404
0
  if (unlikely(ModInst == nullptr)) {
405
0
    return nullptr;
406
0
  }
407
0
  return ModInst->unsafeGetType(Idx);
408
0
}
409
410
const WasmEdge::AST::CompositeType &
411
Executor::getCompositeTypeByIdx(Runtime::StackManager &StackMgr,
412
0
                                const uint32_t Idx) const noexcept {
413
0
  auto *DefType = getDefTypeByIdx(StackMgr, Idx);
414
0
  assuming(DefType);
415
0
  const auto &CompType = DefType->getCompositeType();
416
0
  assuming(!CompType.isFunc());
417
0
  return CompType;
418
0
}
419
420
const ValType &
421
Executor::getStructStorageTypeByIdx(Runtime::StackManager &StackMgr,
422
                                    const uint32_t Idx,
423
0
                                    const uint32_t Off) const noexcept {
424
0
  const auto &CompType = getCompositeTypeByIdx(StackMgr, Idx);
425
0
  assuming(static_cast<uint32_t>(CompType.getFieldTypes().size()) > Off);
426
0
  return CompType.getFieldTypes()[Off].getStorageType();
427
0
}
428
429
const ValType &
430
Executor::getArrayStorageTypeByIdx(Runtime::StackManager &StackMgr,
431
0
                                   const uint32_t Idx) const noexcept {
432
0
  const auto &CompType = getCompositeTypeByIdx(StackMgr, Idx);
433
0
  assuming(static_cast<uint32_t>(CompType.getFieldTypes().size()) == 1);
434
0
  return CompType.getFieldTypes()[0].getStorageType();
435
0
}
436
437
Runtime::Instance::FunctionInstance *
438
Executor::getFuncInstByIdx(Runtime::StackManager &StackMgr,
439
0
                           const uint32_t Idx) const {
440
0
  const auto *ModInst = StackMgr.getModule();
441
  // When the top frame is a dummy frame, the instance cannot be found.
442
0
  if (unlikely(ModInst == nullptr)) {
443
0
    return nullptr;
444
0
  }
445
0
  return ModInst->unsafeGetFunction(Idx);
446
0
}
447
448
Runtime::Instance::TableInstance *
449
Executor::getTabInstByIdx(Runtime::StackManager &StackMgr,
450
0
                          const uint32_t Idx) const {
451
0
  const auto *ModInst = StackMgr.getModule();
452
  // When the top frame is a dummy frame, the instance cannot be found.
453
0
  if (unlikely(ModInst == nullptr)) {
454
0
    return nullptr;
455
0
  }
456
0
  return ModInst->unsafeGetTable(Idx);
457
0
}
458
459
Runtime::Instance::MemoryInstance *
460
Executor::getMemInstByIdx(Runtime::StackManager &StackMgr,
461
0
                          const uint32_t Idx) const {
462
0
  const auto *ModInst = StackMgr.getModule();
463
  // When the top frame is a dummy frame, the instance cannot be found.
464
0
  if (unlikely(ModInst == nullptr)) {
465
0
    return nullptr;
466
0
  }
467
0
  return ModInst->unsafeGetMemory(Idx);
468
0
}
469
470
Runtime::Instance::TagInstance *
471
Executor::getTagInstByIdx(Runtime::StackManager &StackMgr,
472
0
                          const uint32_t Idx) const {
473
0
  const auto *ModInst = StackMgr.getModule();
474
  // When the top frame is a dummy frame, the instance cannot be found.
475
0
  if (unlikely(ModInst == nullptr)) {
476
0
    return nullptr;
477
0
  }
478
0
  return ModInst->unsafeGetTag(Idx);
479
0
}
480
481
Runtime::Instance::GlobalInstance *
482
Executor::getGlobInstByIdx(Runtime::StackManager &StackMgr,
483
0
                           const uint32_t Idx) const {
484
0
  const auto *ModInst = StackMgr.getModule();
485
  // When the top frame is a dummy frame, the instance cannot be found.
486
0
  if (unlikely(ModInst == nullptr)) {
487
0
    return nullptr;
488
0
  }
489
0
  return ModInst->unsafeGetGlobal(Idx);
490
0
}
491
492
Runtime::Instance::ElementInstance *
493
Executor::getElemInstByIdx(Runtime::StackManager &StackMgr,
494
0
                           const uint32_t Idx) const {
495
0
  const auto *ModInst = StackMgr.getModule();
496
  // When the top frame is a dummy frame, the instance cannot be found.
497
0
  if (unlikely(ModInst == nullptr)) {
498
0
    return nullptr;
499
0
  }
500
0
  return ModInst->unsafeGetElem(Idx);
501
0
}
502
503
Runtime::Instance::DataInstance *
504
Executor::getDataInstByIdx(Runtime::StackManager &StackMgr,
505
0
                           const uint32_t Idx) const {
506
0
  const auto *ModInst = StackMgr.getModule();
507
  // When the top frame is a dummy frame, the instance cannot be found.
508
0
  if (unlikely(ModInst == nullptr)) {
509
0
    return nullptr;
510
0
  }
511
0
  return ModInst->unsafeGetData(Idx);
512
0
}
513
514
TypeCode Executor::toBottomType(Runtime::StackManager &StackMgr,
515
0
                                const ValType &Type) const {
516
0
  if (Type.isRefType()) {
517
0
    if (Type.isAbsHeapType()) {
518
0
      switch (Type.getHeapTypeCode()) {
519
0
      case TypeCode::NullFuncRef:
520
0
      case TypeCode::FuncRef:
521
0
        return TypeCode::NullFuncRef;
522
0
      case TypeCode::NullExternRef:
523
0
      case TypeCode::ExternRef:
524
0
        return TypeCode::NullExternRef;
525
0
      case TypeCode::NullRef:
526
0
      case TypeCode::AnyRef:
527
0
      case TypeCode::EqRef:
528
0
      case TypeCode::I31Ref:
529
0
      case TypeCode::StructRef:
530
0
      case TypeCode::ArrayRef:
531
0
        return TypeCode::NullRef;
532
0
      case TypeCode::NullExnRef:
533
0
      case TypeCode::ExnRef:
534
0
        return TypeCode::NullExnRef;
535
0
      default:
536
0
        assumingUnreachable();
537
0
      }
538
0
    } else {
539
0
      const auto &CompType = StackMgr.getModule()
540
0
                                 ->unsafeGetType(Type.getTypeIndex())
541
0
                                 ->getCompositeType();
542
0
      if (CompType.isFunc()) {
543
0
        return TypeCode::NullFuncRef;
544
0
      } else {
545
0
        return TypeCode::NullRef;
546
0
      }
547
0
    }
548
0
  } else {
549
0
    return Type.getCode();
550
0
  }
551
0
}
552
553
void Executor::cleanNumericVal(ValVariant &Val,
554
0
                               const ValType &Type) const noexcept {
555
0
  if (Type.isNumType()) {
556
0
    switch (Type.getCode()) {
557
0
    case TypeCode::I32: {
558
0
      uint32_t V = Val.get<uint32_t>();
559
0
      Val.emplace<uint128_t>(static_cast<uint128_t>(0U));
560
0
      Val.emplace<uint32_t>(V);
561
0
      break;
562
0
    }
563
0
    case TypeCode::F32: {
564
0
      float V = Val.get<float>();
565
0
      Val.emplace<uint128_t>(static_cast<uint128_t>(0U));
566
0
      Val.emplace<float>(V);
567
0
      break;
568
0
    }
569
0
    case TypeCode::I64: {
570
0
      uint64_t V = Val.get<uint64_t>();
571
0
      Val.emplace<uint128_t>(static_cast<uint128_t>(0U));
572
0
      Val.emplace<uint64_t>(V);
573
0
      break;
574
0
    }
575
0
    case TypeCode::F64: {
576
0
      double V = Val.get<double>();
577
0
      Val.emplace<uint128_t>(static_cast<uint128_t>(0U));
578
0
      Val.emplace<double>(V);
579
0
      break;
580
0
    }
581
0
    default:
582
0
      break;
583
0
    }
584
0
  }
585
0
}
586
587
ValVariant Executor::packVal(const ValType &Type,
588
0
                             const ValVariant &Val) const noexcept {
589
0
  if (Type.isPackType()) {
590
0
    switch (Type.getCode()) {
591
0
    case TypeCode::I8:
592
0
      if constexpr (Endian::native == Endian::little) {
593
0
        return ValVariant(Val.get<uint32_t>() & 0xFFU);
594
      } else {
595
        return ValVariant(Val.get<uint32_t>() << 24);
596
      }
597
0
    case TypeCode::I16:
598
0
      if constexpr (Endian::native == Endian::little) {
599
0
        return ValVariant(Val.get<uint32_t>() & 0xFFFFU);
600
      } else {
601
        return ValVariant(Val.get<uint32_t>() << 16);
602
      }
603
0
    default:
604
0
      assumingUnreachable();
605
0
    }
606
0
  }
607
0
  return Val;
608
0
}
609
610
std::vector<ValVariant>
611
Executor::packVals(const ValType &Type,
612
0
                   std::vector<ValVariant> &&Vals) const noexcept {
613
0
  for (uint32_t I = 0; I < Vals.size(); I++) {
614
0
    Vals[I] = packVal(Type, Vals[I]);
615
0
  }
616
0
  return std::move(Vals);
617
0
}
618
619
ValVariant Executor::unpackVal(const ValType &Type, const ValVariant &Val,
620
0
                               bool IsSigned) const noexcept {
621
0
  if (Type.isPackType()) {
622
0
    uint32_t Num = Val.get<uint32_t>();
623
0
    switch (Type.getCode()) {
624
0
    case TypeCode::I8:
625
      if constexpr (Endian::native == Endian::big) {
626
        Num >>= 24;
627
      }
628
0
      if (IsSigned) {
629
0
        return static_cast<uint32_t>(static_cast<int8_t>(Num));
630
0
      } else {
631
0
        return static_cast<uint32_t>(static_cast<uint8_t>(Num));
632
0
      }
633
0
    case TypeCode::I16:
634
      if constexpr (Endian::native == Endian::big) {
635
        Num >>= 16;
636
      }
637
0
      if (IsSigned) {
638
0
        return static_cast<uint32_t>(static_cast<int16_t>(Num));
639
0
      } else {
640
0
        return static_cast<uint32_t>(static_cast<uint16_t>(Num));
641
0
      }
642
0
    default:
643
0
      assumingUnreachable();
644
0
    }
645
0
  }
646
0
  return Val;
647
0
}
648
649
} // namespace Executor
650
} // namespace WasmEdge