1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  *
4  * Copyright 2015 Mozilla Foundation
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #include "wasm/WasmTypeDef.h"
20 
21 #include "mozilla/CheckedInt.h"
22 #include "mozilla/MathAlgorithms.h"
23 
24 #include "jit/JitOptions.h"
25 #include "js/friend/ErrorMessages.h"  // JSMSG_*
26 #include "js/Printf.h"
27 #include "js/Value.h"
28 #include "vm/StringType.h"
29 #include "wasm/WasmJS.h"
30 
31 using namespace js;
32 using namespace js::jit;
33 using namespace js::wasm;
34 
35 using mozilla::CheckedInt32;
36 using mozilla::IsPowerOfTwo;
37 
canHaveJitEntry() const38 bool FuncType::canHaveJitEntry() const {
39   return !hasUnexposableArgOrRet() &&
40          !temporarilyUnsupportedReftypeForEntry() &&
41          !temporarilyUnsupportedResultCountForJitEntry() &&
42          JitOptions.enableWasmJitEntry;
43 }
44 
canHaveJitExit() const45 bool FuncType::canHaveJitExit() const {
46   return !hasUnexposableArgOrRet() && !temporarilyUnsupportedReftypeForExit() &&
47          !temporarilyUnsupportedResultCountForJitExit() &&
48          JitOptions.enableWasmJitExit;
49 }
50 
serializedSize() const51 size_t FuncType::serializedSize() const {
52   return SerializedPodVectorSize(results_) + SerializedPodVectorSize(args_);
53 }
54 
serialize(uint8_t * cursor) const55 uint8_t* FuncType::serialize(uint8_t* cursor) const {
56   cursor = SerializePodVector(cursor, results_);
57   cursor = SerializePodVector(cursor, args_);
58   return cursor;
59 }
60 
deserialize(const uint8_t * cursor)61 const uint8_t* FuncType::deserialize(const uint8_t* cursor) {
62   cursor = DeserializePodVector(cursor, &results_);
63   if (!cursor) {
64     return nullptr;
65   }
66   return DeserializePodVector(cursor, &args_);
67 }
68 
sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const69 size_t FuncType::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
70   return args_.sizeOfExcludingThis(mallocSizeOf);
71 }
72 
RoundUpToAlignment(CheckedInt32 address,uint32_t align)73 static inline CheckedInt32 RoundUpToAlignment(CheckedInt32 address,
74                                               uint32_t align) {
75   MOZ_ASSERT(IsPowerOfTwo(align));
76 
77   // Note: Be careful to order operators such that we first make the
78   // value smaller and then larger, so that we don't get false
79   // overflow errors due to (e.g.) adding `align` and then
80   // subtracting `1` afterwards when merely adding `align-1` would
81   // not have overflowed. Note that due to the nature of two's
82   // complement representation, if `address` is already aligned,
83   // then adding `align-1` cannot itself cause an overflow.
84 
85   return ((address + (align - 1)) / align) * align;
86 }
87 
88 class StructLayout {
89   CheckedInt32 sizeSoFar = 0;
90   uint32_t structAlignment = 1;
91 
92  public:
93   // The field adders return the offset of the the field.
addField(FieldType type)94   CheckedInt32 addField(FieldType type) {
95     uint32_t fieldSize = type.size();
96     uint32_t fieldAlignment = type.alignmentInStruct();
97 
98     // Alignment of the struct is the max of the alignment of its fields.
99     structAlignment = std::max(structAlignment, fieldAlignment);
100 
101     // Align the pointer.
102     CheckedInt32 offset = RoundUpToAlignment(sizeSoFar, fieldAlignment);
103     if (!offset.isValid()) {
104       return offset;
105     }
106 
107     // Allocate space.
108     sizeSoFar = offset + fieldSize;
109     if (!sizeSoFar.isValid()) {
110       return sizeSoFar;
111     }
112 
113     return offset;
114   }
115 
116   // The close method rounds up the structure size to the appropriate
117   // alignment and returns that size.
close()118   CheckedInt32 close() {
119     return RoundUpToAlignment(sizeSoFar, structAlignment);
120   }
121 };
122 
computeLayout()123 bool StructType::computeLayout() {
124   StructLayout layout;
125   for (StructField& field : fields_) {
126     CheckedInt32 offset = layout.addField(field.type);
127     if (!offset.isValid()) {
128       return false;
129     }
130     field.offset = offset.value();
131   }
132 
133   CheckedInt32 size = layout.close();
134   if (!size.isValid()) {
135     return false;
136   }
137   size_ = size.value();
138 
139   return true;
140 }
141 
serializedSize() const142 size_t StructType::serializedSize() const {
143   return SerializedPodVectorSize(fields_) + sizeof(size_);
144 }
145 
serialize(uint8_t * cursor) const146 uint8_t* StructType::serialize(uint8_t* cursor) const {
147   cursor = SerializePodVector(cursor, fields_);
148   cursor = WriteBytes(cursor, &size_, sizeof(size_));
149   return cursor;
150 }
151 
deserialize(const uint8_t * cursor)152 const uint8_t* StructType::deserialize(const uint8_t* cursor) {
153   (cursor = DeserializePodVector(cursor, &fields_)) &&
154       (cursor = ReadBytes(cursor, &size_, sizeof(size_)));
155   return cursor;
156 }
157 
sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const158 size_t StructType::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
159   return fields_.sizeOfExcludingThis(mallocSizeOf);
160 }
161 
serializedSize() const162 size_t TypeDef::serializedSize() const {
163   size_t size = sizeof(kind_);
164   switch (kind_) {
165     case TypeDefKind::Struct: {
166       size += structType_.serializedSize();
167       break;
168     }
169     case TypeDefKind::Func: {
170       size += funcType_.serializedSize();
171       break;
172     }
173     case TypeDefKind::None: {
174       break;
175     }
176     default:
177       MOZ_ASSERT_UNREACHABLE();
178   }
179   return size;
180 }
181 
serialize(uint8_t * cursor) const182 uint8_t* TypeDef::serialize(uint8_t* cursor) const {
183   cursor = WriteBytes(cursor, &kind_, sizeof(kind_));
184   switch (kind_) {
185     case TypeDefKind::Struct: {
186       cursor = structType_.serialize(cursor);
187       break;
188     }
189     case TypeDefKind::Func: {
190       cursor = funcType_.serialize(cursor);
191       break;
192     }
193     case TypeDefKind::None: {
194       break;
195     }
196     default:
197       MOZ_ASSERT_UNREACHABLE();
198   }
199   return cursor;
200 }
201 
deserialize(const uint8_t * cursor)202 const uint8_t* TypeDef::deserialize(const uint8_t* cursor) {
203   cursor = ReadBytes(cursor, &kind_, sizeof(kind_));
204   // kind_ was replaced -- call in-place constructors for union members.
205   switch (kind_) {
206     case TypeDefKind::Struct: {
207       StructType* structType = new (&structType_) StructType();
208       cursor = structType->deserialize(cursor);
209       break;
210     }
211     case TypeDefKind::Func: {
212       FuncType* funcType = new (&funcType_) FuncType();
213       cursor = funcType->deserialize(cursor);
214       break;
215     }
216     case TypeDefKind::None: {
217       break;
218     }
219     default:
220       MOZ_ASSERT_UNREACHABLE();
221   }
222   return cursor;
223 }
224 
sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const225 size_t TypeDef::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
226   switch (kind_) {
227     case TypeDefKind::Struct: {
228       return structType_.sizeOfExcludingThis(mallocSizeOf);
229     }
230     case TypeDefKind::Func: {
231       return funcType_.sizeOfExcludingThis(mallocSizeOf);
232     }
233     case TypeDefKind::None: {
234       return 0;
235     }
236     default:
237       break;
238   }
239   MOZ_ASSERT_UNREACHABLE();
240   return 0;
241 }
242 
isRefEquivalent(RefType one,RefType two,TypeCache * cache) const243 TypeResult TypeContext::isRefEquivalent(RefType one, RefType two,
244                                         TypeCache* cache) const {
245   // Anything's equal to itself.
246   if (one == two) {
247     return TypeResult::True;
248   }
249 
250 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
251   if (features_.functionReferences) {
252     // Two references must have the same nullability to be equal
253     if (one.isNullable() != two.isNullable()) {
254       return TypeResult::False;
255     }
256 
257     // Non type-index references are equal if they have the same kind
258     if (!one.isTypeIndex() && !two.isTypeIndex() && one.kind() == two.kind()) {
259       return TypeResult::True;
260     }
261 
262     // Type-index references can be equal
263     if (one.isTypeIndex() && two.isTypeIndex()) {
264       return isTypeIndexEquivalent(one.typeIndex(), two.typeIndex(), cache);
265     }
266   }
267 #endif
268   return TypeResult::False;
269 }
270 
271 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
isTypeIndexEquivalent(uint32_t one,uint32_t two,TypeCache * cache) const272 TypeResult TypeContext::isTypeIndexEquivalent(uint32_t one, uint32_t two,
273                                               TypeCache* cache) const {
274   MOZ_ASSERT(features_.functionReferences);
275 
276   // Anything's equal to itself.
277   if (one == two) {
278     return TypeResult::True;
279   }
280 
281 #  ifdef ENABLE_WASM_GC
282   if (features_.gc) {
283     // A struct may be equal to a struct
284     if (isStructType(one) && isStructType(two)) {
285       return isStructEquivalent(one, two, cache);
286     }
287 
288     // An array may be equal to an array
289     if (isArrayType(one) && isArrayType(two)) {
290       return isArrayEquivalent(one, two, cache);
291     }
292   }
293 #  endif
294 
295   return TypeResult::False;
296 }
297 #endif
298 
299 #ifdef ENABLE_WASM_GC
isStructEquivalent(uint32_t oneIndex,uint32_t twoIndex,TypeCache * cache) const300 TypeResult TypeContext::isStructEquivalent(uint32_t oneIndex, uint32_t twoIndex,
301                                            TypeCache* cache) const {
302   if (cache->isEquivalent(oneIndex, twoIndex)) {
303     return TypeResult::True;
304   }
305 
306   const StructType& one = structType(oneIndex);
307   const StructType& two = structType(twoIndex);
308 
309   // Structs must have the same number of fields to be equal
310   if (one.fields_.length() != two.fields_.length()) {
311     return TypeResult::False;
312   }
313 
314   // Assume these structs are equal while checking fields. If any field is
315   // not equal then we remove the assumption.
316   if (!cache->markEquivalent(oneIndex, twoIndex)) {
317     return TypeResult::OOM;
318   }
319 
320   for (uint32_t i = 0; i < two.fields_.length(); i++) {
321     TypeResult result =
322         isStructFieldEquivalent(one.fields_[i], two.fields_[i], cache);
323     if (result != TypeResult::True) {
324       cache->unmarkEquivalent(oneIndex, twoIndex);
325       return result;
326     }
327   }
328   return TypeResult::True;
329 }
330 
isStructFieldEquivalent(const StructField one,const StructField two,TypeCache * cache) const331 TypeResult TypeContext::isStructFieldEquivalent(const StructField one,
332                                                 const StructField two,
333                                                 TypeCache* cache) const {
334   // Struct fields must share the same mutability to equal
335   if (one.isMutable != two.isMutable) {
336     return TypeResult::False;
337   }
338   // Struct field types must be equal
339   return isEquivalent(one.type, two.type, cache);
340 }
341 
isArrayEquivalent(uint32_t oneIndex,uint32_t twoIndex,TypeCache * cache) const342 TypeResult TypeContext::isArrayEquivalent(uint32_t oneIndex, uint32_t twoIndex,
343                                           TypeCache* cache) const {
344   if (cache->isEquivalent(oneIndex, twoIndex)) {
345     return TypeResult::True;
346   }
347 
348   const ArrayType& one = arrayType(oneIndex);
349   const ArrayType& two = arrayType(twoIndex);
350 
351   // Assume these arrays are equal while checking fields. If the array
352   // element is not equal then we remove the assumption.
353   if (!cache->markEquivalent(oneIndex, twoIndex)) {
354     return TypeResult::OOM;
355   }
356 
357   TypeResult result = isArrayElementEquivalent(one, two, cache);
358   if (result != TypeResult::True) {
359     cache->unmarkEquivalent(oneIndex, twoIndex);
360   }
361   return result;
362 }
363 
isArrayElementEquivalent(const ArrayType & one,const ArrayType & two,TypeCache * cache) const364 TypeResult TypeContext::isArrayElementEquivalent(const ArrayType& one,
365                                                  const ArrayType& two,
366                                                  TypeCache* cache) const {
367   // Array elements must share the same mutability to be equal
368   if (one.isMutable_ != two.isMutable_) {
369     return TypeResult::False;
370   }
371   // Array elements must be equal
372   return isEquivalent(one.elementType_, two.elementType_, cache);
373 }
374 #endif
375 
isRefSubtypeOf(RefType one,RefType two,TypeCache * cache) const376 TypeResult TypeContext::isRefSubtypeOf(RefType one, RefType two,
377                                        TypeCache* cache) const {
378   // Anything's a subtype of itself.
379   if (one == two) {
380     return TypeResult::True;
381   }
382 
383 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
384   if (features_.functionReferences) {
385     // A subtype must have the same nullability as the supertype or the
386     // supertype must be nullable.
387     if (!(one.isNullable() == two.isNullable() || two.isNullable())) {
388       return TypeResult::False;
389     }
390 
391     // Non type-index references are subtypes if they have the same kind
392     if (!one.isTypeIndex() && !two.isTypeIndex() && one.kind() == two.kind()) {
393       return TypeResult::True;
394     }
395 
396     // Structs are subtypes of eqref
397     if (isStructType(one) && two.isEq()) {
398       return TypeResult::True;
399     }
400 
401     // Arrays are subtypes of eqref
402     if (isArrayType(one) && two.isEq()) {
403       return TypeResult::True;
404     }
405 
406     // Type-index references can be subtypes
407     if (one.isTypeIndex() && two.isTypeIndex()) {
408       return isTypeIndexSubtypeOf(one.typeIndex(), two.typeIndex(), cache);
409     }
410   }
411 #endif
412   return TypeResult::False;
413 }
414 
415 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
isTypeIndexSubtypeOf(uint32_t one,uint32_t two,TypeCache * cache) const416 TypeResult TypeContext::isTypeIndexSubtypeOf(uint32_t one, uint32_t two,
417                                              TypeCache* cache) const {
418   MOZ_ASSERT(features_.functionReferences);
419 
420   // Anything's a subtype of itself.
421   if (one == two) {
422     return TypeResult::True;
423   }
424 
425 #  ifdef ENABLE_WASM_GC
426   if (features_.gc) {
427     // Structs may be subtypes of structs
428     if (isStructType(one) && isStructType(two)) {
429       return isStructSubtypeOf(one, two, cache);
430     }
431 
432     // Arrays may be subtypes of arrays
433     if (isArrayType(one) && isArrayType(two)) {
434       return isArraySubtypeOf(one, two, cache);
435     }
436   }
437 #  endif
438   return TypeResult::False;
439 }
440 #endif
441 
442 #ifdef ENABLE_WASM_GC
isStructSubtypeOf(uint32_t oneIndex,uint32_t twoIndex,TypeCache * cache) const443 TypeResult TypeContext::isStructSubtypeOf(uint32_t oneIndex, uint32_t twoIndex,
444                                           TypeCache* cache) const {
445   if (cache->isSubtypeOf(oneIndex, twoIndex)) {
446     return TypeResult::True;
447   }
448 
449   const StructType& one = structType(oneIndex);
450   const StructType& two = structType(twoIndex);
451 
452   // A subtype must have at least as many fields as its supertype
453   if (one.fields_.length() < two.fields_.length()) {
454     return TypeResult::False;
455   }
456 
457   // Assume these structs are subtypes while checking fields. If any field
458   // fails a check then we remove the assumption.
459   if (!cache->markSubtypeOf(oneIndex, twoIndex)) {
460     return TypeResult::OOM;
461   }
462 
463   for (uint32_t i = 0; i < two.fields_.length(); i++) {
464     TypeResult result =
465         isStructFieldSubtypeOf(one.fields_[i], two.fields_[i], cache);
466     if (result != TypeResult::True) {
467       cache->unmarkSubtypeOf(oneIndex, twoIndex);
468       return result;
469     }
470   }
471   return TypeResult::True;
472 }
473 
isStructFieldSubtypeOf(const StructField one,const StructField two,TypeCache * cache) const474 TypeResult TypeContext::isStructFieldSubtypeOf(const StructField one,
475                                                const StructField two,
476                                                TypeCache* cache) const {
477   // Mutable fields are invariant w.r.t. field types
478   if (one.isMutable && two.isMutable) {
479     return isEquivalent(one.type, two.type, cache);
480   }
481   // Immutable fields are covariant w.r.t. field types
482   if (!one.isMutable && !two.isMutable) {
483     return isSubtypeOf(one.type, two.type, cache);
484   }
485   return TypeResult::False;
486 }
487 
isArraySubtypeOf(uint32_t oneIndex,uint32_t twoIndex,TypeCache * cache) const488 TypeResult TypeContext::isArraySubtypeOf(uint32_t oneIndex, uint32_t twoIndex,
489                                          TypeCache* cache) const {
490   if (cache->isSubtypeOf(oneIndex, twoIndex)) {
491     return TypeResult::True;
492   }
493 
494   const ArrayType& one = arrayType(oneIndex);
495   const ArrayType& two = arrayType(twoIndex);
496 
497   // Assume these arrays are subtypes while checking elements. If the elements
498   // fail the check then we remove the assumption.
499   if (!cache->markSubtypeOf(oneIndex, twoIndex)) {
500     return TypeResult::OOM;
501   }
502 
503   TypeResult result = isArrayElementSubtypeOf(one, two, cache);
504   if (result != TypeResult::True) {
505     cache->unmarkSubtypeOf(oneIndex, twoIndex);
506   }
507   return result;
508 }
509 
isArrayElementSubtypeOf(const ArrayType & one,const ArrayType & two,TypeCache * cache) const510 TypeResult TypeContext::isArrayElementSubtypeOf(const ArrayType& one,
511                                                 const ArrayType& two,
512                                                 TypeCache* cache) const {
513   // Mutable elements are invariant w.r.t. field types
514   if (one.isMutable_ && two.isMutable_) {
515     return isEquivalent(one.elementType_, two.elementType_, cache);
516   }
517   // Immutable elements are covariant w.r.t. field types
518   if (!one.isMutable_ && !two.isMutable_) {
519     return isSubtypeOf(one.elementType_, two.elementType_, cache);
520   }
521   return TypeResult::False;
522 }
523 #endif
524