reg_type_cache.cc revision cb6b0f31ede2275e79e6199ec391147585a37a2a
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 StringPiece& descriptor, bool precise) {
126  RegType* entry = entries_[idx];
127  if (descriptor != entry->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. We use a StringPiece to avoid continual strlen
162  // operations on the descriptor.
163  StringPiece descriptor_sp(descriptor);
164  for (size_t i = primitive_count_; i < entries_.size(); i++) {
165    if (MatchDescriptor(i, descriptor_sp, precise)) {
166      return *(entries_[i]);
167    }
168  }
169  // Class not found in the cache, will create a new type for that.
170  // Try resolving class.
171  mirror::Class* klass = ResolveClass(descriptor, loader);
172  if (klass != NULL) {
173    // Class resolved, first look for the class in the list of entries
174    // Class was not found, must create new type.
175    // To pass the verification, the type should be imprecise,
176    // instantiable or an interface with the precise type set to false.
177    DCHECK(!precise || klass->IsInstantiable());
178    // Create a precise type if:
179    // 1- Class is final and NOT an interface. a precise interface is meaningless !!
180    // 2- Precise Flag passed as true.
181    RegType* entry;
182    // Create an imprecise type if we can't tell for a fact that it is precise.
183    if (klass->CannotBeAssignedFromOtherTypes() || precise) {
184      DCHECK(!(klass->IsAbstract()) || klass->IsArrayClass());
185      DCHECK(!klass->IsInterface());
186      entry = new PreciseReferenceType(klass, descriptor_sp.as_string(), entries_.size());
187    } else {
188      entry = new ReferenceType(klass, descriptor_sp.as_string(), entries_.size());
189    }
190    AddEntry(entry);
191    return *entry;
192  } else {  // Class not resolved.
193    // We tried loading the class and failed, this might get an exception raised
194    // so we want to clear it before we go on.
195    if (can_load_classes_) {
196      DCHECK(Thread::Current()->IsExceptionPending());
197      Thread::Current()->ClearException();
198    } else {
199      DCHECK(!Thread::Current()->IsExceptionPending());
200    }
201    if (IsValidDescriptor(descriptor)) {
202      RegType* entry = new UnresolvedReferenceType(descriptor_sp.as_string(), entries_.size());
203      AddEntry(entry);
204      return *entry;
205    } else {
206      // The descriptor is broken return the unknown type as there's nothing sensible that
207      // could be done at runtime
208      return Conflict();
209    }
210  }
211}
212
213RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* klass, bool precise) {
214  DCHECK(klass != nullptr);
215  if (klass->IsPrimitive()) {
216    // Note: precise isn't used for primitive classes. A char is assignable to an int. All
217    // primitive classes are final.
218    return RegTypeFromPrimitiveType(klass->GetPrimitiveType());
219  } else {
220    // Look for the reference in the list of entries to have.
221    for (size_t i = primitive_count_; i < entries_.size(); i++) {
222      RegType* cur_entry = entries_[i];
223      if (cur_entry->klass_.Read() == klass && MatchingPrecisionForClass(cur_entry, precise)) {
224        return *cur_entry;
225      }
226    }
227    // No reference to the class was found, create new reference.
228    RegType* entry;
229    if (precise) {
230      entry = new PreciseReferenceType(klass, descriptor, entries_.size());
231    } else {
232      entry = new ReferenceType(klass, descriptor, entries_.size());
233    }
234    AddEntry(entry);
235    return *entry;
236  }
237}
238
239RegTypeCache::RegTypeCache(bool can_load_classes) : can_load_classes_(can_load_classes) {
240  if (kIsDebugBuild && can_load_classes) {
241    Thread::Current()->AssertThreadSuspensionIsAllowable();
242  }
243  entries_.reserve(64);
244  FillPrimitiveAndSmallConstantTypes();
245}
246
247RegTypeCache::~RegTypeCache() {
248  CHECK_LE(primitive_count_, entries_.size());
249  // Delete only the non primitive types.
250  if (entries_.size() == kNumPrimitivesAndSmallConstants) {
251    // All entries are from the global pool, nothing to delete.
252    return;
253  }
254  std::vector<RegType*>::iterator non_primitive_begin = entries_.begin();
255  std::advance(non_primitive_begin, kNumPrimitivesAndSmallConstants);
256  STLDeleteContainerPointers(non_primitive_begin, entries_.end());
257}
258
259void RegTypeCache::ShutDown() {
260  if (RegTypeCache::primitive_initialized_) {
261    UndefinedType::Destroy();
262    ConflictType::Destroy();
263    BooleanType::Destroy();
264    ByteType::Destroy();
265    ShortType::Destroy();
266    CharType::Destroy();
267    IntegerType::Destroy();
268    LongLoType::Destroy();
269    LongHiType::Destroy();
270    FloatType::Destroy();
271    DoubleLoType::Destroy();
272    DoubleHiType::Destroy();
273    for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
274      PreciseConstType* type = small_precise_constants_[value - kMinSmallConstant];
275      delete type;
276      small_precise_constants_[value - kMinSmallConstant] = nullptr;
277    }
278    RegTypeCache::primitive_initialized_ = false;
279    RegTypeCache::primitive_count_ = 0;
280  }
281}
282
283template <class Type>
284Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) {
285  mirror::Class* klass = NULL;
286  // Try loading the class from linker.
287  if (!descriptor.empty()) {
288    klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(),
289                                                                       descriptor.c_str());
290  }
291  Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_);
292  RegTypeCache::primitive_count_++;
293  return entry;
294}
295
296void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() {
297  CreatePrimitiveTypeInstance<UndefinedType>("");
298  CreatePrimitiveTypeInstance<ConflictType>("");
299  CreatePrimitiveTypeInstance<BooleanType>("Z");
300  CreatePrimitiveTypeInstance<ByteType>("B");
301  CreatePrimitiveTypeInstance<ShortType>("S");
302  CreatePrimitiveTypeInstance<CharType>("C");
303  CreatePrimitiveTypeInstance<IntegerType>("I");
304  CreatePrimitiveTypeInstance<LongLoType>("J");
305  CreatePrimitiveTypeInstance<LongHiType>("J");
306  CreatePrimitiveTypeInstance<FloatType>("F");
307  CreatePrimitiveTypeInstance<DoubleLoType>("D");
308  CreatePrimitiveTypeInstance<DoubleHiType>("D");
309  for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
310    PreciseConstType* type = new PreciseConstType(value, primitive_count_);
311    small_precise_constants_[value - kMinSmallConstant] = type;
312    primitive_count_++;
313  }
314}
315
316RegType& RegTypeCache::FromUnresolvedMerge(RegType& left, RegType& right) {
317  std::set<uint16_t> types;
318  if (left.IsUnresolvedMergedReference()) {
319    types = (down_cast<UnresolvedMergedType*>(&left))->GetMergedTypes();
320  } else {
321    types.insert(left.GetId());
322  }
323  if (right.IsUnresolvedMergedReference()) {
324    std::set<uint16_t> right_types = (down_cast<UnresolvedMergedType*>(&right))->GetMergedTypes();
325    types.insert(right_types.begin(), right_types.end());
326  } else {
327    types.insert(right.GetId());
328  }
329  // Check if entry already exists.
330  for (size_t i = primitive_count_; i < entries_.size(); i++) {
331    RegType* cur_entry = entries_[i];
332    if (cur_entry->IsUnresolvedMergedReference()) {
333      std::set<uint16_t> cur_entry_types =
334          (down_cast<UnresolvedMergedType*>(cur_entry))->GetMergedTypes();
335      if (cur_entry_types == types) {
336        return *cur_entry;
337      }
338    }
339  }
340  // Create entry.
341  RegType* entry = new UnresolvedMergedType(left.GetId(), right.GetId(), this, entries_.size());
342  AddEntry(entry);
343  if (kIsDebugBuild) {
344    UnresolvedMergedType* tmp_entry = down_cast<UnresolvedMergedType*>(entry);
345    std::set<uint16_t> check_types = tmp_entry->GetMergedTypes();
346    CHECK(check_types == types);
347  }
348  return *entry;
349}
350
351RegType& RegTypeCache::FromUnresolvedSuperClass(RegType& child) {
352  // Check if entry already exists.
353  for (size_t i = primitive_count_; i < entries_.size(); i++) {
354    RegType* cur_entry = entries_[i];
355    if (cur_entry->IsUnresolvedSuperClass()) {
356      UnresolvedSuperClass* tmp_entry =
357          down_cast<UnresolvedSuperClass*>(cur_entry);
358      uint16_t unresolved_super_child_id =
359          tmp_entry->GetUnresolvedSuperClassChildId();
360      if (unresolved_super_child_id == child.GetId()) {
361        return *cur_entry;
362      }
363    }
364  }
365  RegType* entry = new UnresolvedSuperClass(child.GetId(), this, entries_.size());
366  AddEntry(entry);
367  return *entry;
368}
369
370UninitializedType& RegTypeCache::Uninitialized(RegType& type, uint32_t allocation_pc) {
371  UninitializedType* entry = NULL;
372  const std::string& descriptor(type.GetDescriptor());
373  if (type.IsUnresolvedTypes()) {
374    for (size_t i = primitive_count_; i < entries_.size(); i++) {
375      RegType* cur_entry = entries_[i];
376      if (cur_entry->IsUnresolvedAndUninitializedReference() &&
377          down_cast<UnresolvedUninitializedRefType*>(cur_entry)->GetAllocationPc() == allocation_pc &&
378          (cur_entry->GetDescriptor() == descriptor)) {
379        return *down_cast<UnresolvedUninitializedRefType*>(cur_entry);
380      }
381    }
382    entry = new UnresolvedUninitializedRefType(descriptor, allocation_pc, entries_.size());
383  } else {
384    mirror::Class* klass = type.GetClass();
385    for (size_t i = primitive_count_; i < entries_.size(); i++) {
386      RegType* cur_entry = entries_[i];
387      if (cur_entry->IsUninitializedReference() &&
388          down_cast<UninitializedReferenceType*>(cur_entry)
389              ->GetAllocationPc() == allocation_pc &&
390          cur_entry->GetClass() == klass) {
391        return *down_cast<UninitializedReferenceType*>(cur_entry);
392      }
393    }
394    entry = new UninitializedReferenceType(klass, descriptor, allocation_pc, entries_.size());
395  }
396  AddEntry(entry);
397  return *entry;
398}
399
400RegType& RegTypeCache::FromUninitialized(RegType& uninit_type) {
401  RegType* entry;
402
403  if (uninit_type.IsUnresolvedTypes()) {
404    const std::string& descriptor(uninit_type.GetDescriptor());
405    for (size_t i = primitive_count_; i < entries_.size(); i++) {
406      RegType* cur_entry = entries_[i];
407      if (cur_entry->IsUnresolvedReference() &&
408          cur_entry->GetDescriptor() == descriptor) {
409        return *cur_entry;
410      }
411    }
412    entry = new UnresolvedReferenceType(descriptor, entries_.size());
413  } else {
414    mirror::Class* klass = uninit_type.GetClass();
415    if (uninit_type.IsUninitializedThisReference() && !klass->IsFinal()) {
416      // For uninitialized "this reference" look for reference types that are not precise.
417      for (size_t i = primitive_count_; i < entries_.size(); i++) {
418        RegType* cur_entry = entries_[i];
419        if (cur_entry->IsReference() && cur_entry->GetClass() == klass) {
420          return *cur_entry;
421        }
422      }
423      entry = new ReferenceType(klass, "", entries_.size());
424    } else if (klass->IsInstantiable()) {
425      // We're uninitialized because of allocation, look or create a precise type as allocations
426      // may only create objects of that type.
427      for (size_t i = primitive_count_; i < entries_.size(); i++) {
428        RegType* cur_entry = entries_[i];
429        if (cur_entry->IsPreciseReference() && cur_entry->GetClass() == klass) {
430          return *cur_entry;
431        }
432      }
433      entry = new PreciseReferenceType(klass, uninit_type.GetDescriptor(), entries_.size());
434    } else {
435      return Conflict();
436    }
437  }
438  AddEntry(entry);
439  return *entry;
440}
441
442ImpreciseConstType& RegTypeCache::ByteConstant() {
443  ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::min(), false);
444  DCHECK(result.IsImpreciseConstant());
445  return *down_cast<ImpreciseConstType*>(&result);
446}
447
448ImpreciseConstType& RegTypeCache::CharConstant() {
449  int32_t jchar_max = static_cast<int32_t>(std::numeric_limits<jchar>::max());
450  ConstantType& result =  FromCat1Const(jchar_max, false);
451  DCHECK(result.IsImpreciseConstant());
452  return *down_cast<ImpreciseConstType*>(&result);
453}
454
455ImpreciseConstType& RegTypeCache::ShortConstant() {
456  ConstantType& result =  FromCat1Const(std::numeric_limits<jshort>::min(), false);
457  DCHECK(result.IsImpreciseConstant());
458  return *down_cast<ImpreciseConstType*>(&result);
459}
460
461ImpreciseConstType& RegTypeCache::IntConstant() {
462  ConstantType& result = FromCat1Const(std::numeric_limits<jint>::max(), false);
463  DCHECK(result.IsImpreciseConstant());
464  return *down_cast<ImpreciseConstType*>(&result);
465}
466
467ImpreciseConstType& RegTypeCache::PosByteConstant() {
468  ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::max(), false);
469  DCHECK(result.IsImpreciseConstant());
470  return *down_cast<ImpreciseConstType*>(&result);
471}
472
473ImpreciseConstType& RegTypeCache::PosShortConstant() {
474  ConstantType& result =  FromCat1Const(std::numeric_limits<jshort>::max(), false);
475  DCHECK(result.IsImpreciseConstant());
476  return *down_cast<ImpreciseConstType*>(&result);
477}
478
479UninitializedType& RegTypeCache::UninitializedThisArgument(RegType& type) {
480  UninitializedType* entry;
481  const std::string& descriptor(type.GetDescriptor());
482  if (type.IsUnresolvedTypes()) {
483    for (size_t i = primitive_count_; i < entries_.size(); i++) {
484      RegType* cur_entry = entries_[i];
485      if (cur_entry->IsUnresolvedAndUninitializedThisReference() &&
486          cur_entry->GetDescriptor() == descriptor) {
487        return *down_cast<UninitializedType*>(cur_entry);
488      }
489    }
490    entry = new UnresolvedUninitializedThisRefType(descriptor, entries_.size());
491  } else {
492    mirror::Class* klass = type.GetClass();
493    for (size_t i = primitive_count_; i < entries_.size(); i++) {
494      RegType* cur_entry = entries_[i];
495      if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) {
496        return *down_cast<UninitializedType*>(cur_entry);
497      }
498    }
499    entry = new UninitializedThisReferenceType(klass, descriptor, entries_.size());
500  }
501  AddEntry(entry);
502  return *entry;
503}
504
505ConstantType& RegTypeCache::FromCat1NonSmallConstant(int32_t value, bool precise) {
506  for (size_t i = primitive_count_; i < entries_.size(); i++) {
507    RegType* cur_entry = entries_[i];
508    if (cur_entry->klass_.IsNull() && cur_entry->IsConstant() &&
509        cur_entry->IsPreciseConstant() == precise &&
510        (down_cast<ConstantType*>(cur_entry))->ConstantValue() == value) {
511      return *down_cast<ConstantType*>(cur_entry);
512    }
513  }
514  ConstantType* entry;
515  if (precise) {
516    entry = new PreciseConstType(value, entries_.size());
517  } else {
518    entry = new ImpreciseConstType(value, entries_.size());
519  }
520  AddEntry(entry);
521  return *entry;
522}
523
524ConstantType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) {
525  for (size_t i = primitive_count_; i < entries_.size(); i++) {
526    RegType* cur_entry = entries_[i];
527    if (cur_entry->IsConstantLo() && (cur_entry->IsPrecise() == precise) &&
528        (down_cast<ConstantType*>(cur_entry))->ConstantValueLo() == value) {
529      return *down_cast<ConstantType*>(cur_entry);
530    }
531  }
532  ConstantType* entry;
533  if (precise) {
534    entry = new PreciseConstLoType(value, entries_.size());
535  } else {
536    entry = new ImpreciseConstLoType(value, entries_.size());
537  }
538  AddEntry(entry);
539  return *entry;
540}
541
542ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) {
543  for (size_t i = primitive_count_; i < entries_.size(); i++) {
544    RegType* cur_entry = entries_[i];
545    if (cur_entry->IsConstantHi() && (cur_entry->IsPrecise() == precise) &&
546        (down_cast<ConstantType*>(cur_entry))->ConstantValueHi() == value) {
547      return *down_cast<ConstantType*>(cur_entry);
548    }
549  }
550  ConstantType* entry;
551  if (precise) {
552    entry = new PreciseConstHiType(value, entries_.size());
553  } else {
554    entry = new ImpreciseConstHiType(value, entries_.size());
555  }
556  AddEntry(entry);
557  return *entry;
558}
559
560RegType& RegTypeCache::GetComponentType(RegType& array, mirror::ClassLoader* loader) {
561  if (!array.IsArrayTypes()) {
562    return Conflict();
563  } else if (array.IsUnresolvedTypes()) {
564    const std::string& descriptor(array.GetDescriptor());
565    const std::string component(descriptor.substr(1, descriptor.size() - 1));
566    return FromDescriptor(loader, component.c_str(), false);
567  } else {
568    mirror::Class* klass = array.GetClass()->GetComponentType();
569    std::string temp;
570    if (klass->IsErroneous()) {
571      // Arrays may have erroneous component types, use unresolved in that case.
572      // We assume that the primitive classes are not erroneous, so we know it is a
573      // reference type.
574      return FromDescriptor(loader, klass->GetDescriptor(&temp), false);
575    } else {
576      return FromClass(klass->GetDescriptor(&temp), klass,
577                       klass->CannotBeAssignedFromOtherTypes());
578    }
579  }
580}
581
582void RegTypeCache::Dump(std::ostream& os) {
583  for (size_t i = 0; i < entries_.size(); i++) {
584    RegType* cur_entry = entries_[i];
585    if (cur_entry != NULL) {
586      os << i << ": " << cur_entry->Dump() << "\n";
587    }
588  }
589}
590
591void RegTypeCache::VisitRoots(RootCallback* callback, void* arg) {
592  for (RegType* entry : entries_) {
593    entry->VisitRoots(callback, arg);
594  }
595}
596
597void RegTypeCache::AddEntry(RegType* new_entry) {
598  entries_.push_back(new_entry);
599}
600
601}  // namespace verifier
602}  // namespace art
603