reg_type_cache.cc revision 7da9586b559290e1c16207c6513ffe485de61655
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "reg_type_cache-inl.h"
18
19#include "base/casts.h"
20#include "class_linker-inl.h"
21#include "dex_file-inl.h"
22#include "mirror/class-inl.h"
23#include "mirror/object-inl.h"
24
25namespace art {
26namespace verifier {
27
28bool RegTypeCache::primitive_initialized_ = false;
29uint16_t RegTypeCache::primitive_count_ = 0;
30PreciseConstType* RegTypeCache::small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1];
31
32static bool MatchingPrecisionForClass(RegType* entry, bool precise)
33    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
34  if (entry->IsPreciseReference() == precise) {
35    // We were or weren't looking for a precise reference and we found what we need.
36    return true;
37  } else {
38    if (!precise && entry->GetClass()->CannotBeAssignedFromOtherTypes()) {
39      // We weren't looking for a precise reference, as we're looking up based on a descriptor, but
40      // we found a matching entry based on the descriptor. Return the precise entry in that case.
41      return true;
42    }
43    return false;
44  }
45}
46
47void RegTypeCache::FillPrimitiveAndSmallConstantTypes() {
48  entries_.push_back(UndefinedType::GetInstance());
49  entries_.push_back(ConflictType::GetInstance());
50  entries_.push_back(BooleanType::GetInstance());
51  entries_.push_back(ByteType::GetInstance());
52  entries_.push_back(ShortType::GetInstance());
53  entries_.push_back(CharType::GetInstance());
54  entries_.push_back(IntegerType::GetInstance());
55  entries_.push_back(LongLoType::GetInstance());
56  entries_.push_back(LongHiType::GetInstance());
57  entries_.push_back(FloatType::GetInstance());
58  entries_.push_back(DoubleLoType::GetInstance());
59  entries_.push_back(DoubleHiType::GetInstance());
60  for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
61    int32_t i = value - kMinSmallConstant;
62    DCHECK_EQ(entries_.size(), small_precise_constants_[i]->GetId());
63    entries_.push_back(small_precise_constants_[i]);
64  }
65  DCHECK_EQ(entries_.size(), primitive_count_);
66}
67
68RegType& RegTypeCache::FromDescriptor(mirror::ClassLoader* loader, const char* descriptor,
69                                      bool precise) {
70  DCHECK(RegTypeCache::primitive_initialized_);
71  if (descriptor[1] == '\0') {
72    switch (descriptor[0]) {
73      case 'Z':
74        return Boolean();
75      case 'B':
76        return Byte();
77      case 'S':
78        return Short();
79      case 'C':
80        return Char();
81      case 'I':
82        return Integer();
83      case 'J':
84        return LongLo();
85      case 'F':
86        return Float();
87      case 'D':
88        return DoubleLo();
89      case 'V':  // For void types, conflict types.
90      default:
91        return Conflict();
92    }
93  } else if (descriptor[0] == 'L' || descriptor[0] == '[') {
94    return From(loader, descriptor, precise);
95  } else {
96    return Conflict();
97  }
98};
99
100RegType& RegTypeCache::RegTypeFromPrimitiveType(Primitive::Type prim_type) const {
101  CHECK(RegTypeCache::primitive_initialized_);
102  switch (prim_type) {
103    case Primitive::kPrimBoolean:
104      return *BooleanType::GetInstance();
105    case Primitive::kPrimByte:
106      return *ByteType::GetInstance();
107    case Primitive::kPrimShort:
108      return *ShortType::GetInstance();
109    case Primitive::kPrimChar:
110      return *CharType::GetInstance();
111    case Primitive::kPrimInt:
112      return *IntegerType::GetInstance();
113    case Primitive::kPrimLong:
114      return *LongLoType::GetInstance();
115    case Primitive::kPrimFloat:
116      return *FloatType::GetInstance();
117    case Primitive::kPrimDouble:
118      return *DoubleLoType::GetInstance();
119    case Primitive::kPrimVoid:
120    default:
121      return *ConflictType::GetInstance();
122  }
123}
124
125bool RegTypeCache::MatchDescriptor(size_t idx, const char* descriptor, bool precise) {
126  RegType* entry = entries_[idx];
127  if (entry->descriptor_ != descriptor) {
128    return false;
129  }
130  if (entry->HasClass()) {
131    return MatchingPrecisionForClass(entry, precise);
132  }
133  // There is no notion of precise unresolved references, the precise information is just dropped
134  // on the floor.
135  DCHECK(entry->IsUnresolvedReference());
136  return true;
137}
138
139mirror::Class* RegTypeCache::ResolveClass(const char* descriptor, mirror::ClassLoader* loader) {
140  // Class was not found, must create new type.
141  // Try resolving class
142  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
143  Thread* self = Thread::Current();
144  StackHandleScope<1> hs(self);
145  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(loader));
146  mirror::Class* klass = NULL;
147  if (can_load_classes_) {
148    klass = class_linker->FindClass(self, descriptor, class_loader);
149  } else {
150    klass = class_linker->LookupClass(descriptor, loader);
151    if (klass != nullptr && !klass->IsLoaded()) {
152      // We found the class but without it being loaded its not safe for use.
153      klass = nullptr;
154    }
155  }
156  return klass;
157}
158
159RegType& RegTypeCache::From(mirror::ClassLoader* loader, const char* descriptor,
160                            bool precise) {
161  // Try looking up the class in the cache first.
162  for (size_t i = primitive_count_; i < entries_.size(); i++) {
163    if (MatchDescriptor(i, descriptor, precise)) {
164      return *(entries_[i]);
165    }
166  }
167  // Class not found in the cache, will create a new type for that.
168  // Try resolving class.
169  mirror::Class* klass = ResolveClass(descriptor, loader);
170  if (klass != NULL) {
171    // Class resolved, first look for the class in the list of entries
172    // Class was not found, must create new type.
173    // To pass the verification, the type should be imprecise,
174    // instantiable or an interface with the precise type set to false.
175    DCHECK(!precise || klass->IsInstantiable());
176    // Create a precise type if:
177    // 1- Class is final and NOT an interface. a precise interface is meaningless !!
178    // 2- Precise Flag passed as true.
179    RegType* entry;
180    // Create an imprecise type if we can't tell for a fact that it is precise.
181    if (klass->CannotBeAssignedFromOtherTypes() || precise) {
182      DCHECK(!(klass->IsAbstract()) || klass->IsArrayClass());
183      DCHECK(!klass->IsInterface());
184      entry = new PreciseReferenceType(klass, descriptor, entries_.size());
185    } else {
186      entry = new ReferenceType(klass, descriptor, entries_.size());
187    }
188    AddEntry(entry);
189    return *entry;
190  } else {  // Class not resolved.
191    // We tried loading the class and failed, this might get an exception raised
192    // so we want to clear it before we go on.
193    if (can_load_classes_) {
194      DCHECK(Thread::Current()->IsExceptionPending());
195      Thread::Current()->ClearException();
196    } else {
197      DCHECK(!Thread::Current()->IsExceptionPending());
198    }
199    if (IsValidDescriptor(descriptor)) {
200      RegType* entry = new UnresolvedReferenceType(descriptor, entries_.size());
201      AddEntry(entry);
202      return *entry;
203    } else {
204      // The descriptor is broken return the unknown type as there's nothing sensible that
205      // could be done at runtime
206      return Conflict();
207    }
208  }
209}
210
211RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* klass, bool precise) {
212  DCHECK(klass != nullptr && !klass->IsErroneous());
213  if (klass->IsPrimitive()) {
214    // Note: precise isn't used for primitive classes. A char is assignable to an int. All
215    // primitive classes are final.
216    return RegTypeFromPrimitiveType(klass->GetPrimitiveType());
217  } else {
218    // Look for the reference in the list of entries to have.
219    for (size_t i = primitive_count_; i < entries_.size(); i++) {
220      RegType* cur_entry = entries_[i];
221      if (cur_entry->klass_.Read() == klass && MatchingPrecisionForClass(cur_entry, precise)) {
222        return *cur_entry;
223      }
224    }
225    // No reference to the class was found, create new reference.
226    RegType* entry;
227    if (precise) {
228      entry = new PreciseReferenceType(klass, descriptor, entries_.size());
229    } else {
230      entry = new ReferenceType(klass, descriptor, entries_.size());
231    }
232    AddEntry(entry);
233    return *entry;
234  }
235}
236
237RegTypeCache::RegTypeCache(bool can_load_classes) : can_load_classes_(can_load_classes) {
238  if (kIsDebugBuild && can_load_classes) {
239    Thread::Current()->AssertThreadSuspensionIsAllowable();
240  }
241  entries_.reserve(64);
242  FillPrimitiveAndSmallConstantTypes();
243}
244
245RegTypeCache::~RegTypeCache() {
246  CHECK_LE(primitive_count_, entries_.size());
247  // Delete only the non primitive types.
248  if (entries_.size() == kNumPrimitivesAndSmallConstants) {
249    // All entries are from the global pool, nothing to delete.
250    return;
251  }
252  std::vector<RegType*>::iterator non_primitive_begin = entries_.begin();
253  std::advance(non_primitive_begin, kNumPrimitivesAndSmallConstants);
254  STLDeleteContainerPointers(non_primitive_begin, entries_.end());
255}
256
257void RegTypeCache::ShutDown() {
258  if (RegTypeCache::primitive_initialized_) {
259    UndefinedType::Destroy();
260    ConflictType::Destroy();
261    BooleanType::Destroy();
262    ByteType::Destroy();
263    ShortType::Destroy();
264    CharType::Destroy();
265    IntegerType::Destroy();
266    LongLoType::Destroy();
267    LongHiType::Destroy();
268    FloatType::Destroy();
269    DoubleLoType::Destroy();
270    DoubleHiType::Destroy();
271    for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
272      PreciseConstType* type = small_precise_constants_[value - kMinSmallConstant];
273      delete type;
274      small_precise_constants_[value - kMinSmallConstant] = nullptr;
275    }
276    RegTypeCache::primitive_initialized_ = false;
277    RegTypeCache::primitive_count_ = 0;
278  }
279}
280
281template <class Type>
282Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) {
283  mirror::Class* klass = NULL;
284  // Try loading the class from linker.
285  if (!descriptor.empty()) {
286    klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(),
287                                                                       descriptor.c_str());
288  }
289  Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_);
290  RegTypeCache::primitive_count_++;
291  return entry;
292}
293
294void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() {
295  CreatePrimitiveTypeInstance<UndefinedType>("");
296  CreatePrimitiveTypeInstance<ConflictType>("");
297  CreatePrimitiveTypeInstance<BooleanType>("Z");
298  CreatePrimitiveTypeInstance<ByteType>("B");
299  CreatePrimitiveTypeInstance<ShortType>("S");
300  CreatePrimitiveTypeInstance<CharType>("C");
301  CreatePrimitiveTypeInstance<IntegerType>("I");
302  CreatePrimitiveTypeInstance<LongLoType>("J");
303  CreatePrimitiveTypeInstance<LongHiType>("J");
304  CreatePrimitiveTypeInstance<FloatType>("F");
305  CreatePrimitiveTypeInstance<DoubleLoType>("D");
306  CreatePrimitiveTypeInstance<DoubleHiType>("D");
307  for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
308    PreciseConstType* type = new PreciseConstType(value, primitive_count_);
309    small_precise_constants_[value - kMinSmallConstant] = type;
310    primitive_count_++;
311  }
312}
313
314RegType& RegTypeCache::FromUnresolvedMerge(RegType& left, RegType& right) {
315  std::set<uint16_t> types;
316  if (left.IsUnresolvedMergedReference()) {
317    types = (down_cast<UnresolvedMergedType*>(&left))->GetMergedTypes();
318  } else {
319    types.insert(left.GetId());
320  }
321  if (right.IsUnresolvedMergedReference()) {
322    std::set<uint16_t> right_types = (down_cast<UnresolvedMergedType*>(&right))->GetMergedTypes();
323    types.insert(right_types.begin(), right_types.end());
324  } else {
325    types.insert(right.GetId());
326  }
327  // Check if entry already exists.
328  for (size_t i = primitive_count_; i < entries_.size(); i++) {
329    RegType* cur_entry = entries_[i];
330    if (cur_entry->IsUnresolvedMergedReference()) {
331      std::set<uint16_t> cur_entry_types =
332          (down_cast<UnresolvedMergedType*>(cur_entry))->GetMergedTypes();
333      if (cur_entry_types == types) {
334        return *cur_entry;
335      }
336    }
337  }
338  // Create entry.
339  RegType* entry = new UnresolvedMergedType(left.GetId(), right.GetId(), this, entries_.size());
340  AddEntry(entry);
341  if (kIsDebugBuild) {
342    UnresolvedMergedType* tmp_entry = down_cast<UnresolvedMergedType*>(entry);
343    std::set<uint16_t> check_types = tmp_entry->GetMergedTypes();
344    CHECK(check_types == types);
345  }
346  return *entry;
347}
348
349RegType& RegTypeCache::FromUnresolvedSuperClass(RegType& child) {
350  // Check if entry already exists.
351  for (size_t i = primitive_count_; i < entries_.size(); i++) {
352    RegType* cur_entry = entries_[i];
353    if (cur_entry->IsUnresolvedSuperClass()) {
354      UnresolvedSuperClass* tmp_entry =
355          down_cast<UnresolvedSuperClass*>(cur_entry);
356      uint16_t unresolved_super_child_id =
357          tmp_entry->GetUnresolvedSuperClassChildId();
358      if (unresolved_super_child_id == child.GetId()) {
359        return *cur_entry;
360      }
361    }
362  }
363  RegType* entry = new UnresolvedSuperClass(child.GetId(), this, entries_.size());
364  AddEntry(entry);
365  return *entry;
366}
367
368UninitializedType& RegTypeCache::Uninitialized(RegType& type, uint32_t allocation_pc) {
369  UninitializedType* entry = NULL;
370  const std::string& descriptor(type.GetDescriptor());
371  if (type.IsUnresolvedTypes()) {
372    for (size_t i = primitive_count_; i < entries_.size(); i++) {
373      RegType* cur_entry = entries_[i];
374      if (cur_entry->IsUnresolvedAndUninitializedReference() &&
375          down_cast<UnresolvedUninitializedRefType*>(cur_entry)->GetAllocationPc() == allocation_pc &&
376          (cur_entry->GetDescriptor() == descriptor)) {
377        return *down_cast<UnresolvedUninitializedRefType*>(cur_entry);
378      }
379    }
380    entry = new UnresolvedUninitializedRefType(descriptor, allocation_pc, entries_.size());
381  } else {
382    mirror::Class* klass = type.GetClass();
383    for (size_t i = primitive_count_; i < entries_.size(); i++) {
384      RegType* cur_entry = entries_[i];
385      if (cur_entry->IsUninitializedReference() &&
386          down_cast<UninitializedReferenceType*>(cur_entry)
387              ->GetAllocationPc() == allocation_pc &&
388          cur_entry->GetClass() == klass) {
389        return *down_cast<UninitializedReferenceType*>(cur_entry);
390      }
391    }
392    entry = new UninitializedReferenceType(klass, descriptor, allocation_pc, entries_.size());
393  }
394  AddEntry(entry);
395  return *entry;
396}
397
398RegType& RegTypeCache::FromUninitialized(RegType& uninit_type) {
399  RegType* entry;
400
401  if (uninit_type.IsUnresolvedTypes()) {
402    const std::string& descriptor(uninit_type.GetDescriptor());
403    for (size_t i = primitive_count_; i < entries_.size(); i++) {
404      RegType* cur_entry = entries_[i];
405      if (cur_entry->IsUnresolvedReference() &&
406          cur_entry->GetDescriptor() == descriptor) {
407        return *cur_entry;
408      }
409    }
410    entry = new UnresolvedReferenceType(descriptor.c_str(), entries_.size());
411  } else {
412    mirror::Class* klass = uninit_type.GetClass();
413    if (uninit_type.IsUninitializedThisReference() && !klass->IsFinal()) {
414      // For uninitialized "this reference" look for reference types that are not precise.
415      for (size_t i = primitive_count_; i < entries_.size(); i++) {
416        RegType* cur_entry = entries_[i];
417        if (cur_entry->IsReference() && cur_entry->GetClass() == klass) {
418          return *cur_entry;
419        }
420      }
421      entry = new ReferenceType(klass, "", entries_.size());
422    } else if (klass->IsInstantiable()) {
423      // We're uninitialized because of allocation, look or create a precise type as allocations
424      // may only create objects of that type.
425      for (size_t i = primitive_count_; i < entries_.size(); i++) {
426        RegType* cur_entry = entries_[i];
427        if (cur_entry->IsPreciseReference() && cur_entry->GetClass() == klass) {
428          return *cur_entry;
429        }
430      }
431      entry = new PreciseReferenceType(klass, uninit_type.GetDescriptor(), entries_.size());
432    } else {
433      return Conflict();
434    }
435  }
436  AddEntry(entry);
437  return *entry;
438}
439
440ImpreciseConstType& RegTypeCache::ByteConstant() {
441  ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::min(), false);
442  DCHECK(result.IsImpreciseConstant());
443  return *down_cast<ImpreciseConstType*>(&result);
444}
445
446ImpreciseConstType& RegTypeCache::CharConstant() {
447  int32_t jchar_max = static_cast<int32_t>(std::numeric_limits<jchar>::max());
448  ConstantType& result =  FromCat1Const(jchar_max, false);
449  DCHECK(result.IsImpreciseConstant());
450  return *down_cast<ImpreciseConstType*>(&result);
451}
452
453ImpreciseConstType& RegTypeCache::ShortConstant() {
454  ConstantType& result =  FromCat1Const(std::numeric_limits<jshort>::min(), false);
455  DCHECK(result.IsImpreciseConstant());
456  return *down_cast<ImpreciseConstType*>(&result);
457}
458
459ImpreciseConstType& RegTypeCache::IntConstant() {
460  ConstantType& result = FromCat1Const(std::numeric_limits<jint>::max(), false);
461  DCHECK(result.IsImpreciseConstant());
462  return *down_cast<ImpreciseConstType*>(&result);
463}
464
465ImpreciseConstType& RegTypeCache::PosByteConstant() {
466  ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::max(), false);
467  DCHECK(result.IsImpreciseConstant());
468  return *down_cast<ImpreciseConstType*>(&result);
469}
470
471ImpreciseConstType& RegTypeCache::PosShortConstant() {
472  ConstantType& result =  FromCat1Const(std::numeric_limits<jshort>::max(), false);
473  DCHECK(result.IsImpreciseConstant());
474  return *down_cast<ImpreciseConstType*>(&result);
475}
476
477UninitializedType& RegTypeCache::UninitializedThisArgument(RegType& type) {
478  UninitializedType* entry;
479  const std::string& descriptor(type.GetDescriptor());
480  if (type.IsUnresolvedTypes()) {
481    for (size_t i = primitive_count_; i < entries_.size(); i++) {
482      RegType* cur_entry = entries_[i];
483      if (cur_entry->IsUnresolvedAndUninitializedThisReference() &&
484          cur_entry->GetDescriptor() == descriptor) {
485        return *down_cast<UninitializedType*>(cur_entry);
486      }
487    }
488    entry = new UnresolvedUninitializedThisRefType(descriptor, entries_.size());
489  } else {
490    mirror::Class* klass = type.GetClass();
491    for (size_t i = primitive_count_; i < entries_.size(); i++) {
492      RegType* cur_entry = entries_[i];
493      if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) {
494        return *down_cast<UninitializedType*>(cur_entry);
495      }
496    }
497    entry = new UninitializedThisReferenceType(klass, descriptor, entries_.size());
498  }
499  AddEntry(entry);
500  return *entry;
501}
502
503ConstantType& RegTypeCache::FromCat1NonSmallConstant(int32_t value, bool precise) {
504  for (size_t i = primitive_count_; i < entries_.size(); i++) {
505    RegType* cur_entry = entries_[i];
506    if (cur_entry->klass_.IsNull() && cur_entry->IsConstant() &&
507        cur_entry->IsPreciseConstant() == precise &&
508        (down_cast<ConstantType*>(cur_entry))->ConstantValue() == value) {
509      return *down_cast<ConstantType*>(cur_entry);
510    }
511  }
512  ConstantType* entry;
513  if (precise) {
514    entry = new PreciseConstType(value, entries_.size());
515  } else {
516    entry = new ImpreciseConstType(value, entries_.size());
517  }
518  AddEntry(entry);
519  return *entry;
520}
521
522ConstantType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) {
523  for (size_t i = primitive_count_; i < entries_.size(); i++) {
524    RegType* cur_entry = entries_[i];
525    if (cur_entry->IsConstantLo() && (cur_entry->IsPrecise() == precise) &&
526        (down_cast<ConstantType*>(cur_entry))->ConstantValueLo() == value) {
527      return *down_cast<ConstantType*>(cur_entry);
528    }
529  }
530  ConstantType* entry;
531  if (precise) {
532    entry = new PreciseConstLoType(value, entries_.size());
533  } else {
534    entry = new ImpreciseConstLoType(value, entries_.size());
535  }
536  AddEntry(entry);
537  return *entry;
538}
539
540ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) {
541  for (size_t i = primitive_count_; i < entries_.size(); i++) {
542    RegType* cur_entry = entries_[i];
543    if (cur_entry->IsConstantHi() && (cur_entry->IsPrecise() == precise) &&
544        (down_cast<ConstantType*>(cur_entry))->ConstantValueHi() == value) {
545      return *down_cast<ConstantType*>(cur_entry);
546    }
547  }
548  ConstantType* entry;
549  if (precise) {
550    entry = new PreciseConstHiType(value, entries_.size());
551  } else {
552    entry = new ImpreciseConstHiType(value, entries_.size());
553  }
554  AddEntry(entry);
555  return *entry;
556}
557
558RegType& RegTypeCache::GetComponentType(RegType& array, mirror::ClassLoader* loader) {
559  if (!array.IsArrayTypes()) {
560    return Conflict();
561  } else if (array.IsUnresolvedTypes()) {
562    const std::string& descriptor(array.GetDescriptor());
563    const std::string component(descriptor.substr(1, descriptor.size() - 1));
564    return FromDescriptor(loader, component.c_str(), false);
565  } else {
566    mirror::Class* klass = array.GetClass()->GetComponentType();
567    if (klass->IsErroneous()) {
568      // Arrays may have erroneous component types, use unresolved in that case.
569      // We assume that the primitive classes are not erroneous, so we know it is a
570      // reference type.
571      return FromDescriptor(loader, klass->GetDescriptor().c_str(), false);
572    } else {
573      return FromClass(klass->GetDescriptor().c_str(), klass,
574                       klass->CannotBeAssignedFromOtherTypes());
575    }
576  }
577}
578
579void RegTypeCache::Dump(std::ostream& os) {
580  for (size_t i = 0; i < entries_.size(); i++) {
581    RegType* cur_entry = entries_[i];
582    if (cur_entry != NULL) {
583      os << i << ": " << cur_entry->Dump() << "\n";
584    }
585  }
586}
587
588void RegTypeCache::VisitRoots(RootCallback* callback, void* arg) {
589  for (RegType* entry : entries_) {
590    entry->VisitRoots(callback, arg);
591  }
592}
593
594void RegTypeCache::AddEntry(RegType* new_entry) {
595  entries_.push_back(new_entry);
596}
597
598}  // namespace verifier
599}  // namespace art
600