// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef TEST_COMMON_WASM_WASM_RUN_UTILS_H
#define TEST_COMMON_WASM_WASM_RUN_UTILS_H

#include <setjmp.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <array>
#include <memory>

#include "src/base/utils/random-number-generator.h"
#include "src/compiler/compiler-source-position-table.h"
#include "src/compiler/int64-lowering.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node.h"
#include "src/compiler/wasm-compiler.h"
#include "src/objects/object-conversions-inl.h"
#include "src/trap-handler/trap-handler.h"
#include "src/wasm/canonical-types.h"
#include "src/wasm/function-body-decoder.h"
#include "src/wasm/local-decl-encoder.h"
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-external-refs.h"
#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/wasm/wasm-tier.h"
#include "src/zone/accounting-allocator.h"
#include "src/zone/zone.h"
#include "test/common/c-signature.h"
#include "test/common/value-helper.h"
#include "test/common/wasm/flag-utils.h"

#if V8_ENABLE_DRUMBRAKE
#include "src/wasm/interpreter/wasm-interpreter.h"
#endif  // V8_ENABLE_DRUMBRAKE

namespace v8::internal::wasm {

enum class TestExecutionTier : int8_t {
#if V8_ENABLE_DRUMBRAKE
  kInterpreter = static_cast<int8_t>(ExecutionTier::kInterpreter),
#endif  // V8_ENABLE_DRUMBRAKE
  kLiftoff = static_cast<int8_t>(ExecutionTier::kLiftoff),
  kTurbofan = static_cast<int8_t>(ExecutionTier::kTurbofan),
  kLiftoffForFuzzing
};
static_assert(std::is_same_v<std::underlying_type_t<ExecutionTier>,
                             std::underlying_type_t<TestExecutionTier>>,
              "enum types match");

using base::ReadLittleEndianValue;
using base::WriteLittleEndianValue;

constexpr uint32_t kMaxFunctions = 10;
constexpr uint32_t kMaxGlobalsSize = 128;
// Don't execute more than 16k steps.
constexpr int kMaxNumSteps = 16 * 1024;

using compiler::CallDescriptor;
using compiler::MachineTypeForC;
using compiler::Node;

// TODO(titzer): check traps more robustly in tests.
// Currently, in tests, we just return 0xDEADBEEF from the function in which
// the trap occurs if the runtime context is not available to throw a JavaScript
// exception.
#define CHECK_TRAP32(x) \
  CHECK_EQ(0xDEADBEEF, (base::bit_cast<uint32_t>(x)) & 0xFFFFFFFF)
#define CHECK_TRAP64(x)        \
  CHECK_EQ(0xDEADBEEFDEADBEEF, \
           (base::bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF)
#define CHECK_TRAP(x) CHECK_TRAP32(x)

#define WASM_WRAPPER_RETURN_VALUE 8754

#define ADD_CODE(vec, ...)                           \
  do {                                               \
    uint8_t __buf[] = {__VA_ARGS__};                 \
    for (size_t __i = 0; __i < sizeof(__buf); __i++) \
      vec.push_back(__buf[__i]);                     \
  } while (false)

// For tests that must manually import a JSFunction with source code.
struct ManuallyImportedJSFunction {
  const FunctionSig* sig;
  DirectHandle<JSFunction> js_function;
};

// Helper Functions.
bool IsSameNan(uint16_t expected, uint16_t actual);
bool IsSameNan(float expected, float actual);
bool IsSameNan(double expected, double actual);

// A Wasm module builder. Globals are pre-set, however, memory and code may be
// progressively added by a test. In turn, we piecemeal update the runtime
// objects, i.e. {WasmInstanceObject} and {WasmModuleObject}.
class TestingModuleBuilder {
 public:
  TestingModuleBuilder(Zone*, ManuallyImportedJSFunction*, TestExecutionTier,
                       Isolate* isolate);
  ~TestingModuleBuilder();

  WasmModule* module() const { return module_.get(); }

  uint8_t* AddMemory(uint32_t size, SharedFlag shared = SharedFlag{false},
                     AddressType address_type = wasm::AddressType::kI32,
                     std::optional<size_t> max_size = {});

  size_t CodeTableLength() const { return native_module_->num_functions(); }

  template <typename T>
  T* AddMemoryElems(uint32_t count,
                    AddressType address_type = wasm::AddressType::kI32) {
    AddMemory(count * sizeof(T), SharedFlag{false}, address_type);
    return raw_mem_start<T>();
  }

  const WasmGlobal* AddGlobal(ValueType type);

  template <typename T>
  const WasmGlobal* AddGlobal() {
    return AddGlobal(MachineTypeForC<T>());
  }

  WasmValue ReadGlobal(const WasmGlobal& g) const {
    return trusted_instance_data_->GetGlobalValue(isolate_, g);
  }

  void WriteGlobal(const WasmGlobal& g, WasmValue value) const {
    DCHECK(!g.type.is_ref());
    DisallowGarbageCollection no_gc;
    Address storage = trusted_instance_data_->GetGlobalStorage(g, no_gc);
    switch (g.type.kind()) {
#define CASE_TYPE(valuetype, ctype)                               \
  case wasm::valuetype:                                           \
    base::WriteUnalignedValue<ctype>(storage, value.to<ctype>()); \
    return;
      FOREACH_WASMVALUE_CTYPES(CASE_TYPE)
#undef CASE_TYPE
      default:
        UNREACHABLE();
    }
  }

  // TODO(14034): Allow selecting type finality.
  ModuleTypeIndex AddSignature(const FunctionSig* sig) {
    const bool is_final = true;
    module_->AddSignatureForTesting(sig, kNoSuperType, is_final,
                                    SharedFlag{false});
    GetTypeCanonicalizer()->AddRecursiveGroup(module_.get(), 1);
    size_t size = module_->types.size();
    // The {ModuleTypeIndex} can handle more, but users of this class
    // often assume that each generated index fits into a byte, so
    // ensure that here.
    CHECK_GT(127, size);
    return ModuleTypeIndex{static_cast<uint32_t>(size - 1)};
  }

  uint32_t mem_size() const {
    CHECK_EQ(1, module_->memories.size());
    return mem0_size_;
  }

  template <typename T>
  T* raw_mem_start() const {
    DCHECK_NOT_NULL(mem0_start_);
    return reinterpret_cast<T*>(mem0_start_);
  }

  template <typename T>
  T* raw_mem_end() const {
    DCHECK_NOT_NULL(mem0_start_);
    return reinterpret_cast<T*>(mem0_start_ + mem0_size_);
  }

  template <typename T>
  T raw_mem_at(int i) {
    DCHECK_NOT_NULL(mem0_start_);
    return ReadMemory(&(reinterpret_cast<T*>(mem0_start_)[i]));
  }

  template <typename T>
  T raw_val_at(int i) {
    return ReadMemory(reinterpret_cast<T*>(mem0_start_ + i));
  }

  template <typename T>
  void WriteMemory(T* p, T val) {
    DCHECK_LE(mem0_start_, reinterpret_cast<uint8_t*>(p));
    DCHECK_LT(reinterpret_cast<uint8_t*>(p), mem0_start_ + mem0_size_);
    WriteLittleEndianValue<T>(reinterpret_cast<Address>(p), val);
  }

  template <typename T>
  T ReadMemory(T* p) {
    return ReadLittleEndianValue<T>(reinterpret_cast<Address>(p));
  }

  // Zero-initialize the memory.
  void BlankMemory() {
    uint8_t* raw = raw_mem_start<uint8_t>();
    memset(raw, 0, mem0_size_);
  }

  // Pseudo-randomly initialize the memory.
  void RandomizeMemory(unsigned int seed = 88) {
    uint8_t* raw = raw_mem_start<uint8_t>();
    uint8_t* end = raw_mem_end<uint8_t>();
    v8::base::RandomNumberGenerator rng;
    rng.SetSeed(seed);
    rng.NextBytes(raw, end - raw);
  }

  void SetMemoryShared() {
    CHECK_EQ(1, module_->memories.size());
    module_->memories[0].is_shared = SharedFlag{true};
  }

  enum FunctionType { kImport, kWasm };
  uint32_t AddFunction(const FunctionSig* sig, const char* name,
                       FunctionType type);

  // Freezes the signature map of the module and allocates the storage for
  // export wrappers.
  void InitializeWrapperCache();

  // Wrap the code so it can be called as a JS function.
  DirectHandle<JSFunction> WrapCode(uint32_t index);

  // If function_indexes is {nullptr}, the contents of the table will be
  // initialized with null functions.
  void AddIndirectFunctionTable(const uint16_t* function_indexes,
                                uint32_t table_size,
                                ValueType table_type = kWasmFuncRef);

  uint32_t AddBytes(base::Vector<const uint8_t> bytes);

  uint32_t AddException(const FunctionSig* sig);

  uint32_t AddPassiveDataSegment(base::Vector<const uint8_t> bytes);

  WasmFunction* GetFunctionAt(int index) { return &module_->functions[index]; }

  Isolate* isolate() const { return isolate_; }
  DirectHandle<WasmInstanceObject> instance_object() const {
    return instance_object_;
  }
  DirectHandle<WasmTrustedInstanceData> trusted_instance_data() const {
    return trusted_instance_data_;
  }
  WasmCode* GetFunctionCode(uint32_t index) const {
    return native_module_->GetCode(index);
  }

  void SetDebugState() {
    native_module_->SetDebugState(kDebugging);
    execution_tier_ = TestExecutionTier::kLiftoff;
  }

  void SwitchToDebug() {
    SetDebugState();
    WasmCodeRefScope ref_scope;
    native_module_->RemoveCompiledCode(
        NativeModule::RemoveFilter::kRemoveNonDebugCode);
  }

  TestExecutionTier test_execution_tier() const { return execution_tier_; }

  ExecutionTier execution_tier() const {
    switch (execution_tier_) {
#if V8_ENABLE_DRUMBRAKE
      case TestExecutionTier::kInterpreter:
        return ExecutionTier::kInterpreter;
#endif  // V8_ENABLE_DRUMBRAKE
      case TestExecutionTier::kTurbofan:
        return ExecutionTier::kTurbofan;
      case TestExecutionTier::kLiftoff:
        return ExecutionTier::kLiftoff;
      default:
        UNREACHABLE();
    }
  }

  void set_max_steps(int n) { max_steps_ = n; }
  int* max_steps_ptr() { return &max_steps_; }

  void EnableFeature(WasmEnabledFeature feature) {
    enabled_features_.Add(feature);
  }

 private:
  DirectHandle<WasmInstanceObject> InitInstanceObject();

  std::shared_ptr<WasmModule> module_;
  Isolate* isolate_;
  WasmEnabledFeatures enabled_features_;
  uint32_t global_offset_ = 0;
  // The TestingModuleBuilder only supports one memory currently.
  uint8_t* mem0_start_ = nullptr;
  uint32_t mem0_size_ = 0;
  TestExecutionTier execution_tier_;
  DirectHandle<WasmInstanceObject> instance_object_;
  DirectHandle<WasmTrustedInstanceData> trusted_instance_data_;
  NativeModule* native_module_ = nullptr;
  int32_t max_steps_ = kMaxNumSteps;
};

// A helper for compiling wasm functions for testing.
// It contains the internal state for compilation (i.e. TurboFan graph).
class WasmFunctionCompiler {
 public:
  ~WasmFunctionCompiler();

  Isolate* isolate() { return builder_->isolate(); }
  uint32_t function_index() { return function_->func_index; }
  ModuleTypeIndex sig_index() { return function_->sig_index; }

  void Build(std::initializer_list<const uint8_t> bytes) {
    Build(base::VectorOf(bytes));
  }
  void Build(base::Vector<const uint8_t> bytes);

  uint8_t AllocateLocals(uint32_t count, ValueType type) {
    uint32_t index = local_decls_.AddLocals(count, type);
    uint8_t result = static_cast<uint8_t>(index);
    DCHECK_EQ(index, result);
    return result;
  }
  uint8_t AllocateLocal(ValueType type) { return AllocateLocals(1, type); }

  void SetSigIndex(ModuleTypeIndex sig_index) {
    function_->sig_index = sig_index;
  }

  Zone* zone() { return zone_; }

 private:
  friend class WasmRunnerBase;

  WasmFunctionCompiler(Zone* zone, const FunctionSig* sig,
                       TestingModuleBuilder* builder, const char* name);

  Zone* zone_;
  TestingModuleBuilder* builder_;
  WasmFunction* function_;
  LocalDeclEncoder local_decls_;
};

// A helper class to build a module around Wasm bytecode, generate machine
// code, and run that code.
class WasmRunnerBase {
 public:
  WasmRunnerBase(ManuallyImportedJSFunction* maybe_import,
                 TestExecutionTier execution_tier, int num_params,
                 Isolate* isolate)
      : zone_(&allocator_, ZONE_NAME),
        handle_scope_(isolate),
        builder_(&zone_, maybe_import, execution_tier, isolate) {}

  Isolate* isolate() const { return handle_scope_.isolate(); }

  // Builds a graph from the given Wasm code and generates the machine
  // code and call wrapper for that graph. This method must not be called
  // more than once.
  void Build(const uint8_t* start, const uint8_t* end) {
    Build(base::VectorOf(start, end - start));
  }
  void Build(std::initializer_list<const uint8_t> bytes) {
    Build(base::VectorOf(bytes));
  }
  void Build(base::Vector<const uint8_t> bytes) {
    CHECK(!compiled_);
    compiled_ = true;
    functions_[0]->Build(bytes);
  }

  // Resets the state for building the next function.
  // The main function called will always be the first function.
  template <typename ReturnType, typename... ParamTypes>
  WasmFunctionCompiler& NewFunction(const char* name = nullptr) {
    return NewFunction(CreateSig<ReturnType, ParamTypes...>(), name);
  }

  // Resets the state for building the next function.
  // The main function called will be the last generated function.
  // Returns the index of the previously built function.
  WasmFunctionCompiler& NewFunction(const FunctionSig* sig,
                                    const char* name = nullptr) {
    functions_.emplace_back(
        new WasmFunctionCompiler(&zone_, sig, &builder_, name));
    ModuleTypeIndex sig_index = builder().AddSignature(sig);
    functions_.back()->SetSigIndex(sig_index);
    return *functions_.back();
  }

  uint8_t AllocateLocal(ValueType type) {
    return functions_[0]->AllocateLocal(type);
  }
  uint8_t AllocateLocals(uint32_t count, ValueType type) {
    return functions_[0]->AllocateLocals(count, type);
  }

  uint32_t function_index() { return functions_[0]->function_index(); }
  WasmFunction* function() { return functions_[0]->function_; }
  bool possible_nondeterminism() { return possible_nondeterminism_; }
  TestingModuleBuilder& builder() { return builder_; }
  Zone* zone() { return &zone_; }

  void SwitchToDebug() { builder_.SwitchToDebug(); }

  static const CanonicalSig* CanonicalizeSig(const FunctionSig* sig) {
    // TODO(clemensb): Make this a single function call.
    CanonicalTypeIndex sig_id = GetTypeCanonicalizer()->AddRecursiveGroup(sig);
    return GetTypeCanonicalizer()->LookupFunctionSignature(sig_id);
  }

  template <typename ReturnType, typename... ParamTypes>
  FunctionSig* CreateSig() {
    std::array<MachineType, sizeof...(ParamTypes)> param_machine_types{
        {MachineTypeForC<ParamTypes>()...}};
    base::Vector<MachineType> param_vec(param_machine_types.data(),
                                        param_machine_types.size());
    return CreateSig(MachineTypeForC<ReturnType>(), param_vec);
  }

  // TODO(clemensb): Remove, use {CallViaJS} directly.
  void CheckCallApplyViaJS(double expected, uint32_t function_index,
                           base::Vector<const DirectHandle<Object>> args) {
    MaybeDirectHandle<Object> retval = CallViaJS(function_index, args);

    if (retval.is_null()) {
      CHECK_EQ(expected, static_cast<double>(0xDEADBEEF));
    } else {
      DirectHandle<Object> result = retval.ToHandleChecked();
      if (IsSmi(*result)) {
        CHECK_EQ(expected, Smi::ToInt(*result));
      } else {
        CHECK(IsHeapNumber(*result));
        CHECK_DOUBLE_EQ(expected, Cast<HeapNumber>(*result)->value());
      }
    }
  }

  MaybeDirectHandle<Object> CallViaJS(
      uint32_t function_index,
      base::Vector<const DirectHandle<Object>> parameters) {
    // Save the original context, because CEntry (for runtime calls) will
    // reset / invalidate it when returning.
    SaveContext save_context(isolate());

    if (!jsfuncs_.has_value()) {
      jsfuncs_.emplace(isolate());
    }
    if (jsfuncs_->size() <= function_index) {
      jsfuncs_->resize(function_index + 1);
    }
    if ((*jsfuncs_)[function_index].is_null()) {
      (*jsfuncs_)[function_index] = builder_.WrapCode(function_index);
    }
    DirectHandle<JSFunction> jsfunc = (*jsfuncs_)[function_index];
    DirectHandle<Object> global(isolate()->context()->global_object(),
                                isolate());
    return Execution::TryCall(isolate(), jsfunc, global, parameters,
                              Execution::MessageHandling::kReport, nullptr);
  }

 private:
  FunctionSig* CreateSig(MachineType return_type,
                         base::Vector<MachineType> param_types);

 protected:
  wasm::WasmCodeRefScope code_ref_scope_;
  std::optional<DirectHandleVector<JSFunction>> jsfuncs_;

  v8::internal::AccountingAllocator allocator_;
  Zone zone_;
  HandleScope handle_scope_;
  TestingModuleBuilder builder_;
  std::vector<std::unique_ptr<WasmFunctionCompiler>> functions_;
  bool compiled_ = false;
  bool possible_nondeterminism_ = false;
  int32_t main_fn_index_ = 0;
};

template <typename T>
inline WasmValue WasmValueInitializer(T value) {
  return WasmValue(value);
}
template <>
inline WasmValue WasmValueInitializer(int8_t value) {
  return WasmValue(static_cast<int32_t>(value));
}
template <>
inline WasmValue WasmValueInitializer(int16_t value) {
  return WasmValue(static_cast<int32_t>(value));
}

template <typename ReturnType, typename... ParamTypes>
class CommonWasmRunner : public WasmRunnerBase {
 public:
  CommonWasmRunner(Isolate* isolate, TestExecutionTier execution_tier,
                   ManuallyImportedJSFunction* maybe_import = nullptr,
                   const char* main_fn_name = "main")
      : WasmRunnerBase(maybe_import, execution_tier, sizeof...(ParamTypes),
                       isolate) {
    WasmFunctionCompiler& main_fn =
        NewFunction<ReturnType, ParamTypes...>(main_fn_name);
    // Non-zero if there is an import.
    main_fn_index_ = main_fn.function_index();
  }

  template <typename T>
  DirectHandle<Object> MakeParam(T t) {
    Factory* factory = isolate()->factory();
    if constexpr (std::is_integral_v<T> && std::is_signed_v<T> &&
                  sizeof(T) <= sizeof(int)) {
      return factory->NewNumberFromInt(t);
    }
    if constexpr (std::is_integral_v<T> && std::is_unsigned_v<T> &&
                  sizeof(T) <= sizeof(int)) {
      return factory->NewNumberFromUint(t);
    }
    if constexpr (std::is_same_v<T, int64_t>) {
      return BigInt::FromInt64(isolate(), t);
    }
    if constexpr (std::is_same_v<T, uint64_t>) {
      return BigInt::FromUint64(isolate(), t);
    }
    if constexpr (std::is_same_v<T, float>) {
      return factory->NewNumber(t);
    }
    if constexpr (std::is_same_v<T, double>) {
      return factory->NewNumber(t);
    }
    UNIMPLEMENTED();
  }

  ReturnType Call(ParamTypes... p) {
    // We should only call code after having built it (via one of the
    // `Build(...)` methods).
    CHECK(compiled_);

    std::array<DirectHandle<Object>, sizeof...(p)> param_objs = {
        MakeParam(p)...};
    MaybeDirectHandle<Object> retval =
        CallViaJS(function()->func_index, base::VectorOf(param_objs));

    if constexpr (std::is_void_v<ReturnType>) {
      return;
    }

    if (retval.is_null()) {
      return static_cast<ReturnType>(0xDEADBEEFDEADBEEF);
    }

    DirectHandle<Object> result = retval.ToHandleChecked();
    // For int64_t and uint64_t returns we will get a BigInt.
    if constexpr (std::is_integral_v<ReturnType>) {
      if constexpr (sizeof(ReturnType) == sizeof(int64_t)) {
        CHECK(IsBigInt(*result));
        return Cast<BigInt>(*result)->AsInt64();
      }
    }

    // Otherwise it must be a number (Smi or HeapNumber).
    CHECK(IsNumber(*result));
    double value = Object::NumberValue(Cast<Number>(*result));
    // The JS API interprets all Wasm values as signed, hence we cast via the
    // signed equivalent type to avoid undefined behaviour in the casting.
    if constexpr (std::is_integral_v<ReturnType> &&
                  std::is_unsigned_v<ReturnType>) {
      using signed_t = std::make_signed_t<ReturnType>;
      return static_cast<ReturnType>(static_cast<signed_t>(value));
    }
    return static_cast<ReturnType>(value);
  }

  void CheckCallViaJS(double expected, ParamTypes... p) {
    // TODO(clemensb): Inline into callers; use {Call} and {CHECK_EQ} directly.
    ReturnType result = Call(p...);
    if constexpr (std::is_floating_point_v<ReturnType>) {
      if (std::isnan(result)) {
        CHECK(IsSameNan(static_cast<ReturnType>(expected), result));
        return;
      }
    }
    CHECK_EQ(expected, result);
  }

  void CheckCallViaJSTraps(ParamTypes... p) {
    std::array<DirectHandle<Object>, sizeof...(p)> param_objs{MakeParam(p)...};
    MaybeDirectHandle<Object> retval =
        CallViaJS(function()->func_index, base::VectorOf(param_objs));
    CHECK(retval.is_null());
  }

  void SetMaxSteps(int n) { builder_.set_max_steps(n); }
};

// A macro to define tests that run in different engine configurations.
#if V8_ENABLE_DRUMBRAKE
#define TEST_IF_DRUMBRAKE(name)                      \
  TEST(RunWasmInterpreter_##name) {                  \
    FLAG_SCOPE(wasm_jitless);                        \
    WasmInterpreterThread::Initialize();             \
    RunWasm_##name(TestExecutionTier::kInterpreter); \
    WasmInterpreterThread::Terminate();              \
  }
#else
#define TEST_IF_DRUMBRAKE(name)
#endif  // V8_ENABLE_DRUMBRAKE
#define WASM_EXEC_TEST(name)                                                   \
  void RunWasm_##name(TestExecutionTier execution_tier);                       \
  TEST(RunWasmTurbofan_##name) {                                               \
    RunWasm_##name(TestExecutionTier::kTurbofan);                              \
  }                                                                            \
  TEST(RunWasmLiftoff_##name) { RunWasm_##name(TestExecutionTier::kLiftoff); } \
  TEST_IF_DRUMBRAKE(name)                                                      \
  void RunWasm_##name(TestExecutionTier execution_tier)

#define UNINITIALIZED_WASM_EXEC_TEST(name)               \
  void RunWasm_##name(TestExecutionTier execution_tier); \
  UNINITIALIZED_TEST(RunWasmTurbofan_##name) {           \
    RunWasm_##name(TestExecutionTier::kTurbofan);        \
  }                                                      \
  UNINITIALIZED_TEST(RunWasmLiftoff_##name) {            \
    RunWasm_##name(TestExecutionTier::kLiftoff);         \
  }                                                      \
  void RunWasm_##name(TestExecutionTier execution_tier)

#define WASM_COMPILED_EXEC_TEST(name)                                          \
  void RunWasm_##name(TestExecutionTier execution_tier);                       \
  TEST(RunWasmTurbofan_##name) {                                               \
    RunWasm_##name(TestExecutionTier::kTurbofan);                              \
  }                                                                            \
  TEST(RunWasmLiftoff_##name) { RunWasm_##name(TestExecutionTier::kLiftoff); } \
  void RunWasm_##name(TestExecutionTier execution_tier)

}  // namespace v8::internal::wasm

#endif  // TEST_COMMON_WASM_WASM_RUN_UTILS_H
