Commit aadc5cc1 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[wasm-c-api] Roll 35e1532: Properly support anyref

Change-Id: Ib28e408cb6046fd728ceff6e6bf4005a241664e2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1745340Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63247}
parent 20d77ad7
......@@ -1364,7 +1364,7 @@ void PrepareFunctionData(i::Isolate* isolate,
}
void PushArgs(i::wasm::FunctionSig* sig, const Val args[],
i::wasm::CWasmArgumentsPacker* packer) {
i::wasm::CWasmArgumentsPacker* packer, StoreImpl* store) {
for (size_t i = 0; i < sig->parameter_count(); i++) {
i::wasm::ValueType type = sig->GetParam(i);
switch (type) {
......@@ -1381,9 +1381,15 @@ void PushArgs(i::wasm::FunctionSig* sig, const Val args[],
packer->Push(args[i].f64());
break;
case i::wasm::kWasmAnyRef:
case i::wasm::kWasmFuncRef:
packer->Push(impl(args[i].ref())->v8_object()->ptr());
case i::wasm::kWasmFuncRef: {
Ref* ref = args[i].ref();
if (ref == nullptr) {
packer->Push(store->i_isolate()->factory()->null_value()->ptr());
} else {
packer->Push(impl(ref)->v8_object()->ptr());
}
break;
}
case i::wasm::kWasmExnRef:
// TODO(jkummerow): Implement these.
UNIMPLEMENTED();
......@@ -1415,7 +1421,8 @@ void PopArgs(i::wasm::FunctionSig* sig, Val results[],
case i::wasm::kWasmAnyRef:
case i::wasm::kWasmFuncRef: {
i::Address raw = packer->Pop<i::Address>();
if (raw == i::kNullAddress) {
if (raw == i::kNullAddress ||
i::Object(raw).IsNull(store->i_isolate())) {
results[i] = Val(nullptr);
} else {
i::JSReceiver raw_obj = i::JSReceiver::cast(i::Object(raw));
......@@ -1473,7 +1480,7 @@ auto Func::call(const Val args[], Val results[]) const -> own<Trap*> {
function_data->wasm_call_target().ptr() >> i::kSmiTagSize;
i::wasm::CWasmArgumentsPacker packer(function_data->packed_args_size());
PushArgs(sig, args, &packer);
PushArgs(sig, args, &packer, store);
i::Handle<i::Object> object_ref = instance;
if (function_index <
......@@ -1523,6 +1530,8 @@ auto Func::call(const Val args[], Val results[]) const -> own<Trap*> {
i::Address FuncData::v8_callback(void* data, i::Address argv) {
FuncData* self = reinterpret_cast<FuncData*>(data);
i::Isolate* isolate = impl(self->store)->i_isolate();
i::HandleScope scope(isolate);
const vec<ValType*>& param_types = self->type->params();
const vec<ValType*>& result_types = self->type->results();
......@@ -1555,11 +1564,11 @@ i::Address FuncData::v8_callback(void* data, i::Address argv) {
case FUNCREF: {
i::Address raw = v8::base::ReadUnalignedValue<i::Address>(p);
p += sizeof(raw);
if (raw == i::kNullAddress) {
if (raw == i::kNullAddress || i::Object(raw).IsNull(isolate)) {
params[i] = Val(nullptr);
} else {
i::JSReceiver raw_obj = i::JSReceiver::cast(i::Object(raw));
i::Handle<i::JSReceiver> obj(raw_obj, raw_obj.GetIsolate());
i::Handle<i::JSReceiver> obj(raw_obj, isolate);
params[i] = Val(implement<Ref>::type::make(impl(self->store), obj));
}
break;
......@@ -1575,7 +1584,6 @@ i::Address FuncData::v8_callback(void* data, i::Address argv) {
}
if (trap) {
i::Isolate* isolate = impl(self->store)->i_isolate();
isolate->Throw(*impl(trap.get())->v8_object());
i::Object ex = isolate->pending_exception();
isolate->clear_pending_exception();
......@@ -1731,7 +1739,6 @@ auto Table::make(Store* store_abs, const TableType* type, const Ref* ref)
StoreImpl* store = impl(store_abs);
i::Isolate* isolate = store->i_isolate();
i::HandleScope scope(isolate);
auto enabled_features = i::wasm::WasmFeaturesFromFlags();
// Get "element".
i::wasm::ValueType i_type;
......@@ -1740,13 +1747,11 @@ auto Table::make(Store* store_abs, const TableType* type, const Ref* ref)
i_type = i::wasm::kWasmFuncRef;
break;
case ANYREF:
if (enabled_features.anyref) {
i_type = i::wasm::kWasmAnyRef;
break;
} // Else fall through.
V8_FALLTHROUGH;
DCHECK(i::wasm::WasmFeaturesFromFlags().anyref); // See Engine::make().
i_type = i::wasm::kWasmAnyRef;
break;
default:
UNREACHABLE(); // 'element' must be 'FUNCREF'.
UNREACHABLE();
return nullptr;
}
......@@ -1784,8 +1789,18 @@ auto Table::type() const -> own<TableType*> {
uint32_t min = table->current_length();
uint32_t max;
if (!table->maximum_length().ToUint32(&max)) max = 0xFFFFFFFFu;
// TODO(wasm+): support new element types.
return TableType::make(ValType::make(FUNCREF), Limits(min, max));
ValKind kind;
switch (table->type()) {
case i::wasm::kWasmFuncRef:
kind = FUNCREF;
break;
case i::wasm::kWasmAnyRef:
kind = ANYREF;
break;
default:
UNREACHABLE();
}
return TableType::make(ValType::make(kind), Limits(min, max));
}
auto Table::get(size_t index) const -> own<Ref*> {
......@@ -1795,18 +1810,15 @@ auto Table::get(size_t index) const -> own<Ref*> {
i::HandleScope handle_scope(isolate);
i::Handle<i::Object> result =
i::WasmTableObject::Get(isolate, table, static_cast<uint32_t>(index));
if (!result->IsJSFunction()) return own<Ref*>();
DCHECK(i::WasmExportedFunction::IsWasmExportedFunction(*result) ||
i::WasmCapiFunction::IsWasmCapiFunction(*result));
// TODO(wasm+): other references
return implement<Func>::type::make(impl(this)->store(),
i::Handle<i::JSFunction>::cast(result));
if (result->IsNull(isolate)) return own<Ref*>();
// TODO(jkummerow): If we support both JavaScript and the C-API at the same
// time, we need to handle Smis and other JS primitives here.
DCHECK(result->IsJSReceiver());
return implement<Ref>::type::make(impl(this)->store(),
i::Handle<i::JSReceiver>::cast(result));
}
auto Table::set(size_t index, const Ref* ref) -> bool {
if (ref && !impl(ref)->v8_object()->IsFunction()) {
WASM_UNIMPLEMENTED("non-function table elements");
}
i::Handle<i::WasmTableObject> table = impl(this)->v8_object();
if (index >= table->current_length()) return false;
i::Isolate* isolate = table->GetIsolate();
......@@ -2698,7 +2710,7 @@ void wasm_val_delete(wasm_val_t* v) {
void wasm_val_copy(wasm_val_t* out, const wasm_val_t* v) {
*out = *v;
if (is_ref(reveal_valkind(v->kind))) {
out->of.ref = release_ref(v->of.ref->copy());
out->of.ref = v->of.ref ? release_ref(v->of.ref->copy()) : nullptr;
}
}
......
......@@ -233,6 +233,7 @@ WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
global_imports_(zone),
exports_(zone),
functions_(zone),
tables_(zone),
data_segments_(zone),
indirect_functions_(zone),
globals_(zone),
......@@ -269,15 +270,29 @@ uint32_t WasmModuleBuilder::AddSignature(FunctionSig* sig) {
}
uint32_t WasmModuleBuilder::AllocateIndirectFunctions(uint32_t count) {
DCHECK(allocating_indirect_functions_allowed_);
uint32_t index = static_cast<uint32_t>(indirect_functions_.size());
DCHECK_GE(FLAG_wasm_max_table_size, index);
if (count > FLAG_wasm_max_table_size - index) {
return std::numeric_limits<uint32_t>::max();
}
DCHECK(max_table_size_ == 0 ||
indirect_functions_.size() + count <= max_table_size_);
indirect_functions_.resize(indirect_functions_.size() + count,
WasmElemSegment::kNullIndex);
uint32_t new_size = static_cast<uint32_t>(indirect_functions_.size()) + count;
DCHECK(max_table_size_ == 0 || new_size <= max_table_size_);
indirect_functions_.resize(new_size, WasmElemSegment::kNullIndex);
uint32_t max = max_table_size_ > 0 ? max_table_size_ : new_size;
if (tables_.empty()) {
// This cannot use {AddTable} because that would flip the
// {allocating_indirect_functions_allowed_} flag.
tables_.push_back({kWasmFuncRef, new_size, max, true});
} else {
// There can only be the indirect function table so far, otherwise the
// {allocating_indirect_functions_allowed_} flag would have been false.
DCHECK_EQ(1u, tables_.size());
DCHECK_EQ(kWasmFuncRef, tables_[0].type);
DCHECK(tables_[0].has_maximum);
tables_[0].min_size = new_size;
tables_[0].max_size = max;
}
return index;
}
......@@ -290,6 +305,27 @@ void WasmModuleBuilder::SetMaxTableSize(uint32_t max) {
DCHECK_GE(FLAG_wasm_max_table_size, max);
DCHECK_GE(max, indirect_functions_.size());
max_table_size_ = max;
DCHECK(allocating_indirect_functions_allowed_);
if (!tables_.empty()) {
tables_[0].max_size = max;
}
}
uint32_t WasmModuleBuilder::AddTable(ValueType type, uint32_t min_size) {
#if DEBUG
allocating_indirect_functions_allowed_ = false;
#endif
tables_.push_back({type, min_size, 0, false});
return static_cast<uint32_t>(tables_.size() - 1);
}
uint32_t WasmModuleBuilder::AddTable(ValueType type, uint32_t min_size,
uint32_t max_size) {
#if DEBUG
allocating_indirect_functions_allowed_ = false;
#endif
tables_.push_back({type, min_size, max_size, true});
return static_cast<uint32_t>(tables_.size() - 1);
}
uint32_t WasmModuleBuilder::AddImport(Vector<const char> name,
......@@ -408,21 +444,20 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
FixupSection(buffer, start);
}
// == emit function table ====================================================
if (indirect_functions_.size() > 0) {
// == Emit tables ============================================================
if (tables_.size() > 0) {
size_t start = EmitSection(kTableSectionCode, buffer);
buffer->write_u8(1); // table count
buffer->write_u8(kLocalFuncRef);
buffer->write_u8(kHasMaximumFlag);
buffer->write_size(indirect_functions_.size());
size_t max =
max_table_size_ > 0 ? max_table_size_ : indirect_functions_.size();
DCHECK_GE(max, indirect_functions_.size());
buffer->write_size(max);
buffer->write_size(tables_.size());
for (const WasmTable& table : tables_) {
buffer->write_u8(ValueTypes::ValueTypeCodeFor(table.type));
buffer->write_u8(table.has_maximum ? kHasMaximumFlag : kNoMaximumFlag);
buffer->write_size(table.min_size);
if (table.has_maximum) buffer->write_size(table.max_size);
}
FixupSection(buffer, start);
}
// == emit memory declaration ================================================
// == Emit memory declaration ================================================
{
size_t start = EmitSection(kMemorySectionCode, buffer);
buffer->write_u8(1); // memory count
......@@ -473,7 +508,13 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
buffer->write_u8(kExprGetGlobal);
buffer->write_u32v(global.init.val.global_index);
break;
default: {
case WasmInitExpr::kRefNullConst:
buffer->write_u8(kExprRefNull);
break;
case WasmInitExpr::kRefFuncConst:
UNIMPLEMENTED();
break;
case WasmInitExpr::kNone: {
// No initializer, emit a default value.
switch (global.type) {
case kWasmI32:
......
......@@ -243,6 +243,8 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
uint32_t AllocateIndirectFunctions(uint32_t count);
void SetIndirectFunction(uint32_t indirect, uint32_t direct);
void SetMaxTableSize(uint32_t max);
uint32_t AddTable(ValueType type, uint32_t min_size);
uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size);
void MarkStartFunction(WasmFunctionBuilder* builder);
void AddExport(Vector<const char> name, ImportExportKindCode kind,
uint32_t index);
......@@ -288,6 +290,13 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
WasmInitExpr init;
};
struct WasmTable {
ValueType type;
uint32_t min_size;
uint32_t max_size;
bool has_maximum;
};
struct WasmDataSegment {
ZoneVector<byte> data;
uint32_t dest;
......@@ -300,6 +309,7 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
ZoneVector<WasmGlobalImport> global_imports_;
ZoneVector<WasmExport> exports_;
ZoneVector<WasmFunctionBuilder*> functions_;
ZoneVector<WasmTable> tables_;
ZoneVector<WasmDataSegment> data_segments_;
ZoneVector<uint32_t> indirect_functions_;
ZoneVector<WasmGlobal> globals_;
......@@ -313,6 +323,8 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
#if DEBUG
// Once AddExportedImport is called, no more imports can be added.
bool adding_imports_allowed_ = true;
// Indirect functions must be allocated before adding extra tables.
bool allocating_indirect_functions_allowed_ = true;
#endif
};
......
......@@ -690,6 +690,8 @@ struct WasmInitExpr {
val.global_index = index;
} else if (kind == kRefFuncConst) {
val.function_index = index;
} else if (kind == kRefNullConst) {
// Nothing to do.
} else {
// For the other types, the other initializers should be used.
UNREACHABLE();
......
......@@ -32,6 +32,7 @@ v8_executable("wasm_api_tests") {
"callbacks.cc",
"finalize.cc",
"globals.cc",
"hostref.cc",
"memory.cc",
"reflect.cc",
"run-all-wasm-api-tests.cc",
......
// Copyright 2019 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.
#include "test/wasm-api-tests/wasm-api-test.h"
#include <iostream>
namespace v8 {
namespace internal {
namespace wasm {
using ::wasm::Frame;
using ::wasm::Message;
namespace {
own<Trap*> IdentityCallback(const Val args[], Val results[]) {
results[0] = args[0].copy();
return nullptr;
}
} // namespace
TEST_F(WasmCapiTest, HostRef) {
ValueType rr_reps[] = {kWasmAnyRef, kWasmAnyRef};
ValueType ri_reps[] = {kWasmAnyRef, kWasmI32};
ValueType ir_reps[] = {kWasmI32, kWasmAnyRef};
// Naming convention: result_params_sig.
FunctionSig r_r_sig(1, 1, rr_reps);
FunctionSig v_r_sig(0, 1, rr_reps);
FunctionSig r_v_sig(1, 0, rr_reps);
FunctionSig v_ir_sig(0, 2, ir_reps);
FunctionSig r_i_sig(1, 1, ri_reps);
uint32_t func_index = builder()->AddImport(CStrVector("f"), &r_r_sig);
const bool kMutable = true;
const WasmInitExpr global_init(WasmInitExpr::kRefNullConst, 0);
uint32_t global_index =
builder()->AddGlobal(kWasmAnyRef, kMutable, global_init);
uint32_t table_index = builder()->AddTable(kWasmAnyRef, 10);
byte global_set_code[] = {WASM_SET_GLOBAL(global_index, WASM_GET_LOCAL(0))};
AddExportedFunction(CStrVector("global.set"), global_set_code,
sizeof(global_set_code), &v_r_sig);
byte global_get_code[] = {WASM_GET_GLOBAL(global_index)};
AddExportedFunction(CStrVector("global.get"), global_get_code,
sizeof(global_get_code), &r_v_sig);
byte table_set_code[] = {
WASM_TABLE_SET(table_index, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))};
AddExportedFunction(CStrVector("table.set"), table_set_code,
sizeof(table_set_code), &v_ir_sig);
byte table_get_code[] = {WASM_TABLE_GET(table_index, WASM_GET_LOCAL(0))};
AddExportedFunction(CStrVector("table.get"), table_get_code,
sizeof(table_get_code), &r_i_sig);
byte func_call_code[] = {WASM_CALL_FUNCTION(func_index, WASM_GET_LOCAL(0))};
AddExportedFunction(CStrVector("func.call"), func_call_code,
sizeof(func_call_code), &r_r_sig);
own<FuncType*> func_type =
FuncType::make(vec<ValType*>::make(ValType::make(::wasm::ANYREF)),
vec<ValType*>::make(ValType::make(::wasm::ANYREF)));
own<Func*> callback = Func::make(store(), func_type.get(), IdentityCallback);
Extern* imports[] = {callback.get()};
Instantiate(imports);
const Func* global_set = GetExportedFunction(0);
const Func* global_get = GetExportedFunction(1);
const Func* table_set = GetExportedFunction(2);
const Func* table_get = GetExportedFunction(3);
const Func* func_call = GetExportedFunction(4);
own<Foreign*> host1 = Foreign::make(store());
own<Foreign*> host2 = Foreign::make(store());
host1->set_host_info(reinterpret_cast<void*>(1));
host2->set_host_info(reinterpret_cast<void*>(2));
// Basic checks.
EXPECT_TRUE(host1->copy()->same(host1.get()));
EXPECT_TRUE(host2->copy()->same(host2.get()));
Val val = Val::ref(host1->copy());
EXPECT_TRUE(val.ref()->copy()->same(host1.get()));
own<Ref*> ref = val.release_ref();
EXPECT_EQ(nullptr, val.ref());
EXPECT_TRUE(ref->copy()->same(host1.get()));
// Interact with the Global.
Val args[2];
Val results[1];
own<Trap*> trap = global_get->call(nullptr, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref());
args[0] = Val::ref(host1.get()->copy());
trap = global_set->call(args, nullptr);
EXPECT_EQ(nullptr, trap);
trap = global_get->call(nullptr, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host1.get()));
args[0] = Val::ref(host2.get()->copy());
trap = global_set->call(args, nullptr);
EXPECT_EQ(nullptr, trap);
trap = global_get->call(nullptr, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host2.get()));
args[0] = Val::ref(own<Ref*>());
trap = global_set->call(args, nullptr);
EXPECT_EQ(nullptr, trap);
trap = global_get->call(nullptr, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref());
// Interact with the Table.
args[0] = Val::i32(0);
trap = table_get->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref());
args[0] = Val::i32(1);
trap = table_get->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref());
args[0] = Val::i32(0);
args[1] = Val::ref(host1.get()->copy());
trap = table_set->call(args, nullptr);
EXPECT_EQ(nullptr, trap);
args[0] = Val::i32(1);
args[1] = Val::ref(host2.get()->copy());
trap = table_set->call(args, nullptr);
EXPECT_EQ(nullptr, trap);
args[0] = Val::i32(0);
trap = table_get->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host1.get()));
args[0] = Val::i32(1);
trap = table_get->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host2.get()));
args[0] = Val::i32(0);
args[1] = Val::ref(own<Ref*>());
trap = table_set->call(args, nullptr);
EXPECT_EQ(nullptr, trap);
trap = table_get->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref());
// Interact with the Function.
args[0] = Val::ref(own<Ref*>());
trap = func_call->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref());
args[0] = Val::ref(host1.get()->copy());
trap = func_call->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host1.get()));
args[0] = Val::ref(host2.get()->copy());
trap = func_call->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host2.get()));
}
} // namespace wasm
} // namespace internal
} // namespace v8
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "wasm.h"
#define own
// A function to be called from Wasm code.
own wasm_trap_t* callback(
const wasm_val_t args[], wasm_val_t results[]
) {
printf("Calling back...\n> ");
printf("> %p\n",
args[0].of.ref ? wasm_ref_get_host_info(args[0].of.ref) : NULL);
wasm_val_copy(&results[0], &args[0]);
return NULL;
}
wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) {
if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) {
printf("> Error accessing function export %zu!\n", i);
exit(1);
}
return wasm_extern_as_func(exports->data[i]);
}
own wasm_ref_t* call_v_r(const wasm_func_t* func) {
printf("call_v_r... "); fflush(stdout);
wasm_val_t results[1];
if (wasm_func_call(func, NULL, results)) {
printf("> Error calling function!\n");
exit(1);
}
printf("okay\n");
return results[0].of.ref;
}
void call_r_v(const wasm_func_t* func, wasm_ref_t* ref) {
printf("call_r_v... "); fflush(stdout);
wasm_val_t args[1];
args[0].kind = WASM_ANYREF;
args[0].of.ref = ref;
if (wasm_func_call(func, args, NULL)) {
printf("> Error calling function!\n");
exit(1);
}
printf("okay\n");
}
own wasm_ref_t* call_r_r(const wasm_func_t* func, wasm_ref_t* ref) {
printf("call_r_r... "); fflush(stdout);
wasm_val_t args[1];
args[0].kind = WASM_ANYREF;
args[0].of.ref = ref;
wasm_val_t results[1];
if (wasm_func_call(func, args, results)) {
printf("> Error calling function!\n");
exit(1);
}
printf("okay\n");
return results[0].of.ref;
}
void call_ir_v(const wasm_func_t* func, int32_t i, wasm_ref_t* ref) {
printf("call_ir_v... "); fflush(stdout);
wasm_val_t args[2];
args[0].kind = WASM_I32;
args[0].of.i32 = i;
args[1].kind = WASM_ANYREF;
args[1].of.ref = ref;
if (wasm_func_call(func, args, NULL)) {
printf("> Error calling function!\n");
exit(1);
}
printf("okay\n");
}
own wasm_ref_t* call_i_r(const wasm_func_t* func, int32_t i) {
printf("call_i_r... "); fflush(stdout);
wasm_val_t args[1];
args[0].kind = WASM_I32;
args[0].of.i32 = i;
wasm_val_t results[1];
if (wasm_func_call(func, args, results)) {
printf("> Error calling function!\n");
exit(1);
}
printf("okay\n");
return results[0].of.ref;
}
void check(own wasm_ref_t* actual, const wasm_ref_t* expected) {
if (actual != expected &&
!(actual && expected && wasm_ref_same(actual, expected))) {
printf("> Error reading reference, expected %p, got %p\n",
expected ? wasm_ref_get_host_info(expected) : NULL,
actual ? wasm_ref_get_host_info(actual) : NULL);
exit(1);
}
if (actual) wasm_ref_delete(actual);
}
int main(int argc, const char* argv[]) {
// Initialize.
printf("Initializing...\n");
wasm_engine_t* engine = wasm_engine_new();
wasm_store_t* store = wasm_store_new(engine);
// Load binary.
printf("Loading binary...\n");
FILE* file = fopen("hostref.wasm", "r");
if (!file) {
printf("> Error loading module!\n");
return 1;
}
fseek(file, 0L, SEEK_END);
size_t file_size = ftell(file);
fseek(file, 0L, SEEK_SET);
wasm_byte_vec_t binary;
wasm_byte_vec_new_uninitialized(&binary, file_size);
if (fread(binary.data, file_size, 1, file) != 1) {
printf("> Error loading module!\n");
return 1;
}
fclose(file);
// Compile.
printf("Compiling module...\n");
own wasm_module_t* module = wasm_module_new(store, &binary);
if (!module) {
printf("> Error compiling module!\n");
return 1;
}
wasm_byte_vec_delete(&binary);
// Create external callback function.
printf("Creating callback...\n");
own wasm_functype_t* callback_type = wasm_functype_new_1_1(
wasm_valtype_new(WASM_ANYREF), wasm_valtype_new(WASM_ANYREF));
own wasm_func_t* callback_func =
wasm_func_new(store, callback_type, callback);
wasm_functype_delete(callback_type);
// Instantiate.
printf("Instantiating module...\n");
const wasm_extern_t* imports[] = { wasm_func_as_extern(callback_func) };
own wasm_instance_t* instance = wasm_instance_new(store, module, imports);
if (!instance) {
printf("> Error instantiating module!\n");
return 1;
}
wasm_func_delete(callback_func);
wasm_module_delete(module);
// Extract export.
printf("Extracting exports...\n");
own wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports);
size_t i = 0;
wasm_func_t* global_set = get_export_func(&exports, i++);
wasm_func_t* global_get = get_export_func(&exports, i++);
wasm_func_t* table_set = get_export_func(&exports, i++);
wasm_func_t* table_get = get_export_func(&exports, i++);
wasm_func_t* func_call = get_export_func(&exports, i++);
wasm_instance_delete(instance);
// Create host references.
printf("Creating host references...\n");
own wasm_ref_t* host1 = wasm_foreign_as_ref(wasm_foreign_new(store));
own wasm_ref_t* host2 = wasm_foreign_as_ref(wasm_foreign_new(store));
wasm_ref_set_host_info(host1, (void*)1);
wasm_ref_set_host_info(host2, (void*)2);
// Some sanity checks.
check(NULL, NULL);
check(wasm_ref_copy(host1), host1);
check(wasm_ref_copy(host2), host2);
own wasm_val_t val;
val.kind = WASM_ANYREF;
val.of.ref = wasm_ref_copy(host1);
check(wasm_ref_copy(val.of.ref), host1);
own wasm_ref_t* ref = val.of.ref;
val.of.ref = NULL;
check(wasm_ref_copy(ref), host1);
wasm_ref_delete(ref);
// Interact.
printf("Accessing global...\n");
check(call_v_r(global_get), NULL);
call_r_v(global_set, host1);
check(call_v_r(global_get), host1);
call_r_v(global_set, host2);
check(call_v_r(global_get), host2);
call_r_v(global_set, NULL);
check(call_v_r(global_get), NULL);
printf("Accessing table...\n");
check(call_i_r(table_get, 0), NULL);
check(call_i_r(table_get, 1), NULL);
call_ir_v(table_set, 0, host1);
call_ir_v(table_set, 1, host2);
check(call_i_r(table_get, 0), host1);
check(call_i_r(table_get, 1), host2);
call_ir_v(table_set, 0, NULL);
check(call_i_r(table_get, 0), NULL);
printf("Accessing function...\n");
check(call_r_r(func_call, NULL), NULL);
check(call_r_r(func_call, host1), host1);
check(call_r_r(func_call, host2), host2);
wasm_ref_delete(host1);
wasm_ref_delete(host2);
wasm_extern_vec_delete(&exports);
// Shut down.
printf("Shutting down...\n");
wasm_store_delete(store);
wasm_engine_delete(engine);
// All done.
printf("Done.\n");
return 0;
}
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <cinttypes>
#include "wasm.hh"
// A function to be called from Wasm code.
auto callback(
const wasm::Val args[], wasm::Val results[]
) -> wasm::own<wasm::Trap*> {
std::cout << "Calling back..." << std::endl;
std::cout << "> " << (args[0].ref() ? args[0].ref()->get_host_info() : nullptr) << std::endl;
results[0] = args[0].copy();
return nullptr;
}
auto get_export_func(const wasm::vec<wasm::Extern*>& exports, size_t i) -> const wasm::Func* {
if (exports.size() <= i || !exports[i]->func()) {
std::cout << "> Error accessing function export " << i << "/" << exports.size() << "!" << std::endl;
exit(1);
}
return exports[i]->func();
}
void call_r_v(const wasm::Func* func, const wasm::Ref* ref) {
std::cout << "call_r_v... " << std::flush;
wasm::Val args[1] = {wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref*>())};
if (func->call(args, nullptr)) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
std::cout << "okay" << std::endl;
}
auto call_v_r(const wasm::Func* func) -> wasm::own<wasm::Ref*> {
std::cout << "call_v_r... " << std::flush;
wasm::Val results[1];
if (func->call(nullptr, results)) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
std::cout << "okay" << std::endl;
return results[0].release_ref();
}
auto call_r_r(const wasm::Func* func, const wasm::Ref* ref) -> wasm::own<wasm::Ref*> {
std::cout << "call_r_r... " << std::flush;
wasm::Val args[1] = {wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref*>())};
wasm::Val results[1];
if (func->call(args, results)) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
std::cout << "okay" << std::endl;
return results[0].release_ref();
}
void call_ir_v(const wasm::Func* func, int32_t i, const wasm::Ref* ref) {
std::cout << "call_ir_v... " << std::flush;
wasm::Val args[2] = {wasm::Val::i32(i), wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref*>())};
if (func->call(args, nullptr)) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
std::cout << "okay" << std::endl;
}
auto call_i_r(const wasm::Func* func, int32_t i) -> wasm::own<wasm::Ref*> {
std::cout << "call_i_r... " << std::flush;
wasm::Val args[1] = {wasm::Val::i32(i)};
wasm::Val results[1];
if (func->call(args, results)) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
std::cout << "okay" << std::endl;
return results[0].release_ref();
}
void check(wasm::own<wasm::Ref*> actual, const wasm::Ref* expected) {
if (actual.get() != expected &&
!(actual && expected && actual->same(expected))) {
std::cout << "> Error reading reference, expected "
<< (expected ? expected->get_host_info() : nullptr) << ", got "
<< (actual ? actual->get_host_info() : nullptr) << std::endl;
exit(1);
}
}
void run() {
// Initialize.
std::cout << "Initializing..." << std::endl;
auto engine = wasm::Engine::make();
auto store_ = wasm::Store::make(engine.get());
auto store = store_.get();
// Load binary.
std::cout << "Loading binary..." << std::endl;
std::ifstream file("hostref.wasm");
file.seekg(0, std::ios_base::end);
auto file_size = file.tellg();
file.seekg(0);
auto binary = wasm::vec<byte_t>::make_uninitialized(file_size);
file.read(binary.get(), file_size);
file.close();
if (file.fail()) {
std::cout << "> Error loading module!" << std::endl;
return;
}
// Compile.
std::cout << "Compiling module..." << std::endl;
auto module = wasm::Module::make(store, binary);
if (!module) {
std::cout << "> Error compiling module!" << std::endl;
return;
}
// Create external callback function.
std::cout << "Creating callback..." << std::endl;
auto callback_type = wasm::FuncType::make(
wasm::vec<wasm::ValType*>::make(wasm::ValType::make(wasm::ANYREF)),
wasm::vec<wasm::ValType*>::make(wasm::ValType::make(wasm::ANYREF))
);
auto callback_func = wasm::Func::make(store, callback_type.get(), callback);
// Instantiate.
std::cout << "Instantiating module..." << std::endl;
wasm::Extern* imports[] = {callback_func.get()};
auto instance = wasm::Instance::make(store, module.get(), imports);
if (!instance) {
std::cout << "> Error instantiating module!" << std::endl;
return;
}
// Extract export.
std::cout << "Extracting exports..." << std::endl;
auto exports = instance->exports();
size_t i = 0;
auto global_set = get_export_func(exports, i++);
auto global_get = get_export_func(exports, i++);
auto table_set = get_export_func(exports, i++);
auto table_get = get_export_func(exports, i++);
auto func_call = get_export_func(exports, i++);
// Create host references.
std::cout << "Creating host references..." << std::endl;
auto host1 = wasm::Foreign::make(store);
auto host2 = wasm::Foreign::make(store);
host1->set_host_info(reinterpret_cast<void*>(1));
host2->set_host_info(reinterpret_cast<void*>(2));
// Some sanity checks.
check(nullptr, nullptr);
check(host1->copy(), host1.get());
check(host2->copy(), host2.get());
wasm::Val val = wasm::Val::ref(host1->copy());
check(val.ref()->copy(), host1.get());
auto ref = val.release_ref();
assert(val.ref() == nullptr);
check(ref->copy(), host1.get());
// Interact.
std::cout << "Accessing global..." << std::endl;
check(call_v_r(global_get), nullptr);
call_r_v(global_set, host1.get());
check(call_v_r(global_get), host1.get());
call_r_v(global_set, host2.get());
check(call_v_r(global_get), host2.get());
call_r_v(global_set, nullptr);
check(call_v_r(global_get), nullptr);
std::cout << "Accessing table..." << std::endl;
check(call_i_r(table_get, 0), nullptr);
check(call_i_r(table_get, 1), nullptr);
call_ir_v(table_set, 0, host1.get());
call_ir_v(table_set, 1, host2.get());
check(call_i_r(table_get, 0), host1.get());
check(call_i_r(table_get, 1), host2.get());
call_ir_v(table_set, 0, nullptr);
check(call_i_r(table_get, 0), nullptr);
std::cout << "Accessing function..." << std::endl;
check(call_r_r(func_call, nullptr), nullptr);
check(call_r_r(func_call, host1.get()), host1.get());
check(call_r_r(func_call, host2.get()), host2.get());
// Shut down.
std::cout << "Shutting down..." << std::endl;
}
int main(int argc, const char* argv[]) {
run();
std::cout << "Done." << std::endl;
return 0;
}
(module
(import "" "f" (func $fun (param anyref) (result anyref)))
(global $glob (mut anyref) (ref.null))
(table $tab 10 anyref)
(func (export "global.set") (param $r anyref)
(global.set $glob (local.get $r))
)
(func (export "global.get") (result anyref)
(global.get $glob)
)
(func (export "table.set") (param $i i32) (param $r anyref)
(table.set $tab (local.get $i) (local.get $r))
)
(func (export "table.get") (param $i i32) (result anyref)
(table.get $tab (local.get $i))
)
(func (export "func.call") (param $r anyref) (result anyref)
(call $fun (local.get $r))
)
)
......@@ -534,7 +534,7 @@ public:
auto release_ref() -> own<Ref*> {
assert(is_ref());
auto ref = impl_.ref;
ref = nullptr;
impl_.ref = nullptr;
return own<Ref*>(ref);
}
......
......@@ -38,7 +38,7 @@ CLANG_PATH = os.path.join(CHECKOUT_PATH, "third_party", "llvm-build",
"Release+Asserts", "bin")
EXAMPLES = ["hello", "callback", "trap", "reflect", "global", "table",
"memory", "finalize", "serialize", "threads"]
"memory", "finalize", "serialize", "threads", "hostref"]
CLANG = {
"name": "Clang",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment