| /* |
| * Copyright 2020 WebAssembly Community Group participants |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <wasm.h> |
| |
| #include "src/binary-reader.h" |
| #include "src/cast.h" |
| #include "src/error-formatter.h" |
| #include "src/interp/binary-reader-interp.h" |
| #include "src/interp/interp-util.h" |
| #include "src/interp/interp.h" |
| |
| using namespace wabt; |
| using namespace wabt::interp; |
| |
| #define own |
| |
| #ifndef NDEBUG |
| #define TRACE0() TRACE("") |
| #define TRACE(str, ...) \ |
| fprintf(stderr, "CAPI: [%s] " str "\n", __func__, ##__VA_ARGS__) |
| #define TRACE_NO_NL(str, ...) \ |
| fprintf(stderr, "CAPI: [%s] " str, __func__, ##__VA_ARGS__) |
| #else |
| #define TRACE0(...) |
| #define TRACE(...) |
| #define TRACE_NO_NL(...) |
| #endif |
| |
| static Features s_features; |
| static std::unique_ptr<FileStream> s_log_stream; |
| static std::unique_ptr<FileStream> s_stdout_stream; |
| |
| // Type conversion utilities |
| static ValueType ToWabtValueType(wasm_valkind_t); |
| static wasm_valkind_t FromWabtValueType(ValueType); |
| |
| static wasm_externkind_t FromWabtExternKind(ExternKind); |
| |
| static ValueTypes ToWabtValueTypes(const wasm_valtype_vec_t* types); |
| static void FromWabtValueTypes(const ValueTypes&, wasm_valtype_vec_t* out); |
| |
| static wasm_mutability_t FromWabtMutability(Mutability); |
| static Mutability ToWabtMutability(wasm_mutability_t); |
| |
| static Limits ToWabtLimits(const wasm_limits_t&); |
| static wasm_limits_t FromWabtLimits(const Limits&); |
| |
| // Value conversion utilities |
| static TypedValue ToWabtValue(const wasm_val_t&); |
| static wasm_val_t FromWabtValue(Store&, const TypedValue&); |
| |
| static Values ToWabtValues(const wasm_val_t values[], size_t count); |
| static void FromWabtValues(Store& store, |
| wasm_val_t values[], |
| const ValueTypes& types, |
| const Values& wabt_values); |
| |
| // Structs |
| struct wasm_config_t {}; |
| |
| struct wasm_engine_t {}; |
| |
| struct wasm_valtype_t { |
| ValueType I; |
| }; |
| |
| struct wasm_externtype_t { |
| static std::unique_ptr<wasm_externtype_t> New(std::unique_ptr<ExternType>); |
| |
| std::unique_ptr<wasm_externtype_t> Clone() const { return New(I->Clone()); } |
| |
| virtual ~wasm_externtype_t() {} |
| |
| wasm_externtype_t(const wasm_externtype_t& other) = delete; |
| wasm_externtype_t& operator=(const wasm_externtype_t& other) = delete; |
| |
| template <typename T> |
| T* As() const { |
| return cast<T>(I.get()); |
| } |
| |
| std::unique_ptr<ExternType> I; |
| |
| protected: |
| wasm_externtype_t(std::unique_ptr<ExternType> et) : I(std::move(et)) {} |
| }; |
| |
| struct wasm_functype_t : wasm_externtype_t { |
| wasm_functype_t(own wasm_valtype_vec_t* params, |
| own wasm_valtype_vec_t* results) |
| : wasm_externtype_t{MakeUnique<FuncType>(ToWabtValueTypes(params), |
| ToWabtValueTypes(results))}, |
| params(*params), |
| results(*results) {} |
| |
| wasm_functype_t(FuncType ft) : wasm_externtype_t{MakeUnique<FuncType>(ft)} { |
| FromWabtValueTypes(ft.params, ¶ms); |
| FromWabtValueTypes(ft.results, &results); |
| } |
| |
| ~wasm_functype_t() { |
| wasm_valtype_vec_delete(¶ms); |
| wasm_valtype_vec_delete(&results); |
| } |
| |
| // Stored here because API requires returning pointers. |
| wasm_valtype_vec_t params; |
| wasm_valtype_vec_t results; |
| }; |
| |
| struct wasm_globaltype_t : wasm_externtype_t { |
| wasm_globaltype_t(own wasm_valtype_t* type, wasm_mutability_t mut) |
| : wasm_externtype_t{MakeUnique<GlobalType>(type->I, |
| ToWabtMutability(mut))}, |
| valtype{*type} { |
| wasm_valtype_delete(type); |
| } |
| |
| wasm_globaltype_t(GlobalType gt) |
| : wasm_externtype_t{MakeUnique<GlobalType>(gt)}, valtype{gt.type} {} |
| |
| // Stored here because API requires returning pointers. |
| wasm_valtype_t valtype; |
| }; |
| |
| struct wasm_tabletype_t : wasm_externtype_t { |
| wasm_tabletype_t(own wasm_valtype_t* type, const wasm_limits_t* limits) |
| : wasm_externtype_t{MakeUnique<TableType>(type->I, |
| ToWabtLimits(*limits))}, |
| elemtype(*type), |
| limits(*limits) { |
| wasm_valtype_delete(type); |
| } |
| |
| wasm_tabletype_t(TableType tt) |
| : wasm_externtype_t{MakeUnique<TableType>(tt)}, |
| elemtype{tt.element}, |
| limits{FromWabtLimits(tt.limits)} {} |
| |
| // Stored here because API requires returning pointers. |
| wasm_valtype_t elemtype; |
| wasm_limits_t limits; |
| }; |
| |
| struct wasm_memorytype_t : wasm_externtype_t { |
| wasm_memorytype_t(const wasm_limits_t* limits) |
| : wasm_externtype_t{MakeUnique<MemoryType>(ToWabtLimits(*limits))}, |
| limits{*limits} {} |
| |
| wasm_memorytype_t(MemoryType mt) |
| : wasm_externtype_t{MakeUnique<MemoryType>(mt)}, |
| limits{FromWabtLimits(mt.limits)} {} |
| |
| // Stored here because API requires returning pointers. |
| wasm_limits_t limits; |
| }; |
| |
| // static |
| std::unique_ptr<wasm_externtype_t> wasm_externtype_t::New( |
| std::unique_ptr<ExternType> ptr) { |
| switch (ptr->kind) { |
| case ExternKind::Func: |
| return MakeUnique<wasm_functype_t>(*cast<FuncType>(ptr.get())); |
| |
| case ExternKind::Table: |
| return MakeUnique<wasm_tabletype_t>(*cast<TableType>(ptr.get())); |
| |
| case ExternKind::Memory: |
| return MakeUnique<wasm_memorytype_t>(*cast<MemoryType>(ptr.get())); |
| |
| case ExternKind::Global: |
| return MakeUnique<wasm_globaltype_t>(*cast<GlobalType>(ptr.get())); |
| |
| case ExternKind::Tag: |
| break; |
| } |
| |
| assert(false); |
| return {}; |
| } |
| |
| struct wasm_importtype_t { |
| wasm_importtype_t(ImportType it) |
| : I(it), |
| extern_{wasm_externtype_t::New(it.type->Clone())}, |
| module{I.module.size(), const_cast<wasm_byte_t*>(I.module.data())}, |
| name{I.name.size(), const_cast<wasm_byte_t*>(I.name.data())} {} |
| |
| wasm_importtype_t(const wasm_importtype_t& other) |
| : wasm_importtype_t(other.I) {} |
| |
| wasm_importtype_t& operator=(const wasm_importtype_t& other) { |
| wasm_importtype_t copy(other); |
| std::swap(I, copy.I); |
| std::swap(extern_, copy.extern_); |
| std::swap(module, copy.module); |
| std::swap(name, copy.name); |
| return *this; |
| } |
| |
| ImportType I; |
| // Stored here because API requires returning pointers. |
| std::unique_ptr<wasm_externtype_t> extern_; |
| wasm_name_t module; |
| wasm_name_t name; |
| }; |
| |
| struct wasm_exporttype_t { |
| wasm_exporttype_t(ExportType et) |
| : I(et), |
| extern_{wasm_externtype_t::New(et.type->Clone())}, |
| name{I.name.size(), const_cast<wasm_byte_t*>(I.name.data())} {} |
| |
| wasm_exporttype_t(const wasm_exporttype_t& other) |
| : wasm_exporttype_t(other.I) {} |
| |
| wasm_exporttype_t& operator=(const wasm_exporttype_t& other) { |
| wasm_exporttype_t copy(other); |
| std::swap(I, copy.I); |
| std::swap(extern_, copy.extern_); |
| std::swap(name, copy.name); |
| return *this; |
| } |
| |
| ExportType I; |
| // Stored here because API requires returning pointers. |
| std::unique_ptr<wasm_externtype_t> extern_; |
| wasm_name_t name; |
| }; |
| |
| struct wasm_store_t { |
| wasm_store_t(const Features& features) : I(features) {} |
| Store I; |
| }; |
| |
| struct wasm_ref_t { |
| wasm_ref_t(RefPtr<Object> ptr) : I(ptr) {} |
| |
| template <typename T> |
| T* As() const { |
| return cast<T>(I.get()); |
| } |
| |
| RefPtr<Object> I; |
| }; |
| |
| struct wasm_frame_t { |
| Frame I; |
| }; |
| |
| struct wasm_trap_t : wasm_ref_t { |
| wasm_trap_t(RefPtr<Trap> ptr) : wasm_ref_t(ptr) {} |
| }; |
| |
| struct wasm_foreign_t : wasm_ref_t { |
| wasm_foreign_t(RefPtr<Foreign> ptr) : wasm_ref_t(ptr) {} |
| }; |
| |
| struct wasm_module_t : wasm_ref_t { |
| wasm_module_t(RefPtr<Module> ptr, const wasm_byte_vec_t* in) |
| : wasm_ref_t(ptr) { |
| wasm_byte_vec_copy(&binary, in); |
| } |
| |
| wasm_module_t(const wasm_module_t& other) : wasm_ref_t(other.I) { |
| wasm_byte_vec_copy(&binary, &other.binary); |
| } |
| |
| wasm_module_t& operator=(const wasm_module_t& other) { |
| wasm_module_t copy(other); |
| std::swap(I, copy.I); |
| std::swap(binary, copy.binary); |
| return *this; |
| } |
| |
| ~wasm_module_t() { wasm_byte_vec_delete(&binary); } |
| // TODO: This is used for wasm_module_serialize/wasm_module_deserialize. |
| // Currently the standard wasm binary bytes are cached here, but it would be |
| // better to have a serialization of ModuleDesc instead. |
| wasm_byte_vec_t binary; |
| }; |
| |
| struct wasm_shared_module_t : wasm_module_t {}; |
| |
| struct wasm_extern_t : wasm_ref_t { |
| wasm_extern_t(RefPtr<Extern> ptr) : wasm_ref_t(ptr) {} |
| }; |
| |
| struct wasm_func_t : wasm_extern_t { |
| wasm_func_t(RefPtr<Func> ptr) : wasm_extern_t(ptr) {} |
| }; |
| |
| struct wasm_global_t : wasm_extern_t { |
| wasm_global_t(RefPtr<Global> ptr) : wasm_extern_t(ptr) {} |
| }; |
| |
| struct wasm_table_t : wasm_extern_t { |
| wasm_table_t(RefPtr<Table> ptr) : wasm_extern_t(ptr) {} |
| }; |
| |
| struct wasm_memory_t : wasm_extern_t { |
| wasm_memory_t(RefPtr<Memory> ptr) : wasm_extern_t(ptr) {} |
| }; |
| |
| struct wasm_instance_t : wasm_ref_t { |
| wasm_instance_t(RefPtr<Instance> ptr) : wasm_ref_t(ptr) {} |
| }; |
| |
| // Type conversion utilities |
| static ValueType ToWabtValueType(wasm_valkind_t kind) { |
| switch (kind) { |
| case WASM_I32: |
| return ValueType::I32; |
| case WASM_I64: |
| return ValueType::I64; |
| case WASM_F32: |
| return ValueType::F32; |
| case WASM_F64: |
| return ValueType::F64; |
| case WASM_ANYREF: |
| return ValueType::ExternRef; |
| case WASM_FUNCREF: |
| return ValueType::FuncRef; |
| default: |
| TRACE("unexpected wasm_valkind_t: %d", kind); |
| WABT_UNREACHABLE; |
| } |
| WABT_UNREACHABLE; |
| } |
| |
| static wasm_valkind_t FromWabtValueType(ValueType type) { |
| switch (type) { |
| case ValueType::I32: |
| return WASM_I32; |
| case ValueType::I64: |
| return WASM_I64; |
| case ValueType::F32: |
| return WASM_F32; |
| case ValueType::F64: |
| return WASM_F64; |
| case ValueType::ExternRef: |
| return WASM_ANYREF; |
| case ValueType::FuncRef: |
| return WASM_FUNCREF; |
| default: |
| WABT_UNREACHABLE; |
| } |
| } |
| |
| static wasm_externkind_t FromWabtExternKind(ExternKind kind) { |
| switch (kind) { |
| case ExternalKind::Func: |
| return WASM_EXTERN_FUNC; |
| case ExternalKind::Global: |
| return WASM_EXTERN_GLOBAL; |
| case ExternalKind::Table: |
| return WASM_EXTERN_TABLE; |
| case ExternalKind::Memory: |
| return WASM_EXTERN_MEMORY; |
| case ExternalKind::Tag: |
| WABT_UNREACHABLE; |
| } |
| WABT_UNREACHABLE; |
| } |
| |
| static ValueTypes ToWabtValueTypes(const wasm_valtype_vec_t* types) { |
| ValueTypes result; |
| for (size_t i = 0; i < types->size; ++i) { |
| result.push_back(types->data[i]->I); |
| } |
| return result; |
| } |
| |
| static void FromWabtValueTypes(const ValueTypes& types, |
| wasm_valtype_vec_t* out) { |
| wasm_valtype_vec_new_uninitialized(out, types.size()); |
| for (size_t i = 0; i < types.size(); ++i) { |
| out->data[i] = wasm_valtype_new(FromWabtValueType(types[i])); |
| } |
| } |
| |
| static wasm_mutability_t FromWabtMutability(Mutability mut) { |
| return mut == Mutability::Var ? WASM_VAR : WASM_CONST; |
| } |
| |
| static Mutability ToWabtMutability(wasm_mutability_t mut) { |
| return mut == WASM_VAR ? Mutability::Var : Mutability::Const; |
| } |
| |
| // Value conversion utilities |
| |
| static TypedValue ToWabtValue(const wasm_val_t& value) { |
| TypedValue out; |
| out.type = ToWabtValueType(value.kind); |
| switch (value.kind) { |
| case WASM_I32: |
| out.value.Set(value.of.i32); |
| break; |
| case WASM_I64: |
| out.value.Set(value.of.i64); |
| break; |
| case WASM_F32: |
| out.value.Set(value.of.f32); |
| break; |
| case WASM_F64: |
| out.value.Set(value.of.f64); |
| break; |
| case WASM_ANYREF: |
| case WASM_FUNCREF: |
| out.value.Set(value.of.ref ? value.of.ref->I->self() : Ref::Null); |
| break; |
| default: |
| TRACE("unexpected wasm type: %d", value.kind); |
| assert(false); |
| } |
| TRACE("-> %s", TypedValueToString(out).c_str()); |
| return out; |
| } |
| |
| static wasm_val_t FromWabtValue(Store& store, const TypedValue& tv) { |
| TRACE("%s", TypedValueToString(tv).c_str()); |
| wasm_val_t out_value; |
| switch (tv.type) { |
| case Type::I32: |
| out_value.kind = WASM_I32; |
| out_value.of.i32 = tv.value.Get<s32>(); |
| break; |
| case Type::I64: |
| out_value.kind = WASM_I64; |
| out_value.of.i64 = tv.value.Get<s64>(); |
| break; |
| case Type::F32: |
| out_value.kind = WASM_F32; |
| out_value.of.f32 = tv.value.Get<f32>(); |
| break; |
| case Type::F64: |
| out_value.kind = WASM_F64; |
| out_value.of.f64 = tv.value.Get<f64>(); |
| break; |
| case Type::FuncRef: { |
| Ref ref = tv.value.Get<Ref>(); |
| out_value.kind = WASM_FUNCREF; |
| out_value.of.ref = new wasm_func_t(store.UnsafeGet<Func>(ref)); |
| break; |
| } |
| case Type::ExternRef: { |
| Ref ref = tv.value.Get<Ref>(); |
| out_value.kind = WASM_ANYREF; |
| out_value.of.ref = new wasm_foreign_t(store.UnsafeGet<Foreign>(ref)); |
| break; |
| } |
| default: |
| TRACE("unexpected wabt type: %d", static_cast<int>(tv.type)); |
| assert(false); |
| } |
| return out_value; |
| } |
| |
| static Limits ToWabtLimits(const wasm_limits_t& limits) { |
| return Limits(limits.min, limits.max); |
| } |
| |
| static wasm_limits_t FromWabtLimits(const Limits& limits) { |
| return wasm_limits_t{(uint32_t)limits.initial, (uint32_t)limits.max}; |
| } |
| |
| static Values ToWabtValues(const wasm_val_t values[], size_t count) { |
| Values result; |
| for (size_t i = 0; i < count; ++i) { |
| result.push_back(ToWabtValue(values[i]).value); |
| } |
| return result; |
| } |
| |
| static void FromWabtValues(Store& store, |
| wasm_val_t values[], |
| const ValueTypes& types, |
| const Values& wabt_values) { |
| assert(types.size() == wabt_values.size()); |
| for (size_t i = 0; i < types.size(); ++i) { |
| values[i] = FromWabtValue(store, TypedValue{types[i], wabt_values[i]}); |
| } |
| } |
| |
| static std::string ToString(const wasm_message_t* msg) { |
| return std::string(msg->data, msg->size); |
| } |
| |
| static wasm_message_t FromString(const std::string& s) { |
| wasm_message_t result; |
| wasm_byte_vec_new(&result, s.size() + 1, s.c_str()); |
| return result; |
| } |
| |
| static ReadBinaryOptions GetOptions() { |
| const bool kReadDebugNames = true; |
| const bool kStopOnFirstError = true; |
| const bool kFailOnCustomSectionError = true; |
| s_features.EnableAll(); |
| if (getenv("WASM_API_DEBUG") != nullptr) { |
| s_log_stream = FileStream::CreateStderr(); |
| } |
| return ReadBinaryOptions(s_features, s_log_stream.get(), kReadDebugNames, |
| kStopOnFirstError, kFailOnCustomSectionError); |
| } |
| |
| extern "C" { |
| |
| // wasm_valtype |
| |
| own wasm_valtype_t* wasm_valtype_new(wasm_valkind_t kind) { |
| return new wasm_valtype_t{ToWabtValueType(kind)}; |
| } |
| |
| wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t* type) { |
| assert(type); |
| return FromWabtValueType(type->I); |
| } |
| |
| // Helpers |
| |
| static void print_sig(const FuncType& sig) { |
| #ifndef NDEBUG |
| fprintf(stderr, "("); |
| bool first = true; |
| for (auto type : sig.params) { |
| if (!first) { |
| fprintf(stderr, ", "); |
| } |
| first = false; |
| fprintf(stderr, "%s", type.GetName().c_str()); |
| } |
| fprintf(stderr, ") -> ("); |
| first = true; |
| for (auto type : sig.results) { |
| if (!first) { |
| fprintf(stderr, ", "); |
| } |
| first = false; |
| fprintf(stderr, "%s", type.GetName().c_str()); |
| } |
| fprintf(stderr, ")\n"); |
| #endif |
| } |
| |
| // wasm_val |
| |
| void wasm_val_delete(own wasm_val_t* val) { |
| assert(val); |
| if (wasm_valkind_is_ref(val->kind)) { |
| delete val->of.ref; |
| val->of.ref = nullptr; |
| } |
| } |
| |
| void wasm_val_copy(own wasm_val_t* out, const wasm_val_t* in) { |
| TRACE("%s", TypedValueToString(ToWabtValue(*in)).c_str()); |
| if (wasm_valkind_is_ref(in->kind)) { |
| out->kind = in->kind; |
| out->of.ref = in->of.ref ? new wasm_ref_t{*in->of.ref} : nullptr; |
| } else { |
| *out = *in; |
| } |
| TRACE("-> %p %s", out, TypedValueToString(ToWabtValue(*out)).c_str()); |
| } |
| |
| // wasm_trap |
| |
| own wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t* msg) { |
| return new wasm_trap_t{Trap::New(store->I, ToString(msg))}; |
| } |
| |
| void wasm_trap_message(const wasm_trap_t* trap, own wasm_message_t* out) { |
| assert(trap); |
| *out = FromString(trap->As<Trap>()->message()); |
| } |
| |
| own wasm_frame_t* wasm_trap_origin(const wasm_trap_t* trap) { |
| // TODO(sbc): Implement stack traces |
| return nullptr; |
| } |
| |
| void wasm_trap_trace(const wasm_trap_t* trap, own wasm_frame_vec_t* out) { |
| assert(trap); |
| assert(out); |
| wasm_frame_vec_new_empty(out); |
| // std::string msg(trap->message.data, trap->message.size); |
| // TRACE(stderr, "error: %s\n", msg.c_str()); |
| } |
| |
| // wasm_config |
| |
| own wasm_config_t* wasm_config_new() { |
| return new wasm_config_t(); |
| } |
| |
| // wasm_engine |
| |
| own wasm_engine_t* wasm_engine_new() { |
| return new wasm_engine_t(); |
| } |
| |
| own wasm_engine_t* wasm_engine_new_with_config(own wasm_config_t*) { |
| assert(false); |
| return nullptr; |
| } |
| |
| // wasm_store |
| |
| own wasm_store_t* wasm_store_new(wasm_engine_t* engine) { |
| assert(engine); |
| return new wasm_store_t(s_features); |
| } |
| |
| // wasm_module |
| |
| own wasm_module_t* wasm_module_new(wasm_store_t* store, |
| const wasm_byte_vec_t* binary) { |
| Errors errors; |
| ModuleDesc module_desc; |
| if (Failed(ReadBinaryInterp("<internal>", binary->data, binary->size, |
| GetOptions(), &errors, &module_desc))) { |
| FormatErrorsToFile(errors, Location::Type::Binary); |
| return nullptr; |
| } |
| |
| return new wasm_module_t{Module::New(store->I, module_desc), binary}; |
| } |
| |
| bool wasm_module_validate(wasm_store_t* store, const wasm_byte_vec_t* binary) { |
| // TODO: Optimize this; for now it is generating a new module and discarding |
| // it. But since this call only needs to validate, it could do much less. |
| wasm_module_t* module = wasm_module_new(store, binary); |
| if (module == nullptr) { |
| return false; |
| } |
| wasm_module_delete(module); |
| return true; |
| } |
| |
| void wasm_module_imports(const wasm_module_t* module, |
| own wasm_importtype_vec_t* out) { |
| auto&& import_types = module->As<Module>()->import_types(); |
| TRACE("%" PRIzx, import_types.size()); |
| wasm_importtype_vec_new_uninitialized(out, import_types.size()); |
| |
| for (size_t i = 0; i < import_types.size(); ++i) { |
| out->data[i] = new wasm_importtype_t{import_types[i]}; |
| } |
| } |
| |
| void wasm_module_exports(const wasm_module_t* module, |
| own wasm_exporttype_vec_t* out) { |
| auto&& export_types = module->As<Module>()->export_types(); |
| TRACE("%" PRIzx, export_types.size()); |
| wasm_exporttype_vec_new_uninitialized(out, export_types.size()); |
| |
| for (size_t i = 0; i < export_types.size(); ++i) { |
| out->data[i] = new wasm_exporttype_t{export_types[i]}; |
| } |
| } |
| |
| void wasm_module_serialize(const wasm_module_t* module, |
| own wasm_byte_vec_t* out) { |
| wasm_byte_vec_copy(out, &module->binary); |
| } |
| |
| own wasm_module_t* wasm_module_deserialize(wasm_store_t* store, |
| const wasm_byte_vec_t* bytes) { |
| return wasm_module_new(store, bytes); |
| } |
| |
| // wasm_importtype |
| |
| own wasm_importtype_t* wasm_importtype_new(own wasm_name_t* module, |
| own wasm_name_t* name, |
| own wasm_externtype_t* type) { |
| return new wasm_importtype_t{ |
| ImportType{ToString(module), ToString(name), type->I->Clone()}}; |
| } |
| |
| const wasm_name_t* wasm_importtype_module(const wasm_importtype_t* im) { |
| return &im->module; |
| } |
| |
| const wasm_name_t* wasm_importtype_name(const wasm_importtype_t* im) { |
| return &im->name; |
| } |
| |
| const wasm_externtype_t* wasm_importtype_type(const wasm_importtype_t* im) { |
| return im->extern_.get(); |
| } |
| |
| // wasm_exporttype |
| |
| own wasm_exporttype_t* wasm_exporttype_new(own wasm_name_t* name, |
| wasm_externtype_t* type) { |
| return new wasm_exporttype_t{ExportType{ToString(name), type->I->Clone()}}; |
| } |
| |
| const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t* ex) { |
| return &ex->name; |
| } |
| |
| const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t* ex) { |
| TRACE("%p", ex); |
| assert(ex); |
| return ex->extern_.get(); |
| } |
| |
| // wasm_instance |
| |
| own wasm_instance_t* wasm_instance_new(wasm_store_t* store, |
| const wasm_module_t* module, |
| const wasm_extern_t* const imports[], |
| own wasm_trap_t** trap_out) { |
| TRACE("%p %p", store, module); |
| assert(module); |
| assert(store); |
| |
| RefVec import_refs; |
| for (size_t i = 0; i < module->As<Module>()->import_types().size(); i++) { |
| import_refs.push_back(imports[i]->I->self()); |
| } |
| |
| Trap::Ptr trap; |
| auto instance = |
| Instance::Instantiate(store->I, module->I->self(), import_refs, &trap); |
| if (trap) { |
| *trap_out = new wasm_trap_t{trap}; |
| return nullptr; |
| } |
| |
| return new wasm_instance_t{instance}; |
| } |
| |
| void wasm_instance_exports(const wasm_instance_t* instance, |
| own wasm_extern_vec_t* out) { |
| auto&& exports = instance->As<Instance>()->exports(); |
| wasm_extern_vec_new_uninitialized(out, exports.size()); |
| TRACE("%" PRIzx, exports.size()); |
| |
| for (size_t i = 0; i < exports.size(); ++i) { |
| out->data[i] = |
| new wasm_extern_t{instance->I.store()->UnsafeGet<Extern>(exports[i])}; |
| } |
| } |
| |
| // wasm_functype |
| |
| own wasm_functype_t* wasm_functype_new(own wasm_valtype_vec_t* params, |
| own wasm_valtype_vec_t* results) { |
| TRACE("params=%" PRIzx " args=%" PRIzx, params->size, results->size); |
| auto* res = new wasm_functype_t{params, results}; |
| TRACE_NO_NL(""); |
| print_sig(*res->As<FuncType>()); |
| return res; |
| } |
| |
| const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t* f) { |
| TRACE("%" PRIzx, f->params.size); |
| return &f->params; |
| } |
| |
| const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t* f) { |
| return &f->results; |
| } |
| |
| // wasm_func |
| own wasm_func_t* wasm_func_new(wasm_store_t* store, |
| const wasm_functype_t* type, |
| wasm_func_callback_t callback) { |
| FuncType wabt_type = *type->As<FuncType>(); |
| auto lambda = [=](Thread& thread, const Values& wabt_params, |
| Values& wabt_results, Trap::Ptr* out_trap) -> Result { |
| wasm_val_vec_t params, results; |
| wasm_val_vec_new_uninitialized(¶ms, wabt_params.size()); |
| wasm_val_vec_new_uninitialized(&results, wabt_results.size()); |
| FromWabtValues(store->I, params.data, wabt_type.params, wabt_params); |
| auto trap = callback(params.data, results.data); |
| wasm_val_vec_delete(¶ms); |
| if (trap) { |
| *out_trap = trap->I.As<Trap>(); |
| wasm_trap_delete(trap); |
| // Can't use wasm_val_vec_delete since it wasn't populated. |
| delete[] results.data; |
| return Result::Error; |
| } |
| wabt_results = ToWabtValues(results.data, results.size); |
| wasm_val_vec_delete(&results); |
| return Result::Ok; |
| }; |
| |
| return new wasm_func_t{HostFunc::New(store->I, wabt_type, lambda)}; |
| } |
| |
| own wasm_func_t* wasm_func_new_with_env(wasm_store_t* store, |
| const wasm_functype_t* type, |
| wasm_func_callback_with_env_t callback, |
| void* env, |
| void (*finalizer)(void*)) { |
| FuncType wabt_type = *type->As<FuncType>(); |
| auto lambda = [=](Thread& thread, const Values& wabt_params, |
| Values& wabt_results, Trap::Ptr* out_trap) -> Result { |
| wasm_val_vec_t params, results; |
| wasm_val_vec_new_uninitialized(¶ms, wabt_params.size()); |
| wasm_val_vec_new_uninitialized(&results, wabt_results.size()); |
| FromWabtValues(store->I, params.data, wabt_type.params, wabt_params); |
| auto trap = callback(env, params.data, results.data); |
| wasm_val_vec_delete(¶ms); |
| if (trap) { |
| *out_trap = trap->I.As<Trap>(); |
| wasm_trap_delete(trap); |
| // Can't use wasm_val_vec_delete since it wasn't populated. |
| delete[] results.data; |
| return Result::Error; |
| } |
| wabt_results = ToWabtValues(results.data, results.size); |
| wasm_val_vec_delete(&results); |
| return Result::Ok; |
| }; |
| |
| // TODO: This finalizer is different from the host_info finalizer. |
| return new wasm_func_t{HostFunc::New(store->I, wabt_type, lambda)}; |
| } |
| |
| own wasm_functype_t* wasm_func_type(const wasm_func_t* func) { |
| TRACE0(); |
| return new wasm_functype_t{func->As<Func>()->type()}; |
| } |
| |
| size_t wasm_func_result_arity(const wasm_func_t* func) { |
| return func->As<Func>()->type().results.size(); |
| } |
| |
| size_t wasm_func_param_arity(const wasm_func_t* func) { |
| return func->As<Func>()->type().params.size(); |
| } |
| |
| own wasm_trap_t* wasm_func_call(const wasm_func_t* f, |
| const wasm_val_t args[], |
| wasm_val_t results[]) { |
| // TODO: get some information about the function; name/index |
| // TRACE("%d", f->index); |
| |
| auto&& func_type = f->As<Func>()->type(); |
| Values wabt_args = ToWabtValues(args, func_type.params.size()); |
| Values wabt_results; |
| Trap::Ptr trap; |
| if (Failed( |
| f->As<Func>()->Call(*f->I.store(), wabt_args, wabt_results, &trap))) { |
| return new wasm_trap_t{trap}; |
| } |
| FromWabtValues(*f->I.store(), results, func_type.results, wabt_results); |
| return nullptr; |
| } |
| |
| // wasm_globaltype |
| |
| own wasm_globaltype_t* wasm_globaltype_new(own wasm_valtype_t* type, |
| wasm_mutability_t mut) { |
| assert(type); |
| auto* result = new wasm_globaltype_t{GlobalType{ |
| type->I, mut == WASM_CONST ? Mutability::Const : Mutability::Var}}; |
| wasm_valtype_delete(type); |
| return result; |
| } |
| |
| wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t* type) { |
| assert(type); |
| return FromWabtMutability(type->As<GlobalType>()->mut); |
| } |
| |
| const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t* type) { |
| assert(type); |
| return &type->valtype; |
| } |
| |
| // wasm_tabletype |
| |
| own wasm_tabletype_t* wasm_tabletype_new(own wasm_valtype_t* type, |
| const wasm_limits_t* limits) { |
| return new wasm_tabletype_t{type, limits}; |
| } |
| |
| const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t* type) { |
| return &type->elemtype; |
| } |
| |
| const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t* type) { |
| return &type->limits; |
| } |
| |
| // wasm_memorytype |
| |
| own wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t* limits) { |
| return new wasm_memorytype_t(limits); |
| } |
| |
| const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t* t) { |
| return &t->limits; |
| } |
| |
| // wasm_global |
| |
| own wasm_global_t* wasm_global_new(wasm_store_t* store, |
| const wasm_globaltype_t* type, |
| const wasm_val_t* val) { |
| assert(store && type); |
| TypedValue value = ToWabtValue(*val); |
| TRACE("%s", TypedValueToString(value).c_str()); |
| return new wasm_global_t{ |
| Global::New(store->I, *type->As<GlobalType>(), value.value)}; |
| } |
| |
| own wasm_globaltype_t* wasm_global_type(const wasm_global_t* global) { |
| return new wasm_globaltype_t{global->As<Global>()->type()}; |
| } |
| |
| void wasm_global_get(const wasm_global_t* global, own wasm_val_t* out) { |
| assert(global); |
| TRACE0(); |
| TypedValue tv{global->As<Global>()->type().type, global->As<Global>()->Get()}; |
| TRACE(" -> %s", TypedValueToString(tv).c_str()); |
| *out = FromWabtValue(*global->I.store(), tv); |
| } |
| |
| void wasm_global_set(wasm_global_t* global, const wasm_val_t* val) { |
| TRACE0(); |
| if (wasm_valkind_is_ref(val->kind)) { |
| global->As<Global>()->Set(*global->I.store(), val->of.ref->I->self()); |
| } else { |
| global->As<Global>()->UnsafeSet(ToWabtValue(*val).value); |
| } |
| } |
| |
| // wasm_table |
| |
| own wasm_table_t* wasm_table_new(wasm_store_t* store, |
| const wasm_tabletype_t* type, |
| wasm_ref_t* init) { |
| return new wasm_table_t{Table::New(store->I, *type->As<TableType>())}; |
| } |
| |
| own wasm_tabletype_t* wasm_table_type(const wasm_table_t* table) { |
| return new wasm_tabletype_t{table->As<Table>()->type()}; |
| } |
| |
| wasm_table_size_t wasm_table_size(const wasm_table_t* table) { |
| return table->As<Table>()->size(); |
| } |
| |
| own wasm_ref_t* wasm_table_get(const wasm_table_t* table, |
| wasm_table_size_t index) { |
| Ref ref; |
| if (Failed(table->As<Table>()->Get(index, &ref))) { |
| return nullptr; |
| } |
| if (ref == Ref::Null) { |
| return nullptr; |
| } |
| return new wasm_ref_t{table->I.store()->UnsafeGet<Object>(ref)}; |
| } |
| |
| bool wasm_table_set(wasm_table_t* table, |
| wasm_table_size_t index, |
| wasm_ref_t* ref) { |
| return Succeeded(table->As<Table>()->Set(*table->I.store(), index, |
| ref ? ref->I->self() : Ref::Null)); |
| } |
| |
| bool wasm_table_grow(wasm_table_t* table, |
| wasm_table_size_t delta, |
| wasm_ref_t* init) { |
| return Succeeded(table->As<Table>()->Grow( |
| *table->I.store(), delta, init ? init->I->self() : Ref::Null)); |
| } |
| |
| // wams_memory |
| |
| own wasm_memory_t* wasm_memory_new(wasm_store_t* store, |
| const wasm_memorytype_t* type) { |
| TRACE0(); |
| return new wasm_memory_t{Memory::New(store->I, *type->As<MemoryType>())}; |
| } |
| |
| own wasm_memorytype_t* wasm_memory_type(const wasm_memory_t* memory) { |
| return new wasm_memorytype_t{memory->As<Memory>()->type()}; |
| } |
| |
| byte_t* wasm_memory_data(wasm_memory_t* memory) { |
| return reinterpret_cast<byte_t*>(memory->As<Memory>()->UnsafeData()); |
| } |
| |
| wasm_memory_pages_t wasm_memory_size(const wasm_memory_t* memory) { |
| return memory->As<Memory>()->PageSize(); |
| } |
| |
| size_t wasm_memory_data_size(const wasm_memory_t* memory) { |
| return memory->As<Memory>()->ByteSize(); |
| } |
| |
| bool wasm_memory_grow(wasm_memory_t* memory, wasm_memory_pages_t delta) { |
| return Succeeded(memory->As<Memory>()->Grow(delta)); |
| } |
| |
| // wasm_frame |
| |
| own wasm_frame_t* wasm_frame_copy(const wasm_frame_t* frame) { |
| return new wasm_frame_t{*frame}; |
| } |
| |
| wasm_instance_t* wasm_frame_instance(const wasm_frame_t* frame) { |
| // TODO |
| return nullptr; |
| } |
| |
| size_t wasm_frame_module_offset(const wasm_frame_t* frame) { |
| // TODO |
| return 0; |
| } |
| |
| size_t wasm_frame_func_offset(const wasm_frame_t* frame) { |
| // TODO |
| return 0; |
| } |
| |
| uint32_t wasm_frame_func_index(const wasm_frame_t* frame) { |
| // TODO |
| return 0; |
| } |
| |
| // wasm_externtype |
| |
| wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t* type) { |
| assert(type); |
| return FromWabtExternKind(type->I->kind); |
| } |
| |
| // wasm_extern |
| |
| own wasm_externtype_t* wasm_extern_type(const wasm_extern_t* extern_) { |
| return wasm_externtype_t::New(extern_->As<Extern>()->extern_type().Clone()) |
| .release(); |
| } |
| |
| wasm_externkind_t wasm_extern_kind(const wasm_extern_t* extern_) { |
| return FromWabtExternKind(extern_->As<Extern>()->extern_type().kind); |
| } |
| |
| // wasm_foreign |
| |
| own wasm_foreign_t* wasm_foreign_new(wasm_store_t* store) { |
| return new wasm_foreign_t{Foreign::New(store->I, nullptr)}; |
| } |
| |
| // vector types |
| |
| #define WASM_IMPL_OWN(name) \ |
| void wasm_##name##_delete(own wasm_##name##_t* t) { \ |
| assert(t); \ |
| TRACE0(); \ |
| delete t; \ |
| } |
| |
| WASM_IMPL_OWN(frame); |
| WASM_IMPL_OWN(config); |
| WASM_IMPL_OWN(engine); |
| WASM_IMPL_OWN(store); |
| |
| #define WASM_IMPL_VEC_BASE(name, ptr_or_none) \ |
| void wasm_##name##_vec_new_empty(own wasm_##name##_vec_t* out) { \ |
| TRACE0(); \ |
| wasm_##name##_vec_new_uninitialized(out, 0); \ |
| } \ |
| void wasm_##name##_vec_new_uninitialized(own wasm_##name##_vec_t* vec, \ |
| size_t size) { \ |
| TRACE("%" PRIzx, size); \ |
| vec->size = size; \ |
| vec->data = size ? new wasm_##name##_t ptr_or_none[size] : nullptr; \ |
| } |
| |
| #define WASM_IMPL_VEC_PLAIN(name) \ |
| WASM_IMPL_VEC_BASE(name, ) \ |
| void wasm_##name##_vec_new(own wasm_##name##_vec_t* vec, size_t size, \ |
| own wasm_##name##_t const src[]) { \ |
| TRACE0(); \ |
| wasm_##name##_vec_new_uninitialized(vec, size); \ |
| memcpy(vec->data, src, size * sizeof(wasm_##name##_t)); \ |
| } \ |
| void wasm_##name##_vec_copy(own wasm_##name##_vec_t* out, \ |
| const wasm_##name##_vec_t* vec) { \ |
| TRACE("%" PRIzx, vec->size); \ |
| wasm_##name##_vec_new_uninitialized(out, vec->size); \ |
| memcpy(out->data, vec->data, vec->size * sizeof(wasm_##name##_t)); \ |
| } \ |
| void wasm_##name##_vec_delete(own wasm_##name##_vec_t* vec) { \ |
| TRACE0(); \ |
| delete[] vec->data; \ |
| vec->size = 0; \ |
| } |
| |
| WASM_IMPL_VEC_PLAIN(byte); |
| |
| // Special implementation for wasm_val_t, since it's weird. |
| WASM_IMPL_VEC_BASE(val, ) |
| void wasm_val_vec_new(own wasm_val_vec_t* vec, |
| size_t size, |
| own wasm_val_t const src[]) { |
| TRACE0(); |
| wasm_val_vec_new_uninitialized(vec, size); |
| for (size_t i = 0; i < size; ++i) { |
| vec->data[i] = src[i]; |
| } |
| } |
| |
| void wasm_val_vec_copy(own wasm_val_vec_t* out, const wasm_val_vec_t* vec) { |
| TRACE("%" PRIzx, vec->size); |
| wasm_val_vec_new_uninitialized(out, vec->size); |
| for (size_t i = 0; i < vec->size; ++i) { |
| wasm_val_copy(&out->data[i], &vec->data[i]); |
| } |
| } |
| |
| void wasm_val_vec_delete(own wasm_val_vec_t* vec) { |
| TRACE0(); |
| for (size_t i = 0; i < vec->size; ++i) { |
| wasm_val_delete(&vec->data[i]); |
| } |
| delete[] vec->data; |
| vec->size = 0; |
| } |
| |
| #define WASM_IMPL_VEC_OWN(name) \ |
| WASM_IMPL_VEC_BASE(name, *) \ |
| void wasm_##name##_vec_new(own wasm_##name##_vec_t* vec, size_t size, \ |
| own wasm_##name##_t* const src[]) { \ |
| TRACE0(); \ |
| wasm_##name##_vec_new_uninitialized(vec, size); \ |
| for (size_t i = 0; i < size; ++i) { \ |
| vec->data[i] = src[i]; \ |
| } \ |
| } \ |
| void wasm_##name##_vec_copy(own wasm_##name##_vec_t* out, \ |
| const wasm_##name##_vec_t* vec) { \ |
| TRACE("%" PRIzx, vec->size); \ |
| wasm_##name##_vec_new_uninitialized(out, vec->size); \ |
| for (size_t i = 0; i < vec->size; ++i) { \ |
| out->data[i] = wasm_##name##_copy(vec->data[i]); \ |
| } \ |
| } \ |
| void wasm_##name##_vec_delete(wasm_##name##_vec_t* vec) { \ |
| TRACE0(); \ |
| for (size_t i = 0; i < vec->size; ++i) { \ |
| delete vec->data[i]; \ |
| } \ |
| delete[] vec->data; \ |
| vec->size = 0; \ |
| } |
| |
| WASM_IMPL_VEC_OWN(frame); |
| WASM_IMPL_VEC_OWN(extern); |
| |
| #define WASM_IMPL_TYPE(name) \ |
| WASM_IMPL_OWN(name) \ |
| WASM_IMPL_VEC_OWN(name) \ |
| own wasm_##name##_t* wasm_##name##_copy(wasm_##name##_t* other) { \ |
| TRACE0(); \ |
| return new wasm_##name##_t(*other); \ |
| } |
| |
| WASM_IMPL_TYPE(valtype); |
| WASM_IMPL_TYPE(importtype); |
| WASM_IMPL_TYPE(exporttype); |
| |
| #define WASM_IMPL_TYPE_CLONE(name) \ |
| WASM_IMPL_OWN(name) \ |
| WASM_IMPL_VEC_OWN(name) \ |
| own wasm_##name##_t* wasm_##name##_copy(wasm_##name##_t* other) { \ |
| TRACE0(); \ |
| return static_cast<wasm_##name##_t*>(other->Clone().release()); \ |
| } |
| |
| WASM_IMPL_TYPE_CLONE(functype); |
| WASM_IMPL_TYPE_CLONE(globaltype); |
| WASM_IMPL_TYPE_CLONE(tabletype); |
| WASM_IMPL_TYPE_CLONE(memorytype); |
| WASM_IMPL_TYPE_CLONE(externtype); |
| |
| #define WASM_IMPL_REF_BASE(name) \ |
| WASM_IMPL_OWN(name) \ |
| own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t* ref) { \ |
| TRACE0(); \ |
| return new wasm_##name##_t(*ref); \ |
| } \ |
| bool wasm_##name##_same(const wasm_##name##_t* ref, \ |
| const wasm_##name##_t* other) { \ |
| TRACE0(); \ |
| return ref->I == other->I; \ |
| } \ |
| void* wasm_##name##_get_host_info(const wasm_##name##_t* ref) { \ |
| return ref->I->host_info(); \ |
| } \ |
| void wasm_##name##_set_host_info(wasm_##name##_t* ref, void* info) { \ |
| ref->I->set_host_info(info); \ |
| } \ |
| void wasm_##name##_set_host_info_with_finalizer( \ |
| wasm_##name##_t* ref, void* info, void (*finalizer)(void*)) { \ |
| ref->I->set_host_info(info); \ |
| ref->I->set_finalizer([=](Object* o) { finalizer(o->host_info()); }); \ |
| } |
| |
| #define WASM_IMPL_REF(name) \ |
| WASM_IMPL_REF_BASE(name) \ |
| wasm_ref_t* wasm_##name##_as_ref(wasm_##name##_t* subclass) { \ |
| return subclass; \ |
| } \ |
| wasm_##name##_t* wasm_ref_as_##name(wasm_ref_t* ref) { \ |
| return static_cast<wasm_##name##_t*>(ref); \ |
| } \ |
| const wasm_ref_t* wasm_##name##_as_ref_const( \ |
| const wasm_##name##_t* subclass) { \ |
| return subclass; \ |
| } \ |
| const wasm_##name##_t* wasm_ref_as_##name##_const(const wasm_ref_t* ref) { \ |
| return static_cast<const wasm_##name##_t*>(ref); \ |
| } |
| |
| WASM_IMPL_REF_BASE(ref); |
| |
| WASM_IMPL_REF(extern); |
| WASM_IMPL_REF(foreign); |
| WASM_IMPL_REF(func); |
| WASM_IMPL_REF(global); |
| WASM_IMPL_REF(instance); |
| WASM_IMPL_REF(memory); |
| WASM_IMPL_REF(table); |
| WASM_IMPL_REF(trap); |
| |
| #define WASM_IMPL_SHARABLE_REF(name) \ |
| WASM_IMPL_REF(name) \ |
| WASM_IMPL_OWN(shared_##name) \ |
| own wasm_shared_##name##_t* wasm_##name##_share(const wasm_##name##_t* t) { \ |
| return static_cast<wasm_shared_##name##_t*>( \ |
| const_cast<wasm_##name##_t*>(t)); \ |
| } \ |
| own wasm_##name##_t* wasm_##name##_obtain(wasm_store_t*, \ |
| const wasm_shared_##name##_t* t) { \ |
| return static_cast<wasm_##name##_t*>( \ |
| const_cast<wasm_shared_##name##_t*>(t)); \ |
| } |
| |
| WASM_IMPL_SHARABLE_REF(module) |
| |
| #define WASM_IMPL_EXTERN(name) \ |
| const wasm_##name##type_t* wasm_externtype_as_##name##type_const( \ |
| const wasm_externtype_t* t) { \ |
| return static_cast<const wasm_##name##type_t*>(t); \ |
| } \ |
| wasm_##name##type_t* wasm_externtype_as_##name##type(wasm_externtype_t* t) { \ |
| return static_cast<wasm_##name##type_t*>(t); \ |
| } \ |
| wasm_externtype_t* wasm_##name##type_as_externtype(wasm_##name##type_t* t) { \ |
| return static_cast<wasm_externtype_t*>(t); \ |
| } \ |
| const wasm_externtype_t* wasm_##name##type_as_externtype_const( \ |
| const wasm_##name##type_t* t) { \ |
| return static_cast<const wasm_externtype_t*>(t); \ |
| } \ |
| wasm_extern_t* wasm_##name##_as_extern(wasm_##name##_t* name) { \ |
| return static_cast<wasm_extern_t*>(name); \ |
| } \ |
| const wasm_extern_t* wasm_##name##_as_extern_const( \ |
| const wasm_##name##_t* name) { \ |
| return static_cast<const wasm_extern_t*>(name); \ |
| } \ |
| wasm_##name##_t* wasm_extern_as_##name(wasm_extern_t* ext) { \ |
| return static_cast<wasm_##name##_t*>(ext); \ |
| } |
| |
| WASM_IMPL_EXTERN(table); |
| WASM_IMPL_EXTERN(func); |
| WASM_IMPL_EXTERN(global); |
| WASM_IMPL_EXTERN(memory); |
| |
| } // extern "C" |