Commit 2d17bf79 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[wasm-c-api] Roll 3094fe2: Implement Global::get/set for refs

Change-Id: Ibab34553f1499bd5dee7cf7477284783cc0660fc
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1763534Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63318}
parent 3ec10365
...@@ -1340,6 +1340,17 @@ auto Func::result_arity() const -> size_t { ...@@ -1340,6 +1340,17 @@ auto Func::result_arity() const -> size_t {
namespace { namespace {
own<Ref> V8RefValueToWasm(StoreImpl* store, i::Handle<i::Object> value) {
if (value->IsNull(store->i_isolate())) return nullptr;
return implement<Ref>::type::make(store,
i::Handle<i::JSReceiver>::cast(value));
}
i::Handle<i::Object> WasmRefToV8(i::Isolate* isolate, const Ref* ref) {
if (ref == nullptr) return i::ReadOnlyRoots(isolate).null_value_handle();
return impl(ref)->v8_object();
}
void PrepareFunctionData(i::Isolate* isolate, void PrepareFunctionData(i::Isolate* isolate,
i::Handle<i::WasmExportedFunctionData> function_data, i::Handle<i::WasmExportedFunctionData> function_data,
i::wasm::FunctionSig* sig) { i::wasm::FunctionSig* sig) {
...@@ -1378,15 +1389,9 @@ void PushArgs(i::wasm::FunctionSig* sig, const Val args[], ...@@ -1378,15 +1389,9 @@ void PushArgs(i::wasm::FunctionSig* sig, const Val args[],
packer->Push(args[i].f64()); packer->Push(args[i].f64());
break; break;
case i::wasm::kWasmAnyRef: case i::wasm::kWasmAnyRef:
case i::wasm::kWasmFuncRef: { case i::wasm::kWasmFuncRef:
Ref* ref = args[i].ref(); packer->Push(WasmRefToV8(store->i_isolate(), args[i].ref())->ptr());
if (ref == nullptr) {
packer->Push(store->i_isolate()->factory()->null_value()->ptr());
} else {
packer->Push(impl(ref)->v8_object()->ptr());
}
break; break;
}
case i::wasm::kWasmExnRef: case i::wasm::kWasmExnRef:
// TODO(jkummerow): Implement these. // TODO(jkummerow): Implement these.
UNIMPLEMENTED(); UNIMPLEMENTED();
...@@ -1418,14 +1423,8 @@ void PopArgs(i::wasm::FunctionSig* sig, Val results[], ...@@ -1418,14 +1423,8 @@ void PopArgs(i::wasm::FunctionSig* sig, Val results[],
case i::wasm::kWasmAnyRef: case i::wasm::kWasmAnyRef:
case i::wasm::kWasmFuncRef: { case i::wasm::kWasmFuncRef: {
i::Address raw = packer->Pop<i::Address>(); i::Address raw = packer->Pop<i::Address>();
if (raw == i::kNullAddress || i::Handle<i::Object> obj(i::Object(raw), store->i_isolate());
i::Object(raw).IsNull(store->i_isolate())) { results[i] = Val(V8RefValueToWasm(store, obj));
results[i] = Val(nullptr);
} else {
i::JSReceiver raw_obj = i::JSReceiver::cast(i::Object(raw));
i::Handle<i::JSReceiver> obj(raw_obj, store->i_isolate());
results[i] = Val(implement<Ref>::type::make(store, obj));
}
break; break;
} }
case i::wasm::kWasmExnRef: case i::wasm::kWasmExnRef:
...@@ -1527,7 +1526,8 @@ auto Func::call(const Val args[], Val results[]) const -> own<Trap> { ...@@ -1527,7 +1526,8 @@ auto Func::call(const Val args[], Val results[]) const -> own<Trap> {
i::Address FuncData::v8_callback(void* data, i::Address argv) { i::Address FuncData::v8_callback(void* data, i::Address argv) {
FuncData* self = reinterpret_cast<FuncData*>(data); FuncData* self = reinterpret_cast<FuncData*>(data);
i::Isolate* isolate = impl(self->store)->i_isolate(); StoreImpl* store = impl(self->store);
i::Isolate* isolate = store->i_isolate();
i::HandleScope scope(isolate); i::HandleScope scope(isolate);
const ownvec<ValType>& param_types = self->type->params(); const ownvec<ValType>& param_types = self->type->params();
...@@ -1561,13 +1561,8 @@ i::Address FuncData::v8_callback(void* data, i::Address argv) { ...@@ -1561,13 +1561,8 @@ i::Address FuncData::v8_callback(void* data, i::Address argv) {
case FUNCREF: { case FUNCREF: {
i::Address raw = v8::base::ReadUnalignedValue<i::Address>(p); i::Address raw = v8::base::ReadUnalignedValue<i::Address>(p);
p += sizeof(raw); p += sizeof(raw);
if (raw == i::kNullAddress || i::Object(raw).IsNull(isolate)) { i::Handle<i::Object> obj(i::Object(raw), isolate);
params[i] = Val(nullptr); params[i] = Val(V8RefValueToWasm(store, obj));
} else {
i::JSReceiver raw_obj = i::JSReceiver::cast(i::Object(raw));
i::Handle<i::JSReceiver> obj(raw_obj, isolate);
params[i] = Val(implement<Ref>::type::make(impl(self->store), obj));
}
break; break;
} }
} }
...@@ -1608,12 +1603,8 @@ i::Address FuncData::v8_callback(void* data, i::Address argv) { ...@@ -1608,12 +1603,8 @@ i::Address FuncData::v8_callback(void* data, i::Address argv) {
break; break;
case ANYREF: case ANYREF:
case FUNCREF: { case FUNCREF: {
if (results[i].ref() == nullptr) {
v8::base::WriteUnalignedValue(p, i::kNullAddress);
} else {
v8::base::WriteUnalignedValue( v8::base::WriteUnalignedValue(
p, impl(results[i].ref())->v8_object()->ptr()); p, WasmRefToV8(isolate, results[i].ref())->ptr());
}
p += sizeof(i::Address); p += sizeof(i::Address);
break; break;
} }
...@@ -1678,15 +1669,11 @@ auto Global::get() const -> Val { ...@@ -1678,15 +1669,11 @@ auto Global::get() const -> Val {
return Val(v8_global->GetF32()); return Val(v8_global->GetF32());
case F64: case F64:
return Val(v8_global->GetF64()); return Val(v8_global->GetF64());
case ANYREF: { case ANYREF:
i::Handle<i::JSReceiver> obj =
i::Handle<i::JSReceiver>::cast(v8_global->GetRef());
return Val(RefImpl<Ref, i::JSReceiver>::make(impl(this)->store(), obj));
}
case FUNCREF: { case FUNCREF: {
i::Handle<i::JSFunction> obj = StoreImpl* store = impl(this)->store();
i::Handle<i::JSFunction>::cast(v8_global->GetRef()); i::HandleScope scope(store->i_isolate());
return Val(implement<Func>::type::make(impl(this)->store(), obj)); return Val(V8RefValueToWasm(store, v8_global->GetRef()));
} }
default: default:
// TODO(wasm+): support new value types // TODO(wasm+): support new value types
...@@ -1706,10 +1693,12 @@ void Global::set(const Val& val) { ...@@ -1706,10 +1693,12 @@ void Global::set(const Val& val) {
case F64: case F64:
return v8_global->SetF64(val.f64()); return v8_global->SetF64(val.f64());
case ANYREF: case ANYREF:
return v8_global->SetAnyRef(impl(val.ref())->v8_object()); return v8_global->SetAnyRef(
WasmRefToV8(impl(this)->store()->i_isolate(), val.ref()));
case FUNCREF: { case FUNCREF: {
bool result = v8_global->SetFuncRef(impl(this)->store()->i_isolate(), i::Isolate* isolate = impl(this)->store()->i_isolate();
impl(val.ref())->v8_object()); bool result =
v8_global->SetFuncRef(isolate, WasmRefToV8(isolate, val.ref()));
DCHECK(result); DCHECK(result);
USE(result); USE(result);
return; return;
...@@ -1807,12 +1796,10 @@ auto Table::get(size_t index) const -> own<Ref> { ...@@ -1807,12 +1796,10 @@ auto Table::get(size_t index) const -> own<Ref> {
i::HandleScope handle_scope(isolate); i::HandleScope handle_scope(isolate);
i::Handle<i::Object> result = i::Handle<i::Object> result =
i::WasmTableObject::Get(isolate, table, static_cast<uint32_t>(index)); i::WasmTableObject::Get(isolate, table, static_cast<uint32_t>(index));
if (result->IsNull(isolate)) return own<Ref>();
// TODO(jkummerow): If we support both JavaScript and the C-API at the same // 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. // time, we need to handle Smis and other JS primitives here.
DCHECK(result->IsJSReceiver()); DCHECK(result->IsNull(isolate) || result->IsJSReceiver());
return implement<Ref>::type::make(impl(this)->store(), return V8RefValueToWasm(impl(this)->store(), result);
i::Handle<i::JSReceiver>::cast(result));
} }
auto Table::set(size_t index, const Ref* ref) -> bool { auto Table::set(size_t index, const Ref* ref) -> bool {
...@@ -1820,10 +1807,7 @@ auto Table::set(size_t index, const Ref* ref) -> bool { ...@@ -1820,10 +1807,7 @@ auto Table::set(size_t index, const Ref* ref) -> bool {
if (index >= table->current_length()) return false; if (index >= table->current_length()) return false;
i::Isolate* isolate = table->GetIsolate(); i::Isolate* isolate = table->GetIsolate();
i::HandleScope handle_scope(isolate); i::HandleScope handle_scope(isolate);
i::Handle<i::Object> obj = i::Handle<i::Object> obj = WasmRefToV8(isolate, ref);
ref ? i::Handle<i::Object>::cast(impl(ref)->v8_object())
: i::Handle<i::Object>::cast(
i::ReadOnlyRoots(isolate).null_value_handle());
i::WasmTableObject::Set(isolate, table, static_cast<uint32_t>(index), obj); i::WasmTableObject::Set(isolate, table, static_cast<uint32_t>(index), obj);
return true; return true;
} }
...@@ -1837,10 +1821,7 @@ auto Table::grow(size_t delta, const Ref* ref) -> bool { ...@@ -1837,10 +1821,7 @@ auto Table::grow(size_t delta, const Ref* ref) -> bool {
i::Handle<i::WasmTableObject> table = impl(this)->v8_object(); i::Handle<i::WasmTableObject> table = impl(this)->v8_object();
i::Isolate* isolate = table->GetIsolate(); i::Isolate* isolate = table->GetIsolate();
i::HandleScope scope(isolate); i::HandleScope scope(isolate);
i::Handle<i::Object> init_value = i::Handle<i::Object> init_value = WasmRefToV8(isolate, ref);
ref == nullptr
? i::Handle<i::Object>::cast(isolate->factory()->null_value())
: i::Handle<i::Object>::cast(impl(ref)->v8_object());
int result = i::WasmTableObject::Grow( int result = i::WasmTableObject::Grow(
isolate, table, static_cast<uint32_t>(delta), init_value); isolate, table, static_cast<uint32_t>(delta), init_value);
return result >= 0; return result >= 0;
...@@ -2629,7 +2610,7 @@ struct borrowed_val { ...@@ -2629,7 +2610,7 @@ struct borrowed_val {
explicit borrowed_val(wasm::Val&& v) : it(std::move(v)) {} explicit borrowed_val(wasm::Val&& v) : it(std::move(v)) {}
borrowed_val(borrowed_val&& that) : it(std::move(that.it)) {} borrowed_val(borrowed_val&& that) : it(std::move(that.it)) {}
~borrowed_val() { ~borrowed_val() {
if (it.is_ref()) it.release_ref(); if (it.is_ref()) it.release_ref().release();
} }
}; };
......
...@@ -35,9 +35,10 @@ TEST_F(WasmCapiTest, HostRef) { ...@@ -35,9 +35,10 @@ TEST_F(WasmCapiTest, HostRef) {
uint32_t func_index = builder()->AddImport(CStrVector("f"), &r_r_sig); uint32_t func_index = builder()->AddImport(CStrVector("f"), &r_r_sig);
const bool kMutable = true; const bool kMutable = true;
const WasmInitExpr global_init(WasmInitExpr::kRefNullConst, 0); const WasmInitExpr global_init(WasmInitExpr::kRefNullConst, 0);
uint32_t global_index = uint32_t global_index = builder()->AddExportedGlobal(
builder()->AddGlobal(kWasmAnyRef, kMutable, global_init); kWasmAnyRef, kMutable, global_init, CStrVector("global"));
uint32_t table_index = builder()->AddTable(kWasmAnyRef, 10); uint32_t table_index = builder()->AddTable(kWasmAnyRef, 10);
builder()->AddExport(CStrVector("table"), kExternalTable, table_index);
byte global_set_code[] = {WASM_SET_GLOBAL(global_index, WASM_GET_LOCAL(0))}; byte global_set_code[] = {WASM_SET_GLOBAL(global_index, WASM_GET_LOCAL(0))};
AddExportedFunction(CStrVector("global.set"), global_set_code, AddExportedFunction(CStrVector("global.set"), global_set_code,
sizeof(global_set_code), &v_r_sig); sizeof(global_set_code), &v_r_sig);
...@@ -62,11 +63,13 @@ TEST_F(WasmCapiTest, HostRef) { ...@@ -62,11 +63,13 @@ TEST_F(WasmCapiTest, HostRef) {
Extern* imports[] = {callback.get()}; Extern* imports[] = {callback.get()};
Instantiate(imports); Instantiate(imports);
const Func* global_set = GetExportedFunction(0); Global* global = GetExportedGlobal(0);
const Func* global_get = GetExportedFunction(1); Table* table = GetExportedTable(1);
const Func* table_set = GetExportedFunction(2); const Func* global_set = GetExportedFunction(2);
const Func* table_get = GetExportedFunction(3); const Func* global_get = GetExportedFunction(3);
const Func* func_call = GetExportedFunction(4); const Func* table_set = GetExportedFunction(4);
const Func* table_get = GetExportedFunction(5);
const Func* func_call = GetExportedFunction(6);
own<Foreign> host1 = Foreign::make(store()); own<Foreign> host1 = Foreign::make(store());
own<Foreign> host2 = Foreign::make(store()); own<Foreign> host2 = Foreign::make(store());
...@@ -107,6 +110,13 @@ TEST_F(WasmCapiTest, HostRef) { ...@@ -107,6 +110,13 @@ TEST_F(WasmCapiTest, HostRef) {
EXPECT_EQ(nullptr, trap); EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref()); EXPECT_EQ(nullptr, results[0].release_ref());
EXPECT_EQ(nullptr, global->get().release_ref());
global->set(Val(host2->copy()));
trap = global_get->call(nullptr, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host2.get()));
EXPECT_TRUE(global->get().release_ref()->same(host2.get()));
// Interact with the Table. // Interact with the Table.
args[0] = Val::i32(0); args[0] = Val::i32(0);
trap = table_get->call(args, results); trap = table_get->call(args, results);
...@@ -140,6 +150,14 @@ TEST_F(WasmCapiTest, HostRef) { ...@@ -140,6 +150,14 @@ TEST_F(WasmCapiTest, HostRef) {
EXPECT_EQ(nullptr, trap); EXPECT_EQ(nullptr, trap);
EXPECT_EQ(nullptr, results[0].release_ref()); EXPECT_EQ(nullptr, results[0].release_ref());
EXPECT_EQ(nullptr, table->get(2));
table->set(2, host1.get());
args[0] = Val::i32(2);
trap = table_get->call(args, results);
EXPECT_EQ(nullptr, trap);
EXPECT_TRUE(results[0].release_ref()->same(host1.get()));
EXPECT_TRUE(table->get(2)->same(host1.get()));
// Interact with the Function. // Interact with the Function.
args[0] = Val::ref(own<Ref>()); args[0] = Val::ref(own<Ref>());
trap = func_call->call(args, results); trap = func_call->call(args, results);
......
...@@ -28,6 +28,23 @@ wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) { ...@@ -28,6 +28,23 @@ wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) {
return wasm_extern_as_func(exports->data[i]); return wasm_extern_as_func(exports->data[i]);
} }
wasm_global_t* get_export_global(const wasm_extern_vec_t* exports, size_t i) {
if (exports->size <= i || !wasm_extern_as_global(exports->data[i])) {
printf("> Error accessing global export %zu!\n", i);
exit(1);
}
return wasm_extern_as_global(exports->data[i]);
}
wasm_table_t* get_export_table(const wasm_extern_vec_t* exports, size_t i) {
if (exports->size <= i || !wasm_extern_as_table(exports->data[i])) {
printf("> Error accessing table export %zu!\n", i);
exit(1);
}
return wasm_extern_as_table(exports->data[i]);
}
own wasm_ref_t* call_v_r(const wasm_func_t* func) { own wasm_ref_t* call_v_r(const wasm_func_t* func) {
printf("call_v_r... "); fflush(stdout); printf("call_v_r... "); fflush(stdout);
wasm_val_t results[1]; wasm_val_t results[1];
...@@ -165,6 +182,8 @@ int main(int argc, const char* argv[]) { ...@@ -165,6 +182,8 @@ int main(int argc, const char* argv[]) {
own wasm_extern_vec_t exports; own wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports); wasm_instance_exports(instance, &exports);
size_t i = 0; size_t i = 0;
wasm_global_t* global = get_export_global(&exports, i++);
wasm_table_t* table = get_export_table(&exports, i++);
wasm_func_t* global_set = get_export_func(&exports, i++); wasm_func_t* global_set = get_export_func(&exports, i++);
wasm_func_t* global_get = 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_set = get_export_func(&exports, i++);
...@@ -190,9 +209,8 @@ int main(int argc, const char* argv[]) { ...@@ -190,9 +209,8 @@ int main(int argc, const char* argv[]) {
val.of.ref = wasm_ref_copy(host1); val.of.ref = wasm_ref_copy(host1);
check(wasm_ref_copy(val.of.ref), host1); check(wasm_ref_copy(val.of.ref), host1);
own wasm_ref_t* ref = val.of.ref; own wasm_ref_t* ref = val.of.ref;
val.of.ref = NULL;
check(wasm_ref_copy(ref), host1); check(wasm_ref_copy(ref), host1);
wasm_ref_delete(ref); wasm_val_delete(&val);
// Interact. // Interact.
printf("Accessing global...\n"); printf("Accessing global...\n");
...@@ -204,6 +222,16 @@ int main(int argc, const char* argv[]) { ...@@ -204,6 +222,16 @@ int main(int argc, const char* argv[]) {
call_r_v(global_set, NULL); call_r_v(global_set, NULL);
check(call_v_r(global_get), NULL); check(call_v_r(global_get), NULL);
wasm_global_get(global, &val);
assert(val.kind == WASM_ANYREF);
check(val.of.ref, NULL);
val.of.ref = host2;
wasm_global_set(global, &val);
check(call_v_r(global_get), host2);
wasm_global_get(global, &val);
assert(val.kind == WASM_ANYREF);
check(val.of.ref, host2);
printf("Accessing table...\n"); printf("Accessing table...\n");
check(call_i_r(table_get, 0), NULL); check(call_i_r(table_get, 0), NULL);
check(call_i_r(table_get, 1), NULL); check(call_i_r(table_get, 1), NULL);
...@@ -214,6 +242,11 @@ int main(int argc, const char* argv[]) { ...@@ -214,6 +242,11 @@ int main(int argc, const char* argv[]) {
call_ir_v(table_set, 0, NULL); call_ir_v(table_set, 0, NULL);
check(call_i_r(table_get, 0), NULL); check(call_i_r(table_get, 0), NULL);
check(wasm_table_get(table, 2), NULL);
wasm_table_set(table, 2, host1);
check(call_i_r(table_get, 2), host1);
check(wasm_table_get(table, 2), host1);
printf("Accessing function...\n"); printf("Accessing function...\n");
check(call_r_r(func_call, NULL), NULL); check(call_r_r(func_call, NULL), NULL);
check(call_r_r(func_call, host1), host1); check(call_r_r(func_call, host1), host1);
......
...@@ -26,6 +26,23 @@ auto get_export_func(const wasm::ownvec<wasm::Extern>& exports, size_t i) -> con ...@@ -26,6 +26,23 @@ auto get_export_func(const wasm::ownvec<wasm::Extern>& exports, size_t i) -> con
return exports[i]->func(); return exports[i]->func();
} }
auto get_export_global(wasm::ownvec<wasm::Extern>& exports, size_t i) -> wasm::Global* {
if (exports.size() <= i || !exports[i]->global()) {
std::cout << "> Error accessing global export " << i << "!" << std::endl;
exit(1);
}
return exports[i]->global();
}
auto get_export_table(wasm::ownvec<wasm::Extern>& exports, size_t i) -> wasm::Table* {
if (exports.size() <= i || !exports[i]->table()) {
std::cout << "> Error accessing table export " << i << "!" << std::endl;
exit(1);
}
return exports[i]->table();
}
void call_r_v(const wasm::Func* func, const wasm::Ref* ref) { void call_r_v(const wasm::Func* func, const wasm::Ref* ref) {
std::cout << "call_r_v... " << std::flush; std::cout << "call_r_v... " << std::flush;
wasm::Val args[1] = {wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref>())}; wasm::Val args[1] = {wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref>())};
...@@ -141,6 +158,8 @@ void run() { ...@@ -141,6 +158,8 @@ void run() {
std::cout << "Extracting exports..." << std::endl; std::cout << "Extracting exports..." << std::endl;
auto exports = instance->exports(); auto exports = instance->exports();
size_t i = 0; size_t i = 0;
auto global = get_export_global(exports, i++);
auto table = get_export_table(exports, i++);
auto global_set = get_export_func(exports, i++); auto global_set = get_export_func(exports, i++);
auto global_get = get_export_func(exports, i++); auto global_get = get_export_func(exports, i++);
auto table_set = get_export_func(exports, i++); auto table_set = get_export_func(exports, i++);
...@@ -175,6 +194,11 @@ void run() { ...@@ -175,6 +194,11 @@ void run() {
call_r_v(global_set, nullptr); call_r_v(global_set, nullptr);
check(call_v_r(global_get), nullptr); check(call_v_r(global_get), nullptr);
check(global->get().release_ref(), nullptr);
global->set(wasm::Val(host2->copy()));
check(call_v_r(global_get), host2.get());
check(global->get().release_ref(), host2.get());
std::cout << "Accessing table..." << std::endl; std::cout << "Accessing table..." << std::endl;
check(call_i_r(table_get, 0), nullptr); check(call_i_r(table_get, 0), nullptr);
check(call_i_r(table_get, 1), nullptr); check(call_i_r(table_get, 1), nullptr);
...@@ -185,6 +209,11 @@ void run() { ...@@ -185,6 +209,11 @@ void run() {
call_ir_v(table_set, 0, nullptr); call_ir_v(table_set, 0, nullptr);
check(call_i_r(table_get, 0), nullptr); check(call_i_r(table_get, 0), nullptr);
check(table->get(2), nullptr);
table->set(2, host1.get());
check(call_i_r(table_get, 2), host1.get());
check(table->get(2), host1.get());
std::cout << "Accessing function..." << std::endl; std::cout << "Accessing function..." << std::endl;
check(call_r_r(func_call, nullptr), nullptr); check(call_r_r(func_call, nullptr), nullptr);
check(call_r_r(func_call, host1.get()), host1.get()); check(call_r_r(func_call, host1.get()), host1.get());
......
(module (module
(import "" "f" (func $fun (param anyref) (result anyref))) (import "" "f" (func $fun (param anyref) (result anyref)))
(global $glob (mut anyref) (ref.null)) (global $glob (export "global") (mut anyref) (ref.null))
(table $tab 10 anyref) (table $tab (export "table") 10 anyref)
(func (export "global.set") (param $r anyref) (func (export "global.set") (param $r anyref)
(global.set $glob (local.get $r)) (global.set $glob (local.get $r))
......
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