/* * Copyright (C) 2012 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 "reg_type.h" #include #include "base/bit_vector.h" #include "base/casts.h" #include "base/scoped_arena_allocator.h" #include "common_runtime_test.h" #include "compiler_callbacks.h" #include "reg_type-inl.h" #include "reg_type_cache-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" namespace art { namespace verifier { class RegTypeTest : public CommonRuntimeTest {}; TEST_F(RegTypeTest, ConstLoHi) { // Tests creating primitive types types. ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache(Runtime::Current()->GetClassLinker(), true, allocator); const RegType& ref_type_const_0 = cache.FromCat1Const(10, true); const RegType& ref_type_const_1 = cache.FromCat1Const(10, true); const RegType& ref_type_const_2 = cache.FromCat1Const(30, true); const RegType& ref_type_const_3 = cache.FromCat1Const(30, false); EXPECT_TRUE(ref_type_const_0.Equals(ref_type_const_1)); EXPECT_FALSE(ref_type_const_0.Equals(ref_type_const_2)); EXPECT_FALSE(ref_type_const_0.Equals(ref_type_const_3)); const RegType& ref_type_const_wide_0 = cache.FromCat2ConstHi(50, true); const RegType& ref_type_const_wide_1 = cache.FromCat2ConstHi(50, true); EXPECT_TRUE(ref_type_const_wide_0.Equals(ref_type_const_wide_1)); const RegType& ref_type_const_wide_2 = cache.FromCat2ConstLo(50, true); const RegType& ref_type_const_wide_3 = cache.FromCat2ConstLo(50, true); const RegType& ref_type_const_wide_4 = cache.FromCat2ConstLo(55, true); EXPECT_TRUE(ref_type_const_wide_2.Equals(ref_type_const_wide_3)); EXPECT_FALSE(ref_type_const_wide_2.Equals(ref_type_const_wide_4)); } TEST_F(RegTypeTest, Pairs) { ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache(Runtime::Current()->GetClassLinker(), true, allocator); int64_t val = static_cast(1234); const RegType& precise_lo = cache.FromCat2ConstLo(static_cast(val), true); const RegType& precise_hi = cache.FromCat2ConstHi(static_cast(val >> 32), true); const RegType& precise_const = cache.FromCat1Const(static_cast(val >> 32), true); const RegType& long_lo = cache.LongLo(); const RegType& long_hi = cache.LongHi(); // Check the expectations for types. EXPECT_TRUE(precise_lo.IsLowHalf()); EXPECT_FALSE(precise_hi.IsLowHalf()); EXPECT_FALSE(precise_lo.IsHighHalf()); EXPECT_TRUE(precise_hi.IsHighHalf()); EXPECT_TRUE(long_hi.IsLongHighTypes()); EXPECT_TRUE(precise_hi.IsLongHighTypes()); // Check Pairing. EXPECT_FALSE(precise_lo.CheckWidePair(precise_const)); EXPECT_TRUE(precise_lo.CheckWidePair(precise_hi)); // Test Merging. EXPECT_TRUE((long_lo.Merge(precise_lo, &cache, /* verifier= */ nullptr)).IsLongTypes()); EXPECT_TRUE((long_hi.Merge(precise_hi, &cache, /* verifier= */ nullptr)).IsLongHighTypes()); } TEST_F(RegTypeTest, Primitives) { ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache(Runtime::Current()->GetClassLinker(), true, allocator); const RegType& bool_reg_type = cache.Boolean(); EXPECT_FALSE(bool_reg_type.IsUndefined()); EXPECT_FALSE(bool_reg_type.IsConflict()); EXPECT_FALSE(bool_reg_type.IsZero()); EXPECT_FALSE(bool_reg_type.IsOne()); EXPECT_FALSE(bool_reg_type.IsLongConstant()); EXPECT_TRUE(bool_reg_type.IsBoolean()); EXPECT_FALSE(bool_reg_type.IsByte()); EXPECT_FALSE(bool_reg_type.IsChar()); EXPECT_FALSE(bool_reg_type.IsShort()); EXPECT_FALSE(bool_reg_type.IsInteger()); EXPECT_FALSE(bool_reg_type.IsLong()); EXPECT_FALSE(bool_reg_type.IsFloat()); EXPECT_FALSE(bool_reg_type.IsDouble()); EXPECT_FALSE(bool_reg_type.IsReference()); EXPECT_FALSE(bool_reg_type.IsLowHalf()); EXPECT_FALSE(bool_reg_type.IsHighHalf()); EXPECT_FALSE(bool_reg_type.IsLongOrDoubleTypes()); EXPECT_FALSE(bool_reg_type.IsReferenceTypes()); EXPECT_TRUE(bool_reg_type.IsCategory1Types()); EXPECT_FALSE(bool_reg_type.IsCategory2Types()); EXPECT_TRUE(bool_reg_type.IsBooleanTypes()); EXPECT_TRUE(bool_reg_type.IsByteTypes()); EXPECT_TRUE(bool_reg_type.IsShortTypes()); EXPECT_TRUE(bool_reg_type.IsCharTypes()); EXPECT_TRUE(bool_reg_type.IsIntegralTypes()); EXPECT_FALSE(bool_reg_type.IsFloatTypes()); EXPECT_FALSE(bool_reg_type.IsLongTypes()); EXPECT_FALSE(bool_reg_type.IsDoubleTypes()); EXPECT_TRUE(bool_reg_type.IsArrayIndexTypes()); EXPECT_FALSE(bool_reg_type.IsNonZeroReferenceTypes()); EXPECT_TRUE(bool_reg_type.HasClass()); const RegType& byte_reg_type = cache.Byte(); EXPECT_FALSE(byte_reg_type.IsUndefined()); EXPECT_FALSE(byte_reg_type.IsConflict()); EXPECT_FALSE(byte_reg_type.IsZero()); EXPECT_FALSE(byte_reg_type.IsOne()); EXPECT_FALSE(byte_reg_type.IsLongConstant()); EXPECT_FALSE(byte_reg_type.IsBoolean()); EXPECT_TRUE(byte_reg_type.IsByte()); EXPECT_FALSE(byte_reg_type.IsChar()); EXPECT_FALSE(byte_reg_type.IsShort()); EXPECT_FALSE(byte_reg_type.IsInteger()); EXPECT_FALSE(byte_reg_type.IsLong()); EXPECT_FALSE(byte_reg_type.IsFloat()); EXPECT_FALSE(byte_reg_type.IsDouble()); EXPECT_FALSE(byte_reg_type.IsReference()); EXPECT_FALSE(byte_reg_type.IsLowHalf()); EXPECT_FALSE(byte_reg_type.IsHighHalf()); EXPECT_FALSE(byte_reg_type.IsLongOrDoubleTypes()); EXPECT_FALSE(byte_reg_type.IsReferenceTypes()); EXPECT_TRUE(byte_reg_type.IsCategory1Types()); EXPECT_FALSE(byte_reg_type.IsCategory2Types()); EXPECT_FALSE(byte_reg_type.IsBooleanTypes()); EXPECT_TRUE(byte_reg_type.IsByteTypes()); EXPECT_TRUE(byte_reg_type.IsShortTypes()); EXPECT_FALSE(byte_reg_type.IsCharTypes()); EXPECT_TRUE(byte_reg_type.IsIntegralTypes()); EXPECT_FALSE(byte_reg_type.IsFloatTypes()); EXPECT_FALSE(byte_reg_type.IsLongTypes()); EXPECT_FALSE(byte_reg_type.IsDoubleTypes()); EXPECT_TRUE(byte_reg_type.IsArrayIndexTypes()); EXPECT_FALSE(byte_reg_type.IsNonZeroReferenceTypes()); EXPECT_TRUE(byte_reg_type.HasClass()); const RegType& char_reg_type = cache.Char(); EXPECT_FALSE(char_reg_type.IsUndefined()); EXPECT_FALSE(char_reg_type.IsConflict()); EXPECT_FALSE(char_reg_type.IsZero()); EXPECT_FALSE(char_reg_type.IsOne()); EXPECT_FALSE(char_reg_type.IsLongConstant()); EXPECT_FALSE(char_reg_type.IsBoolean()); EXPECT_FALSE(char_reg_type.IsByte()); EXPECT_TRUE(char_reg_type.IsChar()); EXPECT_FALSE(char_reg_type.IsShort()); EXPECT_FALSE(char_reg_type.IsInteger()); EXPECT_FALSE(char_reg_type.IsLong()); EXPECT_FALSE(char_reg_type.IsFloat()); EXPECT_FALSE(char_reg_type.IsDouble()); EXPECT_FALSE(char_reg_type.IsReference()); EXPECT_FALSE(char_reg_type.IsLowHalf()); EXPECT_FALSE(char_reg_type.IsHighHalf()); EXPECT_FALSE(char_reg_type.IsLongOrDoubleTypes()); EXPECT_FALSE(char_reg_type.IsReferenceTypes()); EXPECT_TRUE(char_reg_type.IsCategory1Types()); EXPECT_FALSE(char_reg_type.IsCategory2Types()); EXPECT_FALSE(char_reg_type.IsBooleanTypes()); EXPECT_FALSE(char_reg_type.IsByteTypes()); EXPECT_FALSE(char_reg_type.IsShortTypes()); EXPECT_TRUE(char_reg_type.IsCharTypes()); EXPECT_TRUE(char_reg_type.IsIntegralTypes()); EXPECT_FALSE(char_reg_type.IsFloatTypes()); EXPECT_FALSE(char_reg_type.IsLongTypes()); EXPECT_FALSE(char_reg_type.IsDoubleTypes()); EXPECT_TRUE(char_reg_type.IsArrayIndexTypes()); EXPECT_FALSE(char_reg_type.IsNonZeroReferenceTypes()); EXPECT_TRUE(char_reg_type.HasClass()); const RegType& short_reg_type = cache.Short(); EXPECT_FALSE(short_reg_type.IsUndefined()); EXPECT_FALSE(short_reg_type.IsConflict()); EXPECT_FALSE(short_reg_type.IsZero()); EXPECT_FALSE(short_reg_type.IsOne()); EXPECT_FALSE(short_reg_type.IsLongConstant()); EXPECT_FALSE(short_reg_type.IsBoolean()); EXPECT_FALSE(short_reg_type.IsByte()); EXPECT_FALSE(short_reg_type.IsChar()); EXPECT_TRUE(short_reg_type.IsShort()); EXPECT_FALSE(short_reg_type.IsInteger()); EXPECT_FALSE(short_reg_type.IsLong()); EXPECT_FALSE(short_reg_type.IsFloat()); EXPECT_FALSE(short_reg_type.IsDouble()); EXPECT_FALSE(short_reg_type.IsReference()); EXPECT_FALSE(short_reg_type.IsLowHalf()); EXPECT_FALSE(short_reg_type.IsHighHalf()); EXPECT_FALSE(short_reg_type.IsLongOrDoubleTypes()); EXPECT_FALSE(short_reg_type.IsReferenceTypes()); EXPECT_TRUE(short_reg_type.IsCategory1Types()); EXPECT_FALSE(short_reg_type.IsCategory2Types()); EXPECT_FALSE(short_reg_type.IsBooleanTypes()); EXPECT_FALSE(short_reg_type.IsByteTypes()); EXPECT_TRUE(short_reg_type.IsShortTypes()); EXPECT_FALSE(short_reg_type.IsCharTypes()); EXPECT_TRUE(short_reg_type.IsIntegralTypes()); EXPECT_FALSE(short_reg_type.IsFloatTypes()); EXPECT_FALSE(short_reg_type.IsLongTypes()); EXPECT_FALSE(short_reg_type.IsDoubleTypes()); EXPECT_TRUE(short_reg_type.IsArrayIndexTypes()); EXPECT_FALSE(short_reg_type.IsNonZeroReferenceTypes()); EXPECT_TRUE(short_reg_type.HasClass()); const RegType& int_reg_type = cache.Integer(); EXPECT_FALSE(int_reg_type.IsUndefined()); EXPECT_FALSE(int_reg_type.IsConflict()); EXPECT_FALSE(int_reg_type.IsZero()); EXPECT_FALSE(int_reg_type.IsOne()); EXPECT_FALSE(int_reg_type.IsLongConstant()); EXPECT_FALSE(int_reg_type.IsBoolean()); EXPECT_FALSE(int_reg_type.IsByte()); EXPECT_FALSE(int_reg_type.IsChar()); EXPECT_FALSE(int_reg_type.IsShort()); EXPECT_TRUE(int_reg_type.IsInteger()); EXPECT_FALSE(int_reg_type.IsLong()); EXPECT_FALSE(int_reg_type.IsFloat()); EXPECT_FALSE(int_reg_type.IsDouble()); EXPECT_FALSE(int_reg_type.IsReference()); EXPECT_FALSE(int_reg_type.IsLowHalf()); EXPECT_FALSE(int_reg_type.IsHighHalf()); EXPECT_FALSE(int_reg_type.IsLongOrDoubleTypes()); EXPECT_FALSE(int_reg_type.IsReferenceTypes()); EXPECT_TRUE(int_reg_type.IsCategory1Types()); EXPECT_FALSE(int_reg_type.IsCategory2Types()); EXPECT_FALSE(int_reg_type.IsBooleanTypes()); EXPECT_FALSE(int_reg_type.IsByteTypes()); EXPECT_FALSE(int_reg_type.IsShortTypes()); EXPECT_FALSE(int_reg_type.IsCharTypes()); EXPECT_TRUE(int_reg_type.IsIntegralTypes()); EXPECT_FALSE(int_reg_type.IsFloatTypes()); EXPECT_FALSE(int_reg_type.IsLongTypes()); EXPECT_FALSE(int_reg_type.IsDoubleTypes()); EXPECT_TRUE(int_reg_type.IsArrayIndexTypes()); EXPECT_FALSE(int_reg_type.IsNonZeroReferenceTypes()); EXPECT_TRUE(int_reg_type.HasClass()); const RegType& long_reg_type = cache.LongLo(); EXPECT_FALSE(long_reg_type.IsUndefined()); EXPECT_FALSE(long_reg_type.IsConflict()); EXPECT_FALSE(long_reg_type.IsZero()); EXPECT_FALSE(long_reg_type.IsOne()); EXPECT_FALSE(long_reg_type.IsLongConstant()); EXPECT_FALSE(long_reg_type.IsBoolean()); EXPECT_FALSE(long_reg_type.IsByte()); EXPECT_FALSE(long_reg_type.IsChar()); EXPECT_FALSE(long_reg_type.IsShort()); EXPECT_FALSE(long_reg_type.IsInteger()); EXPECT_TRUE(long_reg_type.IsLong()); EXPECT_FALSE(long_reg_type.IsFloat()); EXPECT_FALSE(long_reg_type.IsDouble()); EXPECT_FALSE(long_reg_type.IsReference()); EXPECT_TRUE(long_reg_type.IsLowHalf()); EXPECT_FALSE(long_reg_type.IsHighHalf()); EXPECT_TRUE(long_reg_type.IsLongOrDoubleTypes()); EXPECT_FALSE(long_reg_type.IsReferenceTypes()); EXPECT_FALSE(long_reg_type.IsCategory1Types()); EXPECT_TRUE(long_reg_type.IsCategory2Types()); EXPECT_FALSE(long_reg_type.IsBooleanTypes()); EXPECT_FALSE(long_reg_type.IsByteTypes()); EXPECT_FALSE(long_reg_type.IsShortTypes()); EXPECT_FALSE(long_reg_type.IsCharTypes()); EXPECT_FALSE(long_reg_type.IsIntegralTypes()); EXPECT_FALSE(long_reg_type.IsFloatTypes()); EXPECT_TRUE(long_reg_type.IsLongTypes()); EXPECT_FALSE(long_reg_type.IsDoubleTypes()); EXPECT_FALSE(long_reg_type.IsArrayIndexTypes()); EXPECT_FALSE(long_reg_type.IsNonZeroReferenceTypes()); EXPECT_TRUE(long_reg_type.HasClass()); const RegType& float_reg_type = cache.Float(); EXPECT_FALSE(float_reg_type.IsUndefined()); EXPECT_FALSE(float_reg_type.IsConflict()); EXPECT_FALSE(float_reg_type.IsZero()); EXPECT_FALSE(float_reg_type.IsOne()); EXPECT_FALSE(float_reg_type.IsLongConstant()); EXPECT_FALSE(float_reg_type.IsBoolean()); EXPECT_FALSE(float_reg_type.IsByte()); EXPECT_FALSE(float_reg_type.IsChar()); EXPECT_FALSE(float_reg_type.IsShort()); EXPECT_FALSE(float_reg_type.IsInteger()); EXPECT_FALSE(float_reg_type.IsLong()); EXPECT_TRUE(float_reg_type.IsFloat()); EXPECT_FALSE(float_reg_type.IsDouble()); EXPECT_FALSE(float_reg_type.IsReference()); EXPECT_FALSE(float_reg_type.IsLowHalf()); EXPECT_FALSE(float_reg_type.IsHighHalf()); EXPECT_FALSE(float_reg_type.IsLongOrDoubleTypes()); EXPECT_FALSE(float_reg_type.IsReferenceTypes()); EXPECT_TRUE(float_reg_type.IsCategory1Types()); EXPECT_FALSE(float_reg_type.IsCategory2Types()); EXPECT_FALSE(float_reg_type.IsBooleanTypes()); EXPECT_FALSE(float_reg_type.IsByteTypes()); EXPECT_FALSE(float_reg_type.IsShortTypes()); EXPECT_FALSE(float_reg_type.IsCharTypes()); EXPECT_FALSE(float_reg_type.IsIntegralTypes()); EXPECT_TRUE(float_reg_type.IsFloatTypes()); EXPECT_FALSE(float_reg_type.IsLongTypes()); EXPECT_FALSE(float_reg_type.IsDoubleTypes()); EXPECT_FALSE(float_reg_type.IsArrayIndexTypes()); EXPECT_FALSE(float_reg_type.IsNonZeroReferenceTypes()); EXPECT_TRUE(float_reg_type.HasClass()); const RegType& double_reg_type = cache.DoubleLo(); EXPECT_FALSE(double_reg_type.IsUndefined()); EXPECT_FALSE(double_reg_type.IsConflict()); EXPECT_FALSE(double_reg_type.IsZero()); EXPECT_FALSE(double_reg_type.IsOne()); EXPECT_FALSE(double_reg_type.IsLongConstant()); EXPECT_FALSE(double_reg_type.IsBoolean()); EXPECT_FALSE(double_reg_type.IsByte()); EXPECT_FALSE(double_reg_type.IsChar()); EXPECT_FALSE(double_reg_type.IsShort()); EXPECT_FALSE(double_reg_type.IsInteger()); EXPECT_FALSE(double_reg_type.IsLong()); EXPECT_FALSE(double_reg_type.IsFloat()); EXPECT_TRUE(double_reg_type.IsDouble()); EXPECT_FALSE(double_reg_type.IsReference()); EXPECT_TRUE(double_reg_type.IsLowHalf()); EXPECT_FALSE(double_reg_type.IsHighHalf()); EXPECT_TRUE(double_reg_type.IsLongOrDoubleTypes()); EXPECT_FALSE(double_reg_type.IsReferenceTypes()); EXPECT_FALSE(double_reg_type.IsCategory1Types()); EXPECT_TRUE(double_reg_type.IsCategory2Types()); EXPECT_FALSE(double_reg_type.IsBooleanTypes()); EXPECT_FALSE(double_reg_type.IsByteTypes()); EXPECT_FALSE(double_reg_type.IsShortTypes()); EXPECT_FALSE(double_reg_type.IsCharTypes()); EXPECT_FALSE(double_reg_type.IsIntegralTypes()); EXPECT_FALSE(double_reg_type.IsFloatTypes()); EXPECT_FALSE(double_reg_type.IsLongTypes()); EXPECT_TRUE(double_reg_type.IsDoubleTypes()); EXPECT_FALSE(double_reg_type.IsArrayIndexTypes()); EXPECT_FALSE(double_reg_type.IsNonZeroReferenceTypes()); EXPECT_TRUE(double_reg_type.HasClass()); } class RegTypeReferenceTest : public CommonRuntimeTest {}; TEST_F(RegTypeReferenceTest, JavalangObjectImprecise) { // Tests matching precisions. A reference type that was created precise doesn't // match the one that is imprecise. ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache(Runtime::Current()->GetClassLinker(), true, allocator); const RegType& imprecise_obj = cache.JavaLangObject(false); const RegType& precise_obj = cache.JavaLangObject(true); const RegType& precise_obj_2 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true); EXPECT_TRUE(precise_obj.Equals(precise_obj_2)); EXPECT_FALSE(imprecise_obj.Equals(precise_obj)); EXPECT_FALSE(imprecise_obj.Equals(precise_obj)); EXPECT_FALSE(imprecise_obj.Equals(precise_obj_2)); } TEST_F(RegTypeReferenceTest, UnresolvedType) { // Tests creating unresolved types. Miss for the first time asking the cache and // a hit second time. ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache(Runtime::Current()->GetClassLinker(), true, allocator); const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); EXPECT_TRUE(ref_type_0.IsNonZeroReferenceTypes()); const RegType& ref_type_1 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); EXPECT_TRUE(ref_type_0.Equals(ref_type_1)); const RegType& unresolved_super_class = cache.FromUnresolvedSuperClass(ref_type_0); EXPECT_TRUE(unresolved_super_class.IsUnresolvedSuperClass()); EXPECT_TRUE(unresolved_super_class.IsNonZeroReferenceTypes()); } TEST_F(RegTypeReferenceTest, UnresolvedUnintializedType) { // Tests creating types uninitialized types from unresolved types. ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache(Runtime::Current()->GetClassLinker(), true, allocator); const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); const RegType& ref_type = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); EXPECT_TRUE(ref_type_0.Equals(ref_type)); // Create an uninitialized type of this unresolved type const RegType& unresolved_unintialised = cache.Uninitialized(ref_type, 1101ull); EXPECT_TRUE(unresolved_unintialised.IsUnresolvedAndUninitializedReference()); EXPECT_TRUE(unresolved_unintialised.IsUninitializedTypes()); EXPECT_TRUE(unresolved_unintialised.IsNonZeroReferenceTypes()); // Create an uninitialized type of this unresolved type with different PC const RegType& ref_type_unresolved_unintialised_1 = cache.Uninitialized(ref_type, 1102ull); EXPECT_TRUE(unresolved_unintialised.IsUnresolvedAndUninitializedReference()); EXPECT_FALSE(unresolved_unintialised.Equals(ref_type_unresolved_unintialised_1)); // Create an uninitialized type of this unresolved type with the same PC const RegType& unresolved_unintialised_2 = cache.Uninitialized(ref_type, 1101ull); EXPECT_TRUE(unresolved_unintialised.Equals(unresolved_unintialised_2)); } TEST_F(RegTypeReferenceTest, Dump) { // Tests types for proper Dump messages. ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache(Runtime::Current()->GetClassLinker(), true, allocator); const RegType& unresolved_ref = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); const RegType& unresolved_ref_another = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExistEither;", true); const RegType& resolved_ref = cache.JavaLangString(); const RegType& resolved_unintialiesd = cache.Uninitialized(resolved_ref, 10); const RegType& unresolved_unintialized = cache.Uninitialized(unresolved_ref, 12); const RegType& unresolved_merged = cache.FromUnresolvedMerge( unresolved_ref, unresolved_ref_another, /* verifier= */ nullptr); std::string expected = "Unresolved Reference: java.lang.DoesNotExist"; EXPECT_EQ(expected, unresolved_ref.Dump()); expected = "Precise Reference: java.lang.String"; EXPECT_EQ(expected, resolved_ref.Dump()); expected ="Uninitialized Reference: java.lang.String Allocation PC: 10"; EXPECT_EQ(expected, resolved_unintialiesd.Dump()); expected = "Unresolved And Uninitialized Reference: java.lang.DoesNotExist Allocation PC: 12"; EXPECT_EQ(expected, unresolved_unintialized.Dump()); expected = "UnresolvedMergedReferences(Zero/null | Unresolved Reference: java.lang.DoesNotExist, Unresolved Reference: java.lang.DoesNotExistEither)"; EXPECT_EQ(expected, unresolved_merged.Dump()); } TEST_F(RegTypeReferenceTest, JavalangString) { // Add a class to the cache then look for the same class and make sure it is a // Hit the second time. Then check for the same effect when using // The JavaLangObject method instead of FromDescriptor. String class is final. ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache(Runtime::Current()->GetClassLinker(), true, allocator); const RegType& ref_type = cache.JavaLangString(); const RegType& ref_type_2 = cache.JavaLangString(); const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/String;", true); EXPECT_TRUE(ref_type.Equals(ref_type_2)); EXPECT_TRUE(ref_type_2.Equals(ref_type_3)); EXPECT_TRUE(ref_type.IsPreciseReference()); // Create an uninitialized type out of this: const RegType& ref_type_unintialized = cache.Uninitialized(ref_type, 0110ull); EXPECT_TRUE(ref_type_unintialized.IsUninitializedReference()); EXPECT_FALSE(ref_type_unintialized.IsUnresolvedAndUninitializedReference()); } TEST_F(RegTypeReferenceTest, JavalangObject) { // Add a class to the cache then look for the same class and make sure it is a // Hit the second time. Then I am checking for the same effect when using // The JavaLangObject method instead of FromDescriptor. Object Class in not final. ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache(Runtime::Current()->GetClassLinker(), true, allocator); const RegType& ref_type = cache.JavaLangObject(true); const RegType& ref_type_2 = cache.JavaLangObject(true); const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true); EXPECT_TRUE(ref_type.Equals(ref_type_2)); EXPECT_TRUE(ref_type_3.Equals(ref_type_2)); EXPECT_EQ(ref_type.GetId(), ref_type_3.GetId()); } TEST_F(RegTypeReferenceTest, Merging) { // Tests merging logic // String and object , LUB is object. ScopedObjectAccess soa(Thread::Current()); ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); RegTypeCache cache_new(Runtime::Current()->GetClassLinker(), true, allocator); const RegType& string = cache_new.JavaLangString(); const RegType& Object = cache_new.JavaLangObject(true); EXPECT_TRUE(string.Merge(Object, &cache_new, /* verifier= */ nullptr).IsJavaLangObject()); // Merge two unresolved types. const RegType& ref_type_0 = cache_new.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); const RegType& ref_type_1 = cache_new.FromDescriptor(nullptr, "Ljava/lang/DoesNotExistToo;", true); EXPECT_FALSE(ref_type_0.Equals(ref_type_1)); const RegType& merged = ref_type_1.Merge(ref_type_0, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsUnresolvedMergedReference()); RegType& merged_nonconst = const_cast(merged); const BitVector& unresolved_parts = down_cast(&merged_nonconst)->GetUnresolvedTypes(); EXPECT_TRUE(unresolved_parts.IsBitSet(ref_type_0.GetId())); EXPECT_TRUE(unresolved_parts.IsBitSet(ref_type_1.GetId())); } TEST_F(RegTypeTest, MergingFloat) { // Testing merging logic with float and float constants. ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache_new(Runtime::Current()->GetClassLinker(), true, allocator); constexpr int32_t kTestConstantValue = 10; const RegType& float_type = cache_new.Float(); const RegType& precise_cst = cache_new.FromCat1Const(kTestConstantValue, true); const RegType& imprecise_cst = cache_new.FromCat1Const(kTestConstantValue, false); { // float MERGE precise cst => float. const RegType& merged = float_type.Merge(precise_cst, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsFloat()); } { // precise cst MERGE float => float. const RegType& merged = precise_cst.Merge(float_type, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsFloat()); } { // float MERGE imprecise cst => float. const RegType& merged = float_type.Merge(imprecise_cst, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsFloat()); } { // imprecise cst MERGE float => float. const RegType& merged = imprecise_cst.Merge(float_type, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsFloat()); } } TEST_F(RegTypeTest, MergingLong) { // Testing merging logic with long and long constants. ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache_new(Runtime::Current()->GetClassLinker(), true, allocator); constexpr int32_t kTestConstantValue = 10; const RegType& long_lo_type = cache_new.LongLo(); const RegType& long_hi_type = cache_new.LongHi(); const RegType& precise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, true); const RegType& imprecise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, false); const RegType& precise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, true); const RegType& imprecise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, false); { // lo MERGE precise cst lo => lo. const RegType& merged = long_lo_type.Merge(precise_cst_lo, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsLongLo()); } { // precise cst lo MERGE lo => lo. const RegType& merged = precise_cst_lo.Merge(long_lo_type, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsLongLo()); } { // lo MERGE imprecise cst lo => lo. const RegType& merged = long_lo_type.Merge( imprecise_cst_lo, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsLongLo()); } { // imprecise cst lo MERGE lo => lo. const RegType& merged = imprecise_cst_lo.Merge( long_lo_type, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsLongLo()); } { // hi MERGE precise cst hi => hi. const RegType& merged = long_hi_type.Merge(precise_cst_hi, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsLongHi()); } { // precise cst hi MERGE hi => hi. const RegType& merged = precise_cst_hi.Merge(long_hi_type, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsLongHi()); } { // hi MERGE imprecise cst hi => hi. const RegType& merged = long_hi_type.Merge( imprecise_cst_hi, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsLongHi()); } { // imprecise cst hi MERGE hi => hi. const RegType& merged = imprecise_cst_hi.Merge( long_hi_type, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsLongHi()); } } TEST_F(RegTypeTest, MergingDouble) { // Testing merging logic with double and double constants. ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache_new(Runtime::Current()->GetClassLinker(), true, allocator); constexpr int32_t kTestConstantValue = 10; const RegType& double_lo_type = cache_new.DoubleLo(); const RegType& double_hi_type = cache_new.DoubleHi(); const RegType& precise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, true); const RegType& imprecise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, false); const RegType& precise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, true); const RegType& imprecise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, false); { // lo MERGE precise cst lo => lo. const RegType& merged = double_lo_type.Merge( precise_cst_lo, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsDoubleLo()); } { // precise cst lo MERGE lo => lo. const RegType& merged = precise_cst_lo.Merge( double_lo_type, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsDoubleLo()); } { // lo MERGE imprecise cst lo => lo. const RegType& merged = double_lo_type.Merge( imprecise_cst_lo, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsDoubleLo()); } { // imprecise cst lo MERGE lo => lo. const RegType& merged = imprecise_cst_lo.Merge( double_lo_type, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsDoubleLo()); } { // hi MERGE precise cst hi => hi. const RegType& merged = double_hi_type.Merge( precise_cst_hi, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsDoubleHi()); } { // precise cst hi MERGE hi => hi. const RegType& merged = precise_cst_hi.Merge( double_hi_type, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsDoubleHi()); } { // hi MERGE imprecise cst hi => hi. const RegType& merged = double_hi_type.Merge( imprecise_cst_hi, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsDoubleHi()); } { // imprecise cst hi MERGE hi => hi. const RegType& merged = imprecise_cst_hi.Merge( double_hi_type, &cache_new, /* verifier= */ nullptr); EXPECT_TRUE(merged.IsDoubleHi()); } } // Without a running MethodVerifier, the class-bearing register types may become stale as the GC // will not visit them. It is easiest to disable moving GC. // // For some of the tests we need (or want) a working RegTypeCache that can load classes. So it is // not generally possible to disable GC using ScopedGCCriticalSection (as it blocks GC and // suspension completely). struct ScopedDisableMovingGC { explicit ScopedDisableMovingGC(Thread* t) : self(t) { Runtime::Current()->GetHeap()->IncrementDisableMovingGC(self); } ~ScopedDisableMovingGC() { Runtime::Current()->GetHeap()->DecrementDisableMovingGC(self); } Thread* self; }; TEST_F(RegTypeTest, MergeSemiLatticeRef) { // (Incomplete) semilattice: // // Excluded for now: * category-2 types // * interfaces // * all of category-1 primitive types, including constants. // This is to demonstrate/codify the reference side, mostly. // // Note: It is not a real semilattice because int = float makes this wonky. :-( // // Conflict // | // #---------#--------------------------#-----------------------------# // | | | // | | Object // | | | // int uninit types #---------------#--------#------------------#---------# // | | | | | | // | unresolved-merge-types | Object[] char[] byte[] // | | | | | | | | // | unresolved-types | #------Number #---------# | | // | | | | | | | | // | | #--------Integer Number[] Number[][] | | // | | | | | | | // | #---------------#--------#---------#--------#---------# // | | // | null // | | // #--------------------------#----------------------------# // | // 0 ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); ScopedDisableMovingGC no_gc(soa.Self()); RegTypeCache cache(Runtime::Current()->GetClassLinker(), true, allocator); const RegType& conflict = cache.Conflict(); const RegType& zero = cache.Zero(); const RegType& null = cache.Null(); const RegType& int_type = cache.Integer(); const RegType& obj = cache.JavaLangObject(false); const RegType& obj_arr = cache.From(nullptr, "[Ljava/lang/Object;", false); ASSERT_FALSE(obj_arr.IsUnresolvedReference()); const RegType& unresolved_a = cache.From(nullptr, "Ldoes/not/resolve/A;", false); ASSERT_TRUE(unresolved_a.IsUnresolvedReference()); const RegType& unresolved_b = cache.From(nullptr, "Ldoes/not/resolve/B;", false); ASSERT_TRUE(unresolved_b.IsUnresolvedReference()); const RegType& unresolved_ab = cache.FromUnresolvedMerge(unresolved_a, unresolved_b, nullptr); ASSERT_TRUE(unresolved_ab.IsUnresolvedMergedReference()); const RegType& uninit_this = cache.UninitializedThisArgument(obj); const RegType& uninit_obj_0 = cache.Uninitialized(obj, 0u); const RegType& uninit_obj_1 = cache.Uninitialized(obj, 1u); const RegType& uninit_unres_this = cache.UninitializedThisArgument(unresolved_a); const RegType& uninit_unres_a_0 = cache.Uninitialized(unresolved_a, 0); const RegType& uninit_unres_b_0 = cache.Uninitialized(unresolved_b, 0); const RegType& number = cache.From(nullptr, "Ljava/lang/Number;", false); ASSERT_FALSE(number.IsUnresolvedReference()); const RegType& integer = cache.From(nullptr, "Ljava/lang/Integer;", false); ASSERT_FALSE(integer.IsUnresolvedReference()); const RegType& uninit_number_0 = cache.Uninitialized(number, 0u); const RegType& uninit_integer_0 = cache.Uninitialized(integer, 0u); const RegType& number_arr = cache.From(nullptr, "[Ljava/lang/Number;", false); ASSERT_FALSE(number_arr.IsUnresolvedReference()); const RegType& integer_arr = cache.From(nullptr, "[Ljava/lang/Integer;", false); ASSERT_FALSE(integer_arr.IsUnresolvedReference()); const RegType& number_arr_arr = cache.From(nullptr, "[[Ljava/lang/Number;", false); ASSERT_FALSE(number_arr_arr.IsUnresolvedReference()); const RegType& char_arr = cache.From(nullptr, "[C", false); ASSERT_FALSE(char_arr.IsUnresolvedReference()); const RegType& byte_arr = cache.From(nullptr, "[B", false); ASSERT_FALSE(byte_arr.IsUnresolvedReference()); const RegType& unresolved_a_num = cache.FromUnresolvedMerge(unresolved_a, number, nullptr); ASSERT_TRUE(unresolved_a_num.IsUnresolvedMergedReference()); const RegType& unresolved_b_num = cache.FromUnresolvedMerge(unresolved_b, number, nullptr); ASSERT_TRUE(unresolved_b_num.IsUnresolvedMergedReference()); const RegType& unresolved_ab_num = cache.FromUnresolvedMerge(unresolved_ab, number, nullptr); ASSERT_TRUE(unresolved_ab_num.IsUnresolvedMergedReference()); const RegType& unresolved_a_int = cache.FromUnresolvedMerge(unresolved_a, integer, nullptr); ASSERT_TRUE(unresolved_a_int.IsUnresolvedMergedReference()); const RegType& unresolved_b_int = cache.FromUnresolvedMerge(unresolved_b, integer, nullptr); ASSERT_TRUE(unresolved_b_int.IsUnresolvedMergedReference()); const RegType& unresolved_ab_int = cache.FromUnresolvedMerge(unresolved_ab, integer, nullptr); ASSERT_TRUE(unresolved_ab_int.IsUnresolvedMergedReference()); std::vector uninitialized_types = { &uninit_this, &uninit_obj_0, &uninit_obj_1, &uninit_number_0, &uninit_integer_0 }; std::vector unresolved_types = { &unresolved_a, &unresolved_b, &unresolved_ab, &unresolved_a_num, &unresolved_b_num, &unresolved_ab_num, &unresolved_a_int, &unresolved_b_int, &unresolved_ab_int }; std::vector uninit_unresolved_types = { &uninit_unres_this, &uninit_unres_a_0, &uninit_unres_b_0 }; std::vector plain_nonobj_classes = { &number, &integer }; std::vector plain_nonobj_arr_classes = { &number_arr, &number_arr_arr, &integer_arr, &char_arr, }; // std::vector others = { &conflict, &zero, &null, &obj, &int_type }; std::vector all_minus_uninit_conflict; all_minus_uninit_conflict.insert(all_minus_uninit_conflict.end(), unresolved_types.begin(), unresolved_types.end()); all_minus_uninit_conflict.insert(all_minus_uninit_conflict.end(), plain_nonobj_classes.begin(), plain_nonobj_classes.end()); all_minus_uninit_conflict.insert(all_minus_uninit_conflict.end(), plain_nonobj_arr_classes.begin(), plain_nonobj_arr_classes.end()); all_minus_uninit_conflict.push_back(&zero); all_minus_uninit_conflict.push_back(&null); all_minus_uninit_conflict.push_back(&obj); std::vector all_minus_uninit; all_minus_uninit.insert(all_minus_uninit.end(), all_minus_uninit_conflict.begin(), all_minus_uninit_conflict.end()); all_minus_uninit.push_back(&conflict); std::vector all; all.insert(all.end(), uninitialized_types.begin(), uninitialized_types.end()); all.insert(all.end(), uninit_unresolved_types.begin(), uninit_unresolved_types.end()); all.insert(all.end(), all_minus_uninit.begin(), all_minus_uninit.end()); all.push_back(&int_type); auto check = [&](const RegType& in1, const RegType& in2, const RegType& expected_out) REQUIRES_SHARED(Locks::mutator_lock_) { const RegType& merge_result = in1.SafeMerge(in2, &cache, nullptr); EXPECT_EQ(&expected_out, &merge_result) << in1.Dump() << " x " << in2.Dump() << " = " << merge_result.Dump() << " != " << expected_out.Dump(); }; // Identity. { for (auto r : all) { check(*r, *r, *r); } } // Define a covering relation through a list of Edges. We'll then derive LUBs from this and // create checks for every pair of types. struct Edge { const RegType& from; const RegType& to; Edge(const RegType& from_, const RegType& to_) : from(from_), to(to_) {} }; std::vector edges; #define ADD_EDGE(from, to) edges.emplace_back((from), (to)) // To Conflict. { for (auto r : uninitialized_types) { ADD_EDGE(*r, conflict); } for (auto r : uninit_unresolved_types) { ADD_EDGE(*r, conflict); } ADD_EDGE(obj, conflict); ADD_EDGE(int_type, conflict); } ADD_EDGE(zero, null); // Unresolved. { ADD_EDGE(null, unresolved_a); ADD_EDGE(null, unresolved_b); ADD_EDGE(unresolved_a, unresolved_ab); ADD_EDGE(unresolved_b, unresolved_ab); ADD_EDGE(number, unresolved_a_num); ADD_EDGE(unresolved_a, unresolved_a_num); ADD_EDGE(number, unresolved_b_num); ADD_EDGE(unresolved_b, unresolved_b_num); ADD_EDGE(number, unresolved_ab_num); ADD_EDGE(unresolved_a_num, unresolved_ab_num); ADD_EDGE(unresolved_b_num, unresolved_ab_num); ADD_EDGE(unresolved_ab, unresolved_ab_num); ADD_EDGE(integer, unresolved_a_int); ADD_EDGE(unresolved_a, unresolved_a_int); ADD_EDGE(integer, unresolved_b_int); ADD_EDGE(unresolved_b, unresolved_b_int); ADD_EDGE(integer, unresolved_ab_int); ADD_EDGE(unresolved_a_int, unresolved_ab_int); ADD_EDGE(unresolved_b_int, unresolved_ab_int); ADD_EDGE(unresolved_ab, unresolved_ab_int); ADD_EDGE(unresolved_a_int, unresolved_a_num); ADD_EDGE(unresolved_b_int, unresolved_b_num); ADD_EDGE(unresolved_ab_int, unresolved_ab_num); ADD_EDGE(unresolved_ab_num, obj); } // Classes. { ADD_EDGE(null, integer); ADD_EDGE(integer, number); ADD_EDGE(number, obj); } // Arrays. { ADD_EDGE(integer_arr, number_arr); ADD_EDGE(number_arr, obj_arr); ADD_EDGE(obj_arr, obj); ADD_EDGE(number_arr_arr, obj_arr); ADD_EDGE(char_arr, obj); ADD_EDGE(byte_arr, obj); ADD_EDGE(null, integer_arr); ADD_EDGE(null, number_arr_arr); ADD_EDGE(null, char_arr); ADD_EDGE(null, byte_arr); } // Primitive. { ADD_EDGE(zero, int_type); } #undef ADD_EDGE // Create merge triples by using the covering relation established by edges to derive the // expected merge for any pair of types. // Expect merge(in1, in2) == out. struct MergeExpectation { const RegType& in1; const RegType& in2; const RegType& out; MergeExpectation(const RegType& in1_, const RegType& in2_, const RegType& out_) : in1(in1_), in2(in2_), out(out_) {} }; std::vector expectations; for (auto r1 : all) { for (auto r2 : all) { if (r1 == r2) { continue; } // Very simple algorithm here that is usually used with adjacency lists. Our graph is // small, it didn't make sense to have lists per node. Thus, the regular guarantees // of O(n + |e|) don't apply, but that is acceptable. // // To compute r1 lub r2 = merge(r1, r2): // 1) Generate the reachable set of r1, name it grey. // 2) Mark all grey reachable nodes of r2 as black. // 3) Find black nodes with no in-edges from other black nodes. // 4) If |3)| == 1, that's the lub. // Generic BFS of the graph induced by edges, starting at start. new_node will be called // with any discovered node, in order. auto bfs = [&](auto new_node, const RegType* start) { std::unordered_set seen; std::queue work_list; work_list.push(start); while (!work_list.empty()) { const RegType* cur = work_list.front(); work_list.pop(); auto it = seen.find(cur); if (it != seen.end()) { continue; } seen.insert(cur); new_node(cur); for (const Edge& edge : edges) { if (&edge.from == cur) { work_list.push(&edge.to); } } } }; std::unordered_set grey; auto compute_grey = [&](const RegType* cur) { grey.insert(cur); // Mark discovered node as grey. }; bfs(compute_grey, r1); std::set black; auto compute_black = [&](const RegType* cur) { // Mark discovered grey node as black. if (grey.find(cur) != grey.end()) { black.insert(cur); } }; bfs(compute_black, r2); std::set no_in_edge(black); // Copy of black, remove nodes with in-edges. for (auto r : black) { for (Edge& e : edges) { if (&e.from == r) { no_in_edge.erase(&e.to); // It doesn't matter whether "to" is black or not, just // attempt to remove it. } } } // Helper to print sets when something went wrong. auto print_set = [](auto& container) REQUIRES_SHARED(Locks::mutator_lock_) { std::string result; for (auto r : container) { result.append(" + "); result.append(r->Dump()); } return result; }; ASSERT_EQ(no_in_edge.size(), 1u) << r1->Dump() << " u " << r2->Dump() << " grey=" << print_set(grey) << " black=" << print_set(black) << " no-in-edge=" << print_set(no_in_edge); expectations.emplace_back(*r1, *r2, **no_in_edge.begin()); } } // Evaluate merge expectations. The merge is expected to be commutative. for (auto& triple : expectations) { check(triple.in1, triple.in2, triple.out); check(triple.in2, triple.in1, triple.out); } } TEST_F(RegTypeTest, ConstPrecision) { // Tests creating primitive types types. ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); RegTypeCache cache_new(Runtime::Current()->GetClassLinker(), true, allocator); const RegType& imprecise_const = cache_new.FromCat1Const(10, false); const RegType& precise_const = cache_new.FromCat1Const(10, true); EXPECT_TRUE(imprecise_const.IsImpreciseConstant()); EXPECT_TRUE(precise_const.IsPreciseConstant()); EXPECT_FALSE(imprecise_const.Equals(precise_const)); } class RegTypeOOMTest : public RegTypeTest { protected: void SetUpRuntimeOptions(RuntimeOptions *options) override { SetUpRuntimeOptionsForFillHeap(options); // We must not appear to be a compiler, or we'll abort on the host. callbacks_.reset(); } }; TEST_F(RegTypeOOMTest, ClassJoinOOM) { // TODO: Figure out why FillHeap isn't good enough under CMS. TEST_DISABLED_WITHOUT_BAKER_READ_BARRIERS(); // Tests that we don't abort with OOMs. ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); ScopedDisableMovingGC no_gc(soa.Self()); // We merge nested array of primitive wrappers. These have a join type of an array of Number of // the same depth. We start with depth five, as we want at least two newly created classes to // test recursion (it's just more likely that nobody uses such deep arrays in runtime bringup). constexpr const char* kIntArrayFive = "[[[[[Ljava/lang/Integer;"; constexpr const char* kFloatArrayFive = "[[[[[Ljava/lang/Float;"; constexpr const char* kNumberArrayFour = "[[[[Ljava/lang/Number;"; constexpr const char* kNumberArrayFive = "[[[[[Ljava/lang/Number;"; RegTypeCache cache(Runtime::Current()->GetClassLinker(), true, allocator); const RegType& int_array_array = cache.From(nullptr, kIntArrayFive, false); ASSERT_TRUE(int_array_array.HasClass()); const RegType& float_array_array = cache.From(nullptr, kFloatArrayFive, false); ASSERT_TRUE(float_array_array.HasClass()); // Check assumptions: the joined classes don't exist, yet. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ASSERT_TRUE(class_linker->LookupClass(soa.Self(), kNumberArrayFour, nullptr) == nullptr); ASSERT_TRUE(class_linker->LookupClass(soa.Self(), kNumberArrayFive, nullptr) == nullptr); // Fill the heap. VariableSizedHandleScope hs(soa.Self()); FillHeap(soa.Self(), class_linker, &hs); const RegType& join_type = int_array_array.Merge(float_array_array, &cache, nullptr); ASSERT_TRUE(join_type.IsUnresolvedReference()); } class RegTypeClassJoinTest : public RegTypeTest { protected: void TestClassJoin(const char* in1, const char* in2, const char* out) { ArenaStack stack(Runtime::Current()->GetArenaPool()); ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("Interfaces"); StackHandleScope<4> hs(soa.Self()); Handle class_loader( hs.NewHandle(soa.Decode(jclass_loader))); Handle c1(hs.NewHandle( class_linker_->FindClass(soa.Self(), in1, class_loader))); Handle c2(hs.NewHandle( class_linker_->FindClass(soa.Self(), in2, class_loader))); ASSERT_TRUE(c1 != nullptr); ASSERT_TRUE(c2 != nullptr); ScopedDisableMovingGC no_gc(soa.Self()); RegTypeCache cache(Runtime::Current()->GetClassLinker(), true, allocator); const RegType& c1_reg_type = *cache.InsertClass(in1, c1.Get(), false); const RegType& c2_reg_type = *cache.InsertClass(in2, c2.Get(), false); const RegType& join_type = c1_reg_type.Merge(c2_reg_type, &cache, nullptr); EXPECT_TRUE(join_type.HasClass()); EXPECT_EQ(join_type.GetDescriptor(), std::string_view(out)); } }; TEST_F(RegTypeClassJoinTest, ClassJoinInterfaces) { TestClassJoin("LInterfaces$K;", "LInterfaces$L;", "LInterfaces$J;"); } TEST_F(RegTypeClassJoinTest, ClassJoinInterfaceClass) { TestClassJoin("LInterfaces$B;", "LInterfaces$L;", "LInterfaces$J;"); } TEST_F(RegTypeClassJoinTest, ClassJoinClassClass) { // This test codifies that we prefer the class hierarchy over interfaces. It's a mostly // arbitrary choice, optimally we'd have set types and could handle multi-inheritance precisely. TestClassJoin("LInterfaces$A;", "LInterfaces$B;", "Ljava/lang/Object;"); } } // namespace verifier } // namespace art