Commit d7cd9051 authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm][anyref] Introduce anyfunc globals

Anyfunc globals are very similar to anyref globals. This CL is mostly
about extending the conditions which guard the anyref globals code.

R=mstarzinger@chromium.org

Bug: v8:7581
Change-Id: Ia92ac4560102cc3ed0060342f92758db28f415ca
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1526004Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60281}
parent 644556e6
...@@ -3251,7 +3251,7 @@ Node* WasmGraphBuilder::BuildCallToRuntime(Runtime::FunctionId f, ...@@ -3251,7 +3251,7 @@ Node* WasmGraphBuilder::BuildCallToRuntime(Runtime::FunctionId f,
Node* WasmGraphBuilder::GetGlobal(uint32_t index) { Node* WasmGraphBuilder::GetGlobal(uint32_t index) {
const wasm::WasmGlobal& global = env_->module->globals[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) { if (global.mutability && global.imported) {
Node* base = nullptr; Node* base = nullptr;
Node* offset = nullptr; Node* offset = nullptr;
...@@ -3282,7 +3282,7 @@ Node* WasmGraphBuilder::GetGlobal(uint32_t index) { ...@@ -3282,7 +3282,7 @@ Node* WasmGraphBuilder::GetGlobal(uint32_t index) {
Node* WasmGraphBuilder::SetGlobal(uint32_t index, Node* val) { Node* WasmGraphBuilder::SetGlobal(uint32_t index, Node* val) {
const wasm::WasmGlobal& global = env_->module->globals[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) { if (global.mutability && global.imported) {
Node* base = nullptr; Node* base = nullptr;
Node* offset = nullptr; Node* offset = nullptr;
......
...@@ -2683,17 +2683,10 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2683,17 +2683,10 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return stack_.data() + old_size; 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) { V8_INLINE Value Pop(int index, ValueType expected) {
auto val = Pop(); auto val = Pop();
if (!VALIDATE(IsSubType(expected, val.type) || val.type == kWasmVar || if (!VALIDATE(ValueTypes::IsSubType(expected, val.type) ||
expected == kWasmVar)) { val.type == kWasmVar || expected == kWasmVar)) {
this->errorf(val.pc, "%s[%d] expected type %s, found %s of type %s", this->errorf(val.pc, "%s[%d] expected type %s, found %s of type %s",
SafeOpcodeNameAt(this->pc_), index, SafeOpcodeNameAt(this->pc_), index,
ValueTypes::TypeName(expected), SafeOpcodeNameAt(val.pc), ValueTypes::TypeName(expected), SafeOpcodeNameAt(val.pc),
...@@ -2739,7 +2732,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2739,7 +2732,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
for (uint32_t i = 0; i < merge->arity; ++i) { for (uint32_t i = 0; i < merge->arity; ++i) {
Value& val = stack_values[i]; Value& val = stack_values[i];
Value& old = (*merge)[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 // If {val.type} is polymorphic, which results from unreachable, make
// it more specific by using the merge value's expected type. // it more specific by using the merge value's expected type.
// If it is not polymorphic, this is a type error. // If it is not polymorphic, this is a type error.
...@@ -2810,7 +2803,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2810,7 +2803,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
for (uint32_t i = 0; i < num_returns; ++i) { for (uint32_t i = 0; i < num_returns; ++i) {
auto& val = stack_values[i]; auto& val = stack_values[i];
ValueType expected_type = this->sig_->GetReturn(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, // If {val.type} is polymorphic, which results from unreachable,
// make it more specific by using the return's expected type. // make it more specific by using the return's expected type.
// If it is not polymorphic, this is a type error. // If it is not polymorphic, this is a type error.
......
...@@ -117,8 +117,8 @@ ValueType TypeOf(const WasmModule* module, const WasmInitExpr& expr) { ...@@ -117,8 +117,8 @@ ValueType TypeOf(const WasmModule* module, const WasmInitExpr& expr) {
return kWasmF32; return kWasmF32;
case WasmInitExpr::kF64Const: case WasmInitExpr::kF64Const:
return kWasmF64; return kWasmF64;
case WasmInitExpr::kAnyRefConst: case WasmInitExpr::kRefNullConst:
return kWasmAnyRef; return kWasmNullRef;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
...@@ -1169,7 +1169,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1169,7 +1169,7 @@ class ModuleDecoderImpl : public Decoder {
ValueTypes::TypeName(module->globals[other_index].type)); ValueTypes::TypeName(module->globals[other_index].type));
} }
} else { } 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", errorf(pos, "type error in global initialization, expected %s, got %s",
ValueTypes::TypeName(global->type), ValueTypes::TypeName(global->type),
ValueTypes::TypeName(TypeOf(module, global->init))); ValueTypes::TypeName(TypeOf(module, global->init)));
...@@ -1185,7 +1185,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1185,7 +1185,7 @@ class ModuleDecoderImpl : public Decoder {
for (WasmGlobal& global : module->globals) { for (WasmGlobal& global : module->globals) {
if (global.mutability && global.imported) { if (global.mutability && global.imported) {
global.index = num_imported_mutable_globals++; global.index = num_imported_mutable_globals++;
} else if (global.type == ValueType::kWasmAnyRef) { } else if (ValueTypes::IsReferenceType(global.type)) {
global.offset = tagged_offset; global.offset = tagged_offset;
// All entries in the tagged_globals_buffer have size 1. // All entries in the tagged_globals_buffer have size 1.
tagged_offset++; tagged_offset++;
...@@ -1435,7 +1435,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1435,7 +1435,7 @@ class ModuleDecoderImpl : public Decoder {
} }
case kExprRefNull: { case kExprRefNull: {
if (enabled_features_.anyref) { if (enabled_features_.anyref) {
expr.kind = WasmInitExpr::kAnyRefConst; expr.kind = WasmInitExpr::kRefNullConst;
len = 0; len = 0;
break; break;
} }
......
...@@ -552,7 +552,6 @@ MaybeHandle<Object> InstanceBuilder::LookupImport(uint32_t index, ...@@ -552,7 +552,6 @@ MaybeHandle<Object> InstanceBuilder::LookupImport(uint32_t index,
// We pre-validated in the js-api layer that the ffi object is present, and // We pre-validated in the js-api layer that the ffi object is present, and
// a JSObject, if the module has imports. // a JSObject, if the module has imports.
DCHECK(!ffi_.is_null()); DCHECK(!ffi_.is_null());
// Look up the module first. // Look up the module first.
MaybeHandle<Object> result = Object::GetPropertyOrElement( MaybeHandle<Object> result = Object::GetPropertyOrElement(
isolate_, ffi_.ToHandleChecked(), module_name); isolate_, ffi_.ToHandleChecked(), module_name);
...@@ -972,7 +971,7 @@ bool InstanceBuilder::ProcessImportedWasmGlobalObject( ...@@ -972,7 +971,7 @@ bool InstanceBuilder::ProcessImportedWasmGlobalObject(
DCHECK_LT(global.index, module_->num_imported_mutable_globals); DCHECK_LT(global.index, module_->num_imported_mutable_globals);
Handle<Object> buffer; Handle<Object> buffer;
Address address_or_offset; Address address_or_offset;
if (global.type == kWasmAnyRef) { if (ValueTypes::IsReferenceType(global.type)) {
static_assert(sizeof(global_object->offset()) <= sizeof(Address), static_assert(sizeof(global_object->offset()) <= sizeof(Address),
"The offset into the globals buffer does not fit into " "The offset into the globals buffer does not fit into "
"the imported_mutable_globals array"); "the imported_mutable_globals array");
...@@ -1050,7 +1049,18 @@ bool InstanceBuilder::ProcessImportedGlobal(Handle<WasmInstanceObject> instance, ...@@ -1050,7 +1049,18 @@ bool InstanceBuilder::ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
return false; 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); WriteGlobalAnyRef(global, value);
return true; return true;
} }
...@@ -1183,7 +1193,7 @@ void InstanceBuilder::InitGlobals() { ...@@ -1183,7 +1193,7 @@ void InstanceBuilder::InitGlobals() {
WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global),
global.init.val.f64_const); global.init.val.f64_const);
break; break;
case WasmInitExpr::kAnyRefConst: case WasmInitExpr::kRefNullConst:
DCHECK(enabled_.anyref); DCHECK(enabled_.anyref);
if (global.imported) break; // We already initialized imported globals. if (global.imported) break; // We already initialized imported globals.
...@@ -1375,7 +1385,7 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) { ...@@ -1375,7 +1385,7 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
if (global.mutability && global.imported) { if (global.mutability && global.imported) {
Handle<FixedArray> buffers_array( Handle<FixedArray> buffers_array(
instance->imported_mutable_globals_buffers(), isolate_); instance->imported_mutable_globals_buffers(), isolate_);
if (global.type == kWasmAnyRef) { if (ValueTypes::IsReferenceType(global.type)) {
tagged_buffer = buffers_array->GetValueChecked<FixedArray>( tagged_buffer = buffers_array->GetValueChecked<FixedArray>(
isolate_, global.index); isolate_, global.index);
// For anyref globals we store the relative offset in the // For anyref globals we store the relative offset in the
...@@ -1398,7 +1408,7 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) { ...@@ -1398,7 +1408,7 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
offset = static_cast<uint32_t>(global_addr - backing_store); offset = static_cast<uint32_t>(global_addr - backing_store);
} }
} else { } else {
if (global.type == kWasmAnyRef) { if (ValueTypes::IsReferenceType(global.type)) {
tagged_buffer = handle(instance->tagged_globals_buffer(), isolate_); tagged_buffer = handle(instance->tagged_globals_buffer(), isolate_);
} else { } else {
untagged_buffer = untagged_buffer =
......
...@@ -178,6 +178,21 @@ class StoreType { ...@@ -178,6 +178,21 @@ class StoreType {
// A collection of ValueType-related static methods. // A collection of ValueType-related static methods.
class V8_EXPORT_PRIVATE ValueTypes { class V8_EXPORT_PRIVATE ValueTypes {
public: 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) { static byte MemSize(MachineType type) {
return 1 << i::ElementSizeLog2Of(type.representation()); return 1 << i::ElementSizeLog2Of(type.representation());
} }
......
...@@ -1202,6 +1202,8 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1202,6 +1202,8 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
type = i::wasm::kWasmF64; type = i::wasm::kWasmF64;
} else if (string->StringEquals(v8_str(isolate, "anyref"))) { } else if (string->StringEquals(v8_str(isolate, "anyref"))) {
type = i::wasm::kWasmAnyRef; type = i::wasm::kWasmAnyRef;
} else if (string->StringEquals(v8_str(isolate, "anyfunc"))) {
type = i::wasm::kWasmAnyFunc;
} else { } else {
thrower.TypeError( thrower.TypeError(
"Descriptor property 'value' must be 'i32', 'i64', 'f32', or " "Descriptor property 'value' must be 'i32', 'i64', 'f32', or "
...@@ -1277,13 +1279,27 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1277,13 +1279,27 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() < 2) { if (args.Length() < 2) {
// When no inital value is provided, we have to use the WebAssembly // When no inital value is provided, we have to use the WebAssembly
// default value 'null', and not the JS default value 'undefined'. // default value 'null', and not the JS default value 'undefined'.
global_obj->SetAnyRef( global_obj->SetAnyRef(i_isolate->factory()->null_value());
handle(i::ReadOnlyRoots(i_isolate).null_value(), i_isolate));
break; break;
} }
global_obj->SetAnyRef(Utils::OpenHandle(*value)); global_obj->SetAnyRef(Utils::OpenHandle(*value));
break; 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: default:
UNREACHABLE(); UNREACHABLE();
} }
...@@ -1640,7 +1656,8 @@ void WebAssemblyGlobalGetValueCommon( ...@@ -1640,7 +1656,8 @@ void WebAssemblyGlobalGetValueCommon(
return_value.Set(receiver->GetF64()); return_value.Set(receiver->GetF64());
break; break;
case i::wasm::kWasmAnyRef: case i::wasm::kWasmAnyRef:
return_value.Set(Utils::ToLocal(receiver->GetAnyRef())); case i::wasm::kWasmAnyFunc:
return_value.Set(Utils::ToLocal(receiver->GetRef()));
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
...@@ -1711,6 +1728,14 @@ void WebAssemblyGlobalSetValue( ...@@ -1711,6 +1728,14 @@ void WebAssemblyGlobalSetValue(
receiver->SetAnyRef(Utils::OpenHandle(*args[0])); receiver->SetAnyRef(Utils::OpenHandle(*args[0]));
break; 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: default:
UNREACHABLE(); UNREACHABLE();
} }
......
...@@ -151,8 +151,9 @@ double WasmGlobalObject::GetF64() { ...@@ -151,8 +151,9 @@ double WasmGlobalObject::GetF64() {
return ReadLittleEndianValue<double>(address()); return ReadLittleEndianValue<double>(address());
} }
Handle<Object> WasmGlobalObject::GetAnyRef() { Handle<Object> WasmGlobalObject::GetRef() {
DCHECK_EQ(type(), wasm::kWasmAnyRef); // We use this getter also for anyfunc.
DCHECK(wasm::ValueTypes::IsReferenceType(type()));
return handle(tagged_buffer()->get(offset()), GetIsolate()); return handle(tagged_buffer()->get(offset()), GetIsolate());
} }
...@@ -177,6 +178,16 @@ void WasmGlobalObject::SetAnyRef(Handle<Object> value) { ...@@ -177,6 +178,16 @@ void WasmGlobalObject::SetAnyRef(Handle<Object> value) {
tagged_buffer()->set(offset(), *value); tagged_buffer()->set(offset(), *value);
} }
bool WasmGlobalObject::SetAnyFunc(Isolate* isolate, Handle<Object> value) {
DCHECK_EQ(type(), wasm::kWasmAnyFunc);
if (!value->IsNull(isolate) &&
!WasmExportedFunction::IsWasmExportedFunction(*value)) {
return false;
}
tagged_buffer()->set(offset(), *value);
return true;
}
// WasmInstanceObject // WasmInstanceObject
PRIMITIVE_ACCESSORS(WasmInstanceObject, memory_start, byte*, kMemoryStartOffset) PRIMITIVE_ACCESSORS(WasmInstanceObject, memory_start, byte*, kMemoryStartOffset)
PRIMITIVE_ACCESSORS(WasmInstanceObject, memory_size, size_t, kMemorySizeOffset) PRIMITIVE_ACCESSORS(WasmInstanceObject, memory_size, size_t, kMemorySizeOffset)
......
...@@ -1327,7 +1327,7 @@ MaybeHandle<WasmGlobalObject> WasmGlobalObject::New( ...@@ -1327,7 +1327,7 @@ MaybeHandle<WasmGlobalObject> WasmGlobalObject::New(
auto global_obj = Handle<WasmGlobalObject>::cast( auto global_obj = Handle<WasmGlobalObject>::cast(
isolate->factory()->NewJSObject(global_ctor)); isolate->factory()->NewJSObject(global_ctor));
if (type == wasm::kWasmAnyRef) { if (wasm::ValueTypes::IsReferenceType(type)) {
DCHECK(maybe_untagged_buffer.is_null()); DCHECK(maybe_untagged_buffer.is_null());
Handle<FixedArray> tagged_buffer; Handle<FixedArray> tagged_buffer;
if (!maybe_tagged_buffer.ToHandle(&tagged_buffer)) { if (!maybe_tagged_buffer.ToHandle(&tagged_buffer)) {
......
...@@ -410,13 +410,14 @@ class WasmGlobalObject : public JSObject { ...@@ -410,13 +410,14 @@ class WasmGlobalObject : public JSObject {
inline int64_t GetI64(); inline int64_t GetI64();
inline float GetF32(); inline float GetF32();
inline double GetF64(); inline double GetF64();
inline Handle<Object> GetAnyRef(); inline Handle<Object> GetRef();
inline void SetI32(int32_t value); inline void SetI32(int32_t value);
inline void SetI64(int64_t value); inline void SetI64(int64_t value);
inline void SetF32(float value); inline void SetF32(float value);
inline void SetF64(double value); inline void SetF64(double value);
inline void SetAnyRef(Handle<Object> value); inline void SetAnyRef(Handle<Object> value);
inline bool SetAnyFunc(Isolate* isolate, Handle<Object> value);
private: private:
// This function returns the address of the global's data in the // This function returns the address of the global's data in the
......
...@@ -609,7 +609,7 @@ struct WasmInitExpr { ...@@ -609,7 +609,7 @@ struct WasmInitExpr {
kI64Const, kI64Const,
kF32Const, kF32Const,
kF64Const, kF64Const,
kAnyRefConst, kRefNullConst,
} kind; } kind;
union { union {
......
This diff is collapsed.
...@@ -154,6 +154,7 @@ let kSig_i_r = makeSig([kWasmAnyRef], [kWasmI32]); ...@@ -154,6 +154,7 @@ let kSig_i_r = makeSig([kWasmAnyRef], [kWasmI32]);
let kSig_v_r = makeSig([kWasmAnyRef], []); let kSig_v_r = makeSig([kWasmAnyRef], []);
let kSig_v_a = makeSig([kWasmAnyFunc], []); let kSig_v_a = makeSig([kWasmAnyFunc], []);
let kSig_v_rr = makeSig([kWasmAnyRef, kWasmAnyRef], []); let kSig_v_rr = makeSig([kWasmAnyRef, kWasmAnyRef], []);
let kSig_v_aa = makeSig([kWasmAnyFunc, kWasmAnyFunc], []);
let kSig_r_v = makeSig([], [kWasmAnyRef]); let kSig_r_v = makeSig([], [kWasmAnyRef]);
let kSig_a_v = makeSig([], [kWasmAnyFunc]); let kSig_a_v = makeSig([], [kWasmAnyFunc]);
let kSig_a_i = makeSig([kWasmI32], [kWasmAnyFunc]); let kSig_a_i = makeSig([kWasmI32], [kWasmAnyFunc]);
...@@ -1051,6 +1052,7 @@ class WasmModuleBuilder { ...@@ -1051,6 +1052,7 @@ class WasmModuleBuilder {
section.emit_bytes(f64_bytes_view); section.emit_bytes(f64_bytes_view);
break; break;
case kWasmAnyRef: case kWasmAnyRef:
case kWasmAnyFunc:
section.emit_u8(kExprRefNull); section.emit_u8(kExprRefNull);
break; break;
} }
......
...@@ -284,7 +284,7 @@ TEST_F(WasmModuleVerifyTest, AnyRefGlobal) { ...@@ -284,7 +284,7 @@ TEST_F(WasmModuleVerifyTest, AnyRefGlobal) {
EXPECT_EQ(kWasmAnyRef, global->type); EXPECT_EQ(kWasmAnyRef, global->type);
EXPECT_FALSE(global->mutability); 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) { ...@@ -2119,7 +2119,7 @@ TEST_F(WasmInitExprDecodeTest, InitExpr_AnyRef) {
WASM_FEATURE_SCOPE(anyref); WASM_FEATURE_SCOPE(anyref);
static const byte data[] = {kExprRefNull, kExprEnd}; static const byte data[] = {kExprRefNull, kExprEnd};
WasmInitExpr expr = DecodeInitExpr(data, data + sizeof(data)); WasmInitExpr expr = DecodeInitExpr(data, data + sizeof(data));
EXPECT_EQ(WasmInitExpr::kAnyRefConst, expr.kind); EXPECT_EQ(WasmInitExpr::kRefNullConst, expr.kind);
} }
TEST_F(WasmInitExprDecodeTest, InitExpr_illegal) { TEST_F(WasmInitExprDecodeTest, InitExpr_illegal) {
......
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