diff options
46 files changed, 2256 insertions, 375 deletions
diff --git a/Android.mk b/Android.mk index 0e86188063..f8c537816f 100644 --- a/Android.mk +++ b/Android.mk @@ -388,6 +388,7 @@ build-art-target: $(TARGET_OUT_EXECUTABLES)/art $(ART_TARGET_DEPENDENCIES) $(TAR # libstdc++ is needed when building for ART_TARGET_LINUX. ART_TARGET_SHARED_LIBRARY_BENCHMARK := $(TARGET_OUT_SHARED_LIBRARIES)/libartbenchmark.so build-art-target-golem: dex2oat dalvikvm patchoat linker libstdc++ \ + $(TARGET_OUT_EXECUTABLES)/art \ $(TARGET_OUT)/etc/public.libraries.txt \ $(ART_TARGET_DEX_DEPENDENCIES) \ $(ART_TARGET_SHARED_LIBRARY_DEPENDENCIES) \ diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk index 1591e34885..27ec8b3f67 100644 --- a/build/Android.common_test.mk +++ b/build/Android.common_test.mk @@ -54,11 +54,11 @@ ART_TEST_FULL ?= false ART_TEST_QUIET ?= true # Do you want interpreter tests run? -ART_TEST_INTERPRETER ?= $(ART_TEST_FULL) -ART_TEST_INTERPRETER_ACCESS_CHECKS ?= $(ART_TEST_FULL) +ART_TEST_INTERPRETER ?= true +ART_TEST_INTERPRETER_ACCESS_CHECKS ?= true # Do you want JIT tests run? -ART_TEST_JIT ?= $(ART_TEST_FULL) +ART_TEST_JIT ?= true # Do you want optimizing compiler tests run? ART_TEST_OPTIMIZING ?= true diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 4814b224ad..5246dbc5cb 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -5110,12 +5110,34 @@ void InstructionCodeGeneratorMIPS64::VisitPackedSwitch(HPackedSwitch* switch_ins } } -void LocationsBuilderMIPS64::VisitClassTableGet(HClassTableGet*) { - UNIMPLEMENTED(FATAL) << "ClassTableGet is unimplemented on mips64"; +void LocationsBuilderMIPS64::VisitClassTableGet(HClassTableGet* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorMIPS64::VisitClassTableGet(HClassTableGet*) { - UNIMPLEMENTED(FATAL) << "ClassTableGet is unimplemented on mips64"; +void InstructionCodeGeneratorMIPS64::VisitClassTableGet(HClassTableGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { + uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( + instruction->GetIndex(), kMips64PointerSize).SizeValue(); + __ LoadFromOffset(kLoadDoubleword, + locations->Out().AsRegister<GpuRegister>(), + locations->InAt(0).AsRegister<GpuRegister>(), + method_offset); + } else { + uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement( + instruction->GetIndex(), kMips64PointerSize)); + __ LoadFromOffset(kLoadDoubleword, + locations->Out().AsRegister<GpuRegister>(), + locations->InAt(0).AsRegister<GpuRegister>(), + mirror::Class::ImtPtrOffset(kMips64PointerSize).Uint32Value()); + __ LoadFromOffset(kLoadDoubleword, + locations->Out().AsRegister<GpuRegister>(), + locations->Out().AsRegister<GpuRegister>(), + method_offset); + } } } // namespace mips64 diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 583008bbe8..8a813bd54c 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -892,10 +892,6 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget( return false; } - if (graph_->GetInstructionSet() == kMips64) { - // TODO: Support HClassTableGet for mips64. - return false; - } ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); PointerSize pointer_size = class_linker->GetImagePointerSize(); diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index 6a57f45e42..5307dc09d9 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -1169,6 +1169,32 @@ void X86Assembler::pand(XmmRegister dst, XmmRegister src) { } +void X86Assembler::andnpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x55); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::andnps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x55); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::pandn(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xDF); + EmitXmmRegisterOperand(dst, src); +} + + void X86Assembler::orpd(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); @@ -1195,6 +1221,43 @@ void X86Assembler::por(XmmRegister dst, XmmRegister src) { } +void X86Assembler::pcmpeqb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x74); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::pcmpeqw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x75); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::pcmpeqd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x76); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::pcmpeqq(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x29); + EmitXmmRegisterOperand(dst, src); +} + + void X86Assembler::shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index e3c123ccaf..f52cf16c8b 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -487,10 +487,19 @@ class X86Assembler FINAL : public Assembler { void andps(XmmRegister dst, const Address& src); void pand(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void andnpd(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void andnps(XmmRegister dst, XmmRegister src); + void pandn(XmmRegister dst, XmmRegister src); + void orpd(XmmRegister dst, XmmRegister src); // no addr variant (for now) void orps(XmmRegister dst, XmmRegister src); void por(XmmRegister dst, XmmRegister src); + void pcmpeqb(XmmRegister dst, XmmRegister src); + void pcmpeqw(XmmRegister dst, XmmRegister src); + void pcmpeqd(XmmRegister dst, XmmRegister src); + void pcmpeqq(XmmRegister dst, XmmRegister src); + void shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm); void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm); void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index 110d0dcd05..23049079e0 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -581,6 +581,18 @@ TEST_F(AssemblerX86Test, PAnd) { DriverStr(RepeatFF(&x86::X86Assembler::pand, "pand %{reg2}, %{reg1}"), "pand"); } +TEST_F(AssemblerX86Test, AndnPD) { + DriverStr(RepeatFF(&x86::X86Assembler::andnpd, "andnpd %{reg2}, %{reg1}"), "andnpd"); +} + +TEST_F(AssemblerX86Test, AndnPS) { + DriverStr(RepeatFF(&x86::X86Assembler::andnps, "andnps %{reg2}, %{reg1}"), "andnps"); +} + +TEST_F(AssemblerX86Test, PAndn) { + DriverStr(RepeatFF(&x86::X86Assembler::pandn, "pandn %{reg2}, %{reg1}"), "pandn"); +} + TEST_F(AssemblerX86Test, OrPD) { DriverStr(RepeatFF(&x86::X86Assembler::orpd, "orpd %{reg2}, %{reg1}"), "orpd"); } @@ -593,6 +605,22 @@ TEST_F(AssemblerX86Test, POr) { DriverStr(RepeatFF(&x86::X86Assembler::por, "por %{reg2}, %{reg1}"), "por"); } +TEST_F(AssemblerX86Test, PCmpeqB) { + DriverStr(RepeatFF(&x86::X86Assembler::pcmpeqb, "pcmpeqb %{reg2}, %{reg1}"), "cmpeqb"); +} + +TEST_F(AssemblerX86Test, PCmpeqW) { + DriverStr(RepeatFF(&x86::X86Assembler::pcmpeqw, "pcmpeqw %{reg2}, %{reg1}"), "cmpeqw"); +} + +TEST_F(AssemblerX86Test, PCmpeqD) { + DriverStr(RepeatFF(&x86::X86Assembler::pcmpeqd, "pcmpeqd %{reg2}, %{reg1}"), "cmpeqd"); +} + +TEST_F(AssemblerX86Test, PCmpeqQ) { + DriverStr(RepeatFF(&x86::X86Assembler::pcmpeqq, "pcmpeqq %{reg2}, %{reg1}"), "cmpeqq"); +} + TEST_F(AssemblerX86Test, ShufPS) { DriverStr(RepeatFFI(&x86::X86Assembler::shufps, 1, "shufps ${imm}, %{reg2}, %{reg1}"), "shufps"); } diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 688fdcc37d..d20a6965c3 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -1375,6 +1375,32 @@ void X86_64Assembler::pand(XmmRegister dst, XmmRegister src) { EmitXmmRegisterOperand(dst.LowBits(), src); } +void X86_64Assembler::andnpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x55); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::andnps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x55); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::pandn(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xDF); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + void X86_64Assembler::orpd(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); @@ -1401,6 +1427,43 @@ void X86_64Assembler::por(XmmRegister dst, XmmRegister src) { EmitXmmRegisterOperand(dst.LowBits(), src); } +void X86_64Assembler::pcmpeqb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x74); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::pcmpeqw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x75); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::pcmpeqd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x76); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::pcmpeqq(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x29); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + void X86_64Assembler::shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 480e7116eb..08e17e81e5 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -515,10 +515,19 @@ class X86_64Assembler FINAL : public Assembler { void andps(XmmRegister dst, XmmRegister src); // no addr variant (for now) void pand(XmmRegister dst, XmmRegister src); + void andnpd(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void andnps(XmmRegister dst, XmmRegister src); + void pandn(XmmRegister dst, XmmRegister src); + void orpd(XmmRegister dst, XmmRegister src); // no addr variant (for now) void orps(XmmRegister dst, XmmRegister src); void por(XmmRegister dst, XmmRegister src); + void pcmpeqb(XmmRegister dst, XmmRegister src); + void pcmpeqw(XmmRegister dst, XmmRegister src); + void pcmpeqd(XmmRegister dst, XmmRegister src); + void pcmpeqq(XmmRegister dst, XmmRegister src); + void shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm); void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm); void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index ba011c968e..20062fdb07 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -1269,6 +1269,18 @@ TEST_F(AssemblerX86_64Test, Pand) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::pand, "pand %{reg2}, %{reg1}"), "pand"); } +TEST_F(AssemblerX86_64Test, andnpd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::andnpd, "andnpd %{reg2}, %{reg1}"), "andnpd"); +} + +TEST_F(AssemblerX86_64Test, andnps) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::andnps, "andnps %{reg2}, %{reg1}"), "andnps"); +} + +TEST_F(AssemblerX86_64Test, Pandn) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::pandn, "pandn %{reg2}, %{reg1}"), "pandn"); +} + TEST_F(AssemblerX86_64Test, Orps) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::orps, "orps %{reg2}, %{reg1}"), "orps"); } @@ -1281,6 +1293,22 @@ TEST_F(AssemblerX86_64Test, Por) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::por, "por %{reg2}, %{reg1}"), "por"); } +TEST_F(AssemblerX86_64Test, PCmpeqb) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::pcmpeqb, "pcmpeqb %{reg2}, %{reg1}"), "pcmpeqb"); +} + +TEST_F(AssemblerX86_64Test, PCmpeqw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::pcmpeqw, "pcmpeqw %{reg2}, %{reg1}"), "pcmpeqw"); +} + +TEST_F(AssemblerX86_64Test, PCmpeqd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::pcmpeqd, "pcmpeqd %{reg2}, %{reg1}"), "pcmpeqd"); +} + +TEST_F(AssemblerX86_64Test, PCmpeqq) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::pcmpeqq, "pcmpeqq %{reg2}, %{reg1}"), "pcmpeqq"); +} + TEST_F(AssemblerX86_64Test, Shufps) { DriverStr(RepeatFFI(&x86_64::X86_64Assembler::shufps, 1, "shufps ${imm}, %{reg2}, %{reg1}"), "shufps"); } diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 01c33591e5..1a1d163304 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -714,12 +714,12 @@ TEST_F(VerifierDepsTest, MoveException_Unresolved) { TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInReferenced) { ASSERT_TRUE(VerifyMethod("StaticField_Resolved_DeclaredInReferenced")); - ASSERT_TRUE(HasClass("Ljava/lang/System;", true, "public final")); + ASSERT_TRUE(HasClass("Ljava/lang/System;", true, "public")); ASSERT_TRUE(HasField("Ljava/lang/System;", "out", "Ljava/io/PrintStream;", true, - "public final static", + "public static", "Ljava/lang/System;")); } @@ -727,13 +727,13 @@ TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInSuperclass1) { ASSERT_TRUE(VerifyMethod("StaticField_Resolved_DeclaredInSuperclass1")); ASSERT_TRUE(HasClass("Ljava/util/SimpleTimeZone;", true, "public")); ASSERT_TRUE(HasField( - "Ljava/util/SimpleTimeZone;", "LONG", "I", true, "public final static", "Ljava/util/TimeZone;")); + "Ljava/util/SimpleTimeZone;", "LONG", "I", true, "public static", "Ljava/util/TimeZone;")); } TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInSuperclass2) { ASSERT_TRUE(VerifyMethod("StaticField_Resolved_DeclaredInSuperclass2")); ASSERT_TRUE(HasField( - "LMySimpleTimeZone;", "SHORT", "I", true, "public final static", "Ljava/util/TimeZone;")); + "LMySimpleTimeZone;", "SHORT", "I", true, "public static", "Ljava/util/TimeZone;")); } TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInInterface1) { @@ -743,7 +743,7 @@ TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInInterface1) { "PI_ENABLE_OUTPUT_ESCAPING", "Ljava/lang/String;", true, - "public final static", + "public static", "Ljavax/xml/transform/Result;")); } @@ -753,7 +753,7 @@ TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInInterface2) { "PI_ENABLE_OUTPUT_ESCAPING", "Ljava/lang/String;", true, - "public final static", + "public static", "Ljavax/xml/transform/Result;")); } @@ -763,7 +763,7 @@ TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInInterface3) { "PI_ENABLE_OUTPUT_ESCAPING", "Ljava/lang/String;", true, - "public final static", + "public static", "Ljavax/xml/transform/Result;")); } @@ -773,13 +773,13 @@ TEST_F(VerifierDepsTest, StaticField_Resolved_DeclaredInInterface4) { "ELEMENT_NODE", "S", true, - "public final static", + "public static", "Lorg/w3c/dom/Node;")); } TEST_F(VerifierDepsTest, StaticField_Unresolved_ReferrerInBoot) { ASSERT_TRUE(VerifyMethod("StaticField_Unresolved_ReferrerInBoot")); - ASSERT_TRUE(HasClass("Ljava/util/TimeZone;", true, "public abstract")); + ASSERT_TRUE(HasClass("Ljava/util/TimeZone;", true, "public")); ASSERT_TRUE(HasField("Ljava/util/TimeZone;", "x", "I", false)); } @@ -851,7 +851,7 @@ TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInReferenced) { TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInSuperclass1) { ASSERT_TRUE(VerifyMethod("InvokeStatic_Resolved_DeclaredInSuperclass1")); - ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public abstract")); + ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public")); ASSERT_TRUE(HasMethod("direct", "Ljavax/net/ssl/SSLSocket;", "setSocketImplFactory", @@ -874,7 +874,7 @@ TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInSuperclass2) { TEST_F(VerifierDepsTest, InvokeStatic_DeclaredInInterface1) { ASSERT_TRUE(VerifyMethod("InvokeStatic_DeclaredInInterface1")); - ASSERT_TRUE(HasClass("Ljava/util/Map$Entry;", true, "public abstract interface")); + ASSERT_TRUE(HasClass("Ljava/util/Map$Entry;", true, "public interface")); ASSERT_TRUE(HasMethod("direct", "Ljava/util/Map$Entry;", "comparingByKey", @@ -896,7 +896,7 @@ TEST_F(VerifierDepsTest, InvokeStatic_DeclaredInInterface2) { TEST_F(VerifierDepsTest, InvokeStatic_Unresolved1) { ASSERT_FALSE(VerifyMethod("InvokeStatic_Unresolved1")); - ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public abstract")); + ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public")); ASSERT_TRUE(HasMethod("direct", "Ljavax/net/ssl/SSLSocket;", "x", "()V", false)); } @@ -914,7 +914,7 @@ TEST_F(VerifierDepsTest, InvokeDirect_Resolved_DeclaredInReferenced) { TEST_F(VerifierDepsTest, InvokeDirect_Resolved_DeclaredInSuperclass1) { ASSERT_FALSE(VerifyMethod("InvokeDirect_Resolved_DeclaredInSuperclass1")); - ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public abstract")); + ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public")); ASSERT_TRUE(HasMethod("direct", "Ljavax/net/ssl/SSLSocket;", "checkOldImpl", @@ -932,7 +932,7 @@ TEST_F(VerifierDepsTest, InvokeDirect_Resolved_DeclaredInSuperclass2) { TEST_F(VerifierDepsTest, InvokeDirect_Unresolved1) { ASSERT_FALSE(VerifyMethod("InvokeDirect_Unresolved1")); - ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public abstract")); + ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public")); ASSERT_TRUE(HasMethod("direct", "Ljavax/net/ssl/SSLSocket;", "x", "()V", false)); } @@ -987,7 +987,7 @@ TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperinterface) { "size", "()I", true, - "public abstract", + "public", "Ljava/util/Set;")); } @@ -1016,13 +1016,13 @@ TEST_F(VerifierDepsTest, InvokeVirtual_ActuallyDirect) { TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInReferenced) { ASSERT_TRUE(VerifyMethod("InvokeInterface_Resolved_DeclaredInReferenced")); - ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public abstract interface")); + ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface")); ASSERT_TRUE(HasMethod("interface", "Ljava/lang/Runnable;", "run", "()V", true, - "public abstract", + "public", "Ljava/lang/Runnable;")); } @@ -1038,7 +1038,7 @@ TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInSuperinterface1) { "run", "()V", true, - "public abstract", + "public", "Ljava/lang/Runnable;")); } @@ -1049,13 +1049,13 @@ TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInSuperinterface2) { "isEmpty", "()Z", true, - "public abstract", + "public", "Ljava/util/Set;")); } TEST_F(VerifierDepsTest, InvokeInterface_Unresolved1) { ASSERT_FALSE(VerifyMethod("InvokeInterface_Unresolved1")); - ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public abstract interface")); + ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface")); ASSERT_TRUE(HasMethod("interface", "Ljava/lang/Runnable;", "x", "()V", false)); } @@ -1066,20 +1066,20 @@ TEST_F(VerifierDepsTest, InvokeInterface_Unresolved2) { TEST_F(VerifierDepsTest, InvokeSuper_ThisAssignable) { ASSERT_TRUE(VerifyMethod("InvokeSuper_ThisAssignable")); - ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public abstract interface")); + ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface")); ASSERT_TRUE(HasAssignable("Ljava/lang/Runnable;", "Ljava/lang/Thread;", true)); ASSERT_TRUE(HasMethod("interface", "Ljava/lang/Runnable;", "run", "()V", true, - "public abstract", + "public", "Ljava/lang/Runnable;")); } TEST_F(VerifierDepsTest, InvokeSuper_ThisNotAssignable) { ASSERT_FALSE(VerifyMethod("InvokeSuper_ThisNotAssignable")); - ASSERT_TRUE(HasClass("Ljava/lang/Integer;", true, "public final")); + ASSERT_TRUE(HasClass("Ljava/lang/Integer;", true, "public")); ASSERT_TRUE(HasAssignable("Ljava/lang/Integer;", "Ljava/lang/Thread;", false)); ASSERT_TRUE(HasMethod( "virtual", "Ljava/lang/Integer;", "intValue", "()I", true, "public", "Ljava/lang/Integer;")); @@ -1087,12 +1087,12 @@ TEST_F(VerifierDepsTest, InvokeSuper_ThisNotAssignable) { TEST_F(VerifierDepsTest, ArgumentType_ResolvedReferenceArray) { ASSERT_TRUE(VerifyMethod("ArgumentType_ResolvedReferenceArray")); - ASSERT_TRUE(HasClass("[Ljava/lang/Thread;", true, "public final abstract")); + ASSERT_TRUE(HasClass("[Ljava/lang/Thread;", true, "public")); } TEST_F(VerifierDepsTest, NewArray_Resolved) { ASSERT_TRUE(VerifyMethod("NewArray_Resolved")); - ASSERT_TRUE(HasClass("[Ljava/lang/IllegalStateException;", true, "public final abstract")); + ASSERT_TRUE(HasClass("[Ljava/lang/IllegalStateException;", true, "public")); } TEST_F(VerifierDepsTest, EncodeDecode) { diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index 96afb906c7..78ddde84a1 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -748,8 +748,7 @@ class ClassDef : public IndexedItem { const TypeId* ClassType() const { return class_type_; } uint32_t GetAccessFlags() const { return access_flags_; } const TypeId* Superclass() const { return superclass_; } - const TypeIdVector* Interfaces() - { return interfaces_ == nullptr ? nullptr : interfaces_->GetTypeList(); } + const TypeList* Interfaces() { return interfaces_; } uint32_t InterfacesOffset() { return interfaces_ == nullptr ? 0 : interfaces_->GetOffset(); } const StringId* SourceFile() const { return source_file_; } AnnotationsDirectoryItem* Annotations() const { return annotations_; } @@ -781,7 +780,7 @@ class TypeAddrPair { uint32_t GetAddress() const { return address_; } private: - const TypeId* type_id_; + const TypeId* type_id_; // This can be nullptr. uint32_t address_; DISALLOW_COPY_AND_ASSIGN(TypeAddrPair); diff --git a/dexlayout/dex_verify.cc b/dexlayout/dex_verify.cc index aec1d0c1b7..54581292ff 100644 --- a/dexlayout/dex_verify.cc +++ b/dexlayout/dex_verify.cc @@ -20,6 +20,8 @@ #include "dex_verify.h" +#include <inttypes.h> + #include "android-base/stringprintf.h" namespace art { @@ -32,7 +34,7 @@ bool VerifyOutputDexFile(dex_ir::Header* orig_header, dex_ir::Collections& orig = orig_header->GetCollections(); dex_ir::Collections& output = output_header->GetCollections(); - // Compare all id sections. + // Compare all id sections. They have a defined order that can't be changed by dexlayout. if (!VerifyIds(orig.StringIds(), output.StringIds(), "string ids", error_msg) || !VerifyIds(orig.TypeIds(), output.TypeIds(), "type ids", error_msg) || !VerifyIds(orig.ProtoIds(), output.ProtoIds(), "proto ids", error_msg) || @@ -40,6 +42,10 @@ bool VerifyOutputDexFile(dex_ir::Header* orig_header, !VerifyIds(orig.MethodIds(), output.MethodIds(), "method ids", error_msg)) { return false; } + // Compare class defs. The order may have been changed by dexlayout. + if (!VerifyClassDefs(orig.ClassDefs(), output.ClassDefs(), error_msg)) { + return false; + } return true; } @@ -49,7 +55,7 @@ template<class T> bool VerifyIds(std::vector<std::unique_ptr<T>>& orig, std::string* error_msg) { if (orig.size() != output.size()) { *error_msg = StringPrintf( - "Mismatched size for %s section, %zu vs %zu.", section_name, orig.size(), output.size()); + "Mismatched size for %s section: %zu vs %zu.", section_name, orig.size(), output.size()); return false; } for (size_t i = 0; i < orig.size(); ++i) { @@ -62,7 +68,7 @@ template<class T> bool VerifyIds(std::vector<std::unique_ptr<T>>& orig, bool VerifyId(dex_ir::StringId* orig, dex_ir::StringId* output, std::string* error_msg) { if (strcmp(orig->Data(), output->Data()) != 0) { - *error_msg = StringPrintf("Mismatched string data for string id %u @ orig offset %x, %s vs %s.", + *error_msg = StringPrintf("Mismatched string data for string id %u at offset %x: %s vs %s.", orig->GetIndex(), orig->GetOffset(), orig->Data(), @@ -74,7 +80,7 @@ bool VerifyId(dex_ir::StringId* orig, dex_ir::StringId* output, std::string* err bool VerifyId(dex_ir::TypeId* orig, dex_ir::TypeId* output, std::string* error_msg) { if (orig->GetStringId()->GetIndex() != output->GetStringId()->GetIndex()) { - *error_msg = StringPrintf("Mismatched string index for type id %u @ orig offset %x, %u vs %u.", + *error_msg = StringPrintf("Mismatched string index for type id %u at offset %x: %u vs %u.", orig->GetIndex(), orig->GetOffset(), orig->GetStringId()->GetIndex(), @@ -86,7 +92,7 @@ bool VerifyId(dex_ir::TypeId* orig, dex_ir::TypeId* output, std::string* error_m bool VerifyId(dex_ir::ProtoId* orig, dex_ir::ProtoId* output, std::string* error_msg) { if (orig->Shorty()->GetIndex() != output->Shorty()->GetIndex()) { - *error_msg = StringPrintf("Mismatched string index for proto id %u @ orig offset %x, %u vs %u.", + *error_msg = StringPrintf("Mismatched string index for proto id %u at offset %x: %u vs %u.", orig->GetIndex(), orig->GetOffset(), orig->Shorty()->GetIndex(), @@ -94,7 +100,7 @@ bool VerifyId(dex_ir::ProtoId* orig, dex_ir::ProtoId* output, std::string* error return false; } if (orig->ReturnType()->GetIndex() != output->ReturnType()->GetIndex()) { - *error_msg = StringPrintf("Mismatched type index for proto id %u @ orig offset %x, %u vs %u.", + *error_msg = StringPrintf("Mismatched type index for proto id %u at offset %x: %u vs %u.", orig->GetIndex(), orig->GetOffset(), orig->ReturnType()->GetIndex(), @@ -102,7 +108,7 @@ bool VerifyId(dex_ir::ProtoId* orig, dex_ir::ProtoId* output, std::string* error return false; } if (!VerifyTypeList(orig->Parameters(), output->Parameters())) { - *error_msg = StringPrintf("Mismatched type list for proto id %u @ orig offset %x.", + *error_msg = StringPrintf("Mismatched type list for proto id %u at offset %x.", orig->GetIndex(), orig->GetOffset()); } @@ -112,7 +118,7 @@ bool VerifyId(dex_ir::ProtoId* orig, dex_ir::ProtoId* output, std::string* error bool VerifyId(dex_ir::FieldId* orig, dex_ir::FieldId* output, std::string* error_msg) { if (orig->Class()->GetIndex() != output->Class()->GetIndex()) { *error_msg = - StringPrintf("Mismatched class type index for field id %u @ orig offset %x, %u vs %u.", + StringPrintf("Mismatched class type index for field id %u at offset %x: %u vs %u.", orig->GetIndex(), orig->GetOffset(), orig->Class()->GetIndex(), @@ -120,7 +126,7 @@ bool VerifyId(dex_ir::FieldId* orig, dex_ir::FieldId* output, std::string* error return false; } if (orig->Type()->GetIndex() != output->Type()->GetIndex()) { - *error_msg = StringPrintf("Mismatched type index for field id %u @ orig offset %x, %u vs %u.", + *error_msg = StringPrintf("Mismatched type index for field id %u at offset %x: %u vs %u.", orig->GetIndex(), orig->GetOffset(), orig->Class()->GetIndex(), @@ -128,7 +134,7 @@ bool VerifyId(dex_ir::FieldId* orig, dex_ir::FieldId* output, std::string* error return false; } if (orig->Name()->GetIndex() != output->Name()->GetIndex()) { - *error_msg = StringPrintf("Mismatched string index for field id %u @ orig offset %x, %u vs %u.", + *error_msg = StringPrintf("Mismatched string index for field id %u at offset %x: %u vs %u.", orig->GetIndex(), orig->GetOffset(), orig->Name()->GetIndex(), @@ -140,7 +146,7 @@ bool VerifyId(dex_ir::FieldId* orig, dex_ir::FieldId* output, std::string* error bool VerifyId(dex_ir::MethodId* orig, dex_ir::MethodId* output, std::string* error_msg) { if (orig->Class()->GetIndex() != output->Class()->GetIndex()) { - *error_msg = StringPrintf("Mismatched type index for method id %u @ orig offset %x, %u vs %u.", + *error_msg = StringPrintf("Mismatched type index for method id %u at offset %x: %u vs %u.", orig->GetIndex(), orig->GetOffset(), orig->Class()->GetIndex(), @@ -148,7 +154,7 @@ bool VerifyId(dex_ir::MethodId* orig, dex_ir::MethodId* output, std::string* err return false; } if (orig->Proto()->GetIndex() != output->Proto()->GetIndex()) { - *error_msg = StringPrintf("Mismatched proto index for method id %u @ orig offset %x, %u vs %u.", + *error_msg = StringPrintf("Mismatched proto index for method id %u at offset %x: %u vs %u.", orig->GetIndex(), orig->GetOffset(), orig->Class()->GetIndex(), @@ -157,7 +163,7 @@ bool VerifyId(dex_ir::MethodId* orig, dex_ir::MethodId* output, std::string* err } if (orig->Name()->GetIndex() != output->Name()->GetIndex()) { *error_msg = - StringPrintf("Mismatched string index for method id %u @ orig offset %x, %u vs %u.", + StringPrintf("Mismatched string index for method id %u at offset %x: %u vs %u.", orig->GetIndex(), orig->GetOffset(), orig->Name()->GetIndex(), @@ -167,6 +173,96 @@ bool VerifyId(dex_ir::MethodId* orig, dex_ir::MethodId* output, std::string* err return true; } +struct ClassDefCompare { + bool operator()(dex_ir::ClassDef* lhs, dex_ir::ClassDef* rhs) const { + return lhs->ClassType()->GetIndex() < rhs->ClassType()->GetIndex(); + } +}; + +// The class defs may have a new order due to dexlayout. Use the class's class_idx to uniquely +// identify them and sort them for comparison. +bool VerifyClassDefs(std::vector<std::unique_ptr<dex_ir::ClassDef>>& orig, + std::vector<std::unique_ptr<dex_ir::ClassDef>>& output, + std::string* error_msg) { + if (orig.size() != output.size()) { + *error_msg = StringPrintf( + "Mismatched size for class defs section: %zu vs %zu.", orig.size(), output.size()); + return false; + } + // Store the class defs into sets sorted by the class's type index. + std::set<dex_ir::ClassDef*, ClassDefCompare> orig_set; + std::set<dex_ir::ClassDef*, ClassDefCompare> output_set; + for (size_t i = 0; i < orig.size(); ++i) { + orig_set.insert(orig[i].get()); + output_set.insert(output[i].get()); + } + auto orig_iter = orig_set.begin(); + auto output_iter = output_set.begin(); + while (orig_iter != orig_set.end() && output_iter != output_set.end()) { + if (!VerifyClassDef(*orig_iter, *output_iter, error_msg)) { + return false; + } + orig_iter++; + output_iter++; + } + return true; +} + +bool VerifyClassDef(dex_ir::ClassDef* orig, dex_ir::ClassDef* output, std::string* error_msg) { + if (orig->ClassType()->GetIndex() != output->ClassType()->GetIndex()) { + *error_msg = + StringPrintf("Mismatched class type index for class def %u at offset %x: %u vs %u.", + orig->GetIndex(), + orig->GetOffset(), + orig->ClassType()->GetIndex(), + output->ClassType()->GetIndex()); + return false; + } + if (orig->GetAccessFlags() != output->GetAccessFlags()) { + *error_msg = + StringPrintf("Mismatched access flags for class def %u at offset %x: %x vs %x.", + orig->GetIndex(), + orig->GetOffset(), + orig->GetAccessFlags(), + output->GetAccessFlags()); + return false; + } + uint32_t orig_super = orig->Superclass() == nullptr ? 0 : orig->Superclass()->GetIndex(); + uint32_t output_super = output->Superclass() == nullptr ? 0 : output->Superclass()->GetIndex(); + if (orig_super != output_super) { + *error_msg = + StringPrintf("Mismatched super class for class def %u at offset %x: %u vs %u.", + orig->GetIndex(), + orig->GetOffset(), + orig_super, + output_super); + return false; + } + if (!VerifyTypeList(orig->Interfaces(), output->Interfaces())) { + *error_msg = StringPrintf("Mismatched type list for class def %u at offset %x.", + orig->GetIndex(), + orig->GetOffset()); + return false; + } + const char* orig_source = orig->SourceFile() == nullptr ? "" : orig->SourceFile()->Data(); + const char* output_source = output->SourceFile() == nullptr ? "" : output->SourceFile()->Data(); + if (strcmp(orig_source, output_source) != 0) { + *error_msg = StringPrintf("Mismatched source file for class def %u at offset %x: %s vs %s.", + orig->GetIndex(), + orig->GetOffset(), + orig_source, + output_source); + return false; + } + if (!VerifyAnnotationsDirectory(orig->Annotations(), output->Annotations(), error_msg)) { + return false; + } + if (!VerifyClassData(orig->GetClassData(), output->GetClassData(), error_msg)) { + return false; + } + return VerifyEncodedArray(orig->StaticValues(), output->StaticValues(), error_msg); +} + bool VerifyTypeList(const dex_ir::TypeList* orig, const dex_ir::TypeList* output) { if (orig == nullptr || output == nullptr) { return orig == output; @@ -184,4 +280,841 @@ bool VerifyTypeList(const dex_ir::TypeList* orig, const dex_ir::TypeList* output return true; } +bool VerifyAnnotationsDirectory(dex_ir::AnnotationsDirectoryItem* orig, + dex_ir::AnnotationsDirectoryItem* output, + std::string* error_msg) { + if (orig == nullptr || output == nullptr) { + if (orig != output) { + *error_msg = "Found unexpected empty annotations directory."; + return false; + } + return true; + } + if (!VerifyAnnotationSet(orig->GetClassAnnotation(), output->GetClassAnnotation(), error_msg)) { + return false; + } + if (!VerifyFieldAnnotations(orig->GetFieldAnnotations(), + output->GetFieldAnnotations(), + orig->GetOffset(), + error_msg)) { + return false; + } + if (!VerifyMethodAnnotations(orig->GetMethodAnnotations(), + output->GetMethodAnnotations(), + orig->GetOffset(), + error_msg)) { + return false; + } + return VerifyParameterAnnotations(orig->GetParameterAnnotations(), + output->GetParameterAnnotations(), + orig->GetOffset(), + error_msg); +} + +bool VerifyFieldAnnotations(dex_ir::FieldAnnotationVector* orig, + dex_ir::FieldAnnotationVector* output, + uint32_t orig_offset, + std::string* error_msg) { + if (orig == nullptr || output == nullptr) { + if (orig != output) { + *error_msg = StringPrintf( + "Found unexpected empty field annotations for annotations directory at offset %x.", + orig_offset); + return false; + } + return true; + } + if (orig->size() != output->size()) { + *error_msg = StringPrintf( + "Mismatched field annotations size for annotations directory at offset %x: %zu vs %zu.", + orig_offset, + orig->size(), + output->size()); + return false; + } + for (size_t i = 0; i < orig->size(); ++i) { + dex_ir::FieldAnnotation* orig_field = (*orig)[i].get(); + dex_ir::FieldAnnotation* output_field = (*output)[i].get(); + if (orig_field->GetFieldId()->GetIndex() != output_field->GetFieldId()->GetIndex()) { + *error_msg = StringPrintf( + "Mismatched field annotation index for annotations directory at offset %x: %u vs %u.", + orig_offset, + orig_field->GetFieldId()->GetIndex(), + output_field->GetFieldId()->GetIndex()); + return false; + } + if (!VerifyAnnotationSet(orig_field->GetAnnotationSetItem(), + output_field->GetAnnotationSetItem(), + error_msg)) { + return false; + } + } + return true; +} + +bool VerifyMethodAnnotations(dex_ir::MethodAnnotationVector* orig, + dex_ir::MethodAnnotationVector* output, + uint32_t orig_offset, + std::string* error_msg) { + if (orig == nullptr || output == nullptr) { + if (orig != output) { + *error_msg = StringPrintf( + "Found unexpected empty method annotations for annotations directory at offset %x.", + orig_offset); + return false; + } + return true; + } + if (orig->size() != output->size()) { + *error_msg = StringPrintf( + "Mismatched method annotations size for annotations directory at offset %x: %zu vs %zu.", + orig_offset, + orig->size(), + output->size()); + return false; + } + for (size_t i = 0; i < orig->size(); ++i) { + dex_ir::MethodAnnotation* orig_method = (*orig)[i].get(); + dex_ir::MethodAnnotation* output_method = (*output)[i].get(); + if (orig_method->GetMethodId()->GetIndex() != output_method->GetMethodId()->GetIndex()) { + *error_msg = StringPrintf( + "Mismatched method annotation index for annotations directory at offset %x: %u vs %u.", + orig_offset, + orig_method->GetMethodId()->GetIndex(), + output_method->GetMethodId()->GetIndex()); + return false; + } + if (!VerifyAnnotationSet(orig_method->GetAnnotationSetItem(), + output_method->GetAnnotationSetItem(), + error_msg)) { + return false; + } + } + return true; +} + +bool VerifyParameterAnnotations(dex_ir::ParameterAnnotationVector* orig, + dex_ir::ParameterAnnotationVector* output, + uint32_t orig_offset, + std::string* error_msg) { + if (orig == nullptr || output == nullptr) { + if (orig != output) { + *error_msg = StringPrintf( + "Found unexpected empty parameter annotations for annotations directory at offset %x.", + orig_offset); + return false; + } + return true; + } + if (orig->size() != output->size()) { + *error_msg = StringPrintf( + "Mismatched parameter annotations size for annotations directory at offset %x: %zu vs %zu.", + orig_offset, + orig->size(), + output->size()); + return false; + } + for (size_t i = 0; i < orig->size(); ++i) { + dex_ir::ParameterAnnotation* orig_param = (*orig)[i].get(); + dex_ir::ParameterAnnotation* output_param = (*output)[i].get(); + if (orig_param->GetMethodId()->GetIndex() != output_param->GetMethodId()->GetIndex()) { + *error_msg = StringPrintf( + "Mismatched parameter annotation index for annotations directory at offset %x: %u vs %u.", + orig_offset, + orig_param->GetMethodId()->GetIndex(), + output_param->GetMethodId()->GetIndex()); + return false; + } + if (!VerifyAnnotationSetRefList(orig_param->GetAnnotations(), + output_param->GetAnnotations(), + error_msg)) { + return false; + } + } + return true; +} + +bool VerifyAnnotationSetRefList(dex_ir::AnnotationSetRefList* orig, + dex_ir::AnnotationSetRefList* output, + std::string* error_msg) { + std::vector<dex_ir::AnnotationSetItem*>* orig_items = orig->GetItems(); + std::vector<dex_ir::AnnotationSetItem*>* output_items = output->GetItems(); + if (orig_items->size() != output_items->size()) { + *error_msg = StringPrintf( + "Mismatched annotation set ref list size at offset %x: %zu vs %zu.", + orig->GetOffset(), + orig_items->size(), + output_items->size()); + return false; + } + for (size_t i = 0; i < orig_items->size(); ++i) { + if (!VerifyAnnotationSet((*orig_items)[i], (*output_items)[i], error_msg)) { + return false; + } + } + return true; +} + +bool VerifyAnnotationSet(dex_ir::AnnotationSetItem* orig, + dex_ir::AnnotationSetItem* output, + std::string* error_msg) { + if (orig == nullptr || output == nullptr) { + if (orig != output) { + *error_msg = "Found unexpected empty annotation set."; + return false; + } + return true; + } + std::vector<dex_ir::AnnotationItem*>* orig_items = orig->GetItems(); + std::vector<dex_ir::AnnotationItem*>* output_items = output->GetItems(); + if (orig_items->size() != output_items->size()) { + *error_msg = StringPrintf("Mismatched size for annotation set at offset %x: %zu vs %zu.", + orig->GetOffset(), + orig_items->size(), + output_items->size()); + return false; + } + for (size_t i = 0; i < orig_items->size(); ++i) { + if (!VerifyAnnotation((*orig_items)[i], (*output_items)[i], error_msg)) { + return false; + } + } + return true; +} + +bool VerifyAnnotation(dex_ir::AnnotationItem* orig, + dex_ir::AnnotationItem* output, + std::string* error_msg) { + if (orig->GetVisibility() != output->GetVisibility()) { + *error_msg = StringPrintf("Mismatched visibility for annotation at offset %x: %u vs %u.", + orig->GetOffset(), + orig->GetVisibility(), + output->GetVisibility()); + return false; + } + return VerifyEncodedAnnotation(orig->GetAnnotation(), + output->GetAnnotation(), + orig->GetOffset(), + error_msg); +} + +bool VerifyEncodedAnnotation(dex_ir::EncodedAnnotation* orig, + dex_ir::EncodedAnnotation* output, + uint32_t orig_offset, + std::string* error_msg) { + if (orig->GetType()->GetIndex() != output->GetType()->GetIndex()) { + *error_msg = StringPrintf( + "Mismatched encoded annotation type for annotation at offset %x: %u vs %u.", + orig_offset, + orig->GetType()->GetIndex(), + output->GetType()->GetIndex()); + return false; + } + dex_ir::AnnotationElementVector* orig_elements = orig->GetAnnotationElements(); + dex_ir::AnnotationElementVector* output_elements = output->GetAnnotationElements(); + if (orig_elements->size() != output_elements->size()) { + *error_msg = StringPrintf( + "Mismatched encoded annotation size for annotation at offset %x: %zu vs %zu.", + orig_offset, + orig_elements->size(), + output_elements->size()); + return false; + } + for (size_t i = 0; i < orig_elements->size(); ++i) { + if (!VerifyAnnotationElement((*orig_elements)[i].get(), + (*output_elements)[i].get(), + orig_offset, + error_msg)) { + return false; + } + } + return true; +} + +bool VerifyAnnotationElement(dex_ir::AnnotationElement* orig, + dex_ir::AnnotationElement* output, + uint32_t orig_offset, + std::string* error_msg) { + if (orig->GetName()->GetIndex() != output->GetName()->GetIndex()) { + *error_msg = StringPrintf( + "Mismatched annotation element name for annotation at offset %x: %u vs %u.", + orig_offset, + orig->GetName()->GetIndex(), + output->GetName()->GetIndex()); + return false; + } + return VerifyEncodedValue(orig->GetValue(), output->GetValue(), orig_offset, error_msg); +} + +bool VerifyEncodedValue(dex_ir::EncodedValue* orig, + dex_ir::EncodedValue* output, + uint32_t orig_offset, + std::string* error_msg) { + if (orig->Type() != output->Type()) { + *error_msg = StringPrintf( + "Mismatched encoded value type for annotation or encoded array at offset %x: %d vs %d.", + orig_offset, + orig->Type(), + output->Type()); + return false; + } + switch (orig->Type()) { + case DexFile::kDexAnnotationByte: + if (orig->GetByte() != output->GetByte()) { + *error_msg = StringPrintf("Mismatched encoded byte for annotation at offset %x: %d vs %d.", + orig_offset, + orig->GetByte(), + output->GetByte()); + return false; + } + break; + case DexFile::kDexAnnotationShort: + if (orig->GetShort() != output->GetShort()) { + *error_msg = StringPrintf("Mismatched encoded short for annotation at offset %x: %d vs %d.", + orig_offset, + orig->GetShort(), + output->GetShort()); + return false; + } + break; + case DexFile::kDexAnnotationChar: + if (orig->GetChar() != output->GetChar()) { + *error_msg = StringPrintf("Mismatched encoded char for annotation at offset %x: %c vs %c.", + orig_offset, + orig->GetChar(), + output->GetChar()); + return false; + } + break; + case DexFile::kDexAnnotationInt: + if (orig->GetInt() != output->GetInt()) { + *error_msg = StringPrintf("Mismatched encoded int for annotation at offset %x: %d vs %d.", + orig_offset, + orig->GetInt(), + output->GetInt()); + return false; + } + break; + case DexFile::kDexAnnotationLong: + if (orig->GetLong() != output->GetLong()) { + *error_msg = StringPrintf( + "Mismatched encoded long for annotation at offset %x: %" PRId64 " vs %" PRId64 ".", + orig_offset, + orig->GetLong(), + output->GetLong()); + return false; + } + break; + case DexFile::kDexAnnotationFloat: + // The float value is encoded, so compare as if it's an int. + if (orig->GetInt() != output->GetInt()) { + *error_msg = StringPrintf( + "Mismatched encoded float for annotation at offset %x: %x (encoded) vs %x (encoded).", + orig_offset, + orig->GetInt(), + output->GetInt()); + return false; + } + break; + case DexFile::kDexAnnotationDouble: + // The double value is encoded, so compare as if it's a long. + if (orig->GetLong() != output->GetLong()) { + *error_msg = StringPrintf( + "Mismatched encoded double for annotation at offset %x: %" PRIx64 + " (encoded) vs %" PRIx64 " (encoded).", + orig_offset, + orig->GetLong(), + output->GetLong()); + return false; + } + break; + case DexFile::kDexAnnotationString: + if (orig->GetStringId()->GetIndex() != output->GetStringId()->GetIndex()) { + *error_msg = StringPrintf( + "Mismatched encoded string for annotation at offset %x: %s vs %s.", + orig_offset, + orig->GetStringId()->Data(), + output->GetStringId()->Data()); + return false; + } + break; + case DexFile::kDexAnnotationType: + if (orig->GetTypeId()->GetIndex() != output->GetTypeId()->GetIndex()) { + *error_msg = StringPrintf("Mismatched encoded type for annotation at offset %x: %u vs %u.", + orig_offset, + orig->GetTypeId()->GetIndex(), + output->GetTypeId()->GetIndex()); + return false; + } + break; + case DexFile::kDexAnnotationField: + case DexFile::kDexAnnotationEnum: + if (orig->GetFieldId()->GetIndex() != output->GetFieldId()->GetIndex()) { + *error_msg = StringPrintf("Mismatched encoded field for annotation at offset %x: %u vs %u.", + orig_offset, + orig->GetFieldId()->GetIndex(), + output->GetFieldId()->GetIndex()); + return false; + } + break; + case DexFile::kDexAnnotationMethod: + if (orig->GetMethodId()->GetIndex() != output->GetMethodId()->GetIndex()) { + *error_msg = StringPrintf( + "Mismatched encoded method for annotation at offset %x: %u vs %u.", + orig_offset, + orig->GetMethodId()->GetIndex(), + output->GetMethodId()->GetIndex()); + return false; + } + break; + case DexFile::kDexAnnotationArray: + if (!VerifyEncodedArray(orig->GetEncodedArray(), output->GetEncodedArray(), error_msg)) { + return false; + } + break; + case DexFile::kDexAnnotationAnnotation: + if (!VerifyEncodedAnnotation(orig->GetEncodedAnnotation(), + output->GetEncodedAnnotation(), + orig_offset, + error_msg)) { + return false; + } + break; + case DexFile::kDexAnnotationNull: + break; + case DexFile::kDexAnnotationBoolean: + if (orig->GetBoolean() != output->GetBoolean()) { + *error_msg = StringPrintf( + "Mismatched encoded boolean for annotation at offset %x: %d vs %d.", + orig_offset, + orig->GetBoolean(), + output->GetBoolean()); + return false; + } + break; + default: + break; + } + return true; +} + +bool VerifyEncodedArray(dex_ir::EncodedArrayItem* orig, + dex_ir::EncodedArrayItem* output, + std::string* error_msg) { + if (orig == nullptr || output == nullptr) { + if (orig != output) { + *error_msg = "Found unexpected empty encoded array."; + return false; + } + return true; + } + dex_ir::EncodedValueVector* orig_vector = orig->GetEncodedValues(); + dex_ir::EncodedValueVector* output_vector = output->GetEncodedValues(); + if (orig_vector->size() != output_vector->size()) { + *error_msg = StringPrintf("Mismatched size for encoded array at offset %x: %zu vs %zu.", + orig->GetOffset(), + orig_vector->size(), + output_vector->size()); + return false; + } + for (size_t i = 0; i < orig_vector->size(); ++i) { + if (!VerifyEncodedValue((*orig_vector)[i].get(), + (*output_vector)[i].get(), + orig->GetOffset(), + error_msg)) { + return false; + } + } + return true; +} + +bool VerifyClassData(dex_ir::ClassData* orig, dex_ir::ClassData* output, std::string* error_msg) { + if (orig == nullptr || output == nullptr) { + if (orig != output) { + *error_msg = "Found unexpected empty class data."; + return false; + } + return true; + } + if (!VerifyFields(orig->StaticFields(), output->StaticFields(), orig->GetOffset(), error_msg)) { + return false; + } + if (!VerifyFields(orig->InstanceFields(), + output->InstanceFields(), + orig->GetOffset(), + error_msg)) { + return false; + } + if (!VerifyMethods(orig->DirectMethods(), + output->DirectMethods(), + orig->GetOffset(), + error_msg)) { + return false; + } + return VerifyMethods(orig->VirtualMethods(), + output->VirtualMethods(), + orig->GetOffset(), + error_msg); +} + +bool VerifyFields(dex_ir::FieldItemVector* orig, + dex_ir::FieldItemVector* output, + uint32_t orig_offset, + std::string* error_msg) { + if (orig->size() != output->size()) { + *error_msg = StringPrintf("Mismatched fields size for class data at offset %x: %zu vs %zu.", + orig_offset, + orig->size(), + output->size()); + return false; + } + for (size_t i = 0; i < orig->size(); ++i) { + dex_ir::FieldItem* orig_field = (*orig)[i].get(); + dex_ir::FieldItem* output_field = (*output)[i].get(); + if (orig_field->GetFieldId()->GetIndex() != output_field->GetFieldId()->GetIndex()) { + *error_msg = StringPrintf("Mismatched field index for class data at offset %x: %u vs %u.", + orig_offset, + orig_field->GetFieldId()->GetIndex(), + output_field->GetFieldId()->GetIndex()); + return false; + } + if (orig_field->GetAccessFlags() != output_field->GetAccessFlags()) { + *error_msg = StringPrintf( + "Mismatched field access flags for class data at offset %x: %u vs %u.", + orig_offset, + orig_field->GetAccessFlags(), + output_field->GetAccessFlags()); + return false; + } + } + return true; +} + +bool VerifyMethods(dex_ir::MethodItemVector* orig, + dex_ir::MethodItemVector* output, + uint32_t orig_offset, + std::string* error_msg) { + if (orig->size() != output->size()) { + *error_msg = StringPrintf("Mismatched methods size for class data at offset %x: %zu vs %zu.", + orig_offset, + orig->size(), + output->size()); + return false; + } + for (size_t i = 0; i < orig->size(); ++i) { + dex_ir::MethodItem* orig_method = (*orig)[i].get(); + dex_ir::MethodItem* output_method = (*output)[i].get(); + if (orig_method->GetMethodId()->GetIndex() != output_method->GetMethodId()->GetIndex()) { + *error_msg = StringPrintf("Mismatched method index for class data at offset %x: %u vs %u.", + orig_offset, + orig_method->GetMethodId()->GetIndex(), + output_method->GetMethodId()->GetIndex()); + return false; + } + if (orig_method->GetAccessFlags() != output_method->GetAccessFlags()) { + *error_msg = StringPrintf( + "Mismatched method access flags for class data at offset %x: %u vs %u.", + orig_offset, + orig_method->GetAccessFlags(), + output_method->GetAccessFlags()); + return false; + } + if (!VerifyCode(orig_method->GetCodeItem(), output_method->GetCodeItem(), error_msg)) { + return false; + } + } + return true; +} + +bool VerifyCode(dex_ir::CodeItem* orig, dex_ir::CodeItem* output, std::string* error_msg) { + if (orig == nullptr || output == nullptr) { + if (orig != output) { + *error_msg = "Found unexpected empty code item."; + return false; + } + return true; + } + if (orig->RegistersSize() != output->RegistersSize()) { + *error_msg = StringPrintf("Mismatched registers size for code item at offset %x: %u vs %u.", + orig->GetOffset(), + orig->RegistersSize(), + output->RegistersSize()); + return false; + } + if (orig->InsSize() != output->InsSize()) { + *error_msg = StringPrintf("Mismatched ins size for code item at offset %x: %u vs %u.", + orig->GetOffset(), + orig->InsSize(), + output->InsSize()); + return false; + } + if (orig->OutsSize() != output->OutsSize()) { + *error_msg = StringPrintf("Mismatched outs size for code item at offset %x: %u vs %u.", + orig->GetOffset(), + orig->OutsSize(), + output->OutsSize()); + return false; + } + if (orig->TriesSize() != output->TriesSize()) { + *error_msg = StringPrintf("Mismatched tries size for code item at offset %x: %u vs %u.", + orig->GetOffset(), + orig->TriesSize(), + output->TriesSize()); + return false; + } + if (!VerifyDebugInfo(orig->DebugInfo(), output->DebugInfo(), error_msg)) { + return false; + } + if (orig->InsnsSize() != output->InsnsSize()) { + *error_msg = StringPrintf("Mismatched insns size for code item at offset %x: %u vs %u.", + orig->GetOffset(), + orig->InsnsSize(), + output->InsnsSize()); + return false; + } + if (memcmp(orig->Insns(), output->Insns(), orig->InsnsSize()) != 0) { + *error_msg = StringPrintf("Mismatched insns for code item at offset %x.", + orig->GetOffset()); + return false; + } + if (!VerifyTries(orig->Tries(), output->Tries(), orig->GetOffset(), error_msg)) { + return false; + } + return VerifyHandlers(orig->Handlers(), output->Handlers(), orig->GetOffset(), error_msg); +} + +bool VerifyDebugInfo(dex_ir::DebugInfoItem* orig, + dex_ir::DebugInfoItem* output, + std::string* error_msg) { + if (orig == nullptr || output == nullptr) { + if (orig != output) { + *error_msg = "Found unexpected empty debug info."; + return false; + } + return true; + } + if (!VerifyPositionInfo(orig->GetPositionInfo(), + output->GetPositionInfo(), + orig->GetOffset(), + error_msg)) { + return false; + } + return VerifyLocalInfo(orig->GetLocalInfo(), + output->GetLocalInfo(), + orig->GetOffset(), + error_msg); +} + +bool VerifyPositionInfo(dex_ir::PositionInfoVector& orig, + dex_ir::PositionInfoVector& output, + uint32_t orig_offset, + std::string* error_msg) { + if (orig.size() != output.size()) { + *error_msg = StringPrintf( + "Mismatched number of positions for debug info at offset %x: %zu vs %zu.", + orig_offset, + orig.size(), + output.size()); + return false; + } + for (size_t i = 0; i < orig.size(); ++i) { + if (orig[i]->address_ != output[i]->address_) { + *error_msg = StringPrintf( + "Mismatched position address for debug info at offset %x: %u vs %u.", + orig_offset, + orig[i]->address_, + output[i]->address_); + return false; + } + if (orig[i]->line_ != output[i]->line_) { + *error_msg = StringPrintf("Mismatched position line for debug info at offset %x: %u vs %u.", + orig_offset, + orig[i]->line_, + output[i]->line_); + return false; + } + } + return true; +} + +bool VerifyLocalInfo(dex_ir::LocalInfoVector& orig, + dex_ir::LocalInfoVector& output, + uint32_t orig_offset, + std::string* error_msg) { + if (orig.size() != output.size()) { + *error_msg = StringPrintf( + "Mismatched number of locals for debug info at offset %x: %zu vs %zu.", + orig_offset, + orig.size(), + output.size()); + return false; + } + for (size_t i = 0; i < orig.size(); ++i) { + if (orig[i]->name_ != output[i]->name_) { + *error_msg = StringPrintf("Mismatched local name for debug info at offset %x: %s vs %s.", + orig_offset, + orig[i]->name_.c_str(), + output[i]->name_.c_str()); + return false; + } + if (orig[i]->descriptor_ != output[i]->descriptor_) { + *error_msg = StringPrintf( + "Mismatched local descriptor for debug info at offset %x: %s vs %s.", + orig_offset, + orig[i]->descriptor_.c_str(), + output[i]->descriptor_.c_str()); + return false; + } + if (orig[i]->signature_ != output[i]->signature_) { + *error_msg = StringPrintf("Mismatched local signature for debug info at offset %x: %s vs %s.", + orig_offset, + orig[i]->signature_.c_str(), + output[i]->signature_.c_str()); + return false; + } + if (orig[i]->start_address_ != output[i]->start_address_) { + *error_msg = StringPrintf( + "Mismatched local start address for debug info at offset %x: %u vs %u.", + orig_offset, + orig[i]->start_address_, + output[i]->start_address_); + return false; + } + if (orig[i]->end_address_ != output[i]->end_address_) { + *error_msg = StringPrintf( + "Mismatched local end address for debug info at offset %x: %u vs %u.", + orig_offset, + orig[i]->end_address_, + output[i]->end_address_); + return false; + } + if (orig[i]->reg_ != output[i]->reg_) { + *error_msg = StringPrintf("Mismatched local reg for debug info at offset %x: %u vs %u.", + orig_offset, + orig[i]->reg_, + output[i]->reg_); + return false; + } + } + return true; +} + +bool VerifyTries(dex_ir::TryItemVector* orig, + dex_ir::TryItemVector* output, + uint32_t orig_offset, + std::string* error_msg) { + if (orig == nullptr || output == nullptr) { + if (orig != output) { + *error_msg = "Found unexpected empty try items."; + return false; + } + return true; + } + if (orig->size() != output->size()) { + *error_msg = StringPrintf("Mismatched tries size for code item at offset %x: %zu vs %zu.", + orig_offset, + orig->size(), + output->size()); + return false; + } + for (size_t i = 0; i < orig->size(); ++i) { + const dex_ir::TryItem* orig_try = (*orig)[i].get(); + const dex_ir::TryItem* output_try = (*output)[i].get(); + if (orig_try->StartAddr() != output_try->StartAddr()) { + *error_msg = StringPrintf( + "Mismatched try item start addr for code item at offset %x: %u vs %u.", + orig_offset, + orig_try->StartAddr(), + output_try->StartAddr()); + return false; + } + if (orig_try->InsnCount() != output_try->InsnCount()) { + *error_msg = StringPrintf( + "Mismatched try item insn count for code item at offset %x: %u vs %u.", + orig_offset, + orig_try->InsnCount(), + output_try->InsnCount()); + return false; + } + if (!VerifyHandler(orig_try->GetHandlers(), + output_try->GetHandlers(), + orig_offset, + error_msg)) { + return false; + } + } + return true; +} + +bool VerifyHandlers(dex_ir::CatchHandlerVector* orig, + dex_ir::CatchHandlerVector* output, + uint32_t orig_offset, + std::string* error_msg) { + if (orig == nullptr || output == nullptr) { + if (orig != output) { + *error_msg = "Found unexpected empty catch handlers."; + return false; + } + return true; + } + if (orig->size() != output->size()) { + *error_msg = StringPrintf( + "Mismatched catch handlers size for code item at offset %x: %zu vs %zu.", + orig_offset, + orig->size(), + output->size()); + return false; + } + for (size_t i = 0; i < orig->size(); ++i) { + if (!VerifyHandler((*orig)[i].get(), (*output)[i].get(), orig_offset, error_msg)) { + return false; + } + } + return true; +} + +bool VerifyHandler(const dex_ir::CatchHandler* orig, + const dex_ir::CatchHandler* output, + uint32_t orig_offset, + std::string* error_msg) { + dex_ir::TypeAddrPairVector* orig_handlers = orig->GetHandlers(); + dex_ir::TypeAddrPairVector* output_handlers = output->GetHandlers(); + if (orig_handlers->size() != output_handlers->size()) { + *error_msg = StringPrintf( + "Mismatched number of catch handlers for code item at offset %x: %zu vs %zu.", + orig_offset, + orig_handlers->size(), + output_handlers->size()); + return false; + } + for (size_t i = 0; i < orig_handlers->size(); ++i) { + const dex_ir::TypeAddrPair* orig_handler = (*orig_handlers)[i].get(); + const dex_ir::TypeAddrPair* output_handler = (*output_handlers)[i].get(); + if (orig_handler->GetTypeId() == nullptr || output_handler->GetTypeId() == nullptr) { + if (orig_handler->GetTypeId() != output_handler->GetTypeId()) { + *error_msg = StringPrintf( + "Found unexpected catch all catch handler for code item at offset %x.", + orig_offset); + return false; + } + } else if (orig_handler->GetTypeId()->GetIndex() != output_handler->GetTypeId()->GetIndex()) { + *error_msg = StringPrintf( + "Mismatched catch handler type for code item at offset %x: %u vs %u.", + orig_offset, + orig_handler->GetTypeId()->GetIndex(), + output_handler->GetTypeId()->GetIndex()); + return false; + } + if (orig_handler->GetAddress() != output_handler->GetAddress()) { + *error_msg = StringPrintf( + "Mismatched catch handler address for code item at offset %x: %u vs %u.", + orig_offset, + orig_handler->GetAddress(), + output_handler->GetAddress()); + return false; + } + } + return true; +} + } // namespace art diff --git a/dexlayout/dex_verify.h b/dexlayout/dex_verify.h index a19431c143..58c95d6947 100644 --- a/dexlayout/dex_verify.h +++ b/dexlayout/dex_verify.h @@ -24,12 +24,12 @@ #include "dex_ir.h" namespace art { - // Check that the output dex file contains the same data as the original. // Compares the dex IR of both dex files. Allows the dex files to have different layouts. bool VerifyOutputDexFile(dex_ir::Header* orig_header, dex_ir::Header* output_header, std::string* error_msg); + template<class T> bool VerifyIds(std::vector<std::unique_ptr<T>>& orig, std::vector<std::unique_ptr<T>>& output, const char* section_name, @@ -39,8 +39,87 @@ bool VerifyId(dex_ir::TypeId* orig, dex_ir::TypeId* output, std::string* error_m bool VerifyId(dex_ir::ProtoId* orig, dex_ir::ProtoId* output, std::string* error_msg); bool VerifyId(dex_ir::FieldId* orig, dex_ir::FieldId* output, std::string* error_msg); bool VerifyId(dex_ir::MethodId* orig, dex_ir::MethodId* output, std::string* error_msg); + +bool VerifyClassDefs(std::vector<std::unique_ptr<dex_ir::ClassDef>>& orig, + std::vector<std::unique_ptr<dex_ir::ClassDef>>& output, + std::string* error_msg); +bool VerifyClassDef(dex_ir::ClassDef* orig, dex_ir::ClassDef* output, std::string* error_msg); + bool VerifyTypeList(const dex_ir::TypeList* orig, const dex_ir::TypeList* output); +bool VerifyAnnotationsDirectory(dex_ir::AnnotationsDirectoryItem* orig, + dex_ir::AnnotationsDirectoryItem* output, + std::string* error_msg); +bool VerifyFieldAnnotations(dex_ir::FieldAnnotationVector* orig, + dex_ir::FieldAnnotationVector* output, + uint32_t orig_offset, + std::string* error_msg); +bool VerifyMethodAnnotations(dex_ir::MethodAnnotationVector* orig, + dex_ir::MethodAnnotationVector* output, + uint32_t orig_offset, + std::string* error_msg); +bool VerifyParameterAnnotations(dex_ir::ParameterAnnotationVector* orig, + dex_ir::ParameterAnnotationVector* output, + uint32_t orig_offset, + std::string* error_msg); +bool VerifyAnnotationSetRefList(dex_ir::AnnotationSetRefList* orig, + dex_ir::AnnotationSetRefList* output, + std::string* error_msg); +bool VerifyAnnotationSet(dex_ir::AnnotationSetItem* orig, + dex_ir::AnnotationSetItem* output, + std::string* error_msg); +bool VerifyAnnotation(dex_ir::AnnotationItem* orig, + dex_ir::AnnotationItem* output, + std::string* error_msg); +bool VerifyEncodedAnnotation(dex_ir::EncodedAnnotation* orig, + dex_ir::EncodedAnnotation* output, + uint32_t orig_offset, + std::string* error_msg); +bool VerifyAnnotationElement(dex_ir::AnnotationElement* orig, + dex_ir::AnnotationElement* output, + uint32_t orig_offset, + std::string* error_msg); +bool VerifyEncodedValue(dex_ir::EncodedValue* orig, + dex_ir::EncodedValue* output, + uint32_t orig_offset, + std::string* error_msg); +bool VerifyEncodedArray(dex_ir::EncodedArrayItem* orig, + dex_ir::EncodedArrayItem* output, + std::string* error_msg); + +bool VerifyClassData(dex_ir::ClassData* orig, dex_ir::ClassData* output, std::string* error_msg); +bool VerifyFields(dex_ir::FieldItemVector* orig, + dex_ir::FieldItemVector* output, + uint32_t orig_offset, + std::string* error_msg); +bool VerifyMethods(dex_ir::MethodItemVector* orig, + dex_ir::MethodItemVector* output, + uint32_t orig_offset, + std::string* error_msg); +bool VerifyCode(dex_ir::CodeItem* orig, dex_ir::CodeItem* output, std::string* error_msg); +bool VerifyDebugInfo(dex_ir::DebugInfoItem* orig, + dex_ir::DebugInfoItem* output, + std::string* error_msg); +bool VerifyPositionInfo(dex_ir::PositionInfoVector& orig, + dex_ir::PositionInfoVector& output, + uint32_t orig_offset, + std::string* error_msg); +bool VerifyLocalInfo(dex_ir::LocalInfoVector& orig, + dex_ir::LocalInfoVector& output, + uint32_t orig_offset, + std::string* error_msg); +bool VerifyTries(dex_ir::TryItemVector* orig, + dex_ir::TryItemVector* output, + uint32_t orig_offset, + std::string* error_msg); +bool VerifyHandlers(dex_ir::CatchHandlerVector* orig, + dex_ir::CatchHandlerVector* output, + uint32_t orig_offset, + std::string* error_msg); +bool VerifyHandler(const dex_ir::CatchHandler* orig, + const dex_ir::CatchHandler* output, + uint32_t orig_offset, + std::string* error_msg); } // namespace art #endif // ART_DEXLAYOUT_DEX_VERIFY_H_ diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index a3104240a0..615bcf92ea 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -1369,10 +1369,11 @@ void DexLayout::DumpClass(int idx, char** last_package) { } // Interfaces. - const dex_ir::TypeIdVector* interfaces = class_def->Interfaces(); + const dex_ir::TypeList* interfaces = class_def->Interfaces(); if (interfaces != nullptr) { - for (uint32_t i = 0; i < interfaces->size(); i++) { - DumpInterface((*interfaces)[i], i); + const dex_ir::TypeIdVector* interfaces_vector = interfaces->GetTypeList(); + for (uint32_t i = 0; i < interfaces_vector->size(); i++) { + DumpInterface((*interfaces_vector)[i], i); } // for } diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 685e26c78d..b47f8f0fc2 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -347,7 +347,11 @@ inline const char* ArtMethod::GetDeclaringClassSourceFile() { inline uint16_t ArtMethod::GetClassDefIndex() { DCHECK(!IsProxyMethod()); - return GetDeclaringClass()->GetDexClassDefIndex(); + if (LIKELY(!IsObsolete())) { + return GetDeclaringClass()->GetDexClassDefIndex(); + } else { + return FindObsoleteDexClassDefIndex(); + } } inline const DexFile::ClassDef& ArtMethod::GetClassDef() { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 9d74e7c92b..80a877350b 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -104,6 +104,16 @@ mirror::DexCache* ArtMethod::GetObsoleteDexCache() { UNREACHABLE(); } +uint16_t ArtMethod::FindObsoleteDexClassDefIndex() { + DCHECK(!Runtime::Current()->IsAotCompiler()) << PrettyMethod(); + DCHECK(IsObsolete()); + const DexFile* dex_file = GetDexFile(); + const dex::TypeIndex declaring_class_type = dex_file->GetMethodId(GetDexMethodIndex()).class_idx_; + const DexFile::ClassDef* class_def = dex_file->FindClassDef(declaring_class_type); + CHECK(class_def != nullptr); + return dex_file->GetIndexForClassDef(*class_def); +} + mirror::String* ArtMethod::GetNameAsString(Thread* self) { CHECK(!IsProxyMethod()); StackHandleScope<1> hs(self); diff --git a/runtime/art_method.h b/runtime/art_method.h index cd1950c0e2..2248c3bd9d 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -700,6 +700,8 @@ class ArtMethod FINAL { } ptr_sized_fields_; private: + uint16_t FindObsoleteDexClassDefIndex() REQUIRES_SHARED(Locks::mutator_lock_); + bool IsAnnotatedWith(jclass klass, uint32_t visibility); static constexpr size_t PtrSizedFieldsOffset(PointerSize pointer_size) { diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index d39ea35a90..6b9654dc49 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -41,7 +41,80 @@ struct DexFile::AnnotationValue { }; namespace { -mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, + +// A helper class that contains all the data needed to do annotation lookup. +class ClassData { + public: + explicit ClassData(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) + : ClassData(ScopedNullHandle<mirror::Class>(), // klass + method, + *method->GetDexFile(), + &method->GetClassDef()) {} + + // Requires Scope to be able to create at least 1 handles. + template <typename Scope> + ClassData(Scope& hs, ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) + : ClassData(hs.NewHandle(field->GetDeclaringClass())) { } + + explicit ClassData(Handle<mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) + : ClassData(klass, // klass + nullptr, // method + klass->GetDexFile(), + klass->GetClassDef()) {} + + const DexFile& GetDexFile() const REQUIRES_SHARED(Locks::mutator_lock_) { + return dex_file_; + } + + const DexFile::ClassDef* GetClassDef() const REQUIRES_SHARED(Locks::mutator_lock_) { + return class_def_; + } + + ObjPtr<mirror::DexCache> GetDexCache() const REQUIRES_SHARED(Locks::mutator_lock_) { + if (method_ != nullptr) { + return method_->GetDexCache(); + } else { + return real_klass_->GetDexCache(); + } + } + + ObjPtr<mirror::ClassLoader> GetClassLoader() const REQUIRES_SHARED(Locks::mutator_lock_) { + if (method_ != nullptr) { + return method_->GetDeclaringClass()->GetClassLoader(); + } else { + return real_klass_->GetClassLoader(); + } + } + + ObjPtr<mirror::Class> GetRealClass() const REQUIRES_SHARED(Locks::mutator_lock_) { + if (method_ != nullptr) { + return method_->GetDeclaringClass(); + } else { + return real_klass_.Get(); + } + } + + private: + ClassData(Handle<mirror::Class> klass, + ArtMethod* method, + const DexFile& dex_file, + const DexFile::ClassDef* class_def) REQUIRES_SHARED(Locks::mutator_lock_) + : real_klass_(klass), + method_(method), + dex_file_(dex_file), + class_def_(class_def) { + DCHECK((method_ == nullptr) || real_klass_.IsNull()); + } + + Handle<mirror::Class> real_klass_; + ArtMethod* method_; + const DexFile& dex_file_; + const DexFile::ClassDef* class_def_; + + DISALLOW_COPY_AND_ASSIGN(ClassData); +}; + +mirror::Object* CreateAnnotationMember(const ClassData& klass, Handle<mirror::Class> annotation_class, const uint8_t** annotation) REQUIRES_SHARED(Locks::mutator_lock_); @@ -185,9 +258,8 @@ const uint8_t* SearchEncodedAnnotation(const DexFile& dex_file, const DexFile::AnnotationSetItem* FindAnnotationSetForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile* dex_file = method->GetDexFile(); - mirror::Class* klass = method->GetDeclaringClass(); const DexFile::AnnotationsDirectoryItem* annotations_dir = - dex_file->GetAnnotationsDirectory(*klass->GetClassDef()); + dex_file->GetAnnotationsDirectory(method->GetClassDef()); if (annotations_dir == nullptr) { return nullptr; } @@ -209,9 +281,8 @@ const DexFile::AnnotationSetItem* FindAnnotationSetForMethod(ArtMethod* method) const DexFile::ParameterAnnotationsItem* FindAnnotationsItemForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile* dex_file = method->GetDexFile(); - mirror::Class* klass = method->GetDeclaringClass(); const DexFile::AnnotationsDirectoryItem* annotations_dir = - dex_file->GetAnnotationsDirectory(*klass->GetClassDef()); + dex_file->GetAnnotationsDirectory(method->GetClassDef()); if (annotations_dir == nullptr) { return nullptr; } @@ -230,30 +301,34 @@ const DexFile::ParameterAnnotationsItem* FindAnnotationsItemForMethod(ArtMethod* return nullptr; } -const DexFile::AnnotationSetItem* FindAnnotationSetForClass(Handle<mirror::Class> klass) +const DexFile::AnnotationSetItem* FindAnnotationSetForClass(const ClassData& klass) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); const DexFile::AnnotationsDirectoryItem* annotations_dir = - dex_file.GetAnnotationsDirectory(*klass->GetClassDef()); + dex_file.GetAnnotationsDirectory(*klass.GetClassDef()); if (annotations_dir == nullptr) { return nullptr; } return dex_file.GetClassAnnotationSet(annotations_dir); } -mirror::Object* ProcessEncodedAnnotation(Handle<mirror::Class> klass, const uint8_t** annotation) +mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** annotation) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t type_index = DecodeUnsignedLeb128(annotation); uint32_t size = DecodeUnsignedLeb128(annotation); Thread* self = Thread::Current(); ScopedObjectAccessUnchecked soa(self); - StackHandleScope<2> hs(self); + StackHandleScope<4> hs(self); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> annotation_class(hs.NewHandle( - class_linker->ResolveType(klass->GetDexFile(), dex::TypeIndex(type_index), klass.Get()))); + class_linker->ResolveType(klass.GetDexFile(), + dex::TypeIndex(type_index), + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader())))); if (annotation_class == nullptr) { - LOG(INFO) << "Unable to resolve " << klass->PrettyClass() << " annotation class " << type_index; + LOG(INFO) << "Unable to resolve " << klass.GetRealClass()->PrettyClass() + << " annotation class " << type_index; DCHECK(Thread::Current()->IsExceptionPending()); Thread::Current()->ClearException(); return nullptr; @@ -300,13 +375,13 @@ mirror::Object* ProcessEncodedAnnotation(Handle<mirror::Class> klass, const uint } template <bool kTransactionActive> -bool ProcessAnnotationValue(Handle<mirror::Class> klass, +bool ProcessAnnotationValue(const ClassData& klass, const uint8_t** annotation_ptr, DexFile::AnnotationValue* annotation_value, Handle<mirror::Class> array_class, DexFile::AnnotationResultStyle result_style) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); Thread* self = Thread::Current(); ObjPtr<mirror::Object> element_object = nullptr; bool set_object = false; @@ -361,9 +436,8 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, annotation_value->value_.SetI(index); } else { StackHandleScope<1> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); element_object = Runtime::Current()->GetClassLinker()->ResolveString( - klass->GetDexFile(), dex::StringIndex(index), dex_cache); + klass.GetDexFile(), dex::StringIndex(index), hs.NewHandle(klass.GetDexCache())); set_object = true; if (element_object == nullptr) { return false; @@ -377,8 +451,12 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, annotation_value->value_.SetI(index); } else { dex::TypeIndex type_index(index); + StackHandleScope<2> hs(self); element_object = Runtime::Current()->GetClassLinker()->ResolveType( - klass->GetDexFile(), type_index, klass.Get()); + klass.GetDexFile(), + type_index, + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader())); set_object = true; if (element_object == nullptr) { CHECK(self->IsExceptionPending()); @@ -399,12 +477,13 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, if (result_style == DexFile::kAllRaw) { annotation_value->value_.SetI(index); } else { - StackHandleScope<2> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(self); ArtMethod* method = class_linker->ResolveMethodWithoutInvokeType( - klass->GetDexFile(), index, dex_cache, class_loader); + klass.GetDexFile(), + index, + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader())); if (method == nullptr) { return false; } @@ -439,10 +518,11 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, annotation_value->value_.SetI(index); } else { StackHandleScope<2> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); ArtField* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS( - klass->GetDexFile(), index, dex_cache, class_loader); + klass.GetDexFile(), + index, + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader())); if (field == nullptr) { return false; } @@ -467,10 +547,12 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, annotation_value->value_.SetI(index); } else { StackHandleScope<3> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); ArtField* enum_field = Runtime::Current()->GetClassLinker()->ResolveField( - klass->GetDexFile(), index, dex_cache, class_loader, true); + klass.GetDexFile(), + index, + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader()), + true); if (enum_field == nullptr) { return false; } else { @@ -595,10 +677,10 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, return true; } -mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, +mirror::Object* CreateAnnotationMember(const ClassData& klass, Handle<mirror::Class> annotation_class, const uint8_t** annotation) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); Thread* self = Thread::Current(); ScopedObjectAccessUnchecked soa(self); StackHandleScope<5> hs(self); @@ -666,12 +748,12 @@ mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, } const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( - Handle<mirror::Class> klass, + const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set, uint32_t visibility, Handle<mirror::Class> annotation_class) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); for (uint32_t i = 0; i < annotation_set->size_; ++i) { const DexFile::AnnotationItem* annotation_item = dex_file.GetAnnotationItem(annotation_set, i); if (!IsVisibilityCompatible(annotation_item->visibility_, visibility)) { @@ -679,12 +761,16 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( } const uint8_t* annotation = annotation_item->annotation_; uint32_t type_index = DecodeUnsignedLeb128(&annotation); + StackHandleScope<2> hs(Thread::Current()); mirror::Class* resolved_class = Runtime::Current()->GetClassLinker()->ResolveType( - klass->GetDexFile(), dex::TypeIndex(type_index), klass.Get()); + klass.GetDexFile(), + dex::TypeIndex(type_index), + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader())); if (resolved_class == nullptr) { std::string temp; LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d", - klass->GetDescriptor(&temp), type_index); + klass.GetRealClass()->GetDescriptor(&temp), type_index); CHECK(Thread::Current()->IsExceptionPending()); Thread::Current()->ClearException(); continue; @@ -698,7 +784,7 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( } mirror::Object* GetAnnotationObjectFromAnnotationSet( - Handle<mirror::Class> klass, + const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set, uint32_t visibility, Handle<mirror::Class> annotation_class) @@ -712,13 +798,13 @@ mirror::Object* GetAnnotationObjectFromAnnotationSet( return ProcessEncodedAnnotation(klass, &annotation); } -mirror::Object* GetAnnotationValue(Handle<mirror::Class> klass, +mirror::Object* GetAnnotationValue(const ClassData& klass, const DexFile::AnnotationItem* annotation_item, const char* annotation_name, Handle<mirror::Class> array_class, uint32_t expected_type) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); const uint8_t* annotation = SearchEncodedAnnotation(dex_file, annotation_item->annotation_, annotation_name); if (annotation == nullptr) { @@ -745,10 +831,10 @@ mirror::Object* GetAnnotationValue(Handle<mirror::Class> klass, return annotation_value.value_.GetL(); } -mirror::ObjectArray<mirror::String>* GetSignatureValue(Handle<mirror::Class> klass, +mirror::ObjectArray<mirror::String>* GetSignatureValue(const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); StackHandleScope<1> hs(Thread::Current()); const DexFile::AnnotationItem* annotation_item = SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/Signature;", @@ -771,10 +857,10 @@ mirror::ObjectArray<mirror::String>* GetSignatureValue(Handle<mirror::Class> kla return obj->AsObjectArray<mirror::String>(); } -mirror::ObjectArray<mirror::Class>* GetThrowsValue(Handle<mirror::Class> klass, +mirror::ObjectArray<mirror::Class>* GetThrowsValue(const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); StackHandleScope<1> hs(Thread::Current()); const DexFile::AnnotationItem* annotation_item = SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/Throws;", @@ -798,11 +884,11 @@ mirror::ObjectArray<mirror::Class>* GetThrowsValue(Handle<mirror::Class> klass, } mirror::ObjectArray<mirror::Object>* ProcessAnnotationSet( - Handle<mirror::Class> klass, + const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set, uint32_t visibility) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); Thread* self = Thread::Current(); ScopedObjectAccessUnchecked soa(self); StackHandleScope<2> hs(self); @@ -856,11 +942,11 @@ mirror::ObjectArray<mirror::Object>* ProcessAnnotationSet( } mirror::ObjectArray<mirror::Object>* ProcessAnnotationSetRefList( - Handle<mirror::Class> klass, + const ClassData& klass, const DexFile::AnnotationSetRefList* set_ref_list, uint32_t size) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); + const DexFile& dex_file = klass.GetDexFile(); Thread* self = Thread::Current(); ScopedObjectAccessUnchecked soa(self); StackHandleScope<1> hs(self); @@ -899,15 +985,17 @@ mirror::Object* GetAnnotationForField(ArtField* field, Handle<mirror::Class> ann return nullptr; } StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass())); - return GetAnnotationObjectFromAnnotationSet(field_class, annotation_set, - DexFile::kDexVisibilityRuntime, annotation_class); + const ClassData field_class(hs, field); + return GetAnnotationObjectFromAnnotationSet(field_class, + annotation_set, + DexFile::kDexVisibilityRuntime, + annotation_class); } mirror::ObjectArray<mirror::Object>* GetAnnotationsForField(ArtField* field) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForField(field); StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass())); + const ClassData field_class(hs, field); return ProcessAnnotationSet(field_class, annotation_set, DexFile::kDexVisibilityRuntime); } @@ -917,7 +1005,7 @@ mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForField(ArtField* fi return nullptr; } StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass())); + const ClassData field_class(hs, field); return GetSignatureValue(field_class, annotation_set); } @@ -927,17 +1015,17 @@ bool IsFieldAnnotationPresent(ArtField* field, Handle<mirror::Class> annotation_ return false; } StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass())); + const ClassData field_class(hs, field); const DexFile::AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet( field_class, annotation_set, DexFile::kDexVisibilityRuntime, annotation_class); return annotation_item != nullptr; } mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) { - const DexFile* dex_file = method->GetDexFile(); - mirror::Class* klass = method->GetDeclaringClass(); + const ClassData klass(method); + const DexFile* dex_file = &klass.GetDexFile(); const DexFile::AnnotationsDirectoryItem* annotations_dir = - dex_file->GetAnnotationsDirectory(*klass->GetClassDef()); + dex_file->GetAnnotationsDirectory(*klass.GetClassDef()); if (annotations_dir == nullptr) { return nullptr; } @@ -965,10 +1053,9 @@ mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) { return nullptr; } DexFile::AnnotationValue annotation_value; - StackHandleScope<2> hs(Thread::Current()); - Handle<mirror::Class> h_klass(hs.NewHandle(klass)); + StackHandleScope<1> hs(Thread::Current()); Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */))); - if (!ProcessAnnotationValue<false>(h_klass, + if (!ProcessAnnotationValue<false>(klass, &annotation, &annotation_value, return_type, @@ -983,17 +1070,15 @@ mirror::Object* GetAnnotationForMethod(ArtMethod* method, Handle<mirror::Class> if (annotation_set == nullptr) { return nullptr; } - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); - return GetAnnotationObjectFromAnnotationSet(method_class, annotation_set, + return GetAnnotationObjectFromAnnotationSet(ClassData(method), annotation_set, DexFile::kDexVisibilityRuntime, annotation_class); } mirror::ObjectArray<mirror::Object>* GetAnnotationsForMethod(ArtMethod* method) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); - return ProcessAnnotationSet(method_class, annotation_set, DexFile::kDexVisibilityRuntime); + return ProcessAnnotationSet(ClassData(method), + annotation_set, + DexFile::kDexVisibilityRuntime); } mirror::ObjectArray<mirror::Class>* GetExceptionTypesForMethod(ArtMethod* method) { @@ -1001,9 +1086,7 @@ mirror::ObjectArray<mirror::Class>* GetExceptionTypesForMethod(ArtMethod* method if (annotation_set == nullptr) { return nullptr; } - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); - return GetThrowsValue(method_class, annotation_set); + return GetThrowsValue(ClassData(method), annotation_set); } mirror::ObjectArray<mirror::Object>* GetParameterAnnotations(ArtMethod* method) { @@ -1019,9 +1102,7 @@ mirror::ObjectArray<mirror::Object>* GetParameterAnnotations(ArtMethod* method) return nullptr; } uint32_t size = set_ref_list->size_; - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); - return ProcessAnnotationSetRefList(method_class, set_ref_list, size); + return ProcessAnnotationSetRefList(ClassData(method), set_ref_list, size); } mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method, @@ -1045,9 +1126,7 @@ mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method, const DexFile::AnnotationSetItem* annotation_set = dex_file->GetSetRefItemItem(annotation_set_ref); - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); - return GetAnnotationObjectFromAnnotationSet(method_class, + return GetAnnotationObjectFromAnnotationSet(ClassData(method), annotation_set, DexFile::kDexVisibilityRuntime, annotation_class); @@ -1072,7 +1151,7 @@ bool GetParametersMetadataForMethod(ArtMethod* method, return false; } - StackHandleScope<5> hs(Thread::Current()); + StackHandleScope<4> hs(Thread::Current()); // Extract the parameters' names String[]. ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString(); @@ -1082,9 +1161,9 @@ bool GetParametersMetadataForMethod(ArtMethod* method, return false; } - Handle<mirror::Class> klass = hs.NewHandle(method->GetDeclaringClass()); + ClassData data(method); Handle<mirror::Object> names_obj = - hs.NewHandle(GetAnnotationValue(klass, + hs.NewHandle(GetAnnotationValue(data, annotation_item, "names", string_array_class, @@ -1099,7 +1178,7 @@ bool GetParametersMetadataForMethod(ArtMethod* method, return false; } Handle<mirror::Object> access_flags_obj = - hs.NewHandle(GetAnnotationValue(klass, + hs.NewHandle(GetAnnotationValue(data, annotation_item, "accessFlags", int_array_class, @@ -1118,9 +1197,7 @@ mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForMethod(ArtMethod* if (annotation_set == nullptr) { return nullptr; } - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); - return GetSignatureValue(method_class, annotation_set); + return GetSignatureValue(ClassData(method), annotation_set); } bool IsMethodAnnotationPresent(ArtMethod* method, Handle<mirror::Class> annotation_class, @@ -1129,37 +1206,39 @@ bool IsMethodAnnotationPresent(ArtMethod* method, Handle<mirror::Class> annotati if (annotation_set == nullptr) { return false; } - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); const DexFile::AnnotationItem* annotation_item = - GetAnnotationItemFromAnnotationSet(method_class, annotation_set, visibility, - annotation_class); + GetAnnotationItemFromAnnotationSet(ClassData(method), + annotation_set, visibility, annotation_class); return annotation_item != nullptr; } mirror::Object* GetAnnotationForClass(Handle<mirror::Class> klass, Handle<mirror::Class> annotation_class) { - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return nullptr; } - return GetAnnotationObjectFromAnnotationSet(klass, annotation_set, DexFile::kDexVisibilityRuntime, + return GetAnnotationObjectFromAnnotationSet(data, + annotation_set, + DexFile::kDexVisibilityRuntime, annotation_class); } mirror::ObjectArray<mirror::Object>* GetAnnotationsForClass(Handle<mirror::Class> klass) { - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); - return ProcessAnnotationSet(klass, annotation_set, DexFile::kDexVisibilityRuntime); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); + return ProcessAnnotationSet(data, annotation_set, DexFile::kDexVisibilityRuntime); } mirror::ObjectArray<mirror::Class>* GetDeclaredClasses(Handle<mirror::Class> klass) { - const DexFile& dex_file = klass->GetDexFile(); - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return nullptr; } const DexFile::AnnotationItem* annotation_item = - SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/MemberClasses;", + SearchAnnotationSet(data.GetDexFile(), annotation_set, "Ldalvik/annotation/MemberClasses;", DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return nullptr; @@ -1172,7 +1251,7 @@ mirror::ObjectArray<mirror::Class>* GetDeclaredClasses(Handle<mirror::Class> kla return nullptr; } mirror::Object* obj = - GetAnnotationValue(klass, annotation_item, "value", class_array_class, + GetAnnotationValue(data, annotation_item, "value", class_array_class, DexFile::kDexAnnotationArray); if (obj == nullptr) { return nullptr; @@ -1181,18 +1260,18 @@ mirror::ObjectArray<mirror::Class>* GetDeclaredClasses(Handle<mirror::Class> kla } mirror::Class* GetDeclaringClass(Handle<mirror::Class> klass) { - const DexFile& dex_file = klass->GetDexFile(); - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return nullptr; } const DexFile::AnnotationItem* annotation_item = - SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/EnclosingClass;", + SearchAnnotationSet(data.GetDexFile(), annotation_set, "Ldalvik/annotation/EnclosingClass;", DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return nullptr; } - mirror::Object* obj = GetAnnotationValue(klass, annotation_item, "value", + mirror::Object* obj = GetAnnotationValue(data, annotation_item, "value", ScopedNullHandle<mirror::Class>(), DexFile::kDexAnnotationType); if (obj == nullptr) { @@ -1202,28 +1281,30 @@ mirror::Class* GetDeclaringClass(Handle<mirror::Class> klass) { } mirror::Class* GetEnclosingClass(Handle<mirror::Class> klass) { - const DexFile& dex_file = klass->GetDexFile(); mirror::Class* declaring_class = GetDeclaringClass(klass); if (declaring_class != nullptr) { return declaring_class; } - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return nullptr; } const DexFile::AnnotationItem* annotation_item = - SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/EnclosingMethod;", + SearchAnnotationSet(data.GetDexFile(), + annotation_set, + "Ldalvik/annotation/EnclosingMethod;", DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return nullptr; } const uint8_t* annotation = - SearchEncodedAnnotation(dex_file, annotation_item->annotation_, "value"); + SearchEncodedAnnotation(data.GetDexFile(), annotation_item->annotation_, "value"); if (annotation == nullptr) { return nullptr; } DexFile::AnnotationValue annotation_value; - if (!ProcessAnnotationValue<false>(klass, + if (!ProcessAnnotationValue<false>(data, &annotation, &annotation_value, ScopedNullHandle<mirror::Class>(), @@ -1234,10 +1315,11 @@ mirror::Class* GetEnclosingClass(Handle<mirror::Class> klass) { return nullptr; } StackHandleScope<2> hs(Thread::Current()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); ArtMethod* method = Runtime::Current()->GetClassLinker()->ResolveMethodWithoutInvokeType( - klass->GetDexFile(), annotation_value.value_.GetI(), dex_cache, class_loader); + data.GetDexFile(), + annotation_value.value_.GetI(), + hs.NewHandle(data.GetDexCache()), + hs.NewHandle(data.GetClassLoader())); if (method == nullptr) { return nullptr; } @@ -1245,39 +1327,44 @@ mirror::Class* GetEnclosingClass(Handle<mirror::Class> klass) { } mirror::Object* GetEnclosingMethod(Handle<mirror::Class> klass) { - const DexFile& dex_file = klass->GetDexFile(); - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return nullptr; } const DexFile::AnnotationItem* annotation_item = - SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/EnclosingMethod;", + SearchAnnotationSet(data.GetDexFile(), + annotation_set, + "Ldalvik/annotation/EnclosingMethod;", DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return nullptr; } - return GetAnnotationValue(klass, annotation_item, "value", ScopedNullHandle<mirror::Class>(), + return GetAnnotationValue(data, annotation_item, "value", ScopedNullHandle<mirror::Class>(), DexFile::kDexAnnotationMethod); } bool GetInnerClass(Handle<mirror::Class> klass, mirror::String** name) { - const DexFile& dex_file = klass->GetDexFile(); - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return false; } const DexFile::AnnotationItem* annotation_item = SearchAnnotationSet( - dex_file, annotation_set, "Ldalvik/annotation/InnerClass;", DexFile::kDexVisibilitySystem); + data.GetDexFile(), + annotation_set, + "Ldalvik/annotation/InnerClass;", + DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return false; } const uint8_t* annotation = - SearchEncodedAnnotation(dex_file, annotation_item->annotation_, "name"); + SearchEncodedAnnotation(data.GetDexFile(), annotation_item->annotation_, "name"); if (annotation == nullptr) { return false; } DexFile::AnnotationValue annotation_value; - if (!ProcessAnnotationValue<false>(klass, + if (!ProcessAnnotationValue<false>(data, &annotation, &annotation_value, ScopedNullHandle<mirror::Class>(), @@ -1293,24 +1380,24 @@ bool GetInnerClass(Handle<mirror::Class> klass, mirror::String** name) { } bool GetInnerClassFlags(Handle<mirror::Class> klass, uint32_t* flags) { - const DexFile& dex_file = klass->GetDexFile(); - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return false; } const DexFile::AnnotationItem* annotation_item = - SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/InnerClass;", + SearchAnnotationSet(data.GetDexFile(), annotation_set, "Ldalvik/annotation/InnerClass;", DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return false; } const uint8_t* annotation = - SearchEncodedAnnotation(dex_file, annotation_item->annotation_, "accessFlags"); + SearchEncodedAnnotation(data.GetDexFile(), annotation_item->annotation_, "accessFlags"); if (annotation == nullptr) { return false; } DexFile::AnnotationValue annotation_value; - if (!ProcessAnnotationValue<false>(klass, + if (!ProcessAnnotationValue<false>(data, &annotation, &annotation_value, ScopedNullHandle<mirror::Class>(), @@ -1325,20 +1412,22 @@ bool GetInnerClassFlags(Handle<mirror::Class> klass, uint32_t* flags) { } mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForClass(Handle<mirror::Class> klass) { - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return nullptr; } - return GetSignatureValue(klass, annotation_set); + return GetSignatureValue(data, annotation_set); } bool IsClassAnnotationPresent(Handle<mirror::Class> klass, Handle<mirror::Class> annotation_class) { - const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + ClassData data(klass); + const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { return false; } const DexFile::AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet( - klass, annotation_set, DexFile::kDexVisibilityRuntime, annotation_class); + data, annotation_set, DexFile::kDexVisibilityRuntime, annotation_class); return annotation_item != nullptr; } diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index aea9708ddc..7136f101aa 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -2171,7 +2171,9 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref) { fall_back_to_non_moving = true; to_ref = heap_->non_moving_space_->Alloc(Thread::Current(), obj_size, &non_moving_space_bytes_allocated, nullptr, &dummy); - CHECK(to_ref != nullptr) << "Fall-back non-moving space allocation failed"; + CHECK(to_ref != nullptr) << "Fall-back non-moving space allocation failed for a " + << obj_size << " byte object in region type " + << region_space_->GetRegionType(from_ref); bytes_allocated = non_moving_space_bytes_allocated; // Mark it in the mark bitmap. accounting::ContinuousSpaceBitmap* mark_bitmap = diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 00487c6728..2724b0062a 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -42,8 +42,6 @@ ProfileSaver::ProfileSaver(const ProfileSaverOptions& options, const std::vector<std::string>& code_paths) : jit_code_cache_(jit_code_cache), shutting_down_(false), - last_save_number_of_methods_(0), - last_save_number_of_classes_(0), last_time_ns_saver_woke_up_(0), jit_activity_notifications_(0), wait_lock_("ProfileSaver wait lock"), @@ -171,10 +169,10 @@ void ProfileSaver::NotifyJitActivityInternal() { } } -ProfileCompilationInfo* ProfileSaver::GetCachedProfiledInfo(const std::string& filename) { +ProfileSaver::ProfileInfoCache* ProfileSaver::GetCachedProfiledInfo(const std::string& filename) { auto info_it = profile_cache_.find(filename); if (info_it == profile_cache_.end()) { - info_it = profile_cache_.Put(filename, ProfileCompilationInfo()); + info_it = profile_cache_.Put(filename, ProfileInfoCache()); } return &info_it->second; } @@ -248,8 +246,9 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { << " (" << classes.GetDexLocation() << ")"; } } - ProfileCompilationInfo* info = GetCachedProfiledInfo(filename); - info->AddMethodsAndClasses(profile_methods_for_location, resolved_classes_for_location); + ProfileInfoCache* cached_info = GetCachedProfiledInfo(filename); + cached_info->profile.AddMethodsAndClasses(profile_methods_for_location, + resolved_classes_for_location); total_number_of_profile_entries_cached += resolved_classes_for_location.size(); } max_number_of_profile_entries_cached_ = std::max( @@ -283,14 +282,15 @@ bool ProfileSaver::ProcessProfilingInfo(uint16_t* new_methods) { total_number_of_code_cache_queries_++; } - ProfileCompilationInfo* cached_info = GetCachedProfiledInfo(filename); - cached_info->AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>()); + ProfileInfoCache* cached_info = GetCachedProfiledInfo(filename); + ProfileCompilationInfo* cached_profile = &cached_info->profile; + cached_profile->AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>()); int64_t delta_number_of_methods = - cached_info->GetNumberOfMethods() - - static_cast<int64_t>(last_save_number_of_methods_); + cached_profile->GetNumberOfMethods() - + static_cast<int64_t>(cached_info->last_save_number_of_methods); int64_t delta_number_of_classes = - cached_info->GetNumberOfResolvedClasses() - - static_cast<int64_t>(last_save_number_of_classes_); + cached_profile->GetNumberOfResolvedClasses() - + static_cast<int64_t>(cached_info->last_save_number_of_classes); if (delta_number_of_methods < options_.GetMinMethodsToSave() && delta_number_of_classes < options_.GetMinClassesToSave()) { @@ -304,12 +304,12 @@ bool ProfileSaver::ProcessProfilingInfo(uint16_t* new_methods) { uint64_t bytes_written; // Force the save. In case the profile data is corrupted or the the profile // has the wrong version this will "fix" the file to the correct format. - if (cached_info->MergeAndSave(filename, &bytes_written, /*force*/ true)) { - last_save_number_of_methods_ = cached_info->GetNumberOfMethods(); - last_save_number_of_classes_ = cached_info->GetNumberOfResolvedClasses(); + if (cached_profile->MergeAndSave(filename, &bytes_written, /*force*/ true)) { + cached_info->last_save_number_of_methods = cached_profile->GetNumberOfMethods(); + cached_info->last_save_number_of_classes = cached_profile->GetNumberOfResolvedClasses(); // Clear resolved classes. No need to store them around as // they don't change after the first write. - cached_info->ClearResolvedClasses(); + cached_profile->ClearResolvedClasses(); if (bytes_written > 0) { total_number_of_writes_++; total_bytes_written_ += bytes_written; @@ -326,8 +326,8 @@ bool ProfileSaver::ProcessProfilingInfo(uint16_t* new_methods) { total_number_of_failed_writes_++; } total_number_of_profile_entries_cached += - cached_info->GetNumberOfMethods() + - cached_info->GetNumberOfResolvedClasses(); + cached_profile->GetNumberOfMethods() + + cached_profile->GetNumberOfResolvedClasses(); } max_number_of_profile_entries_cached_ = std::max( max_number_of_profile_entries_cached_, @@ -526,10 +526,8 @@ bool ProfileSaver::HasSeenMethod(const std::string& profile, uint16_t method_idx) { MutexLock mu(Thread::Current(), *Locks::profiler_lock_); if (instance_ != nullptr) { - ProfileCompilationInfo* info = instance_->GetCachedProfiledInfo(profile); - if (info != nullptr) { - return info->ContainsMethod(MethodReference(dex_file, method_idx)); - } + const ProfileCompilationInfo& info = instance_->GetCachedProfiledInfo(profile)->profile; + return info.ContainsMethod(MethodReference(dex_file, method_idx)); } return false; } diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index ec8342ad9e..8e0682d3fe 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -59,6 +59,14 @@ class ProfileSaver { uint16_t method_idx); private: + // A cache structure which keeps track of the data saved to disk. + // It is used to reduce the number of disk read/writes. + struct ProfileInfoCache { + ProfileCompilationInfo profile; + uint32_t last_save_number_of_methods = 0; + uint32_t last_save_number_of_classes = 0; + }; + ProfileSaver(const ProfileSaverOptions& options, const std::string& output_filename, jit::JitCodeCache* jit_code_cache, @@ -90,7 +98,7 @@ class ProfileSaver { // Retrieves the cached profile compilation info for the given profile file. // If no entry exists, a new empty one will be created, added to the cache and // then returned. - ProfileCompilationInfo* GetCachedProfiledInfo(const std::string& filename); + ProfileInfoCache* GetCachedProfiledInfo(const std::string& filename); // Fetches the current resolved classes and methods from the ClassLinker and stores them in the // profile_cache_ for later save. void FetchAndCacheResolvedClassesAndMethods(); @@ -110,8 +118,6 @@ class ProfileSaver { GUARDED_BY(Locks::profiler_lock_); bool shutting_down_ GUARDED_BY(Locks::profiler_lock_); - uint32_t last_save_number_of_methods_; - uint32_t last_save_number_of_classes_; uint64_t last_time_ns_saver_woke_up_ GUARDED_BY(wait_lock_); uint32_t jit_activity_notifications_; @@ -119,7 +125,7 @@ class ProfileSaver { // profile information. The size of this cache is usually very small and tops // to just a few hundreds entries in the ProfileCompilationInfo objects. // It helps avoiding unnecessary writes to disk. - SafeMap<std::string, ProfileCompilationInfo> profile_cache_; + SafeMap<std::string, ProfileInfoCache> profile_cache_; // Save period condition support. Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 5048bad121..898d07d22c 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -61,7 +61,7 @@ class VdexFile { private: static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; - static constexpr uint8_t kVdexVersion[] = { '0', '0', '4', '\0' }; // dexlayout incompatibility + static constexpr uint8_t kVdexVersion[] = { '0', '0', '5', '\0' }; // access flags uint8_t magic_[4]; uint8_t version_[4]; diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index 52f7e348ce..740b7dd7d4 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -309,6 +309,7 @@ PreciseReferenceType::PreciseReferenceType(mirror::Class* klass, const StringPie // Note: no check for IsInstantiable() here. We may produce this in case an InstantiationError // would be thrown at runtime, but we need to continue verification and *not* create a // hard failure or abort. + CheckConstructorInvariants(this); } std::string UnresolvedMergedType::Dump() const { @@ -789,7 +790,7 @@ void RegType::CheckInvariants() const { if (!klass_.IsNull()) { CHECK(!descriptor_.empty()) << *this; std::string temp; - CHECK_EQ(descriptor_.ToString(), klass_.Read()->GetDescriptor(&temp)) << *this; + CHECK_EQ(descriptor_, klass_.Read()->GetDescriptor(&temp)) << *this; } } @@ -820,9 +821,7 @@ UnresolvedMergedType::UnresolvedMergedType(const RegType& resolved, reg_type_cache_(reg_type_cache), resolved_part_(resolved), unresolved_types_(unresolved, false, unresolved.GetAllocator()) { - if (kIsDebugBuild) { - CheckInvariants(); - } + CheckConstructorInvariants(this); } void UnresolvedMergedType::CheckInvariants() const { CHECK(reg_type_cache_ != nullptr); diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h index 472381dd9b..dedf77f7db 100644 --- a/runtime/verifier/reg_type.h +++ b/runtime/verifier/reg_type.h @@ -274,14 +274,17 @@ class RegType { uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : descriptor_(descriptor), klass_(klass), - cache_id_(cache_id) { + cache_id_(cache_id) {} + + template <typename Class> + void CheckConstructorInvariants(Class* this_ ATTRIBUTE_UNUSED) const + REQUIRES_SHARED(Locks::mutator_lock_) { + static_assert(std::is_final<Class>::value, "Class must be final."); if (kIsDebugBuild) { CheckInvariants(); } } - void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_); - const StringPiece descriptor_; mutable GcRoot<mirror::Class> klass_; // Non-const only due to moving classes. const uint16_t cache_id_; @@ -289,6 +292,8 @@ class RegType { friend class RegTypeCache; private: + virtual void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_); + /* * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of @@ -339,7 +344,9 @@ class ConflictType FINAL : public RegType { private: ConflictType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : RegType(klass, descriptor, cache_id) {} + : RegType(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } static const ConflictType* instance_; }; @@ -368,7 +375,9 @@ class UndefinedType FINAL : public RegType { private: UndefinedType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : RegType(klass, descriptor, cache_id) {} + : RegType(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } static const UndefinedType* instance_; }; @@ -387,7 +396,7 @@ class Cat1Type : public PrimitiveType { uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); }; -class IntegerType : public Cat1Type { +class IntegerType FINAL : public Cat1Type { public: bool IsInteger() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); @@ -401,7 +410,9 @@ class IntegerType : public Cat1Type { private: IntegerType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : Cat1Type(klass, descriptor, cache_id) {} + : Cat1Type(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } static const IntegerType* instance_; }; @@ -419,7 +430,9 @@ class BooleanType FINAL : public Cat1Type { private: BooleanType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : Cat1Type(klass, descriptor, cache_id) {} + : Cat1Type(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } static const BooleanType* instance_; }; @@ -438,7 +451,9 @@ class ByteType FINAL : public Cat1Type { private: ByteType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : Cat1Type(klass, descriptor, cache_id) {} + : Cat1Type(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } static const ByteType* instance_; }; @@ -456,7 +471,9 @@ class ShortType FINAL : public Cat1Type { private: ShortType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : Cat1Type(klass, descriptor, cache_id) {} + : Cat1Type(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } static const ShortType* instance_; }; @@ -474,7 +491,9 @@ class CharType FINAL : public Cat1Type { private: CharType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : Cat1Type(klass, descriptor, cache_id) {} + : Cat1Type(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } static const CharType* instance_; }; @@ -492,7 +511,9 @@ class FloatType FINAL : public Cat1Type { private: FloatType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : Cat1Type(klass, descriptor, cache_id) {} + : Cat1Type(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } static const FloatType* instance_; }; @@ -517,7 +538,9 @@ class LongLoType FINAL : public Cat2Type { private: LongLoType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : Cat2Type(klass, descriptor, cache_id) {} + : Cat2Type(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } static const LongLoType* instance_; }; @@ -535,7 +558,9 @@ class LongHiType FINAL : public Cat2Type { private: LongHiType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : Cat2Type(klass, descriptor, cache_id) {} + : Cat2Type(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } static const LongHiType* instance_; }; @@ -554,7 +579,9 @@ class DoubleLoType FINAL : public Cat2Type { private: DoubleLoType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : Cat2Type(klass, descriptor, cache_id) {} + : Cat2Type(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } static const DoubleLoType* instance_; }; @@ -572,7 +599,9 @@ class DoubleHiType FINAL : public Cat2Type { private: DoubleHiType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : Cat2Type(klass, descriptor, cache_id) {} + : Cat2Type(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } static const DoubleHiType* instance_; }; @@ -637,7 +666,9 @@ class PreciseConstType FINAL : public ConstantType { public: PreciseConstType(uint32_t constant, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : ConstantType(constant, cache_id) {} + : ConstantType(constant, cache_id) { + CheckConstructorInvariants(this); + } bool IsPreciseConstant() const OVERRIDE { return true; } @@ -648,7 +679,9 @@ class PreciseConstLoType FINAL : public ConstantType { public: PreciseConstLoType(uint32_t constant, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : ConstantType(constant, cache_id) {} + : ConstantType(constant, cache_id) { + CheckConstructorInvariants(this); + } bool IsPreciseConstantLo() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); }; @@ -657,7 +690,9 @@ class PreciseConstHiType FINAL : public ConstantType { public: PreciseConstHiType(uint32_t constant, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : ConstantType(constant, cache_id) {} + : ConstantType(constant, cache_id) { + CheckConstructorInvariants(this); + } bool IsPreciseConstantHi() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); }; @@ -667,6 +702,7 @@ class ImpreciseConstType FINAL : public ConstantType { ImpreciseConstType(uint32_t constat, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : ConstantType(constat, cache_id) { + CheckConstructorInvariants(this); } bool IsImpreciseConstant() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); @@ -676,7 +712,9 @@ class ImpreciseConstLoType FINAL : public ConstantType { public: ImpreciseConstLoType(uint32_t constant, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : ConstantType(constant, cache_id) {} + : ConstantType(constant, cache_id) { + CheckConstructorInvariants(this); + } bool IsImpreciseConstantLo() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); }; @@ -685,7 +723,9 @@ class ImpreciseConstHiType FINAL : public ConstantType { public: ImpreciseConstHiType(uint32_t constant, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : ConstantType(constant, cache_id) {} + : ConstantType(constant, cache_id) { + CheckConstructorInvariants(this); + } bool IsImpreciseConstantHi() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); }; @@ -718,7 +758,9 @@ class UninitializedReferenceType FINAL : public UninitializedType { const StringPiece& descriptor, uint32_t allocation_pc, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : UninitializedType(klass, descriptor, allocation_pc, cache_id) {} + : UninitializedType(klass, descriptor, allocation_pc, cache_id) { + CheckConstructorInvariants(this); + } bool IsUninitializedReference() const OVERRIDE { return true; } @@ -735,9 +777,7 @@ class UnresolvedUninitializedRefType FINAL : public UninitializedType { uint32_t allocation_pc, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : UninitializedType(nullptr, descriptor, allocation_pc, cache_id) { - if (kIsDebugBuild) { - CheckInvariants(); - } + CheckConstructorInvariants(this); } bool IsUnresolvedAndUninitializedReference() const OVERRIDE { return true; } @@ -747,7 +787,7 @@ class UnresolvedUninitializedRefType FINAL : public UninitializedType { std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); private: - void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_); + void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE; }; // Similar to UninitializedReferenceType but special case for the this argument @@ -759,9 +799,7 @@ class UninitializedThisReferenceType FINAL : public UninitializedType { uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : UninitializedType(klass, descriptor, 0, cache_id) { - if (kIsDebugBuild) { - CheckInvariants(); - } + CheckConstructorInvariants(this); } virtual bool IsUninitializedThisReference() const OVERRIDE { return true; } @@ -771,7 +809,7 @@ class UninitializedThisReferenceType FINAL : public UninitializedType { std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); private: - void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_); + void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE; }; class UnresolvedUninitializedThisRefType FINAL : public UninitializedType { @@ -780,9 +818,7 @@ class UnresolvedUninitializedThisRefType FINAL : public UninitializedType { uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : UninitializedType(nullptr, descriptor, 0, cache_id) { - if (kIsDebugBuild) { - CheckInvariants(); - } + CheckConstructorInvariants(this); } bool IsUnresolvedAndUninitializedThisReference() const OVERRIDE { return true; } @@ -792,7 +828,7 @@ class UnresolvedUninitializedThisRefType FINAL : public UninitializedType { std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); private: - void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_); + void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE; }; // A type of register holding a reference to an Object of type GetClass or a @@ -801,7 +837,9 @@ class ReferenceType FINAL : public RegType { public: ReferenceType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) - : RegType(klass, descriptor, cache_id) {} + : RegType(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } bool IsReference() const OVERRIDE { return true; } @@ -848,9 +886,7 @@ class UnresolvedReferenceType FINAL : public UnresolvedType { UnresolvedReferenceType(const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : UnresolvedType(descriptor, cache_id) { - if (kIsDebugBuild) { - CheckInvariants(); - } + CheckConstructorInvariants(this); } bool IsUnresolvedReference() const OVERRIDE { return true; } @@ -860,7 +896,7 @@ class UnresolvedReferenceType FINAL : public UnresolvedType { std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); private: - void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_); + void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE; }; // Type representing the super-class of an unresolved type. @@ -872,9 +908,7 @@ class UnresolvedSuperClass FINAL : public UnresolvedType { : UnresolvedType("", cache_id), unresolved_child_id_(child_id), reg_type_cache_(reg_type_cache) { - if (kIsDebugBuild) { - CheckInvariants(); - } + CheckConstructorInvariants(this); } bool IsUnresolvedSuperClass() const OVERRIDE { return true; } @@ -889,7 +923,7 @@ class UnresolvedSuperClass FINAL : public UnresolvedType { std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); private: - void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_); + void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE; const uint16_t unresolved_child_id_; const RegTypeCache* const reg_type_cache_; @@ -925,7 +959,7 @@ class UnresolvedMergedType FINAL : public UnresolvedType { std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); private: - void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_); + void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE; const RegTypeCache* const reg_type_cache_; diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index 000cf7c393..d477ecdb7c 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -68,13 +68,17 @@ const VerifierDeps::DexFileDeps* VerifierDeps::GetDexFileDeps(const DexFile& dex return (it == dex_deps_.end()) ? nullptr : it->second.get(); } +// Access flags that impact vdex verification. +static constexpr uint32_t kAccVdexAccessFlags = + kAccPublic | kAccPrivate | kAccProtected | kAccStatic | kAccInterface; + template <typename T> uint16_t VerifierDeps::GetAccessFlags(T* element) { static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant"); if (element == nullptr) { return VerifierDeps::kUnresolvedMarker; } else { - uint16_t access_flags = Low16Bits(element->GetAccessFlags()); + uint16_t access_flags = Low16Bits(element->GetAccessFlags()) & kAccVdexAccessFlags; CHECK_NE(access_flags, VerifierDeps::kUnresolvedMarker); return access_flags; } diff --git a/test/701-easy-div-rem/build b/test/701-easy-div-rem/build index 666fe895b5..d83ee82b47 100644 --- a/test/701-easy-div-rem/build +++ b/test/701-easy-div-rem/build @@ -21,12 +21,4 @@ set -e mkdir src python ./genMain.py -# Increase the file size limitation for classes.lst as the machine generated -# source file contains a lot of methods and is quite large. - -# Jack generates big temp files so only apply ulimit for dx. -if [ ${USE_JACK} = "false" ]; then - ulimit -S 4096 -fi - ./default-build diff --git a/test/961-default-iface-resolution-gen/build b/test/961-default-iface-resolution-gen/build index ccebbe4ac9..2f7e3ba553 100755 --- a/test/961-default-iface-resolution-gen/build +++ b/test/961-default-iface-resolution-gen/build @@ -17,15 +17,6 @@ # make us exit on a failure set -e -# We will be making more files than the ulimit is set to allow. Remove it temporarily. -OLD_ULIMIT=`ulimit -S` -ulimit -S unlimited - -restore_ulimit() { - ulimit -S "$OLD_ULIMIT" -} -trap 'restore_ulimit' ERR - if [[ $@ != *"--jvm"* ]]; then # Don't do anything with jvm # Hard-wired use of experimental jack. @@ -39,6 +30,3 @@ mkdir -p ./src ./util-src/generate_java.py ./src ./expected.txt ./default-build "$@" --experimental default-methods - -# Reset the ulimit back to its initial value -restore_ulimit diff --git a/test/964-default-iface-init-gen/build b/test/964-default-iface-init-gen/build index ccebbe4ac9..2f7e3ba553 100755 --- a/test/964-default-iface-init-gen/build +++ b/test/964-default-iface-init-gen/build @@ -17,15 +17,6 @@ # make us exit on a failure set -e -# We will be making more files than the ulimit is set to allow. Remove it temporarily. -OLD_ULIMIT=`ulimit -S` -ulimit -S unlimited - -restore_ulimit() { - ulimit -S "$OLD_ULIMIT" -} -trap 'restore_ulimit' ERR - if [[ $@ != *"--jvm"* ]]; then # Don't do anything with jvm # Hard-wired use of experimental jack. @@ -39,6 +30,3 @@ mkdir -p ./src ./util-src/generate_java.py ./src ./expected.txt ./default-build "$@" --experimental default-methods - -# Reset the ulimit back to its initial value -restore_ulimit diff --git a/test/968-default-partial-compile-gen/build b/test/968-default-partial-compile-gen/build index 1e9f8aadd5..00ccb89faf 100755 --- a/test/968-default-partial-compile-gen/build +++ b/test/968-default-partial-compile-gen/build @@ -17,15 +17,6 @@ # make us exit on a failure set -e -# We will be making more files than the ulimit is set to allow. Remove it temporarily. -OLD_ULIMIT=`ulimit -S` -ulimit -S unlimited - -restore_ulimit() { - ulimit -S "$OLD_ULIMIT" -} -trap 'restore_ulimit' ERR - # TODO: Support running with jack. if [[ $@ == *"--jvm"* ]]; then @@ -45,6 +36,3 @@ else # Use the default build script ./default-build "$@" "$EXTRA_ARGS" --experimental default-methods fi - -# Reset the ulimit back to its initial value -restore_ulimit diff --git a/test/970-iface-super-resolution-gen/build b/test/970-iface-super-resolution-gen/build index fd1b271c1c..7217fac601 100755 --- a/test/970-iface-super-resolution-gen/build +++ b/test/970-iface-super-resolution-gen/build @@ -17,15 +17,6 @@ # make us exit on a failure set -e -# We will be making more files than the ulimit is set to allow. Remove it temporarily. -OLD_ULIMIT=`ulimit -S` -ulimit -S unlimited - -restore_ulimit() { - ulimit -S "$OLD_ULIMIT" -} -trap 'restore_ulimit' ERR - # Should we compile with Java source code. By default we will use Smali. USES_JAVA_SOURCE="false" if [[ $@ == *"--jvm"* ]]; then @@ -50,6 +41,3 @@ else fi ./default-build "$@" --experimental default-methods - -# Reset the ulimit back to its initial value -restore_ulimit diff --git a/test/971-iface-super/build b/test/971-iface-super/build index 1e9f8aadd5..00ccb89faf 100755 --- a/test/971-iface-super/build +++ b/test/971-iface-super/build @@ -17,15 +17,6 @@ # make us exit on a failure set -e -# We will be making more files than the ulimit is set to allow. Remove it temporarily. -OLD_ULIMIT=`ulimit -S` -ulimit -S unlimited - -restore_ulimit() { - ulimit -S "$OLD_ULIMIT" -} -trap 'restore_ulimit' ERR - # TODO: Support running with jack. if [[ $@ == *"--jvm"* ]]; then @@ -45,6 +36,3 @@ else # Use the default build script ./default-build "$@" "$EXTRA_ARGS" --experimental default-methods fi - -# Reset the ulimit back to its initial value -restore_ulimit diff --git a/test/980-redefine-object/check b/test/980-redefine-object/check new file mode 100755 index 0000000000..987066fe15 --- /dev/null +++ b/test/980-redefine-object/check @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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. + +# The number of paused background threads (and therefore InterruptedExceptions) +# can change so we will just delete their lines from the log. + +sed "/Object allocated of type 'Ljava\/lang\/InterruptedException;'/d" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null diff --git a/test/980-redefine-object/expected.txt b/test/980-redefine-object/expected.txt new file mode 100644 index 0000000000..6e9bce027a --- /dev/null +++ b/test/980-redefine-object/expected.txt @@ -0,0 +1,52 @@ + Initializing and loading the TestWatcher class that will (eventually) be notified of object allocations + Allocating an j.l.Object before redefining Object class + Allocating a Transform before redefining Object class + Redefining the Object class to add a hook into the <init> method +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Allocating an j.l.Object after redefining Object class +Object allocated of type 'Ljava/lang/Object;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Allocating a Transform after redefining Object class +Object allocated of type 'LTransform;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Allocating an int[] after redefining Object class +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Allocating an array list +Object allocated of type 'Ljava/util/ArrayList;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Adding a bunch of stuff to the array list +Object allocated of type 'Ljava/lang/Object;' +Object allocated of type 'Ljava/lang/Object;' +Object allocated of type 'LTransform;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Allocating a linked list +Object allocated of type 'Ljava/util/LinkedList;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Adding a bunch of stuff to the linked list +Object allocated of type 'Ljava/lang/Object;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'Ljava/lang/Object;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'LTransform;' +Object allocated of type 'Ljava/util/LinkedList$Node;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Throwing from down 4 stack frames +Object allocated of type 'Ljava/lang/Exception;' +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Exception caught. +Object allocated of type 'Ljava/lang/StringBuilder;' +Object allocated of type 'Ljava/nio/HeapCharBuffer;' + Finishing test! diff --git a/test/980-redefine-object/info.txt b/test/980-redefine-object/info.txt new file mode 100644 index 0000000000..f3e01b596d --- /dev/null +++ b/test/980-redefine-object/info.txt @@ -0,0 +1,23 @@ +Tests basic functions in the jvmti plugin. + +This tests that we are able to redefine methods/constructors on the +java.lang.Object class at runtime. + +This also (indirectly) tests that we correctly handle reading annotations on +obsolete methods. This is something that is not normally done since there is no +way to get a reference to an obsolete method outside of the runtime but some +annotations on the Object class are read by the runtime directly. + +NB This test cannot be run on the RI at the moment. + +If this test starts failing during the doCommonClassRedefinition call it is +possible that the definition of Object contained in the base64 DEX_BYTES array +has become stale and will need to be recreated. The only difference from the +normal Object dex bytes is that (a) it contains only the bytes of the Object +class itself, and (b) it adds an +'invoke-static {p0}, Ljava/lang/Object;->NotifyConstructed(Ljava/lang/Object;)V' +to the <init> function. + +It is also possible it could fail due to the pattern of allocations caused by +doing string concatenation or printing changing. In this case you should simply +update the expected.txt file. diff --git a/test/980-redefine-object/redefine_object.cc b/test/980-redefine-object/redefine_object.cc new file mode 100644 index 0000000000..daae08792a --- /dev/null +++ b/test/980-redefine-object/redefine_object.cc @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 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 <inttypes.h> +#include <iostream> + +#include "android-base/stringprintf.h" +#include "base/logging.h" +#include "base/macros.h" +#include "jni.h" +#include "jvmti.h" +#include "ScopedUtfChars.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test980RedefineObjects { + +extern "C" JNIEXPORT void JNICALL Java_Main_bindFunctionsForClass( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass target) { + BindFunctionsOnClass(jvmti_env, env, target); +} + +extern "C" JNIEXPORT void JNICALL Java_art_test_TestWatcher_NotifyConstructed( + JNIEnv* env, jclass TestWatcherClass ATTRIBUTE_UNUSED, jobject constructed) { + char* sig = nullptr; + char* generic_sig = nullptr; + if (JvmtiErrorToException(env, jvmti_env->GetClassSignature(env->GetObjectClass(constructed), + &sig, + &generic_sig))) { + // Exception. + return; + } + std::cout << "Object allocated of type '" << sig << "'" << std::endl; + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig)); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(generic_sig)); +} + +} // namespace Test980RedefineObjects +} // namespace art diff --git a/test/980-redefine-object/run b/test/980-redefine-object/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/980-redefine-object/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 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. + +./default-run "$@" --jvmti diff --git a/test/980-redefine-object/src-ex/TestWatcher.java b/test/980-redefine-object/src-ex/TestWatcher.java new file mode 100644 index 0000000000..d15e68871c --- /dev/null +++ b/test/980-redefine-object/src-ex/TestWatcher.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2017 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. + */ + +package art.test; + +public class TestWatcher { + // NB This function is native since it is called in the Object.<init> method and so cannot cause + // any java allocations at all. The normal System.out.print* functions will cause allocations to + // occur so we cannot use them. This means the easiest way to report the object as being created + // is to go into native code and do it there. + public static native void NotifyConstructed(Object o); +} diff --git a/test/980-redefine-object/src/Main.java b/test/980-redefine-object/src/Main.java new file mode 100644 index 0000000000..348951c4ba --- /dev/null +++ b/test/980-redefine-object/src/Main.java @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2017 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. + */ + +import java.util.ArrayList; +import java.util.Base64; +import java.util.LinkedList; + +public class Main { + + // TODO We should make this run on the RI. + /** + * This test cannot be run on the RI. + */ + private static final byte[] CLASS_BYTES = new byte[0]; + + // TODO It might be a good idea to replace this hard-coded Object definition with a + // retransformation based test. + /** + * Base64 encoding of the following smali file. + * + * .class public Ljava/lang/Object; + * .source "Object.java" + * # instance fields + * .field private transient shadow$_klass_:Ljava/lang/Class; + * .annotation system Ldalvik/annotation/Signature; + * value = { + * "Ljava/lang/Class", + * "<*>;" + * } + * .end annotation + * .end field + * + * .field private transient shadow$_monitor_:I + * # direct methods + * .method public constructor <init>()V + * .registers 1 + * .prologue + * invoke-static {p0}, Lart/test/TestWatcher;->NotifyConstructed(Ljava/lang/Object;)V + * return-void + * .end method + * + * .method static identityHashCode(Ljava/lang/Object;)I + * .registers 7 + * .prologue + * iget v0, p0, Ljava/lang/Object;->shadow$_monitor_:I + * const/high16 v3, -0x40000000 # -2.0f + * const/high16 v2, -0x80000000 + * const v1, 0xfffffff + * const/high16 v4, -0x40000000 # -2.0f + * and-int/2addr v4, v0 + * const/high16 v5, -0x80000000 + * if-ne v4, v5, :cond_15 + * const v4, 0xfffffff + * and-int/2addr v4, v0 + * return v4 + * :cond_15 + * invoke-static {p0}, Ljava/lang/Object;->identityHashCodeNative(Ljava/lang/Object;)I + * move-result v4 + * return v4 + * .end method + * + * .method private static native identityHashCodeNative(Ljava/lang/Object;)I + * .annotation build Ldalvik/annotation/optimization/FastNative; + * .end annotation + * .end method + * + * .method private native internalClone()Ljava/lang/Object; + * .annotation build Ldalvik/annotation/optimization/FastNative; + * .end annotation + * .end method + * + * + * # virtual methods + * .method protected clone()Ljava/lang/Object; + * .registers 4 + * .annotation system Ldalvik/annotation/Throws; + * value = { + * Ljava/lang/CloneNotSupportedException; + * } + * .end annotation + * + * .prologue + * instance-of v0, p0, Ljava/lang/Cloneable; + * if-nez v0, :cond_2d + * new-instance v0, Ljava/lang/CloneNotSupportedException; + * new-instance v1, Ljava/lang/StringBuilder; + * invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V + * const-string/jumbo v2, "Class " + * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + * move-result-object v1 + * invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + * move-result-object v2 + * invoke-virtual {v2}, Ljava/lang/Class;->getName()Ljava/lang/String; + * move-result-object v2 + * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + * move-result-object v1 + * const-string/jumbo v2, " doesn\'t implement Cloneable" + * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + * move-result-object v1 + * invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + * move-result-object v1 + * invoke-direct {v0, v1}, Ljava/lang/CloneNotSupportedException;-><init>(Ljava/lang/String;)V + * throw v0 + * :cond_2d + * invoke-direct {p0}, Ljava/lang/Object;->internalClone()Ljava/lang/Object; + * move-result-object v0 + * return-object v0 + * .end method + * + * .method public equals(Ljava/lang/Object;)Z + * .registers 3 + * .prologue + * if-ne p0, p1, :cond_4 + * const/4 v0, 0x1 + * :goto_3 + * return v0 + * :cond_4 + * const/4 v0, 0x0 + * goto :goto_3 + * .end method + * + * .method protected finalize()V + * .registers 1 + * .annotation system Ldalvik/annotation/Throws; + * value = { + * Ljava/lang/Throwable; + * } + * .end annotation + * .prologue + * return-void + * .end method + * + * .method public final getClass()Ljava/lang/Class; + * .registers 2 + * .annotation system Ldalvik/annotation/Signature; + * value = { + * "()", + * "Ljava/lang/Class", + * "<*>;" + * } + * .end annotation + * .prologue + * iget-object v0, p0, Ljava/lang/Object;->shadow$_klass_:Ljava/lang/Class; + * return-object v0 + * .end method + * + * .method public hashCode()I + * .registers 2 + * .prologue + * invoke-static {p0}, Ljava/lang/Object;->identityHashCode(Ljava/lang/Object;)I + * move-result v0 + * return v0 + * .end method + * + * .method public final native notify()V + * .annotation build Ldalvik/annotation/optimization/FastNative; + * .end annotation + * .end method + * + * .method public final native notifyAll()V + * .annotation build Ldalvik/annotation/optimization/FastNative; + * .end annotation + * .end method + * + * .method public toString()Ljava/lang/String; + * .registers 3 + * .prologue + * new-instance v0, Ljava/lang/StringBuilder; + * invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V + * invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + * move-result-object v1 + * invoke-virtual {v1}, Ljava/lang/Class;->getName()Ljava/lang/String; + * move-result-object v1 + * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + * move-result-object v0 + * const-string/jumbo v1, "@" + * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + * move-result-object v0 + * invoke-virtual {p0}, Ljava/lang/Object;->hashCode()I + * move-result v1 + * invoke-static {v1}, Ljava/lang/Integer;->toHexString(I)Ljava/lang/String; + * move-result-object v1 + * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + * move-result-object v0 + * invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + * move-result-object v0 + * return-object v0 + * .end method + * + * .method public final native wait()V + * .annotation system Ldalvik/annotation/Throws; + * value = { + * Ljava/lang/InterruptedException; + * } + * .end annotation + * + * .annotation build Ldalvik/annotation/optimization/FastNative; + * .end annotation + * .end method + * + * .method public final wait(J)V + * .registers 4 + * .annotation system Ldalvik/annotation/Throws; + * value = { + * Ljava/lang/InterruptedException; + * } + * .end annotation + * .prologue + * const/4 v0, 0x0 + * invoke-virtual {p0, p1, p2, v0}, Ljava/lang/Object;->wait(JI)V + * return-void + * .end method + * + * .method public final native wait(JI)V + * .annotation system Ldalvik/annotation/Throws; + * value = { + * Ljava/lang/InterruptedException; + * } + * .end annotation + * + * .annotation build Ldalvik/annotation/optimization/FastNative; + * .end annotation + * .end method + */ + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQDUlMR9j03MYuOKekKs2p7zJzu2IfDb7RlMCgAAcAAAAHhWNBIAAAAAAAAAAIgJAAA6" + + "AAAAcAAAABEAAABYAQAADQAAAJwBAAACAAAAOAIAABYAAABIAgAAAQAAAPgCAAA0BwAAGAMAABgD" + + "AAA2AwAAOgMAAEADAABIAwAASwMAAFMDAABWAwAAWgMAAF0DAABgAwAAZAMAAGgDAACAAwAAnwMA" + + "ALsDAADoAwAA+gMAAA0EAAA1BAAATAQAAGEEAACDBAAAlwQAAKsEAADGBAAA3QQAAPAEAAD9BAAA" + + "AAUAAAQFAAAJBQAADQUAABAFAAAUBQAAHAUAACMFAAArBQAANQUAAD8FAABIBQAAUgUAAGQFAAB8" + + "BQAAiwUAAJUFAACnBQAAugUAAM0FAADVBQAA3QUAAOgFAADtBQAA/QUAAA8GAAAcBgAAJgYAAC0G" + + "AAAGAAAACAAAAAwAAAANAAAADgAAAA8AAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAA" + + "ABkAAAAcAAAAIAAAAAYAAAAAAAAAAAAAAAcAAAAAAAAAPAYAAAkAAAAGAAAAAAAAAAkAAAALAAAA" + + "AAAAAAkAAAAMAAAAAAAAAAoAAAAMAAAARAYAAAsAAAANAAAAVAYAABwAAAAPAAAAAAAAAB0AAAAP" + + "AAAATAYAAB4AAAAPAAAANAYAAB8AAAAPAAAAPAYAAB8AAAAPAAAAVAYAACEAAAAQAAAAPAYAAAsA" + + "BgA0AAAACwAAADUAAAACAAoAGgAAAAYABAAnAAAABwALAAMAAAAJAAUANgAAAAsABwADAAAACwAD" + + "ACMAAAALAAwAJAAAAAsABwAlAAAACwACACYAAAALAAAAKAAAAAsAAQApAAAACwABACoAAAALAAMA" + + "KwAAAAsABwAxAAAACwAHADIAAAALAAQANwAAAAsABwA5AAAACwAIADkAAAALAAkAOQAAAA0ABwAD" + + "AAAADQAGACIAAAANAAQANwAAAAsAAAABAAAA/////wAAAAAbAAAA0AYAAD4JAAAAAAAAHCBkb2Vz" + + "bid0IGltcGxlbWVudCBDbG9uZWFibGUAAigpAAQ8Kj47AAY8aW5pdD4AAUAABkNsYXNzIAABSQAC" + + "SUwAAUoAAUwAAkxJAAJMTAAWTGFydC90ZXN0L1Rlc3RXYXRjaGVyOwAdTGRhbHZpay9hbm5vdGF0" + + "aW9uL1NpZ25hdHVyZTsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ACtMZGFsdmlrL2Fubm90" + + "YXRpb24vb3B0aW1pemF0aW9uL0Zhc3ROYXRpdmU7ABBMamF2YS9sYW5nL0NsYXNzABFMamF2YS9s" + + "YW5nL0NsYXNzOwAmTGphdmEvbGFuZy9DbG9uZU5vdFN1cHBvcnRlZEV4Y2VwdGlvbjsAFUxqYXZh" + + "L2xhbmcvQ2xvbmVhYmxlOwATTGphdmEvbGFuZy9JbnRlZ2VyOwAgTGphdmEvbGFuZy9JbnRlcnJ1" + + "cHRlZEV4Y2VwdGlvbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlM" + + "amF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7ABVMamF2YS9sYW5nL1Rocm93YWJsZTsAEU5vdGlmeUNv" + + "bnN0cnVjdGVkAAtPYmplY3QuamF2YQABVgACVkoAA1ZKSQACVkwAAVoAAlpMAAZhcHBlbmQABWNs" + + "b25lAAZlcXVhbHMACGZpbmFsaXplAAhnZXRDbGFzcwAHZ2V0TmFtZQAIaGFzaENvZGUAEGlkZW50" + + "aXR5SGFzaENvZGUAFmlkZW50aXR5SGFzaENvZGVOYXRpdmUADWludGVybmFsQ2xvbmUACGxvY2tX" + + "b3JkABBsb2NrV29yZEhhc2hNYXNrABFsb2NrV29yZFN0YXRlSGFzaAARbG9ja1dvcmRTdGF0ZU1h" + + "c2sABm1pbGxpcwAGbm90aWZ5AAlub3RpZnlBbGwAA29iagAOc2hhZG93JF9rbGFzc18AEHNoYWRv" + + "dyRfbW9uaXRvcl8AC3RvSGV4U3RyaW5nAAh0b1N0cmluZwAFdmFsdWUABHdhaXQAAAIAAAABAAAA" + + "AQAAAAsAAAABAAAAAAAAAAEAAAABAAAAAQAAAAwAAgQBOBwBGAcCBAE4HAEYCgIDATgcAhcQFwIC" + + "BAE4HAEYDgAFAAIDATgcAxcBFxAXAgAAAAAAAAAAAAEAAABaBgAAAgAAAGIGAAB8BgAAAQAAAGIG" + + "AAABAAAAagYAAAEAAAB0BgAAAQAAAHwGAAABAAAAfwYAAAAAAAABAAAACgAAAAAAAAAAAAAAsAYA" + + "AAUAAACUBgAABwAAALgGAAAIAAAAyAYAAAsAAADABgAADAAAAMAGAAANAAAAwAYAAA4AAADABgAA" + + "EAAAAJwGAAARAAAAqAYAABIAAACcBgAAKAAHDgBwATQHDi0DAC0BLQMDMAEtAwIvATwDAS4BeFsA" + + "7AEABw5LARoPOsYArAEBNAcOAMUEAAcOAEEABw4AaAAHDgCRAgAHDgCmAwExBw5LAAAAAQABAAEA" + + "AAA4BwAABAAAAHEQAAAAAA4ABwABAAEAAAA9BwAAGgAAAFJgAQAVAwDAFQIAgBQB////DxUEAMC1" + + "BBUFAIAzVAcAFAT///8PtQQPBHEQCwAGAAoEDwQEAAEAAgAAAFkHAAAyAAAAIDAIADkAKwAiAAcA" + + "IgENAHAQEwABABsCBQAAAG4gFAAhAAwBbhAIAAMADAJuEAEAAgAMAm4gFAAhAAwBGwIAAAAAbiAU" + + "ACEADAFuEBUAAQAMAXAgAgAQACcAcBAMAAMADAARAAMAAgAAAAAAZQcAAAYAAAAzIQQAEhAPABIA" + + "KP4BAAEAAAAAAGwHAAABAAAADgAAAAIAAQAAAAAAcgcAAAMAAABUEAAAEQAAAAIAAQABAAAAdwcA" + + "AAUAAABxEAoAAQAKAA8AAAADAAEAAgAAAHwHAAApAAAAIgANAHAQEwAAAG4QCAACAAwBbhABAAEA" + + "DAFuIBQAEAAMABsBBAAAAG4gFAAQAAwAbhAJAAIACgFxEAMAAQAMAW4gFAAQAAwAbhAVAAAADAAR" + + "AAAABAADAAQAAACCBwAABQAAABIAbkASACEDDgAAAgQLAIIBAYIBBIGABIwPBgikDwGKAgABggIA" + + "BQToDwEB3BABBPgQARGMEQEBpBEEkQIAAZECAAEBwBEBkQIAARGkEgGRAgAAABAAAAAAAAAAAQAA" + + "AAAAAAABAAAAOgAAAHAAAAACAAAAEQAAAFgBAAADAAAADQAAAJwBAAAEAAAAAgAAADgCAAAFAAAA" + + "FgAAAEgCAAAGAAAAAQAAAPgCAAACIAAAOgAAABgDAAABEAAABQAAADQGAAAEIAAABgAAAFoGAAAD" + + "EAAACQAAAIwGAAAGIAAAAQAAANAGAAADIAAACQAAADgHAAABIAAACQAAAIwHAAAAIAAAAQAAAD4J" + + "AAAAEAAAAQAAAIgJAAA="); + + private static final String LISTENER_LOCATION = + System.getenv("DEX_LOCATION") + "/980-redefine-object-ex.jar"; + + public static void main(String[] args) { + doTest(); + } + + private static void ensureTestWatcherInitialized() { + try { + // Make sure the TestWatcher class can be found from the Object <init> function. + addToBootClassLoader(LISTENER_LOCATION); + // Load TestWatcher from the bootclassloader and make sure it is initialized. + Class<?> testwatcher_class = Class.forName("art.test.TestWatcher", true, null); + // Bind the native functions of testwatcher_class. + bindFunctionsForClass(testwatcher_class); + } catch (Exception e) { + throw new Error("Exception while making testwatcher", e); + } + } + + // NB This function will cause 2 objects of type "Ljava/nio/HeapCharBuffer;" and + // "Ljava/nio/HeapCharBuffer;" to be allocated each time it is called. + private static void safePrintln(Object o) { + System.out.flush(); + System.out.print("\t" + o + "\n"); + System.out.flush(); + } + + private static void throwFrom(int depth) throws Exception { + if (depth <= 0) { + throw new Exception("Throwing the exception"); + } else { + throwFrom(depth - 1); + } + } + + public static void doTest() { + safePrintln("Initializing and loading the TestWatcher class that will (eventually) be " + + "notified of object allocations"); + // Make sure the TestWatcher class is initialized before we do anything else. + ensureTestWatcherInitialized(); + safePrintln("Allocating an j.l.Object before redefining Object class"); + // Make sure these aren't shown. + Object o = new Object(); + safePrintln("Allocating a Transform before redefining Object class"); + Transform t = new Transform(); + + // Redefine the Object Class. + safePrintln("Redefining the Object class to add a hook into the <init> method"); + doCommonClassRedefinition(Object.class, CLASS_BYTES, DEX_BYTES); + + safePrintln("Allocating an j.l.Object after redefining Object class"); + Object o2 = new Object(); + safePrintln("Allocating a Transform after redefining Object class"); + Transform t2 = new Transform(); + + // This shouldn't cause the Object constructor to be run. + safePrintln("Allocating an int[] after redefining Object class"); + int[] abc = new int[12]; + + // Try adding stuff to an array list. + safePrintln("Allocating an array list"); + ArrayList<Object> al = new ArrayList<>(); + safePrintln("Adding a bunch of stuff to the array list"); + al.add(new Object()); + al.add(new Object()); + al.add(o2); + al.add(o); + al.add(t); + al.add(t2); + al.add(new Transform()); + + // Try adding stuff to a LinkedList + safePrintln("Allocating a linked list"); + LinkedList<Object> ll = new LinkedList<>(); + safePrintln("Adding a bunch of stuff to the linked list"); + ll.add(new Object()); + ll.add(new Object()); + ll.add(o2); + ll.add(o); + ll.add(t); + ll.add(t2); + ll.add(new Transform()); + + // Try making an exception. + safePrintln("Throwing from down 4 stack frames"); + try { + throwFrom(4); + } catch (Exception e) { + safePrintln("Exception caught."); + } + + safePrintln("Finishing test!"); + } + + private static native void addToBootClassLoader(String s); + + private static native void bindFunctionsForClass(Class<?> target); + + // Transforms the class + private static native void doCommonClassRedefinition(Class<?> target, + byte[] class_file, + byte[] dex_file); +} diff --git a/test/980-redefine-object/src/Transform.java b/test/980-redefine-object/src/Transform.java new file mode 100644 index 0000000000..23f67d96c7 --- /dev/null +++ b/test/980-redefine-object/src/Transform.java @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2016 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. + */ + +class Transform { } diff --git a/test/Android.bp b/test/Android.bp index 9911af0b03..4ebfd7429a 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -275,6 +275,7 @@ art_cc_defaults { "936-search-onload/search_onload.cc", "944-transform-classloaders/classloader.cc", "945-obsolete-native/obsolete_native.cc", + "980-redefine-object/redefine_object.cc", ], shared_libs: [ "libbase", diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index e4e571cbcf..808e58a7bd 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -444,27 +444,11 @@ fi JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni" +COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xnorelocate" if [ "$RELOCATE" = "y" ]; then - COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xnorelocate" FLAGS="${FLAGS} -Xrelocate" - if [ "$HOST" = "y" ]; then - # Run test sets a fairly draconian ulimit that we will likely blow right over - # since we are relocating. Get the total size of the /system/framework directory - # in 512 byte blocks and set it as the ulimit. This should be more than enough - # room. - if [ ! `uname` = "Darwin" ]; then # TODO: Darwin doesn't support "du -B..." - ulimit -S $(du -c -B512 ${ANDROID_HOST_OUT}/framework 2>/dev/null | tail -1 | cut -f1) || exit 1 - fi - fi else FLAGS="$FLAGS -Xnorelocate" - COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xnorelocate" -fi - -if [ "$HOST" = "y" ]; then - # Increase ulimit to 128MB in case we are running hprof test, - # or string append test with art-debug-gc. - ulimit -S 128000 || exit 1 fi if [ "$HOST" = "n" ]; then diff --git a/test/run-test b/test/run-test index 1ac285769d..1715423a5c 100755 --- a/test/run-test +++ b/test/run-test @@ -766,27 +766,14 @@ fi run_args="${run_args} --testlib ${testlib}" -# To cause tests to fail fast, limit the file sizes created by dx, dex2oat and ART output to 2MB. -build_file_size_limit=2048 -run_file_size_limit=2048 - -# Add tests requiring a higher ulimit to this list. Ulimits might need to be raised to deal with -# large amounts of expected output or large generated files. -if echo "$test_dir" | grep -Eq "(083|089|961|964|971)" > /dev/null; then - build_file_size_limit=5120 - run_file_size_limit=5120 -fi -if [ "$run_checker" = "yes" -a "$target_mode" = "yes" ]; then - # We will need to `adb pull` the .cfg output from the target onto the host to - # run checker on it. This file can be big. - build_file_size_limit=32768 - run_file_size_limit=32768 -fi -if [ ${USE_JACK} = "false" ]; then - # Set ulimit if we build with dx only, Jack can generate big temp files. - if ! ulimit -S "$build_file_size_limit"; then - err_echo "ulimit file size setting failed" - fi +# To cause tests to fail fast, limit the file sizes created by dx, dex2oat and +# ART output to approximately 128MB. This should be more than sufficient +# for any test while still catching cases of runaway output. +# Set a hard limit to encourage ART developers to increase the ulimit here if +# needed to support a test case rather than resetting the limit in the run +# script for the particular test in question. +if ! ulimit -f -H 128000; then + err_echo "ulimit file size setting failed" fi good="no" @@ -797,9 +784,6 @@ if [ "$dev_mode" = "yes" ]; then build_exit="$?" echo "build exit status: $build_exit" 1>&2 if [ "$build_exit" = '0' ]; then - if ! ulimit -S "$run_file_size_limit"; then - err_echo "ulimit file size setting failed" - fi echo "${test_dir}: running..." 1>&2 "./${run}" $run_args "$@" 2>&1 run_exit="$?" @@ -825,9 +809,6 @@ elif [ "$update_mode" = "yes" ]; then "./${build}" $build_args >"$build_output" 2>&1 build_exit="$?" if [ "$build_exit" = '0' ]; then - if ! ulimit -S "$run_file_size_limit"; then - err_echo "ulimit file size setting failed" - fi echo "${test_dir}: running..." 1>&2 "./${run}" $run_args "$@" >"$output" 2>&1 if [ "$run_checker" = "yes" ]; then @@ -862,9 +843,6 @@ else "./${build}" $build_args >"$build_output" 2>&1 build_exit="$?" if [ "$build_exit" = '0' ]; then - if ! ulimit -S "$run_file_size_limit"; then - err_echo "ulimit file size setting failed" - fi echo "${test_dir}: running..." 1>&2 "./${run}" $run_args "$@" >"$output" 2>&1 run_exit="$?" @@ -934,9 +912,6 @@ if [ "$bisection_search" = "yes" -a "$good" != "yes" ]; then echo "${test_dir}: not bisecting, checker test." 1>&2 else # Increase file size limit, bisection search can generate large logfiles. - if ! ulimit -S unlimited; then - err_echo "ulimit file size setting failed" - fi echo "${test_dir}: bisecting..." 1>&2 cwd=`pwd` maybe_device_mode="" diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index b814e14916..be84f898b2 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -184,10 +184,18 @@ def setup_test_env(): if env.ART_TEST_OPTIMIZING_GRAPH_COLOR: COMPILER_TYPES.add('regalloc_gc') OPTIMIZING_COMPILER_TYPES.add('regalloc_gc') - if env.ART_TEST_OPTIMIZING or not COMPILER_TYPES: # Default + if env.ART_TEST_OPTIMIZING: COMPILER_TYPES.add('optimizing') OPTIMIZING_COMPILER_TYPES.add('optimizing') + # By default we run all 'compiler' variants. + if not COMPILER_TYPES: + COMPILER_TYPES.add('optimizing') + COMPILER_TYPES.add('jit') + COMPILER_TYPES.add('interpreter') + COMPILER_TYPES.add('interp-ac') + OPTIMIZING_COMPILER_TYPES.add('optimizing') + if env.ART_TEST_RUN_TEST_RELOCATE: RELOCATE_TYPES.add('relocate') if env.ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT: diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc index 4ddd0aafb0..6316a9c368 100644 --- a/test/ti-agent/common_helper.cc +++ b/test/ti-agent/common_helper.cc @@ -520,11 +520,14 @@ void BindFunctions(jvmtiEnv* jenv, JNIEnv* env, const char* class_name) { LOG(FATAL) << "Could not load " << class_name; } } + BindFunctionsOnClass(jenv, env, klass.get()); +} +void BindFunctionsOnClass(jvmtiEnv* jenv, JNIEnv* env, jclass klass) { // Use JVMTI to get the methods. jint method_count; jmethodID* methods; - jvmtiError methods_result = jenv->GetClassMethods(klass.get(), &method_count, &methods); + jvmtiError methods_result = jenv->GetClassMethods(klass, &method_count, &methods); if (methods_result != JVMTI_ERROR_NONE) { LOG(FATAL) << "Could not get methods"; } @@ -538,7 +541,7 @@ void BindFunctions(jvmtiEnv* jenv, JNIEnv* env, const char* class_name) { } constexpr jint kNative = static_cast<jint>(kAccNative); if ((modifiers & kNative) != 0) { - BindMethod(jenv, env, klass.get(), methods[i]); + BindMethod(jenv, env, klass, methods[i]); } } diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h index 0a316edc7b..f10356dcbb 100644 --- a/test/ti-agent/common_helper.h +++ b/test/ti-agent/common_helper.h @@ -81,6 +81,7 @@ bool JvmtiErrorToException(JNIEnv* env, jvmtiError error); // // This will abort on failure. void BindFunctions(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name); +void BindFunctionsOnClass(jvmtiEnv* jvmti_env, JNIEnv* env, jclass klass); } // namespace art |