/* * 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 #include "wabt/binary-reader.h" #include "wabt/cast.h" #include "wabt/error-formatter.h" #include "wabt/interp/binary-reader-interp.h" #include "wabt/interp/interp-util.h" #include "wabt/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 s_log_stream; static std::unique_ptr 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_vec_t* values); static void FromWabtValues(Store& store, wasm_val_vec_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 New(std::unique_ptr); std::unique_ptr 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 T* As() const { return cast(I.get()); } std::unique_ptr I; protected: wasm_externtype_t(std::unique_ptr 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{std::make_unique( ToWabtValueTypes(params), ToWabtValueTypes(results))}, params(*params), results(*results) {} wasm_functype_t(FuncType ft) : wasm_externtype_t{std::make_unique(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{std::make_unique(type->I, ToWabtMutability(mut))}, valtype{*type} { wasm_valtype_delete(type); } wasm_globaltype_t(GlobalType gt) : wasm_externtype_t{std::make_unique(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{std::make_unique(type->I, ToWabtLimits(*limits))}, elemtype(*type), limits(*limits) { wasm_valtype_delete(type); } wasm_tabletype_t(TableType tt) : wasm_externtype_t{std::make_unique(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{std::make_unique( ToWabtLimits(*limits), WABT_DEFAULT_PAGE_SIZE)}, // wasm-c-api doesn't support // custom-page-sizes yet limits{*limits} {} wasm_memorytype_t(MemoryType mt) : wasm_externtype_t{std::make_unique(mt)}, limits{FromWabtLimits(mt.limits)} {} // Stored here because API requires returning pointers. wasm_limits_t limits; }; // static std::unique_ptr wasm_externtype_t::New( std::unique_ptr ptr) { switch (ptr->kind) { case ExternKind::Func: return std::make_unique(*cast(ptr.get())); case ExternKind::Table: return std::make_unique(*cast(ptr.get())); case ExternKind::Memory: return std::make_unique(*cast(ptr.get())); case ExternKind::Global: return std::make_unique(*cast(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(I.module.data())}, name{I.name.size(), const_cast(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 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(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 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 ptr) : I(ptr) {} template T* As() const { return cast(I.get()); } RefPtr I; }; struct wasm_frame_t { Frame I; }; struct wasm_trap_t : wasm_ref_t { wasm_trap_t(RefPtr ptr) : wasm_ref_t(ptr) {} }; struct wasm_foreign_t : wasm_ref_t { wasm_foreign_t(RefPtr ptr) : wasm_ref_t(ptr) {} }; struct wasm_module_t : wasm_ref_t { wasm_module_t(RefPtr 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 ptr) : wasm_ref_t(ptr) {} }; struct wasm_func_t : wasm_extern_t { wasm_func_t(RefPtr ptr) : wasm_extern_t(ptr) {} }; struct wasm_global_t : wasm_extern_t { wasm_global_t(RefPtr ptr) : wasm_extern_t(ptr) {} }; struct wasm_table_t : wasm_extern_t { wasm_table_t(RefPtr ptr) : wasm_extern_t(ptr) {} }; struct wasm_memory_t : wasm_extern_t { wasm_memory_t(RefPtr ptr) : wasm_extern_t(ptr) {} }; struct wasm_instance_t : wasm_ref_t { wasm_instance_t(RefPtr 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(); break; case Type::I64: out_value.kind = WASM_I64; out_value.of.i64 = tv.value.Get(); break; case Type::F32: out_value.kind = WASM_F32; out_value.of.f32 = tv.value.Get(); break; case Type::F64: out_value.kind = WASM_F64; out_value.of.f64 = tv.value.Get(); break; case Type::FuncRef: { Ref ref = tv.value.Get(); out_value.kind = WASM_FUNCREF; out_value.of.ref = new wasm_func_t(store.UnsafeGet(ref)); break; } case Type::ExternRef: { Ref ref = tv.value.Get(); out_value.kind = WASM_ANYREF; out_value.of.ref = new wasm_foreign_t(store.UnsafeGet(ref)); break; } default: TRACE("unexpected wabt type: %d", static_cast(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_vec_t* values) { Values result; for (size_t i = 0; i < values->size; ++i) { result.push_back(ToWabtValue(values->data[i]).value); } return result; } static void FromWabtValues(Store& store, wasm_val_vec_t* values, const ValueTypes& types, const Values& wabt_values) { assert(types.size() == wabt_values.size()); assert(types.size() == values->size); for (size_t i = 0; i < types.size(); ++i) { values->data[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()->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("", 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()->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()->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_vec_t* 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()->import_types().size(); i++) { import_refs.push_back(imports->data[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()->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(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()); 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(); 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, ¶ms, wabt_type.params, wabt_params); auto trap = callback(¶ms, &results); wasm_val_vec_delete(¶ms); if (trap) { *out_trap = trap->I.As(); 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); 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(); 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, ¶ms, wabt_type.params, wabt_params); auto trap = callback(env, ¶ms, &results); wasm_val_vec_delete(¶ms); if (trap) { *out_trap = trap->I.As(); 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); 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()->type()}; } size_t wasm_func_result_arity(const wasm_func_t* func) { return func->As()->type().results.size(); } size_t wasm_func_param_arity(const wasm_func_t* func) { return func->As()->type().params.size(); } own wasm_trap_t* wasm_func_call(const wasm_func_t* f, const wasm_val_vec_t* args, wasm_val_vec_t* results) { // TODO: get some information about the function; name/index // TRACE("%d", f->index); auto&& func_type = f->As()->type(); Values wabt_args = ToWabtValues(args); Values wabt_results; Trap::Ptr trap; if (Failed( f->As()->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()->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(), value.value)}; } own wasm_globaltype_t* wasm_global_type(const wasm_global_t* global) { return new wasm_globaltype_t{global->As()->type()}; } void wasm_global_get(const wasm_global_t* global, own wasm_val_t* out) { assert(global); TRACE0(); TypedValue tv{global->As()->type().type, global->As()->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()->Set(*global->I.store(), val->of.ref->I->self()); } else { global->As()->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())}; } own wasm_tabletype_t* wasm_table_type(const wasm_table_t* table) { return new wasm_tabletype_t{table->As
()->type()}; } wasm_table_size_t wasm_table_size(const wasm_table_t* table) { return table->As
()->size(); } own wasm_ref_t* wasm_table_get(const wasm_table_t* table, wasm_table_size_t index) { Ref ref; if (Failed(table->As
()->Get(index, &ref))) { return nullptr; } if (ref == Ref::Null) { return nullptr; } return new wasm_ref_t{table->I.store()->UnsafeGet(ref)}; } bool wasm_table_set(wasm_table_t* table, wasm_table_size_t index, wasm_ref_t* ref) { return Succeeded(table->As
()->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
()->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())}; } own wasm_memorytype_t* wasm_memory_type(const wasm_memory_t* memory) { return new wasm_memorytype_t{memory->As()->type()}; } byte_t* wasm_memory_data(wasm_memory_t* memory) { return reinterpret_cast(memory->As()->UnsafeData()); } wasm_memory_pages_t wasm_memory_size(const wasm_memory_t* memory) { return memory->As()->PageSize(); } size_t wasm_memory_data_size(const wasm_memory_t* memory) { return memory->As()->ByteSize(); } bool wasm_memory_grow(wasm_memory_t* memory, wasm_memory_pages_t delta) { return Succeeded(memory->As()->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_type().Clone()) .release(); } wasm_externkind_t wasm_extern_kind(const wasm_extern_t* extern_) { return FromWabtExternKind(extern_->As()->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(const 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(const wasm_##name##_t* other) { \ TRACE0(); \ return static_cast(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(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(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( \ const_cast(t)); \ } \ own wasm_##name##_t* wasm_##name##_obtain(wasm_store_t*, \ const wasm_shared_##name##_t* t) { \ return static_cast( \ const_cast(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(t); \ } \ wasm_##name##type_t* wasm_externtype_as_##name##type(wasm_externtype_t* t) { \ return static_cast(t); \ } \ wasm_externtype_t* wasm_##name##type_as_externtype(wasm_##name##type_t* t) { \ return static_cast(t); \ } \ const wasm_externtype_t* wasm_##name##type_as_externtype_const( \ const wasm_##name##type_t* t) { \ return static_cast(t); \ } \ wasm_extern_t* wasm_##name##_as_extern(wasm_##name##_t* name) { \ return static_cast(name); \ } \ const wasm_extern_t* wasm_##name##_as_extern_const( \ const wasm_##name##_t* name) { \ return static_cast(name); \ } \ wasm_##name##_t* wasm_extern_as_##name(wasm_extern_t* ext) { \ return static_cast(ext); \ } WASM_IMPL_EXTERN(table); WASM_IMPL_EXTERN(func); WASM_IMPL_EXTERN(global); WASM_IMPL_EXTERN(memory); } // extern "C"