1 /*
2 * Copyright (C) 2017 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 "aot_class_linker.h"
18
19 #include "base/stl_util.h"
20 #include "class_status.h"
21 #include "compiler_callbacks.h"
22 #include "dex/class_reference.h"
23 #include "gc/heap.h"
24 #include "handle_scope-inl.h"
25 #include "interpreter/interpreter_switch_impl.h"
26 #include "mirror/class-inl.h"
27 #include "runtime.h"
28 #include "scoped_thread_state_change-inl.h"
29 #include "transaction.h"
30 #include "verifier/verifier_enums.h"
31
32 namespace art HIDDEN {
33
AotClassLinker(InternTable * intern_table)34 AotClassLinker::AotClassLinker(InternTable* intern_table)
35 : ClassLinker(intern_table, /*fast_class_not_found_exceptions=*/ false),
36 preinitialization_transactions_() {}
37
~AotClassLinker()38 AotClassLinker::~AotClassLinker() {}
39
CanAllocClass()40 bool AotClassLinker::CanAllocClass() {
41 // AllocClass doesn't work under transaction, so we abort.
42 if (IsActiveTransaction()) {
43 AbortTransactionF(Thread::Current(), "Can't resolve type within transaction.");
44 return false;
45 }
46 return ClassLinker::CanAllocClass();
47 }
48
49 // Wrap the original InitializeClass with creation of transaction when in strict mode.
InitializeClass(Thread * self,Handle<mirror::Class> klass,bool can_init_statics,bool can_init_parents)50 bool AotClassLinker::InitializeClass(Thread* self,
51 Handle<mirror::Class> klass,
52 bool can_init_statics,
53 bool can_init_parents) {
54 bool strict_mode = IsActiveStrictTransactionMode();
55
56 DCHECK(klass != nullptr);
57 if (klass->IsInitialized() || klass->IsInitializing()) {
58 return ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
59 }
60
61 // When compiling a boot image extension, do not initialize a class defined
62 // in a dex file belonging to the boot image we're compiling against.
63 // However, we must allow the initialization of TransactionAbortError,
64 // VerifyError, etc. outside of a transaction.
65 if (!strict_mode &&
66 Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass->GetDexCache())) {
67 if (IsActiveTransaction()) {
68 AbortTransactionF(self,
69 "Can't initialize %s because it is defined in a boot image dex file.",
70 klass->PrettyTypeOf().c_str());
71 return false;
72 }
73 CHECK(klass->IsThrowableClass()) << klass->PrettyDescriptor();
74 }
75
76 // When in strict_mode, don't initialize a class if it belongs to boot but not initialized.
77 if (strict_mode && klass->IsBootStrapClassLoaded()) {
78 AbortTransactionF(self,
79 "Can't resolve %s because it is an uninitialized boot class.",
80 klass->PrettyTypeOf().c_str());
81 return false;
82 }
83
84 // Don't initialize klass if it's superclass is not initialized, because superclass might abort
85 // the transaction and rolled back after klass's change is commited.
86 if (strict_mode && !klass->IsInterface() && klass->HasSuperClass()) {
87 if (klass->GetSuperClass()->GetStatus() == ClassStatus::kInitializing) {
88 AbortTransactionF(self,
89 "Can't resolve %s because it's superclass is not initialized.",
90 klass->PrettyTypeOf().c_str());
91 return false;
92 }
93 }
94
95 if (strict_mode) {
96 EnterTransactionMode(/*strict=*/ true, klass.Get());
97 }
98 bool success = ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
99
100 if (strict_mode) {
101 if (success) {
102 // Exit Transaction if success.
103 ExitTransactionMode();
104 } else {
105 // If not successfully initialized, don't rollback immediately, leave the cleanup to compiler
106 // driver which needs abort message and exception.
107 DCHECK(self->IsExceptionPending());
108 }
109 }
110 return success;
111 }
112
PerformClassVerification(Thread * self,verifier::VerifierDeps * verifier_deps,Handle<mirror::Class> klass,verifier::HardFailLogMode log_level,std::string * error_msg)113 verifier::FailureKind AotClassLinker::PerformClassVerification(
114 Thread* self,
115 verifier::VerifierDeps* verifier_deps,
116 Handle<mirror::Class> klass,
117 verifier::HardFailLogMode log_level,
118 std::string* error_msg) {
119 Runtime* const runtime = Runtime::Current();
120 CompilerCallbacks* callbacks = runtime->GetCompilerCallbacks();
121 ClassStatus old_status = callbacks->GetPreviousClassState(
122 ClassReference(&klass->GetDexFile(), klass->GetDexClassDefIndex()));
123 // Was it verified? Report no failure.
124 if (old_status >= ClassStatus::kVerified) {
125 return verifier::FailureKind::kNoFailure;
126 }
127 if (old_status >= ClassStatus::kVerifiedNeedsAccessChecks) {
128 return verifier::FailureKind::kAccessChecksFailure;
129 }
130 // Does it need to be verified at runtime? Report soft failure.
131 if (old_status >= ClassStatus::kRetryVerificationAtRuntime) {
132 // Error messages from here are only reported through -verbose:class. It is not worth it to
133 // create a message.
134 return verifier::FailureKind::kSoftFailure;
135 }
136 // Do the actual work.
137 return ClassLinker::PerformClassVerification(self, verifier_deps, klass, log_level, error_msg);
138 }
139
140 static const std::vector<const DexFile*>* gAppImageDexFiles = nullptr;
141
SetAppImageDexFiles(const std::vector<const DexFile * > * app_image_dex_files)142 void AotClassLinker::SetAppImageDexFiles(const std::vector<const DexFile*>* app_image_dex_files) {
143 gAppImageDexFiles = app_image_dex_files;
144 }
145
CanReferenceInBootImageExtensionOrAppImage(ObjPtr<mirror::Class> klass,gc::Heap * heap)146 bool AotClassLinker::CanReferenceInBootImageExtensionOrAppImage(
147 ObjPtr<mirror::Class> klass, gc::Heap* heap) {
148 // Do not allow referencing a class or instance of a class defined in a dex file
149 // belonging to the boot image we're compiling against but not itself in the boot image;
150 // or a class referencing such classes as component type, superclass or interface.
151 // Allowing this could yield duplicate class objects from multiple images.
152
153 if (heap->ObjectIsInBootImageSpace(klass)) {
154 return true; // Already included in the boot image we're compiling against.
155 }
156
157 // Treat arrays and primitive types specially because they do not have a DexCache that we
158 // can use to check whether the dex file belongs to the boot image we're compiling against.
159 DCHECK(!klass->IsPrimitive()); // Primitive classes must be in the primary boot image.
160 if (klass->IsArrayClass()) {
161 DCHECK(heap->ObjectIsInBootImageSpace(klass->GetIfTable())); // IfTable is OK.
162 // Arrays of all dimensions are tied to the dex file of the non-array component type.
163 do {
164 klass = klass->GetComponentType();
165 } while (klass->IsArrayClass());
166 if (klass->IsPrimitive()) {
167 return false;
168 }
169 // Do not allow arrays of erroneous classes (the array class is not itself erroneous).
170 if (klass->IsErroneous()) {
171 return false;
172 }
173 }
174
175 auto can_reference_dex_cache = [&](ObjPtr<mirror::DexCache> dex_cache)
176 REQUIRES_SHARED(Locks::mutator_lock_) {
177 // We cannot reference a boot image dex cache for classes
178 // that were not themselves in the boot image.
179 if (heap->ObjectIsInBootImageSpace(dex_cache)) {
180 return false;
181 }
182 // App image compilation can pull in dex files from parent or library class loaders.
183 // Classes from such dex files cannot be included or referenced in the current app image
184 // to avoid conflicts with classes in the parent or library class loader's app image.
185 if (gAppImageDexFiles != nullptr &&
186 !ContainsElement(*gAppImageDexFiles, dex_cache->GetDexFile())) {
187 return false;
188 }
189 return true;
190 };
191
192 // Check the class itself.
193 if (!can_reference_dex_cache(klass->GetDexCache())) {
194 return false;
195 }
196
197 // Check superclasses.
198 ObjPtr<mirror::Class> superclass = klass->GetSuperClass();
199 while (!heap->ObjectIsInBootImageSpace(superclass)) {
200 DCHECK(superclass != nullptr); // Cannot skip Object which is in the primary boot image.
201 if (!can_reference_dex_cache(superclass->GetDexCache())) {
202 return false;
203 }
204 superclass = superclass->GetSuperClass();
205 }
206
207 // Check IfTable. This includes direct and indirect interfaces.
208 ObjPtr<mirror::IfTable> if_table = klass->GetIfTable();
209 for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) {
210 ObjPtr<mirror::Class> interface = if_table->GetInterface(i);
211 DCHECK(interface != nullptr);
212 if (!heap->ObjectIsInBootImageSpace(interface) &&
213 !can_reference_dex_cache(interface->GetDexCache())) {
214 return false;
215 }
216 }
217
218 if (kIsDebugBuild) {
219 // All virtual methods must come from classes we have already checked above.
220 PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
221 ObjPtr<mirror::Class> k = klass;
222 while (!heap->ObjectIsInBootImageSpace(k)) {
223 for (auto& m : k->GetVirtualMethods(pointer_size)) {
224 ObjPtr<mirror::Class> declaring_class = m.GetDeclaringClass();
225 CHECK(heap->ObjectIsInBootImageSpace(declaring_class) ||
226 can_reference_dex_cache(declaring_class->GetDexCache()));
227 }
228 k = k->GetSuperClass();
229 }
230 }
231
232 return true;
233 }
234
SetSdkChecker(std::unique_ptr<SdkChecker> && sdk_checker)235 void AotClassLinker::SetSdkChecker(std::unique_ptr<SdkChecker>&& sdk_checker) {
236 sdk_checker_ = std::move(sdk_checker);
237 }
238
GetSdkChecker() const239 const SdkChecker* AotClassLinker::GetSdkChecker() const {
240 return sdk_checker_.get();
241 }
242
DenyAccessBasedOnPublicSdk(ArtMethod * art_method) const243 bool AotClassLinker::DenyAccessBasedOnPublicSdk(ArtMethod* art_method) const {
244 return sdk_checker_ != nullptr && sdk_checker_->ShouldDenyAccess(art_method);
245 }
DenyAccessBasedOnPublicSdk(ArtField * art_field) const246 bool AotClassLinker::DenyAccessBasedOnPublicSdk(ArtField* art_field) const {
247 return sdk_checker_ != nullptr && sdk_checker_->ShouldDenyAccess(art_field);
248 }
DenyAccessBasedOnPublicSdk(std::string_view type_descriptor) const249 bool AotClassLinker::DenyAccessBasedOnPublicSdk(std::string_view type_descriptor) const {
250 return sdk_checker_ != nullptr && sdk_checker_->ShouldDenyAccess(type_descriptor);
251 }
252
SetEnablePublicSdkChecks(bool enabled)253 void AotClassLinker::SetEnablePublicSdkChecks(bool enabled) {
254 if (sdk_checker_ != nullptr) {
255 sdk_checker_->SetEnabled(enabled);
256 }
257 }
258
259 // Transaction support.
260
IsActiveTransaction() const261 bool AotClassLinker::IsActiveTransaction() const {
262 bool result = Runtime::Current()->IsActiveTransaction();
263 DCHECK_EQ(result, !preinitialization_transactions_.empty() && !GetTransaction()->IsRollingBack());
264 return result;
265 }
266
EnterTransactionMode(bool strict,mirror::Class * root)267 void AotClassLinker::EnterTransactionMode(bool strict, mirror::Class* root) {
268 Runtime* runtime = Runtime::Current();
269 ArenaPool* arena_pool = nullptr;
270 ArenaStack* arena_stack = nullptr;
271 if (preinitialization_transactions_.empty()) { // Top-level transaction?
272 // Make initialized classes visibly initialized now. If that happened during the transaction
273 // and then the transaction was aborted, we would roll back the status update but not the
274 // ClassLinker's bookkeeping structures, so these classes would never be visibly initialized.
275 {
276 Thread* self = Thread::Current();
277 StackHandleScope<1> hs(self);
278 HandleWrapper<mirror::Class> h(hs.NewHandleWrapper(&root));
279 ScopedThreadSuspension sts(self, ThreadState::kNative);
280 MakeInitializedClassesVisiblyInitialized(Thread::Current(), /*wait=*/ true);
281 }
282 // Pass the runtime `ArenaPool` to the transaction.
283 arena_pool = runtime->GetArenaPool();
284 } else {
285 // Pass the `ArenaStack` from previous transaction to the new one.
286 arena_stack = preinitialization_transactions_.front().GetArenaStack();
287 }
288 preinitialization_transactions_.emplace_front(strict, root, arena_stack, arena_pool);
289 runtime->SetActiveTransaction();
290 }
291
ExitTransactionMode()292 void AotClassLinker::ExitTransactionMode() {
293 DCHECK(IsActiveTransaction());
294 preinitialization_transactions_.pop_front();
295 if (preinitialization_transactions_.empty()) {
296 Runtime::Current()->ClearActiveTransaction();
297 } else {
298 DCHECK(IsActiveTransaction()); // Trigger the DCHECK() in `IsActiveTransaction()`.
299 }
300 }
301
RollbackAllTransactions()302 void AotClassLinker::RollbackAllTransactions() {
303 // If transaction is aborted, all transactions will be kept in the list.
304 // Rollback and exit all of them.
305 while (IsActiveTransaction()) {
306 RollbackAndExitTransactionMode();
307 }
308 }
309
RollbackAndExitTransactionMode()310 void AotClassLinker::RollbackAndExitTransactionMode() {
311 DCHECK(IsActiveTransaction());
312 Runtime::Current()->ClearActiveTransaction();
313 preinitialization_transactions_.front().Rollback();
314 preinitialization_transactions_.pop_front();
315 if (!preinitialization_transactions_.empty()) {
316 Runtime::Current()->SetActiveTransaction();
317 }
318 }
319
GetTransaction() const320 const Transaction* AotClassLinker::GetTransaction() const {
321 DCHECK(!preinitialization_transactions_.empty());
322 return &preinitialization_transactions_.front();
323 }
324
GetTransaction()325 Transaction* AotClassLinker::GetTransaction() {
326 DCHECK(!preinitialization_transactions_.empty());
327 return &preinitialization_transactions_.front();
328 }
329
IsActiveStrictTransactionMode() const330 bool AotClassLinker::IsActiveStrictTransactionMode() const {
331 return IsActiveTransaction() && GetTransaction()->IsStrict();
332 }
333
TransactionWriteConstraint(Thread * self,ObjPtr<mirror::Object> obj)334 bool AotClassLinker::TransactionWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) {
335 DCHECK(IsActiveTransaction());
336 if (GetTransaction()->WriteConstraint(obj)) {
337 Runtime* runtime = Runtime::Current();
338 DCHECK(runtime->GetHeap()->ObjectIsInBootImageSpace(obj) || obj->IsClass());
339 const char* extra = runtime->GetHeap()->ObjectIsInBootImageSpace(obj) ? "boot image " : "";
340 AbortTransactionF(
341 self, "Can't set fields of %s%s", extra, obj->PrettyTypeOf().c_str());
342 return true;
343 }
344 return false;
345 }
346
TransactionWriteValueConstraint(Thread * self,ObjPtr<mirror::Object> value)347 bool AotClassLinker::TransactionWriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) {
348 DCHECK(IsActiveTransaction());
349 if (GetTransaction()->WriteValueConstraint(value)) {
350 DCHECK(value != nullptr);
351 const char* description = value->IsClass() ? "class" : "instance of";
352 ObjPtr<mirror::Class> klass = value->IsClass() ? value->AsClass() : value->GetClass();
353 AbortTransactionF(
354 self, "Can't store reference to %s %s", description, klass->PrettyDescriptor().c_str());
355 return true;
356 }
357 return false;
358 }
359
TransactionReadConstraint(Thread * self,ObjPtr<mirror::Object> obj)360 bool AotClassLinker::TransactionReadConstraint(Thread* self, ObjPtr<mirror::Object> obj) {
361 DCHECK(obj->IsClass());
362 if (GetTransaction()->ReadConstraint(obj)) {
363 AbortTransactionF(self,
364 "Can't read static fields of %s since it does not belong to clinit's class.",
365 obj->PrettyTypeOf().c_str());
366 return true;
367 }
368 return false;
369 }
370
TransactionAllocationConstraint(Thread * self,ObjPtr<mirror::Class> klass)371 bool AotClassLinker::TransactionAllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass) {
372 DCHECK(IsActiveTransaction());
373 if (klass->IsFinalizable()) {
374 AbortTransactionF(self,
375 "Allocating finalizable object in transaction: %s",
376 klass->PrettyDescriptor().c_str());
377 return true;
378 }
379 return false;
380 }
381
RecordWriteFieldBoolean(mirror::Object * obj,MemberOffset field_offset,uint8_t value,bool is_volatile)382 void AotClassLinker::RecordWriteFieldBoolean(mirror::Object* obj,
383 MemberOffset field_offset,
384 uint8_t value,
385 bool is_volatile) {
386 DCHECK(IsActiveTransaction());
387 GetTransaction()->RecordWriteFieldBoolean(obj, field_offset, value, is_volatile);
388 }
389
RecordWriteFieldByte(mirror::Object * obj,MemberOffset field_offset,int8_t value,bool is_volatile)390 void AotClassLinker::RecordWriteFieldByte(mirror::Object* obj,
391 MemberOffset field_offset,
392 int8_t value,
393 bool is_volatile) {
394 DCHECK(IsActiveTransaction());
395 GetTransaction()->RecordWriteFieldByte(obj, field_offset, value, is_volatile);
396 }
397
RecordWriteFieldChar(mirror::Object * obj,MemberOffset field_offset,uint16_t value,bool is_volatile)398 void AotClassLinker::RecordWriteFieldChar(mirror::Object* obj,
399 MemberOffset field_offset,
400 uint16_t value,
401 bool is_volatile) {
402 DCHECK(IsActiveTransaction());
403 GetTransaction()->RecordWriteFieldChar(obj, field_offset, value, is_volatile);
404 }
405
RecordWriteFieldShort(mirror::Object * obj,MemberOffset field_offset,int16_t value,bool is_volatile)406 void AotClassLinker::RecordWriteFieldShort(mirror::Object* obj,
407 MemberOffset field_offset,
408 int16_t value,
409 bool is_volatile) {
410 DCHECK(IsActiveTransaction());
411 GetTransaction()->RecordWriteFieldShort(obj, field_offset, value, is_volatile);
412 }
413
RecordWriteField32(mirror::Object * obj,MemberOffset field_offset,uint32_t value,bool is_volatile)414 void AotClassLinker::RecordWriteField32(mirror::Object* obj,
415 MemberOffset field_offset,
416 uint32_t value,
417 bool is_volatile) {
418 DCHECK(IsActiveTransaction());
419 GetTransaction()->RecordWriteField32(obj, field_offset, value, is_volatile);
420 }
421
RecordWriteField64(mirror::Object * obj,MemberOffset field_offset,uint64_t value,bool is_volatile)422 void AotClassLinker::RecordWriteField64(mirror::Object* obj,
423 MemberOffset field_offset,
424 uint64_t value,
425 bool is_volatile) {
426 DCHECK(IsActiveTransaction());
427 GetTransaction()->RecordWriteField64(obj, field_offset, value, is_volatile);
428 }
429
RecordWriteFieldReference(mirror::Object * obj,MemberOffset field_offset,ObjPtr<mirror::Object> value,bool is_volatile)430 void AotClassLinker::RecordWriteFieldReference(mirror::Object* obj,
431 MemberOffset field_offset,
432 ObjPtr<mirror::Object> value,
433 bool is_volatile) {
434 DCHECK(IsActiveTransaction());
435 GetTransaction()->RecordWriteFieldReference(obj, field_offset, value.Ptr(), is_volatile);
436 }
437
RecordWriteArray(mirror::Array * array,size_t index,uint64_t value)438 void AotClassLinker::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) {
439 DCHECK(IsActiveTransaction());
440 GetTransaction()->RecordWriteArray(array, index, value);
441 }
442
RecordStrongStringInsertion(ObjPtr<mirror::String> s)443 void AotClassLinker::RecordStrongStringInsertion(ObjPtr<mirror::String> s) {
444 DCHECK(IsActiveTransaction());
445 GetTransaction()->RecordStrongStringInsertion(s);
446 }
447
RecordWeakStringInsertion(ObjPtr<mirror::String> s)448 void AotClassLinker::RecordWeakStringInsertion(ObjPtr<mirror::String> s) {
449 DCHECK(IsActiveTransaction());
450 GetTransaction()->RecordWeakStringInsertion(s);
451 }
452
RecordStrongStringRemoval(ObjPtr<mirror::String> s)453 void AotClassLinker::RecordStrongStringRemoval(ObjPtr<mirror::String> s) {
454 DCHECK(IsActiveTransaction());
455 GetTransaction()->RecordStrongStringRemoval(s);
456 }
457
RecordWeakStringRemoval(ObjPtr<mirror::String> s)458 void AotClassLinker::RecordWeakStringRemoval(ObjPtr<mirror::String> s) {
459 DCHECK(IsActiveTransaction());
460 GetTransaction()->RecordWeakStringRemoval(s);
461 }
462
RecordResolveString(ObjPtr<mirror::DexCache> dex_cache,dex::StringIndex string_idx)463 void AotClassLinker::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache,
464 dex::StringIndex string_idx) {
465 DCHECK(IsActiveTransaction());
466 GetTransaction()->RecordResolveString(dex_cache, string_idx);
467 }
468
RecordResolveMethodType(ObjPtr<mirror::DexCache> dex_cache,dex::ProtoIndex proto_idx)469 void AotClassLinker::RecordResolveMethodType(ObjPtr<mirror::DexCache> dex_cache,
470 dex::ProtoIndex proto_idx) {
471 DCHECK(IsActiveTransaction());
472 GetTransaction()->RecordResolveMethodType(dex_cache, proto_idx);
473 }
474
ThrowTransactionAbortError(Thread * self)475 void AotClassLinker::ThrowTransactionAbortError(Thread* self) {
476 DCHECK(IsActiveTransaction());
477 // Passing nullptr means we rethrow an exception with the earlier transaction abort message.
478 GetTransaction()->ThrowAbortError(self, nullptr);
479 }
480
AbortTransactionF(Thread * self,const char * fmt,...)481 void AotClassLinker::AbortTransactionF(Thread* self, const char* fmt, ...) {
482 va_list args;
483 va_start(args, fmt);
484 AbortTransactionV(self, fmt, args);
485 va_end(args);
486 }
487
AbortTransactionV(Thread * self,const char * fmt,va_list args)488 void AotClassLinker::AbortTransactionV(Thread* self, const char* fmt, va_list args) {
489 CHECK(IsActiveTransaction());
490 // Constructs abort message.
491 std::string abort_message;
492 android::base::StringAppendV(&abort_message, fmt, args);
493 // Throws an exception so we can abort the transaction and rollback every change.
494 //
495 // Throwing an exception may cause its class initialization. If we mark the transaction
496 // aborted before that, we may warn with a false alarm. Throwing the exception before
497 // marking the transaction aborted avoids that.
498 // But now the transaction can be nested, and abort the transaction will relax the constraints
499 // for constructing stack trace.
500 GetTransaction()->Abort(abort_message);
501 GetTransaction()->ThrowAbortError(self, &abort_message);
502 }
503
IsTransactionAborted() const504 bool AotClassLinker::IsTransactionAborted() const {
505 DCHECK(IsActiveTransaction());
506 return GetTransaction()->IsAborted();
507 }
508
VisitTransactionRoots(RootVisitor * visitor)509 void AotClassLinker::VisitTransactionRoots(RootVisitor* visitor) {
510 for (Transaction& transaction : preinitialization_transactions_) {
511 transaction.VisitRoots(visitor);
512 }
513 }
514
GetTransactionalInterpreter()515 const void* AotClassLinker::GetTransactionalInterpreter() {
516 return reinterpret_cast<const void*>(
517 &interpreter::ExecuteSwitchImplCpp</*transaction_active=*/ true>);
518 }
519
520 } // namespace art
521