[wasm][reference-types] Enable ref.null in Wasm code

Add decoding of ref.null as a valid argument for references in
TurboFan, LiftOff and the interpreter.

R=ahaas@chromium.org
R=jkummerow@chromium.org

Bug: chromium:10063
Change-Id: I1e2d9c76f616dacb3aa06f8b535543bdcdcf0783
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1991485
Commit-Queue: Emanuel Ziegler <ecmziegler@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65788}
This commit is contained in:
Emanuel Ziegler 2020-01-15 12:55:19 +01:00 committed by Commit Bot
parent e659917aa3
commit ea69636247
22 changed files with 691 additions and 43 deletions

View File

@ -2098,6 +2098,7 @@ Node* WasmGraphBuilder::Throw(uint32_t exception_index,
break;
case wasm::kWasmAnyRef:
case wasm::kWasmFuncRef:
case wasm::kWasmNullRef:
case wasm::kWasmExnRef:
STORE_FIXED_ARRAY_SLOT_ANY(values_array, index, value);
++index;
@ -2238,6 +2239,7 @@ Node* WasmGraphBuilder::GetExceptionValues(Node* except_obj,
break;
case wasm::kWasmAnyRef:
case wasm::kWasmFuncRef:
case wasm::kWasmNullRef:
case wasm::kWasmExnRef:
value = LOAD_FIXED_ARRAY_SLOT_ANY(values_array, index);
++index;
@ -3427,6 +3429,7 @@ void WasmGraphBuilder::GetTableBaseAndOffset(uint32_t table_index,
Node* WasmGraphBuilder::TableGet(uint32_t table_index, Node* index,
wasm::WasmCodePosition position) {
if (env_->module->tables[table_index].type == wasm::kWasmAnyRef ||
env_->module->tables[table_index].type == wasm::kWasmNullRef ||
env_->module->tables[table_index].type == wasm::kWasmExnRef) {
Node* base = nullptr;
Node* offset = nullptr;
@ -3456,6 +3459,7 @@ Node* WasmGraphBuilder::TableGet(uint32_t table_index, Node* index,
Node* WasmGraphBuilder::TableSet(uint32_t table_index, Node* index, Node* val,
wasm::WasmCodePosition position) {
if (env_->module->tables[table_index].type == wasm::kWasmAnyRef ||
env_->module->tables[table_index].type == wasm::kWasmNullRef ||
env_->module->tables[table_index].type == wasm::kWasmExnRef) {
Node* base = nullptr;
Node* offset = nullptr;
@ -5365,6 +5369,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
return BuildChangeFloat64ToTagged(node);
case wasm::kWasmAnyRef:
case wasm::kWasmFuncRef:
case wasm::kWasmNullRef:
case wasm::kWasmExnRef:
return node;
default:
@ -5455,9 +5460,27 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
switch (type) {
case wasm::kWasmAnyRef:
case wasm::kWasmExnRef:
// The parameter is of type anyref or exnref, we take it as is.
return input;
case wasm::kWasmNullRef: {
Node* check = graph()->NewNode(mcgraph()->machine()->WordEqual(), input,
RefNull());
Diamond null_check(graph(), mcgraph()->common(), check,
BranchHint::kTrue);
null_check.Chain(Control());
Node* effect = Effect();
BuildCallToRuntimeWithContext(Runtime::kWasmThrowTypeError, js_context,
nullptr, 0, &effect, null_check.if_false);
SetEffect(null_check.EffectPhi(Effect(), effect));
SetControl(null_check.merge);
return input;
}
case wasm::kWasmFuncRef: {
Node* check =
BuildChangeSmiToInt32(SetEffect(BuildCallToRuntimeWithContext(

View File

@ -236,11 +236,13 @@ RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
#undef CASE_ARG_TYPE
case wasm::kWasmAnyRef:
case wasm::kWasmFuncRef:
case wasm::kWasmNullRef:
case wasm::kWasmExnRef: {
DCHECK_EQ(wasm::ValueTypes::ElementSizeInBytes(sig->GetParam(i)),
kSystemPointerSize);
Handle<Object> ref(base::ReadUnalignedValue<Object>(arg_buf_ptr),
isolate);
DCHECK_IMPLIES(sig->GetParam(i) == wasm::kWasmNullRef, ref->IsNull());
wasm_args[i] = wasm::WasmValue(ref);
arg_buf_ptr += kSystemPointerSize;
break;
@ -287,9 +289,12 @@ RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
#undef CASE_RET_TYPE
case wasm::kWasmAnyRef:
case wasm::kWasmFuncRef:
case wasm::kWasmNullRef:
case wasm::kWasmExnRef: {
DCHECK_EQ(wasm::ValueTypes::ElementSizeInBytes(sig->GetReturn(i)),
kSystemPointerSize);
DCHECK_IMPLIES(sig->GetReturn(i) == wasm::kWasmNullRef,
wasm_rets[i].to_anyref()->IsNull());
base::WriteUnalignedValue<Object>(arg_buf_ptr,
*wasm_rets[i].to_anyref());
arg_buf_ptr += kSystemPointerSize;

View File

@ -1412,6 +1412,7 @@ void PushArgs(i::wasm::FunctionSig* sig, const Val args[],
break;
case i::wasm::kWasmAnyRef:
case i::wasm::kWasmFuncRef:
case i::wasm::kWasmNullRef:
packer->Push(WasmRefToV8(store->i_isolate(), args[i].ref())->ptr());
break;
case i::wasm::kWasmExnRef:
@ -1443,9 +1444,11 @@ void PopArgs(i::wasm::FunctionSig* sig, Val results[],
results[i] = Val(packer->Pop<double>());
break;
case i::wasm::kWasmAnyRef:
case i::wasm::kWasmFuncRef: {
case i::wasm::kWasmFuncRef:
case i::wasm::kWasmNullRef: {
i::Address raw = packer->Pop<i::Address>();
i::Handle<i::Object> obj(i::Object(raw), store->i_isolate());
DCHECK_IMPLIES(type == i::wasm::kWasmNullRef, obj->IsNull());
results[i] = Val(V8RefValueToWasm(store, obj));
break;
}

View File

@ -236,6 +236,9 @@ inline bool decode_local_type(uint8_t val, ValueType* result) {
case kLocalAnyRef:
*result = kWasmAnyRef;
return true;
case kLocalNullRef:
*result = kWasmNullRef;
return true;
case kLocalExnRef:
*result = kWasmExnRef;
return true;
@ -858,6 +861,15 @@ class WasmDecoder : public Decoder {
"invalid local type 'funcref', enable with "
"--experimental-wasm-anyref");
return false;
case kLocalNullRef:
if (enabled.has_anyref()) {
type = kWasmNullRef;
break;
}
decoder->error(decoder->pc() - 1,
"invalid local type 'nullref', enable with "
"--experimental-wasm-anyref");
return false;
case kLocalExnRef:
if (enabled.has_eh()) {
type = kWasmExnRef;

View File

@ -701,6 +701,7 @@ class WasmGraphBuildingInterface {
return builder_->S128Zero();
case kWasmAnyRef:
case kWasmFuncRef:
case kWasmNullRef:
case kWasmExnRef:
return builder_->RefNull();
default:

View File

@ -1616,6 +1616,9 @@ class ModuleDecoderImpl : public Decoder {
case kLocalAnyRef:
if (enabled_features_.has_anyref()) return kWasmAnyRef;
break;
case kLocalNullRef:
if (enabled_features_.has_anyref()) return kWasmNullRef;
break;
case kLocalExnRef:
if (enabled_features_.has_eh()) return kWasmExnRef;
break;
@ -1641,6 +1644,13 @@ class ModuleDecoderImpl : public Decoder {
"Invalid type. Set --experimental-wasm-anyref to use 'AnyRef'");
}
return kWasmAnyRef;
case kLocalNullRef:
if (!enabled_features_.has_anyref()) {
error(
pc_ - 1,
"Invalid type. Set --experimental-wasm-anyref to use 'NullRef'");
}
return kWasmNullRef;
case kLocalExnRef:
if (!enabled_features_.has_eh()) {
error(pc_ - 1,

View File

@ -753,7 +753,9 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global,
}
case kWasmAnyRef:
case kWasmFuncRef:
case kWasmNullRef:
case kWasmExnRef: {
DCHECK_IMPLIES(global.type == kWasmNullRef, value->GetRef()->IsNull());
tagged_globals_->set(global.offset, *value->GetRef());
break;
}
@ -1155,8 +1157,6 @@ bool InstanceBuilder::ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
}
if (ValueTypes::IsReferenceType(global.type)) {
// There shouldn't be any null-ref globals.
DCHECK_NE(ValueType::kWasmNullRef, global.type);
if (global.type == ValueType::kWasmFuncRef) {
if (!value->IsNull(isolate_) &&
!WasmExportedFunction::IsWasmExportedFunction(*value)) {
@ -1165,6 +1165,15 @@ bool InstanceBuilder::ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
import_index, module_name, import_name);
return false;
}
} else if (global.type == ValueType::kWasmNullRef) {
if (value->IsNullOrUndefined(isolate_)) {
WriteGlobalAnyRef(global, isolate_->factory()->null_value());
return true;
} else {
ReportLinkError("imported nullref global must be null", import_index,
module_name, import_name);
return false;
}
}
WriteGlobalAnyRef(global, value);
return true;

View File

@ -213,7 +213,8 @@ class V8_EXPORT_PRIVATE ValueTypes {
}
static inline bool IsReferenceType(ValueType type) {
return type == kWasmAnyRef || type == kWasmFuncRef || type == kWasmExnRef;
return type == kWasmAnyRef || type == kWasmFuncRef ||
type == kWasmNullRef || type == kWasmExnRef;
}
static inline ValueType CommonSubType(ValueType a, ValueType b) {
@ -243,9 +244,11 @@ class V8_EXPORT_PRIVATE ValueTypes {
return 16;
case kWasmAnyRef:
case kWasmFuncRef:
case kWasmNullRef:
case kWasmExnRef:
return kSystemPointerSize;
default:
case kWasmStmt:
case kWasmBottom:
UNREACHABLE();
}
}
@ -262,9 +265,11 @@ class V8_EXPORT_PRIVATE ValueTypes {
return 4;
case kWasmAnyRef:
case kWasmFuncRef:
case kWasmNullRef:
case kWasmExnRef:
return kSystemPointerSizeLog2;
default:
case kWasmStmt:
case kWasmBottom:
UNREACHABLE();
}
}
@ -287,11 +292,13 @@ class V8_EXPORT_PRIVATE ValueTypes {
return kLocalAnyRef;
case kWasmFuncRef:
return kLocalFuncRef;
case kWasmNullRef:
return kLocalNullRef;
case kWasmExnRef:
return kLocalExnRef;
case kWasmStmt:
return kLocalVoid;
default:
case kWasmBottom:
UNREACHABLE();
}
}
@ -308,13 +315,14 @@ class V8_EXPORT_PRIVATE ValueTypes {
return MachineType::Float64();
case kWasmAnyRef:
case kWasmFuncRef:
case kWasmNullRef:
case kWasmExnRef:
return MachineType::TaggedPointer();
case kWasmS128:
return MachineType::Simd128();
case kWasmStmt:
return MachineType::None();
default:
case kWasmBottom:
UNREACHABLE();
}
}
@ -338,7 +346,7 @@ class V8_EXPORT_PRIVATE ValueTypes {
return MachineRepresentation::kSimd128;
case kWasmStmt:
return MachineRepresentation::kNone;
default:
case kWasmBottom:
UNREACHABLE();
}
}
@ -382,10 +390,11 @@ class V8_EXPORT_PRIVATE ValueTypes {
return 's';
case kWasmStmt:
return 'v';
case kWasmNullRef:
return 'n';
case kWasmExnRef:
case kWasmBottom:
return '*';
default:
return '?';
}
}
@ -413,8 +422,6 @@ class V8_EXPORT_PRIVATE ValueTypes {
return "<stmt>";
case kWasmBottom:
return "<bot>";
default:
return "<unknown>";
}
}

View File

@ -28,6 +28,7 @@ enum ValueTypeCode : uint8_t {
kLocalS128 = 0x7b,
kLocalFuncRef = 0x70,
kLocalAnyRef = 0x6f,
kLocalNullRef = 0x6e,
kLocalExnRef = 0x68,
};
// Binary encoding of other types.

View File

@ -1464,6 +1464,7 @@ class ThreadImpl {
#undef CASE_TYPE
case kWasmAnyRef:
case kWasmFuncRef:
case kWasmNullRef:
case kWasmExnRef: {
val = WasmValue(isolate_->factory()->null_value());
break;
@ -2861,8 +2862,10 @@ class ThreadImpl {
}
case kWasmAnyRef:
case kWasmFuncRef:
case kWasmNullRef:
case kWasmExnRef: {
Handle<Object> anyref = value.to_anyref();
DCHECK_IMPLIES(sig->GetParam(i) == kWasmNullRef, anyref->IsNull());
encoded_values->set(encoded_index++, *anyref);
break;
}
@ -2963,8 +2966,10 @@ class ThreadImpl {
}
case kWasmAnyRef:
case kWasmFuncRef:
case kWasmNullRef:
case kWasmExnRef: {
Handle<Object> anyref(encoded_values->get(encoded_index++), isolate_);
DCHECK_IMPLIES(sig->GetParam(i) == kWasmNullRef, anyref->IsNull());
value = WasmValue(anyref);
break;
}
@ -3122,6 +3127,7 @@ class ThreadImpl {
V8_FALLTHROUGH;
}
case kExprSelect: {
HandleScope scope(isolate_); // Avoid leaking handles.
WasmValue cond = Pop();
WasmValue fval = Pop();
WasmValue tval = Pop();
@ -3419,6 +3425,7 @@ class ThreadImpl {
#undef CASE_TYPE
case kWasmAnyRef:
case kWasmFuncRef:
case kWasmNullRef:
case kWasmExnRef: {
HandleScope handle_scope(isolate_); // Avoid leaking handles.
Handle<FixedArray> global_buffer; // The buffer of the global.
@ -3426,7 +3433,9 @@ class ThreadImpl {
std::tie(global_buffer, global_index) =
WasmInstanceObject::GetGlobalBufferAndIndex(instance_object_,
global);
global_buffer->set(global_index, *Pop().to_anyref());
Handle<Object> ref = Pop().to_anyref();
DCHECK_IMPLIES(global.type == kWasmNullRef, ref->IsNull());
global_buffer->set(global_index, *ref);
break;
}
default:
@ -3882,7 +3891,10 @@ class ThreadImpl {
break;
case kWasmAnyRef:
case kWasmFuncRef:
case kWasmNullRef:
case kWasmExnRef:
DCHECK_IMPLIES(sig->GetParam(i) == kWasmNullRef,
arg.to_anyref()->IsNull());
packer.Push(arg.to_anyref()->ptr());
break;
default:
@ -3921,8 +3933,10 @@ class ThreadImpl {
break;
case kWasmAnyRef:
case kWasmFuncRef:
case kWasmNullRef:
case kWasmExnRef: {
Handle<Object> ref(Object(packer.Pop<Address>()), isolate);
DCHECK_IMPLIES(sig->GetReturn(i) == kWasmNullRef, ref->IsNull());
Push(WasmValue(ref));
break;
}

View File

@ -1098,6 +1098,9 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
} else if (enabled_features.has_anyref() &&
string->StringEquals(v8_str(isolate, "anyref"))) {
type = i::wasm::kWasmAnyRef;
} else if (enabled_features.has_anyref() &&
string->StringEquals(v8_str(isolate, "nullref"))) {
type = i::wasm::kWasmNullRef;
} else {
thrower.TypeError("Descriptor property 'element' must be 'anyfunc'");
return;
@ -1227,6 +1230,9 @@ bool GetValueType(Isolate* isolate, MaybeLocal<Value> maybe,
} else if (enabled_features.has_anyref() &&
string->StringEquals(v8_str(isolate, "anyfunc"))) {
*type = i::wasm::kWasmFuncRef;
} else if (enabled_features.has_anyref() &&
string->StringEquals(v8_str(isolate, "nullref"))) {
*type = i::wasm::kWasmNullRef;
} else if (enabled_features.has_eh() &&
string->StringEquals(v8_str(isolate, "exnref"))) {
*type = i::wasm::kWasmExnRef;
@ -1350,7 +1356,7 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
case i::wasm::kWasmAnyRef:
case i::wasm::kWasmExnRef: {
if (args.Length() < 2) {
// When no inital value is provided, we have to use the WebAssembly
// When no initial value is provided, we have to use the WebAssembly
// default value 'null', and not the JS default value 'undefined'.
global_obj->SetAnyRef(i_isolate->factory()->null_value());
break;
@ -1358,9 +1364,20 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
global_obj->SetAnyRef(Utils::OpenHandle(*value));
break;
}
case i::wasm::kWasmNullRef:
if (args.Length() < 2) {
// When no initial value is provided, we have to use the WebAssembly
// default value 'null', and not the JS default value 'undefined'.
global_obj->SetNullRef(i_isolate->factory()->null_value());
break;
}
if (!global_obj->SetNullRef(Utils::OpenHandle(*value))) {
thrower.TypeError("The value of nullref globals must be null");
}
break;
case i::wasm::kWasmFuncRef: {
if (args.Length() < 2) {
// When no inital value is provided, we have to use the WebAssembly
// When no initial value is provided, we have to use the WebAssembly
// default value 'null', and not the JS default value 'undefined'.
global_obj->SetFuncRef(i_isolate, i_isolate->factory()->null_value());
break;
@ -1789,7 +1806,10 @@ void WebAssemblyGlobalGetValueCommon(
break;
case i::wasm::kWasmAnyRef:
case i::wasm::kWasmFuncRef:
case i::wasm::kWasmNullRef:
case i::wasm::kWasmExnRef:
DCHECK_IMPLIES(receiver->type() == i::wasm::kWasmNullRef,
receiver->GetRef()->IsNull());
return_value.Set(Utils::ToLocal(receiver->GetRef()));
break;
default:
@ -1862,6 +1882,11 @@ void WebAssemblyGlobalSetValue(
receiver->SetAnyRef(Utils::OpenHandle(*args[0]));
break;
}
case i::wasm::kWasmNullRef:
if (!receiver->SetNullRef(Utils::OpenHandle(*args[0]))) {
thrower.TypeError("The value of nullref must be null");
}
break;
case i::wasm::kWasmFuncRef: {
if (!receiver->SetFuncRef(i_isolate, Utils::OpenHandle(*args[0]))) {
thrower.TypeError(

View File

@ -209,6 +209,10 @@ Handle<String> ToValueTypeString(Isolate* isolate, ValueType type) {
string = factory->InternalizeUtf8String("anyfunc");
break;
}
case i::wasm::kWasmNullRef: {
string = factory->InternalizeUtf8String("nullref");
break;
}
case i::wasm::kWasmExnRef: {
string = factory->InternalizeUtf8String("exnref");
break;

View File

@ -184,6 +184,15 @@ void WasmGlobalObject::SetAnyRef(Handle<Object> value) {
tagged_buffer().set(offset(), *value);
}
bool WasmGlobalObject::SetNullRef(Handle<Object> value) {
DCHECK_EQ(type(), wasm::kWasmNullRef);
if (!value->IsNull()) {
return false;
}
tagged_buffer().set(offset(), *value);
return true;
}
bool WasmGlobalObject::SetFuncRef(Isolate* isolate, Handle<Object> value) {
DCHECK_EQ(type(), wasm::kWasmFuncRef);
if (!value->IsNull(isolate) &&

View File

@ -523,6 +523,10 @@ bool WasmTableObject::IsValidElement(Isolate* isolate,
table->type() == wasm::kWasmExnRef) {
return true;
}
// Nullref only takes {null}.
if (table->type() == wasm::kWasmNullRef) {
return entry->IsNull(isolate);
}
// FuncRef tables can store {null}, {WasmExportedFunction}, {WasmJSFunction},
// or {WasmCapiFunction} objects.
if (entry->IsNull(isolate)) return true;
@ -1794,6 +1798,7 @@ uint32_t WasmExceptionPackage::GetEncodedSize(
break;
case wasm::kWasmAnyRef:
case wasm::kWasmFuncRef:
case wasm::kWasmNullRef:
case wasm::kWasmExnRef:
encoded_size += 1;
break;

View File

@ -369,6 +369,7 @@ class WasmGlobalObject : public JSObject {
inline void SetF32(float value);
inline void SetF64(double value);
inline void SetAnyRef(Handle<Object> value);
inline bool SetNullRef(Handle<Object> value);
inline bool SetFuncRef(Isolate* isolate, Handle<Object> value);
private:

View File

@ -28,6 +28,7 @@ class TestSignatures {
sig_i_r(1, 1, kIntAnyRefTypes4),
sig_i_rr(1, 2, kIntAnyRefTypes4),
sig_i_a(1, 1, kIntFuncRefTypes4),
sig_i_n(1, 1, kIntNullRefTypes4),
sig_l_v(1, 0, kLongTypes4),
sig_l_l(1, 1, kLongTypes4),
sig_l_ll(1, 2, kLongTypes4),
@ -40,12 +41,14 @@ class TestSignatures {
sig_a_v(1, 0, kFuncTypes4),
sig_r_r(1, 1, kRefTypes4),
sig_a_a(1, 1, kFuncTypes4),
sig_n_v(1, 0, kIntNullRefTypes4 + 1),
sig_v_v(0, 0, kIntTypes4),
sig_v_i(0, 1, kIntTypes4),
sig_v_ii(0, 2, kIntTypes4),
sig_v_iii(0, 3, kIntTypes4),
sig_v_r(0, 1, kRefTypes4),
sig_v_a(0, 1, kFuncTypes4),
sig_v_n(0, 1, kIntNullRefTypes4 + 1),
sig_s_i(1, 1, kSimd128IntTypes4),
sig_ii_v(2, 0, kIntTypes4),
sig_iii_v(3, 0, kIntTypes4) {
@ -61,12 +64,14 @@ class TestSignatures {
for (int i = 1; i < 4; i++) kIntDoubleTypes4[i] = kWasmF64;
for (int i = 1; i < 4; i++) kIntAnyRefTypes4[i] = kWasmAnyRef;
for (int i = 1; i < 4; i++) kIntFuncRefTypes4[i] = kWasmFuncRef;
for (int i = 1; i < 4; i++) kIntNullRefTypes4[i] = kWasmNullRef;
for (int i = 0; i < 4; i++) kSimd128IntTypes4[i] = kWasmS128;
kIntLongTypes4[0] = kWasmI32;
kIntFloatTypes4[0] = kWasmI32;
kIntDoubleTypes4[0] = kWasmI32;
kIntAnyRefTypes4[0] = kWasmI32;
kIntFuncRefTypes4[0] = kWasmI32;
kIntNullRefTypes4[0] = kWasmI32;
kSimd128IntTypes4[1] = kWasmI32;
}
@ -87,6 +92,7 @@ class TestSignatures {
FunctionSig* i_r() { return &sig_i_r; }
FunctionSig* i_rr() { return &sig_i_rr; }
FunctionSig* i_a() { return &sig_i_a; }
FunctionSig* i_n() { return &sig_i_n; }
FunctionSig* f_f() { return &sig_f_f; }
FunctionSig* f_ff() { return &sig_f_ff; }
@ -97,6 +103,7 @@ class TestSignatures {
FunctionSig* a_v() { return &sig_a_v; }
FunctionSig* r_r() { return &sig_r_r; }
FunctionSig* a_a() { return &sig_a_a; }
FunctionSig* n_v() { return &sig_n_v; }
FunctionSig* v_v() { return &sig_v_v; }
FunctionSig* v_i() { return &sig_v_i; }
@ -104,6 +111,7 @@ class TestSignatures {
FunctionSig* v_iii() { return &sig_v_iii; }
FunctionSig* v_r() { return &sig_v_r; }
FunctionSig* v_a() { return &sig_v_a; }
FunctionSig* v_n() { return &sig_v_n; }
FunctionSig* s_i() { return &sig_s_i; }
FunctionSig* ii_v() { return &sig_ii_v; }
@ -130,6 +138,7 @@ class TestSignatures {
ValueType kIntDoubleTypes4[4];
ValueType kIntAnyRefTypes4[4];
ValueType kIntFuncRefTypes4[4];
ValueType kIntNullRefTypes4[4];
ValueType kSimd128IntTypes4[4];
FunctionSig sig_i_v;
@ -144,6 +153,7 @@ class TestSignatures {
FunctionSig sig_i_r;
FunctionSig sig_i_rr;
FunctionSig sig_i_a;
FunctionSig sig_i_n;
FunctionSig sig_l_v;
FunctionSig sig_l_l;
@ -159,6 +169,7 @@ class TestSignatures {
FunctionSig sig_a_v;
FunctionSig sig_r_r;
FunctionSig sig_a_a;
FunctionSig sig_n_v;
FunctionSig sig_v_v;
FunctionSig sig_v_i;
@ -166,6 +177,7 @@ class TestSignatures {
FunctionSig sig_v_iii;
FunctionSig sig_v_r;
FunctionSig sig_v_a;
FunctionSig sig_v_n;
FunctionSig sig_s_i;
FunctionSig sig_ii_v;

View File

@ -0,0 +1,12 @@
// Copyright 2020 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.
// Flags: --experimental-wasm-anyref -experimental-wasm-eh
// Flags: --wasm-interpret-all
// This is just a wrapper for an existing reference types test case that runs
// with the --wasm-interpret-all flag added. If we ever decide to add a test
// variant for this, then we can remove this file.
load("test/mjsunit/wasm/nullref.js");

View File

@ -0,0 +1,347 @@
// Copyright 2020 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.
// Flags: --experimental-wasm-anyref --experimental-wasm-eh
load('test/mjsunit/wasm/wasm-module-builder.js');
let kSig_n_n = makeSig([kWasmNullRef], [kWasmNullRef]);
let kSig_r_n = makeSig([kWasmNullRef], [kWasmAnyRef]);
let kSig_a_n = makeSig([kWasmNullRef], [kWasmAnyFunc]);
let kSig_e_n = makeSig([kWasmNullRef], [kWasmExnRef]);
let kSig_n_v = makeSig([], [kWasmNullRef]);
let kSig_n_r = makeSig([kWasmAnyRef], [kWasmNullRef]);
let kSig_n_a = makeSig([kWasmAnyFunc], [kWasmNullRef]);
let kSig_n_e = makeSig([kWasmExnRef], [kWasmNullRef]);
let kSig_v_n = makeSig([kWasmNullRef], []);
let kSig_v_in = makeSig([kWasmI32, kWasmNullRef], []);
let kSig_n_i = makeSig([kWasmI32], [kWasmNullRef]);
let kSig_r_i = makeSig([kWasmI32], [kWasmAnyRef]);
let kSig_e_i = makeSig([kWasmI32], [kWasmExnRef]);
(function testNullRefIdentityFunction() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addFunction('nullRef', kSig_n_n)
.addBody([kExprLocalGet, 0])
.exportFunc();
builder.addFunction('anyRef', kSig_r_n)
.addBody([kExprLocalGet, 0])
.exportFunc();
builder.addFunction('funcRef', kSig_a_n)
.addBody([kExprLocalGet, 0])
.exportFunc();
builder.addFunction('exnRef', kSig_e_n)
.addBody([kExprLocalGet, 0])
.exportFunc();
const instance = builder.instantiate();
assertThrows(() => instance.exports.nullRef(a => a), TypeError);
assertThrows(() => instance.exports.nullRef(print), TypeError);
assertThrows(() => instance.exports.nullRef({'hello': 'world'}), TypeError);
assertEquals(null, instance.exports.nullRef(null));
assertEquals(null, instance.exports.anyRef(null));
assertEquals(null, instance.exports.funcRef(null));
assertEquals(null, instance.exports.exnRef(null));
})();
(function testNullRefFromAnyRefFail() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_n_r);
builder.addFunction('main', sig_index)
.addBody([kExprLocalGet, 0])
.exportFunc();
assertThrows(() => builder.instantiate(), WebAssembly.CompileError);
})();
(function testNullRefFromFuncRefFail() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_n_a);
builder.addFunction('main', sig_index)
.addBody([kExprLocalGet, 0])
.exportFunc();
assertThrows(() => builder.instantiate(), WebAssembly.CompileError);
})();
(function testNullRefFromExnRefFail() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_n_e);
builder.addFunction('main', sig_index)
.addBody([kExprLocalGet, 0])
.exportFunc();
assertThrows(() => builder.instantiate(), WebAssembly.CompileError);
})();
(function testNullRefDefaultValue() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_n_v);
builder.addFunction('main', sig_index)
.addLocals({nullref_count: 1})
.addBody([kExprLocalGet, 0])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
(function testNullRefFromAnyRefFail() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_n_v);
builder.addFunction('main', sig_index)
.addLocals({anyref_count: 1})
.addBody([kExprLocalGet, 0])
.exportFunc();
assertThrows(() => builder.instantiate(), WebAssembly.CompileError);
})();
(function testNullRefFromFuncRefFail() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_n_v);
builder.addFunction('main', sig_index)
.addLocals({anyfunc_count: 1})
.addBody([kExprLocalGet, 0])
.exportFunc();
assertThrows(() => builder.instantiate(), WebAssembly.CompileError);
})();
(function testNullRefFromExnRefFail() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_n_v);
builder.addFunction('main', sig_index)
.addLocals({exnref_count: 1})
.addBody([kExprLocalGet, 0])
.exportFunc();
assertThrows(() => builder.instantiate(), WebAssembly.CompileError);
})();
(function testAssignNullRefToNullRefLocal() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_n_n);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull, kExprLocalSet, 0, kExprLocalGet, 0])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main(null));
})();
(function testImplicitReturnNullRefAsNullRef() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_n_v);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
(function testExplicitReturnNullRefAsNullRef() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_n_v);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull, kExprReturn])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
(function testGetNullRefGlobal() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const initialized = builder.addGlobal(kWasmNullRef, true)
.exportAs('initialized');
initialized.init = null;
const uninitialized = builder.addGlobal(kWasmNullRef, true)
.exportAs('uninitialized');
const sig_n_v = builder.addType(kSig_n_v);
const sig_v_n = builder.addType(kSig_v_n);
const sig_v_v = builder.addType(kSig_v_v);
builder.addFunction('get_initialized', sig_n_v)
.addBody([kExprGlobalGet, initialized.index])
.exportFunc();
builder.addFunction('get_uninitialized', sig_n_v)
.addBody([kExprGlobalGet, initialized.index])
.exportFunc();
builder.addFunction('set_initialized', sig_v_n)
.addBody([kExprLocalGet, 0, kExprGlobalSet, initialized.index])
.exportFunc();
builder.addFunction('reset_initialized', sig_v_v)
.addBody([kExprRefNull, kExprGlobalSet, initialized.index])
.exportFunc();
const instance = builder.instantiate();
assertTrue(instance.exports.initialized instanceof WebAssembly.Global);
assertTrue(instance.exports.uninitialized instanceof WebAssembly.Global);
assertEquals(instance.exports.initialized.value, null);
assertEquals(instance.exports.uninitialized.value, null);
assertEquals(null, instance.exports.get_initialized());
assertEquals(null, instance.exports.get_uninitialized());
instance.exports.set_initialized(null);
assertEquals(instance.exports.initialized.value, null);
assertEquals(null, instance.exports.get_initialized());
instance.exports.reset_initialized();
assertEquals(instance.exports.initialized.value, null);
assertEquals(null, instance.exports.get_initialized());
})();
(function testGetNullRefImportedGlobal() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const initialized_index = builder.addImportedGlobal("foo", "initialized", kWasmNullRef);
const uninitialized_index = builder.addImportedGlobal("foo", "uninitialized", kWasmNullRef);
const sig_n_v = builder.addType(kSig_n_v);
const sig_v_n = builder.addType(kSig_v_n);
const sig_v_v = builder.addType(kSig_v_v);
builder.addFunction('get_initialized', sig_n_v)
.addBody([kExprGlobalGet, initialized_index])
.exportFunc();
builder.addFunction('get_uninitialized', sig_n_v)
.addBody([kExprGlobalGet, uninitialized_index])
.exportFunc();
assertThrows(() => builder.instantiate({foo: {initialized: {}}}), WebAssembly.LinkError);
assertThrows(() => builder.instantiate({foo: {initialized: a => a}}), WebAssembly.LinkError);
const instance = builder.instantiate({foo: {initialized: null}});
assertEquals(null, instance.exports.get_initialized());
assertEquals(null, instance.exports.get_uninitialized());
})();
(function testNullRefTable() {
print(arguments.callee.name);
let table = new WebAssembly.Table({element: "nullref", initial: 2});
assertEquals(null, table.get(0));
table.set(1, null);
assertEquals(null, table.get(1));
assertThrows(() => table.set(2, null), RangeError);
table.grow(2);
assertEquals(null, table.get(2));
table.set(3, null);
assertEquals(null, table.get(3));
assertThrows(() => table.set(4, null), RangeError);
assertThrows(() => table.set(0, {}), TypeError);
assertThrows(() => table.set(0, a => a), TypeError);
})();
(function testAddNullRefTable() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const table = builder.addTable(kWasmNullRef, 3, 10);
builder.addFunction('set_null', kSig_v_i)
.addBody([kExprLocalGet, 0, kExprRefNull, kExprTableSet, table.index])
.exportFunc();
builder.addFunction('set', kSig_v_in)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprTableSet, table.index])
.exportFunc();
builder.addFunction('get_null', kSig_n_i)
.addBody([kExprLocalGet, 0, kExprTableGet, table.index])
.exportFunc();
builder.addFunction('get_any', kSig_r_i)
.addBody([kExprLocalGet, 0, kExprTableGet, table.index])
.exportFunc();
builder.addFunction('get_func', kSig_a_i)
.addBody([kExprLocalGet, 0, kExprTableGet, table.index])
.exportFunc();
builder.addFunction('get_exn', kSig_e_i)
.addBody([kExprLocalGet, 0, kExprTableGet, table.index])
.exportFunc();
const instance = builder.instantiate();
instance.exports.set_null(1);
instance.exports.set(2, null);
assertEquals(null, instance.exports.get_null(0));
assertEquals(null, instance.exports.get_null(1));
assertEquals(null, instance.exports.get_null(2));
assertEquals(null, instance.exports.get_any(0));
assertEquals(null, instance.exports.get_any(1));
assertEquals(null, instance.exports.get_any(2));
assertEquals(null, instance.exports.get_func(0));
assertEquals(null, instance.exports.get_func(1));
assertEquals(null, instance.exports.get_func(2));
assertEquals(null, instance.exports.get_exn(0));
assertEquals(null, instance.exports.get_exn(1));
assertEquals(null, instance.exports.get_exn(2));
})();
(function testImportNullRefTable() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const table_index = builder.addImportedTable("imp", "table", 2, 10, kWasmNullRef);
builder.addFunction('get_null', kSig_n_i)
.addBody([kExprLocalGet, 0, kExprTableGet, table_index])
.exportFunc();
builder.addFunction('get_any', kSig_r_i)
.addBody([kExprLocalGet, 0, kExprTableGet, table_index])
.exportFunc();
builder.addFunction('get_func', kSig_a_i)
.addBody([kExprLocalGet, 0, kExprTableGet, table_index])
.exportFunc();
builder.addFunction('get_exn', kSig_e_i)
.addBody([kExprLocalGet, 0, kExprTableGet, table_index])
.exportFunc();
let table_func = new WebAssembly.Table({element: "anyfunc", initial: 2, maximum: 10});
assertThrows(() => builder.instantiate({imp: {table: table_func}}),
WebAssembly.LinkError, /imported table does not match the expected type/);
let table_any = new WebAssembly.Table({element: "anyref", initial: 2, maximum: 10});
assertThrows(() => builder.instantiate({imp: {table: table_any}}),
WebAssembly.LinkError, /imported table does not match the expected type/);
let table_null = new WebAssembly.Table({element: "nullref", initial: 2, maximum: 10});
table_null.set(1, null);
const instance = builder.instantiate({imp: {table: table_null}});
assertEquals(null, instance.exports.get_null(0));
assertEquals(null, instance.exports.get_null(1));
assertEquals(null, instance.exports.get_any(0));
assertEquals(null, instance.exports.get_any(1));
assertEquals(null, instance.exports.get_func(0));
assertEquals(null, instance.exports.get_func(1));
assertEquals(null, instance.exports.get_exn(0));
assertEquals(null, instance.exports.get_exn(1));
assertThrows(() => instance.exports.get_null(2), WebAssembly.RuntimeError);
table_null.grow(1);
assertEquals(null, instance.exports.get_null(2));
})();
(function testImportNullRefTableIntoAnyRefTable() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const table_index = builder.addImportedTable("imp", "table", 2, 10, kWasmAnyRef);
builder.addFunction('get', kSig_r_v)
.addBody([kExprI32Const, 0, kExprTableGet, table_index])
.exportFunc();
let table_null = new WebAssembly.Table({element: "nullref", initial: 2, maximum: 10});
assertThrows(() => builder.instantiate({imp: {table: table_null}}),
WebAssembly.LinkError, /imported table does not match the expected type/);
})();

View File

@ -99,6 +99,7 @@ let kWasmF64 = 0x7c;
let kWasmS128 = 0x7b;
let kWasmAnyRef = 0x6f;
let kWasmAnyFunc = 0x70;
let kWasmNullRef = 0x6e;
let kWasmExnRef = 0x68;
let kExternalFunction = 0;
@ -800,9 +801,9 @@ class WasmModuleBuilder {
}
addTable(type, initial_size, max_size = undefined) {
if (type != kWasmAnyRef && type != kWasmAnyFunc && type != kWasmExnRef) {
if (type != kWasmAnyRef && type != kWasmAnyFunc && type != kWasmNullRef && type != kWasmExnRef) {
throw new Error(
'Tables must be of type kWasmAnyRef, kWasmAnyFunc, or kWasmExnRef');
'Tables must be of type kWasmAnyRef, kWasmAnyFunc, kWasmNullRef or kWasmExnRef');
}
let table = new WasmTableBuilder(this, type, initial_size, max_size);
table.index = this.tables.length + this.num_imported_tables;
@ -1086,6 +1087,7 @@ class WasmModuleBuilder {
break;
case kWasmAnyFunc:
case kWasmAnyRef:
case kWasmNullRef:
if (global.function_index !== undefined) {
section.emit_u8(kExprRefFunc);
section.emit_u32v(global.function_index);
@ -1272,6 +1274,9 @@ class WasmModuleBuilder {
if (l.anyfunc_count > 0) {
local_decls.push({count: l.anyfunc_count, type: kWasmAnyFunc});
}
if (l.nullref_count > 0) {
local_decls.push({count: l.nullref_count, type: kWasmNullRef});
}
if (l.except_count > 0) {
local_decls.push({count: l.except_count, type: kWasmExnRef});
}

View File

@ -38,8 +38,8 @@ static const byte kCodeGetLocal1[] = {kExprLocalGet, 1};
static const byte kCodeSetLocal0[] = {WASM_SET_LOCAL(0, WASM_ZERO)};
static const byte kCodeTeeLocal0[] = {WASM_TEE_LOCAL(0, WASM_ZERO)};
static const ValueType kValueTypes[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
kWasmAnyRef};
static const ValueType kValueTypes[] = {kWasmI32, kWasmI64, kWasmF32,
kWasmF64, kWasmAnyRef, kWasmNullRef};
static const MachineType machineTypes[] = {
MachineType::Int8(), MachineType::Uint8(), MachineType::Int16(),
MachineType::Uint16(), MachineType::Int32(), MachineType::Uint32(),
@ -243,7 +243,7 @@ class TestModuleBuilder {
byte AddTable(ValueType type, uint32_t initial_size, bool has_maximum_size,
uint32_t maximum_size) {
CHECK(type == kWasmAnyRef || type == kWasmFuncRef);
CHECK(type == kWasmAnyRef || type == kWasmFuncRef || type == kWasmNullRef);
mod.tables.emplace_back();
WasmTable& table = mod.tables.back();
table.type = type;
@ -296,6 +296,7 @@ TEST_F(FunctionBodyDecoderTest, Int32Const1) {
TEST_F(FunctionBodyDecoderTest, RefNull) {
WASM_FEATURE_SCOPE(anyref);
ExpectValidates(sigs.r_v(), {kExprRefNull});
ExpectValidates(sigs.n_v(), {kExprRefNull});
}
TEST_F(FunctionBodyDecoderTest, RefFunc) {
@ -1738,7 +1739,8 @@ TEST_F(FunctionBodyDecoderTest, MultiReturnType) {
ExpectValidates(&sig_cd_v, {WASM_CALL_FUNCTION0(0)});
if (a == c && b == d) {
if (ValueTypes::IsSubType(kValueTypes[c], kValueTypes[a]) &&
ValueTypes::IsSubType(kValueTypes[d], kValueTypes[b])) {
ExpectValidates(&sig_ab_v, {WASM_CALL_FUNCTION0(0)});
} else {
ExpectFailure(&sig_ab_v, {WASM_CALL_FUNCTION0(0)});
@ -1984,7 +1986,8 @@ TEST_F(FunctionBodyDecoderTest, AllGetGlobalCombinations) {
TestModuleBuilder builder;
module = builder.module();
builder.AddGlobal(global_type);
Validate(local_type == global_type, &sig, {WASM_GET_GLOBAL(0)});
Validate(ValueTypes::IsSubType(global_type, local_type), &sig,
{WASM_GET_GLOBAL(0)});
}
}
}
@ -1998,7 +2001,7 @@ TEST_F(FunctionBodyDecoderTest, AllSetGlobalCombinations) {
TestModuleBuilder builder;
module = builder.module();
builder.AddGlobal(global_type);
Validate(local_type == global_type, &sig,
Validate(ValueTypes::IsSubType(local_type, global_type), &sig,
{WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0))});
}
}
@ -2012,11 +2015,14 @@ TEST_F(FunctionBodyDecoderTest, TableSet) {
byte tab_func1 = builder.AddTable(kWasmFuncRef, 20, true, 30);
byte tab_func2 = builder.AddTable(kWasmFuncRef, 10, false, 20);
byte tab_ref2 = builder.AddTable(kWasmAnyRef, 10, false, 20);
ValueType sig_types[]{kWasmAnyRef, kWasmFuncRef, kWasmI32};
FunctionSig sig(0, 3, sig_types);
byte tab_null1 = builder.AddTable(kWasmNullRef, 10, true, 20);
byte tab_null2 = builder.AddTable(kWasmNullRef, 10, false, 20);
ValueType sig_types[]{kWasmAnyRef, kWasmFuncRef, kWasmNullRef, kWasmI32};
FunctionSig sig(0, 4, sig_types);
byte local_ref = 0;
byte local_func = 1;
byte local_int = 2;
byte local_null = 2;
byte local_int = 3;
ExpectValidates(&sig, {WASM_TABLE_SET(tab_ref1, WASM_I32V(6),
WASM_GET_LOCAL(local_ref))});
ExpectValidates(&sig, {WASM_TABLE_SET(tab_func1, WASM_I32V(5),
@ -2039,12 +2045,42 @@ TEST_F(FunctionBodyDecoderTest, TableSet) {
WASM_GET_LOCAL(local_int))});
ExpectFailure(&sig, {WASM_TABLE_SET(tab_func1, WASM_I32V(3),
WASM_GET_LOCAL(local_int))});
// We can store nullref values as funcref or anyref but not the other way
// round.
ExpectValidates(&sig, {WASM_TABLE_SET(tab_null1, WASM_I32V(3),
WASM_GET_LOCAL(local_null))});
ExpectValidates(&sig, {WASM_TABLE_SET(tab_ref1, WASM_I32V(8),
WASM_GET_LOCAL(local_null))});
ExpectValidates(&sig, {WASM_TABLE_SET(tab_func1, WASM_I32V(8),
WASM_GET_LOCAL(local_null))});
ExpectFailure(&sig, {WASM_TABLE_SET(tab_null1, WASM_I32V(3),
WASM_GET_LOCAL(local_ref))});
ExpectFailure(&sig, {WASM_TABLE_SET(tab_null1, WASM_I32V(3),
WASM_GET_LOCAL(local_func))});
ExpectFailure(&sig, {WASM_TABLE_SET(tab_null1, WASM_I32V(3),
WASM_GET_LOCAL(local_int))});
ExpectValidates(&sig, {WASM_TABLE_SET(tab_null2, WASM_I32V(3),
WASM_GET_LOCAL(local_null))});
ExpectValidates(&sig, {WASM_TABLE_SET(tab_ref2, WASM_I32V(8),
WASM_GET_LOCAL(local_null))});
ExpectValidates(&sig, {WASM_TABLE_SET(tab_func2, WASM_I32V(8),
WASM_GET_LOCAL(local_null))});
ExpectFailure(&sig, {WASM_TABLE_SET(tab_null2, WASM_I32V(3),
WASM_GET_LOCAL(local_ref))});
ExpectFailure(&sig, {WASM_TABLE_SET(tab_null2, WASM_I32V(3),
WASM_GET_LOCAL(local_func))});
ExpectFailure(&sig, {WASM_TABLE_SET(tab_null2, WASM_I32V(3),
WASM_GET_LOCAL(local_int))});
// Out-of-bounds table index should fail.
byte oob_tab = 37;
ExpectFailure(
&sig, {WASM_TABLE_SET(oob_tab, WASM_I32V(9), WASM_GET_LOCAL(local_ref))});
ExpectFailure(&sig, {WASM_TABLE_SET(oob_tab, WASM_I32V(3),
WASM_GET_LOCAL(local_func))});
ExpectFailure(&sig, {WASM_TABLE_SET(oob_tab, WASM_I32V(3),
WASM_GET_LOCAL(local_null))});
}
TEST_F(FunctionBodyDecoderTest, TableGet) {
@ -2362,7 +2398,8 @@ TEST_F(FunctionBodyDecoderTest, Break_TypeCheckAll1) {
sig.GetReturn(), WASM_IF(WASM_ZERO, WASM_BRV(0, WASM_GET_LOCAL(0))),
WASM_GET_LOCAL(1))};
Validate(i == j, &sig, code);
Validate(ValueTypes::IsSubType(kValueTypes[j], kValueTypes[i]), &sig,
code);
}
}
}
@ -2376,7 +2413,8 @@ TEST_F(FunctionBodyDecoderTest, Break_TypeCheckAll2) {
WASM_BRV_IF_ZERO(0, WASM_GET_LOCAL(0)),
WASM_GET_LOCAL(1))};
Validate(i == j, &sig, code);
Validate(ValueTypes::IsSubType(kValueTypes[j], kValueTypes[i]), &sig,
code);
}
}
}
@ -2390,7 +2428,8 @@ TEST_F(FunctionBodyDecoderTest, Break_TypeCheckAll3) {
WASM_GET_LOCAL(1),
WASM_BRV_IF_ZERO(0, WASM_GET_LOCAL(0)))};
Validate(i == j, &sig, code);
Validate(ValueTypes::IsSubType(kValueTypes[j], kValueTypes[i]), &sig,
code);
}
}
}
@ -2434,7 +2473,8 @@ TEST_F(FunctionBodyDecoderTest, BreakIf_val_type) {
types[1], WASM_BRV_IF(0, WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
WASM_DROP, WASM_GET_LOCAL(0))};
Validate(i == j, &sig, code);
Validate(ValueTypes::IsSubType(kValueTypes[j], kValueTypes[i]), &sig,
code);
}
}
}
@ -2665,7 +2705,7 @@ TEST_F(FunctionBodyDecoderTest, Select_fail2) {
ValueType type = kValueTypes[i];
if (type == kWasmI32) continue;
// Select without specified type is only allowed for number types.
if (type == kWasmAnyRef) continue;
if (type == kWasmAnyRef || type == kWasmNullRef) continue;
ValueType types[] = {type, kWasmI32, type};
FunctionSig sig(1, 2, types);
@ -3204,22 +3244,37 @@ TEST_F(FunctionBodyDecoderTest, TableGrow) {
TestModuleBuilder builder;
byte tab_func = builder.AddTable(kWasmFuncRef, 10, true, 20);
byte tab_ref = builder.AddTable(kWasmAnyRef, 10, true, 20);
byte tab_null = builder.AddTable(kWasmNullRef, 10, true, 20);
module = builder.module();
ExpectFailure(sigs.i_a(),
{WASM_TABLE_GROW(tab_func, WASM_REF_NULL, WASM_ONE)});
ExpectFailure(sigs.i_n(),
{WASM_TABLE_GROW(tab_null, WASM_REF_NULL, WASM_ONE)});
WASM_FEATURE_SCOPE(anyref);
ExpectValidates(sigs.i_a(),
{WASM_TABLE_GROW(tab_func, WASM_REF_NULL, WASM_ONE)});
ExpectValidates(sigs.i_r(),
{WASM_TABLE_GROW(tab_ref, WASM_REF_NULL, WASM_ONE)});
ExpectValidates(sigs.i_n(),
{WASM_TABLE_GROW(tab_null, WASM_REF_NULL, WASM_ONE)});
// FuncRef table cannot be initialized with an anyref value.
ExpectFailure(sigs.i_r(),
{WASM_TABLE_GROW(tab_func, WASM_GET_LOCAL(0), WASM_ONE)});
// Anyref table can be initialized with an funcref value.
// FuncRef table can be initialized with a nullref value.
ExpectValidates(sigs.i_n(),
{WASM_TABLE_GROW(tab_func, WASM_GET_LOCAL(0), WASM_ONE)});
// Anyref table can be initialized with an funcref or nullref value.
ExpectValidates(sigs.i_a(),
{WASM_TABLE_GROW(tab_ref, WASM_GET_LOCAL(0), WASM_ONE)});
ExpectValidates(sigs.i_n(),
{WASM_TABLE_GROW(tab_ref, WASM_GET_LOCAL(0), WASM_ONE)});
// NullRef table cannot be initialized with an funcref or anyref value.
ExpectFailure(sigs.i_a(),
{WASM_TABLE_GROW(tab_null, WASM_GET_LOCAL(0), WASM_ONE)});
ExpectFailure(sigs.i_r(),
{WASM_TABLE_GROW(tab_null, WASM_GET_LOCAL(0), WASM_ONE)});
// Check that the table index gets verified.
ExpectFailure(sigs.i_r(),
{WASM_TABLE_GROW(tab_ref + 2, WASM_REF_NULL, WASM_ONE)});
@ -3241,22 +3296,32 @@ TEST_F(FunctionBodyDecoderTest, TableFill) {
TestModuleBuilder builder;
byte tab_func = builder.AddTable(kWasmFuncRef, 10, true, 20);
byte tab_ref = builder.AddTable(kWasmAnyRef, 10, true, 20);
byte tab_null = builder.AddTable(kWasmNullRef, 10, true, 20);
module = builder.module();
ExpectFailure(sigs.v_a(),
{WASM_TABLE_FILL(tab_func, WASM_ONE, WASM_REF_NULL, WASM_ONE)});
ExpectFailure(sigs.v_n(),
{WASM_TABLE_FILL(tab_null, WASM_ONE, WASM_REF_NULL, WASM_ONE)});
WASM_FEATURE_SCOPE(anyref);
ExpectValidates(sigs.v_a(), {WASM_TABLE_FILL(tab_func, WASM_ONE,
WASM_REF_NULL, WASM_ONE)});
ExpectValidates(sigs.v_r(), {WASM_TABLE_FILL(tab_ref, WASM_ONE, WASM_REF_NULL,
WASM_ONE)});
ExpectValidates(sigs.v_n(), {WASM_TABLE_FILL(tab_null, WASM_ONE,
WASM_REF_NULL, WASM_ONE)});
// FuncRef table cannot be initialized with an anyref value.
ExpectFailure(sigs.v_r(), {WASM_TABLE_FILL(tab_func, WASM_ONE,
WASM_GET_LOCAL(0), WASM_ONE)});
// Anyref table can be initialized with an funcref value.
// FuncRef table can be initialized with an nullref value.
ExpectValidates(sigs.v_n(), {WASM_TABLE_FILL(tab_func, WASM_ONE,
WASM_GET_LOCAL(0), WASM_ONE)});
// Anyref table can be initialized with an funcref or nullref value.
ExpectValidates(sigs.v_a(), {WASM_TABLE_FILL(tab_ref, WASM_ONE,
WASM_GET_LOCAL(0), WASM_ONE)});
ExpectValidates(sigs.v_n(), {WASM_TABLE_FILL(tab_ref, WASM_ONE,
WASM_GET_LOCAL(0), WASM_ONE)});
// Check that the table index gets verified.
ExpectFailure(sigs.v_r(), {WASM_TABLE_FILL(tab_ref + 2, WASM_ONE,
WASM_REF_NULL, WASM_ONE)});

View File

@ -168,7 +168,8 @@ struct ValueTypePair {
{kLocalF32, kWasmF32}, // --
{kLocalF64, kWasmF64}, // --
{kLocalFuncRef, kWasmFuncRef}, // --
{kLocalAnyRef, kWasmAnyRef} // --
{kLocalAnyRef, kWasmAnyRef}, // --
{kLocalNullRef, kWasmNullRef} // --
};
class WasmModuleVerifyTest : public TestWithIsolateAndZone {
@ -324,6 +325,34 @@ TEST_F(WasmModuleVerifyTest, FuncRefGlobal) {
}
}
TEST_F(WasmModuleVerifyTest, NullRefGlobal) {
WASM_FEATURE_SCOPE(anyref);
static const byte data[] = {
// sig#0 ---------------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID,
// funcs ---------------------------------------------------------------
TWO_EMPTY_FUNCTIONS(SIG_INDEX(0)),
SECTION(Global, // --
ENTRY_COUNT(1), // --
kLocalNullRef, // local type
0, // immutable
WASM_INIT_EXPR_REF_NULL), // init
TWO_EMPTY_BODIES};
{
// Should decode to one global.
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(1u, result.value()->globals.size());
EXPECT_EQ(2u, result.value()->functions.size());
EXPECT_EQ(0u, result.value()->data_segments.size());
const WasmGlobal* global = &result.value()->globals[0];
EXPECT_EQ(kWasmNullRef, global->type);
EXPECT_FALSE(global->mutability);
EXPECT_EQ(WasmInitExpr::kRefNullConst, global->init.kind);
}
}
TEST_F(WasmModuleVerifyTest, InvalidFuncRefGlobal) {
WASM_FEATURE_SCOPE(anyref);
static const byte data[] = {
@ -340,6 +369,27 @@ TEST_F(WasmModuleVerifyTest, InvalidFuncRefGlobal) {
EXPECT_FAILURE(data);
}
TEST_F(WasmModuleVerifyTest, InvalidNullRefGlobal) {
WASM_FEATURE_SCOPE(anyref);
// Initialize nullref from funcref
static const byte data[] = {
// sig#0 ---------------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID,
// funcs ---------------------------------------------------------------
TWO_EMPTY_FUNCTIONS(SIG_INDEX(0)),
SECTION(Global, // --
ENTRY_COUNT(1), // --
kLocalNullRef, // local type
0, // immutable
WASM_INIT_EXPR_REF_FUNC(1)), // init
TWO_EMPTY_BODIES};
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(
result,
"type error in global initialization, expected nullref, got funcref");
}
TEST_F(WasmModuleVerifyTest, AnyRefGlobalWithGlobalInit) {
WASM_FEATURE_SCOPE(anyref);
static const byte data[] = {
@ -373,6 +423,40 @@ TEST_F(WasmModuleVerifyTest, AnyRefGlobalWithGlobalInit) {
}
}
TEST_F(WasmModuleVerifyTest, NullRefGlobalWithGlobalInit) {
WASM_FEATURE_SCOPE(anyref);
static const byte data[] = {
SECTION(Import, // --
ENTRY_COUNT(1), // number of imports
ADD_COUNT('m'), // module name
ADD_COUNT('n'), // global name
kExternalGlobal, // import kind
kLocalNullRef, // type
0), // mutability
SECTION(Global, // --
ENTRY_COUNT(1),
kLocalNullRef, // local type
0, // immutable
WASM_INIT_EXPR_GLOBAL(0)),
};
{
// Should decode to exactly one global.
ModuleResult result = DecodeModule(data, data + sizeof(data));
std::cout << result.error().message() << std::endl;
EXPECT_OK(result);
EXPECT_EQ(2u, result.value()->globals.size());
EXPECT_EQ(0u, result.value()->functions.size());
EXPECT_EQ(0u, result.value()->data_segments.size());
const WasmGlobal* global = &result.value()->globals.back();
EXPECT_EQ(kWasmNullRef, global->type);
EXPECT_FALSE(global->mutability);
EXPECT_EQ(WasmInitExpr::kGlobalIndex, global->init.kind);
}
}
TEST_F(WasmModuleVerifyTest, Global_invalid_type) {
static const byte data[] = {
SECTION(Global, // --
@ -1551,7 +1635,7 @@ TEST_F(WasmSignatureDecodeTest, Fail_off_end) {
TEST_F(WasmSignatureDecodeTest, Fail_anyref_without_flag) {
// Disable AnyRef support and check that decoding fails.
WASM_FEATURE_SCOPE_VAL(anyref, false);
byte ref_types[] = {kLocalFuncRef, kLocalAnyRef};
byte ref_types[] = {kLocalFuncRef, kLocalAnyRef, kLocalNullRef};
for (byte invalid_type : ref_types) {
for (size_t i = 0;; i++) {
byte data[] = {SIG_ENTRY_x_xx(kLocalI32, kLocalI32, kLocalI32)};

View File

@ -24,12 +24,6 @@
'proposals/multi-value/call': [FAIL],
'proposals/multi-value/if': [FAIL],
'proposals/multi-value/func': [FAIL],
'proposals/reference-types/br_table' : [FAIL],
'proposals/reference-types/ref_is_null' : [FAIL],
'proposals/reference-types/linking' : [FAIL],
'proposals/reference-types/ref_null' : [FAIL],
'proposals/reference-types/select' : [FAIL],
}], # ALWAYS
['arch == mipsel or arch == mips64el or arch == mips or arch == mips64', {