diff options
61 files changed, 1862 insertions, 257 deletions
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 15e4cd8e9c..c72edb18a3 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -2348,6 +2348,16 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy, const ImageInfo& image_info) { + if (orig->IsAbstract()) { + // Ignore the single-implementation info for abstract method. + // Do this on orig instead of copy, otherwise there is a crash due to methods + // are copied before classes. + // TODO: handle fixup of single-implementation method for abstract method. + orig->SetHasSingleImplementation(false); + orig->SetSingleImplementation( + nullptr, Runtime::Current()->GetClassLinker()->GetImagePointerSize()); + } + memcpy(copy, orig, ArtMethod::Size(target_ptr_size_)); copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked())); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 1e89ba590c..cf824a14e3 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1533,7 +1533,9 @@ void CodeGeneratorARM64::MoveLocation(Location destination, HConstant* src_cst = source.GetConstant(); CPURegister temp; if (src_cst->IsZeroBitPattern()) { - temp = (src_cst->IsLongConstant() || src_cst->IsDoubleConstant()) ? xzr : wzr; + temp = (src_cst->IsLongConstant() || src_cst->IsDoubleConstant()) + ? Register(xzr) + : Register(wzr); } else { if (src_cst->IsIntConstant()) { temp = temps.AcquireW(); @@ -1903,6 +1905,9 @@ void LocationsBuilderARM64::HandleFieldGet(HInstruction* instruction) { LocationSummary::kNoCall); if (object_field_get_with_read_barrier && kUseBakerReadBarrier) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. + // We need a temporary register for the read barrier marking slow + // path in CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier. + locations->AddTemp(Location::RequiresRegister()); } locations->SetInAt(0, Location::RequiresRegister()); if (Primitive::IsFloatingPointType(instruction->GetType())) { @@ -1930,11 +1935,9 @@ void InstructionCodeGeneratorARM64::HandleFieldGet(HInstruction* instruction, if (field_type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { // Object FieldGet with Baker's read barrier case. - MacroAssembler* masm = GetVIXLAssembler(); - UseScratchRegisterScope temps(masm); // /* HeapReference<Object> */ out = *(base + offset) Register base = RegisterFrom(base_loc, Primitive::kPrimNot); - Register temp = temps.AcquireW(); + Register temp = WRegisterFrom(locations->GetTemp(0)); // Note that potential implicit null checks are handled in this // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier call. codegen_->GenerateFieldLoadWithBakerReadBarrier( diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 893e465392..f4d3ec54ee 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -7679,15 +7679,21 @@ void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_in vixl32::Register jump_offset = temps.Acquire(); // Load jump offset from the table. - __ Adr(table_base, jump_table->GetTableStartLabel()); - __ Ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2)); + { + const size_t jump_size = switch_instr->GetNumEntries() * sizeof(int32_t); + ExactAssemblyScope aas(GetVIXLAssembler(), + (vixl32::kMaxInstructionSizeInBytes * 4) + jump_size, + CodeBufferCheckScope::kMaximumSize); + __ adr(table_base, jump_table->GetTableStartLabel()); + __ ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2)); - // Jump to target block by branching to table_base(pc related) + offset. - vixl32::Register target_address = table_base; - __ Add(target_address, table_base, jump_offset); - __ Bx(target_address); + // Jump to target block by branching to table_base(pc related) + offset. + vixl32::Register target_address = table_base; + __ add(target_address, table_base, jump_offset); + __ bx(target_address); - jump_table->EmitTable(codegen_); + jump_table->EmitTable(codegen_); + } } } void LocationsBuilderARMVIXL::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) { diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h index 776a483d43..93ea090583 100644 --- a/compiler/optimizing/common_arm64.h +++ b/compiler/optimizing/common_arm64.h @@ -130,8 +130,8 @@ inline vixl::aarch64::CPURegister InputCPURegisterOrZeroRegAt(HInstruction* inst Primitive::Type input_type = input->GetType(); if (input->IsConstant() && input->AsConstant()->IsZeroBitPattern()) { return (Primitive::ComponentSize(input_type) >= vixl::aarch64::kXRegSizeInBytes) - ? vixl::aarch64::xzr - : vixl::aarch64::wzr; + ? vixl::aarch64::Register(vixl::aarch64::xzr) + : vixl::aarch64::Register(vixl::aarch64::wzr); } return InputCPURegisterAt(instr, index); } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 5d40f75618..7772e8f973 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -304,7 +304,8 @@ ArtMethod* HInliner::TryCHADevirtualization(ArtMethod* resolved_method) { // We do not support HDeoptimize in OSR methods. return nullptr; } - return resolved_method->GetSingleImplementation(); + PointerSize pointer_size = caller_compilation_unit_.GetClassLinker()->GetImagePointerSize(); + return resolved_method->GetSingleImplementation(pointer_size); } bool HInliner::TryInline(HInvoke* invoke_instruction) { diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 68c2d2e36e..91d9c56d10 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -2742,14 +2742,36 @@ void IntrinsicCodeGeneratorARMVIXL::VisitReferenceGetReferent(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderARMVIXL::VisitMathCeil(HInvoke* invoke) { + if (features_.HasARMv8AInstructions()) { + CreateFPToFPLocations(arena_, invoke); + } +} + +void IntrinsicCodeGeneratorARMVIXL::VisitMathCeil(HInvoke* invoke) { + ArmVIXLAssembler* assembler = GetAssembler(); + DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions()); + __ Vrintp(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0)); +} + +void IntrinsicLocationsBuilderARMVIXL::VisitMathFloor(HInvoke* invoke) { + if (features_.HasARMv8AInstructions()) { + CreateFPToFPLocations(arena_, invoke); + } +} + +void IntrinsicCodeGeneratorARMVIXL::VisitMathFloor(HInvoke* invoke) { + ArmVIXLAssembler* assembler = GetAssembler(); + DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions()); + __ Vrintm(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0)); +} + UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinDoubleDouble) UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinFloatFloat) UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxDoubleDouble) UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxFloatFloat) UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinLongLong) UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxLongLong) -UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathCeil) // Could be done by changing rounding mode, maybe? -UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathFloor) // Could be done by changing rounding mode, maybe? UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRint) UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble) // Could be done by changing rounding mode, maybe? UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundFloat) // Could be done by changing rounding mode, maybe? diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index a9a1e6f592..1b9bd7eb31 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -165,7 +165,7 @@ size_t StackMapStream::PrepareForFillIn() { inline_info_size_, register_mask_max_, stack_mask_number_of_bits); - stack_maps_size_ = stack_maps_.size() * stack_map_size; + stack_maps_size_ = RoundUp(stack_maps_.size() * stack_map_size, kBitsPerByte) / kBitsPerByte; dex_register_location_catalog_size_ = ComputeDexRegisterLocationCatalogSize(); size_t non_header_size = @@ -178,7 +178,7 @@ size_t StackMapStream::PrepareForFillIn() { CodeInfoEncoding code_info_encoding; code_info_encoding.non_header_size = non_header_size; code_info_encoding.number_of_stack_maps = stack_maps_.size(); - code_info_encoding.stack_map_size_in_bytes = stack_map_size; + code_info_encoding.stack_map_size_in_bits = stack_map_size; code_info_encoding.stack_map_encoding = stack_map_encoding_; code_info_encoding.inline_info_encoding = inline_info_encoding_; code_info_encoding.number_of_location_catalog_entries = location_catalog_entries_.size(); @@ -322,7 +322,7 @@ void StackMapStream::FillIn(MemoryRegion region) { stack_map.SetDexPc(stack_map_encoding_, entry.dex_pc); stack_map.SetNativePcCodeOffset(stack_map_encoding_, entry.native_pc_code_offset); stack_map.SetRegisterMask(stack_map_encoding_, entry.register_mask); - size_t number_of_stack_mask_bits = stack_map.GetNumberOfStackMaskBits(stack_map_encoding_); + size_t number_of_stack_mask_bits = code_info.GetNumberOfStackMaskBits(encoding); if (entry.sp_mask != nullptr) { for (size_t bit = 0; bit < number_of_stack_mask_bits; bit++) { stack_map.SetStackMaskBit(stack_map_encoding_, bit, entry.sp_mask->IsBitSet(bit)); @@ -551,7 +551,7 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { entry.native_pc_code_offset.Uint32Value(instruction_set_)); DCHECK_EQ(stack_map.GetDexPc(stack_map_encoding), entry.dex_pc); DCHECK_EQ(stack_map.GetRegisterMask(stack_map_encoding), entry.register_mask); - size_t num_stack_mask_bits = stack_map.GetNumberOfStackMaskBits(stack_map_encoding); + size_t num_stack_mask_bits = code_info.GetNumberOfStackMaskBits(encoding); if (entry.sp_mask != nullptr) { DCHECK_GE(num_stack_mask_bits, entry.sp_mask->GetNumberOfBits()); for (size_t b = 0; b < num_stack_mask_bits; b++) { diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index f68695bcbc..da4597e385 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -27,10 +27,10 @@ namespace art { // Check that the stack mask of given stack map is identical // to the given bit vector. Returns true if they are same. static bool CheckStackMask( + int number_of_bits, const StackMap& stack_map, StackMapEncoding& encoding, const BitVector& bit_vector) { - int number_of_bits = stack_map.GetNumberOfStackMaskBits(encoding); if (bit_vector.GetHighestBitSet() >= number_of_bits) { return false; } @@ -81,7 +81,10 @@ TEST(StackMapTest, Test1) { ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA)); ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding.stack_map_encoding)); - ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask)); + ASSERT_TRUE(CheckStackMask(code_info.GetNumberOfStackMaskBits(encoding), + stack_map, + encoding.stack_map_encoding, + sp_mask)); ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding)); DexRegisterMap dex_register_map = @@ -196,7 +199,10 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA)); ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding.stack_map_encoding)); - ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask1)); + ASSERT_TRUE(CheckStackMask(code_info.GetNumberOfStackMaskBits(encoding), + stack_map, + encoding.stack_map_encoding, + sp_mask1)); ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding)); DexRegisterMap dex_register_map = @@ -255,7 +261,10 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(128u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA)); ASSERT_EQ(0xFFu, stack_map.GetRegisterMask(encoding.stack_map_encoding)); - ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask2)); + ASSERT_TRUE(CheckStackMask(code_info.GetNumberOfStackMaskBits(encoding), + stack_map, + encoding.stack_map_encoding, + sp_mask2)); ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding)); DexRegisterMap dex_register_map = @@ -309,7 +318,10 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(192u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA)); ASSERT_EQ(0xABu, stack_map.GetRegisterMask(encoding.stack_map_encoding)); - ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask3)); + ASSERT_TRUE(CheckStackMask(code_info.GetNumberOfStackMaskBits(encoding), + stack_map, + encoding.stack_map_encoding, + sp_mask3)); ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding)); DexRegisterMap dex_register_map = @@ -363,7 +375,10 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(256u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA)); ASSERT_EQ(0xCDu, stack_map.GetRegisterMask(encoding.stack_map_encoding)); - ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask4)); + ASSERT_TRUE(CheckStackMask(code_info.GetNumberOfStackMaskBits(encoding), + stack_map, + encoding.stack_map_encoding, + sp_mask4)); ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding)); DexRegisterMap dex_register_map = diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 69901c13cd..b6da6c13f3 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1575,7 +1575,7 @@ class OatDumper { stats_.AddBits( Stats::kByteKindStackMapRegisterMask, stack_map_encoding.GetRegisterMaskEncoding().BitSize() * num_stack_maps); - const size_t stack_mask_bits = encoding.stack_map_size_in_bytes * kBitsPerByte - + const size_t stack_mask_bits = encoding.stack_map_size_in_bits - stack_map_encoding.GetStackMaskBitOffset(); stats_.AddBits( Stats::kByteKindStackMapMask, @@ -1584,7 +1584,7 @@ class OatDumper { stack_map_encoding.GetStackMaskBitOffset() + stack_mask_bits; stats_.AddBits( Stats::kByteKindStackMapOther, - (encoding.stack_map_size_in_bytes * kBitsPerByte - stack_map_bits) * num_stack_maps); + (encoding.stack_map_size_in_bits - stack_map_bits) * num_stack_maps); const size_t stack_map_bytes = helper.GetCodeInfo().GetStackMapsSize(encoding); const size_t location_catalog_bytes = helper.GetCodeInfo().GetDexRegisterLocationCatalogSize(encoding); diff --git a/runtime/Android.bp b/runtime/Android.bp index 7f985139bb..196c65e11a 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -154,6 +154,7 @@ cc_defaults { "native/java_lang_Thread.cc", "native/java_lang_Throwable.cc", "native/java_lang_VMClassLoader.cc", + "native/java_lang_invoke_MethodHandleImpl.cc", "native/java_lang_ref_FinalizerReference.cc", "native/java_lang_ref_Reference.cc", "native/java_lang_reflect_Array.cc", diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index a35c7ab701..7ec3900aa9 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -244,7 +244,9 @@ inline bool ArtMethod::IsImtUnimplementedMethod() { } inline const DexFile* ArtMethod::GetDexFile() { - return GetDexCache()->GetDexFile(); + // It is safe to avoid the read barrier here since the dex file is constant, so if we read the + // from-space dex file pointer it will be equal to the to-space copy. + return GetDexCache<kWithoutReadBarrier>()->GetDexFile(); } inline const char* ArtMethod::GetDeclaringClassDescriptor() { @@ -361,9 +363,11 @@ inline mirror::ClassLoader* ArtMethod::GetClassLoader() { return GetDeclaringClass()->GetClassLoader(); } +template <ReadBarrierOption kReadBarrierOption> inline mirror::DexCache* ArtMethod::GetDexCache() { if (LIKELY(!IsObsolete())) { - return GetDeclaringClass()->GetDexCache(); + mirror::Class* klass = GetDeclaringClass<kReadBarrierOption>(); + return klass->GetDexCache<kDefaultVerifyFlags, kReadBarrierOption>(); } else { DCHECK(!IsProxyMethod()); return GetObsoleteDexCache(); @@ -379,14 +383,13 @@ inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy(PointerSize pointer_size) if (LIKELY(!IsProxyMethod())) { return this; } - mirror::Class* klass = GetDeclaringClass(); ArtMethod* interface_method = mirror::DexCache::GetElementPtrSize( GetDexCacheResolvedMethods(pointer_size), GetDexMethodIndex(), pointer_size); DCHECK(interface_method != nullptr); DCHECK_EQ(interface_method, - Runtime::Current()->GetClassLinker()->FindMethodForProxy(klass, this)); + Runtime::Current()->GetClassLinker()->FindMethodForProxy(GetDeclaringClass(), this)); return interface_method; } diff --git a/runtime/art_method.cc b/runtime/art_method.cc index a3d9ba61f3..ec789f51ef 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -55,15 +55,13 @@ extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, const char*); -ArtMethod* ArtMethod::GetSingleImplementation() { +ArtMethod* ArtMethod::GetSingleImplementation(PointerSize pointer_size) { DCHECK(!IsNative()); if (!IsAbstract()) { // A non-abstract's single implementation is itself. return this; } - // TODO: add single-implementation logic for abstract method by storing it - // in ptr_sized_fields_. - return nullptr; + return reinterpret_cast<ArtMethod*>(GetDataPtrSize(pointer_size)); } ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa, diff --git a/runtime/art_method.h b/runtime/art_method.h index 17f343d442..f145d7c416 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -456,7 +456,7 @@ class ArtMethod FINAL { } } - ArtMethod* GetSingleImplementation() + ArtMethod* GetSingleImplementation(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); ALWAYS_INLINE void SetSingleImplementation(ArtMethod* method, PointerSize pointer_size) { @@ -563,6 +563,7 @@ class ArtMethod FINAL { mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_); + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> mirror::DexCache* GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_); mirror::DexCache* GetObsoleteDexCache() REQUIRES_SHARED(Locks::mutator_lock_); @@ -684,7 +685,8 @@ class ArtMethod FINAL { ArtMethod** dex_cache_resolved_methods_; // Pointer to JNI function registered to this method, or a function to resolve the JNI function, - // or the profiling data for non-native methods, or an ImtConflictTable. + // or the profiling data for non-native methods, or an ImtConflictTable, or the + // single-implementation of an abstract method. void* data_; // Method dispatch from quick compiled code invokes this pointer which may cause bridging into diff --git a/runtime/bit_memory_region.h b/runtime/bit_memory_region.h new file mode 100644 index 0000000000..90a198193e --- /dev/null +++ b/runtime/bit_memory_region.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#ifndef ART_RUNTIME_BIT_MEMORY_REGION_H_ +#define ART_RUNTIME_BIT_MEMORY_REGION_H_ + +#include "memory_region.h" + +namespace art { + +// Bit memory region is a bit offset subregion of a normal memoryregion. This is useful for +// abstracting away the bit start offset to avoid needing passing as an argument everywhere. +class BitMemoryRegion FINAL : public ValueObject { + public: + BitMemoryRegion() = default; + BitMemoryRegion(MemoryRegion region, size_t bit_offset, size_t bit_size) { + bit_start_ = bit_offset % kBitsPerByte; + const size_t start = bit_offset / kBitsPerByte; + const size_t end = (bit_offset + bit_size + kBitsPerByte - 1) / kBitsPerByte; + region_ = region.Subregion(start, end - start); + } + + void* pointer() const { return region_.pointer(); } + size_t size() const { return region_.size(); } + size_t BitOffset() const { return bit_start_; } + size_t size_in_bits() const { + return region_.size_in_bits(); + } + + // Load a single bit in the region. The bit at offset 0 is the least + // significant bit in the first byte. + ALWAYS_INLINE bool LoadBit(uintptr_t bit_offset) const { + return region_.LoadBit(bit_offset + bit_start_); + } + + ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) const { + region_.StoreBit(bit_offset + bit_start_, value); + } + + ALWAYS_INLINE uint32_t LoadBits(uintptr_t bit_offset, size_t length) const { + return region_.LoadBits(bit_offset + bit_start_, length); + } + + // Store at a bit offset from inside the bit memory region. + ALWAYS_INLINE void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) { + region_.StoreBits(bit_offset + bit_start_, value, length); + } + + private: + MemoryRegion region_; + size_t bit_start_ = 0; +}; + +} // namespace art + +#endif // ART_RUNTIME_BIT_MEMORY_REGION_H_ diff --git a/runtime/cha.cc b/runtime/cha.cc index d94b091ebd..e726bdbcb8 100644 --- a/runtime/cha.cc +++ b/runtime/cha.cc @@ -185,7 +185,8 @@ class CHACheckpoint FINAL : public Closure { }; void ClassHierarchyAnalysis::VerifyNonSingleImplementation(mirror::Class* verify_class, - uint16_t verify_index) { + uint16_t verify_index, + ArtMethod* excluded_method) { // Grab cha_lock_ to make sure all single-implementation updates are seen. PointerSize image_pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); @@ -195,9 +196,14 @@ void ClassHierarchyAnalysis::VerifyNonSingleImplementation(mirror::Class* verify return; } ArtMethod* verify_method = verify_class->GetVTableEntry(verify_index, image_pointer_size); - DCHECK(!verify_method->HasSingleImplementation()) - << "class: " << verify_class->PrettyClass() - << " verify_method: " << verify_method->PrettyMethod(true); + if (verify_method != excluded_method) { + DCHECK(!verify_method->HasSingleImplementation()) + << "class: " << verify_class->PrettyClass() + << " verify_method: " << verify_method->PrettyMethod(true); + if (verify_method->IsAbstract()) { + DCHECK(verify_method->GetSingleImplementation(image_pointer_size) == nullptr); + } + } verify_class = verify_class->GetSuperClass(); } } @@ -206,41 +212,160 @@ void ClassHierarchyAnalysis::CheckSingleImplementationInfo( Handle<mirror::Class> klass, ArtMethod* virtual_method, ArtMethod* method_in_super, - std::unordered_set<ArtMethod*>& invalidated_single_impl_methods) { + std::unordered_set<ArtMethod*>& invalidated_single_impl_methods, + PointerSize pointer_size) { // TODO: if klass is not instantiable, virtual_method isn't invocable yet so // even if it overrides, it doesn't invalidate single-implementation // assumption. - DCHECK_NE(virtual_method, method_in_super); + DCHECK((virtual_method != method_in_super) || virtual_method->IsAbstract()); DCHECK(method_in_super->GetDeclaringClass()->IsResolved()) << "class isn't resolved"; // If virtual_method doesn't come from a default interface method, it should // be supplied by klass. - DCHECK(virtual_method->IsCopied() || + DCHECK(virtual_method == method_in_super || + virtual_method->IsCopied() || virtual_method->GetDeclaringClass() == klass.Get()); - // A new virtual_method should set method_in_super to - // non-single-implementation (if not set already). - // We don't grab cha_lock_. Single-implementation flag won't be set to true - // again once it's set to false. + // To make updating single-implementation flags simple, we always maintain the following + // invariant: + // Say all virtual methods in the same vtable slot, starting from the bottom child class + // to super classes, is a sequence of unique methods m3, m2, m1, ... (after removing duplicate + // methods for inherited methods). + // For example for the following class hierarchy, + // class A { void m() { ... } } + // class B extends A { void m() { ... } } + // class C extends B {} + // class D extends C { void m() { ... } } + // the sequence is D.m(), B.m(), A.m(). + // The single-implementation status for that sequence of methods begin with one or two true's, + // then become all falses. The only case where two true's are possible is for one abstract + // method m and one non-abstract method mImpl that overrides method m. + // With the invariant, when linking in a new class, we only need to at most update one or + // two methods in the sequence for their single-implementation status, in order to maintain + // the invariant. + if (!method_in_super->HasSingleImplementation()) { // method_in_super already has multiple implementations. All methods in the // same vtable slots in its super classes should have // non-single-implementation already. if (kIsDebugBuild) { VerifyNonSingleImplementation(klass->GetSuperClass()->GetSuperClass(), - method_in_super->GetMethodIndex()); + method_in_super->GetMethodIndex(), + nullptr /* excluded_method */); } return; } // Native methods don't have single-implementation flag set. DCHECK(!method_in_super->IsNative()); - // Invalidate method_in_super's single-implementation status. - invalidated_single_impl_methods.insert(method_in_super); + + uint16_t method_index = method_in_super->GetMethodIndex(); + if (method_in_super->IsAbstract()) { + if (kIsDebugBuild) { + // An abstract method should have made all methods in the same vtable + // slot above it in the class hierarchy having non-single-implementation. + mirror::Class* super_super = klass->GetSuperClass()->GetSuperClass(); + VerifyNonSingleImplementation(super_super, + method_index, + method_in_super); + } + + if (virtual_method->IsAbstract()) { + // SUPER: abstract, VIRTUAL: abstract. + if (method_in_super == virtual_method) { + DCHECK(klass->IsInstantiable()); + // An instantiable subclass hasn't provided a concrete implementation of + // the abstract method. Invoking method_in_super may throw AbstractMethodError. + // This is an uncommon case, so we simply treat method_in_super as not + // having single-implementation. + invalidated_single_impl_methods.insert(method_in_super); + return; + } else { + // One abstract method overrides another abstract method. This is an uncommon + // case. We simply treat method_in_super as not having single-implementation. + invalidated_single_impl_methods.insert(method_in_super); + return; + } + } else { + // SUPER: abstract, VIRTUAL: non-abstract. + // A non-abstract method overrides an abstract method. + if (method_in_super->GetSingleImplementation(pointer_size) == nullptr) { + // Abstract method_in_super has no implementation yet. + // We need to grab cha_lock_ for further checking/updating due to possible + // races. + MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_); + if (!method_in_super->HasSingleImplementation()) { + return; + } + if (method_in_super->GetSingleImplementation(pointer_size) == nullptr) { + // virtual_method becomes the first implementation for method_in_super. + method_in_super->SetSingleImplementation(virtual_method, pointer_size); + // Keep method_in_super's single-implementation status. + return; + } + // Fall through to invalidate method_in_super's single-implementation status. + } + // Abstract method_in_super already got one implementation. + // Invalidate method_in_super's single-implementation status. + invalidated_single_impl_methods.insert(method_in_super); + return; + } + } else { + if (virtual_method->IsAbstract()) { + // SUPER: non-abstract, VIRTUAL: abstract. + // An abstract method overrides a non-abstract method. This is an uncommon + // case, we simply treat both methods as not having single-implementation. + invalidated_single_impl_methods.insert(virtual_method); + // Fall-through to handle invalidating method_in_super of its + // single-implementation status. + } + + // SUPER: non-abstract, VIRTUAL: non-abstract/abstract(fall-through from previous if). + // Invalidate method_in_super's single-implementation status. + invalidated_single_impl_methods.insert(method_in_super); + + // method_in_super might be the single-implementation of another abstract method, + // which should be also invalidated of its single-implementation status. + mirror::Class* super_super = klass->GetSuperClass()->GetSuperClass(); + while (super_super != nullptr && + method_index < super_super->GetVTableLength()) { + ArtMethod* method_in_super_super = super_super->GetVTableEntry(method_index, pointer_size); + if (method_in_super_super != method_in_super) { + if (method_in_super_super->IsAbstract()) { + if (method_in_super_super->HasSingleImplementation()) { + // Invalidate method_in_super's single-implementation status. + invalidated_single_impl_methods.insert(method_in_super_super); + // No need to further traverse up the class hierarchy since if there + // are cases that one abstract method overrides another method, we + // should have made that method having non-single-implementation already. + } else { + // method_in_super_super is already non-single-implementation. + // No need to further traverse up the class hierarchy. + } + } else { + DCHECK(!method_in_super_super->HasSingleImplementation()); + // No need to further traverse up the class hierarchy since two non-abstract + // methods (method_in_super and method_in_super_super) should have set all + // other methods (abstract or not) in the vtable slot to be non-single-implementation. + } + + if (kIsDebugBuild) { + VerifyNonSingleImplementation(super_super->GetSuperClass(), + method_index, + method_in_super_super); + } + // No need to go any further. + return; + } else { + super_super = super_super->GetSuperClass(); + } + } + } } void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle<mirror::Class> klass, - ArtMethod* method) { + ArtMethod* method, + PointerSize pointer_size) { DCHECK(method->IsCopied() || method->GetDeclaringClass() == klass.Get()); if (klass->IsFinal() || method->IsFinal()) { // Final classes or methods do not need CHA for devirtualization. @@ -253,16 +378,21 @@ void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle<mirror::Class> // cannot be inlined. It's not worthwhile to devirtualize the // call which can add a deoptimization point. DCHECK(!method->HasSingleImplementation()); - } else { - method->SetHasSingleImplementation(true); - if (method->IsAbstract()) { - // There is no real implementation yet. - // TODO: implement single-implementation logic for abstract methods. - DCHECK(method->GetSingleImplementation() == nullptr); + } else if (method->IsAbstract()) { + if (method->GetDeclaringClass()->IsInstantiable()) { + // Rare case, but we do accept it (such as 800-smali/smali/b_26143249.smali). + // Do not attempt to devirtualize it. + method->SetHasSingleImplementation(false); } else { - // Single implementation of non-abstract method is itself. - DCHECK_EQ(method->GetSingleImplementation(), method); + // Abstract method starts with single-implementation flag set and null + // implementation method. + method->SetHasSingleImplementation(true); + DCHECK(method->GetSingleImplementation(pointer_size) == nullptr); } + } else { + method->SetHasSingleImplementation(true); + // Single implementation of non-abstract method is itself. + DCHECK_EQ(method->GetSingleImplementation(pointer_size), method); } } @@ -286,19 +416,29 @@ void ClassHierarchyAnalysis::UpdateAfterLoadingOf(Handle<mirror::Class> klass) { ArtMethod* method_in_super = super_class->GetVTableEntry(i, image_pointer_size); if (method == method_in_super) { // vtable slot entry is inherited from super class. + if (method->IsAbstract() && klass->IsInstantiable()) { + // An instantiable class that inherits an abstract method is treated as + // supplying an implementation that throws AbstractMethodError. + CheckSingleImplementationInfo(klass, + method, + method_in_super, + invalidated_single_impl_methods, + image_pointer_size); + } continue; } - InitSingleImplementationFlag(klass, method); + InitSingleImplementationFlag(klass, method, image_pointer_size); CheckSingleImplementationInfo(klass, method, method_in_super, - invalidated_single_impl_methods); + invalidated_single_impl_methods, + image_pointer_size); } // For new virtual methods that don't override. for (int32_t i = super_class->GetVTableLength(); i < klass->GetVTableLength(); ++i) { ArtMethod* method = klass->GetVTableEntry(i, image_pointer_size); - InitSingleImplementationFlag(klass, method); + InitSingleImplementationFlag(klass, method, image_pointer_size); } Runtime* const runtime = Runtime::Current(); @@ -321,6 +461,10 @@ void ClassHierarchyAnalysis::UpdateAfterLoadingOf(Handle<mirror::Class> klass) { continue; } invalidated->SetHasSingleImplementation(false); + if (invalidated->IsAbstract()) { + // Clear the single implementation method. + invalidated->SetSingleImplementation(nullptr, image_pointer_size); + } if (runtime->IsAotCompiler()) { // No need to invalidate any compiled code as the AotCompiler doesn't diff --git a/runtime/cha.h b/runtime/cha.h index ada5c89981..a56a752d8c 100644 --- a/runtime/cha.h +++ b/runtime/cha.h @@ -112,7 +112,9 @@ class ClassHierarchyAnalysis { void UpdateAfterLoadingOf(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); private: - void InitSingleImplementationFlag(Handle<mirror::Class> klass, ArtMethod* method) + void InitSingleImplementationFlag(Handle<mirror::Class> klass, + ArtMethod* method, + PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); // `virtual_method` in `klass` overrides `method_in_super`. @@ -123,12 +125,16 @@ class ClassHierarchyAnalysis { Handle<mirror::Class> klass, ArtMethod* virtual_method, ArtMethod* method_in_super, - std::unordered_set<ArtMethod*>& invalidated_single_impl_methods) + std::unordered_set<ArtMethod*>& invalidated_single_impl_methods, + PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - // Verify all methods in the same vtable slot from verify_class and its supers - // don't have single-implementation. - void VerifyNonSingleImplementation(mirror::Class* verify_class, uint16_t verify_index) + // For all methods in vtable slot at `verify_index` of `verify_class` and its + // superclasses, single-implementation status should be false, except if the + // method is `excluded_method`. + void VerifyNonSingleImplementation(mirror::Class* verify_class, + uint16_t verify_index, + ArtMethod* excluded_method) REQUIRES_SHARED(Locks::mutator_lock_); // A map that maps a method to a set of compiled code that assumes that method has a diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index e806e7d608..17510bb598 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -743,15 +743,22 @@ struct MethodTypeOffsets : public CheckOffsets<mirror::MethodType> { } }; -struct MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> { - MethodHandleImplOffsets() : CheckOffsets<mirror::MethodHandleImpl>( +struct MethodHandleOffsets : public CheckOffsets<mirror::MethodHandle> { + MethodHandleOffsets() : CheckOffsets<mirror::MethodHandle>( false, "Ljava/lang/invoke/MethodHandle;") { - addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, art_field_or_method_), "artFieldOrMethod"); - addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, cached_spread_invoker_), + addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, art_field_or_method_), "artFieldOrMethod"); + addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, cached_spread_invoker_), "cachedSpreadInvoker"); - addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, handle_kind_), "handleKind"); - addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, nominal_type_), "nominalType"); - addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, method_type_), "type"); + addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, handle_kind_), "handleKind"); + addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, nominal_type_), "nominalType"); + addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, method_type_), "type"); + } +}; + +struct MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> { + MethodHandleImplOffsets() : CheckOffsets<mirror::MethodHandleImpl>( + false, "Ljava/lang/invoke/MethodHandleImpl;") { + addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, info_), "info"); } }; @@ -785,6 +792,7 @@ TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) { EXPECT_TRUE(FieldOffsets().Check()); EXPECT_TRUE(ExecutableOffsets().Check()); EXPECT_TRUE(MethodTypeOffsets().Check()); + EXPECT_TRUE(MethodHandleOffsets().Check()); EXPECT_TRUE(MethodHandleImplOffsets().Check()); EXPECT_TRUE(EmulatedStackFrameOffsets().Check()); } diff --git a/runtime/memory_region.cc b/runtime/memory_region.cc index 5bf0f40eff..b0ecab40c5 100644 --- a/runtime/memory_region.cc +++ b/runtime/memory_region.cc @@ -43,8 +43,8 @@ void MemoryRegion::StoreBits(uintptr_t bit_offset, uint32_t value, size_t length // Bits are stored in this order {7 6 5 4 3 2 1 0}. // How many remaining bits in current byte is (bit_offset % kBitsPerByte) + 1. uint8_t* out = ComputeInternalPointer<uint8_t>(bit_offset >> kBitsPerByteLog2); - auto orig_len = length; - auto orig_value = value; + size_t orig_len = length; + uint32_t orig_value = value; uintptr_t bit_remainder = bit_offset % kBitsPerByte; while (true) { const uintptr_t remaining_bits = kBitsPerByte - bit_remainder; diff --git a/runtime/memory_region_test.cc b/runtime/memory_region_test.cc index 72e03a485a..6634c60193 100644 --- a/runtime/memory_region_test.cc +++ b/runtime/memory_region_test.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "bit_memory_region.h" #include "memory_region.h" #include "gtest/gtest.h" @@ -55,4 +56,35 @@ TEST(MemoryRegion, StoreUnaligned) { } } +TEST(MemoryRegion, TestBits) { + const size_t n = 8; + uint8_t data[n] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + MemoryRegion region(&data, n); + uint32_t value = 0xDEADBEEF; + // Try various offsets and lengths. + for (size_t bit_offset = 0; bit_offset < 2 * kBitsPerByte; ++bit_offset) { + for (size_t length = 0; length < 2 * kBitsPerByte; ++length) { + const uint32_t length_mask = (1 << length) - 1; + uint32_t masked_value = value & length_mask; + BitMemoryRegion bmr(region, bit_offset, length); + region.StoreBits(bit_offset, masked_value, length); + EXPECT_EQ(region.LoadBits(bit_offset, length), masked_value); + EXPECT_EQ(bmr.LoadBits(0, length), masked_value); + // Check adjacent bits to make sure they were not incorrectly cleared. + EXPECT_EQ(region.LoadBits(0, bit_offset), (1u << bit_offset) - 1); + EXPECT_EQ(region.LoadBits(bit_offset + length, length), length_mask); + region.StoreBits(bit_offset, length_mask, length); + // Store with bit memory region. + bmr.StoreBits(0, masked_value, length); + EXPECT_EQ(bmr.LoadBits(0, length), masked_value); + // Check adjacent bits to make sure they were not incorrectly cleared. + EXPECT_EQ(region.LoadBits(0, bit_offset), (1u << bit_offset) - 1); + EXPECT_EQ(region.LoadBits(bit_offset + length, length), length_mask); + region.StoreBits(bit_offset, length_mask, length); + // Flip the value to try different edge bit combinations. + value = ~value; + } + } +} + } // namespace art diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 2fb8d28de9..6a65e1271f 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -71,9 +71,10 @@ inline ClassLoader* Class::GetClassLoader() { OFFSET_OF_OBJECT_MEMBER(Class, class_loader_)); } -template<VerifyObjectFlags kVerifyFlags> +template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline DexCache* Class::GetDexCache() { - return GetFieldObject<DexCache, kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_)); + return GetFieldObject<DexCache, kVerifyFlags, kReadBarrierOption>( + OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_)); } inline uint32_t Class::GetCopiedMethodsStartOffset() { diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 7f6aa12fdb..c9f27ad53f 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -722,7 +722,8 @@ class MANAGED Class FINAL : public Object { void DumpClass(std::ostream& os, int flags) REQUIRES_SHARED(Locks::mutator_lock_); - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> DexCache* GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_); // Also updates the dex_cache_strings_ variable from new_dex_cache. diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h index 2f26a22ca8..dca30626e0 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -25,6 +25,7 @@ namespace art { +struct MethodHandleOffsets; struct MethodHandleImplOffsets; namespace mirror { @@ -105,7 +106,7 @@ class MANAGED MethodHandle : public Object { return MemberOffset(OFFSETOF_MEMBER(MethodHandle, handle_kind_)); } - friend struct art::MethodHandleImplOffsets; // for verifying offset information + friend struct art::MethodHandleOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandle); }; @@ -121,6 +122,11 @@ class MANAGED MethodHandleImpl : public MethodHandle { static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); private: + static MemberOffset InfoOffset() { + return MemberOffset(OFFSETOF_MEMBER(MethodHandleImpl, info_)); + } + + HeapReference<mirror::Object> info_; // Unused by the runtime. static GcRoot<mirror::Class> static_class_; // java.lang.invoke.MethodHandleImpl.class friend struct art::MethodHandleImplOffsets; // for verifying offset information diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.cc b/runtime/native/java_lang_invoke_MethodHandleImpl.cc new file mode 100644 index 0000000000..72a37f875d --- /dev/null +++ b/runtime/native/java_lang_invoke_MethodHandleImpl.cc @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2008 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 "java_lang_invoke_MethodHandleImpl.h" + +#include "art_method.h" +#include "handle_scope-inl.h" +#include "jni_internal.h" +#include "mirror/field.h" +#include "mirror/method.h" +#include "mirror/method_handle_impl.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" + +namespace art { + +static jobject MethodHandleImpl_getMemberInternal(JNIEnv* env, jobject thiz) { + ScopedObjectAccess soa(env); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodHandleImpl> handle = hs.NewHandle( + soa.Decode<mirror::MethodHandleImpl>(thiz)); + + // Check the handle kind, we need to materialize a Field for field accessors, + // a Method for method invokers and a Constructor for constructors. + const mirror::MethodHandle::Kind handle_kind = handle->GetHandleKind(); + + // We check this here because we pass false to CreateFromArtField and + // CreateFromArtMethod. + DCHECK(!Runtime::Current()->IsActiveTransaction()); + + MutableHandle<mirror::Object> h_object(hs.NewHandle<mirror::Object>(nullptr)); + if (handle_kind >= mirror::MethodHandle::kFirstAccessorKind) { + ArtField* const field = handle->GetTargetField(); + h_object.Assign(mirror::Field::CreateFromArtField<kRuntimePointerSize, false>( + soa.Self(), field, false /* force_resolve */)); + } else { + ArtMethod* const method = handle->GetTargetMethod(); + if (method->IsConstructor()) { + h_object.Assign(mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize, false>( + soa.Self(), method)); + } else { + h_object.Assign(mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>( + soa.Self(), method)); + } + } + + if (UNLIKELY(h_object.Get() == nullptr)) { + soa.Self()->AssertPendingOOMException(); + return nullptr; + } + + return soa.AddLocalReference<jobject>(h_object.Get()); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(MethodHandleImpl, getMemberInternal, "()Ljava/lang/reflect/Member;"), +}; + +void register_java_lang_invoke_MethodHandleImpl(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/invoke/MethodHandleImpl"); +} + +} // namespace art diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.h b/runtime/native/java_lang_invoke_MethodHandleImpl.h new file mode 100644 index 0000000000..0e50371697 --- /dev/null +++ b/runtime/native/java_lang_invoke_MethodHandleImpl.h @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_INVOKE_METHODHANDLEIMPL_H_ +#define ART_RUNTIME_NATIVE_JAVA_LANG_INVOKE_METHODHANDLEIMPL_H_ + +#include <jni.h> + +namespace art { + +void register_java_lang_invoke_MethodHandleImpl(JNIEnv* env); + +} // namespace art + +#endif // ART_RUNTIME_NATIVE_JAVA_LANG_INVOKE_METHODHANDLEIMPL_H_ diff --git a/runtime/oat.h b/runtime/oat.h index 4a68036e00..106bd4096f 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '1', '0', '4', '\0' }; // Array allocation entrypoints + static constexpr uint8_t kOatVersion[] = { '1', '0', '5', '\0' }; // Stack map alignment change. static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index 976a1e7902..c01e3f4152 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -22,6 +22,7 @@ cc_defaults { "OpenjdkJvmTi.cc", "ti_class.cc", "ti_class_definition.cc", + "ti_class_loader.cc", "ti_dump.cc", "ti_field.cc", "ti_heap.cc", diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index c0c301fae5..a815a603a7 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -1368,6 +1368,7 @@ extern "C" bool ArtPlugin_Initialize() { ThreadUtil::Register(&gEventHandler); ClassUtil::Register(&gEventHandler); DumpUtil::Register(&gEventHandler); + SearchUtil::Register(); runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler); runtime->AddSystemWeakHolder(&gObjectTagTable); @@ -1380,6 +1381,7 @@ extern "C" bool ArtPlugin_Deinitialize() { ThreadUtil::Unregister(); ClassUtil::Unregister(); DumpUtil::Unregister(); + SearchUtil::Unregister(); return true; } diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index d484c98483..c14fd84264 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -54,6 +54,7 @@ #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" #include "thread_list.h" +#include "ti_class_loader.h" #include "ti_redefine.h" #include "utils.h" @@ -228,12 +229,15 @@ struct ClassCallback : public art::ClassLoadCallback { return; } - // TODO Check Redefined dex file for invariants. + // TODO Check Redefined dex file for all invariants. LOG(WARNING) << "Dex file created by class-definition time transformation of " << descriptor << " is not checked for all retransformation invariants."; - // TODO Put it in classpath - LOG(WARNING) << "Dex file created for class-definition time transformation of " - << descriptor << " was not added to any classpaths!"; + + if (!ClassLoaderHelper::AddToClassLoader(self, class_loader, dex_file.get())) { + LOG(ERROR) << "Unable to add " << descriptor << " to class loader!"; + return; + } + // Actually set the ClassExt's original bytes once we have actually succeeded. ext->SetOriginalDexFileBytes(arr.Get()); // Set the return values diff --git a/runtime/openjdkjvmti/ti_class_loader.cc b/runtime/openjdkjvmti/ti_class_loader.cc new file mode 100644 index 0000000000..b68fc60c6c --- /dev/null +++ b/runtime/openjdkjvmti/ti_class_loader.cc @@ -0,0 +1,202 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_class_loader.h" + +#include <limits> + +#include "android-base/stringprintf.h" + +#include "art_jvmti.h" +#include "base/array_slice.h" +#include "base/logging.h" +#include "dex_file.h" +#include "dex_file_types.h" +#include "events-inl.h" +#include "gc/allocation_listener.h" +#include "gc/heap.h" +#include "instrumentation.h" +#include "jit/jit.h" +#include "jit/jit_code_cache.h" +#include "jni_env_ext-inl.h" +#include "jvmti_allocator.h" +#include "mirror/class.h" +#include "mirror/class_ext.h" +#include "mirror/object.h" +#include "object_lock.h" +#include "runtime.h" +#include "ScopedLocalRef.h" +#include "transform.h" + +namespace openjdkjvmti { + +bool ClassLoaderHelper::AddToClassLoader(art::Thread* self, + art::Handle<art::mirror::ClassLoader> loader, + const art::DexFile* dex_file) { + art::StackHandleScope<2> hs(self); + art::Handle<art::mirror::Object> java_dex_file_obj(hs.NewHandle(FindSourceDexFileObject(self, + loader))); + if (java_dex_file_obj.IsNull()) { + return false; + } + art::Handle<art::mirror::LongArray> cookie(hs.NewHandle( + AllocateNewDexFileCookie(self, java_dex_file_obj, dex_file))); + if (cookie.IsNull()) { + return false; + } + art::ScopedAssertNoThreadSuspension nts("Replacing cookie fields in j.l.DexFile object"); + UpdateJavaDexFile(java_dex_file_obj.Get(), cookie.Get()); + return true; +} + +void ClassLoaderHelper::UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file, + art::ObjPtr<art::mirror::LongArray> new_cookie) { + art::ArtField* internal_cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField( + "mInternalCookie", "Ljava/lang/Object;"); + art::ArtField* cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField( + "mCookie", "Ljava/lang/Object;"); + CHECK(internal_cookie_field != nullptr); + art::ObjPtr<art::mirror::LongArray> orig_internal_cookie( + internal_cookie_field->GetObject(java_dex_file)->AsLongArray()); + art::ObjPtr<art::mirror::LongArray> orig_cookie( + cookie_field->GetObject(java_dex_file)->AsLongArray()); + internal_cookie_field->SetObject<false>(java_dex_file, new_cookie); + if (!orig_cookie.IsNull()) { + cookie_field->SetObject<false>(java_dex_file, new_cookie); + } +} + +// TODO Really wishing I had that mirror of java.lang.DexFile now. +art::ObjPtr<art::mirror::LongArray> ClassLoaderHelper::AllocateNewDexFileCookie( + art::Thread* self, + art::Handle<art::mirror::Object> java_dex_file_obj, + const art::DexFile* dex_file) { + art::StackHandleScope<2> hs(self); + // mCookie is nulled out if the DexFile has been closed but mInternalCookie sticks around until + // the object is finalized. Since they always point to the same array if mCookie is not null we + // just use the mInternalCookie field. We will update one or both of these fields later. + // TODO Should I get the class from the classloader or directly? + art::ArtField* internal_cookie_field = java_dex_file_obj->GetClass()->FindDeclaredInstanceField( + "mInternalCookie", "Ljava/lang/Object;"); + // TODO Add check that mCookie is either null or same as mInternalCookie + CHECK(internal_cookie_field != nullptr); + art::Handle<art::mirror::LongArray> cookie( + hs.NewHandle(internal_cookie_field->GetObject(java_dex_file_obj.Get())->AsLongArray())); + // TODO Maybe make these non-fatal. + CHECK(cookie.Get() != nullptr); + CHECK_GE(cookie->GetLength(), 1); + art::Handle<art::mirror::LongArray> new_cookie( + hs.NewHandle(art::mirror::LongArray::Alloc(self, cookie->GetLength() + 1))); + if (new_cookie.Get() == nullptr) { + self->AssertPendingOOMException(); + return nullptr; + } + // Copy the oat-dex field at the start. + // TODO Should I clear this field? + // TODO This is a really crappy thing here with the first element being different. + new_cookie->SetWithoutChecks<false>(0, cookie->GetWithoutChecks(0)); + new_cookie->SetWithoutChecks<false>( + 1, static_cast<int64_t>(reinterpret_cast<intptr_t>(dex_file))); + new_cookie->Memcpy(2, cookie.Get(), 1, cookie->GetLength() - 1); + return new_cookie.Get(); +} + +// TODO This should return the actual source java.lang.DexFile object for the klass being loaded. +art::ObjPtr<art::mirror::Object> ClassLoaderHelper::FindSourceDexFileObject( + art::Thread* self, art::Handle<art::mirror::ClassLoader> loader) { + const char* dex_path_list_element_array_name = "[Ldalvik/system/DexPathList$Element;"; + const char* dex_path_list_element_name = "Ldalvik/system/DexPathList$Element;"; + const char* dex_file_name = "Ldalvik/system/DexFile;"; + const char* dex_path_list_name = "Ldalvik/system/DexPathList;"; + const char* dex_class_loader_name = "Ldalvik/system/BaseDexClassLoader;"; + + CHECK(!self->IsExceptionPending()); + art::StackHandleScope<5> hs(self); + art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker(); + + art::Handle<art::mirror::ClassLoader> null_loader(hs.NewHandle<art::mirror::ClassLoader>( + nullptr)); + art::Handle<art::mirror::Class> base_dex_loader_class(hs.NewHandle(class_linker->FindClass( + self, dex_class_loader_name, null_loader))); + + // Get all the ArtFields so we can look in the BaseDexClassLoader + art::ArtField* path_list_field = base_dex_loader_class->FindDeclaredInstanceField( + "pathList", dex_path_list_name); + CHECK(path_list_field != nullptr); + + art::ArtField* dex_path_list_element_field = + class_linker->FindClass(self, dex_path_list_name, null_loader) + ->FindDeclaredInstanceField("dexElements", dex_path_list_element_array_name); + CHECK(dex_path_list_element_field != nullptr); + + art::ArtField* element_dex_file_field = + class_linker->FindClass(self, dex_path_list_element_name, null_loader) + ->FindDeclaredInstanceField("dexFile", dex_file_name); + CHECK(element_dex_file_field != nullptr); + + // Check if loader is a BaseDexClassLoader + art::Handle<art::mirror::Class> loader_class(hs.NewHandle(loader->GetClass())); + // Currently only base_dex_loader is allowed to actually define classes but if this changes in the + // future we should make sure to support all class loader types. + if (!loader_class->IsSubClass(base_dex_loader_class.Get())) { + LOG(ERROR) << "The classloader is not a BaseDexClassLoader which is currently the only " + << "supported class loader type!"; + return nullptr; + } + // Start navigating the fields of the loader (now known to be a BaseDexClassLoader derivative) + art::Handle<art::mirror::Object> path_list( + hs.NewHandle(path_list_field->GetObject(loader.Get()))); + CHECK(path_list.Get() != nullptr); + CHECK(!self->IsExceptionPending()); + art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle( + dex_path_list_element_field->GetObject(path_list.Get())-> + AsObjectArray<art::mirror::Object>())); + CHECK(!self->IsExceptionPending()); + CHECK(dex_elements_list.Get() != nullptr); + size_t num_elements = dex_elements_list->GetLength(); + // Iterate over the DexPathList$Element to find the right one + for (size_t i = 0; i < num_elements; i++) { + art::ObjPtr<art::mirror::Object> current_element = dex_elements_list->Get(i); + CHECK(!current_element.IsNull()); + // TODO It would be cleaner to put the art::DexFile into the dalvik.system.DexFile the class + // comes from but it is more annoying because we would need to find this class. It is not + // necessary for proper function since we just need to be in front of the classes old dex file + // in the path. + art::ObjPtr<art::mirror::Object> first_dex_file( + element_dex_file_field->GetObject(current_element)); + if (!first_dex_file.IsNull()) { + return first_dex_file; + } + } + return nullptr; +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_class_loader.h b/runtime/openjdkjvmti/ti_class_loader.h new file mode 100644 index 0000000000..17ed0eb196 --- /dev/null +++ b/runtime/openjdkjvmti/ti_class_loader.h @@ -0,0 +1,96 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_LOADER_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_LOADER_H_ + +#include <string> + +#include <jni.h> + +#include "art_jvmti.h" +#include "art_method.h" +#include "base/array_slice.h" +#include "class_linker.h" +#include "dex_file.h" +#include "gc_root-inl.h" +#include "globals.h" +#include "jni_env_ext-inl.h" +#include "jvmti.h" +#include "linear_alloc.h" +#include "mem_map.h" +#include "mirror/array-inl.h" +#include "mirror/array.h" +#include "mirror/class-inl.h" +#include "mirror/class.h" +#include "mirror/class_loader-inl.h" +#include "mirror/string-inl.h" +#include "oat_file.h" +#include "obj_ptr.h" +#include "scoped_thread_state_change-inl.h" +#include "stack.h" +#include "ti_class_definition.h" +#include "thread_list.h" +#include "transform.h" +#include "utf.h" +#include "utils/dex_cache_arrays_layout-inl.h" + +namespace openjdkjvmti { + +// Class that can redefine a single class's methods. +// TODO We should really make this be driven by an outside class so we can do multiple classes at +// the same time and have less required cleanup. +class ClassLoaderHelper { + public: + static bool AddToClassLoader(art::Thread* self, + art::Handle<art::mirror::ClassLoader> loader, + const art::DexFile* dex_file) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + // Finds a java.lang.DexFile object that is associated with the given ClassLoader. Each of these + // j.l.DexFile objects holds several art::DexFile*s in it. + // TODO This should return the actual source java.lang.DexFile object for the klass being loaded. + static art::ObjPtr<art::mirror::Object> FindSourceDexFileObject( + art::Thread* self, art::Handle<art::mirror::ClassLoader> loader) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + static art::ObjPtr<art::mirror::LongArray> AllocateNewDexFileCookie( + art::Thread* self, + art::Handle<art::mirror::Object> java_dex_file, + const art::DexFile* new_dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_); + + static void UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file, + art::ObjPtr<art::mirror::LongArray> new_cookie) + REQUIRES(art::Roles::uninterruptible_) REQUIRES_SHARED(art::Locks::mutator_lock_); +}; + +} // namespace openjdkjvmti +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_LOADER_H_ diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc index 4970288250..60371cfafe 100644 --- a/runtime/openjdkjvmti/ti_phase.cc +++ b/runtime/openjdkjvmti/ti_phase.cc @@ -136,4 +136,8 @@ void PhaseUtil::Unregister() { art::Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gPhaseCallback); } +jvmtiPhase PhaseUtil::GetPhaseUnchecked() { + return PhaseUtil::current_phase_; +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_phase.h b/runtime/openjdkjvmti/ti_phase.h index bd15fa6891..851fc27de5 100644 --- a/runtime/openjdkjvmti/ti_phase.h +++ b/runtime/openjdkjvmti/ti_phase.h @@ -57,6 +57,8 @@ class PhaseUtil { struct PhaseCallback; + static jvmtiPhase GetPhaseUnchecked(); + private: static jvmtiPhase current_phase_; }; diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index d2ddc21cd4..b76d74ae79 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -54,6 +54,7 @@ #include "object_lock.h" #include "runtime.h" #include "ScopedLocalRef.h" +#include "ti_class_loader.h" #include "transform.h" namespace openjdkjvmti { @@ -386,85 +387,6 @@ jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition return OK; } -// TODO *MAJOR* This should return the actual source java.lang.DexFile object for the klass. -// TODO Make mirror of DexFile and associated types to make this less hellish. -// TODO Make mirror of BaseDexClassLoader and associated types to make this less hellish. -art::mirror::Object* Redefiner::ClassRedefinition::FindSourceDexFileObject( - art::Handle<art::mirror::ClassLoader> loader) { - const char* dex_path_list_element_array_name = "[Ldalvik/system/DexPathList$Element;"; - const char* dex_path_list_element_name = "Ldalvik/system/DexPathList$Element;"; - const char* dex_file_name = "Ldalvik/system/DexFile;"; - const char* dex_path_list_name = "Ldalvik/system/DexPathList;"; - const char* dex_class_loader_name = "Ldalvik/system/BaseDexClassLoader;"; - - CHECK(!driver_->self_->IsExceptionPending()); - art::StackHandleScope<11> hs(driver_->self_); - art::ClassLinker* class_linker = driver_->runtime_->GetClassLinker(); - - art::Handle<art::mirror::ClassLoader> null_loader(hs.NewHandle<art::mirror::ClassLoader>( - nullptr)); - art::Handle<art::mirror::Class> base_dex_loader_class(hs.NewHandle(class_linker->FindClass( - driver_->self_, dex_class_loader_name, null_loader))); - - // Get all the ArtFields so we can look in the BaseDexClassLoader - art::ArtField* path_list_field = base_dex_loader_class->FindDeclaredInstanceField( - "pathList", dex_path_list_name); - CHECK(path_list_field != nullptr); - - art::ArtField* dex_path_list_element_field = - class_linker->FindClass(driver_->self_, dex_path_list_name, null_loader) - ->FindDeclaredInstanceField("dexElements", dex_path_list_element_array_name); - CHECK(dex_path_list_element_field != nullptr); - - art::ArtField* element_dex_file_field = - class_linker->FindClass(driver_->self_, dex_path_list_element_name, null_loader) - ->FindDeclaredInstanceField("dexFile", dex_file_name); - CHECK(element_dex_file_field != nullptr); - - // Check if loader is a BaseDexClassLoader - art::Handle<art::mirror::Class> loader_class(hs.NewHandle(loader->GetClass())); - if (!loader_class->IsSubClass(base_dex_loader_class.Get())) { - LOG(ERROR) << "The classloader is not a BaseDexClassLoader which is currently the only " - << "supported class loader type!"; - return nullptr; - } - // Start navigating the fields of the loader (now known to be a BaseDexClassLoader derivative) - art::Handle<art::mirror::Object> path_list( - hs.NewHandle(path_list_field->GetObject(loader.Get()))); - CHECK(path_list.Get() != nullptr); - CHECK(!driver_->self_->IsExceptionPending()); - art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle( - dex_path_list_element_field->GetObject(path_list.Get())-> - AsObjectArray<art::mirror::Object>())); - CHECK(!driver_->self_->IsExceptionPending()); - CHECK(dex_elements_list.Get() != nullptr); - size_t num_elements = dex_elements_list->GetLength(); - art::MutableHandle<art::mirror::Object> current_element( - hs.NewHandle<art::mirror::Object>(nullptr)); - art::MutableHandle<art::mirror::Object> first_dex_file( - hs.NewHandle<art::mirror::Object>(nullptr)); - // Iterate over the DexPathList$Element to find the right one - // TODO Or not ATM just return the first one. - for (size_t i = 0; i < num_elements; i++) { - current_element.Assign(dex_elements_list->Get(i)); - CHECK(current_element.Get() != nullptr); - CHECK(!driver_->self_->IsExceptionPending()); - CHECK(dex_elements_list.Get() != nullptr); - CHECK_EQ(current_element->GetClass(), class_linker->FindClass(driver_->self_, - dex_path_list_element_name, - null_loader)); - // TODO It would be cleaner to put the art::DexFile into the dalvik.system.DexFile the class - // comes from but it is more annoying because we would need to find this class. It is not - // necessary for proper function since we just need to be in front of the classes old dex file - // in the path. - first_dex_file.Assign(element_dex_file_field->GetObject(current_element.Get())); - if (first_dex_file.Get() != nullptr) { - return first_dex_file.Get(); - } - } - return nullptr; -} - art::mirror::Class* Redefiner::ClassRedefinition::GetMirrorClass() { return driver_->self_->DecodeJObject(klass_)->AsClass(); } @@ -478,39 +400,6 @@ art::mirror::DexCache* Redefiner::ClassRedefinition::CreateNewDexCache( return driver_->runtime_->GetClassLinker()->RegisterDexFile(*dex_file_, loader.Get()); } -// TODO Really wishing I had that mirror of java.lang.DexFile now. -art::mirror::LongArray* Redefiner::ClassRedefinition::AllocateDexFileCookie( - art::Handle<art::mirror::Object> java_dex_file_obj) { - art::StackHandleScope<2> hs(driver_->self_); - // mCookie is nulled out if the DexFile has been closed but mInternalCookie sticks around until - // the object is finalized. Since they always point to the same array if mCookie is not null we - // just use the mInternalCookie field. We will update one or both of these fields later. - // TODO Should I get the class from the classloader or directly? - art::ArtField* internal_cookie_field = java_dex_file_obj->GetClass()->FindDeclaredInstanceField( - "mInternalCookie", "Ljava/lang/Object;"); - // TODO Add check that mCookie is either null or same as mInternalCookie - CHECK(internal_cookie_field != nullptr); - art::Handle<art::mirror::LongArray> cookie( - hs.NewHandle(internal_cookie_field->GetObject(java_dex_file_obj.Get())->AsLongArray())); - // TODO Maybe make these non-fatal. - CHECK(cookie.Get() != nullptr); - CHECK_GE(cookie->GetLength(), 1); - art::Handle<art::mirror::LongArray> new_cookie( - hs.NewHandle(art::mirror::LongArray::Alloc(driver_->self_, cookie->GetLength() + 1))); - if (new_cookie.Get() == nullptr) { - driver_->self_->AssertPendingOOMException(); - return nullptr; - } - // Copy the oat-dex field at the start. - // TODO Should I clear this field? - // TODO This is a really crappy thing here with the first element being different. - new_cookie->SetWithoutChecks<false>(0, cookie->GetWithoutChecks(0)); - new_cookie->SetWithoutChecks<false>( - 1, static_cast<int64_t>(reinterpret_cast<intptr_t>(dex_file_.get()))); - new_cookie->Memcpy(2, cookie.Get(), 1, cookie->GetLength() - 1); - return new_cookie.Get(); -} - void Redefiner::RecordFailure(jvmtiError result, const std::string& class_sig, const std::string& error_msg) { @@ -854,14 +743,18 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations( RecordFailure(ERR(INTERNAL), "Unable to find class loader!"); return false; } - art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(FindSourceDexFileObject(loader))); + art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle( + ClassLoaderHelper::FindSourceDexFileObject(driver_->self_, loader))); holder->SetJavaDexFile(klass_index, dex_file_obj.Get()); if (dex_file_obj.Get() == nullptr) { // TODO Better error msg. RecordFailure(ERR(INTERNAL), "Unable to find class loader!"); return false; } - holder->SetNewDexFileCookie(klass_index, AllocateDexFileCookie(dex_file_obj)); + holder->SetNewDexFileCookie(klass_index, + ClassLoaderHelper::AllocateNewDexFileCookie(driver_->self_, + dex_file_obj, + dex_file_.get()).Ptr()); if (holder->GetNewDexFileCookie(klass_index) == nullptr) { driver_->self_->AssertPendingOOMException(); driver_->self_->ClearException(); @@ -973,8 +866,10 @@ jvmtiError Redefiner::Run() { // TODO We need to decide on & implement semantics for JNI jmethodids when we redefine methods. int32_t cnt = 0; for (Redefiner::ClassRedefinition& redef : redefinitions_) { + art::ScopedAssertNoThreadSuspension nts("Updating runtime objects for redefinition"); art::mirror::Class* klass = holder.GetMirrorClass(cnt); - redef.UpdateJavaDexFile(holder.GetJavaDexFile(cnt), holder.GetNewDexFileCookie(cnt)); + ClassLoaderHelper::UpdateJavaDexFile(holder.GetJavaDexFile(cnt), + holder.GetNewDexFileCookie(cnt)); // TODO Rewrite so we don't do a stack walk for each and every class. redef.FindAndAllocateObsoleteMethods(klass); redef.UpdateClass(klass, holder.GetNewDexCache(cnt), holder.GetOriginalDexFileBytes(cnt)); @@ -1090,24 +985,6 @@ void Redefiner::ClassRedefinition::UpdateClass( ext->SetOriginalDexFileBytes(original_dex_file); } -void Redefiner::ClassRedefinition::UpdateJavaDexFile( - art::ObjPtr<art::mirror::Object> java_dex_file, - art::ObjPtr<art::mirror::LongArray> new_cookie) { - art::ArtField* internal_cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField( - "mInternalCookie", "Ljava/lang/Object;"); - art::ArtField* cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField( - "mCookie", "Ljava/lang/Object;"); - CHECK(internal_cookie_field != nullptr); - art::ObjPtr<art::mirror::LongArray> orig_internal_cookie( - internal_cookie_field->GetObject(java_dex_file)->AsLongArray()); - art::ObjPtr<art::mirror::LongArray> orig_cookie( - cookie_field->GetObject(java_dex_file)->AsLongArray()); - internal_cookie_field->SetObject<false>(java_dex_file, new_cookie); - if (!orig_cookie.IsNull()) { - cookie_field->SetObject<false>(java_dex_file, new_cookie); - } -} - // This function does all (java) allocations we need to do for the Class being redefined. // TODO Change this name maybe? bool Redefiner::ClassRedefinition::EnsureClassAllocationsFinished() { diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h index fdc13eee32..85df6e1024 100644 --- a/runtime/openjdkjvmti/ti_redefine.h +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -127,18 +127,9 @@ class Redefiner { art::mirror::Class* GetMirrorClass() REQUIRES_SHARED(art::Locks::mutator_lock_); art::mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(art::Locks::mutator_lock_); - // This finds the java.lang.DexFile we will add the native DexFile to as part of the classpath. - // TODO Make sure the DexFile object returned is the one that the klass_ actually comes from. - art::mirror::Object* FindSourceDexFileObject(art::Handle<art::mirror::ClassLoader> loader) - REQUIRES_SHARED(art::Locks::mutator_lock_); - art::mirror::DexCache* CreateNewDexCache(art::Handle<art::mirror::ClassLoader> loader) REQUIRES_SHARED(art::Locks::mutator_lock_); - // Allocates and fills the new DexFileCookie - art::mirror::LongArray* AllocateDexFileCookie(art::Handle<art::mirror::Object> j_dex_file_obj) - REQUIRES_SHARED(art::Locks::mutator_lock_); - // This may return nullptr with a OOME pending if allocation fails. art::mirror::ByteArray* AllocateOrGetOriginalDexFileBytes() REQUIRES_SHARED(art::Locks::mutator_lock_); diff --git a/runtime/openjdkjvmti/ti_search.cc b/runtime/openjdkjvmti/ti_search.cc index 913d2b6a74..df80f85ed8 100644 --- a/runtime/openjdkjvmti/ti_search.cc +++ b/runtime/openjdkjvmti/ti_search.cc @@ -34,15 +34,177 @@ #include "jni.h" #include "art_jvmti.h" +#include "base/enums.h" #include "base/macros.h" #include "class_linker.h" #include "dex_file.h" +#include "jni_internal.h" +#include "mirror/class-inl.h" +#include "mirror/object.h" +#include "mirror/string.h" +#include "obj_ptr-inl.h" #include "runtime.h" +#include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" #include "ScopedLocalRef.h" +#include "ti_phase.h" +#include "thread-inl.h" +#include "thread_list.h" namespace openjdkjvmti { +static std::vector<std::string> gSystemOnloadSegments; + +static art::ObjPtr<art::mirror::Object> GetSystemProperties(art::Thread* self, + art::ClassLinker* class_linker) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::ObjPtr<art::mirror::Class> system_class = + class_linker->LookupClass(self, "Ljava/lang/System;", nullptr); + DCHECK(system_class != nullptr); + DCHECK(system_class->IsInitialized()); + + art::ArtField* props_field = + system_class->FindDeclaredStaticField("props", "Ljava/util/Properties;"); + DCHECK(props_field != nullptr); + + art::ObjPtr<art::mirror::Object> props_obj = props_field->GetObject(system_class); + DCHECK(props_obj != nullptr); + + return props_obj; +} + +static void Update() REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (gSystemOnloadSegments.empty()) { + return; + } + + // In the on-load phase we have to modify java.class.path to influence the system classloader. + // As this is an unmodifiable system property, we have to access the "defaults" field. + art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker(); + DCHECK(class_linker != nullptr); + art::Thread* self = art::Thread::Current(); + + // Prepare: collect classes, fields and methods. + art::ObjPtr<art::mirror::Class> properties_class = + class_linker->LookupClass(self, "Ljava/util/Properties;", nullptr); + DCHECK(properties_class != nullptr); + + ScopedLocalRef<jobject> defaults_jobj(self->GetJniEnv(), nullptr); + { + art::ObjPtr<art::mirror::Object> props_obj = GetSystemProperties(self, class_linker); + + art::ArtField* defaults_field = + properties_class->FindDeclaredInstanceField("defaults", "Ljava/util/Properties;"); + DCHECK(defaults_field != nullptr); + + art::ObjPtr<art::mirror::Object> defaults_obj = defaults_field->GetObject(props_obj); + DCHECK(defaults_obj != nullptr); + defaults_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(defaults_obj)); + } + + art::ArtMethod* get_property = + properties_class->FindDeclaredVirtualMethod( + "getProperty", + "(Ljava/lang/String;)Ljava/lang/String;", + art::kRuntimePointerSize); + DCHECK(get_property != nullptr); + art::ArtMethod* set_property = + properties_class->FindDeclaredVirtualMethod( + "setProperty", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;", + art::kRuntimePointerSize); + DCHECK(set_property != nullptr); + + // This is an allocation. Do this late to avoid the need for handles. + ScopedLocalRef<jobject> cp_jobj(self->GetJniEnv(), nullptr); + { + art::ObjPtr<art::mirror::Object> cp_key = + art::mirror::String::AllocFromModifiedUtf8(self, "java.class.path"); + if (cp_key == nullptr) { + self->AssertPendingOOMException(); + self->ClearException(); + return; + } + cp_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(cp_key)); + } + + // OK, now get the current value. + std::string str_value; + { + ScopedLocalRef<jobject> old_value(self->GetJniEnv(), + self->GetJniEnv()->CallObjectMethod( + defaults_jobj.get(), + art::jni::EncodeArtMethod(get_property), + cp_jobj.get())); + DCHECK(old_value.get() != nullptr); + + str_value = self->DecodeJObject(old_value.get())->AsString()->ToModifiedUtf8(); + self->GetJniEnv()->DeleteLocalRef(old_value.release()); + } + + // Update the value by appending the new segments. + for (const std::string& segment : gSystemOnloadSegments) { + if (!str_value.empty()) { + str_value += ":"; + } + str_value += segment; + } + gSystemOnloadSegments.clear(); + + // Create the new value object. + ScopedLocalRef<jobject> new_val_jobj(self->GetJniEnv(), nullptr); + { + art::ObjPtr<art::mirror::Object> new_value = + art::mirror::String::AllocFromModifiedUtf8(self, str_value.c_str()); + if (new_value == nullptr) { + self->AssertPendingOOMException(); + self->ClearException(); + return; + } + + new_val_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(new_value)); + } + + // Write to the defaults. + ScopedLocalRef<jobject> res_obj(self->GetJniEnv(), + self->GetJniEnv()->CallObjectMethod(defaults_jobj.get(), + art::jni::EncodeArtMethod(set_property), + cp_jobj.get(), + new_val_jobj.get())); + if (self->IsExceptionPending()) { + self->ClearException(); + return; + } +} + +struct SearchCallback : public art::RuntimePhaseCallback { + void NextRuntimePhase(RuntimePhase phase) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (phase == RuntimePhase::kStart) { + // It's time to update the system properties. + Update(); + } + } +}; + +static SearchCallback gSearchCallback; + +void SearchUtil::Register() { + art::Runtime* runtime = art::Runtime::Current(); + + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Add search callback"); + runtime->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gSearchCallback); +} + +void SearchUtil::Unregister() { + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Remove search callback"); + art::Runtime* runtime = art::Runtime::Current(); + runtime->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gSearchCallback); +} + jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_UNUSED, const char* segment) { art::Runtime* current = art::Runtime::Current(); @@ -78,14 +240,21 @@ jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env ATTRIBUT return ERR(NULL_POINTER); } - art::Runtime* current = art::Runtime::Current(); - if (current == nullptr) { + jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked(); + + if (phase == jvmtiPhase::JVMTI_PHASE_ONLOAD) { + // We could try and see whether it is a valid path. We could also try to allocate Java + // objects to avoid later OOME. + gSystemOnloadSegments.push_back(segment); + return ERR(NONE); + } else if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) { return ERR(WRONG_PHASE); } - jobject sys_class_loader = current->GetSystemClassLoader(); + + jobject sys_class_loader = art::Runtime::Current()->GetSystemClassLoader(); if (sys_class_loader == nullptr) { - // TODO: Support classpath change in OnLoad. - return ERR(WRONG_PHASE); + // This is unexpected. + return ERR(INTERNAL); } // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside, diff --git a/runtime/openjdkjvmti/ti_search.h b/runtime/openjdkjvmti/ti_search.h index 6a52e80405..cd7b4be28c 100644 --- a/runtime/openjdkjvmti/ti_search.h +++ b/runtime/openjdkjvmti/ti_search.h @@ -32,12 +32,17 @@ #ifndef ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_ #define ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_ +#include <vector> + #include "jvmti.h" namespace openjdkjvmti { class SearchUtil { public: + static void Register(); + static void Unregister(); + static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment); static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment); diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index b809c3eb56..8d758a4a4b 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -438,7 +438,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { const uint8_t* addr = reinterpret_cast<const uint8_t*>(GetCurrentQuickFrame()) + offset; value = *reinterpret_cast<const uint32_t*>(addr); uint32_t bit = (offset >> 2); - if (stack_map.GetNumberOfStackMaskBits(encoding.stack_map_encoding) > bit && + if (code_info.GetNumberOfStackMaskBits(encoding) > bit && stack_map.GetStackMaskBit(encoding.stack_map_encoding, bit)) { is_reference = true; } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index b079349d31..0ea42960dd 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -114,6 +114,7 @@ #include "native/java_lang_Thread.h" #include "native/java_lang_Throwable.h" #include "native/java_lang_VMClassLoader.h" +#include "native/java_lang_invoke_MethodHandleImpl.h" #include "native/java_lang_ref_FinalizerReference.h" #include "native/java_lang_ref_Reference.h" #include "native/java_lang_reflect_Array.h" @@ -1539,6 +1540,7 @@ void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) { register_java_lang_Class(env); register_java_lang_DexCache(env); register_java_lang_Object(env); + register_java_lang_invoke_MethodHandleImpl(env); register_java_lang_ref_FinalizerReference(env); register_java_lang_reflect_Array(env); register_java_lang_reflect_Constructor(env); diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 690b069c8e..e093293e75 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -198,7 +198,7 @@ void StackMap::Dump(VariableIndentationOutputStream* vios, << "StackMap" << header_suffix << std::hex << " [native_pc=0x" << code_offset + pc_offset << "]" - << " [entry_size=0x" << encoding.stack_map_size_in_bytes << "]" + << " [entry_size=0x" << encoding.stack_map_size_in_bits << " bits]" << " (dex_pc=0x" << GetDexPc(stack_map_encoding) << ", native_pc_offset=0x" << pc_offset << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(stack_map_encoding) @@ -206,7 +206,7 @@ void StackMap::Dump(VariableIndentationOutputStream* vios, << ", register_mask=0x" << GetRegisterMask(stack_map_encoding) << std::dec << ", stack_mask=0b"; - for (size_t i = 0, e = GetNumberOfStackMaskBits(stack_map_encoding); i < e; ++i) { + for (size_t i = 0, e = code_info.GetNumberOfStackMaskBits(encoding); i < e; ++i) { vios->Stream() << GetStackMaskBit(stack_map_encoding, e - i - 1); } vios->Stream() << ")\n"; diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 578252181d..679218d5be 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -20,6 +20,7 @@ #include "arch/code_offset.h" #include "base/bit_vector.h" #include "base/bit_utils.h" +#include "bit_memory_region.h" #include "dex_file.h" #include "memory_region.h" #include "leb128.h" @@ -665,12 +666,14 @@ struct FieldEncoding { ALWAYS_INLINE size_t BitSize() const { return end_offset_ - start_offset_; } - ALWAYS_INLINE int32_t Load(const MemoryRegion& region) const { + template <typename Region> + ALWAYS_INLINE int32_t Load(const Region& region) const { DCHECK_LE(end_offset_, region.size_in_bits()); return static_cast<int32_t>(region.LoadBits(start_offset_, BitSize())) + min_value_; } - ALWAYS_INLINE void Store(MemoryRegion region, int32_t value) const { + template <typename Region> + ALWAYS_INLINE void Store(Region region, int32_t value) const { region.StoreBits(start_offset_, value - min_value_, BitSize()); DCHECK_EQ(Load(region), value); } @@ -686,7 +689,7 @@ class StackMapEncoding { StackMapEncoding() {} // Set stack map bit layout based on given sizes. - // Returns the size of stack map in bytes. + // Returns the size of stack map in bits. size_t SetFromSizes(size_t native_pc_max, size_t dex_pc_max, size_t dex_register_map_size, @@ -719,7 +722,7 @@ class StackMapEncoding { stack_mask_bit_offset_ = dchecked_integral_cast<uint8_t>(bit_offset); bit_offset += stack_mask_bit_size; - return RoundUp(bit_offset, kBitsPerByte) / kBitsPerByte; + return bit_offset; } ALWAYS_INLINE FieldEncoding GetNativePcEncoding() const { @@ -741,6 +744,10 @@ class StackMapEncoding { // The end offset is not encoded. It is implicitly the end of stack map entry. return stack_mask_bit_offset_; } + ALWAYS_INLINE size_t GetNumberOfStackMaskBits(size_t stack_map_bits) const { + // Note that the stack mask bits are last. + return stack_map_bits - GetStackMaskBitOffset(); + } void Dump(VariableIndentationOutputStream* vios) const; @@ -769,7 +776,7 @@ class StackMapEncoding { class StackMap { public: StackMap() {} - explicit StackMap(MemoryRegion region) : region_(region) {} + explicit StackMap(BitMemoryRegion region) : region_(region) {} ALWAYS_INLINE bool IsValid() const { return region_.pointer() != nullptr; } @@ -817,10 +824,6 @@ class StackMap { encoding.GetRegisterMaskEncoding().Store(region_, mask); } - ALWAYS_INLINE size_t GetNumberOfStackMaskBits(const StackMapEncoding& encoding) const { - return region_.size_in_bits() - encoding.GetStackMaskBitOffset(); - } - ALWAYS_INLINE bool GetStackMaskBit(const StackMapEncoding& encoding, size_t index) const { return region_.LoadBit(encoding.GetStackMaskBitOffset() + index); } @@ -838,7 +841,9 @@ class StackMap { } ALWAYS_INLINE bool Equals(const StackMap& other) const { - return region_.pointer() == other.region_.pointer() && region_.size() == other.region_.size(); + return region_.pointer() == other.region_.pointer() && + region_.size() == other.region_.size() && + region_.BitOffset() == other.region_.BitOffset(); } void Dump(VariableIndentationOutputStream* vios, @@ -860,7 +865,7 @@ class StackMap { private: static constexpr int kFixedSize = 0; - MemoryRegion region_; + BitMemoryRegion region_; friend class StackMapStream; }; @@ -1026,7 +1031,7 @@ class InlineInfo { struct CodeInfoEncoding { uint32_t non_header_size; uint32_t number_of_stack_maps; - uint32_t stack_map_size_in_bytes; + uint32_t stack_map_size_in_bits; uint32_t number_of_location_catalog_entries; StackMapEncoding stack_map_encoding; InlineInfoEncoding inline_info_encoding; @@ -1038,7 +1043,7 @@ struct CodeInfoEncoding { const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data); non_header_size = DecodeUnsignedLeb128(&ptr); number_of_stack_maps = DecodeUnsignedLeb128(&ptr); - stack_map_size_in_bytes = DecodeUnsignedLeb128(&ptr); + stack_map_size_in_bits = DecodeUnsignedLeb128(&ptr); number_of_location_catalog_entries = DecodeUnsignedLeb128(&ptr); static_assert(alignof(StackMapEncoding) == 1, "StackMapEncoding should not require alignment"); @@ -1059,7 +1064,7 @@ struct CodeInfoEncoding { void Compress(Vector* dest) const { EncodeUnsignedLeb128(dest, non_header_size); EncodeUnsignedLeb128(dest, number_of_stack_maps); - EncodeUnsignedLeb128(dest, stack_map_size_in_bytes); + EncodeUnsignedLeb128(dest, stack_map_size_in_bits); EncodeUnsignedLeb128(dest, number_of_location_catalog_entries); const uint8_t* stack_map_ptr = reinterpret_cast<const uint8_t*>(&stack_map_encoding); dest->insert(dest->end(), stack_map_ptr, stack_map_ptr + sizeof(StackMapEncoding)); @@ -1078,7 +1083,7 @@ struct CodeInfoEncoding { * * where CodeInfoEncoding is of the form: * - * [non_header_size, number_of_stack_maps, stack_map_size_in_bytes, + * [non_header_size, number_of_stack_maps, stack_map_size_in_bits, * number_of_location_catalog_entries, StackMapEncoding] */ class CodeInfo { @@ -1108,9 +1113,13 @@ class CodeInfo { GetDexRegisterLocationCatalogSize(encoding))); } + ALWAYS_INLINE size_t GetNumberOfStackMaskBits(const CodeInfoEncoding& encoding) const { + return encoding.stack_map_encoding.GetNumberOfStackMaskBits(encoding.stack_map_size_in_bits); + } + ALWAYS_INLINE StackMap GetStackMapAt(size_t i, const CodeInfoEncoding& encoding) const { - size_t stack_map_size = encoding.stack_map_size_in_bytes; - return StackMap(GetStackMaps(encoding).Subregion(i * stack_map_size, stack_map_size)); + const size_t map_size = encoding.stack_map_size_in_bits; + return StackMap(BitMemoryRegion(GetStackMaps(encoding), i * map_size, map_size)); } uint32_t GetNumberOfLocationCatalogEntries(const CodeInfoEncoding& encoding) const { @@ -1128,7 +1137,8 @@ class CodeInfo { // Get the size of all the stack maps of this CodeInfo object, in bytes. size_t GetStackMapsSize(const CodeInfoEncoding& encoding) const { - return encoding.stack_map_size_in_bytes * GetNumberOfStackMaps(encoding); + return RoundUp(encoding.stack_map_size_in_bits * GetNumberOfStackMaps(encoding), kBitsPerByte) / + kBitsPerByte; } uint32_t GetDexRegisterLocationCatalogOffset(const CodeInfoEncoding& encoding) const { @@ -1278,7 +1288,7 @@ class CodeInfo { << encoding.non_header_size << "\n" << encoding.number_of_location_catalog_entries << "\n" << encoding.number_of_stack_maps << "\n" - << encoding.stack_map_size_in_bytes; + << encoding.stack_map_size_in_bits; } } diff --git a/runtime/thread.cc b/runtime/thread.cc index 66a03a6826..ae87569e7e 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -3035,7 +3035,7 @@ class ReferenceMapVisitor : public StackVisitor { T vreg_info(m, code_info, encoding, map, visitor_); // Visit stack entries that hold pointers. - size_t number_of_bits = map.GetNumberOfStackMaskBits(encoding.stack_map_encoding); + size_t number_of_bits = code_info.GetNumberOfStackMaskBits(encoding); for (size_t i = 0; i < number_of_bits; ++i) { if (map.GetStackMaskBit(encoding.stack_map_encoding, i)) { auto* ref_addr = vreg_base + i; diff --git a/test/616-cha-abstract/expected.txt b/test/616-cha-abstract/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-abstract/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-abstract/info.txt b/test/616-cha-abstract/info.txt new file mode 100644 index 0000000000..4f7e01391d --- /dev/null +++ b/test/616-cha-abstract/info.txt @@ -0,0 +1 @@ +Test for Class Hierarchy Analysis (CHA) on abstract method. diff --git a/test/616-cha-abstract/run b/test/616-cha-abstract/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-abstract/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# 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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-abstract/src/Main.java b/test/616-cha-abstract/src/Main.java new file mode 100644 index 0000000000..e1d7db170d --- /dev/null +++ b/test/616-cha-abstract/src/Main.java @@ -0,0 +1,159 @@ +/* + * 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. + */ + +abstract class Base { + abstract void foo(int i); + + void printError(String msg) { + System.out.println(msg); + } +} + +class Main1 extends Base { + void foo(int i) { + if (i != 1) { + printError("error1"); + } + } +} + +class Main2 extends Main1 { + void foo(int i) { + if (i != 2) { + printError("error2"); + } + } +} + +public class Main { + static Main1 sMain1; + static Main1 sMain2; + + static boolean sIsOptimizing = true; + static boolean sHasJIT = true; + static volatile boolean sOtherThreadStarted; + + private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) { + if (hasSingleImplementation(clazz, method_name) != b) { + System.out.println(clazz + "." + method_name + + " doesn't have single implementation value of " + b); + } + } + + // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked. + // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined. + // After Dummy.createMain2() which links in Main2, live testOverride() on stack + // should be deoptimized. + static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) { + if (setHasJIT) { + if (isInterpreted()) { + sHasJIT = false; + } + return; + } + + if (createMain2 && (sIsOptimizing || sHasJIT)) { + assertIsManaged(); + } + + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + + if (createMain2) { + // Wait for the other thread to start. + while (!sOtherThreadStarted); + // Create an Main2 instance and assign it to sMain2. + // sMain1 is kept the same. + sMain2 = Dummy.createMain2(); + // Wake up the other thread. + synchronized(Main.class) { + Main.class.notify(); + } + } else if (wait) { + // This is the other thread. + synchronized(Main.class) { + sOtherThreadStarted = true; + // Wait for Main2 to be linked and deoptimization is triggered. + try { + Main.class.wait(); + } catch (Exception e) { + } + } + } + + // There should be a deoptimization here right after Main2 is linked by + // calling Dummy.createMain2(), even though sMain1 didn't change. + // The behavior here would be different if inline-cache is used, which + // doesn't deoptimize since sMain1 still hits the type cache. + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) { + // This method should be deoptimized right after Main2 is created. + assertIsInterpreted(); + } + + if (sMain2 != null) { + sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2); + } + } + + // Test scenarios under which CHA-based devirtualization happens, + // and class loading that overrides a method can invalidate compiled code. + public static void main(String[] args) { + System.loadLibrary(args[0]); + + if (isInterpreted()) { + sIsOptimizing = false; + } + + // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet. + sMain1 = new Main1(); + + ensureJitCompiled(Main.class, "testOverride"); + testOverride(false, false, true); + + if (sHasJIT && !sIsOptimizing) { + assertSingleImplementation(Base.class, "foo", true); + assertSingleImplementation(Main1.class, "foo", true); + } else { + // Main2 is verified ahead-of-time so it's linked in already. + } + + // Create another thread that also calls sMain1.foo(). + // Try to test suspend and deopt another thread. + new Thread() { + public void run() { + testOverride(false, true, false); + } + }.start(); + + // This will create Main2 instance in the middle of testOverride(). + testOverride(true, false, false); + assertSingleImplementation(Base.class, "foo", false); + assertSingleImplementation(Main1.class, "foo", false); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); + private static native void assertIsInterpreted(); + private static native void assertIsManaged(); + private static native boolean isInterpreted(); + private static native boolean hasSingleImplementation(Class<?> clazz, String method_name); +} + +// Put createMain2() in another class to avoid class loading due to verifier. +class Dummy { + static Main1 createMain2() { + return new Main2(); + } +} diff --git a/test/635-checker-arm64-volatile-load-cc/expected.txt b/test/635-checker-arm64-volatile-load-cc/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/635-checker-arm64-volatile-load-cc/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/635-checker-arm64-volatile-load-cc/info.txt b/test/635-checker-arm64-volatile-load-cc/info.txt new file mode 100644 index 0000000000..5d67df4a19 --- /dev/null +++ b/test/635-checker-arm64-volatile-load-cc/info.txt @@ -0,0 +1,3 @@ +Regression test checking that the VIXL ARM64 scratch register pool is +not exhausted when generating a volatile field load with a large +offset with (Baker) read barriers (b/34726333). diff --git a/test/635-checker-arm64-volatile-load-cc/src/Main.java b/test/635-checker-arm64-volatile-load-cc/src/Main.java new file mode 100644 index 0000000000..6a26e940ac --- /dev/null +++ b/test/635-checker-arm64-volatile-load-cc/src/Main.java @@ -0,0 +1,284 @@ +/* + * 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. + */ + +public class Main { + + static volatile Object s000, s001, s002, s003, s004, s005, s006, s007, s008, s009; + static volatile Object s010, s011, s012, s013, s014, s015, s016, s017, s018, s019; + static volatile Object s020, s021, s022, s023, s024, s025, s026, s027, s028, s029; + static volatile Object s030, s031, s032, s033, s034, s035, s036, s037, s038, s039; + static volatile Object s040, s041, s042, s043, s044, s045, s046, s047, s048, s049; + static volatile Object s050, s051, s052, s053, s054, s055, s056, s057, s058, s059; + static volatile Object s060, s061, s062, s063, s064, s065, s066, s067, s068, s069; + static volatile Object s070, s071, s072, s073, s074, s075, s076, s077, s078, s079; + static volatile Object s080, s081, s082, s083, s084, s085, s086, s087, s088, s089; + static volatile Object s090, s091, s092, s093, s094, s095, s096, s097, s098, s099; + + static volatile Object s100, s101, s102, s103, s104, s105, s106, s107, s108, s109; + static volatile Object s110, s111, s112, s113, s114, s115, s116, s117, s118, s119; + static volatile Object s120, s121, s122, s123, s124, s125, s126, s127, s128, s129; + static volatile Object s130, s131, s132, s133, s134, s135, s136, s137, s138, s139; + static volatile Object s140, s141, s142, s143, s144, s145, s146, s147, s148, s149; + static volatile Object s150, s151, s152, s153, s154, s155, s156, s157, s158, s159; + static volatile Object s160, s161, s162, s163, s164, s165, s166, s167, s168, s169; + static volatile Object s170, s171, s172, s173, s174, s175, s176, s177, s178, s179; + static volatile Object s180, s181, s182, s183, s184, s185, s186, s187, s188, s189; + static volatile Object s190, s191, s192, s193, s194, s195, s196, s197, s198, s199; + + static volatile Object s200, s201, s202, s203, s204, s205, s206, s207, s208, s209; + static volatile Object s210, s211, s212, s213, s214, s215, s216, s217, s218, s219; + static volatile Object s220, s221, s222, s223, s224, s225, s226, s227, s228, s229; + static volatile Object s230, s231, s232, s233, s234, s235, s236, s237, s238, s239; + static volatile Object s240, s241, s242, s243, s244, s245, s246, s247, s248, s249; + static volatile Object s250, s251, s252, s253, s254, s255, s256, s257, s258, s259; + static volatile Object s260, s261, s262, s263, s264, s265, s266, s267, s268, s269; + static volatile Object s270, s271, s272, s273, s274, s275, s276, s277, s278, s279; + static volatile Object s280, s281, s282, s283, s284, s285, s286, s287, s288, s289; + static volatile Object s290, s291, s292, s293, s294, s295, s296, s297, s298, s299; + + static volatile Object s300, s301, s302, s303, s304, s305, s306, s307, s308, s309; + static volatile Object s310, s311, s312, s313, s314, s315, s316, s317, s318, s319; + static volatile Object s320, s321, s322, s323, s324, s325, s326, s327, s328, s329; + static volatile Object s330, s331, s332, s333, s334, s335, s336, s337, s338, s339; + static volatile Object s340, s341, s342, s343, s344, s345, s346, s347, s348, s349; + static volatile Object s350, s351, s352, s353, s354, s355, s356, s357, s358, s359; + static volatile Object s360, s361, s362, s363, s364, s365, s366, s367, s368, s369; + static volatile Object s370, s371, s372, s373, s374, s375, s376, s377, s378, s379; + static volatile Object s380, s381, s382, s383, s384, s385, s386, s387, s388, s389; + static volatile Object s390, s391, s392, s393, s394, s395, s396, s397, s398, s399; + + static volatile Object s400, s401, s402, s403, s404, s405, s406, s407, s408, s409; + static volatile Object s410, s411, s412, s413, s414, s415, s416, s417, s418, s419; + static volatile Object s420, s421, s422, s423, s424, s425, s426, s427, s428, s429; + static volatile Object s430, s431, s432, s433, s434, s435, s436, s437, s438, s439; + static volatile Object s440, s441, s442, s443, s444, s445, s446, s447, s448, s449; + static volatile Object s450, s451, s452, s453, s454, s455, s456, s457, s458, s459; + static volatile Object s460, s461, s462, s463, s464, s465, s466, s467, s468, s469; + static volatile Object s470, s471, s472, s473, s474, s475, s476, s477, s478, s479; + static volatile Object s480, s481, s482, s483, s484, s485, s486, s487, s488, s489; + static volatile Object s490, s491, s492, s493, s494, s495, s496, s497, s498, s499; + + static volatile Object s500, s501, s502, s503, s504, s505, s506, s507, s508, s509; + static volatile Object s510, s511, s512, s513, s514, s515, s516, s517, s518, s519; + static volatile Object s520, s521, s522, s523, s524, s525, s526, s527, s528, s529; + static volatile Object s530, s531, s532, s533, s534, s535, s536, s537, s538, s539; + static volatile Object s540, s541, s542, s543, s544, s545, s546, s547, s548, s549; + static volatile Object s550, s551, s552, s553, s554, s555, s556, s557, s558, s559; + static volatile Object s560, s561, s562, s563, s564, s565, s566, s567, s568, s569; + static volatile Object s570, s571, s572, s573, s574, s575, s576, s577, s578, s579; + static volatile Object s580, s581, s582, s583, s584, s585, s586, s587, s588, s589; + static volatile Object s590, s591, s592, s593, s594, s595, s596, s597, s598, s599; + + static volatile Object s600, s601, s602, s603, s604, s605, s606, s607, s608, s609; + static volatile Object s610, s611, s612, s613, s614, s615, s616, s617, s618, s619; + static volatile Object s620, s621, s622, s623, s624, s625, s626, s627, s628, s629; + static volatile Object s630, s631, s632, s633, s634, s635, s636, s637, s638, s639; + static volatile Object s640, s641, s642, s643, s644, s645, s646, s647, s648, s649; + static volatile Object s650, s651, s652, s653, s654, s655, s656, s657, s658, s659; + static volatile Object s660, s661, s662, s663, s664, s665, s666, s667, s668, s669; + static volatile Object s670, s671, s672, s673, s674, s675, s676, s677, s678, s679; + static volatile Object s680, s681, s682, s683, s684, s685, s686, s687, s688, s689; + static volatile Object s690, s691, s692, s693, s694, s695, s696, s697, s698, s699; + + static volatile Object s700, s701, s702, s703, s704, s705, s706, s707, s708, s709; + static volatile Object s710, s711, s712, s713, s714, s715, s716, s717, s718, s719; + static volatile Object s720, s721, s722, s723, s724, s725, s726, s727, s728, s729; + static volatile Object s730, s731, s732, s733, s734, s735, s736, s737, s738, s739; + static volatile Object s740, s741, s742, s743, s744, s745, s746, s747, s748, s749; + static volatile Object s750, s751, s752, s753, s754, s755, s756, s757, s758, s759; + static volatile Object s760, s761, s762, s763, s764, s765, s766, s767, s768, s769; + static volatile Object s770, s771, s772, s773, s774, s775, s776, s777, s778, s779; + static volatile Object s780, s781, s782, s783, s784, s785, s786, s787, s788, s789; + static volatile Object s790, s791, s792, s793, s794, s795, s796, s797, s798, s799; + + static volatile Object s800, s801, s802, s803, s804, s805, s806, s807, s808, s809; + static volatile Object s810, s811, s812, s813, s814, s815, s816, s817, s818, s819; + static volatile Object s820, s821, s822, s823, s824, s825, s826, s827, s828, s829; + static volatile Object s830, s831, s832, s833, s834, s835, s836, s837, s838, s839; + static volatile Object s840, s841, s842, s843, s844, s845, s846, s847, s848, s849; + static volatile Object s850, s851, s852, s853, s854, s855, s856, s857, s858, s859; + static volatile Object s860, s861, s862, s863, s864, s865, s866, s867, s868, s869; + static volatile Object s870, s871, s872, s873, s874, s875, s876, s877, s878, s879; + static volatile Object s880, s881, s882, s883, s884, s885, s886, s887, s888, s889; + static volatile Object s890, s891, s892, s893, s894, s895, s896, s897, s898, s899; + + static volatile Object s900, s901, s902, s903, s904, s905, s906, s907, s908, s909; + static volatile Object s910, s911, s912, s913, s914, s915, s916, s917, s918, s919; + static volatile Object s920, s921, s922, s923, s924, s925, s926, s927, s928, s929; + static volatile Object s930, s931, s932, s933, s934, s935, s936, s937, s938, s939; + static volatile Object s940, s941, s942, s943, s944, s945, s946, s947, s948, s949; + static volatile Object s950, s951, s952, s953, s954, s955, s956, s957, s958, s959; + static volatile Object s960, s961, s962, s963, s964, s965, s966, s967, s968, s969; + static volatile Object s970, s971, s972, s973, s974, s975, s976, s977, s978, s979; + static volatile Object s980, s981, s982, s983, s984, s985, s986, s987, s988, s989; + static volatile Object s990, s991, s992, s993, s994, s995, s996, s997, s998, s999; + + + volatile Object i0000, i0001, i0002, i0003, i0004, i0005, i0006, i0007, i0008, i0009; + volatile Object i0010, i0011, i0012, i0013, i0014, i0015, i0016, i0017, i0018, i0019; + volatile Object i0020, i0021, i0022, i0023, i0024, i0025, i0026, i0027, i0028, i0029; + volatile Object i0030, i0031, i0032, i0033, i0034, i0035, i0036, i0037, i0038, i0039; + volatile Object i0040, i0041, i0042, i0043, i0044, i0045, i0046, i0047, i0048, i0049; + volatile Object i0050, i0051, i0052, i0053, i0054, i0055, i0056, i0057, i0058, i0059; + volatile Object i0060, i0061, i0062, i0063, i0064, i0065, i0066, i0067, i0068, i0069; + volatile Object i0070, i0071, i0072, i0073, i0074, i0075, i0076, i0077, i0078, i0079; + volatile Object i0080, i0081, i0082, i0083, i0084, i0085, i0086, i0087, i0088, i0089; + volatile Object i0090, i0091, i0092, i0093, i0094, i0095, i0096, i0097, i0098, i0099; + + volatile Object i0100, i0101, i0102, i0103, i0104, i0105, i0106, i0107, i0108, i0109; + volatile Object i0110, i0111, i0112, i0113, i0114, i0115, i0116, i0117, i0118, i0119; + volatile Object i0120, i0121, i0122, i0123, i0124, i0125, i0126, i0127, i0128, i0129; + volatile Object i0130, i0131, i0132, i0133, i0134, i0135, i0136, i0137, i0138, i0139; + volatile Object i0140, i0141, i0142, i0143, i0144, i0145, i0146, i0147, i0148, i0149; + volatile Object i0150, i0151, i0152, i0153, i0154, i0155, i0156, i0157, i0158, i0159; + volatile Object i0160, i0161, i0162, i0163, i0164, i0165, i0166, i0167, i0168, i0169; + volatile Object i0170, i0171, i0172, i0173, i0174, i0175, i0176, i0177, i0178, i0179; + volatile Object i0180, i0181, i0182, i0183, i0184, i0185, i0186, i0187, i0188, i0189; + volatile Object i0190, i0191, i0192, i0193, i0194, i0195, i0196, i0197, i0198, i0199; + + volatile Object i0200, i0201, i0202, i0203, i0204, i0205, i0206, i0207, i0208, i0209; + volatile Object i0210, i0211, i0212, i0213, i0214, i0215, i0216, i0217, i0218, i0219; + volatile Object i0220, i0221, i0222, i0223, i0224, i0225, i0226, i0227, i0228, i0229; + volatile Object i0230, i0231, i0232, i0233, i0234, i0235, i0236, i0237, i0238, i0239; + volatile Object i0240, i0241, i0242, i0243, i0244, i0245, i0246, i0247, i0248, i0249; + volatile Object i0250, i0251, i0252, i0253, i0254, i0255, i0256, i0257, i0258, i0259; + volatile Object i0260, i0261, i0262, i0263, i0264, i0265, i0266, i0267, i0268, i0269; + volatile Object i0270, i0271, i0272, i0273, i0274, i0275, i0276, i0277, i0278, i0279; + volatile Object i0280, i0281, i0282, i0283, i0284, i0285, i0286, i0287, i0288, i0289; + volatile Object i0290, i0291, i0292, i0293, i0294, i0295, i0296, i0297, i0298, i0299; + + volatile Object i0300, i0301, i0302, i0303, i0304, i0305, i0306, i0307, i0308, i0309; + volatile Object i0310, i0311, i0312, i0313, i0314, i0315, i0316, i0317, i0318, i0319; + volatile Object i0320, i0321, i0322, i0323, i0324, i0325, i0326, i0327, i0328, i0329; + volatile Object i0330, i0331, i0332, i0333, i0334, i0335, i0336, i0337, i0338, i0339; + volatile Object i0340, i0341, i0342, i0343, i0344, i0345, i0346, i0347, i0348, i0349; + volatile Object i0350, i0351, i0352, i0353, i0354, i0355, i0356, i0357, i0358, i0359; + volatile Object i0360, i0361, i0362, i0363, i0364, i0365, i0366, i0367, i0368, i0369; + volatile Object i0370, i0371, i0372, i0373, i0374, i0375, i0376, i0377, i0378, i0379; + volatile Object i0380, i0381, i0382, i0383, i0384, i0385, i0386, i0387, i0388, i0389; + volatile Object i0390, i0391, i0392, i0393, i0394, i0395, i0396, i0397, i0398, i0399; + + volatile Object i0400, i0401, i0402, i0403, i0404, i0405, i0406, i0407, i0408, i0409; + volatile Object i0410, i0411, i0412, i0413, i0414, i0415, i0416, i0417, i0418, i0419; + volatile Object i0420, i0421, i0422, i0423, i0424, i0425, i0426, i0427, i0428, i0429; + volatile Object i0430, i0431, i0432, i0433, i0434, i0435, i0436, i0437, i0438, i0439; + volatile Object i0440, i0441, i0442, i0443, i0444, i0445, i0446, i0447, i0448, i0449; + volatile Object i0450, i0451, i0452, i0453, i0454, i0455, i0456, i0457, i0458, i0459; + volatile Object i0460, i0461, i0462, i0463, i0464, i0465, i0466, i0467, i0468, i0469; + volatile Object i0470, i0471, i0472, i0473, i0474, i0475, i0476, i0477, i0478, i0479; + volatile Object i0480, i0481, i0482, i0483, i0484, i0485, i0486, i0487, i0488, i0489; + volatile Object i0490, i0491, i0492, i0493, i0494, i0495, i0496, i0497, i0498, i0499; + + volatile Object i0500, i0501, i0502, i0503, i0504, i0505, i0506, i0507, i0508, i0509; + volatile Object i0510, i0511, i0512, i0513, i0514, i0515, i0516, i0517, i0518, i0519; + volatile Object i0520, i0521, i0522, i0523, i0524, i0525, i0526, i0527, i0528, i0529; + volatile Object i0530, i0531, i0532, i0533, i0534, i0535, i0536, i0537, i0538, i0539; + volatile Object i0540, i0541, i0542, i0543, i0544, i0545, i0546, i0547, i0548, i0549; + volatile Object i0550, i0551, i0552, i0553, i0554, i0555, i0556, i0557, i0558, i0559; + volatile Object i0560, i0561, i0562, i0563, i0564, i0565, i0566, i0567, i0568, i0569; + volatile Object i0570, i0571, i0572, i0573, i0574, i0575, i0576, i0577, i0578, i0579; + volatile Object i0580, i0581, i0582, i0583, i0584, i0585, i0586, i0587, i0588, i0589; + volatile Object i0590, i0591, i0592, i0593, i0594, i0595, i0596, i0597, i0598, i0599; + + volatile Object i0600, i0601, i0602, i0603, i0604, i0605, i0606, i0607, i0608, i0609; + volatile Object i0610, i0611, i0612, i0613, i0614, i0615, i0616, i0617, i0618, i0619; + volatile Object i0620, i0621, i0622, i0623, i0624, i0625, i0626, i0627, i0628, i0629; + volatile Object i0630, i0631, i0632, i0633, i0634, i0635, i0636, i0637, i0638, i0639; + volatile Object i0640, i0641, i0642, i0643, i0644, i0645, i0646, i0647, i0648, i0649; + volatile Object i0650, i0651, i0652, i0653, i0654, i0655, i0656, i0657, i0658, i0659; + volatile Object i0660, i0661, i0662, i0663, i0664, i0665, i0666, i0667, i0668, i0669; + volatile Object i0670, i0671, i0672, i0673, i0674, i0675, i0676, i0677, i0678, i0679; + volatile Object i0680, i0681, i0682, i0683, i0684, i0685, i0686, i0687, i0688, i0689; + volatile Object i0690, i0691, i0692, i0693, i0694, i0695, i0696, i0697, i0698, i0699; + + volatile Object i0700, i0701, i0702, i0703, i0704, i0705, i0706, i0707, i0708, i0709; + volatile Object i0710, i0711, i0712, i0713, i0714, i0715, i0716, i0717, i0718, i0719; + volatile Object i0720, i0721, i0722, i0723, i0724, i0725, i0726, i0727, i0728, i0729; + volatile Object i0730, i0731, i0732, i0733, i0734, i0735, i0736, i0737, i0738, i0739; + volatile Object i0740, i0741, i0742, i0743, i0744, i0745, i0746, i0747, i0748, i0749; + volatile Object i0750, i0751, i0752, i0753, i0754, i0755, i0756, i0757, i0758, i0759; + volatile Object i0760, i0761, i0762, i0763, i0764, i0765, i0766, i0767, i0768, i0769; + volatile Object i0770, i0771, i0772, i0773, i0774, i0775, i0776, i0777, i0778, i0779; + volatile Object i0780, i0781, i0782, i0783, i0784, i0785, i0786, i0787, i0788, i0789; + volatile Object i0790, i0791, i0792, i0793, i0794, i0795, i0796, i0797, i0798, i0799; + + volatile Object i0800, i0801, i0802, i0803, i0804, i0805, i0806, i0807, i0808, i0809; + volatile Object i0810, i0811, i0812, i0813, i0814, i0815, i0816, i0817, i0818, i0819; + volatile Object i0820, i0821, i0822, i0823, i0824, i0825, i0826, i0827, i0828, i0829; + volatile Object i0830, i0831, i0832, i0833, i0834, i0835, i0836, i0837, i0838, i0839; + volatile Object i0840, i0841, i0842, i0843, i0844, i0845, i0846, i0847, i0848, i0849; + volatile Object i0850, i0851, i0852, i0853, i0854, i0855, i0856, i0857, i0858, i0859; + volatile Object i0860, i0861, i0862, i0863, i0864, i0865, i0866, i0867, i0868, i0869; + volatile Object i0870, i0871, i0872, i0873, i0874, i0875, i0876, i0877, i0878, i0879; + volatile Object i0880, i0881, i0882, i0883, i0884, i0885, i0886, i0887, i0888, i0889; + volatile Object i0890, i0891, i0892, i0893, i0894, i0895, i0896, i0897, i0898, i0899; + + volatile Object i0900, i0901, i0902, i0903, i0904, i0905, i0906, i0907, i0908, i0909; + volatile Object i0910, i0911, i0912, i0913, i0914, i0915, i0916, i0917, i0918, i0919; + volatile Object i0920, i0921, i0922, i0923, i0924, i0925, i0926, i0927, i0928, i0929; + volatile Object i0930, i0931, i0932, i0933, i0934, i0935, i0936, i0937, i0938, i0939; + volatile Object i0940, i0941, i0942, i0943, i0944, i0945, i0946, i0947, i0948, i0949; + volatile Object i0950, i0951, i0952, i0953, i0954, i0955, i0956, i0957, i0958, i0959; + volatile Object i0960, i0961, i0962, i0963, i0964, i0965, i0966, i0967, i0968, i0969; + volatile Object i0970, i0971, i0972, i0973, i0974, i0975, i0976, i0977, i0978, i0979; + volatile Object i0980, i0981, i0982, i0983, i0984, i0985, i0986, i0987, i0988, i0989; + volatile Object i0990, i0991, i0992, i0993, i0994, i0995, i0996, i0997, i0998, i0999; + + volatile Object i1000, i1001, i1002, i1003, i1004, i1005, i1006, i1007, i1008, i1009; + volatile Object i1010, i1011, i1012, i1013, i1014, i1015, i1016, i1017, i1018, i1019; + volatile Object i1020, i1021, i1022, i1023, i1024, i1025, i1026, i1027, i1028, i1029; + volatile Object i1030, i1031, i1032, i1033, i1034, i1035, i1036, i1037, i1038, i1039; + volatile Object i1040, i1041, i1042, i1043, i1044, i1045, i1046, i1047, i1048, i1049; + volatile Object i1050, i1051, i1052, i1053, i1054, i1055, i1056, i1057, i1058, i1059; + volatile Object i1060, i1061, i1062, i1063, i1064, i1065, i1066, i1067, i1068, i1069; + volatile Object i1070, i1071, i1072, i1073, i1074, i1075, i1076, i1077, i1078, i1079; + volatile Object i1080, i1081, i1082, i1083, i1084, i1085, i1086, i1087, i1088, i1089; + volatile Object i1090, i1091, i1092, i1093, i1094, i1095, i1096, i1097, i1098, i1099; + + + // Note: ARM64, registers X16 and X17 are respectively IP0 and IP1, + // the scratch registers used by the VIXL AArch64 assembler (and to + // some extent, by ART's ARM64 code generator). + + /// CHECK-START-ARM64: void Main.testStaticVolatileFieldGetWithLargeOffset() disassembly (after) + /// CHECK: StaticFieldGet + /// CHECK: mov x17, #<<Offset:0x[0-9a-f]{4}>> + /// CHECK: add x16, {{x\d+}}, x17 + /// CHECK: ldar {{w\d+}}, [x16] + static void testStaticVolatileFieldGetWithLargeOffset() { + // The offset of this static field cannot be encoded as an immediate on ARM64. + Object s = s999; + } + + /// CHECK-START-ARM64: void Main.testInstanceVolatileFieldGetWithLargeOffset() disassembly (after) + /// CHECK: InstanceFieldGet + /// CHECK: mov x17, #<<Offset:0x[0-9a-f]{4}>> + /// CHECK: add x16, {{x\d+}}, x17 + /// CHECK: ldar {{w\d+}}, [x16] + void testInstanceVolatileFieldGetWithLargeOffset() { + // The offset of this instance field cannot be encoded as an immediate on ARM64. + Object i = i1029; + } + + + public static void main(String[] args) { + testStaticVolatileFieldGetWithLargeOffset(); + Main m = new Main(); + m.testInstanceVolatileFieldGetWithLargeOffset(); + System.out.println("passed"); + } + +} diff --git a/test/936-search-onload/build b/test/936-search-onload/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/936-search-onload/build @@ -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-build "$@" --experimental agents diff --git a/test/936-search-onload/expected.txt b/test/936-search-onload/expected.txt new file mode 100644 index 0000000000..2eec8e1aac --- /dev/null +++ b/test/936-search-onload/expected.txt @@ -0,0 +1,3 @@ +B was loaded with boot classloader +A was loaded with system classloader +Done diff --git a/test/936-search-onload/info.txt b/test/936-search-onload/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/936-search-onload/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/936-search-onload/run b/test/936-search-onload/run new file mode 100755 index 0000000000..67923a7984 --- /dev/null +++ b/test/936-search-onload/run @@ -0,0 +1,21 @@ +#!/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. + +# This test checks whether dex files can be injected into parent classloaders. App images preload +# classes, which will make the injection moot. Turn off app images to avoid the issue. + +./default-run "$@" --jvmti \ + --no-app-image diff --git a/test/936-search-onload/search_onload.cc b/test/936-search-onload/search_onload.cc new file mode 100644 index 0000000000..2286a467d3 --- /dev/null +++ b/test/936-search-onload/search_onload.cc @@ -0,0 +1,63 @@ +/* + * 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 "search_onload.h" + +#include <inttypes.h> + +#include "android-base/stringprintf.h" +#include "base/logging.h" +#include "base/macros.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" +#include "ScopedUtfChars.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test936SearchOnload { + +jint OnLoad(JavaVM* vm, + char* options ATTRIBUTE_UNUSED, + void* reserved ATTRIBUTE_UNUSED) { + if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { + printf("Unable to get jvmti env!\n"); + return 1; + } + SetAllCapabilities(jvmti_env); + + char* dex_loc = getenv("DEX_LOCATION"); + std::string dex1 = android::base::StringPrintf("%s/936-search-onload.jar", dex_loc); + std::string dex2 = android::base::StringPrintf("%s/936-search-onload-ex.jar", dex_loc); + + jvmtiError result = jvmti_env->AddToBootstrapClassLoaderSearch(dex1.c_str()); + if (result != JVMTI_ERROR_NONE) { + printf("Could not add to bootstrap classloader.\n"); + return 1; + } + + result = jvmti_env->AddToSystemClassLoaderSearch(dex2.c_str()); + if (result != JVMTI_ERROR_NONE) { + printf("Could not add to system classloader.\n"); + return 1; + } + + return JNI_OK; +} + +} // namespace Test936SearchOnload +} // namespace art diff --git a/test/936-search-onload/search_onload.h b/test/936-search-onload/search_onload.h new file mode 100644 index 0000000000..e55689252d --- /dev/null +++ b/test/936-search-onload/search_onload.h @@ -0,0 +1,30 @@ +/* + * 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. + */ + +#ifndef ART_TEST_936_SEARCH_ONLOAD_SEARCH_ONLOAD_H_ +#define ART_TEST_936_SEARCH_ONLOAD_SEARCH_ONLOAD_H_ + +#include <jni.h> + +namespace art { +namespace Test936SearchOnload { + +jint OnLoad(JavaVM* vm, char* options, void* reserved); + +} // namespace Test936SearchOnload +} // namespace art + +#endif // ART_TEST_936_SEARCH_ONLOAD_SEARCH_ONLOAD_H_ diff --git a/test/936-search-onload/src-ex/A.java b/test/936-search-onload/src-ex/A.java new file mode 100644 index 0000000000..64acb2fcfe --- /dev/null +++ b/test/936-search-onload/src-ex/A.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +public class A { +}
\ No newline at end of file diff --git a/test/936-search-onload/src/B.java b/test/936-search-onload/src/B.java new file mode 100644 index 0000000000..f1458c3bca --- /dev/null +++ b/test/936-search-onload/src/B.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +public class B { +}
\ No newline at end of file diff --git a/test/936-search-onload/src/Main.java b/test/936-search-onload/src/Main.java new file mode 100644 index 0000000000..2e7a87193b --- /dev/null +++ b/test/936-search-onload/src/Main.java @@ -0,0 +1,47 @@ +/* + * 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.Arrays; + +public class Main { + public static void main(String[] args) throws Exception { + doTest(); + } + + private static void doTest() throws Exception { + doTest(true, "B"); + doTest(false, "A"); + System.out.println("Done"); + } + + private static void doTest(boolean boot, String className) throws Exception { + ClassLoader expectedClassLoader; + if (boot) { + expectedClassLoader = Object.class.getClassLoader(); + } else { + expectedClassLoader = ClassLoader.getSystemClassLoader(); + } + + Class<?> c = Class.forName(className, false, ClassLoader.getSystemClassLoader()); + if (c.getClassLoader() != expectedClassLoader) { + throw new RuntimeException(className + "(" + boot + "): " + + c.getClassLoader() + " vs " + expectedClassLoader); + } else { + System.out.println(className + " was loaded with " + (boot ? "boot" : "system") + + " classloader"); + } + } +} diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java index f8daba6239..801904d9c9 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -15,6 +15,7 @@ */ import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandleInfo; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; @@ -77,6 +78,7 @@ public class Main { testReturnValueConversions(); testVariableArity(); testVariableArity_MethodHandles_bind(); + testRevealDirect(); } public static void testfindSpecial_invokeSuperBehaviour() throws Throwable { @@ -384,6 +386,10 @@ public class Main { public String publicMethod() { return "publicMethod"; } + + public String publicVarArgsMethod(String... args) { + return "publicVarArgsMethod"; + } } public static void testUnreflects() throws Throwable { @@ -1486,4 +1492,117 @@ public class Main { fail(); } catch (WrongMethodTypeException e) {} } + + public static void testRevealDirect() throws Throwable { + // Test with a virtual method : + MethodType type = MethodType.methodType(String.class); + MethodHandle handle = MethodHandles.lookup().findVirtual( + UnreflectTester.class, "publicMethod", type); + + // Comparisons with an equivalent member obtained via reflection : + MethodHandleInfo info = MethodHandles.lookup().revealDirect(handle); + Method meth = UnreflectTester.class.getMethod("publicMethod"); + + assertEquals(MethodHandleInfo.REF_invokeVirtual, info.getReferenceKind()); + assertEquals("publicMethod", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(meth, info.reflectAs(Method.class, MethodHandles.lookup())); + assertEquals(type, info.getMethodType()); + + // Resolution via a public lookup should fail because the method in question + // isn't public. + try { + info.reflectAs(Method.class, MethodHandles.publicLookup()); + fail(); + } catch (IllegalArgumentException expected) { + } + + // Test with a static method : + handle = MethodHandles.lookup().findStatic(UnreflectTester.class, + "publicStaticMethod", + MethodType.methodType(String.class)); + + info = MethodHandles.lookup().revealDirect(handle); + meth = UnreflectTester.class.getMethod("publicStaticMethod"); + assertEquals(MethodHandleInfo.REF_invokeStatic, info.getReferenceKind()); + assertEquals("publicStaticMethod", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(meth, info.reflectAs(Method.class, MethodHandles.lookup())); + assertEquals(type, info.getMethodType()); + + // Test with a var-args method : + type = MethodType.methodType(String.class, String[].class); + handle = MethodHandles.lookup().findVirtual(UnreflectTester.class, + "publicVarArgsMethod", type); + + info = MethodHandles.lookup().revealDirect(handle); + meth = UnreflectTester.class.getMethod("publicVarArgsMethod", String[].class); + assertEquals(MethodHandleInfo.REF_invokeVirtual, info.getReferenceKind()); + assertEquals("publicVarArgsMethod", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertTrue(info.isVarArgs()); + assertEquals(meth, info.reflectAs(Method.class, MethodHandles.lookup())); + assertEquals(type, info.getMethodType()); + + // Test with a constructor : + Constructor cons = UnreflectTester.class.getConstructor(String.class, boolean.class); + type = MethodType.methodType(void.class, String.class, boolean.class); + handle = MethodHandles.lookup().findConstructor(UnreflectTester.class, type); + + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_newInvokeSpecial, info.getReferenceKind()); + assertEquals("<init>", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(cons, info.reflectAs(Constructor.class, MethodHandles.lookup())); + assertEquals(type, info.getMethodType()); + + // Test with a static field : + Field field = UnreflectTester.class.getField("publicStaticField"); + + handle = MethodHandles.lookup().findStaticSetter( + UnreflectTester.class, "publicStaticField", String.class); + + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_putStatic, info.getReferenceKind()); + assertEquals("publicStaticField", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup())); + assertEquals(MethodType.methodType(void.class, String.class), info.getMethodType()); + + // Test with a setter on the same field, the type of the handle should change + // but everything else must remain the same. + handle = MethodHandles.lookup().findStaticGetter( + UnreflectTester.class, "publicStaticField", String.class); + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_getStatic, info.getReferenceKind()); + assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup())); + assertEquals(MethodType.methodType(String.class), info.getMethodType()); + + // Test with an instance field : + field = UnreflectTester.class.getField("publicField"); + + handle = MethodHandles.lookup().findSetter( + UnreflectTester.class, "publicField", String.class); + + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_putField, info.getReferenceKind()); + assertEquals("publicField", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup())); + assertEquals(MethodType.methodType(void.class, String.class), info.getMethodType()); + + // Test with a setter on the same field, the type of the handle should change + // but everything else must remain the same. + handle = MethodHandles.lookup().findGetter( + UnreflectTester.class, "publicField", String.class); + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_getField, info.getReferenceKind()); + assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup())); + assertEquals(MethodType.methodType(String.class), info.getMethodType()); + } } diff --git a/test/Android.bp b/test/Android.bp index 287df1375e..1070645040 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -272,6 +272,7 @@ art_cc_defaults { "929-search/search.cc", "931-agent-thread/agent_thread.cc", "933-misc-events/misc_events.cc", + "936-search-onload/search_onload.cc", ], shared_libs: [ "libbase", diff --git a/test/run-test b/test/run-test index a228789701..847fcf7eb6 100755 --- a/test/run-test +++ b/test/run-test @@ -156,6 +156,7 @@ while true; do shift elif [ "x$1" = "x--jvm" ]; then target_mode="no" + DEX_LOCATION="$tmp_dir" runtime="jvm" image_args="" prebuild_mode="no" diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index c30c2b1118..621d45a1bc 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -28,6 +28,7 @@ #include "901-hello-ti-agent/basics.h" #include "909-attach-agent/attach.h" +#include "936-search-onload/search_onload.h" namespace art { @@ -112,6 +113,7 @@ static AgentLib agents[] = { { "932-transform-saves", common_retransform::OnLoad, nullptr }, { "934-load-transform", common_retransform::OnLoad, nullptr }, { "935-non-retransformable", common_transform::OnLoad, nullptr }, + { "936-search-onload", Test936SearchOnload::OnLoad, nullptr }, { "937-hello-retransform-package", common_retransform::OnLoad, nullptr }, }; |