diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc index 8a8e133cf5..20df4c9723 100644 --- a/src/compiler/wasm-compiler.cc +++ b/src/compiler/wasm-compiler.cc @@ -3251,7 +3251,7 @@ Node* WasmGraphBuilder::BuildCallToRuntime(Runtime::FunctionId f, Node* WasmGraphBuilder::GetGlobal(uint32_t index) { const wasm::WasmGlobal& global = env_->module->globals[index]; - if (global.type == wasm::ValueType::kWasmAnyRef) { + if (wasm::ValueTypes::IsReferenceType(global.type)) { if (global.mutability && global.imported) { Node* base = nullptr; Node* offset = nullptr; @@ -3282,7 +3282,7 @@ Node* WasmGraphBuilder::GetGlobal(uint32_t index) { Node* WasmGraphBuilder::SetGlobal(uint32_t index, Node* val) { const wasm::WasmGlobal& global = env_->module->globals[index]; - if (global.type == wasm::ValueType::kWasmAnyRef) { + if (wasm::ValueTypes::IsReferenceType(global.type)) { if (global.mutability && global.imported) { Node* base = nullptr; Node* offset = nullptr; diff --git a/src/wasm/function-body-decoder-impl.h b/src/wasm/function-body-decoder-impl.h index 2d8f41c6ff..17b2989235 100644 --- a/src/wasm/function-body-decoder-impl.h +++ b/src/wasm/function-body-decoder-impl.h @@ -2683,17 +2683,10 @@ class WasmFullDecoder : public WasmDecoder { return stack_.data() + old_size; } - V8_INLINE bool IsSubType(ValueType expected, ValueType actual) { - return (expected == actual) || - (expected == kWasmAnyRef && actual == kWasmNullRef) || - (expected == kWasmAnyRef && actual == kWasmAnyFunc) || - (expected == kWasmAnyFunc && actual == kWasmNullRef); - } - V8_INLINE Value Pop(int index, ValueType expected) { auto val = Pop(); - if (!VALIDATE(IsSubType(expected, val.type) || val.type == kWasmVar || - expected == kWasmVar)) { + if (!VALIDATE(ValueTypes::IsSubType(expected, val.type) || + val.type == kWasmVar || expected == kWasmVar)) { this->errorf(val.pc, "%s[%d] expected type %s, found %s of type %s", SafeOpcodeNameAt(this->pc_), index, ValueTypes::TypeName(expected), SafeOpcodeNameAt(val.pc), @@ -2739,7 +2732,7 @@ class WasmFullDecoder : public WasmDecoder { for (uint32_t i = 0; i < merge->arity; ++i) { Value& val = stack_values[i]; Value& old = (*merge)[i]; - if (IsSubType(old.type, val.type)) continue; + if (ValueTypes::IsSubType(old.type, val.type)) continue; // If {val.type} is polymorphic, which results from unreachable, make // it more specific by using the merge value's expected type. // If it is not polymorphic, this is a type error. @@ -2810,7 +2803,7 @@ class WasmFullDecoder : public WasmDecoder { for (uint32_t i = 0; i < num_returns; ++i) { auto& val = stack_values[i]; ValueType expected_type = this->sig_->GetReturn(i); - if (IsSubType(expected_type, val.type)) continue; + if (ValueTypes::IsSubType(expected_type, val.type)) continue; // If {val.type} is polymorphic, which results from unreachable, // make it more specific by using the return's expected type. // If it is not polymorphic, this is a type error. diff --git a/src/wasm/module-decoder.cc b/src/wasm/module-decoder.cc index c60eeba44f..6c3b6ec511 100644 --- a/src/wasm/module-decoder.cc +++ b/src/wasm/module-decoder.cc @@ -117,8 +117,8 @@ ValueType TypeOf(const WasmModule* module, const WasmInitExpr& expr) { return kWasmF32; case WasmInitExpr::kF64Const: return kWasmF64; - case WasmInitExpr::kAnyRefConst: - return kWasmAnyRef; + case WasmInitExpr::kRefNullConst: + return kWasmNullRef; default: UNREACHABLE(); } @@ -1169,7 +1169,7 @@ class ModuleDecoderImpl : public Decoder { ValueTypes::TypeName(module->globals[other_index].type)); } } else { - if (global->type != TypeOf(module, global->init)) { + if (!ValueTypes::IsSubType(global->type, TypeOf(module, global->init))) { errorf(pos, "type error in global initialization, expected %s, got %s", ValueTypes::TypeName(global->type), ValueTypes::TypeName(TypeOf(module, global->init))); @@ -1185,7 +1185,7 @@ class ModuleDecoderImpl : public Decoder { for (WasmGlobal& global : module->globals) { if (global.mutability && global.imported) { global.index = num_imported_mutable_globals++; - } else if (global.type == ValueType::kWasmAnyRef) { + } else if (ValueTypes::IsReferenceType(global.type)) { global.offset = tagged_offset; // All entries in the tagged_globals_buffer have size 1. tagged_offset++; @@ -1435,7 +1435,7 @@ class ModuleDecoderImpl : public Decoder { } case kExprRefNull: { if (enabled_features_.anyref) { - expr.kind = WasmInitExpr::kAnyRefConst; + expr.kind = WasmInitExpr::kRefNullConst; len = 0; break; } diff --git a/src/wasm/module-instantiate.cc b/src/wasm/module-instantiate.cc index 8ce0ae10ac..d1329f24b0 100644 --- a/src/wasm/module-instantiate.cc +++ b/src/wasm/module-instantiate.cc @@ -552,7 +552,6 @@ MaybeHandle InstanceBuilder::LookupImport(uint32_t index, // We pre-validated in the js-api layer that the ffi object is present, and // a JSObject, if the module has imports. DCHECK(!ffi_.is_null()); - // Look up the module first. MaybeHandle result = Object::GetPropertyOrElement( isolate_, ffi_.ToHandleChecked(), module_name); @@ -972,7 +971,7 @@ bool InstanceBuilder::ProcessImportedWasmGlobalObject( DCHECK_LT(global.index, module_->num_imported_mutable_globals); Handle buffer; Address address_or_offset; - if (global.type == kWasmAnyRef) { + if (ValueTypes::IsReferenceType(global.type)) { static_assert(sizeof(global_object->offset()) <= sizeof(Address), "The offset into the globals buffer does not fit into " "the imported_mutable_globals array"); @@ -1050,7 +1049,18 @@ bool InstanceBuilder::ProcessImportedGlobal(Handle instance, return false; } - if (global.type == ValueType::kWasmAnyRef) { + if (ValueTypes::IsReferenceType(global.type)) { + // There shouldn't be any null-ref globals. + DCHECK_NE(ValueType::kWasmNullRef, global.type); + if (global.type == ValueType::kWasmAnyFunc) { + if (!value->IsNull(isolate_) && + !WasmExportedFunction::IsWasmExportedFunction(*value)) { + ReportLinkError( + "imported anyfunc global must be null or an exported function", + import_index, module_name, import_name); + return false; + } + } WriteGlobalAnyRef(global, value); return true; } @@ -1183,7 +1193,7 @@ void InstanceBuilder::InitGlobals() { WriteLittleEndianValue(GetRawGlobalPtr(global), global.init.val.f64_const); break; - case WasmInitExpr::kAnyRefConst: + case WasmInitExpr::kRefNullConst: DCHECK(enabled_.anyref); if (global.imported) break; // We already initialized imported globals. @@ -1375,7 +1385,7 @@ void InstanceBuilder::ProcessExports(Handle instance) { if (global.mutability && global.imported) { Handle buffers_array( instance->imported_mutable_globals_buffers(), isolate_); - if (global.type == kWasmAnyRef) { + if (ValueTypes::IsReferenceType(global.type)) { tagged_buffer = buffers_array->GetValueChecked( isolate_, global.index); // For anyref globals we store the relative offset in the @@ -1398,7 +1408,7 @@ void InstanceBuilder::ProcessExports(Handle instance) { offset = static_cast(global_addr - backing_store); } } else { - if (global.type == kWasmAnyRef) { + if (ValueTypes::IsReferenceType(global.type)) { tagged_buffer = handle(instance->tagged_globals_buffer(), isolate_); } else { untagged_buffer = diff --git a/src/wasm/value-type.h b/src/wasm/value-type.h index 5cb24e7911..ca75585211 100644 --- a/src/wasm/value-type.h +++ b/src/wasm/value-type.h @@ -178,6 +178,21 @@ class StoreType { // A collection of ValueType-related static methods. class V8_EXPORT_PRIVATE ValueTypes { public: + static inline bool IsSubType(ValueType expected, ValueType actual) { + return (expected == actual) || + (expected == kWasmAnyRef && actual == kWasmNullRef) || + (expected == kWasmAnyRef && actual == kWasmAnyFunc) || + (expected == kWasmAnyFunc && actual == kWasmNullRef); + } + + static inline bool IsReferenceType(ValueType type) { + // This function assumes at the moment that it is never called with + // {kWasmNullRef}. If this assumption is wrong, it should be added to the + // result calculation below. + DCHECK_NE(type, kWasmNullRef); + return type == kWasmAnyRef || type == kWasmAnyFunc; + } + static byte MemSize(MachineType type) { return 1 << i::ElementSizeLog2Of(type.representation()); } diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc index 08527ffcb9..2dbad073b7 100644 --- a/src/wasm/wasm-js.cc +++ b/src/wasm/wasm-js.cc @@ -1202,6 +1202,8 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo& args) { type = i::wasm::kWasmF64; } else if (string->StringEquals(v8_str(isolate, "anyref"))) { type = i::wasm::kWasmAnyRef; + } else if (string->StringEquals(v8_str(isolate, "anyfunc"))) { + type = i::wasm::kWasmAnyFunc; } else { thrower.TypeError( "Descriptor property 'value' must be 'i32', 'i64', 'f32', or " @@ -1277,13 +1279,27 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo& args) { if (args.Length() < 2) { // When no inital value is provided, we have to use the WebAssembly // default value 'null', and not the JS default value 'undefined'. - global_obj->SetAnyRef( - handle(i::ReadOnlyRoots(i_isolate).null_value(), i_isolate)); + global_obj->SetAnyRef(i_isolate->factory()->null_value()); break; } global_obj->SetAnyRef(Utils::OpenHandle(*value)); break; } + case i::wasm::kWasmAnyFunc: { + if (args.Length() < 2) { + // When no inital value is provided, we have to use the WebAssembly + // default value 'null', and not the JS default value 'undefined'. + global_obj->SetAnyFunc(i_isolate, i_isolate->factory()->null_value()); + break; + } + + if (!global_obj->SetAnyFunc(i_isolate, Utils::OpenHandle(*value))) { + thrower.TypeError( + "The value of anyfunc globals must be null or an " + "exported function"); + } + break; + } default: UNREACHABLE(); } @@ -1640,7 +1656,8 @@ void WebAssemblyGlobalGetValueCommon( return_value.Set(receiver->GetF64()); break; case i::wasm::kWasmAnyRef: - return_value.Set(Utils::ToLocal(receiver->GetAnyRef())); + case i::wasm::kWasmAnyFunc: + return_value.Set(Utils::ToLocal(receiver->GetRef())); break; default: UNREACHABLE(); @@ -1711,6 +1728,14 @@ void WebAssemblyGlobalSetValue( receiver->SetAnyRef(Utils::OpenHandle(*args[0])); break; } + case i::wasm::kWasmAnyFunc: { + if (!receiver->SetAnyFunc(i_isolate, Utils::OpenHandle(*args[0]))) { + thrower.TypeError( + "value of an anyfunc reference must be either null or an " + "exported function"); + } + break; + } default: UNREACHABLE(); } diff --git a/src/wasm/wasm-objects-inl.h b/src/wasm/wasm-objects-inl.h index 443eefa13d..9e3b70f44f 100644 --- a/src/wasm/wasm-objects-inl.h +++ b/src/wasm/wasm-objects-inl.h @@ -151,8 +151,9 @@ double WasmGlobalObject::GetF64() { return ReadLittleEndianValue(address()); } -Handle WasmGlobalObject::GetAnyRef() { - DCHECK_EQ(type(), wasm::kWasmAnyRef); +Handle WasmGlobalObject::GetRef() { + // We use this getter also for anyfunc. + DCHECK(wasm::ValueTypes::IsReferenceType(type())); return handle(tagged_buffer()->get(offset()), GetIsolate()); } @@ -177,6 +178,16 @@ void WasmGlobalObject::SetAnyRef(Handle value) { tagged_buffer()->set(offset(), *value); } +bool WasmGlobalObject::SetAnyFunc(Isolate* isolate, Handle value) { + DCHECK_EQ(type(), wasm::kWasmAnyFunc); + if (!value->IsNull(isolate) && + !WasmExportedFunction::IsWasmExportedFunction(*value)) { + return false; + } + tagged_buffer()->set(offset(), *value); + return true; +} + // WasmInstanceObject PRIMITIVE_ACCESSORS(WasmInstanceObject, memory_start, byte*, kMemoryStartOffset) PRIMITIVE_ACCESSORS(WasmInstanceObject, memory_size, size_t, kMemorySizeOffset) diff --git a/src/wasm/wasm-objects.cc b/src/wasm/wasm-objects.cc index c7318c73b3..2ada86bb9a 100644 --- a/src/wasm/wasm-objects.cc +++ b/src/wasm/wasm-objects.cc @@ -1327,7 +1327,7 @@ MaybeHandle WasmGlobalObject::New( auto global_obj = Handle::cast( isolate->factory()->NewJSObject(global_ctor)); - if (type == wasm::kWasmAnyRef) { + if (wasm::ValueTypes::IsReferenceType(type)) { DCHECK(maybe_untagged_buffer.is_null()); Handle tagged_buffer; if (!maybe_tagged_buffer.ToHandle(&tagged_buffer)) { diff --git a/src/wasm/wasm-objects.h b/src/wasm/wasm-objects.h index ba964b430d..c92e219752 100644 --- a/src/wasm/wasm-objects.h +++ b/src/wasm/wasm-objects.h @@ -410,13 +410,14 @@ class WasmGlobalObject : public JSObject { inline int64_t GetI64(); inline float GetF32(); inline double GetF64(); - inline Handle GetAnyRef(); + inline Handle GetRef(); inline void SetI32(int32_t value); inline void SetI64(int64_t value); inline void SetF32(float value); inline void SetF64(double value); inline void SetAnyRef(Handle value); + inline bool SetAnyFunc(Isolate* isolate, Handle value); private: // This function returns the address of the global's data in the diff --git a/src/wasm/wasm-opcodes.h b/src/wasm/wasm-opcodes.h index ca443d09bb..71829b6479 100644 --- a/src/wasm/wasm-opcodes.h +++ b/src/wasm/wasm-opcodes.h @@ -609,7 +609,7 @@ struct WasmInitExpr { kI64Const, kF32Const, kF64Const, - kAnyRefConst, + kRefNullConst, } kind; union { diff --git a/test/mjsunit/wasm/anyref-globals.js b/test/mjsunit/wasm/anyref-globals.js index f1facefb28..c0d9de4e69 100644 --- a/test/mjsunit/wasm/anyref-globals.js +++ b/test/mjsunit/wasm/anyref-globals.js @@ -9,33 +9,49 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); (function TestDefaultValue() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); - const g_nullref = builder.addGlobal(kWasmAnyRef, true); - builder.addFunction("main", kSig_r_v) - .addBody([kExprGetGlobal, g_nullref.index]) - .exportAs("main"); + const g_nullref = builder.addGlobal(kWasmAnyRef, true).index; + const g_nullfunc = builder.addGlobal(kWasmAnyFunc, true).index; + builder.addFunction("get_anyref_global", kSig_r_v) + .addBody([kExprGetGlobal, g_nullref]) + .exportAs("get_anyref_global"); + builder.addFunction("get_anyfunc_global", kSig_a_v) + .addBody([kExprGetGlobal, g_nullfunc]) + .exportAs("get_anyfunc_global"); const instance = builder.instantiate(); - assertNull(instance.exports.main()); + assertEquals(null, instance.exports.get_anyref_global()); + assertEquals(null, instance.exports.get_anyfunc_global()); })(); (function TestDefaultValueSecondGlobal() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); const g_setref = builder.addGlobal(kWasmAnyRef, true); + const g_setfunc = builder.addGlobal(kWasmAnyFunc, true); const g_nullref = builder.addGlobal(kWasmAnyRef, true); - builder.addFunction("main", kSig_r_r) + const g_nullfunc = builder.addGlobal(kWasmAnyFunc, true); + builder.addFunction("get_anyref_global", kSig_r_r) .addBody([ - kExprGetLocal, 0, - kExprSetGlobal, g_setref.index, - kExprGetGlobal, g_nullref.index + kExprGetLocal, 0, + kExprSetGlobal, g_setref.index, + kExprGetGlobal, g_nullref.index ]) - .exportAs("main"); + .exportAs("get_anyref_global"); + builder.addFunction("get_anyfunc_global", kSig_a_a) + .addBody([ + kExprGetLocal, 0, + kExprSetGlobal, g_setfunc.index, + kExprGetGlobal, g_nullfunc.index + ]) + .exportAs("get_anyfunc_global"); const instance = builder.instantiate(); - assertNull(instance.exports.main({})); + assertEquals(null, instance.exports.get_anyref_global({})); + assertEquals(null, instance.exports.get_anyfunc_global( + instance.exports.get_anyref_global)); })(); -(function TestGlobalChangeValue() { +(function TestAnyRefGlobalChangeValue() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); // Dummy global for offset. @@ -43,15 +59,35 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); const g = builder.addGlobal(kWasmAnyRef, true); builder.addFunction("main", kSig_r_r) .addBody([ - kExprGetLocal, 0, - kExprSetGlobal, g.index, - kExprGetGlobal, g.index + kExprGetLocal, 0, + kExprSetGlobal, g.index, + kExprGetGlobal, g.index ]) .exportAs("main"); const instance = builder.instantiate(); - const test_value = {hello: 'world'}; + const test_value = { hello: 'world' }; + assertSame(test_value, instance.exports.main(test_value)); +})(); + +(function TestAnyFuncGlobalChangeValue() { + print(arguments.callee.name); + let builder = new WasmModuleBuilder(); + // Dummy global for offset. + builder.addGlobal(kWasmAnyFunc, true); + const g = builder.addGlobal(kWasmAnyFunc, true); + builder.addFunction("main", kSig_a_a) + .addBody([ + kExprGetLocal, 0, + kExprSetGlobal, g.index, + kExprGetGlobal, g.index + ]) + .exportAs("main"); + + const instance = builder.instantiate(); + + const test_value = instance.exports.main; assertSame(test_value, instance.exports.main(test_value)); })(); @@ -64,16 +100,16 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); const g = builder.addGlobal(kWasmAnyRef, true); builder.addFunction("main", kSig_r_r) .addBody([ - kExprGetLocal, 0, - kExprSetGlobal, g.index, - kExprCallFunction, gc_index, // call gc - kExprGetGlobal, g.index + kExprGetLocal, 0, + kExprSetGlobal, g.index, + kExprCallFunction, gc_index, // call gc + kExprGetGlobal, g.index ]) .exportAs("main"); - const instance = builder.instantiate({q: {gc: gc}}); + const instance = builder.instantiate({ q: { gc: gc } }); - const test_value = {hello: 'world'}; + const test_value = { hello: 'world' }; assertSame(test_value, instance.exports.main(test_value)); assertSame(5, instance.exports.main(5)); assertSame("Hello", instance.exports.main("Hello")); @@ -85,20 +121,20 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); const g = builder.addGlobal(kWasmAnyRef, true); builder.addFunction("get_global", kSig_r_v) .addBody([ - kExprGetGlobal, g.index + kExprGetGlobal, g.index ]) .exportAs("get_global"); builder.addFunction("set_global", kSig_v_r) .addBody([ - kExprGetLocal, 0, - kExprSetGlobal, g.index + kExprGetLocal, 0, + kExprSetGlobal, g.index ]) .exportAs("set_global"); const instance = builder.instantiate(); - let test_value = {hello: 'world'}; + let test_value = { hello: 'world' }; instance.exports.set_global(test_value); test_value = null; gc(); @@ -108,38 +144,73 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); assertEquals('world', result.hello); })(); -(function TestImported() { +(function TestImportedAnyRef() { print(arguments.callee.name); function Test(obj) { let builder = new WasmModuleBuilder(); const g = builder.addImportedGlobal('m', 'val', kWasmAnyRef); builder.addFunction('main', kSig_r_v) - .addBody([kExprGetGlobal, g]) - .exportAs('main'); + .addBody([kExprGetGlobal, g]) + .exportAs('main'); - const instance = builder.instantiate({m: {val: obj}}); + const instance = builder.instantiate({ m: { val: obj } }); assertSame(obj, instance.exports.main()); } Test(null); Test(undefined); Test(1653); Test("mystring"); - Test({q: 14}); + Test({ q: 14 }); Test(print); })(); +function dummy_func() { + let builder = new WasmModuleBuilder(); + builder.addFunction("dummy", kSig_i_v) + .addBody([kExprI32Const, 12]) + .exportAs("dummy"); + return builder.instantiate().exports.dummy; +} + +(function TestImportedAnyFunc() { + print(arguments.callee.name); + + let builder = new WasmModuleBuilder(); + const g = builder.addImportedGlobal('m', 'val', kWasmAnyFunc); + builder.addFunction('main', kSig_a_v) + .addBody([kExprGetGlobal, g]) + .exportAs('main'); + + const module = builder.toModule(); + const instance = new WebAssembly.Instance(module, { m: { val: null } }); + assertSame(null, instance.exports.main()); + + const instance2 = new WebAssembly.Instance( + module, { m: { val: instance.exports.main } }); + assertSame(instance.exports.main, instance2.exports.main()); + + assertThrows(() => new WebAssembly.Instance(module, { m: { val: {} } }), + WebAssembly.LinkError); +})(); + (function TestAnyRefGlobalObjectDefaultValue() { print(arguments.callee.name); - let default_init = new WebAssembly.Global({value: 'anyref', mutable: true}); + let default_init = new WebAssembly.Global({ value: 'anyref', mutable: true }); assertSame(null, default_init.value); assertSame(null, default_init.valueOf()); })(); +(function TestAnyFuncGlobalObjectDefaultValue() { + print(arguments.callee.name); + let default_init = new WebAssembly.Global({ value: 'anyfunc', mutable: true }); + assertSame(null, default_init.value); + assertSame(null, default_init.valueOf()); +})(); (function TestAnyRefGlobalObject() { print(arguments.callee.name); function TestGlobal(obj) { - const global = new WebAssembly.Global({value: 'anyref'}, obj); + const global = new WebAssembly.Global({ value: 'anyref' }, obj); assertSame(obj, global.value); assertSame(obj, global.valueOf()); } @@ -148,13 +219,28 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); TestGlobal(undefined); TestGlobal(1663); TestGlobal("testmyglobal"); - TestGlobal({a: 11}); + TestGlobal({ a: 11 }); TestGlobal(print); })(); +(function TestAnyFuncGlobalObject() { + print(arguments.callee.name); + + const dummy = dummy_func(); + const global = new WebAssembly.Global({ value: 'anyfunc' }, dummy); + assertSame(dummy, global.value); + assertSame(dummy, global.valueOf()); + + const global_null = new WebAssembly.Global({ value: 'anyfunc' }, null); + assertSame(null, global_null.value); + assertSame(null, global_null.valueOf()); + + assertThrows(() => new WebAssembly.Global({ value: 'anyfunc' }, {}), TypeError); +})(); + (function TestAnyRefGlobalObjectSetValue() { print(arguments.callee.name); - let global = new WebAssembly.Global({value: 'anyref', mutable: true}); + let global = new WebAssembly.Global({ value: 'anyref', mutable: true }); function TestGlobal(obj) { global.value = obj; @@ -166,32 +252,60 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); assertThrows(() => TestGlobal(undefined), TypeError); TestGlobal(1663); TestGlobal("testmyglobal"); - TestGlobal({a: 11}); + TestGlobal({ a: 11 }); TestGlobal(print); })(); -(function TestExportMutableAnyRefGlobal() { + +(function TestAnyFuncGlobalObjectSetValue() { + print(arguments.callee.name); + let global = new WebAssembly.Global({ value: 'anyfunc', mutable: true }); + + const dummy = dummy_func(); + global.value = dummy; + assertSame(dummy, global.value); + assertSame(dummy, global.valueOf()); + + global.value = null; + assertSame(null, global.value); + assertSame(null, global.valueOf()); + + assertThrows(() => global.value = {}, TypeError); +})(); + +(function TestExportMutableRefGlobal() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); const g1 = builder.addGlobal(kWasmAnyRef, true).exportAs("global1"); + const g2 = builder.addGlobal(kWasmAnyFunc, true).exportAs("global2"); builder.addGlobal(kWasmI32, true); // Dummy. builder.addGlobal(kWasmAnyRef, true); // Dummy. - const g2 = builder.addGlobal(kWasmAnyRef, true).exportAs("global2"); - builder.addFunction("main", kSig_v_rr) + const g3 = builder.addGlobal(kWasmAnyRef, true).exportAs("global3"); + const g4 = builder.addGlobal(kWasmAnyFunc, true).exportAs("global4"); + builder.addFunction("main", + makeSig([kWasmAnyRef, kWasmAnyFunc, kWasmAnyRef, kWasmAnyFunc], [])) .addBody([ - kExprGetLocal, 0, - kExprSetGlobal, g1.index, - kExprGetLocal, 1, - kExprSetGlobal, g2.index + kExprGetLocal, 0, + kExprSetGlobal, g1.index, + kExprGetLocal, 1, + kExprSetGlobal, g2.index, + kExprGetLocal, 2, + kExprSetGlobal, g3.index, + kExprGetLocal, 3, + kExprSetGlobal, g4.index ]) .exportAs("main"); const instance = builder.instantiate(); - const obj1 = {x: 221}; - const obj2 = print; - instance.exports.main(obj1, obj2); + const obj1 = { x: 221 }; + const func2 = instance.exports.main; + const obj3 = print; + const func4 = dummy_func(); + instance.exports.main(obj1, func2, obj3, func4); assertSame(obj1, instance.exports.global1.value); - assertSame(obj2, instance.exports.global2.value); + assertSame(func2, instance.exports.global2.value); + assertSame(obj3, instance.exports.global3.value); + assertSame(func4, instance.exports.global4.value); })(); (function TestImportMutableAnyRefGlobal() { @@ -200,21 +314,38 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); let builder = new WasmModuleBuilder(); const g = builder.addImportedGlobal('m', 'val', kWasmAnyRef, true); builder.addFunction('main', kSig_r_v) - .addBody([kExprGetGlobal, g]) - .exportAs('main'); + .addBody([kExprGetGlobal, g]) + .exportAs('main'); - const global = new WebAssembly.Global({value: 'anyref', mutable: 'true'}, obj); - const instance = builder.instantiate({m: {val: global}}); + const global = new WebAssembly.Global({ value: 'anyref', mutable: 'true' }, obj); + const instance = builder.instantiate({ m: { val: global } }); assertSame(obj, instance.exports.main()); } Test(null); Test(undefined); Test(1653); Test("mystring"); - Test({q: 14}); + Test({ q: 14 }); Test(print); })(); +(function TestImportMutableAnyFuncGlobal() { + print(arguments.callee.name); + function Test(obj) { + let builder = new WasmModuleBuilder(); + const g = builder.addImportedGlobal('m', 'val', kWasmAnyFunc, true); + builder.addFunction('main', kSig_a_v) + .addBody([kExprGetGlobal, g]) + .exportAs('main'); + + const global = new WebAssembly.Global({ value: 'anyfunc', mutable: 'true' }, obj); + const instance = builder.instantiate({ m: { val: global } }); + assertSame(obj, instance.exports.main()); + } + Test(dummy_func()); + Test(null); +})(); + (function TestImportMutableAnyRefGlobalFromOtherInstance() { print(arguments.callee.name); @@ -227,23 +358,23 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); builder1.addFunction("set_globals", kSig_v_rr) .addBody([ - kExprGetLocal, 0, - kExprSetGlobal, g2.index, - kExprGetLocal, 1, - kExprSetGlobal, g3.index, + kExprGetLocal, 0, + kExprSetGlobal, g2.index, + kExprGetLocal, 1, + kExprSetGlobal, g3.index, ]) .exportAs("set_globals"); builder1.addFunction('get_global2', kSig_r_v) - .addBody([kExprGetGlobal, g2.index]) - .exportAs('get_global2'); + .addBody([kExprGetGlobal, g2.index]) + .exportAs('get_global2'); builder1.addFunction('get_global3', kSig_r_v) - .addBody([kExprGetGlobal, g3.index]) - .exportAs('get_global3'); + .addBody([kExprGetGlobal, g3.index]) + .exportAs('get_global3'); const instance1 = builder1.instantiate(); - const obj2 = {x: 221}; + const obj2 = { x: 221 }; const obj3 = print; instance1.exports.set_globals(obj2, obj3); @@ -261,20 +392,20 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); builder2.addFunction("set_globals", kSig_v_rr) .addBody([ - kExprGetLocal, 0, - kExprSetGlobal, i2, - kExprGetLocal, 1, - kExprSetGlobal, i3, + kExprGetLocal, 0, + kExprSetGlobal, i2, + kExprGetLocal, 1, + kExprSetGlobal, i3, ]) .exportAs("set_globals"); builder2.addFunction('get_global2', kSig_r_v) - .addBody([kExprGetGlobal, i2]) - .exportAs('get_global2'); + .addBody([kExprGetGlobal, i2]) + .exportAs('get_global2'); builder2.addFunction('get_global3', kSig_r_v) - .addBody([kExprGetGlobal, i3]) - .exportAs('get_global3'); + .addBody([kExprGetGlobal, i3]) + .exportAs('get_global3'); const instance2 = builder2.instantiate(instance1); // Check if the globals were imported correctly. @@ -300,8 +431,8 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); assertEquals("foo", instance2.exports.reexport2.value); assertEquals(66343, instance2.exports.reexport3.value); - const bar2 = {f: "oo"}; - const bar3 = {b: "ar"}; + const bar2 = { f: "oo" }; + const bar3 = { b: "ar" }; instance2.exports.reexport2.value = bar2; instance2.exports.reexport3.value = bar3; @@ -310,3 +441,90 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); assertSame(bar2, instance2.exports.get_global2()); assertSame(bar3, instance2.exports.get_global3()); })(); + +(function TestImportMutableAnyFuncGlobalFromOtherInstance() { + print(arguments.callee.name); + + // Create an instance which exports globals. + let builder1 = new WasmModuleBuilder(); + const g3 = builder1.addGlobal(kWasmAnyFunc, true).exportAs("e3"); + builder1.addGlobal(kWasmI32, true).exportAs("e1"); // Dummy. + builder1.addGlobal(kWasmAnyFunc, true).exportAs("e4"); // Dummy. + const g2 = builder1.addGlobal(kWasmAnyFunc, true).exportAs("e2"); + + builder1.addFunction("set_globals", kSig_v_aa) + .addBody([ + kExprGetLocal, 0, + kExprSetGlobal, g2.index, + kExprGetLocal, 1, + kExprSetGlobal, g3.index, + ]) + .exportAs("set_globals"); + + builder1.addFunction('get_global2', kSig_a_v) + .addBody([kExprGetGlobal, g2.index]) + .exportAs('get_global2'); + + builder1.addFunction('get_global3', kSig_a_v) + .addBody([kExprGetGlobal, g3.index]) + .exportAs('get_global3'); + + const instance1 = builder1.instantiate(); + const obj2 = dummy_func(); + const obj3 = instance1.exports.set_globals; + const obj4 = instance1.exports.get_global3; + instance1.exports.set_globals(obj2, obj3); + + // Create an instance which imports the globals of the other instance. + let builder2 = new WasmModuleBuilder(); + const i1 = builder2.addImportedGlobal('exports', 'e1', kWasmI32, true); + const i2 = builder2.addImportedGlobal('exports', 'e2', kWasmAnyFunc, true); + const i3 = builder2.addImportedGlobal('exports', 'e3', kWasmAnyFunc, true); + const i4 = builder2.addImportedGlobal('exports', 'e4', kWasmAnyFunc, true); + + builder2.addExportOfKind("reexport1", kExternalGlobal, i1); + builder2.addExportOfKind("reexport2", kExternalGlobal, i2); + builder2.addExportOfKind("reexport3", kExternalGlobal, i3); + builder2.addExportOfKind("reexport4", kExternalGlobal, i4); + + builder2.addFunction("set_globals", kSig_v_aa) + .addBody([ + kExprGetLocal, 0, + kExprSetGlobal, i2, + kExprGetLocal, 1, + kExprSetGlobal, i3, + ]) + .exportAs("set_globals"); + + builder2.addFunction('get_global2', kSig_a_v) + .addBody([kExprGetGlobal, i2]) + .exportAs('get_global2'); + + builder2.addFunction('get_global3', kSig_a_v) + .addBody([kExprGetGlobal, i3]) + .exportAs('get_global3'); + + const instance2 = builder2.instantiate(instance1); + // Check if the globals were imported correctly. + assertSame(obj2, instance2.exports.get_global2()); + assertSame(obj3, instance2.exports.get_global3()); + + assertSame(obj2, instance2.exports.reexport2.value); + assertSame(obj3, instance2.exports.reexport3.value); + + // Check if instance2 can make changes visible for instance1. + instance2.exports.set_globals(null, obj4); + assertEquals(null, instance1.exports.get_global2()); + assertEquals(obj4, instance1.exports.get_global3()); + + assertEquals(null, instance2.exports.reexport2.value); + assertEquals(obj4, instance2.exports.reexport3.value); + + // Check if instance1 can make changes visible for instance2. + instance1.exports.set_globals(obj2, obj3); + assertEquals(obj2, instance2.exports.get_global2()); + assertEquals(obj3, instance2.exports.get_global3()); + + assertEquals(obj2, instance2.exports.reexport2.value); + assertEquals(obj3, instance2.exports.reexport3.value); +})(); diff --git a/test/mjsunit/wasm/wasm-module-builder.js b/test/mjsunit/wasm/wasm-module-builder.js index 4345b99d99..f3430c590f 100644 --- a/test/mjsunit/wasm/wasm-module-builder.js +++ b/test/mjsunit/wasm/wasm-module-builder.js @@ -154,6 +154,7 @@ let kSig_i_r = makeSig([kWasmAnyRef], [kWasmI32]); let kSig_v_r = makeSig([kWasmAnyRef], []); let kSig_v_a = makeSig([kWasmAnyFunc], []); let kSig_v_rr = makeSig([kWasmAnyRef, kWasmAnyRef], []); +let kSig_v_aa = makeSig([kWasmAnyFunc, kWasmAnyFunc], []); let kSig_r_v = makeSig([], [kWasmAnyRef]); let kSig_a_v = makeSig([], [kWasmAnyFunc]); let kSig_a_i = makeSig([kWasmI32], [kWasmAnyFunc]); @@ -1051,6 +1052,7 @@ class WasmModuleBuilder { section.emit_bytes(f64_bytes_view); break; case kWasmAnyRef: + case kWasmAnyFunc: section.emit_u8(kExprRefNull); break; } diff --git a/test/unittests/wasm/module-decoder-unittest.cc b/test/unittests/wasm/module-decoder-unittest.cc index b3b069b7cc..5162dfcd9f 100644 --- a/test/unittests/wasm/module-decoder-unittest.cc +++ b/test/unittests/wasm/module-decoder-unittest.cc @@ -284,7 +284,7 @@ TEST_F(WasmModuleVerifyTest, AnyRefGlobal) { EXPECT_EQ(kWasmAnyRef, global->type); EXPECT_FALSE(global->mutability); - EXPECT_EQ(WasmInitExpr::kAnyRefConst, global->init.kind); + EXPECT_EQ(WasmInitExpr::kRefNullConst, global->init.kind); } } @@ -2119,7 +2119,7 @@ TEST_F(WasmInitExprDecodeTest, InitExpr_AnyRef) { WASM_FEATURE_SCOPE(anyref); static const byte data[] = {kExprRefNull, kExprEnd}; WasmInitExpr expr = DecodeInitExpr(data, data + sizeof(data)); - EXPECT_EQ(WasmInitExpr::kAnyRefConst, expr.kind); + EXPECT_EQ(WasmInitExpr::kRefNullConst, expr.kind); } TEST_F(WasmInitExprDecodeTest, InitExpr_illegal) {