[wasm-gc] Introduce supertype of all arrays
We introduce a type arrayref, which is a supertype of all array types and a subtype of dataref. We change array.len to accept values of type (ref null array). Drive-by: Fix kEq/kData case in TypecheckJSObject. Bug: v8:7748 Change-Id: I47c6a4487ddf5e7280c1427f43abe87a97c896bd Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3368105 Reviewed-by: Simon Zünd <szuend@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Cr-Commit-Position: refs/heads/main@{#78565}
This commit is contained in:
parent
eb129a5cf1
commit
a9668e25e6
@ -6453,8 +6453,9 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
|
||||
WasmInternalFunction::kExternalOffset));
|
||||
}
|
||||
}
|
||||
case wasm::HeapType::kData:
|
||||
case wasm::HeapType::kEq:
|
||||
case wasm::HeapType::kData:
|
||||
case wasm::HeapType::kArray:
|
||||
case wasm::HeapType::kI31:
|
||||
// TODO(7748): Update this when JS interop is settled.
|
||||
if (type.kind() == wasm::kOptRef) {
|
||||
@ -6644,6 +6645,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
|
||||
BuildCheckValidRefValue(input, js_context, type);
|
||||
return BuildUnpackObjectWrapper(input);
|
||||
case wasm::HeapType::kData:
|
||||
case wasm::HeapType::kArray:
|
||||
case wasm::HeapType::kEq:
|
||||
case wasm::HeapType::kI31:
|
||||
// TODO(7748): Update this when JS interop has settled.
|
||||
|
@ -796,6 +796,9 @@ Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type,
|
||||
case wasm::HeapType::kData:
|
||||
generic_name = "data";
|
||||
break;
|
||||
case wasm::HeapType::kArray:
|
||||
generic_name = "array";
|
||||
break;
|
||||
case wasm::HeapType::kAny:
|
||||
generic_name = "any";
|
||||
break;
|
||||
|
@ -203,6 +203,7 @@ HeapType read_heap_type(Decoder* decoder, const byte* pc,
|
||||
case kEqRefCode:
|
||||
case kI31RefCode:
|
||||
case kDataRefCode:
|
||||
case kArrayRefCode:
|
||||
case kAnyRefCode:
|
||||
if (!VALIDATE(enabled.has_gc())) {
|
||||
DecodeError<validate>(
|
||||
@ -269,6 +270,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
|
||||
case kEqRefCode:
|
||||
case kI31RefCode:
|
||||
case kDataRefCode:
|
||||
case kArrayRefCode:
|
||||
case kAnyRefCode:
|
||||
if (!VALIDATE(enabled.has_gc())) {
|
||||
DecodeError<validate>(
|
||||
@ -281,9 +283,10 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
|
||||
case kExternRefCode:
|
||||
case kFuncRefCode: {
|
||||
HeapType heap_type = HeapType::from_code(code);
|
||||
Nullability nullability = code == kI31RefCode || code == kDataRefCode
|
||||
? kNonNullable
|
||||
: kNullable;
|
||||
Nullability nullability =
|
||||
code == kI31RefCode || code == kDataRefCode || code == kArrayRefCode
|
||||
? kNonNullable
|
||||
: kNullable;
|
||||
return ValueType::Ref(heap_type, nullability);
|
||||
}
|
||||
case kI32Code:
|
||||
@ -4312,9 +4315,11 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
|
||||
}
|
||||
case kExprArrayLen: {
|
||||
NON_CONST_ONLY
|
||||
// Read but ignore an immediate array type index.
|
||||
// TODO(7748): Remove this once we are ready to make breaking changes.
|
||||
ArrayIndexImmediate<validate> imm(this, this->pc_ + opcode_length);
|
||||
if (!this->Validate(this->pc_ + opcode_length, imm)) return 0;
|
||||
Value array_obj = Peek(0, 0, ValueType::Ref(imm.index, kNullable));
|
||||
Value array_obj =
|
||||
Peek(0, 0, ValueType::Ref(HeapType::kArray, kNullable));
|
||||
Value value = CreateValue(kWasmI32);
|
||||
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayLen, array_obj, &value);
|
||||
Drop(array_obj);
|
||||
|
@ -66,6 +66,7 @@ class HeapType {
|
||||
kEq, // shorthand: q
|
||||
kI31, // shorthand: j
|
||||
kData, // shorthand: o
|
||||
kArray, // shorthand: g
|
||||
kAny, // shorthand: a
|
||||
// This value is used to represent failures in the parsing of heap types and
|
||||
// does not correspond to a wasm heap type.
|
||||
@ -90,6 +91,8 @@ class HeapType {
|
||||
return HeapType(kAny);
|
||||
case ValueTypeCode::kDataRefCode:
|
||||
return HeapType(kData);
|
||||
case ValueTypeCode::kArrayRefCode:
|
||||
return HeapType(kArray);
|
||||
default:
|
||||
return HeapType(kBottom);
|
||||
}
|
||||
@ -142,6 +145,8 @@ class HeapType {
|
||||
return std::string("i31");
|
||||
case kData:
|
||||
return std::string("data");
|
||||
case kArray:
|
||||
return std::string("array");
|
||||
case kAny:
|
||||
return std::string("any");
|
||||
default:
|
||||
@ -165,6 +170,8 @@ class HeapType {
|
||||
return mask | kI31RefCode;
|
||||
case kData:
|
||||
return mask | kDataRefCode;
|
||||
case kArray:
|
||||
return mask | kArrayRefCode;
|
||||
case kAny:
|
||||
return mask | kAnyRefCode;
|
||||
default:
|
||||
@ -470,6 +477,8 @@ class ValueType {
|
||||
return kI31RefCode;
|
||||
case HeapType::kData:
|
||||
return kDataRefCode;
|
||||
case HeapType::kArray:
|
||||
return kArrayRefCode;
|
||||
default:
|
||||
return kRefCode;
|
||||
}
|
||||
@ -493,11 +502,19 @@ class ValueType {
|
||||
// Returns true iff the heap type is needed to encode this type in the wasm
|
||||
// binary format, taking into account available type shorthands.
|
||||
constexpr bool encoding_needs_heap_type() const {
|
||||
return (kind() == kRef && heap_representation() != HeapType::kI31 &&
|
||||
heap_representation() != HeapType::kData) ||
|
||||
(kind() == kOptRef && (heap_type().is_index() ||
|
||||
heap_representation() == HeapType::kI31 ||
|
||||
heap_representation() == HeapType::kData));
|
||||
switch (kind()) {
|
||||
case kRef:
|
||||
return heap_representation() != HeapType::kI31 &&
|
||||
heap_representation() != HeapType::kArray &&
|
||||
heap_representation() != HeapType::kData;
|
||||
case kOptRef:
|
||||
return heap_representation() != HeapType::kFunc &&
|
||||
heap_representation() != HeapType::kExtern &&
|
||||
heap_representation() != HeapType::kEq &&
|
||||
heap_representation() != HeapType::kAny;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr int kLastUsedBit = 30;
|
||||
@ -588,6 +605,8 @@ constexpr ValueType kWasmEqRef = ValueType::Ref(HeapType::kEq, kNullable);
|
||||
constexpr ValueType kWasmI31Ref = ValueType::Ref(HeapType::kI31, kNonNullable);
|
||||
constexpr ValueType kWasmDataRef =
|
||||
ValueType::Ref(HeapType::kData, kNonNullable);
|
||||
constexpr ValueType kWasmArrayRef =
|
||||
ValueType::Ref(HeapType::kArray, kNonNullable);
|
||||
constexpr ValueType kWasmAnyRef = ValueType::Ref(HeapType::kAny, kNullable);
|
||||
|
||||
// This is used in wasm.tq.
|
||||
|
@ -47,6 +47,7 @@ enum ValueTypeCode : uint8_t {
|
||||
kRttWithDepthCode = 0x69,
|
||||
kRttCode = 0x68,
|
||||
kDataRefCode = 0x67,
|
||||
kArrayRefCode = 0x66
|
||||
};
|
||||
// Binary encoding of other types.
|
||||
constexpr uint8_t kWasmFunctionTypeCode = 0x60;
|
||||
|
@ -1528,6 +1528,7 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
case i::wasm::HeapType::kEq:
|
||||
case internal::wasm::HeapType::kI31:
|
||||
case internal::wasm::HeapType::kData:
|
||||
case internal::wasm::HeapType::kArray:
|
||||
default:
|
||||
// TODO(7748): Implement these.
|
||||
UNIMPLEMENTED();
|
||||
@ -1719,6 +1720,7 @@ void EncodeExceptionValues(v8::Isolate* isolate,
|
||||
case i::wasm::HeapType::kEq:
|
||||
case i::wasm::HeapType::kI31:
|
||||
case i::wasm::HeapType::kData:
|
||||
case i::wasm::HeapType::kArray:
|
||||
values_out->set(index++, *Utils::OpenHandle(*value));
|
||||
break;
|
||||
case internal::wasm::HeapType::kBottom:
|
||||
@ -2272,6 +2274,7 @@ void WebAssemblyExceptionGetArg(
|
||||
case i::wasm::HeapType::kEq:
|
||||
case i::wasm::HeapType::kI31:
|
||||
case i::wasm::HeapType::kData:
|
||||
case i::wasm::HeapType::kArray:
|
||||
decode_index++;
|
||||
break;
|
||||
case i::wasm::HeapType::kBottom:
|
||||
@ -2330,6 +2333,7 @@ void WebAssemblyExceptionGetArg(
|
||||
case i::wasm::HeapType::kAny:
|
||||
case i::wasm::HeapType::kEq:
|
||||
case i::wasm::HeapType::kI31:
|
||||
case i::wasm::HeapType::kArray:
|
||||
case i::wasm::HeapType::kData: {
|
||||
auto obj = values->get(decode_index);
|
||||
result = Utils::ToLocal(i::Handle<i::Object>(obj, i_isolate));
|
||||
@ -2410,10 +2414,10 @@ void WebAssemblyGlobalGetValueCommon(
|
||||
case i::wasm::kOptRef:
|
||||
switch (receiver->type().heap_representation()) {
|
||||
case i::wasm::HeapType::kExtern:
|
||||
case i::wasm::HeapType::kAny:
|
||||
return_value.Set(Utils::ToLocal(receiver->GetRef()));
|
||||
break;
|
||||
case i::wasm::HeapType::kFunc:
|
||||
case i::wasm::HeapType::kAny: {
|
||||
case i::wasm::HeapType::kFunc: {
|
||||
i::Handle<i::Object> result = receiver->GetRef();
|
||||
if (result->IsWasmInternalFunction()) {
|
||||
result = handle(
|
||||
@ -2423,10 +2427,11 @@ void WebAssemblyGlobalGetValueCommon(
|
||||
return_value.Set(Utils::ToLocal(result));
|
||||
break;
|
||||
}
|
||||
case internal::wasm::HeapType::kBottom:
|
||||
case i::wasm::HeapType::kBottom:
|
||||
UNREACHABLE();
|
||||
case internal::wasm::HeapType::kI31:
|
||||
case internal::wasm::HeapType::kData:
|
||||
case i::wasm::HeapType::kI31:
|
||||
case i::wasm::HeapType::kData:
|
||||
case i::wasm::HeapType::kArray:
|
||||
case i::wasm::HeapType::kEq:
|
||||
default:
|
||||
// TODO(7748): Implement these.
|
||||
@ -2517,10 +2522,11 @@ void WebAssemblyGlobalSetValue(
|
||||
}
|
||||
break;
|
||||
}
|
||||
case internal::wasm::HeapType::kBottom:
|
||||
case i::wasm::HeapType::kBottom:
|
||||
UNREACHABLE();
|
||||
case internal::wasm::HeapType::kI31:
|
||||
case internal::wasm::HeapType::kData:
|
||||
case i::wasm::HeapType::kI31:
|
||||
case i::wasm::HeapType::kData:
|
||||
case i::wasm::HeapType::kArray:
|
||||
case i::wasm::HeapType::kEq:
|
||||
default:
|
||||
// TODO(7748): Implement these.
|
||||
|
@ -408,6 +408,7 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
|
||||
return;
|
||||
case wasm::HeapType::kEq:
|
||||
case wasm::HeapType::kData:
|
||||
case wasm::HeapType::kArray:
|
||||
case wasm::HeapType::kI31:
|
||||
// TODO(7748): Implement once we have struct/arrays/i31ref tables.
|
||||
UNREACHABLE();
|
||||
@ -449,6 +450,7 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate,
|
||||
case wasm::HeapType::kEq:
|
||||
case wasm::HeapType::kI31:
|
||||
case wasm::HeapType::kData:
|
||||
case wasm::HeapType::kArray:
|
||||
case wasm::HeapType::kAny:
|
||||
// TODO(7748): Implement once we have a story for struct/arrays/i31ref in
|
||||
// JS.
|
||||
@ -2209,8 +2211,9 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
|
||||
case kOptRef:
|
||||
if (value->IsNull(isolate)) return true;
|
||||
V8_FALLTHROUGH;
|
||||
case kRef:
|
||||
switch (expected.heap_representation()) {
|
||||
case kRef: {
|
||||
HeapType::Representation repr = expected.heap_representation();
|
||||
switch (repr) {
|
||||
case HeapType::kFunc: {
|
||||
if (!(WasmExternalFunction::IsWasmExternalFunction(*value) ||
|
||||
WasmCapiFunction::IsWasmCapiFunction(*value))) {
|
||||
@ -2225,6 +2228,7 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
|
||||
case HeapType::kAny:
|
||||
return true;
|
||||
case HeapType::kData:
|
||||
case HeapType::kArray:
|
||||
case HeapType::kEq:
|
||||
case HeapType::kI31: {
|
||||
// TODO(7748): Change this when we have a decision on the JS API for
|
||||
@ -2242,22 +2246,21 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
|
||||
value = it.GetDataValue();
|
||||
}
|
||||
|
||||
if (expected.is_reference_to(HeapType::kEq)) return true;
|
||||
|
||||
if (expected.is_reference_to(HeapType::kData)) {
|
||||
if (value->IsSmi()) {
|
||||
*error_message = "dataref-typed object must be a heap object";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
DCHECK(expected.is_reference_to(HeapType::kI31));
|
||||
if (repr == HeapType::kI31) {
|
||||
if (!value->IsSmi()) {
|
||||
*error_message = "i31ref-typed object cannot be a heap object";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!((repr == HeapType::kEq && value->IsSmi()) ||
|
||||
(repr != HeapType::kArray && value->IsWasmStruct()) ||
|
||||
value->IsWasmArray())) {
|
||||
*error_message = "object incompatible with wasm type";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
if (module == nullptr) {
|
||||
@ -2327,6 +2330,7 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
|
||||
"Javascript is not supported yet.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case kRtt:
|
||||
case kRttWithDepth:
|
||||
// TODO(7748): Implement when the JS API for rtts is decided on.
|
||||
|
@ -387,6 +387,9 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
|
||||
case HeapType::kData:
|
||||
return super_heap == sub_heap || super_heap == HeapType::kEq ||
|
||||
super_heap == HeapType::kAny;
|
||||
case HeapType::kArray:
|
||||
return super_heap == HeapType::kArray || super_heap == HeapType::kData ||
|
||||
super_heap == HeapType::kEq || super_heap == HeapType::kAny;
|
||||
case HeapType::kBottom:
|
||||
UNREACHABLE();
|
||||
default:
|
||||
@ -403,6 +406,8 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
|
||||
case HeapType::kEq:
|
||||
case HeapType::kData:
|
||||
return !sub_module->has_signature(sub_index);
|
||||
case HeapType::kArray:
|
||||
return sub_module->has_array(sub_index);
|
||||
case HeapType::kExtern:
|
||||
case HeapType::kI31:
|
||||
return false;
|
||||
|
@ -26,16 +26,15 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
|
||||
// Type equivalence (~) is described by the following rules (structural
|
||||
// equivalence):
|
||||
// - Two numeric types are equivalent iff they are equal.
|
||||
// - optref(ht1) ~ optref(ht2) iff ht1 ~ ht2.
|
||||
// - ref(ht1) ~ ref(ht2) iff ht1 ~ ht2.
|
||||
// - T(ht1) ~ T(ht2) iff ht1 ~ ht2 for T in {ref, optref, rtt}.
|
||||
// - rtt(d1, ht1) ~ rtt(d2, ht2) iff (d1 = d2 and ht1 ~ ht2).
|
||||
// For heap types, the following rules hold:
|
||||
// Equivalence of heap types ht1 ~ ht2 is defined as follows:
|
||||
// - Two generic heap types are equivalent iff they are equal.
|
||||
// - Two structs are equivalent iff they contain the same number of fields and
|
||||
// these are pairwise equivalent.
|
||||
// - Two functions are equivalent iff they contain the same number of parameters
|
||||
// and returns and these are pairwise equivalent.
|
||||
// - Two arrays are equivalent iff their underlying types are equivalent.
|
||||
// - Two arrays are equivalent iff their element types are equivalent.
|
||||
V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
|
||||
const WasmModule* module1,
|
||||
const WasmModule* module2);
|
||||
@ -47,7 +46,8 @@ V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
|
||||
// - numeric types are subtype-related iff they are equal.
|
||||
// - optref(ht1) <: optref(ht2) iff ht1 <: ht2.
|
||||
// - ref(ht1) <: ref/optref(ht2) iff ht1 <: ht2.
|
||||
// - rtt1 <: rtt2 iff rtt1 ~ rtt2.
|
||||
// - rtt1(d, ht1) <: rtt2(ht2) iff ht1 ~ ht2.
|
||||
// - rtt1 <: rtt2 iff rtt1 ~ rtt2, otherwise
|
||||
// For heap types, the following subtyping rules hold:
|
||||
// - The abstract heap types form the following type hierarchy:
|
||||
// any
|
||||
@ -55,11 +55,15 @@ V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
|
||||
// eq func extern
|
||||
// / \
|
||||
// i31 data
|
||||
// - All structs and arrays are subtypes of data.
|
||||
// |
|
||||
// array
|
||||
// - All functions are subtypes of func.
|
||||
// - All structs are subtypes of data.
|
||||
// - All arrays are subtypes of array.
|
||||
// - Struct subtyping: Subtype must have at least as many fields as supertype,
|
||||
// covariance for immutable fields, equivalence for mutable fields.
|
||||
// - Array subtyping (mutable only) is the equivalence relation.
|
||||
// - Array subtyping: subtyping of respective element types for immutable
|
||||
// arrays, equivalence of element types for mutable arrays.
|
||||
// - Function subtyping depends on the enabled wasm features: if
|
||||
// --experimental-wasm-gc is enabled, then subtyping is computed
|
||||
// contravariantly for parameter types and covariantly for return types.
|
||||
|
@ -935,12 +935,12 @@ WASM_COMPILED_EXEC_TEST(WasmBasicArray) {
|
||||
kExprEnd});
|
||||
|
||||
// Reads and returns an array's length.
|
||||
const byte kGetLength = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {},
|
||||
{WASM_ARRAY_LEN(type_index, WASM_ARRAY_NEW_WITH_RTT(
|
||||
type_index, WASM_I32V(0), WASM_I32V(42),
|
||||
WASM_RTT_CANON(type_index))),
|
||||
kExprEnd});
|
||||
const byte kGetLength =
|
||||
tester.DefineFunction(tester.sigs.i_v(), {},
|
||||
{WASM_ARRAY_LEN(WASM_ARRAY_NEW_WITH_RTT(
|
||||
type_index, WASM_I32V(0), WASM_I32V(42),
|
||||
WASM_RTT_CANON(type_index))),
|
||||
kExprEnd});
|
||||
|
||||
// Create an array of length 2, initialized to [42, 42].
|
||||
const byte kAllocate = tester.DefineFunction(
|
||||
|
@ -3170,6 +3170,7 @@ class WasmInterpreterInternals {
|
||||
case HeapType::kFunc:
|
||||
case HeapType::kEq:
|
||||
case HeapType::kData:
|
||||
case HeapType::kArray:
|
||||
case HeapType::kI31:
|
||||
case HeapType::kAny: {
|
||||
Handle<Object> ref = value.to_ref();
|
||||
@ -3275,6 +3276,7 @@ class WasmInterpreterInternals {
|
||||
case HeapType::kFunc:
|
||||
case HeapType::kEq:
|
||||
case HeapType::kData:
|
||||
case HeapType::kArray:
|
||||
case HeapType::kI31:
|
||||
case HeapType::kAny: {
|
||||
Handle<Object> ref(encoded_values->get(encoded_index++),
|
||||
|
@ -564,8 +564,8 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
|
||||
array, index, WASM_GC_OP(kExprArrayGetS), static_cast<byte>(typeidx)
|
||||
#define WASM_ARRAY_SET(typeidx, array, index, value) \
|
||||
array, index, value, WASM_GC_OP(kExprArraySet), static_cast<byte>(typeidx)
|
||||
#define WASM_ARRAY_LEN(typeidx, array) \
|
||||
array, WASM_GC_OP(kExprArrayLen), static_cast<byte>(typeidx)
|
||||
#define WASM_ARRAY_LEN(array) \
|
||||
array, WASM_GC_OP(kExprArrayLen), /* dummy index */ 0
|
||||
#define WASM_ARRAY_COPY(dst_idx, src_idx, dst_array, dst_index, src_array, \
|
||||
src_index, length) \
|
||||
dst_array, dst_index, src_array, src_index, length, \
|
||||
|
@ -2138,6 +2138,20 @@ void WasmGenerator::GenerateRef(HeapType type, DataRange* data,
|
||||
}
|
||||
return;
|
||||
}
|
||||
case HeapType::kArray: {
|
||||
DCHECK(liftoff_as_reference_);
|
||||
constexpr uint8_t fallback_to_dataref = 1;
|
||||
uint8_t random =
|
||||
data->get<uint8_t>() % (num_arrays_ + fallback_to_dataref);
|
||||
// Try generating one of the alternatives and continue to the rest of the
|
||||
// methods in case it fails.
|
||||
if (random >= num_arrays_) {
|
||||
if (GenerateOneOf(alternatives_other, type, data, nullability)) return;
|
||||
random = data->get<uint8_t>() % num_arrays_;
|
||||
}
|
||||
GenerateRef(HeapType(random), data, nullability);
|
||||
return;
|
||||
}
|
||||
case HeapType::kData: {
|
||||
DCHECK(liftoff_as_reference_);
|
||||
constexpr uint8_t fallback_to_dataref = 2;
|
||||
|
@ -231,6 +231,8 @@ std::string ValueTypeToConstantName(ValueType type) {
|
||||
return "kWasmAnyRef";
|
||||
case HeapType::kData:
|
||||
return "wasmOptRefType(kWasmDataRef)";
|
||||
case HeapType::kArray:
|
||||
return "wasmOptRefType(kWasmArrayRef)";
|
||||
case HeapType::kI31:
|
||||
return "wasmOptRefType(kWasmI31Ref)";
|
||||
case HeapType::kBottom:
|
||||
@ -249,6 +251,8 @@ std::string ValueTypeToConstantName(ValueType type) {
|
||||
return "wasmRefType(kWasmAnyRef)";
|
||||
case HeapType::kData:
|
||||
return "wasmRefType(kWasmDataRef)";
|
||||
case HeapType::kArray:
|
||||
return "wasmRefType(kWasmArrayRef)";
|
||||
case HeapType::kI31:
|
||||
return "wasmRefType(kWasmI31Ref)";
|
||||
case HeapType::kBottom:
|
||||
@ -272,6 +276,8 @@ std::string HeapTypeToConstantName(HeapType heap_type) {
|
||||
return "kWasmI31Ref";
|
||||
case HeapType::kData:
|
||||
return "kWasmDataRef";
|
||||
case HeapType::kArray:
|
||||
return "kWasmArrayRef";
|
||||
case HeapType::kAny:
|
||||
return "kWasmAnyRef";
|
||||
case HeapType::kBottom:
|
||||
|
@ -121,6 +121,7 @@ let kWasmAnyRef = -0x12;
|
||||
let kWasmEqRef = -0x13;
|
||||
let kWasmI31Ref = -0x16;
|
||||
let kWasmDataRef = -0x19;
|
||||
let kWasmArrayRef = -0x20;
|
||||
|
||||
// Use the positive-byte versions inside function bodies.
|
||||
let kLeb128Mask = 0x7f;
|
||||
@ -129,8 +130,9 @@ let kAnyFuncCode = kFuncRefCode; // Alias named as in the JS API spec
|
||||
let kExternRefCode = kWasmExternRef & kLeb128Mask;
|
||||
let kAnyRefCode = kWasmAnyRef & kLeb128Mask;
|
||||
let kEqRefCode = kWasmEqRef & kLeb128Mask;
|
||||
let kI31RefCode = kWasmI31Ref & kLeb128Mask;
|
||||
let kDataRefCode = kWasmDataRef & kLeb128Mask;
|
||||
let kI31RefCode = kWasmI31Ref & kLeb128Mask;
|
||||
let kDataRefCode = kWasmDataRef & kLeb128Mask;
|
||||
let kArrayRefCode = kWasmArrayRef & kLeb128Mask;
|
||||
|
||||
let kWasmOptRef = 0x6c;
|
||||
let kWasmRef = 0x6b;
|
||||
|
@ -4071,6 +4071,9 @@ TEST_F(FunctionBodyDecoderTest, GCArray) {
|
||||
ValueType reps_c_r[] = {kWasmFuncRef, array_type};
|
||||
ValueType reps_f_r[] = {kWasmF32, array_type};
|
||||
ValueType reps_i_r[] = {kWasmI32, array_type};
|
||||
ValueType reps_i_a[] = {kWasmI32, kWasmArrayRef};
|
||||
ValueType reps_i_s[] = {kWasmI32,
|
||||
ValueType::Ref(struct_type_index, kNonNullable)};
|
||||
const FunctionSig sig_c_r(1, 1, reps_c_r);
|
||||
const FunctionSig sig_v_r(0, 1, &array_type);
|
||||
const FunctionSig sig_v_r2(0, 1, &immutable_array_type);
|
||||
@ -4078,6 +4081,8 @@ TEST_F(FunctionBodyDecoderTest, GCArray) {
|
||||
const FunctionSig sig_f_r(1, 1, reps_f_r);
|
||||
const FunctionSig sig_v_cr(0, 2, reps_c_r);
|
||||
const FunctionSig sig_i_r(1, 1, reps_i_r);
|
||||
const FunctionSig sig_i_a(1, 1, reps_i_a);
|
||||
const FunctionSig sig_i_s(1, 1, reps_i_s);
|
||||
|
||||
/** array.new_with_rtt **/
|
||||
ExpectValidates(&sig_r_v,
|
||||
@ -4186,16 +4191,16 @@ TEST_F(FunctionBodyDecoderTest, GCArray) {
|
||||
"array.set[2] expected type funcref, found i64.const of type i64");
|
||||
|
||||
/** array.len **/
|
||||
ExpectValidates(&sig_i_r,
|
||||
{WASM_ARRAY_LEN(array_type_index, WASM_LOCAL_GET(0))});
|
||||
// Works both with conrete array types and arrayref.
|
||||
ExpectValidates(&sig_i_r, {WASM_ARRAY_LEN(WASM_LOCAL_GET(0))});
|
||||
ExpectValidates(&sig_i_a, {WASM_ARRAY_LEN(WASM_LOCAL_GET(0))});
|
||||
// Wrong return type.
|
||||
ExpectFailure(&sig_f_r, {WASM_ARRAY_LEN(array_type_index, WASM_LOCAL_GET(0))},
|
||||
kAppendEnd,
|
||||
ExpectFailure(&sig_f_r, {WASM_ARRAY_LEN(WASM_LOCAL_GET(0))}, kAppendEnd,
|
||||
"type error in fallthru[0] (expected f32, got i32)");
|
||||
// Non-array type index.
|
||||
ExpectFailure(&sig_i_r,
|
||||
{WASM_ARRAY_LEN(struct_type_index, WASM_LOCAL_GET(0))},
|
||||
kAppendEnd, "invalid array index: 1");
|
||||
// Non-array argument.
|
||||
ExpectFailure(&sig_i_s, {WASM_ARRAY_LEN(WASM_LOCAL_GET(0))}, kAppendEnd,
|
||||
"array.len[0] expected type (ref null array), found local.get "
|
||||
"of type (ref 1)");
|
||||
|
||||
// Immutable array.
|
||||
// Allocating and reading is OK:
|
||||
|
@ -14,8 +14,12 @@ namespace subtyping_unittest {
|
||||
class WasmSubtypingTest : public ::testing::Test {};
|
||||
using FieldInit = std::pair<ValueType, bool>;
|
||||
|
||||
ValueType ref(uint32_t index) { return ValueType::Ref(index, kNonNullable); }
|
||||
ValueType optRef(uint32_t index) { return ValueType::Ref(index, kNullable); }
|
||||
constexpr ValueType ref(uint32_t index) {
|
||||
return ValueType::Ref(index, kNonNullable);
|
||||
}
|
||||
constexpr ValueType optRef(uint32_t index) {
|
||||
return ValueType::Ref(index, kNullable);
|
||||
}
|
||||
|
||||
FieldInit mut(ValueType type) { return FieldInit(type, true); }
|
||||
FieldInit immut(ValueType type) { return FieldInit(type, false); }
|
||||
@ -73,12 +77,12 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
||||
/* 16 */ DefineSignature(module, {ref(0)}, {ref(0)});
|
||||
}
|
||||
|
||||
ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
|
||||
kWasmS128};
|
||||
ValueType ref_types[] = {kWasmExternRef, kWasmFuncRef, kWasmEqRef,
|
||||
kWasmI31Ref, kWasmDataRef, kWasmAnyRef,
|
||||
optRef(0), ref(0), optRef(2),
|
||||
ref(2), optRef(11), ref(11)};
|
||||
constexpr ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
|
||||
kWasmS128};
|
||||
constexpr ValueType ref_types[] = {
|
||||
kWasmExternRef, kWasmFuncRef, kWasmEqRef, kWasmI31Ref, kWasmDataRef,
|
||||
kWasmArrayRef, kWasmAnyRef, optRef(0), ref(0), optRef(2),
|
||||
ref(2), optRef(11), ref(11)};
|
||||
|
||||
#define SUBTYPE(type1, type2) \
|
||||
EXPECT_TRUE(IsSubtypeOf(type1, type2, module1, module))
|
||||
@ -112,9 +116,12 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
||||
ref_type != kWasmAnyRef && ref_type != optRef(11) &&
|
||||
ref_type != ref(11));
|
||||
// Non-nullable struct/array types are subtypes of dataref.
|
||||
SUBTYPE_IFF(
|
||||
ref_type, kWasmDataRef,
|
||||
ref_type == kWasmDataRef || ref_type == ref(0) || ref_type == ref(2));
|
||||
SUBTYPE_IFF(ref_type, kWasmDataRef,
|
||||
ref_type == kWasmDataRef || ref_type == kWasmArrayRef ||
|
||||
ref_type == ref(0) || ref_type == ref(2));
|
||||
// Non-nullable array types are subtypes of arrayref.
|
||||
SUBTYPE_IFF(ref_type, kWasmArrayRef,
|
||||
ref_type == kWasmArrayRef || ref_type == ref(2));
|
||||
// Functions are subtypes of funcref.
|
||||
SUBTYPE_IFF(ref_type, kWasmFuncRef,
|
||||
ref_type == kWasmFuncRef || ref_type == optRef(11) ||
|
||||
@ -128,8 +135,10 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
||||
}
|
||||
|
||||
// The rest of ref. types are unrelated.
|
||||
for (ValueType type_1 : {kWasmExternRef, kWasmFuncRef, kWasmI31Ref}) {
|
||||
for (ValueType type_2 : {kWasmExternRef, kWasmFuncRef, kWasmI31Ref}) {
|
||||
for (ValueType type_1 :
|
||||
{kWasmExternRef, kWasmFuncRef, kWasmI31Ref, kWasmArrayRef}) {
|
||||
for (ValueType type_2 :
|
||||
{kWasmExternRef, kWasmFuncRef, kWasmI31Ref, kWasmArrayRef}) {
|
||||
SUBTYPE_IFF(type_1, type_2, type_1 == type_2);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user