/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "transaction.h" #include "common_runtime_test.h" #include "mirror/array-inl.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" namespace art { class TransactionTest : public CommonRuntimeTest {}; TEST_F(TransactionTest, Object_class) { ScopedObjectAccess soa(Thread::Current()); SirtRef sirt_klass(soa.Self(), class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); ASSERT_TRUE(sirt_klass.get() != nullptr); Transaction transaction; Runtime::Current()->EnterTransactionMode(&transaction); SirtRef sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self())); ASSERT_TRUE(sirt_obj.get() != nullptr); ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get()); Runtime::Current()->ExitTransactionMode(); // Aborting transaction must not clear the Object::class field. transaction.Abort(); EXPECT_EQ(sirt_obj->GetClass(), sirt_klass.get()); } TEST_F(TransactionTest, Object_monitor) { ScopedObjectAccess soa(Thread::Current()); SirtRef sirt_klass(soa.Self(), class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); ASSERT_TRUE(sirt_klass.get() != nullptr); SirtRef sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self())); ASSERT_TRUE(sirt_obj.get() != nullptr); ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get()); // Lock object's monitor outside the transaction. sirt_obj->MonitorEnter(soa.Self()); uint32_t old_lock_word = sirt_obj->GetLockWord(false).GetValue(); Transaction transaction; Runtime::Current()->EnterTransactionMode(&transaction); // Unlock object's monitor inside the transaction. sirt_obj->MonitorExit(soa.Self()); uint32_t new_lock_word = sirt_obj->GetLockWord(false).GetValue(); Runtime::Current()->ExitTransactionMode(); // Aborting transaction must not clear the Object::class field. transaction.Abort(); uint32_t aborted_lock_word = sirt_obj->GetLockWord(false).GetValue(); EXPECT_NE(old_lock_word, new_lock_word); EXPECT_EQ(aborted_lock_word, new_lock_word); } TEST_F(TransactionTest, Array_length) { ScopedObjectAccess soa(Thread::Current()); SirtRef sirt_klass(soa.Self(), class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")); ASSERT_TRUE(sirt_klass.get() != nullptr); constexpr int32_t kArraySize = 2; Transaction transaction; Runtime::Current()->EnterTransactionMode(&transaction); // Allocate an array during transaction. SirtRef sirt_obj(soa.Self(), mirror::Array::Alloc(soa.Self(), sirt_klass.get(), kArraySize, sirt_klass->GetComponentSize(), Runtime::Current()->GetHeap()->GetCurrentAllocator())); ASSERT_TRUE(sirt_obj.get() != nullptr); ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get()); Runtime::Current()->ExitTransactionMode(); // Aborting transaction must not clear the Object::class field. transaction.Abort(); EXPECT_EQ(sirt_obj->GetLength(), kArraySize); } TEST_F(TransactionTest, StaticFieldsTest) { ScopedObjectAccess soa(Thread::Current()); SirtRef class_loader( soa.Self(), soa.Decode(LoadDex("Transaction"))); ASSERT_TRUE(class_loader.get() != nullptr); SirtRef sirt_klass(soa.Self(), class_linker_->FindClass(soa.Self(), "LStaticFieldsTest;", class_loader)); ASSERT_TRUE(sirt_klass.get() != nullptr); class_linker_->EnsureInitialized(sirt_klass, true, true); ASSERT_TRUE(sirt_klass->IsInitialized()); // Lookup fields. mirror::ArtField* booleanField = sirt_klass->FindDeclaredStaticField("booleanField", "Z"); ASSERT_TRUE(booleanField != nullptr); ASSERT_EQ(FieldHelper(booleanField).GetTypeAsPrimitiveType(), Primitive::kPrimBoolean); ASSERT_EQ(booleanField->GetBoolean(sirt_klass.get()), false); mirror::ArtField* byteField = sirt_klass->FindDeclaredStaticField("byteField", "B"); ASSERT_TRUE(byteField != nullptr); ASSERT_EQ(FieldHelper(byteField).GetTypeAsPrimitiveType(), Primitive::kPrimByte); ASSERT_EQ(byteField->GetByte(sirt_klass.get()), 0); mirror::ArtField* charField = sirt_klass->FindDeclaredStaticField("charField", "C"); ASSERT_TRUE(charField != nullptr); ASSERT_EQ(FieldHelper(charField).GetTypeAsPrimitiveType(), Primitive::kPrimChar); ASSERT_EQ(charField->GetChar(sirt_klass.get()), 0u); mirror::ArtField* shortField = sirt_klass->FindDeclaredStaticField("shortField", "S"); ASSERT_TRUE(shortField != nullptr); ASSERT_EQ(FieldHelper(shortField).GetTypeAsPrimitiveType(), Primitive::kPrimShort); ASSERT_EQ(shortField->GetShort(sirt_klass.get()), 0); mirror::ArtField* intField = sirt_klass->FindDeclaredStaticField("intField", "I"); ASSERT_TRUE(intField != nullptr); ASSERT_EQ(FieldHelper(intField).GetTypeAsPrimitiveType(), Primitive::kPrimInt); ASSERT_EQ(intField->GetInt(sirt_klass.get()), 0); mirror::ArtField* longField = sirt_klass->FindDeclaredStaticField("longField", "J"); ASSERT_TRUE(longField != nullptr); ASSERT_EQ(FieldHelper(longField).GetTypeAsPrimitiveType(), Primitive::kPrimLong); ASSERT_EQ(longField->GetLong(sirt_klass.get()), static_cast(0)); mirror::ArtField* floatField = sirt_klass->FindDeclaredStaticField("floatField", "F"); ASSERT_TRUE(floatField != nullptr); ASSERT_EQ(FieldHelper(floatField).GetTypeAsPrimitiveType(), Primitive::kPrimFloat); ASSERT_EQ(floatField->GetFloat(sirt_klass.get()), static_cast(0.0f)); mirror::ArtField* doubleField = sirt_klass->FindDeclaredStaticField("doubleField", "D"); ASSERT_TRUE(doubleField != nullptr); ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble); ASSERT_EQ(doubleField->GetDouble(sirt_klass.get()), static_cast(0.0)); mirror::ArtField* objectField = sirt_klass->FindDeclaredStaticField("objectField", "Ljava/lang/Object;"); ASSERT_TRUE(objectField != nullptr); ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot); ASSERT_EQ(objectField->GetObject(sirt_klass.get()), nullptr); // Create a java.lang.Object instance to set objectField. SirtRef object_klass(soa.Self(), class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); ASSERT_TRUE(object_klass.get() != nullptr); SirtRef sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self())); ASSERT_TRUE(sirt_obj.get() != nullptr); ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get()); // Modify fields inside transaction and abort it. Transaction transaction; Runtime::Current()->EnterTransactionMode(&transaction); booleanField->SetBoolean(sirt_klass.get(), true); byteField->SetByte(sirt_klass.get(), 1); charField->SetChar(sirt_klass.get(), 1u); shortField->SetShort(sirt_klass.get(), 1); intField->SetInt(sirt_klass.get(), 1); longField->SetLong(sirt_klass.get(), 1); floatField->SetFloat(sirt_klass.get(), 1.0); doubleField->SetDouble(sirt_klass.get(), 1.0); objectField->SetObject(sirt_klass.get(), sirt_obj.get()); Runtime::Current()->ExitTransactionMode(); transaction.Abort(); // Check values have properly been restored to their original (default) value. EXPECT_EQ(booleanField->GetBoolean(sirt_klass.get()), false); EXPECT_EQ(byteField->GetByte(sirt_klass.get()), 0); EXPECT_EQ(charField->GetChar(sirt_klass.get()), 0u); EXPECT_EQ(shortField->GetShort(sirt_klass.get()), 0); EXPECT_EQ(intField->GetInt(sirt_klass.get()), 0); EXPECT_EQ(longField->GetLong(sirt_klass.get()), static_cast(0)); EXPECT_EQ(floatField->GetFloat(sirt_klass.get()), static_cast(0.0f)); EXPECT_EQ(doubleField->GetDouble(sirt_klass.get()), static_cast(0.0)); EXPECT_EQ(objectField->GetObject(sirt_klass.get()), nullptr); } TEST_F(TransactionTest, InstanceFieldsTest) { ScopedObjectAccess soa(Thread::Current()); SirtRef class_loader( soa.Self(), soa.Decode(LoadDex("Transaction"))); ASSERT_TRUE(class_loader.get() != nullptr); SirtRef sirt_klass(soa.Self(), class_linker_->FindClass(soa.Self(), "LInstanceFieldsTest;", class_loader)); ASSERT_TRUE(sirt_klass.get() != nullptr); class_linker_->EnsureInitialized(sirt_klass, true, true); ASSERT_TRUE(sirt_klass->IsInitialized()); // Allocate an InstanceFieldTest object. SirtRef sirt_instance(soa.Self(), sirt_klass->AllocObject(soa.Self())); ASSERT_TRUE(sirt_instance.get() != nullptr); // Lookup fields. mirror::ArtField* booleanField = sirt_klass->FindDeclaredInstanceField("booleanField", "Z"); ASSERT_TRUE(booleanField != nullptr); ASSERT_EQ(FieldHelper(booleanField).GetTypeAsPrimitiveType(), Primitive::kPrimBoolean); ASSERT_EQ(booleanField->GetBoolean(sirt_instance.get()), false); mirror::ArtField* byteField = sirt_klass->FindDeclaredInstanceField("byteField", "B"); ASSERT_TRUE(byteField != nullptr); ASSERT_EQ(FieldHelper(byteField).GetTypeAsPrimitiveType(), Primitive::kPrimByte); ASSERT_EQ(byteField->GetByte(sirt_instance.get()), 0); mirror::ArtField* charField = sirt_klass->FindDeclaredInstanceField("charField", "C"); ASSERT_TRUE(charField != nullptr); ASSERT_EQ(FieldHelper(charField).GetTypeAsPrimitiveType(), Primitive::kPrimChar); ASSERT_EQ(charField->GetChar(sirt_instance.get()), 0u); mirror::ArtField* shortField = sirt_klass->FindDeclaredInstanceField("shortField", "S"); ASSERT_TRUE(shortField != nullptr); ASSERT_EQ(FieldHelper(shortField).GetTypeAsPrimitiveType(), Primitive::kPrimShort); ASSERT_EQ(shortField->GetShort(sirt_instance.get()), 0); mirror::ArtField* intField = sirt_klass->FindDeclaredInstanceField("intField", "I"); ASSERT_TRUE(intField != nullptr); ASSERT_EQ(FieldHelper(intField).GetTypeAsPrimitiveType(), Primitive::kPrimInt); ASSERT_EQ(intField->GetInt(sirt_instance.get()), 0); mirror::ArtField* longField = sirt_klass->FindDeclaredInstanceField("longField", "J"); ASSERT_TRUE(longField != nullptr); ASSERT_EQ(FieldHelper(longField).GetTypeAsPrimitiveType(), Primitive::kPrimLong); ASSERT_EQ(longField->GetLong(sirt_instance.get()), static_cast(0)); mirror::ArtField* floatField = sirt_klass->FindDeclaredInstanceField("floatField", "F"); ASSERT_TRUE(floatField != nullptr); ASSERT_EQ(FieldHelper(floatField).GetTypeAsPrimitiveType(), Primitive::kPrimFloat); ASSERT_EQ(floatField->GetFloat(sirt_instance.get()), static_cast(0.0f)); mirror::ArtField* doubleField = sirt_klass->FindDeclaredInstanceField("doubleField", "D"); ASSERT_TRUE(doubleField != nullptr); ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble); ASSERT_EQ(doubleField->GetDouble(sirt_instance.get()), static_cast(0.0)); mirror::ArtField* objectField = sirt_klass->FindDeclaredInstanceField("objectField", "Ljava/lang/Object;"); ASSERT_TRUE(objectField != nullptr); ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot); ASSERT_EQ(objectField->GetObject(sirt_instance.get()), nullptr); // Create a java.lang.Object instance to set objectField. SirtRef object_klass(soa.Self(), class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); ASSERT_TRUE(object_klass.get() != nullptr); SirtRef sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self())); ASSERT_TRUE(sirt_obj.get() != nullptr); ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get()); // Modify fields inside transaction and abort it. Transaction transaction; Runtime::Current()->EnterTransactionMode(&transaction); booleanField->SetBoolean(sirt_instance.get(), true); byteField->SetByte(sirt_instance.get(), 1); charField->SetChar(sirt_instance.get(), 1u); shortField->SetShort(sirt_instance.get(), 1); intField->SetInt(sirt_instance.get(), 1); longField->SetLong(sirt_instance.get(), 1); floatField->SetFloat(sirt_instance.get(), 1.0); doubleField->SetDouble(sirt_instance.get(), 1.0); objectField->SetObject(sirt_instance.get(), sirt_obj.get()); Runtime::Current()->ExitTransactionMode(); transaction.Abort(); // Check values have properly been restored to their original (default) value. EXPECT_EQ(booleanField->GetBoolean(sirt_instance.get()), false); EXPECT_EQ(byteField->GetByte(sirt_instance.get()), 0); EXPECT_EQ(charField->GetChar(sirt_instance.get()), 0u); EXPECT_EQ(shortField->GetShort(sirt_instance.get()), 0); EXPECT_EQ(intField->GetInt(sirt_instance.get()), 0); EXPECT_EQ(longField->GetLong(sirt_instance.get()), static_cast(0)); EXPECT_EQ(floatField->GetFloat(sirt_instance.get()), static_cast(0.0f)); EXPECT_EQ(doubleField->GetDouble(sirt_instance.get()), static_cast(0.0)); EXPECT_EQ(objectField->GetObject(sirt_instance.get()), nullptr); } TEST_F(TransactionTest, StaticArrayFieldsTest) { ScopedObjectAccess soa(Thread::Current()); SirtRef class_loader( soa.Self(), soa.Decode(LoadDex("Transaction"))); ASSERT_TRUE(class_loader.get() != nullptr); SirtRef sirt_klass(soa.Self(), class_linker_->FindClass(soa.Self(), "LStaticArrayFieldsTest;", class_loader)); ASSERT_TRUE(sirt_klass.get() != nullptr); class_linker_->EnsureInitialized(sirt_klass, true, true); ASSERT_TRUE(sirt_klass->IsInitialized()); // Lookup fields. mirror::ArtField* booleanArrayField = sirt_klass->FindDeclaredStaticField("booleanArrayField", "[Z"); ASSERT_TRUE(booleanArrayField != nullptr); mirror::BooleanArray* booleanArray = booleanArrayField->GetObject(sirt_klass.get())->AsBooleanArray(); ASSERT_TRUE(booleanArray != nullptr); ASSERT_EQ(booleanArray->GetLength(), 1); ASSERT_EQ(booleanArray->GetWithoutChecks(0), false); mirror::ArtField* byteArrayField = sirt_klass->FindDeclaredStaticField("byteArrayField", "[B"); ASSERT_TRUE(byteArrayField != nullptr); mirror::ByteArray* byteArray = byteArrayField->GetObject(sirt_klass.get())->AsByteArray(); ASSERT_TRUE(byteArray != nullptr); ASSERT_EQ(byteArray->GetLength(), 1); ASSERT_EQ(byteArray->GetWithoutChecks(0), 0); mirror::ArtField* charArrayField = sirt_klass->FindDeclaredStaticField("charArrayField", "[C"); ASSERT_TRUE(charArrayField != nullptr); mirror::CharArray* charArray = charArrayField->GetObject(sirt_klass.get())->AsCharArray(); ASSERT_TRUE(charArray != nullptr); ASSERT_EQ(charArray->GetLength(), 1); ASSERT_EQ(charArray->GetWithoutChecks(0), 0u); mirror::ArtField* shortArrayField = sirt_klass->FindDeclaredStaticField("shortArrayField", "[S"); ASSERT_TRUE(shortArrayField != nullptr); mirror::ShortArray* shortArray = shortArrayField->GetObject(sirt_klass.get())->AsShortArray(); ASSERT_TRUE(shortArray != nullptr); ASSERT_EQ(shortArray->GetLength(), 1); ASSERT_EQ(shortArray->GetWithoutChecks(0), 0); mirror::ArtField* intArrayField = sirt_klass->FindDeclaredStaticField("intArrayField", "[I"); ASSERT_TRUE(intArrayField != nullptr); mirror::IntArray* intArray = intArrayField->GetObject(sirt_klass.get())->AsIntArray(); ASSERT_TRUE(intArray != nullptr); ASSERT_EQ(intArray->GetLength(), 1); ASSERT_EQ(intArray->GetWithoutChecks(0), 0); mirror::ArtField* longArrayField = sirt_klass->FindDeclaredStaticField("longArrayField", "[J"); ASSERT_TRUE(longArrayField != nullptr); mirror::LongArray* longArray = longArrayField->GetObject(sirt_klass.get())->AsLongArray(); ASSERT_TRUE(longArray != nullptr); ASSERT_EQ(longArray->GetLength(), 1); ASSERT_EQ(longArray->GetWithoutChecks(0), static_cast(0)); mirror::ArtField* floatArrayField = sirt_klass->FindDeclaredStaticField("floatArrayField", "[F"); ASSERT_TRUE(floatArrayField != nullptr); mirror::FloatArray* floatArray = floatArrayField->GetObject(sirt_klass.get())->AsFloatArray(); ASSERT_TRUE(floatArray != nullptr); ASSERT_EQ(floatArray->GetLength(), 1); ASSERT_EQ(floatArray->GetWithoutChecks(0), static_cast(0.0f)); mirror::ArtField* doubleArrayField = sirt_klass->FindDeclaredStaticField("doubleArrayField", "[D"); ASSERT_TRUE(doubleArrayField != nullptr); mirror::DoubleArray* doubleArray = doubleArrayField->GetObject(sirt_klass.get())->AsDoubleArray(); ASSERT_TRUE(doubleArray != nullptr); ASSERT_EQ(doubleArray->GetLength(), 1); ASSERT_EQ(doubleArray->GetWithoutChecks(0), static_cast(0.0f)); mirror::ArtField* objectArrayField = sirt_klass->FindDeclaredStaticField("objectArrayField", "[Ljava/lang/Object;"); ASSERT_TRUE(objectArrayField != nullptr); mirror::ObjectArray* objectArray = objectArrayField->GetObject(sirt_klass.get())->AsObjectArray(); ASSERT_TRUE(objectArray != nullptr); ASSERT_EQ(objectArray->GetLength(), 1); ASSERT_EQ(objectArray->GetWithoutChecks(0), nullptr); // Create a java.lang.Object instance to set objectField. SirtRef object_klass(soa.Self(), class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); ASSERT_TRUE(object_klass.get() != nullptr); SirtRef sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self())); ASSERT_TRUE(sirt_obj.get() != nullptr); ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get()); // Modify fields inside transaction and abort it. Transaction transaction; Runtime::Current()->EnterTransactionMode(&transaction); booleanArray->SetWithoutChecks(0, true); byteArray->SetWithoutChecks(0, 1); charArray->SetWithoutChecks(0, 1u); shortArray->SetWithoutChecks(0, 1); intArray->SetWithoutChecks(0, 1); longArray->SetWithoutChecks(0, 1); floatArray->SetWithoutChecks(0, 1.0); doubleArray->SetWithoutChecks(0, 1.0); objectArray->SetWithoutChecks(0, sirt_obj.get()); Runtime::Current()->ExitTransactionMode(); transaction.Abort(); // Check values have properly been restored to their original (default) value. EXPECT_EQ(booleanArray->GetWithoutChecks(0), false); EXPECT_EQ(byteArray->GetWithoutChecks(0), 0); EXPECT_EQ(charArray->GetWithoutChecks(0), 0u); EXPECT_EQ(shortArray->GetWithoutChecks(0), 0); EXPECT_EQ(intArray->GetWithoutChecks(0), 0); EXPECT_EQ(longArray->GetWithoutChecks(0), static_cast(0)); EXPECT_EQ(floatArray->GetWithoutChecks(0), static_cast(0.0f)); EXPECT_EQ(doubleArray->GetWithoutChecks(0), static_cast(0.0f)); EXPECT_EQ(objectArray->GetWithoutChecks(0), nullptr); } TEST_F(TransactionTest, EmptyClass) { ScopedObjectAccess soa(Thread::Current()); SirtRef class_loader( soa.Self(), soa.Decode(LoadDex("Transaction"))); ASSERT_TRUE(class_loader.get() != nullptr); SirtRef sirt_klass(soa.Self(), class_linker_->FindClass(soa.Self(), "LTransaction$EmptyStatic;", class_loader)); ASSERT_TRUE(sirt_klass.get() != nullptr); class_linker_->VerifyClass(sirt_klass); ASSERT_TRUE(sirt_klass->IsVerified()); Transaction transaction; Runtime::Current()->EnterTransactionMode(&transaction); class_linker_->EnsureInitialized(sirt_klass, true, true); Runtime::Current()->ExitTransactionMode(); ASSERT_FALSE(soa.Self()->IsExceptionPending()); } TEST_F(TransactionTest, StaticFieldClass) { ScopedObjectAccess soa(Thread::Current()); SirtRef class_loader( soa.Self(), soa.Decode(LoadDex("Transaction"))); ASSERT_TRUE(class_loader.get() != nullptr); SirtRef sirt_klass(soa.Self(), class_linker_->FindClass(soa.Self(), "LTransaction$StaticFieldClass;", class_loader)); ASSERT_TRUE(sirt_klass.get() != nullptr); class_linker_->VerifyClass(sirt_klass); ASSERT_TRUE(sirt_klass->IsVerified()); Transaction transaction; Runtime::Current()->EnterTransactionMode(&transaction); class_linker_->EnsureInitialized(sirt_klass, true, true); Runtime::Current()->ExitTransactionMode(); ASSERT_FALSE(soa.Self()->IsExceptionPending()); } TEST_F(TransactionTest, BlacklistedClass) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("Transaction"); SirtRef class_loader(soa.Self(), soa.Decode(jclass_loader)); ASSERT_TRUE(class_loader.get() != nullptr); // Load and verify java.lang.ExceptionInInitializerError and java.lang.InternalError which will // be thrown by class initialization due to native call. SirtRef sirt_klass(soa.Self(), class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/ExceptionInInitializerError;")); ASSERT_TRUE(sirt_klass.get() != nullptr); class_linker_->VerifyClass(sirt_klass); ASSERT_TRUE(sirt_klass->IsVerified()); sirt_klass.reset(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/InternalError;")); ASSERT_TRUE(sirt_klass.get() != nullptr); class_linker_->VerifyClass(sirt_klass); ASSERT_TRUE(sirt_klass->IsVerified()); // Load and verify Transaction$NativeSupport used in class initialization. sirt_klass.reset(class_linker_->FindClass(soa.Self(), "LTransaction$NativeSupport;", class_loader)); ASSERT_TRUE(sirt_klass.get() != nullptr); class_linker_->VerifyClass(sirt_klass); ASSERT_TRUE(sirt_klass->IsVerified()); sirt_klass.reset(class_linker_->FindClass(soa.Self(), "LTransaction$BlacklistedClass;", class_loader)); ASSERT_TRUE(sirt_klass.get() != nullptr); class_linker_->VerifyClass(sirt_klass); ASSERT_TRUE(sirt_klass->IsVerified()); Transaction transaction; Runtime::Current()->EnterTransactionMode(&transaction); class_linker_->EnsureInitialized(sirt_klass, true, true); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(soa.Self()->IsExceptionPending()); } } // namespace art