1 /*
2 * Copyright (C) 2011 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 #ifndef ART_RUNTIME_CLASS_LINKER_INL_H_
18 #define ART_RUNTIME_CLASS_LINKER_INL_H_
19
20 #include <atomic>
21
22 #include "android-base/thread_annotations.h"
23 #include "art_field-inl.h"
24 #include "art_method-inl.h"
25 #include "base/mutex.h"
26 #include "class_linker.h"
27 #include "dex/dex_file.h"
28 #include "dex/dex_file_structs.h"
29 #include "gc_root-inl.h"
30 #include "handle_scope-inl.h"
31 #include "jni/jni_internal.h"
32 #include "mirror/class_loader.h"
33 #include "mirror/dex_cache-inl.h"
34 #include "mirror/iftable.h"
35 #include "mirror/object_array-inl.h"
36 #include "obj_ptr-inl.h"
37 #include "scoped_thread_state_change-inl.h"
38 #include "well_known_classes.h"
39
40 namespace art {
41
FindArrayClass(Thread * self,ObjPtr<mirror::Class> element_class)42 inline ObjPtr<mirror::Class> ClassLinker::FindArrayClass(Thread* self,
43 ObjPtr<mirror::Class> element_class) {
44 for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
45 // Read the cached array class once to avoid races with other threads setting it.
46 ObjPtr<mirror::Class> array_class = find_array_class_cache_[i].Read();
47 if (array_class != nullptr && array_class->GetComponentType() == element_class) {
48 return array_class;
49 }
50 }
51 std::string descriptor = "[";
52 std::string temp;
53 descriptor += element_class->GetDescriptor(&temp);
54 StackHandleScope<1> hs(Thread::Current());
55 Handle<mirror::ClassLoader> class_loader(hs.NewHandle(element_class->GetClassLoader()));
56 ObjPtr<mirror::Class> array_class = FindClass(self, descriptor.c_str(), class_loader);
57 if (array_class != nullptr) {
58 // Benign races in storing array class and incrementing index.
59 size_t victim_index = find_array_class_cache_next_victim_;
60 find_array_class_cache_[victim_index] = GcRoot<mirror::Class>(array_class);
61 find_array_class_cache_next_victim_ = (victim_index + 1) % kFindArrayCacheSize;
62 } else {
63 // We should have a NoClassDefFoundError.
64 self->AssertPendingException();
65 }
66 return array_class;
67 }
68
ResolveString(dex::StringIndex string_idx,ArtField * referrer)69 inline ObjPtr<mirror::String> ClassLinker::ResolveString(dex::StringIndex string_idx,
70 ArtField* referrer) {
71 Thread::PoisonObjectPointersIfDebug();
72 DCHECK(!Thread::Current()->IsExceptionPending());
73 // We do not need the read barrier for getting the DexCache for the initial resolved type
74 // lookup as both from-space and to-space copies point to the same native resolved types array.
75 ObjPtr<mirror::String> resolved =
76 referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedString(string_idx);
77 if (resolved == nullptr) {
78 resolved = DoResolveString(string_idx, referrer->GetDexCache());
79 }
80 return resolved;
81 }
82
ResolveString(dex::StringIndex string_idx,ArtMethod * referrer)83 inline ObjPtr<mirror::String> ClassLinker::ResolveString(dex::StringIndex string_idx,
84 ArtMethod* referrer) {
85 Thread::PoisonObjectPointersIfDebug();
86 DCHECK(!Thread::Current()->IsExceptionPending());
87 // We do not need the read barrier for getting the DexCache for the initial resolved type
88 // lookup as both from-space and to-space copies point to the same native resolved types array.
89 ObjPtr<mirror::String> resolved =
90 referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedString(string_idx);
91 if (resolved == nullptr) {
92 resolved = DoResolveString(string_idx, referrer->GetDexCache());
93 }
94 return resolved;
95 }
96
ResolveString(dex::StringIndex string_idx,Handle<mirror::DexCache> dex_cache)97 inline ObjPtr<mirror::String> ClassLinker::ResolveString(dex::StringIndex string_idx,
98 Handle<mirror::DexCache> dex_cache) {
99 Thread::PoisonObjectPointersIfDebug();
100 DCHECK(!Thread::Current()->IsExceptionPending());
101 ObjPtr<mirror::String> resolved = dex_cache->GetResolvedString(string_idx);
102 if (resolved == nullptr) {
103 resolved = DoResolveString(string_idx, dex_cache);
104 }
105 return resolved;
106 }
107
LookupString(dex::StringIndex string_idx,ObjPtr<mirror::DexCache> dex_cache)108 inline ObjPtr<mirror::String> ClassLinker::LookupString(dex::StringIndex string_idx,
109 ObjPtr<mirror::DexCache> dex_cache) {
110 ObjPtr<mirror::String> resolved = dex_cache->GetResolvedString(string_idx);
111 if (resolved == nullptr) {
112 resolved = DoLookupString(string_idx, dex_cache);
113 }
114 return resolved;
115 }
116
ResolveType(dex::TypeIndex type_idx,ObjPtr<mirror::Class> referrer)117 inline ObjPtr<mirror::Class> ClassLinker::ResolveType(dex::TypeIndex type_idx,
118 ObjPtr<mirror::Class> referrer) {
119 if (kObjPtrPoisoning) {
120 StackHandleScope<1> hs(Thread::Current());
121 HandleWrapperObjPtr<mirror::Class> referrer_wrapper = hs.NewHandleWrapper(&referrer);
122 Thread::Current()->PoisonObjectPointers();
123 }
124 DCHECK(!Thread::Current()->IsExceptionPending());
125 // We do not need the read barrier for getting the DexCache for the initial resolved type
126 // lookup as both from-space and to-space copies point to the same native resolved types array.
127 ObjPtr<mirror::Class> resolved_type =
128 referrer->GetDexCache<kDefaultVerifyFlags, kWithoutReadBarrier>()->GetResolvedType(type_idx);
129 if (resolved_type == nullptr) {
130 resolved_type = DoResolveType(type_idx, referrer);
131 }
132 return resolved_type;
133 }
134
ResolveType(dex::TypeIndex type_idx,ArtField * referrer)135 inline ObjPtr<mirror::Class> ClassLinker::ResolveType(dex::TypeIndex type_idx,
136 ArtField* referrer) {
137 Thread::PoisonObjectPointersIfDebug();
138 DCHECK(!Thread::Current()->IsExceptionPending());
139 // We do not need the read barrier for getting the DexCache for the initial resolved type
140 // lookup as both from-space and to-space copies point to the same native resolved types array.
141 ObjPtr<mirror::Class> resolved_type =
142 referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx);
143 if (UNLIKELY(resolved_type == nullptr)) {
144 resolved_type = DoResolveType(type_idx, referrer);
145 }
146 return resolved_type;
147 }
148
ResolveType(dex::TypeIndex type_idx,ArtMethod * referrer)149 inline ObjPtr<mirror::Class> ClassLinker::ResolveType(dex::TypeIndex type_idx,
150 ArtMethod* referrer) {
151 Thread::PoisonObjectPointersIfDebug();
152 DCHECK(!Thread::Current()->IsExceptionPending());
153 // We do not need the read barrier for getting the DexCache for the initial resolved type
154 // lookup as both from-space and to-space copies point to the same native resolved types array.
155 ObjPtr<mirror::Class> resolved_type =
156 referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx);
157 if (UNLIKELY(resolved_type == nullptr)) {
158 resolved_type = DoResolveType(type_idx, referrer);
159 }
160 return resolved_type;
161 }
162
ResolveType(dex::TypeIndex type_idx,Handle<mirror::DexCache> dex_cache,Handle<mirror::ClassLoader> class_loader)163 inline ObjPtr<mirror::Class> ClassLinker::ResolveType(dex::TypeIndex type_idx,
164 Handle<mirror::DexCache> dex_cache,
165 Handle<mirror::ClassLoader> class_loader) {
166 DCHECK(dex_cache != nullptr);
167 Thread::PoisonObjectPointersIfDebug();
168 ObjPtr<mirror::Class> resolved = dex_cache->GetResolvedType(type_idx);
169 if (resolved == nullptr) {
170 resolved = DoResolveType(type_idx, dex_cache, class_loader);
171 }
172 return resolved;
173 }
174
LookupResolvedType(dex::TypeIndex type_idx,ObjPtr<mirror::Class> referrer)175 inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(dex::TypeIndex type_idx,
176 ObjPtr<mirror::Class> referrer) {
177 // We do not need the read barrier for getting the DexCache for the initial resolved type
178 // lookup as both from-space and to-space copies point to the same native resolved types array.
179 ObjPtr<mirror::Class> type =
180 referrer->GetDexCache<kDefaultVerifyFlags, kWithoutReadBarrier>()->GetResolvedType(type_idx);
181 if (type == nullptr) {
182 type = DoLookupResolvedType(type_idx, referrer);
183 }
184 return type;
185 }
186
LookupResolvedType(dex::TypeIndex type_idx,ArtField * referrer)187 inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(dex::TypeIndex type_idx,
188 ArtField* referrer) {
189 // We do not need the read barrier for getting the DexCache for the initial resolved type
190 // lookup as both from-space and to-space copies point to the same native resolved types array.
191 ObjPtr<mirror::Class> type =
192 referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx);
193 if (type == nullptr) {
194 type = DoLookupResolvedType(type_idx, referrer->GetDeclaringClass());
195 }
196 return type;
197 }
198
LookupResolvedType(dex::TypeIndex type_idx,ArtMethod * referrer)199 inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(dex::TypeIndex type_idx,
200 ArtMethod* referrer) {
201 // We do not need the read barrier for getting the DexCache for the initial resolved type
202 // lookup as both from-space and to-space copies point to the same native resolved types array.
203 ObjPtr<mirror::Class> type =
204 referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx);
205 if (type == nullptr) {
206 type = DoLookupResolvedType(type_idx, referrer->GetDeclaringClass());
207 }
208 return type;
209 }
210
LookupResolvedType(dex::TypeIndex type_idx,ObjPtr<mirror::DexCache> dex_cache,ObjPtr<mirror::ClassLoader> class_loader)211 inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(
212 dex::TypeIndex type_idx,
213 ObjPtr<mirror::DexCache> dex_cache,
214 ObjPtr<mirror::ClassLoader> class_loader) {
215 ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
216 if (type == nullptr) {
217 type = DoLookupResolvedType(type_idx, dex_cache, class_loader);
218 }
219 return type;
220 }
221
222 template <bool kThrowOnError, typename ClassGetter>
CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_cache,InvokeType type,ClassGetter class_getter)223 inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_cache,
224 InvokeType type,
225 ClassGetter class_getter) {
226 switch (type) {
227 case kStatic:
228 case kSuper:
229 case kPolymorphic:
230 break;
231 case kInterface: {
232 // We have to check whether the method id really belongs to an interface (dex static bytecode
233 // constraints A15, A16). Otherwise you must not invoke-interface on it.
234 ObjPtr<mirror::Class> klass = class_getter();
235 if (UNLIKELY(!klass->IsInterface())) {
236 if (kThrowOnError) {
237 ThrowIncompatibleClassChangeError(klass,
238 "Found class %s, but interface was expected",
239 klass->PrettyDescriptor().c_str());
240 }
241 return true;
242 }
243 break;
244 }
245 case kDirect:
246 if (dex_cache->GetDexFile()->SupportsDefaultMethods()) {
247 break;
248 }
249 FALLTHROUGH_INTENDED;
250 case kVirtual: {
251 // Similarly, invoke-virtual (and invoke-direct without default methods) must reference
252 // a non-interface class (dex static bytecode constraint A24, A25).
253 ObjPtr<mirror::Class> klass = class_getter();
254 if (UNLIKELY(klass->IsInterface())) {
255 if (kThrowOnError) {
256 ThrowIncompatibleClassChangeError(klass,
257 "Found interface %s, but class was expected",
258 klass->PrettyDescriptor().c_str());
259 }
260 return true;
261 }
262 break;
263 }
264 default:
265 LOG(FATAL) << "Unreachable - invocation type: " << type;
266 UNREACHABLE();
267 }
268 return false;
269 }
270
271 template <bool kThrow>
CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_cache,InvokeType type,uint32_t method_idx,ObjPtr<mirror::ClassLoader> class_loader)272 inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_cache,
273 InvokeType type,
274 uint32_t method_idx,
275 ObjPtr<mirror::ClassLoader> class_loader) {
276 return CheckInvokeClassMismatch<kThrow>(
277 dex_cache,
278 type,
279 [this, dex_cache, method_idx, class_loader]() REQUIRES_SHARED(Locks::mutator_lock_) {
280 const dex::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(method_idx);
281 ObjPtr<mirror::Class> klass =
282 LookupResolvedType(method_id.class_idx_, dex_cache, class_loader);
283 DCHECK(klass != nullptr) << dex_cache->GetDexFile()->PrettyMethod(method_idx);
284 return klass;
285 });
286 }
287
LookupResolvedMethod(uint32_t method_idx,ObjPtr<mirror::DexCache> dex_cache,ObjPtr<mirror::ClassLoader> class_loader)288 inline ArtMethod* ClassLinker::LookupResolvedMethod(uint32_t method_idx,
289 ObjPtr<mirror::DexCache> dex_cache,
290 ObjPtr<mirror::ClassLoader> class_loader) {
291 ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx);
292 if (resolved == nullptr) {
293 const DexFile& dex_file = *dex_cache->GetDexFile();
294 const dex::MethodId& method_id = dex_file.GetMethodId(method_idx);
295 ObjPtr<mirror::Class> klass = LookupResolvedType(method_id.class_idx_, dex_cache, class_loader);
296 if (klass != nullptr) {
297 resolved = FindResolvedMethod(klass, dex_cache, class_loader, method_idx);
298 }
299 }
300 return resolved;
301 }
302
303 template <InvokeType type, ClassLinker::ResolveMode kResolveMode>
GetResolvedMethod(uint32_t method_idx,ArtMethod * referrer)304 inline ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer) {
305 DCHECK(referrer != nullptr);
306 // Note: The referrer can be a Proxy constructor. In that case, we need to do the
307 // lookup in the context of the original method from where it steals the code.
308 // However, we delay the GetInterfaceMethodIfProxy() until needed.
309 DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor());
310 // We do not need the read barrier for getting the DexCache for the initial resolved method
311 // lookup as both from-space and to-space copies point to the same native resolved methods array.
312 ArtMethod* resolved_method = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedMethod(
313 method_idx);
314 if (resolved_method == nullptr) {
315 return nullptr;
316 }
317 DCHECK(!resolved_method->IsRuntimeMethod());
318 if (kResolveMode == ResolveMode::kCheckICCEAndIAE) {
319 referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
320 // Check if the invoke type matches the class type.
321 ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
322 ObjPtr<mirror::ClassLoader> class_loader = referrer->GetClassLoader();
323 const dex::MethodId& method_id = referrer->GetDexFile()->GetMethodId(method_idx);
324 ObjPtr<mirror::Class> cls = LookupResolvedType(method_id.class_idx_, dex_cache, class_loader);
325 if (cls == nullptr) {
326 // The verifier breaks the invariant that a resolved method must have its
327 // class in the class table. Because this method should only lookup and not
328 // resolve class, return null. The caller is responsible for calling
329 // `ResolveMethod` afterwards.
330 // b/73760543
331 return nullptr;
332 }
333 if (CheckInvokeClassMismatch</* kThrow= */ false>(dex_cache, type, method_idx, class_loader)) {
334 return nullptr;
335 }
336 // Check access.
337 ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
338 if (!referring_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(),
339 resolved_method,
340 dex_cache,
341 method_idx)) {
342 return nullptr;
343 }
344 // Check if the invoke type matches the method type.
345 if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
346 return nullptr;
347 }
348 }
349 return resolved_method;
350 }
351
352 template <ClassLinker::ResolveMode kResolveMode>
ResolveMethod(Thread * self,uint32_t method_idx,ArtMethod * referrer,InvokeType type)353 inline ArtMethod* ClassLinker::ResolveMethod(Thread* self,
354 uint32_t method_idx,
355 ArtMethod* referrer,
356 InvokeType type) {
357 DCHECK(referrer != nullptr);
358 // Note: The referrer can be a Proxy constructor. In that case, we need to do the
359 // lookup in the context of the original method from where it steals the code.
360 // However, we delay the GetInterfaceMethodIfProxy() until needed.
361 DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor());
362 Thread::PoisonObjectPointersIfDebug();
363 // We do not need the read barrier for getting the DexCache for the initial resolved method
364 // lookup as both from-space and to-space copies point to the same native resolved methods array.
365 ArtMethod* resolved_method = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedMethod(
366 method_idx);
367 DCHECK(resolved_method == nullptr || !resolved_method->IsRuntimeMethod());
368 if (UNLIKELY(resolved_method == nullptr)) {
369 referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
370 ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
371 StackHandleScope<2> hs(self);
372 Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(referrer->GetDexCache()));
373 Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
374 resolved_method = ResolveMethod<kResolveMode>(method_idx,
375 h_dex_cache,
376 h_class_loader,
377 referrer,
378 type);
379 } else if (kResolveMode == ResolveMode::kCheckICCEAndIAE) {
380 referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
381 const dex::MethodId& method_id = referrer->GetDexFile()->GetMethodId(method_idx);
382 ObjPtr<mirror::Class> cls =
383 LookupResolvedType(method_id.class_idx_,
384 referrer->GetDexCache(),
385 referrer->GetClassLoader());
386 if (cls == nullptr) {
387 // The verifier breaks the invariant that a resolved method must have its
388 // class in the class table, so resolve the type in case we haven't found it.
389 // b/73760543
390 StackHandleScope<2> hs(Thread::Current());
391 Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(referrer->GetDexCache()));
392 Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(referrer->GetClassLoader()));
393 cls = ResolveType(method_id.class_idx_, h_dex_cache, h_class_loader);
394 if (hs.Self()->IsExceptionPending()) {
395 return nullptr;
396 }
397 }
398 // Check if the invoke type matches the class type.
399 if (CheckInvokeClassMismatch</* kThrow= */ true>(
400 referrer->GetDexCache(), type, [cls]() { return cls; })) {
401 DCHECK(Thread::Current()->IsExceptionPending());
402 return nullptr;
403 }
404 // Check access.
405 ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
406 if (!referring_class->CheckResolvedMethodAccess(resolved_method->GetDeclaringClass(),
407 resolved_method,
408 referrer->GetDexCache(),
409 method_idx,
410 type)) {
411 DCHECK(Thread::Current()->IsExceptionPending());
412 return nullptr;
413 }
414 // Check if the invoke type matches the method type.
415 if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
416 ThrowIncompatibleClassChangeError(type,
417 resolved_method->GetInvokeType(),
418 resolved_method,
419 referrer);
420 return nullptr;
421 }
422 }
423 // Note: We cannot check here to see whether we added the method to the cache. It
424 // might be an erroneous class, which results in it being hidden from us.
425 return resolved_method;
426 }
427
LookupResolvedField(uint32_t field_idx,ArtMethod * referrer,bool is_static)428 inline ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx,
429 ArtMethod* referrer,
430 bool is_static) {
431 // We do not need the read barrier for getting the DexCache for the initial resolved field
432 // lookup as both from-space and to-space copies point to the same native resolved fields array.
433 ArtField* field = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedField(
434 field_idx);
435 if (field == nullptr) {
436 ObjPtr<mirror::ClassLoader> class_loader = referrer->GetDeclaringClass()->GetClassLoader();
437 field = LookupResolvedField(field_idx, referrer->GetDexCache(), class_loader, is_static);
438 }
439 return field;
440 }
441
ResolveField(uint32_t field_idx,ArtMethod * referrer,bool is_static)442 inline ArtField* ClassLinker::ResolveField(uint32_t field_idx,
443 ArtMethod* referrer,
444 bool is_static) {
445 Thread::PoisonObjectPointersIfDebug();
446 // We do not need the read barrier for getting the DexCache for the initial resolved field
447 // lookup as both from-space and to-space copies point to the same native resolved fields array.
448 ArtField* resolved_field = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedField(
449 field_idx);
450 if (UNLIKELY(resolved_field == nullptr)) {
451 StackHandleScope<2> hs(Thread::Current());
452 ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
453 Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
454 Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referring_class->GetClassLoader()));
455 resolved_field = ResolveField(field_idx, dex_cache, class_loader, is_static);
456 // Note: We cannot check here to see whether we added the field to the cache. The type
457 // might be an erroneous class, which results in it being hidden from us.
458 }
459 return resolved_field;
460 }
461
462 template <class Visitor>
VisitClassTables(const Visitor & visitor)463 inline void ClassLinker::VisitClassTables(const Visitor& visitor) {
464 Thread* const self = Thread::Current();
465 WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
466 for (const ClassLoaderData& data : class_loaders_) {
467 if (data.class_table != nullptr) {
468 visitor(data.class_table);
469 }
470 }
471 }
472
473 template <ReadBarrierOption kReadBarrierOption>
GetClassRoots()474 inline ObjPtr<mirror::ObjectArray<mirror::Class>> ClassLinker::GetClassRoots() {
475 ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots =
476 class_roots_.Read<kReadBarrierOption>();
477 DCHECK(class_roots != nullptr);
478 return class_roots;
479 }
480
481 template <typename Visitor>
VisitKnownDexFiles(Thread * self,Visitor visitor)482 void ClassLinker::VisitKnownDexFiles(Thread* self, Visitor visitor) {
483 ReaderMutexLock rmu(self, *Locks::dex_lock_);
484 std::for_each(dex_caches_.begin(),
485 dex_caches_.end(),
486 [&](DexCacheData& dcd) REQUIRES(Locks::mutator_lock_) {
487 if (dcd.IsValid()) {
488 visitor(dcd.dex_file);
489 }
490 });
491 }
492
493 } // namespace art
494
495 #endif // ART_RUNTIME_CLASS_LINKER_INL_H_
496