| // 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. | 
 |  | 
 | #include "src/wasm/wasm-subtyping.h" | 
 | #include "test/unittests/test-utils.h" | 
 |  | 
 | namespace v8 { | 
 | namespace internal { | 
 | namespace wasm { | 
 | 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); } | 
 |  | 
 | FieldInit mut(ValueType type) { return FieldInit(type, true); } | 
 | FieldInit immut(ValueType type) { return FieldInit(type, false); } | 
 |  | 
 | void DefineStruct(WasmModule* module, std::initializer_list<FieldInit> fields) { | 
 |   StructType::Builder builder(module->signature_zone.get(), | 
 |                               static_cast<uint32_t>(fields.size())); | 
 |   for (FieldInit field : fields) { | 
 |     builder.AddField(field.first, field.second); | 
 |   } | 
 |   return module->add_struct_type(builder.Build()); | 
 | } | 
 |  | 
 | void DefineArray(WasmModule* module, FieldInit element_type) { | 
 |   module->add_array_type(module->signature_zone->New<ArrayType>( | 
 |       element_type.first, element_type.second)); | 
 | } | 
 |  | 
 | TEST_F(WasmSubtypingTest, Subtyping) { | 
 |   v8::internal::AccountingAllocator allocator; | 
 |   WasmModule module1_(std::make_unique<Zone>(&allocator, ZONE_NAME)); | 
 |   WasmModule module2_(std::make_unique<Zone>(&allocator, ZONE_NAME)); | 
 |  | 
 |   WasmModule* module1 = &module1_; | 
 |   WasmModule* module2 = &module2_; | 
 |  | 
 |   // Set up two identical modules. | 
 |   for (WasmModule* module : {module1, module2}) { | 
 |     /* 0 */ DefineStruct(module, {mut(ref(2)), immut(optRef(2))}); | 
 |     /* 1 */ DefineStruct(module, {mut(ref(2)), immut(ref(2))}); | 
 |     /* 2 */ DefineArray(module, immut(ref(0))); | 
 |     /* 3 */ DefineArray(module, immut(ref(1))); | 
 |     /* 4 */ DefineStruct(module, {mut(ref(2)), immut(ref(3)), immut(kWasmF64)}); | 
 |     /* 5 */ DefineStruct(module, {mut(optRef(2)), immut(ref(2))}); | 
 |     /* 6 */ DefineArray(module, mut(kWasmI32)); | 
 |     /* 7 */ DefineArray(module, immut(kWasmI32)); | 
 |     /* 8 */ DefineStruct(module, {mut(kWasmI32), immut(optRef(8))}); | 
 |     /* 9 */ DefineStruct(module, {mut(kWasmI32), immut(optRef(8))}); | 
 |   } | 
 |  | 
 |   ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64, | 
 |                                kWasmS128}; | 
 |   ValueType ref_types[] = {kWasmExternRef, kWasmFuncRef, kWasmExnRef, | 
 |                            kWasmEqRef,     kWasmI31Ref,  optRef(0), | 
 |                            ref(0),         optRef(2),    ref(2)}; | 
 |  | 
 |   // Type judgements across modules should work the same as within one module. | 
 |   for (WasmModule* module : {module1, module2}) { | 
 |     // Value types are unrelated, except if they are equal. | 
 |     for (ValueType subtype : numeric_types) { | 
 |       for (ValueType supertype : numeric_types) { | 
 |         CHECK_EQ(IsSubtypeOf(subtype, supertype, module1, module), | 
 |                  subtype == supertype); | 
 |       } | 
 |     } | 
 |  | 
 |     // Value types are unrelated with reference types. | 
 |     for (ValueType value_type : numeric_types) { | 
 |       for (ValueType ref_type : ref_types) { | 
 |         CHECK(!IsSubtypeOf(value_type, ref_type, module1, module)); | 
 |         CHECK(!IsSubtypeOf(ref_type, value_type, module1, module)); | 
 |       } | 
 |     } | 
 |  | 
 |     for (ValueType ref_type : ref_types) { | 
 |       // Concrete reference types and i31ref are subtypes of eqref, | 
 |       // exnref/externref/funcref are not. | 
 |       CHECK_EQ(IsSubtypeOf(ref_type, kWasmEqRef, module1, module), | 
 |                ref_type != kWasmFuncRef && ref_type != kWasmExternRef && | 
 |                    ref_type != kWasmExnRef); | 
 |       // Each reference type is a subtype of itself. | 
 |       CHECK(IsSubtypeOf(ref_type, ref_type, module1, module)); | 
 |     } | 
 |  | 
 |     // The rest of ref. types are unrelated. | 
 |     for (ValueType type_1 : | 
 |          {kWasmExternRef, kWasmFuncRef, kWasmExnRef, kWasmI31Ref}) { | 
 |       for (ValueType type_2 : | 
 |            {kWasmExternRef, kWasmFuncRef, kWasmExnRef, kWasmI31Ref}) { | 
 |         CHECK_EQ(IsSubtypeOf(type_1, type_2, module1, module), | 
 |                  type_1 == type_2); | 
 |       } | 
 |     } | 
 |  | 
 |     // Unrelated refs are unrelated. | 
 |     CHECK(!IsSubtypeOf(ref(0), ref(2), module1, module)); | 
 |     CHECK(!IsSubtypeOf(optRef(3), optRef(1), module1, module)); | 
 |     // ref is a subtype of optref for the same struct/array. | 
 |     CHECK(IsSubtypeOf(ref(0), optRef(0), module1, module)); | 
 |     CHECK(IsSubtypeOf(ref(2), optRef(2), module1, module)); | 
 |     // optref is not a subtype of ref for the same struct/array. | 
 |     CHECK(!IsSubtypeOf(optRef(0), ref(0), module1, module)); | 
 |     CHECK(!IsSubtypeOf(optRef(2), ref(2), module1, module)); | 
 |     // ref is a subtype of optref if the same is true for the underlying | 
 |     // structs/arrays. | 
 |     CHECK(IsSubtypeOf(ref(3), optRef(2), module1, module)); | 
 |     // Prefix subtyping for structs. | 
 |     CHECK(IsSubtypeOf(optRef(4), optRef(0), module1, module)); | 
 |     // Mutable fields are invariant. | 
 |     CHECK(!IsSubtypeOf(ref(0), ref(5), module1, module)); | 
 |     // Immutable fields are covariant. | 
 |     CHECK(IsSubtypeOf(ref(1), ref(0), module1, module)); | 
 |     // Prefix subtyping + immutable field covariance for structs. | 
 |     CHECK(IsSubtypeOf(optRef(4), optRef(1), module1, module)); | 
 |     // No subtyping between mutable/immutable fields. | 
 |     CHECK(!IsSubtypeOf(ref(7), ref(6), module1, module)); | 
 |     CHECK(!IsSubtypeOf(ref(6), ref(7), module1, module)); | 
 |     // Recursive types. | 
 |     CHECK(IsSubtypeOf(ref(9), ref(8), module1, module)); | 
 |  | 
 |     // Identical rtts are subtypes of each other. | 
 |     CHECK(IsSubtypeOf(ValueType::Rtt(5, 3), ValueType::Rtt(5, 3), module1, | 
 |                       module2)); | 
 |     CHECK(IsSubtypeOf(ValueType::Rtt(HeapType::kExn, 3), | 
 |                       ValueType::Rtt(HeapType::kExn, 3), module1, module2)); | 
 |     // Rtts of different depth are unrelated. | 
 |     CHECK(!IsSubtypeOf(ValueType::Rtt(5, 1), ValueType::Rtt(5, 3), module1, | 
 |                        module2)); | 
 |     CHECK(!IsSubtypeOf(ValueType::Rtt(5, 8), ValueType::Rtt(5, 3), module1, | 
 |                        module2)); | 
 |     // Rtts of identical types are subtype-related. | 
 |     CHECK(IsSubtypeOf(ValueType::Rtt(8, 1), ValueType::Rtt(9, 1), module1, | 
 |                       module)); | 
 |     // Rtts of subtypes are not related. | 
 |     CHECK(!IsSubtypeOf(ValueType::Rtt(1, 1), ValueType::Rtt(0, 1), module1, | 
 |                        module)); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace subtyping_unittest | 
 | }  // namespace wasm | 
 | }  // namespace internal | 
 | }  // namespace v8 |