1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/wasm/wasm-subtyping.h"
6
7 #include "src/base/platform/mutex.h"
8 #include "src/wasm/canonical-types.h"
9 #include "src/wasm/wasm-module.h"
10 #include "src/zone/zone-containers.h"
11
12 namespace v8 {
13 namespace internal {
14 namespace wasm {
15
16 namespace {
17
EquivalentIndices(uint32_t index1,uint32_t index2,const WasmModule * module1,const WasmModule * module2)18 V8_INLINE bool EquivalentIndices(uint32_t index1, uint32_t index2,
19 const WasmModule* module1,
20 const WasmModule* module2) {
21 DCHECK(index1 != index2 || module1 != module2);
22 if (!FLAG_wasm_type_canonicalization) return false;
23 return module1->isorecursive_canonical_type_ids[index1] ==
24 module2->isorecursive_canonical_type_ids[index2];
25 }
26
ValidStructSubtypeDefinition(uint32_t subtype_index,uint32_t supertype_index,const WasmModule * sub_module,const WasmModule * super_module)27 bool ValidStructSubtypeDefinition(uint32_t subtype_index,
28 uint32_t supertype_index,
29 const WasmModule* sub_module,
30 const WasmModule* super_module) {
31 const StructType* sub_struct = sub_module->types[subtype_index].struct_type;
32 const StructType* super_struct =
33 super_module->types[supertype_index].struct_type;
34
35 if (sub_struct->field_count() < super_struct->field_count()) {
36 return false;
37 }
38
39 for (uint32_t i = 0; i < super_struct->field_count(); i++) {
40 bool sub_mut = sub_struct->mutability(i);
41 bool super_mut = super_struct->mutability(i);
42 if (sub_mut != super_mut ||
43 (sub_mut &&
44 !EquivalentTypes(sub_struct->field(i), super_struct->field(i),
45 sub_module, super_module)) ||
46 (!sub_mut && !IsSubtypeOf(sub_struct->field(i), super_struct->field(i),
47 sub_module, super_module))) {
48 return false;
49 }
50 }
51 return true;
52 }
53
ValidArraySubtypeDefinition(uint32_t subtype_index,uint32_t supertype_index,const WasmModule * sub_module,const WasmModule * super_module)54 bool ValidArraySubtypeDefinition(uint32_t subtype_index,
55 uint32_t supertype_index,
56 const WasmModule* sub_module,
57 const WasmModule* super_module) {
58 const ArrayType* sub_array = sub_module->types[subtype_index].array_type;
59 const ArrayType* super_array =
60 super_module->types[supertype_index].array_type;
61 bool sub_mut = sub_array->mutability();
62 bool super_mut = super_array->mutability();
63
64 return (sub_mut && super_mut &&
65 EquivalentTypes(sub_array->element_type(),
66 super_array->element_type(), sub_module,
67 super_module)) ||
68 (!sub_mut && !super_mut &&
69 IsSubtypeOf(sub_array->element_type(), super_array->element_type(),
70 sub_module, super_module));
71 }
72
ValidFunctionSubtypeDefinition(uint32_t subtype_index,uint32_t supertype_index,const WasmModule * sub_module,const WasmModule * super_module)73 bool ValidFunctionSubtypeDefinition(uint32_t subtype_index,
74 uint32_t supertype_index,
75 const WasmModule* sub_module,
76 const WasmModule* super_module) {
77 const FunctionSig* sub_func = sub_module->types[subtype_index].function_sig;
78 const FunctionSig* super_func =
79 super_module->types[supertype_index].function_sig;
80
81 if (sub_func->parameter_count() != super_func->parameter_count() ||
82 sub_func->return_count() != super_func->return_count()) {
83 return false;
84 }
85
86 for (uint32_t i = 0; i < sub_func->parameter_count(); i++) {
87 // Contravariance for params.
88 if (!IsSubtypeOf(super_func->parameters()[i], sub_func->parameters()[i],
89 super_module, sub_module)) {
90 return false;
91 }
92 }
93 for (uint32_t i = 0; i < sub_func->return_count(); i++) {
94 // Covariance for returns.
95 if (!IsSubtypeOf(sub_func->returns()[i], super_func->returns()[i],
96 sub_module, super_module)) {
97 return false;
98 }
99 }
100
101 return true;
102 }
103
104 } // namespace
105
ValidSubtypeDefinition(uint32_t subtype_index,uint32_t supertype_index,const WasmModule * sub_module,const WasmModule * super_module)106 bool ValidSubtypeDefinition(uint32_t subtype_index, uint32_t supertype_index,
107 const WasmModule* sub_module,
108 const WasmModule* super_module) {
109 TypeDefinition::Kind sub_kind = sub_module->types[subtype_index].kind;
110 TypeDefinition::Kind super_kind = super_module->types[supertype_index].kind;
111 if (sub_kind != super_kind) return false;
112 switch (sub_kind) {
113 case TypeDefinition::kFunction:
114 return ValidFunctionSubtypeDefinition(subtype_index, supertype_index,
115 sub_module, super_module);
116 case TypeDefinition::kStruct:
117 return ValidStructSubtypeDefinition(subtype_index, supertype_index,
118 sub_module, super_module);
119 case TypeDefinition::kArray:
120 return ValidArraySubtypeDefinition(subtype_index, supertype_index,
121 sub_module, super_module);
122 }
123 }
124
IsSubtypeOfImpl(ValueType subtype,ValueType supertype,const WasmModule * sub_module,const WasmModule * super_module)125 V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
126 ValueType subtype, ValueType supertype, const WasmModule* sub_module,
127 const WasmModule* super_module) {
128 DCHECK(subtype != supertype || sub_module != super_module);
129
130 switch (subtype.kind()) {
131 case kI32:
132 case kI64:
133 case kF32:
134 case kF64:
135 case kS128:
136 case kI8:
137 case kI16:
138 case kVoid:
139 case kBottom:
140 return subtype == supertype;
141 case kRtt:
142 return supertype.kind() == kRtt &&
143 EquivalentIndices(subtype.ref_index(), supertype.ref_index(),
144 sub_module, super_module);
145 case kRef:
146 case kOptRef:
147 break;
148 }
149
150 DCHECK(subtype.is_object_reference());
151
152 bool compatible_references = subtype.is_nullable()
153 ? supertype.is_nullable()
154 : supertype.is_object_reference();
155 if (!compatible_references) return false;
156
157 DCHECK(supertype.is_object_reference());
158
159 // Now check that sub_heap and super_heap are subtype-related.
160
161 HeapType sub_heap = subtype.heap_type();
162 HeapType super_heap = supertype.heap_type();
163
164 switch (sub_heap.representation()) {
165 case HeapType::kFunc:
166 // funcref is a subtype of anyref (aka externref) under wasm-gc.
167 return sub_heap == super_heap ||
168 (FLAG_experimental_wasm_gc && super_heap == HeapType::kAny);
169 case HeapType::kEq:
170 return sub_heap == super_heap || super_heap == HeapType::kAny;
171 case HeapType::kAny:
172 return super_heap == HeapType::kAny;
173 case HeapType::kI31:
174 case HeapType::kData:
175 return super_heap == sub_heap || super_heap == HeapType::kEq ||
176 super_heap == HeapType::kAny;
177 case HeapType::kArray:
178 return super_heap == HeapType::kArray || super_heap == HeapType::kData ||
179 super_heap == HeapType::kEq || super_heap == HeapType::kAny;
180 case HeapType::kBottom:
181 UNREACHABLE();
182 default:
183 break;
184 }
185
186 DCHECK(sub_heap.is_index());
187 uint32_t sub_index = sub_heap.ref_index();
188 DCHECK(sub_module->has_type(sub_index));
189
190 switch (super_heap.representation()) {
191 case HeapType::kFunc:
192 return sub_module->has_signature(sub_index);
193 case HeapType::kEq:
194 case HeapType::kData:
195 return !sub_module->has_signature(sub_index);
196 case HeapType::kArray:
197 return sub_module->has_array(sub_index);
198 case HeapType::kI31:
199 return false;
200 case HeapType::kAny:
201 return true;
202 case HeapType::kBottom:
203 UNREACHABLE();
204 default:
205 break;
206 }
207
208 DCHECK(super_heap.is_index());
209 uint32_t super_index = super_heap.ref_index();
210 DCHECK(super_module->has_type(super_index));
211 // The {IsSubtypeOf} entry point already has a fast path checking ValueType
212 // equality; here we catch (ref $x) being a subtype of (ref null $x).
213 if (sub_module == super_module && sub_index == super_index) return true;
214
215 if (FLAG_wasm_type_canonicalization) {
216 return GetTypeCanonicalizer()->IsCanonicalSubtype(sub_index, super_index,
217 sub_module, super_module);
218 } else {
219 uint32_t explicit_super = sub_module->supertype(sub_index);
220 while (true) {
221 if (explicit_super == super_index) return true;
222 // Reached the end of the explicitly defined inheritance chain.
223 if (explicit_super == kNoSuperType) return false;
224 explicit_super = sub_module->supertype(explicit_super);
225 }
226 }
227 }
228
EquivalentTypes(ValueType type1,ValueType type2,const WasmModule * module1,const WasmModule * module2)229 V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
230 const WasmModule* module1,
231 const WasmModule* module2) {
232 if (type1 == type2 && module1 == module2) return true;
233 if (!type1.has_index()) return type1 == type2;
234 if (type1.kind() != type2.kind()) return false;
235
236 DCHECK(type1.has_index() && type2.has_index() &&
237 (type1 != type2 || module1 != module2));
238
239 DCHECK(type1.has_index() && module1->has_type(type1.ref_index()) &&
240 type2.has_index() && module2->has_type(type2.ref_index()));
241
242 return EquivalentIndices(type1.ref_index(), type2.ref_index(), module1,
243 module2);
244 }
245
246 } // namespace wasm
247 } // namespace internal
248 } // namespace v8
249