diff options
53 files changed, 1747 insertions, 394 deletions
diff --git a/benchmark/jobject-benchmark/jobject_benchmark.cc b/benchmark/jobject-benchmark/jobject_benchmark.cc index de43f73800..7e0a5362c9 100644 --- a/benchmark/jobject-benchmark/jobject_benchmark.cc +++ b/benchmark/jobject-benchmark/jobject_benchmark.cc @@ -29,7 +29,7 @@ extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeAddRemoveLocal( ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj); CHECK(obj != nullptr); for (jint i = 0; i < reps; ++i) { - jobject ref = soa.Env()->AddLocalReference<jobject>(obj.Ptr()); + jobject ref = soa.Env()->AddLocalReference<jobject>(obj); soa.Env()->DeleteLocalRef(ref); } } @@ -39,7 +39,7 @@ extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeDecodeLocal( ScopedObjectAccess soa(env); ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj); CHECK(obj != nullptr); - jobject ref = soa.Env()->AddLocalReference<jobject>(obj.Ptr()); + jobject ref = soa.Env()->AddLocalReference<jobject>(obj); for (jint i = 0; i < reps; ++i) { CHECK_EQ(soa.Decode<mirror::Object>(ref), obj); } diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index dca290c561..6b56fe0b7f 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -392,11 +392,13 @@ jobject JniCompilerTest::class_loader_; // -- TODO: We can support (1) if we remove the mutator lock assert during stub lookup. # define JNI_TEST_NORMAL_ONLY(TestName) \ TEST_F(JniCompilerTest, TestName ## NormalCompiler) { \ + ScopedCheckHandleScope top_handle_scope_check; \ SCOPED_TRACE("Normal JNI with compiler"); \ gCurrentJni = static_cast<uint32_t>(JniKind::kNormal); \ TestName ## Impl(); \ } \ TEST_F(JniCompilerTest, TestName ## NormalGeneric) { \ + ScopedCheckHandleScope top_handle_scope_check; \ SCOPED_TRACE("Normal JNI with generic"); \ gCurrentJni = static_cast<uint32_t>(JniKind::kNormal); \ TEST_DISABLED_FOR_MIPS(); \ @@ -408,12 +410,14 @@ jobject JniCompilerTest::class_loader_; #define JNI_TEST(TestName) \ JNI_TEST_NORMAL_ONLY(TestName) \ TEST_F(JniCompilerTest, TestName ## FastCompiler) { \ + ScopedCheckHandleScope top_handle_scope_check; \ SCOPED_TRACE("@FastNative JNI with compiler"); \ gCurrentJni = static_cast<uint32_t>(JniKind::kFast); \ TestName ## Impl(); \ } \ \ TEST_F(JniCompilerTest, TestName ## FastGeneric) { \ + ScopedCheckHandleScope top_handle_scope_check; \ SCOPED_TRACE("@FastNative JNI with generic"); \ gCurrentJni = static_cast<uint32_t>(JniKind::kFast); \ TEST_DISABLED_FOR_MIPS(); \ @@ -424,11 +428,13 @@ jobject JniCompilerTest::class_loader_; // Test (@CriticalNative) x (compiler, generic) only. #define JNI_TEST_CRITICAL_ONLY(TestName) \ TEST_F(JniCompilerTest, TestName ## CriticalCompiler) { \ + ScopedCheckHandleScope top_handle_scope_check; \ SCOPED_TRACE("@CriticalNative JNI with compiler"); \ gCurrentJni = static_cast<uint32_t>(JniKind::kCritical); \ TestName ## Impl(); \ } \ TEST_F(JniCompilerTest, TestName ## CriticalGeneric) { \ + ScopedCheckHandleScope top_handle_scope_check; \ SCOPED_TRACE("@CriticalNative JNI with generic"); \ gCurrentJni = static_cast<uint32_t>(JniKind::kCritical); \ SetCheckGenericJni(true); \ @@ -515,6 +521,21 @@ struct ScopedDisableCheckNumStackReferences { bool ScopedDisableCheckNumStackReferences::sCheckNumStackReferences = true; +// Check that the handle scope at the start of this block is the same as the handle scope at the end of the block. +struct ScopedCheckHandleScope { + ScopedCheckHandleScope() { + handle_scope_ = Thread::Current()->GetTopHandleScope(); + } + + ~ScopedCheckHandleScope() { + EXPECT_EQ(handle_scope_, Thread::Current()->GetTopHandleScope()) + << "Top-most handle scope must be the same after all the JNI " + << "invocations have finished (as before they were invoked)."; + } + + HandleScope* handle_scope_; +}; + static void expectNumStackReferences(size_t val1, size_t val2) { // In rare cases when JNI functions call themselves recursively, // disable this test because it will have a false negative. diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index 0987df76dc..b9266f75bf 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -21,6 +21,7 @@ art_cc_binary { "dex_ir.cc", "dex_ir_builder.cc", "dex_visualize.cc", + "dex_writer.cc", ], cflags: ["-Wall"], shared_libs: [ diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index aff03cd6ea..bc909c3b56 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -55,6 +55,54 @@ static void GetLocalsCb(void* context, const DexFile::LocalInfo& entry) { entry.end_address_, entry.reg_))); } +static uint32_t GetDebugInfoStreamSize(const uint8_t* debug_info_stream) { + const uint8_t* stream = debug_info_stream; + DecodeUnsignedLeb128(&stream); // line_start + uint32_t parameters_size = DecodeUnsignedLeb128(&stream); + for (uint32_t i = 0; i < parameters_size; ++i) { + DecodeUnsignedLeb128P1(&stream); // Parameter name. + } + + for (;;) { + uint8_t opcode = *stream++; + switch (opcode) { + case DexFile::DBG_END_SEQUENCE: + return stream - debug_info_stream; // end of stream. + case DexFile::DBG_ADVANCE_PC: + DecodeUnsignedLeb128(&stream); // addr_diff + break; + case DexFile::DBG_ADVANCE_LINE: + DecodeSignedLeb128(&stream); // line_diff + break; + case DexFile::DBG_START_LOCAL: + DecodeUnsignedLeb128(&stream); // register_num + DecodeUnsignedLeb128P1(&stream); // name_idx + DecodeUnsignedLeb128P1(&stream); // type_idx + break; + case DexFile::DBG_START_LOCAL_EXTENDED: + DecodeUnsignedLeb128(&stream); // register_num + DecodeUnsignedLeb128P1(&stream); // name_idx + DecodeUnsignedLeb128P1(&stream); // type_idx + DecodeUnsignedLeb128P1(&stream); // sig_idx + break; + case DexFile::DBG_END_LOCAL: + case DexFile::DBG_RESTART_LOCAL: + DecodeUnsignedLeb128(&stream); // register_num + break; + case DexFile::DBG_SET_PROLOGUE_END: + case DexFile::DBG_SET_EPILOGUE_BEGIN: + break; + case DexFile::DBG_SET_FILE: { + DecodeUnsignedLeb128P1(&stream); // name_idx + break; + } + default: { + break; + } + } + } +} + EncodedValue* Collections::ReadEncodedValue(const uint8_t** data) { const uint8_t encoded_value = *(*data)++; const uint8_t type = encoded_value & 0x1f; @@ -179,7 +227,7 @@ void Collections::CreateTypeId(const DexFile& dex_file, uint32_t i) { void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) { const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(i); const DexFile::TypeList* type_list = dex_file.GetProtoParameters(disk_proto_id); - TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_, true); + TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_); ProtoId* proto_id = new ProtoId(GetStringId(disk_proto_id.shorty_idx_), GetTypeId(disk_proto_id.return_type_idx_), @@ -210,7 +258,7 @@ void Collections::CreateClassDef(const DexFile& dex_file, uint32_t i) { const TypeId* superclass = GetTypeIdOrNullPtr(disk_class_def.superclass_idx_); const DexFile::TypeList* type_list = dex_file.GetInterfacesList(disk_class_def); - TypeList* interfaces_type_list = CreateTypeList(type_list, disk_class_def.interfaces_off_, false); + TypeList* interfaces_type_list = CreateTypeList(type_list, disk_class_def.interfaces_off_); const StringId* source_file = GetStringIdOrNullPtr(disk_class_def.source_file_idx_); // Annotations. @@ -232,9 +280,8 @@ void Collections::CreateClassDef(const DexFile& dex_file, uint32_t i) { class_defs_.AddIndexedItem(class_def, ClassDefsOffset() + i * ClassDef::ItemSize(), i); } -TypeList* Collections::CreateTypeList( - const DexFile::TypeList* dex_type_list, uint32_t offset, bool allow_empty) { - if (dex_type_list == nullptr && !allow_empty) { +TypeList* Collections::CreateTypeList(const DexFile::TypeList* dex_type_list, uint32_t offset) { + if (dex_type_list == nullptr) { return nullptr; } // TODO: Create more efficient lookup for existing type lists. @@ -244,7 +291,7 @@ TypeList* Collections::CreateTypeList( } } TypeIdVector* type_vector = new TypeIdVector(); - uint32_t size = dex_type_list == nullptr ? 0 : dex_type_list->Size(); + uint32_t size = dex_type_list->Size(); for (uint32_t index = 0; index < size; ++index) { type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_)); } @@ -257,6 +304,11 @@ EncodedArrayItem* Collections::CreateEncodedArrayItem(const uint8_t* static_data if (static_data == nullptr) { return nullptr; } + for (std::unique_ptr<EncodedArrayItem>& existing_array_item : EncodedArrayItems()) { + if (existing_array_item->GetOffset() == offset) { + return existing_array_item.get(); + } + } uint32_t size = DecodeUnsignedLeb128(&static_data); EncodedValueVector* values = new EncodedValueVector(); for (uint32_t i = 0; i < size; ++i) { @@ -270,6 +322,11 @@ EncodedArrayItem* Collections::CreateEncodedArrayItem(const uint8_t* static_data AnnotationItem* Collections::CreateAnnotationItem(const DexFile::AnnotationItem* annotation, uint32_t offset) { + for (std::unique_ptr<AnnotationItem>& existing_annotation_item : AnnotationItems()) { + if (existing_annotation_item->GetOffset() == offset) { + return existing_annotation_item.get(); + } + } uint8_t visibility = annotation->visibility_; const uint8_t* annotation_data = annotation->annotation_; EncodedValue* encoded_value = @@ -284,9 +341,14 @@ AnnotationItem* Collections::CreateAnnotationItem(const DexFile::AnnotationItem* AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file, const DexFile::AnnotationSetItem& disk_annotations_item, uint32_t offset) { - if (disk_annotations_item.size_ == 0) { + if (disk_annotations_item.size_ == 0 && offset == 0) { return nullptr; } + for (std::unique_ptr<AnnotationSetItem>& existing_anno_set_item : AnnotationSetItems()) { + if (existing_anno_set_item->GetOffset() == offset) { + return existing_anno_set_item.get(); + } + } std::vector<AnnotationItem*>* items = new std::vector<AnnotationItem*>(); for (uint32_t i = 0; i < disk_annotations_item.size_; ++i) { const DexFile::AnnotationItem* annotation = @@ -305,6 +367,11 @@ AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file, AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexFile& dex_file, const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset) { + for (std::unique_ptr<AnnotationsDirectoryItem>& anno_dir_item : AnnotationsDirectoryItems()) { + if (anno_dir_item->GetOffset() == offset) { + return anno_dir_item.get(); + } + } const DexFile::AnnotationSetItem* class_set_item = dex_file.GetClassAnnotationSet(disk_annotations_item); AnnotationSetItem* class_annotation = nullptr; @@ -367,16 +434,25 @@ AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexF ParameterAnnotation* Collections::GenerateParameterAnnotation( const DexFile& dex_file, MethodId* method_id, const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset) { - std::vector<AnnotationSetItem*>* annotations = new std::vector<AnnotationSetItem*>(); - for (uint32_t i = 0; i < annotation_set_ref_list->size_; ++i) { - const DexFile::AnnotationSetItem* annotation_set_item = - dex_file.GetSetRefItemItem(&annotation_set_ref_list->list_[i]); - uint32_t set_offset = annotation_set_ref_list->list_[i].annotations_off_; - annotations->push_back(CreateAnnotationSetItem(dex_file, *annotation_set_item, set_offset)); + AnnotationSetRefList* set_ref_list = nullptr; + for (std::unique_ptr<AnnotationSetRefList>& existing_set_ref_list : AnnotationSetRefLists()) { + if (existing_set_ref_list->GetOffset() == offset) { + set_ref_list = existing_set_ref_list.get(); + break; + } } - AnnotationSetRefList* new_ref_list = new AnnotationSetRefList(annotations); - annotation_set_ref_lists_.AddItem(new_ref_list, offset); - return new ParameterAnnotation(method_id, new_ref_list); + if (set_ref_list == nullptr) { + std::vector<AnnotationSetItem*>* annotations = new std::vector<AnnotationSetItem*>(); + for (uint32_t i = 0; i < annotation_set_ref_list->size_; ++i) { + const DexFile::AnnotationSetItem* annotation_set_item = + dex_file.GetSetRefItemItem(&annotation_set_ref_list->list_[i]); + uint32_t set_offset = annotation_set_ref_list->list_[i].annotations_off_; + annotations->push_back(CreateAnnotationSetItem(dex_file, *annotation_set_item, set_offset)); + } + set_ref_list = new AnnotationSetRefList(annotations); + annotation_set_ref_lists_.AddItem(set_ref_list, offset); + } + return new ParameterAnnotation(method_id, set_ref_list); } CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, @@ -390,7 +466,10 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(&disk_code_item); DebugInfoItem* debug_info = nullptr; if (debug_info_stream != nullptr) { - debug_info = new DebugInfoItem(); + uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream); + uint8_t* debug_info_buffer = new uint8_t[debug_info_size]; + memcpy(debug_info_buffer, debug_info_stream, debug_info_size); + debug_info = new DebugInfoItem(debug_info_size, debug_info_buffer); debug_info_items_.AddItem(debug_info, disk_code_item.debug_info_off_); } @@ -399,26 +478,41 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, memcpy(insns, disk_code_item.insns_, insns_size * sizeof(uint16_t)); TryItemVector* tries = nullptr; + CatchHandlerVector* handler_list = nullptr; if (tries_size > 0) { tries = new TryItemVector(); + handler_list = new CatchHandlerVector(); for (uint32_t i = 0; i < tries_size; ++i) { const DexFile::TryItem* disk_try_item = dex_file.GetTryItems(disk_code_item, i); uint32_t start_addr = disk_try_item->start_addr_; uint16_t insn_count = disk_try_item->insn_count_; - CatchHandlerVector* handlers = new CatchHandlerVector(); - for (CatchHandlerIterator it(disk_code_item, *disk_try_item); it.HasNext(); it.Next()) { - const uint16_t type_index = it.GetHandlerTypeIndex(); - const TypeId* type_id = GetTypeIdOrNullPtr(type_index); - handlers->push_back(std::unique_ptr<const CatchHandler>( - new CatchHandler(type_id, it.GetHandlerAddress()))); + uint16_t handler_off = disk_try_item->handler_off_; + const CatchHandler* handlers = nullptr; + for (std::unique_ptr<const CatchHandler>& existing_handlers : *handler_list) { + if (handler_off == existing_handlers->GetListOffset()) { + handlers = existing_handlers.get(); + } + } + if (handlers == nullptr) { + bool catch_all = false; + TypeAddrPairVector* addr_pairs = new TypeAddrPairVector(); + for (CatchHandlerIterator it(disk_code_item, *disk_try_item); it.HasNext(); it.Next()) { + const uint16_t type_index = it.GetHandlerTypeIndex(); + const TypeId* type_id = GetTypeIdOrNullPtr(type_index); + catch_all |= type_id == nullptr; + addr_pairs->push_back(std::unique_ptr<const TypeAddrPair>( + new TypeAddrPair(type_id, it.GetHandlerAddress()))); + } + handlers = new CatchHandler(catch_all, handler_off, addr_pairs); + handler_list->push_back(std::unique_ptr<const CatchHandler>(handlers)); } TryItem* try_item = new TryItem(start_addr, insn_count, handlers); tries->push_back(std::unique_ptr<const TryItem>(try_item)); } } // TODO: Calculate the size of the code item. - CodeItem* code_item = - new CodeItem(registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries); + CodeItem* code_item = new CodeItem( + registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries, handler_list); code_items_.AddItem(code_item, offset); return code_item; } diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index f3d2c900e3..5e686d3c00 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -24,6 +24,7 @@ #include "dex_file-inl.h" #include "leb128.h" +#include "utf.h" namespace art { namespace dex_ir { @@ -137,10 +138,22 @@ class Collections { std::vector<std::unique_ptr<FieldId>>& FieldIds() { return field_ids_.Collection(); } std::vector<std::unique_ptr<MethodId>>& MethodIds() { return method_ids_.Collection(); } std::vector<std::unique_ptr<ClassDef>>& ClassDefs() { return class_defs_.Collection(); } - + std::vector<std::unique_ptr<StringData>>& StringDatas() { return string_datas_.Collection(); } std::vector<std::unique_ptr<TypeList>>& TypeLists() { return type_lists_.Collection(); } std::vector<std::unique_ptr<EncodedArrayItem>>& EncodedArrayItems() { return encoded_array_items_.Collection(); } + std::vector<std::unique_ptr<AnnotationItem>>& AnnotationItems() + { return annotation_items_.Collection(); } + std::vector<std::unique_ptr<AnnotationSetItem>>& AnnotationSetItems() + { return annotation_set_items_.Collection(); } + std::vector<std::unique_ptr<AnnotationSetRefList>>& AnnotationSetRefLists() + { return annotation_set_ref_lists_.Collection(); } + std::vector<std::unique_ptr<AnnotationsDirectoryItem>>& AnnotationsDirectoryItems() + { return annotations_directory_items_.Collection(); } + std::vector<std::unique_ptr<DebugInfoItem>>& DebugInfoItems() + { return debug_info_items_.Collection(); } + std::vector<std::unique_ptr<CodeItem>>& CodeItems() { return code_items_.Collection(); } + std::vector<std::unique_ptr<ClassData>>& ClassDatas() { return class_datas_.Collection(); } void CreateStringId(const DexFile& dex_file, uint32_t i); void CreateTypeId(const DexFile& dex_file, uint32_t i); @@ -149,7 +162,7 @@ class Collections { void CreateMethodId(const DexFile& dex_file, uint32_t i); void CreateClassDef(const DexFile& dex_file, uint32_t i); - TypeList* CreateTypeList(const DexFile::TypeList* type_list, uint32_t offset, bool allow_empty); + TypeList* CreateTypeList(const DexFile::TypeList* type_list, uint32_t offset); EncodedArrayItem* CreateEncodedArrayItem(const uint8_t* static_data, uint32_t offset); AnnotationItem* CreateAnnotationItem(const DexFile::AnnotationItem* annotation, uint32_t offset); AnnotationSetItem* CreateAnnotationSetItem(const DexFile& dex_file, @@ -182,14 +195,16 @@ class Collections { uint32_t ClassDefsOffset() const { return class_defs_.GetOffset(); } uint32_t StringDatasOffset() const { return string_datas_.GetOffset(); } uint32_t TypeListsOffset() const { return type_lists_.GetOffset(); } - uint32_t EncodedArrayOffset() const { return encoded_array_items_.GetOffset(); } - uint32_t AnnotationOffset() const { return annotation_items_.GetOffset(); } - uint32_t AnnotationSetOffset() const { return annotation_set_items_.GetOffset(); } + uint32_t EncodedArrayItemsOffset() const { return encoded_array_items_.GetOffset(); } + uint32_t AnnotationItemsOffset() const { return annotation_items_.GetOffset(); } + uint32_t AnnotationSetItemsOffset() const { return annotation_set_items_.GetOffset(); } uint32_t AnnotationSetRefListsOffset() const { return annotation_set_ref_lists_.GetOffset(); } - uint32_t AnnotationsDirectoryOffset() const { return annotations_directory_items_.GetOffset(); } - uint32_t DebugInfoOffset() const { return debug_info_items_.GetOffset(); } + uint32_t AnnotationsDirectoryItemsOffset() const + { return annotations_directory_items_.GetOffset(); } + uint32_t DebugInfoItemsOffset() const { return debug_info_items_.GetOffset(); } uint32_t CodeItemsOffset() const { return code_items_.GetOffset(); } uint32_t ClassDatasOffset() const { return class_datas_.GetOffset(); } + uint32_t MapItemOffset() const { return map_item_offset_; } void SetStringIdsOffset(uint32_t new_offset) { string_ids_.SetOffset(new_offset); } void SetTypeIdsOffset(uint32_t new_offset) { type_ids_.SetOffset(new_offset); } @@ -199,16 +214,19 @@ class Collections { void SetClassDefsOffset(uint32_t new_offset) { class_defs_.SetOffset(new_offset); } void SetStringDatasOffset(uint32_t new_offset) { string_datas_.SetOffset(new_offset); } void SetTypeListsOffset(uint32_t new_offset) { type_lists_.SetOffset(new_offset); } - void SetEncodedArrayOffset(uint32_t new_offset) { encoded_array_items_.SetOffset(new_offset); } - void SetAnnotationOffset(uint32_t new_offset) { annotation_items_.SetOffset(new_offset); } - void SetAnnotationSetOffset(uint32_t new_offset) { annotation_set_items_.SetOffset(new_offset); } + void SetEncodedArrayItemsOffset(uint32_t new_offset) + { encoded_array_items_.SetOffset(new_offset); } + void SetAnnotationItemsOffset(uint32_t new_offset) { annotation_items_.SetOffset(new_offset); } + void SetAnnotationSetItemsOffset(uint32_t new_offset) + { annotation_set_items_.SetOffset(new_offset); } void SetAnnotationSetRefListsOffset(uint32_t new_offset) { annotation_set_ref_lists_.SetOffset(new_offset); } - void SetAnnotationsDirectoryOffset(uint32_t new_offset) + void SetAnnotationsDirectoryItemsOffset(uint32_t new_offset) { annotations_directory_items_.SetOffset(new_offset); } - void SetDebugInfoOffset(uint32_t new_offset) { debug_info_items_.SetOffset(new_offset); } + void SetDebugInfoItemsOffset(uint32_t new_offset) { debug_info_items_.SetOffset(new_offset); } void SetCodeItemsOffset(uint32_t new_offset) { code_items_.SetOffset(new_offset); } void SetClassDatasOffset(uint32_t new_offset) { class_datas_.SetOffset(new_offset); } + void SetMapItemOffset(uint32_t new_offset) { map_item_offset_ = new_offset; } uint32_t StringIdsSize() const { return string_ids_.Size(); } uint32_t TypeIdsSize() const { return type_ids_.Size(); } @@ -216,15 +234,14 @@ class Collections { uint32_t FieldIdsSize() const { return field_ids_.Size(); } uint32_t MethodIdsSize() const { return method_ids_.Size(); } uint32_t ClassDefsSize() const { return class_defs_.Size(); } - uint32_t StringDatasSize() const { return string_datas_.Size(); } uint32_t TypeListsSize() const { return type_lists_.Size(); } - uint32_t EncodedArraySize() const { return encoded_array_items_.Size(); } - uint32_t AnnotationSize() const { return annotation_items_.Size(); } - uint32_t AnnotationSetSize() const { return annotation_set_items_.Size(); } + uint32_t EncodedArrayItemsSize() const { return encoded_array_items_.Size(); } + uint32_t AnnotationItemsSize() const { return annotation_items_.Size(); } + uint32_t AnnotationSetItemsSize() const { return annotation_set_items_.Size(); } uint32_t AnnotationSetRefListsSize() const { return annotation_set_ref_lists_.Size(); } - uint32_t AnnotationsDirectorySize() const { return annotations_directory_items_.Size(); } - uint32_t DebugInfoSize() const { return debug_info_items_.Size(); } + uint32_t AnnotationsDirectoryItemsSize() const { return annotations_directory_items_.Size(); } + uint32_t DebugInfoItemsSize() const { return debug_info_items_.Size(); } uint32_t CodeItemsSize() const { return code_items_.Size(); } uint32_t ClassDatasSize() const { return class_datas_.Size(); } @@ -255,6 +272,8 @@ class Collections { CollectionWithOffset<CodeItem> code_items_; CollectionWithOffset<ClassData> class_datas_; + uint32_t map_item_offset_ = 0; + DISALLOW_COPY_AND_ASSIGN(Collections); }; @@ -364,7 +383,7 @@ class Header : public Item { class StringData : public Item { public: explicit StringData(const char* data) : data_(strdup(data)) { - size_ = UnsignedLeb128Size(strlen(data)) + strlen(data); + size_ = UnsignedLeb128Size(CountModifiedUtf8Chars(data)) + strlen(data); } const char* Data() const { return data_.get(); } @@ -372,7 +391,7 @@ class StringData : public Item { void Accept(AbstractDispatcher* dispatch) const { dispatch->Dispatch(this); } private: - std::unique_ptr<const char> data_; + UniqueCPtr<const char> data_; DISALLOW_COPY_AND_ASSIGN(StringData); }; @@ -442,14 +461,14 @@ class ProtoId : public IndexedItem { const StringId* Shorty() const { return shorty_; } const TypeId* ReturnType() const { return return_type_; } - const TypeIdVector& Parameters() const { return *parameters_->GetTypeList(); } + const TypeList* Parameters() const { return parameters_; } void Accept(AbstractDispatcher* dispatch) const { dispatch->Dispatch(this); } private: const StringId* shorty_; const TypeId* return_type_; - TypeList* parameters_; + TypeList* parameters_; // This can be nullptr. DISALLOW_COPY_AND_ASSIGN(ProtoId); }; @@ -533,7 +552,7 @@ class MethodItem : public Item { private: uint32_t access_flags_; const MethodId* method_id_; - const CodeItem* code_; + const CodeItem* code_; // This can be nullptr. DISALLOW_COPY_AND_ASSIGN(MethodItem); }; @@ -691,8 +710,8 @@ class ClassDef : public IndexedItem { interfaces_(interfaces), source_file_(source_file), annotations_(annotations), - static_values_(static_values), - class_data_(class_data) { size_ = kClassDefItemSize; } + class_data_(class_data), + static_values_(static_values) { size_ = kClassDefItemSize; } ~ClassDef() OVERRIDE { } @@ -706,8 +725,8 @@ class ClassDef : public IndexedItem { uint32_t InterfacesOffset() { return interfaces_ == nullptr ? 0 : interfaces_->GetOffset(); } const StringId* SourceFile() const { return source_file_; } AnnotationsDirectoryItem* Annotations() const { return annotations_; } - EncodedArrayItem* StaticValues() { return static_values_; } ClassData* GetClassData() { return class_data_; } + EncodedArrayItem* StaticValues() { return static_values_; } MethodItem* GenerateMethodItem(Header& header, ClassDataItemIterator& cdii); @@ -716,19 +735,19 @@ class ClassDef : public IndexedItem { private: const TypeId* class_type_; uint32_t access_flags_; - const TypeId* superclass_; - TypeList* interfaces_; - const StringId* source_file_; - AnnotationsDirectoryItem* annotations_; - EncodedArrayItem* static_values_; - ClassData* class_data_; + const TypeId* superclass_; // This can be nullptr. + TypeList* interfaces_; // This can be nullptr. + const StringId* source_file_; // This can be nullptr. + AnnotationsDirectoryItem* annotations_; // This can be nullptr. + ClassData* class_data_; // This can be nullptr. + EncodedArrayItem* static_values_; // This can be nullptr. DISALLOW_COPY_AND_ASSIGN(ClassDef); }; -class CatchHandler { +class TypeAddrPair { public: - CatchHandler(const TypeId* type_id, uint32_t address) : type_id_(type_id), address_(address) { } + TypeAddrPair(const TypeId* type_id, uint32_t address) : type_id_(type_id), address_(address) { } const TypeId* GetTypeId() const { return type_id_; } uint32_t GetAddress() const { return address_; } @@ -737,6 +756,25 @@ class CatchHandler { const TypeId* type_id_; uint32_t address_; + DISALLOW_COPY_AND_ASSIGN(TypeAddrPair); +}; + +using TypeAddrPairVector = std::vector<std::unique_ptr<const TypeAddrPair>>; + +class CatchHandler { + public: + explicit CatchHandler(bool catch_all, uint16_t list_offset, TypeAddrPairVector* handlers) + : catch_all_(catch_all), list_offset_(list_offset), handlers_(handlers) { } + + bool HasCatchAll() const { return catch_all_; } + uint16_t GetListOffset() const { return list_offset_; } + TypeAddrPairVector* GetHandlers() const { return handlers_.get(); } + + private: + bool catch_all_; + uint16_t list_offset_; + std::unique_ptr<TypeAddrPairVector> handlers_; + DISALLOW_COPY_AND_ASSIGN(CatchHandler); }; @@ -744,20 +782,20 @@ using CatchHandlerVector = std::vector<std::unique_ptr<const CatchHandler>>; class TryItem : public Item { public: - TryItem(uint32_t start_addr, uint16_t insn_count, CatchHandlerVector* handlers) + TryItem(uint32_t start_addr, uint16_t insn_count, const CatchHandler* handlers) : start_addr_(start_addr), insn_count_(insn_count), handlers_(handlers) { } ~TryItem() OVERRIDE { } uint32_t StartAddr() const { return start_addr_; } uint16_t InsnCount() const { return insn_count_; } - const CatchHandlerVector& GetHandlers() const { return *handlers_.get(); } + const CatchHandler* GetHandlers() const { return handlers_; } void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); } private: uint32_t start_addr_; uint16_t insn_count_; - std::unique_ptr<CatchHandlerVector> handlers_; + const CatchHandler* handlers_; DISALLOW_COPY_AND_ASSIGN(TryItem); }; @@ -772,14 +810,16 @@ class CodeItem : public Item { DebugInfoItem* debug_info, uint32_t insns_size, uint16_t* insns, - TryItemVector* tries) + TryItemVector* tries, + CatchHandlerVector* handlers) : registers_size_(registers_size), ins_size_(ins_size), outs_size_(outs_size), debug_info_(debug_info), insns_size_(insns_size), insns_(insns), - tries_(tries) { } + tries_(tries), + handlers_(handlers) { } ~CodeItem() OVERRIDE { } @@ -791,6 +831,7 @@ class CodeItem : public Item { uint32_t InsnsSize() const { return insns_size_; } uint16_t* Insns() const { return insns_.get(); } TryItemVector* Tries() const { return tries_.get(); } + CatchHandlerVector* Handlers() const { return handlers_.get(); } void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); } @@ -798,10 +839,11 @@ class CodeItem : public Item { uint16_t registers_size_; uint16_t ins_size_; uint16_t outs_size_; - DebugInfoItem* debug_info_; + DebugInfoItem* debug_info_; // This can be nullptr. uint32_t insns_size_; std::unique_ptr<uint16_t[]> insns_; - std::unique_ptr<TryItemVector> tries_; + std::unique_ptr<TryItemVector> tries_; // This can be nullptr. + std::unique_ptr<CatchHandlerVector> handlers_; // This can be nullptr. DISALLOW_COPY_AND_ASSIGN(CodeItem); }; @@ -841,12 +883,19 @@ using LocalInfoVector = std::vector<std::unique_ptr<LocalInfo>>; class DebugInfoItem : public Item { public: - DebugInfoItem() = default; + DebugInfoItem(uint32_t debug_info_size, uint8_t* debug_info) + : debug_info_size_(debug_info_size), debug_info_(debug_info) { } + + uint32_t GetDebugInfoSize() const { return debug_info_size_; } + uint8_t* GetDebugInfo() const { return debug_info_.get(); } PositionInfoVector& GetPositionInfo() { return positions_; } LocalInfoVector& GetLocalInfo() { return locals_; } private: + uint32_t debug_info_size_; + std::unique_ptr<uint8_t[]> debug_info_; + PositionInfoVector positions_; LocalInfoVector locals_; @@ -899,7 +948,7 @@ class AnnotationSetRefList : public Item { void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); } private: - std::unique_ptr<std::vector<AnnotationSetItem*>> items_; + std::unique_ptr<std::vector<AnnotationSetItem*>> items_; // Elements of vector can be nullptr. DISALLOW_COPY_AND_ASSIGN(AnnotationSetRefList); }; @@ -974,10 +1023,10 @@ class AnnotationsDirectoryItem : public Item { void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); } private: - AnnotationSetItem* class_annotation_; - std::unique_ptr<FieldAnnotationVector> field_annotations_; - std::unique_ptr<MethodAnnotationVector> method_annotations_; - std::unique_ptr<ParameterAnnotationVector> parameter_annotations_; + AnnotationSetItem* class_annotation_; // This can be nullptr. + std::unique_ptr<FieldAnnotationVector> field_annotations_; // This can be nullptr. + std::unique_ptr<MethodAnnotationVector> method_annotations_; // This can be nullptr. + std::unique_ptr<ParameterAnnotationVector> parameter_annotations_; // This can be nullptr. DISALLOW_COPY_AND_ASSIGN(AnnotationsDirectoryItem); }; diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc index 599f48b3b6..68ff2a253d 100644 --- a/dexlayout/dex_ir_builder.cc +++ b/dexlayout/dex_ir_builder.cc @@ -70,6 +70,8 @@ Header* DexIrBuilder(const DexFile& dex_file) { for (uint32_t i = 0; i < dex_file.NumClassDefs(); ++i) { collections.CreateClassDef(dex_file, i); } + // MapItem. + collections.SetMapItemOffset(disk_header.map_off_); CheckAndSetRemainingOffsets(dex_file, &collections); @@ -124,7 +126,7 @@ static void CheckAndSetRemainingOffsets(const DexFile& dex_file, Collections* co collections->SetAnnotationSetRefListsOffset(item->offset_); break; case DexFile::kDexTypeAnnotationSetItem: - collections->SetAnnotationSetOffset(item->offset_); + collections->SetAnnotationSetItemsOffset(item->offset_); break; case DexFile::kDexTypeClassDataItem: collections->SetClassDatasOffset(item->offset_); @@ -136,16 +138,16 @@ static void CheckAndSetRemainingOffsets(const DexFile& dex_file, Collections* co collections->SetStringDatasOffset(item->offset_); break; case DexFile::kDexTypeDebugInfoItem: - collections->SetDebugInfoOffset(item->offset_); + collections->SetDebugInfoItemsOffset(item->offset_); break; case DexFile::kDexTypeAnnotationItem: - collections->SetAnnotationOffset(item->offset_); + collections->SetAnnotationItemsOffset(item->offset_); break; case DexFile::kDexTypeEncodedArrayItem: - collections->SetEncodedArrayOffset(item->offset_); + collections->SetEncodedArrayItemsOffset(item->offset_); break; case DexFile::kDexTypeAnnotationsDirectoryItem: - collections->SetAnnotationsDirectoryOffset(item->offset_); + collections->SetAnnotationsDirectoryItemsOffset(item->offset_); break; default: LOG(ERROR) << "Unknown map list item type."; diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc index be7bade8ef..46dff5f5f2 100644 --- a/dexlayout/dex_visualize.cc +++ b/dexlayout/dex_visualize.cc @@ -87,18 +87,18 @@ static const std::vector<FileSection> kFileSections = { }, { "EncArr", DexFile::kDexTypeEncodedArrayItem, - &dex_ir::Collections::EncodedArraySize, - &dex_ir::Collections::EncodedArrayOffset + &dex_ir::Collections::EncodedArrayItemsSize, + &dex_ir::Collections::EncodedArrayItemsOffset }, { "Annotation", DexFile::kDexTypeAnnotationItem, - &dex_ir::Collections::AnnotationSize, - &dex_ir::Collections::AnnotationOffset + &dex_ir::Collections::AnnotationItemsSize, + &dex_ir::Collections::AnnotationItemsOffset }, { "AnnoSet", DexFile::kDexTypeAnnotationSetItem, - &dex_ir::Collections::AnnotationSetSize, - &dex_ir::Collections::AnnotationSetOffset + &dex_ir::Collections::AnnotationSetItemsSize, + &dex_ir::Collections::AnnotationSetItemsOffset }, { "AnnoSetRL", DexFile::kDexTypeAnnotationSetRefList, @@ -107,13 +107,13 @@ static const std::vector<FileSection> kFileSections = { }, { "AnnoDir", DexFile::kDexTypeAnnotationsDirectoryItem, - &dex_ir::Collections::AnnotationsDirectorySize, - &dex_ir::Collections::AnnotationsDirectoryOffset + &dex_ir::Collections::AnnotationsDirectoryItemsSize, + &dex_ir::Collections::AnnotationsDirectoryItemsOffset }, { "DebugInfo", DexFile::kDexTypeDebugInfoItem, - &dex_ir::Collections::DebugInfoSize, - &dex_ir::Collections::DebugInfoOffset + &dex_ir::Collections::DebugInfoItemsSize, + &dex_ir::Collections::DebugInfoItemsOffset }, { "CodeItem", DexFile::kDexTypeCodeItem, @@ -244,9 +244,11 @@ class Dumper { return; } DumpStringId(proto_id->Shorty(), class_index); - const dex_ir::TypeIdVector& parameters = proto_id->Parameters(); - for (const dex_ir::TypeId* t : parameters) { - DumpTypeId(t, class_index); + const dex_ir::TypeList* type_list = proto_id->Parameters(); + if (type_list != nullptr) { + for (const dex_ir::TypeId* t : *type_list->GetTypeList()) { + DumpTypeId(t, class_index); + } } DumpTypeId(proto_id->ReturnType(), class_index); } diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc new file mode 100644 index 0000000000..dba5da0fb1 --- /dev/null +++ b/dexlayout/dex_writer.cc @@ -0,0 +1,648 @@ +/* + * 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. + * + * Header file of an in-memory representation of DEX files. + */ + +#include <stdint.h> + +#include <queue> +#include <vector> + +#include "dex_writer.h" +#include "utf.h" + +namespace art { + +size_t EncodeIntValue(int32_t value, uint8_t* buffer) { + size_t length = 0; + if (value >= 0) { + while (value > 0x7f) { + buffer[length++] = static_cast<uint8_t>(value); + value >>= 8; + } + } else { + while (value < -0x80) { + buffer[length++] = static_cast<uint8_t>(value); + value >>= 8; + } + } + buffer[length++] = static_cast<uint8_t>(value); + return length; +} + +size_t EncodeUIntValue(uint32_t value, uint8_t* buffer) { + size_t length = 0; + do { + buffer[length++] = static_cast<uint8_t>(value); + value >>= 8; + } while (value != 0); + return length; +} + +size_t EncodeLongValue(int64_t value, uint8_t* buffer) { + size_t length = 0; + if (value >= 0) { + while (value > 0x7f) { + buffer[length++] = static_cast<uint8_t>(value); + value >>= 8; + } + } else { + while (value < -0x80) { + buffer[length++] = static_cast<uint8_t>(value); + value >>= 8; + } + } + buffer[length++] = static_cast<uint8_t>(value); + return length; +} + +union FloatUnion { + float f_; + uint32_t i_; +}; + +size_t EncodeFloatValue(float value, uint8_t* buffer) { + FloatUnion float_union; + float_union.f_ = value; + uint32_t int_value = float_union.i_; + size_t index = 3; + do { + buffer[index--] = int_value >> 24; + int_value <<= 8; + } while (int_value != 0); + return 3 - index; +} + +union DoubleUnion { + double d_; + uint64_t l_; +}; + +size_t EncodeDoubleValue(double value, uint8_t* buffer) { + DoubleUnion double_union; + double_union.d_ = value; + uint64_t long_value = double_union.l_; + size_t index = 7; + do { + buffer[index--] = long_value >> 56; + long_value <<= 8; + } while (long_value != 0); + return 7 - index; +} + +size_t DexWriter::Write(const void* buffer, size_t length, size_t offset) { + return dex_file_->PwriteFully(buffer, length, offset) ? length : 0; +} + +size_t DexWriter::WriteSleb128(uint32_t value, size_t offset) { + uint8_t buffer[8]; + EncodeSignedLeb128(buffer, value); + return Write(buffer, SignedLeb128Size(value), offset); +} + +size_t DexWriter::WriteUleb128(uint32_t value, size_t offset) { + uint8_t buffer[8]; + EncodeUnsignedLeb128(buffer, value); + return Write(buffer, UnsignedLeb128Size(value), offset); +} + +size_t DexWriter::WriteEncodedValue(dex_ir::EncodedValue* encoded_value, size_t offset) { + size_t original_offset = offset; + size_t start = 0; + size_t length; + uint8_t buffer[8]; + int8_t type = encoded_value->Type(); + switch (type) { + case DexFile::kDexAnnotationByte: + length = EncodeIntValue(encoded_value->GetByte(), buffer); + break; + case DexFile::kDexAnnotationShort: + length = EncodeIntValue(encoded_value->GetShort(), buffer); + break; + case DexFile::kDexAnnotationChar: + length = EncodeUIntValue(encoded_value->GetChar(), buffer); + break; + case DexFile::kDexAnnotationInt: + length = EncodeIntValue(encoded_value->GetInt(), buffer); + break; + case DexFile::kDexAnnotationLong: + length = EncodeLongValue(encoded_value->GetLong(), buffer); + break; + case DexFile::kDexAnnotationFloat: + length = EncodeFloatValue(encoded_value->GetFloat(), buffer); + start = 4 - length; + break; + case DexFile::kDexAnnotationDouble: + length = EncodeDoubleValue(encoded_value->GetDouble(), buffer); + start = 8 - length; + break; + case DexFile::kDexAnnotationString: + length = EncodeUIntValue(encoded_value->GetStringId()->GetIndex(), buffer); + break; + case DexFile::kDexAnnotationType: + length = EncodeUIntValue(encoded_value->GetTypeId()->GetIndex(), buffer); + break; + case DexFile::kDexAnnotationField: + case DexFile::kDexAnnotationEnum: + length = EncodeUIntValue(encoded_value->GetFieldId()->GetIndex(), buffer); + break; + case DexFile::kDexAnnotationMethod: + length = EncodeUIntValue(encoded_value->GetMethodId()->GetIndex(), buffer); + break; + case DexFile::kDexAnnotationArray: + offset += WriteEncodedValueHeader(type, 0, offset); + offset += WriteEncodedArray(encoded_value->GetEncodedArray()->GetEncodedValues(), offset); + return offset - original_offset; + case DexFile::kDexAnnotationAnnotation: + offset += WriteEncodedValueHeader(type, 0, offset); + offset += WriteEncodedAnnotation(encoded_value->GetEncodedAnnotation(), offset); + return offset - original_offset; + case DexFile::kDexAnnotationNull: + return WriteEncodedValueHeader(type, 0, offset); + case DexFile::kDexAnnotationBoolean: + return WriteEncodedValueHeader(type, encoded_value->GetBoolean() ? 1 : 0, offset); + default: + return 0; + } + offset += WriteEncodedValueHeader(type, length - 1, offset); + offset += Write(buffer + start, length, offset); + return offset - original_offset; +} + +size_t DexWriter::WriteEncodedValueHeader(int8_t value_type, size_t value_arg, size_t offset) { + uint8_t buffer[1] = { static_cast<uint8_t>((value_arg << 5) | value_type) }; + return Write(buffer, sizeof(uint8_t), offset); +} + +size_t DexWriter::WriteEncodedArray(dex_ir::EncodedValueVector* values, size_t offset) { + size_t original_offset = offset; + offset += WriteUleb128(values->size(), offset); + for (std::unique_ptr<dex_ir::EncodedValue>& value : *values) { + offset += WriteEncodedValue(value.get(), offset); + } + return offset - original_offset; +} + +size_t DexWriter::WriteEncodedAnnotation(dex_ir::EncodedAnnotation* annotation, size_t offset) { + size_t original_offset = offset; + offset += WriteUleb128(annotation->GetType()->GetIndex(), offset); + offset += WriteUleb128(annotation->GetAnnotationElements()->size(), offset); + for (std::unique_ptr<dex_ir::AnnotationElement>& annotation_element : + *annotation->GetAnnotationElements()) { + offset += WriteUleb128(annotation_element->GetName()->GetIndex(), offset); + offset += WriteEncodedValue(annotation_element->GetValue(), offset); + } + return offset - original_offset; +} + +size_t DexWriter::WriteEncodedFields(dex_ir::FieldItemVector* fields, size_t offset) { + size_t original_offset = offset; + uint32_t prev_index = 0; + for (std::unique_ptr<dex_ir::FieldItem>& field : *fields) { + uint32_t index = field->GetFieldId()->GetIndex(); + offset += WriteUleb128(index - prev_index, offset); + offset += WriteUleb128(field->GetAccessFlags(), offset); + prev_index = index; + } + return offset - original_offset; +} + +size_t DexWriter::WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t offset) { + size_t original_offset = offset; + uint32_t prev_index = 0; + for (std::unique_ptr<dex_ir::MethodItem>& method : *methods) { + uint32_t index = method->GetMethodId()->GetIndex(); + uint32_t code_off = method->GetCodeItem() == nullptr ? 0 : method->GetCodeItem()->GetOffset(); + offset += WriteUleb128(index - prev_index, offset); + offset += WriteUleb128(method->GetAccessFlags(), offset); + offset += WriteUleb128(code_off, offset); + prev_index = index; + } + return offset - original_offset; +} + +void DexWriter::WriteStrings() { + uint32_t string_data_off[1]; + for (std::unique_ptr<dex_ir::StringId>& string_id : header_.GetCollections().StringIds()) { + string_data_off[0] = string_id->DataItem()->GetOffset(); + Write(string_data_off, string_id->GetSize(), string_id->GetOffset()); + } + + for (std::unique_ptr<dex_ir::StringData>& string_data : header_.GetCollections().StringDatas()) { + uint32_t offset = string_data->GetOffset(); + offset += WriteUleb128(CountModifiedUtf8Chars(string_data->Data()), offset); + Write(string_data->Data(), strlen(string_data->Data()), offset); + } +} + +void DexWriter::WriteTypes() { + uint32_t descriptor_idx[1]; + for (std::unique_ptr<dex_ir::TypeId>& type_id : header_.GetCollections().TypeIds()) { + descriptor_idx[0] = type_id->GetStringId()->GetIndex(); + Write(descriptor_idx, type_id->GetSize(), type_id->GetOffset()); + } +} + +void DexWriter::WriteTypeLists() { + uint32_t size[1]; + uint16_t list[1]; + for (std::unique_ptr<dex_ir::TypeList>& type_list : header_.GetCollections().TypeLists()) { + size[0] = type_list->GetTypeList()->size(); + uint32_t offset = type_list->GetOffset(); + offset += Write(size, sizeof(uint32_t), offset); + for (const dex_ir::TypeId* type_id : *type_list->GetTypeList()) { + list[0] = type_id->GetIndex(); + offset += Write(list, sizeof(uint16_t), offset); + } + } +} + +void DexWriter::WriteProtos() { + uint32_t buffer[3]; + for (std::unique_ptr<dex_ir::ProtoId>& proto_id : header_.GetCollections().ProtoIds()) { + buffer[0] = proto_id->Shorty()->GetIndex(); + buffer[1] = proto_id->ReturnType()->GetIndex(); + buffer[2] = proto_id->Parameters() == nullptr ? 0 : proto_id->Parameters()->GetOffset(); + Write(buffer, proto_id->GetSize(), proto_id->GetOffset()); + } +} + +void DexWriter::WriteFields() { + uint16_t buffer[4]; + for (std::unique_ptr<dex_ir::FieldId>& field_id : header_.GetCollections().FieldIds()) { + buffer[0] = field_id->Class()->GetIndex(); + buffer[1] = field_id->Type()->GetIndex(); + buffer[2] = field_id->Name()->GetIndex(); + buffer[3] = field_id->Name()->GetIndex() >> 16; + Write(buffer, field_id->GetSize(), field_id->GetOffset()); + } +} + +void DexWriter::WriteMethods() { + uint16_t buffer[4]; + for (std::unique_ptr<dex_ir::MethodId>& method_id : header_.GetCollections().MethodIds()) { + buffer[0] = method_id->Class()->GetIndex(); + buffer[1] = method_id->Proto()->GetIndex(); + buffer[2] = method_id->Name()->GetIndex(); + buffer[3] = method_id->Name()->GetIndex() >> 16; + Write(buffer, method_id->GetSize(), method_id->GetOffset()); + } +} + +void DexWriter::WriteEncodedArrays() { + for (std::unique_ptr<dex_ir::EncodedArrayItem>& encoded_array : + header_.GetCollections().EncodedArrayItems()) { + WriteEncodedArray(encoded_array->GetEncodedValues(), encoded_array->GetOffset()); + } +} + +void DexWriter::WriteAnnotations() { + uint8_t visibility[1]; + for (std::unique_ptr<dex_ir::AnnotationItem>& annotation : + header_.GetCollections().AnnotationItems()) { + visibility[0] = annotation->GetVisibility(); + size_t offset = annotation->GetOffset(); + offset += Write(visibility, sizeof(uint8_t), offset); + WriteEncodedAnnotation(annotation->GetAnnotation(), offset); + } +} + +void DexWriter::WriteAnnotationSets() { + uint32_t size[1]; + uint32_t annotation_off[1]; + for (std::unique_ptr<dex_ir::AnnotationSetItem>& annotation_set : + header_.GetCollections().AnnotationSetItems()) { + size[0] = annotation_set->GetItems()->size(); + size_t offset = annotation_set->GetOffset(); + offset += Write(size, sizeof(uint32_t), offset); + for (dex_ir::AnnotationItem* annotation : *annotation_set->GetItems()) { + annotation_off[0] = annotation->GetOffset(); + offset += Write(annotation_off, sizeof(uint32_t), offset); + } + } +} + +void DexWriter::WriteAnnotationSetRefs() { + uint32_t size[1]; + uint32_t annotations_off[1]; + for (std::unique_ptr<dex_ir::AnnotationSetRefList>& annotation_set_ref : + header_.GetCollections().AnnotationSetRefLists()) { + size[0] = annotation_set_ref->GetItems()->size(); + size_t offset = annotation_set_ref->GetOffset(); + offset += Write(size, sizeof(uint32_t), offset); + for (dex_ir::AnnotationSetItem* annotation_set : *annotation_set_ref->GetItems()) { + annotations_off[0] = annotation_set == nullptr ? 0 : annotation_set->GetOffset(); + offset += Write(annotations_off, sizeof(uint32_t), offset); + } + } +} + +void DexWriter::WriteAnnotationsDirectories() { + uint32_t directory_buffer[4]; + uint32_t annotation_buffer[2]; + for (std::unique_ptr<dex_ir::AnnotationsDirectoryItem>& annotations_directory : + header_.GetCollections().AnnotationsDirectoryItems()) { + directory_buffer[0] = annotations_directory->GetClassAnnotation() == nullptr ? 0 : + annotations_directory->GetClassAnnotation()->GetOffset(); + directory_buffer[1] = annotations_directory->GetFieldAnnotations() == nullptr ? 0 : + annotations_directory->GetFieldAnnotations()->size(); + directory_buffer[2] = annotations_directory->GetMethodAnnotations() == nullptr ? 0 : + annotations_directory->GetMethodAnnotations()->size(); + directory_buffer[3] = annotations_directory->GetParameterAnnotations() == nullptr ? 0 : + annotations_directory->GetParameterAnnotations()->size(); + uint32_t offset = annotations_directory->GetOffset(); + offset += Write(directory_buffer, 4 * sizeof(uint32_t), offset); + if (annotations_directory->GetFieldAnnotations() != nullptr) { + for (std::unique_ptr<dex_ir::FieldAnnotation>& field : + *annotations_directory->GetFieldAnnotations()) { + annotation_buffer[0] = field->GetFieldId()->GetIndex(); + annotation_buffer[1] = field->GetAnnotationSetItem()->GetOffset(); + offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset); + } + } + if (annotations_directory->GetMethodAnnotations() != nullptr) { + for (std::unique_ptr<dex_ir::MethodAnnotation>& method : + *annotations_directory->GetMethodAnnotations()) { + annotation_buffer[0] = method->GetMethodId()->GetIndex(); + annotation_buffer[1] = method->GetAnnotationSetItem()->GetOffset(); + offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset); + } + } + if (annotations_directory->GetParameterAnnotations() != nullptr) { + for (std::unique_ptr<dex_ir::ParameterAnnotation>& parameter : + *annotations_directory->GetParameterAnnotations()) { + annotation_buffer[0] = parameter->GetMethodId()->GetIndex(); + annotation_buffer[1] = parameter->GetAnnotations()->GetOffset(); + offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset); + } + } + } +} + +void DexWriter::WriteDebugInfoItems() { + for (std::unique_ptr<dex_ir::DebugInfoItem>& info : header_.GetCollections().DebugInfoItems()) { + Write(info->GetDebugInfo(), info->GetDebugInfoSize(), info->GetOffset()); + } +} + +void DexWriter::WriteCodeItems() { + uint16_t uint16_buffer[4]; + uint32_t uint32_buffer[2]; + for (std::unique_ptr<dex_ir::CodeItem>& code_item : header_.GetCollections().CodeItems()) { + uint16_buffer[0] = code_item->RegistersSize(); + uint16_buffer[1] = code_item->InsSize(); + uint16_buffer[2] = code_item->OutsSize(); + uint16_buffer[3] = code_item->TriesSize(); + uint32_buffer[0] = code_item->DebugInfo() == nullptr ? 0 : code_item->DebugInfo()->GetOffset(); + uint32_buffer[1] = code_item->InsnsSize(); + size_t offset = code_item->GetOffset(); + offset += Write(uint16_buffer, 4 * sizeof(uint16_t), offset); + offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset); + offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset); + if (code_item->TriesSize() != 0) { + if (code_item->InsnsSize() % 2 != 0) { + uint16_t padding[1] = { 0 }; + offset += Write(padding, sizeof(uint16_t), offset); + } + uint32_t start_addr[1]; + uint16_t insn_count_and_handler_off[2]; + for (std::unique_ptr<const dex_ir::TryItem>& try_item : *code_item->Tries()) { + start_addr[0] = try_item->StartAddr(); + insn_count_and_handler_off[0] = try_item->InsnCount(); + insn_count_and_handler_off[1] = try_item->GetHandlers()->GetListOffset(); + offset += Write(start_addr, sizeof(uint32_t), offset); + offset += Write(insn_count_and_handler_off, 2 * sizeof(uint16_t), offset); + } + // Leave offset pointing to the end of the try items. + WriteUleb128(code_item->Handlers()->size(), offset); + for (std::unique_ptr<const dex_ir::CatchHandler>& handlers : *code_item->Handlers()) { + size_t list_offset = offset + handlers->GetListOffset(); + uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 : + handlers->GetHandlers()->size(); + list_offset += WriteSleb128(size, list_offset); + for (std::unique_ptr<const dex_ir::TypeAddrPair>& handler : *handlers->GetHandlers()) { + if (handler->GetTypeId() != nullptr) { + list_offset += WriteUleb128(handler->GetTypeId()->GetIndex(), list_offset); + } + list_offset += WriteUleb128(handler->GetAddress(), list_offset); + } + } + } + } +} + +void DexWriter::WriteClasses() { + uint32_t class_def_buffer[8]; + for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_.GetCollections().ClassDefs()) { + class_def_buffer[0] = class_def->ClassType()->GetIndex(); + class_def_buffer[1] = class_def->GetAccessFlags(); + class_def_buffer[2] = class_def->Superclass() == nullptr ? DexFile::kDexNoIndex : + class_def->Superclass()->GetIndex(); + class_def_buffer[3] = class_def->InterfacesOffset(); + class_def_buffer[4] = class_def->SourceFile() == nullptr ? DexFile::kDexNoIndex : + class_def->SourceFile()->GetIndex(); + class_def_buffer[5] = class_def->Annotations() == nullptr ? 0 : + class_def->Annotations()->GetOffset(); + class_def_buffer[6] = class_def->GetClassData() == nullptr ? 0 : + class_def->GetClassData()->GetOffset(); + class_def_buffer[7] = class_def->StaticValues() == nullptr ? 0 : + class_def->StaticValues()->GetOffset(); + size_t offset = class_def->GetOffset(); + Write(class_def_buffer, class_def->GetSize(), offset); + } + + for (std::unique_ptr<dex_ir::ClassData>& class_data : header_.GetCollections().ClassDatas()) { + size_t offset = class_data->GetOffset(); + offset += WriteUleb128(class_data->StaticFields()->size(), offset); + offset += WriteUleb128(class_data->InstanceFields()->size(), offset); + offset += WriteUleb128(class_data->DirectMethods()->size(), offset); + offset += WriteUleb128(class_data->VirtualMethods()->size(), offset); + offset += WriteEncodedFields(class_data->StaticFields(), offset); + offset += WriteEncodedFields(class_data->InstanceFields(), offset); + offset += WriteEncodedMethods(class_data->DirectMethods(), offset); + offset += WriteEncodedMethods(class_data->VirtualMethods(), offset); + } +} + +struct MapItemContainer { + MapItemContainer(uint32_t type, uint32_t size, uint32_t offset) + : type_(type), size_(size), offset_(offset) { } + + bool operator<(const MapItemContainer& other) const { + return offset_ > other.offset_; + } + + uint32_t type_; + uint32_t size_; + uint32_t offset_; +}; + +void DexWriter::WriteMapItem() { + dex_ir::Collections& collection = header_.GetCollections(); + std::priority_queue<MapItemContainer> queue; + + // Header and index section. + queue.push(MapItemContainer(DexFile::kDexTypeHeaderItem, 1, 0)); + if (collection.StringIdsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeStringIdItem, collection.StringIdsSize(), + collection.StringIdsOffset())); + } + if (collection.TypeIdsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeTypeIdItem, collection.TypeIdsSize(), + collection.TypeIdsOffset())); + } + if (collection.ProtoIdsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeProtoIdItem, collection.ProtoIdsSize(), + collection.ProtoIdsOffset())); + } + if (collection.FieldIdsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeFieldIdItem, collection.FieldIdsSize(), + collection.FieldIdsOffset())); + } + if (collection.MethodIdsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeMethodIdItem, collection.MethodIdsSize(), + collection.MethodIdsOffset())); + } + if (collection.ClassDefsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeClassDefItem, collection.ClassDefsSize(), + collection.ClassDefsOffset())); + } + + // Data section. + queue.push(MapItemContainer(DexFile::kDexTypeMapList, 1, collection.MapItemOffset())); + if (collection.TypeListsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeTypeList, collection.TypeListsSize(), + collection.TypeListsOffset())); + } + if (collection.AnnotationSetRefListsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeAnnotationSetRefList, + collection.AnnotationSetRefListsSize(), collection.AnnotationSetRefListsOffset())); + } + if (collection.AnnotationSetItemsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeAnnotationSetItem, + collection.AnnotationSetItemsSize(), collection.AnnotationSetItemsOffset())); + } + if (collection.ClassDatasSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeClassDataItem, collection.ClassDatasSize(), + collection.ClassDatasOffset())); + } + if (collection.CodeItemsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeCodeItem, collection.CodeItemsSize(), + collection.CodeItemsOffset())); + } + if (collection.StringDatasSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeStringDataItem, collection.StringDatasSize(), + collection.StringDatasOffset())); + } + if (collection.DebugInfoItemsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeDebugInfoItem, collection.DebugInfoItemsSize(), + collection.DebugInfoItemsOffset())); + } + if (collection.AnnotationItemsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeAnnotationItem, collection.AnnotationItemsSize(), + collection.AnnotationItemsOffset())); + } + if (collection.EncodedArrayItemsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeEncodedArrayItem, + collection.EncodedArrayItemsSize(), collection.EncodedArrayItemsOffset())); + } + if (collection.AnnotationsDirectoryItemsSize() != 0) { + queue.push(MapItemContainer(DexFile::kDexTypeAnnotationsDirectoryItem, + collection.AnnotationsDirectoryItemsSize(), collection.AnnotationsDirectoryItemsOffset())); + } + + uint32_t offset = collection.MapItemOffset(); + uint16_t uint16_buffer[2]; + uint32_t uint32_buffer[2]; + uint16_buffer[1] = 0; + uint32_buffer[0] = queue.size(); + offset += Write(uint32_buffer, sizeof(uint32_t), offset); + while (!queue.empty()) { + const MapItemContainer& map_item = queue.top(); + uint16_buffer[0] = map_item.type_; + uint32_buffer[0] = map_item.size_; + uint32_buffer[1] = map_item.offset_; + offset += Write(uint16_buffer, 2 * sizeof(uint16_t), offset); + offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset); + queue.pop(); + } +} + +void DexWriter::WriteHeader() { + uint32_t buffer[20]; + dex_ir::Collections& collections = header_.GetCollections(); + size_t offset = 0; + offset += Write(header_.Magic(), 8 * sizeof(uint8_t), offset); + buffer[0] = header_.Checksum(); + offset += Write(buffer, sizeof(uint32_t), offset); + offset += Write(header_.Signature(), 20 * sizeof(uint8_t), offset); + uint32_t file_size = header_.FileSize(); + buffer[0] = file_size; + buffer[1] = header_.GetSize(); + buffer[2] = header_.EndianTag(); + buffer[3] = header_.LinkSize(); + buffer[4] = header_.LinkOffset(); + buffer[5] = collections.MapItemOffset(); + buffer[6] = collections.StringIdsSize(); + buffer[7] = collections.StringIdsOffset(); + buffer[8] = collections.TypeIdsSize(); + buffer[9] = collections.TypeIdsOffset(); + buffer[10] = collections.ProtoIdsSize(); + buffer[11] = collections.ProtoIdsOffset(); + buffer[12] = collections.FieldIdsSize(); + buffer[13] = collections.FieldIdsOffset(); + buffer[14] = collections.MethodIdsSize(); + buffer[15] = collections.MethodIdsOffset(); + uint32_t class_defs_size = collections.ClassDefsSize(); + uint32_t class_defs_off = collections.ClassDefsOffset(); + buffer[16] = class_defs_size; + buffer[17] = class_defs_off; + uint32_t data_off = class_defs_off + class_defs_size * dex_ir::ClassDef::ItemSize(); + uint32_t data_size = file_size - data_off; + buffer[18] = data_size; + buffer[19] = data_off; + Write(buffer, 20 * sizeof(uint32_t), offset); +} + +void DexWriter::WriteFile() { + if (dex_file_.get() == nullptr) { + fprintf(stderr, "Can't open output dex file\n"); + return; + } + + WriteStrings(); + WriteTypes(); + WriteTypeLists(); + WriteProtos(); + WriteFields(); + WriteMethods(); + WriteEncodedArrays(); + WriteAnnotations(); + WriteAnnotationSets(); + WriteAnnotationSetRefs(); + WriteAnnotationsDirectories(); + WriteDebugInfoItems(); + WriteCodeItems(); + WriteClasses(); + WriteMapItem(); + WriteHeader(); +} + +void DexWriter::OutputDexFile(dex_ir::Header& header, const char* file_name) { + (new DexWriter(header, file_name))->WriteFile(); +} + +} // namespace art diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h new file mode 100644 index 0000000000..9104295472 --- /dev/null +++ b/dexlayout/dex_writer.h @@ -0,0 +1,74 @@ +/* + * 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. + * + * Header file of an in-memory representation of DEX files. + */ + +#ifndef ART_DEXLAYOUT_DEX_WRITER_H_ +#define ART_DEXLAYOUT_DEX_WRITER_H_ + +#include "base/unix_file/fd_file.h" +#include "dex_ir.h" +#include "os.h" + +namespace art { + +class DexWriter { + public: + DexWriter(dex_ir::Header& header, const char* file_name) : header_(header), + dex_file_(OS::CreateEmptyFileWriteOnly(file_name)) { } + + static void OutputDexFile(dex_ir::Header& header, const char* file_name); + + private: + void WriteFile(); + + size_t Write(const void* buffer, size_t length, size_t offset); + size_t WriteSleb128(uint32_t value, size_t offset); + size_t WriteUleb128(uint32_t value, size_t offset); + size_t WriteEncodedValue(dex_ir::EncodedValue* encoded_value, size_t offset); + size_t WriteEncodedValueHeader(int8_t value_type, size_t value_arg, size_t offset); + size_t WriteEncodedArray(dex_ir::EncodedValueVector* values, size_t offset); + size_t WriteEncodedAnnotation(dex_ir::EncodedAnnotation* annotation, size_t offset); + size_t WriteEncodedFields(dex_ir::FieldItemVector* fields, size_t offset); + size_t WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t offset); + + void WriteStrings(); + void WriteTypes(); + void WriteTypeLists(); + void WriteProtos(); + void WriteFields(); + void WriteMethods(); + void WriteEncodedArrays(); + void WriteAnnotations(); + void WriteAnnotationSets(); + void WriteAnnotationSetRefs(); + void WriteAnnotationsDirectories(); + void WriteDebugInfoItems(); + void WriteCodeItems(); + void WriteClasses(); + void WriteMapItem(); + void WriteHeader(); + + dex_ir::Header& header_; + std::unique_ptr<File> dex_file_; + + DISALLOW_COPY_AND_ASSIGN(DexWriter); +}; + + +} // namespace art + +#endif // ART_DEXLAYOUT_DEX_WRITER_H_ diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index e6141372a6..a9ae55fd8b 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -30,11 +30,11 @@ #include <sstream> #include <vector> -#include "base/unix_file/fd_file.h" #include "dex_ir_builder.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" #include "dex_visualize.h" +#include "dex_writer.h" #include "jit/offline_profiling_info.h" #include "os.h" #include "utils.h" @@ -251,10 +251,12 @@ static std::string GetSignatureForProtoId(const dex_ir::ProtoId* proto) { return "<no signature>"; } - const std::vector<const dex_ir::TypeId*>& params = proto->Parameters(); std::string result("("); - for (uint32_t i = 0; i < params.size(); ++i) { - result += params[i]->GetStringId()->Data(); + const dex_ir::TypeList* type_list = proto->Parameters(); + if (type_list != nullptr) { + for (const dex_ir::TypeId* type_id : *type_list->GetTypeList()) { + result += type_id->GetStringId()->Data(); + } } result += ")"; result += proto->ReturnType()->GetStringId()->Data(); @@ -673,7 +675,7 @@ static void DumpCatches(const dex_ir::CodeItem* code) { const uint32_t start = try_item->StartAddr(); const uint32_t end = start + try_item->InsnCount(); fprintf(out_file_, " 0x%04x - 0x%04x\n", start, end); - for (auto& handler : try_item->GetHandlers()) { + for (auto& handler : *try_item->GetHandlers()->GetHandlers()) { const dex_ir::TypeId* type_id = handler->GetTypeId(); const char* descriptor = (type_id == nullptr) ? "<any>" : type_id->GetStringId()->Data(); fprintf(out_file_, " %s -> 0x%04x\n", descriptor, handler->GetAddress()); @@ -1502,96 +1504,6 @@ static void DumpClass(const DexFile* dex_file, } /* -static uint32_t GetDataSectionOffset(dex_ir::Header& header) { - return dex_ir::Header::ItemSize() + - header.GetCollections().StringIdsSize() * dex_ir::StringId::ItemSize() + - header.GetCollections().TypeIdsSize() * dex_ir::TypeId::ItemSize() + - header.GetCollections().ProtoIdsSize() * dex_ir::ProtoId::ItemSize() + - header.GetCollections().FieldIdsSize() * dex_ir::FieldId::ItemSize() + - header.GetCollections().MethodIdsSize() * dex_ir::MethodId::ItemSize() + - header.GetCollections().ClassDefsSize() * dex_ir::ClassDef::ItemSize(); -} - -static bool Align(File* file, uint32_t& offset) { - uint8_t zero_buffer[] = { 0, 0, 0 }; - uint32_t zeroes = (-offset) & 3; - if (zeroes > 0) { - if (!file->PwriteFully(zero_buffer, zeroes, offset)) { - return false; - } - offset += zeroes; - } - return true; -} - -static bool WriteStrings(File* dex_file, dex_ir::Header& header, - uint32_t& index_offset, uint32_t& data_offset) { - uint32_t index = 0; - uint32_t index_buffer[1]; - uint32_t string_length; - uint32_t length_length; - uint8_t length_buffer[8]; - for (std::unique_ptr<dex_ir::StringId>& string_id : header.GetCollections().StringIds()) { - string_id->SetOffset(index); - index_buffer[0] = data_offset; - string_length = strlen(string_id->Data()); - length_length = UnsignedLeb128Size(string_length); - EncodeUnsignedLeb128(length_buffer, string_length); - - if (!dex_file->PwriteFully(index_buffer, 4, index_offset) || - !dex_file->PwriteFully(length_buffer, length_length, data_offset) || - !dex_file->PwriteFully(string_id->Data(), string_length, data_offset + length_length)) { - return false; - } - - index++; - index_offset += 4; - data_offset += string_length + length_length; - } - return true; -} - -static bool WriteTypes(File* dex_file, dex_ir::Header& header, uint32_t& index_offset) { - uint32_t index = 0; - uint32_t index_buffer[1]; - for (std::unique_ptr<dex_ir::TypeId>& type_id : header.GetCollections().TypeIds()) { - type_id->SetIndex(index); - index_buffer[0] = type_id->GetStringId()->GetOffset(); - - if (!dex_file->PwriteFully(index_buffer, 4, index_offset)) { - return false; - } - - index++; - index_offset += 4; - } - return true; -} - -static bool WriteTypeLists(File* dex_file, dex_ir::Header& header, uint32_t& data_offset) { - if (!Align(dex_file, data_offset)) { - return false; - } - - return true; -} - -static void OutputDexFile(dex_ir::Header& header, const char* file_name) { - LOG(INFO) << "FILE NAME: " << file_name; - std::unique_ptr<File> dex_file(OS::CreateEmptyFileWriteOnly(file_name)); - if (dex_file == nullptr) { - fprintf(stderr, "Can't open %s\n", file_name); - return; - } - - uint32_t index_offset = dex_ir::Header::ItemSize(); - uint32_t data_offset = GetDataSectionOffset(header); - WriteStrings(dex_file.get(), header, index_offset, data_offset); - WriteTypes(dex_file.get(), header, index_offset); -} -*/ - -/* * Dumps the requested sections of the file. */ static void ProcessDexFile(const char* file_name, const DexFile* dex_file, size_t dex_file_index) { @@ -1634,13 +1546,13 @@ static void ProcessDexFile(const char* file_name, const DexFile* dex_file, size_ fprintf(out_file_, "</api>\n"); } - /* // Output dex file. - if (options_.output_dex_files_) { - std::string output_dex_filename = dex_file->GetLocation() + ".out"; - OutputDexFile(*header, output_dex_filename.c_str()); + if (options_.output_dex_directory_ != nullptr) { + std::string output_location(options_.output_dex_directory_); + size_t last_slash = dex_file->GetLocation().rfind("/"); + output_location.append(dex_file->GetLocation().substr(last_slash)); + DexWriter::OutputDexFile(*header, output_location.c_str()); } - */ } /* diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h index c4892d278b..c01eb79ecf 100644 --- a/dexlayout/dexlayout.h +++ b/dexlayout/dexlayout.h @@ -43,7 +43,6 @@ struct Options { bool disassemble_; bool exports_only_; bool ignore_bad_checksum_; - bool output_dex_files_; bool show_annotations_; bool show_cfg_; bool show_file_headers_; @@ -51,6 +50,7 @@ struct Options { bool verbose_; bool visualize_pattern_; OutputFormat output_format_; + const char* output_dex_directory_; const char* output_file_name_; const char* profile_file_name_; }; diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index f385b09119..728e389fcf 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -43,7 +43,7 @@ static const char* kProgramName = "dexlayout"; static void Usage(void) { fprintf(stderr, "Copyright (C) 2016 The Android Open Source Project\n\n"); fprintf(stderr, "%s: [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile] [-p profile]" - " [-s] [-w] dexfile...\n\n", kProgramName); + " [-s] [-w directory] dexfile...\n\n", kProgramName); fprintf(stderr, " -a : display annotations\n"); fprintf(stderr, " -b : build dex_ir\n"); fprintf(stderr, " -c : verify checksum and exit\n"); @@ -57,7 +57,7 @@ static void Usage(void) { fprintf(stderr, " -o : output file name (defaults to stdout)\n"); fprintf(stderr, " -p : profile file name (defaults to no profile)\n"); fprintf(stderr, " -s : visualize reference pattern\n"); - fprintf(stderr, " -w : output dex files\n"); + fprintf(stderr, " -w : output dex directory \n"); } /* @@ -75,7 +75,7 @@ int DexlayoutDriver(int argc, char** argv) { // Parse all arguments. while (1) { - const int ic = getopt(argc, argv, "abcdefghil:o:p:sw"); + const int ic = getopt(argc, argv, "abcdefghil:o:p:sw:"); if (ic < 0) { break; // done } @@ -127,8 +127,8 @@ int DexlayoutDriver(int argc, char** argv) { options_.visualize_pattern_ = true; options_.verbose_ = false; break; - case 'w': // output dex files - options_.output_dex_files_ = true; + case 'w': // output dex files directory + options_.output_dex_directory_ = optarg; break; default: want_usage = true; diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 42b64c3888..89544d7ef4 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -31,43 +31,83 @@ class DexLayoutTest : public CommonRuntimeTest { protected: virtual void SetUp() { CommonRuntimeTest::SetUp(); - // TODO: Test with other dex files for improved coverage. - // Dogfood our own lib core dex file. - dex_file_ = GetLibCoreDexFileNames()[0]; } - // Runs test with given arguments. - bool Exec(std::string* error_msg) { + // Runs FullPlainOutput test. + bool FullPlainOutputExec(std::string* error_msg) { // TODO: dexdump2 -> dexdump ? ScratchFile dexdump_output; std::string dexdump_filename = dexdump_output.GetFilename(); std::string dexdump = GetTestAndroidRoot() + "/bin/dexdump2"; EXPECT_TRUE(OS::FileExists(dexdump.c_str())) << dexdump << " should be a valid file path"; - std::vector<std::string> dexdump_exec_argv = - { dexdump, "-d", "-f", "-h", "-l", "plain", "-o", dexdump_filename, dex_file_ }; ScratchFile dexlayout_output; std::string dexlayout_filename = dexlayout_output.GetFilename(); std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector<std::string> dexlayout_exec_argv = - { dexlayout, "-d", "-f", "-h", "-l", "plain", "-o", dexlayout_filename, dex_file_ }; - if (!::art::Exec(dexdump_exec_argv, error_msg)) { - return false; - } - if (!::art::Exec(dexlayout_exec_argv, error_msg)) { - return false; - } - std::vector<std::string> diff_exec_argv = - { "/usr/bin/diff", dexdump_filename, dexlayout_filename }; - if (!::art::Exec(diff_exec_argv, error_msg)) { - return false; + for (const std::string &dex_file : GetLibCoreDexFileNames()) { + std::vector<std::string> dexdump_exec_argv = + { dexdump, "-d", "-f", "-h", "-l", "plain", "-o", dexdump_filename, dex_file }; + std::vector<std::string> dexlayout_exec_argv = + { dexlayout, "-d", "-f", "-h", "-l", "plain", "-o", dexlayout_filename, dex_file }; + + if (!::art::Exec(dexdump_exec_argv, error_msg)) { + return false; + } + if (!::art::Exec(dexlayout_exec_argv, error_msg)) { + return false; + } + std::vector<std::string> diff_exec_argv = + { "/usr/bin/diff", dexdump_filename, dexlayout_filename }; + if (!::art::Exec(diff_exec_argv, error_msg)) { + return false; + } } return true; } - std::string dex_file_; + // Runs DexFileOutput test. + bool DexFileOutputExec(std::string* error_msg) { + ScratchFile tmp_file; + std::string tmp_name = tmp_file.GetFilename(); + size_t tmp_last_slash = tmp_name.rfind("/"); + std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1); + std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; + EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; + + for (const std::string &dex_file : GetLibCoreDexFileNames()) { + std::vector<std::string> dexlayout_exec_argv = + { dexlayout, "-d", "-f", "-h", "-l", "plain", "-w", tmp_dir, "-o", tmp_name, dex_file }; + + if (!::art::Exec(dexlayout_exec_argv, error_msg)) { + return false; + } + + size_t dex_file_last_slash = dex_file.rfind("/"); + std::string dex_file_name = dex_file.substr(dex_file_last_slash + 1); + std::vector<std::string> unzip_exec_argv = + { "/usr/bin/unzip", dex_file, "classes.dex", "-d", tmp_dir}; + if (!::art::Exec(unzip_exec_argv, error_msg)) { + return false; + } + std::vector<std::string> diff_exec_argv = + { "/usr/bin/diff", tmp_dir + "classes.dex" , tmp_dir + dex_file_name }; + if (!::art::Exec(diff_exec_argv, error_msg)) { + return false; + } + std::vector<std::string> rm_zip_exec_argv = { "/bin/rm", tmp_dir + "classes.dex" }; + if (!::art::Exec(rm_zip_exec_argv, error_msg)) { + return false; + } + std::vector<std::string> rm_out_exec_argv = { "/bin/rm", tmp_dir + dex_file_name }; + if (!::art::Exec(rm_out_exec_argv, error_msg)) { + return false; + } + } + + return true; + } }; @@ -75,7 +115,14 @@ TEST_F(DexLayoutTest, FullPlainOutput) { // Disable test on target. TEST_DISABLED_FOR_TARGET(); std::string error_msg; - ASSERT_TRUE(Exec(&error_msg)) << error_msg; + ASSERT_TRUE(FullPlainOutputExec(&error_msg)) << error_msg; +} + +TEST_F(DexLayoutTest, DexFileOutput) { + // Disable test on target. + TEST_DISABLED_FOR_TARGET(); + std::string error_msg; + ASSERT_TRUE(DexFileOutputExec(&error_msg)) << error_msg; } } // namespace art diff --git a/runtime/base/dumpable-inl.h b/runtime/base/dumpable-inl.h new file mode 100644 index 0000000000..2cdf083f01 --- /dev/null +++ b/runtime/base/dumpable-inl.h @@ -0,0 +1,35 @@ +/* + * 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_RUNTIME_BASE_DUMPABLE_INL_H_ +#define ART_RUNTIME_BASE_DUMPABLE_INL_H_ + +#include "base/dumpable.h" +#include "base/mutex.h" +#include "thread-inl.h" + +namespace art { + +template<typename T> +inline std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs) { + Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); + rhs.Dump(os); + return os; +} + +} // namespace art + +#endif // ART_RUNTIME_BASE_DUMPABLE_INL_H_ diff --git a/runtime/base/dumpable.h b/runtime/base/dumpable.h index 9bc4089163..9ef8d69b48 100644 --- a/runtime/base/dumpable.h +++ b/runtime/base/dumpable.h @@ -20,6 +20,7 @@ #include <ostream> #include "base/macros.h" +#include "base/mutex.h" namespace art { @@ -50,6 +51,27 @@ std::ostream& operator<<(std::ostream& os, const Dumpable<T>& rhs) { return os; } +template<typename T> +class MutatorLockedDumpable { + public: + explicit MutatorLockedDumpable(T& value) REQUIRES_SHARED(Locks::mutator_lock_) : value_(value) {} + + void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_) { + value_.Dump(os); + } + + private: + const T& value_; + + DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable); +}; + +template<typename T> +std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs) + // TODO: should be REQUIRES_SHARED(Locks::mutator_lock_) however annotalysis + // currently fails for this. + NO_THREAD_SAFETY_ANALYSIS; + } // namespace art #endif // ART_RUNTIME_BASE_DUMPABLE_H_ diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 53135b6f39..ed0f0c0236 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -4297,11 +4297,11 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& CHECK_EQ(interfaces_sfield.GetDeclaringClass(), klass.Get()); interfaces_sfield.SetObject<false>( klass.Get(), - soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces).Ptr()); + soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces)); CHECK_EQ(throws_sfield.GetDeclaringClass(), klass.Get()); throws_sfield.SetObject<false>( klass.Get(), - soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>>(throws).Ptr()); + soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>>(throws)); { // Lock on klass is released. Lock new class object. @@ -4331,9 +4331,9 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name); CHECK_EQ(klass.Get()->GetInterfaces(), - soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces).Ptr()); + soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces)); CHECK_EQ(klass.Get()->GetThrows(), - soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>>(throws).Ptr()); + soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>>(throws)); } return klass.Get(); } diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 546653960f..d45495cf68 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -875,7 +875,7 @@ TEST_F(ClassLinkerTest, LookupResolvedType) { uint32_t type_idx = klass->GetClassDef()->class_idx_; ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache(); const DexFile& dex_file = klass->GetDexFile(); - EXPECT_EQ(dex_cache->GetResolvedType(type_idx), klass.Ptr()); + EXPECT_OBJ_PTR_EQ(dex_cache->GetResolvedType(type_idx), klass); EXPECT_OBJ_PTR_EQ( class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()), klass); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index a7feeef89f..7006f70687 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1995,7 +1995,7 @@ JDWP::JdwpError Dbg::GetThreadName(JDWP::ObjectId thread_id, std::string* name) CHECK(thread_object != nullptr) << error; ArtField* java_lang_Thread_name_field = soa.DecodeField(WellKnownClasses::java_lang_Thread_name); - ObjPtr<mirror::String> s(java_lang_Thread_name_field->GetObject(thread_object)); + ObjPtr<mirror::String> s(java_lang_Thread_name_field->GetObject(thread_object)->AsString()); if (s != nullptr) { *name = s->ToModifiedUtf8(); } diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h index 5cc1de209d..e05f8f307c 100644 --- a/runtime/indirect_reference_table-inl.h +++ b/runtime/indirect_reference_table-inl.h @@ -19,7 +19,9 @@ #include "indirect_reference_table.h" +#include "base/dumpable.h" #include "gc_root-inl.h" +#include "obj_ptr-inl.h" #include "runtime-inl.h" #include "verify_object-inl.h" @@ -82,17 +84,17 @@ inline bool IndirectReferenceTable::CheckEntry(const char* what, IndirectRef ire } template<ReadBarrierOption kReadBarrierOption> -inline mirror::Object* IndirectReferenceTable::Get(IndirectRef iref) const { +inline ObjPtr<mirror::Object> IndirectReferenceTable::Get(IndirectRef iref) const { if (!GetChecked(iref)) { return nullptr; } uint32_t idx = ExtractIndex(iref); - mirror::Object* obj = table_[idx].GetReference()->Read<kReadBarrierOption>(); - VerifyObject(obj); + ObjPtr<mirror::Object> obj = table_[idx].GetReference()->Read<kReadBarrierOption>(); + VerifyObject(obj.Ptr()); return obj; } -inline void IndirectReferenceTable::Update(IndirectRef iref, mirror::Object* obj) { +inline void IndirectReferenceTable::Update(IndirectRef iref, ObjPtr<mirror::Object> obj) { if (!GetChecked(iref)) { LOG(WARNING) << "IndirectReferenceTable Update failed to find reference " << iref; return; @@ -101,6 +103,19 @@ inline void IndirectReferenceTable::Update(IndirectRef iref, mirror::Object* obj table_[idx].SetReference(obj); } +inline void IrtEntry::Add(ObjPtr<mirror::Object> obj) { + ++serial_; + if (serial_ == kIRTPrevCount) { + serial_ = 0; + } + references_[serial_] = GcRoot<mirror::Object>(obj); +} + +inline void IrtEntry::SetReference(ObjPtr<mirror::Object> obj) { + DCHECK_LT(serial_, kIRTPrevCount); + references_[serial_] = GcRoot<mirror::Object>(obj); +} + } // namespace art #endif // ART_RUNTIME_INDIRECT_REFERENCE_TABLE_INL_H_ diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index b742ccc4df..d59bb39ccf 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -16,6 +16,7 @@ #include "indirect_reference_table-inl.h" +#include "base/dumpable-inl.h" #include "base/systrace.h" #include "jni_internal.h" #include "nth_caller_visitor.h" @@ -46,32 +47,6 @@ const char* GetIndirectRefKindString(const IndirectRefKind& kind) { return "IndirectRefKind Error"; } -template<typename T> -class MutatorLockedDumpable { - public: - explicit MutatorLockedDumpable(T& value) - REQUIRES_SHARED(Locks::mutator_lock_) : value_(value) { - } - - void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_) { - value_.Dump(os); - } - - private: - T& value_; - - DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable); -}; - -template<typename T> -std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs) -// TODO: should be REQUIRES_SHARED(Locks::mutator_lock_) however annotalysis -// currently fails for this. - NO_THREAD_SAFETY_ANALYSIS { - rhs.Dump(os); - return os; -} - void IndirectReferenceTable::AbortIfNoCheckJNI(const std::string& msg) { // If -Xcheck:jni is on, it'll give a more detailed error before aborting. JavaVMExt* vm = Runtime::Current()->GetJavaVM(); @@ -118,13 +93,13 @@ bool IndirectReferenceTable::IsValid() const { return table_mem_map_.get() != nullptr; } -IndirectRef IndirectReferenceTable::Add(uint32_t cookie, mirror::Object* obj) { +IndirectRef IndirectReferenceTable::Add(uint32_t cookie, ObjPtr<mirror::Object> obj) { IRTSegmentState prevState; prevState.all = cookie; size_t topIndex = segment_state_.parts.topIndex; CHECK(obj != nullptr); - VerifyObject(obj); + VerifyObject(obj.Ptr()); DCHECK(table_ != nullptr); DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles); @@ -171,9 +146,9 @@ IndirectRef IndirectReferenceTable::Add(uint32_t cookie, mirror::Object* obj) { void IndirectReferenceTable::AssertEmpty() { for (size_t i = 0; i < Capacity(); ++i) { if (!table_[i].GetReference()->IsNull()) { - ScopedObjectAccess soa(Thread::Current()); LOG(FATAL) << "Internal Error: non-empty local reference table\n" << MutatorLockedDumpable<IndirectReferenceTable>(*this); + UNREACHABLE(); } } } @@ -299,7 +274,7 @@ void IndirectReferenceTable::Dump(std::ostream& os) const { os << kind_ << " table dump:\n"; ReferenceTable::Table entries; for (size_t i = 0; i < Capacity(); ++i) { - mirror::Object* obj = table_[i].GetReference()->Read<kWithoutReadBarrier>(); + ObjPtr<mirror::Object> obj = table_[i].GetReference()->Read<kWithoutReadBarrier>(); if (obj != nullptr) { obj = table_[i].GetReference()->Read(); entries.push_back(GcRoot<mirror::Object>(obj)); diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h index e194f792b5..64de7a86ef 100644 --- a/runtime/indirect_reference_table.h +++ b/runtime/indirect_reference_table.h @@ -25,6 +25,7 @@ #include "base/logging.h" #include "base/mutex.h" #include "gc_root.h" +#include "obj_ptr.h" #include "object_callbacks.h" #include "offsets.h" #include "read_barrier_option.h" @@ -200,24 +201,18 @@ union IRTSegmentState { static const size_t kIRTPrevCount = kIsDebugBuild ? 7 : 3; class IrtEntry { public: - void Add(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { - ++serial_; - if (serial_ == kIRTPrevCount) { - serial_ = 0; - } - references_[serial_] = GcRoot<mirror::Object>(obj); - } + void Add(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_); + GcRoot<mirror::Object>* GetReference() { DCHECK_LT(serial_, kIRTPrevCount); return &references_[serial_]; } + uint32_t GetSerial() const { return serial_; } - void SetReference(mirror::Object* obj) { - DCHECK_LT(serial_, kIRTPrevCount); - references_[serial_] = GcRoot<mirror::Object>(obj); - } + + void SetReference(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_); private: uint32_t serial_; @@ -237,7 +232,7 @@ class IrtIterator { return *this; } - GcRoot<mirror::Object>* operator*() { + GcRoot<mirror::Object>* operator*() REQUIRES_SHARED(Locks::mutator_lock_) { // This does not have a read barrier as this is used to visit roots. return table_[i_].GetReference(); } @@ -277,7 +272,7 @@ class IndirectReferenceTable { * Returns nullptr if the table is full (max entries reached, or alloc * failed during expansion). */ - IndirectRef Add(uint32_t cookie, mirror::Object* obj) + IndirectRef Add(uint32_t cookie, ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_); /* @@ -286,12 +281,13 @@ class IndirectReferenceTable { * Returns kInvalidIndirectRefObject if iref is invalid. */ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier> - mirror::Object* Get(IndirectRef iref) const REQUIRES_SHARED(Locks::mutator_lock_) + ObjPtr<mirror::Object> Get(IndirectRef iref) const REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE; // Synchronized get which reads a reference, acquiring a lock if necessary. template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier> - mirror::Object* SynchronizedGet(IndirectRef iref) const REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<mirror::Object> SynchronizedGet(IndirectRef iref) const + REQUIRES_SHARED(Locks::mutator_lock_) { return Get<kReadBarrierOption>(iref); } @@ -300,7 +296,7 @@ class IndirectReferenceTable { * * Updates an existing indirect reference to point to a new object. */ - void Update(IndirectRef iref, mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_); + void Update(IndirectRef iref, ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_); /* * Remove an existing entry. @@ -313,7 +309,7 @@ class IndirectReferenceTable { */ bool Remove(uint32_t cookie, IndirectRef iref); - void AssertEmpty(); + void AssertEmpty() REQUIRES_SHARED(Locks::mutator_lock_); void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_); @@ -377,7 +373,7 @@ class IndirectReferenceTable { static void AbortIfNoCheckJNI(const std::string& msg); /* extra debugging checks */ - bool GetChecked(IndirectRef) const; + bool GetChecked(IndirectRef) const REQUIRES_SHARED(Locks::mutator_lock_); bool CheckEntry(const char*, IndirectRef, int) const; /* semi-public - read/write by jni down calls */ diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc index 58d487d14a..0380f3ee7c 100644 --- a/runtime/indirect_reference_table_test.cc +++ b/runtime/indirect_reference_table_test.cc @@ -81,9 +81,9 @@ TEST_F(IndirectReferenceTableTest, BasicTest) { EXPECT_TRUE(iref2 != nullptr); CheckDump(&irt, 3, 3); - EXPECT_EQ(obj0, irt.Get(iref0)); - EXPECT_EQ(obj1, irt.Get(iref1)); - EXPECT_EQ(obj2, irt.Get(iref2)); + EXPECT_OBJ_PTR_EQ(obj0, irt.Get(iref0)); + EXPECT_OBJ_PTR_EQ(obj1, irt.Get(iref1)); + EXPECT_OBJ_PTR_EQ(obj2, irt.Get(iref2)); EXPECT_TRUE(irt.Remove(cookie, iref0)); CheckDump(&irt, 2, 2); diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 6b281107ed..7d54d0a802 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -185,6 +185,13 @@ static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, return false; } const uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + // Debug code for b/31357497. To be removed. + if (kUseReadBarrier) { + CHECK(receiver->GetClass() != nullptr) + << "Null class found in object " << receiver << " in region type " + << Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()-> + RegionSpace()->GetRegionType(receiver); + } CHECK(receiver->GetClass()->ShouldHaveEmbeddedVTable()); ArtMethod* const called_method = receiver->GetClass()->GetEmbeddedVTableEntry( vtable_idx, kRuntimePointerSize); diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 89cbbe6c0e..ac5401f5d9 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -1441,15 +1441,15 @@ void UnstartedRuntime::UnstartedMethodInvoke( JNIEnvExt* env = self->GetJniEnv(); ScopedObjectAccessUnchecked soa(self); - mirror::Object* java_method_obj = shadow_frame->GetVRegReference(arg_offset); + ObjPtr<mirror::Object> java_method_obj = shadow_frame->GetVRegReference(arg_offset); ScopedLocalRef<jobject> java_method(env, java_method_obj == nullptr ? nullptr :env->AddLocalReference<jobject>(java_method_obj)); - mirror::Object* java_receiver_obj = shadow_frame->GetVRegReference(arg_offset + 1); + ObjPtr<mirror::Object> java_receiver_obj = shadow_frame->GetVRegReference(arg_offset + 1); ScopedLocalRef<jobject> java_receiver(env, java_receiver_obj == nullptr ? nullptr : env->AddLocalReference<jobject>(java_receiver_obj)); - mirror::Object* java_args_obj = shadow_frame->GetVRegReference(arg_offset + 2); + ObjPtr<mirror::Object> java_args_obj = shadow_frame->GetVRegReference(arg_offset + 2); ScopedLocalRef<jobject> java_args(env, java_args_obj == nullptr ? nullptr : env->AddLocalReference<jobject>(java_args_obj)); diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index 215f2b3ddf..101c146072 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -538,7 +538,7 @@ jobject JavaVMExt::AddGlobalRef(Thread* self, ObjPtr<mirror::Object> obj) { return nullptr; } WriterMutexLock mu(self, globals_lock_); - IndirectRef ref = globals_.Add(IRT_FIRST_SEGMENT, obj.Ptr()); + IndirectRef ref = globals_.Add(IRT_FIRST_SEGMENT, obj); return reinterpret_cast<jobject>(ref); } @@ -550,7 +550,7 @@ jweak JavaVMExt::AddWeakGlobalRef(Thread* self, ObjPtr<mirror::Object> obj) { while (UNLIKELY(!MayAccessWeakGlobals(self))) { weak_globals_add_condition_.WaitHoldingLocks(self); } - IndirectRef ref = weak_globals_.Add(IRT_FIRST_SEGMENT, obj.Ptr()); + IndirectRef ref = weak_globals_.Add(IRT_FIRST_SEGMENT, obj); return reinterpret_cast<jweak>(ref); } @@ -641,7 +641,7 @@ void JavaVMExt::BroadcastForNewWeakGlobals() { } mirror::Object* JavaVMExt::DecodeGlobal(IndirectRef ref) { - return globals_.SynchronizedGet(ref); + return globals_.SynchronizedGet(ref).Ptr(); } void JavaVMExt::UpdateGlobal(Thread* self, IndirectRef ref, mirror::Object* result) { @@ -669,7 +669,7 @@ mirror::Object* JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) { // if MayAccessWeakGlobals is false. DCHECK_EQ(GetIndirectRefKind(ref), kWeakGlobal); if (LIKELY(MayAccessWeakGlobalsUnlocked(self))) { - return weak_globals_.SynchronizedGet(ref); + return weak_globals_.SynchronizedGet(ref).Ptr(); } MutexLock mu(self, weak_globals_lock_); return DecodeWeakGlobalLocked(self, ref); @@ -682,7 +682,7 @@ mirror::Object* JavaVMExt::DecodeWeakGlobalLocked(Thread* self, IndirectRef ref) while (UNLIKELY(!MayAccessWeakGlobals(self))) { weak_globals_add_condition_.WaitHoldingLocks(self); } - return weak_globals_.Get(ref); + return weak_globals_.Get(ref).Ptr(); } mirror::Object* JavaVMExt::DecodeWeakGlobalDuringShutdown(Thread* self, IndirectRef ref) { @@ -695,7 +695,7 @@ mirror::Object* JavaVMExt::DecodeWeakGlobalDuringShutdown(Thread* self, Indirect if (!kUseReadBarrier) { DCHECK(allow_accessing_weak_globals_.LoadSequentiallyConsistent()); } - return weak_globals_.SynchronizedGet(ref); + return weak_globals_.SynchronizedGet(ref).Ptr(); } bool JavaVMExt::IsWeakGlobalCleared(Thread* self, IndirectRef ref) { diff --git a/runtime/jni_env_ext-inl.h b/runtime/jni_env_ext-inl.h index 685b056e8d..2cc7342ff8 100644 --- a/runtime/jni_env_ext-inl.h +++ b/runtime/jni_env_ext-inl.h @@ -19,12 +19,14 @@ #include "jni_env_ext.h" +#include "indirect_reference_table-inl.h" +#include "obj_ptr-inl.h" #include "utils.h" namespace art { template<typename T> -inline T JNIEnvExt::AddLocalReference(mirror::Object* obj) { +inline T JNIEnvExt::AddLocalReference(ObjPtr<mirror::Object> obj) { IndirectRef ref = locals.Add(local_ref_cookie, obj); // TODO: fix this to understand PushLocalFrame, so we can turn it on. diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h index 79dfb0d41d..121f84854b 100644 --- a/runtime/jni_env_ext.h +++ b/runtime/jni_env_ext.h @@ -47,8 +47,7 @@ struct JNIEnvExt : public JNIEnv { void PopFrame() REQUIRES_SHARED(Locks::mutator_lock_); template<typename T> - T AddLocalReference(mirror::Object* obj) - REQUIRES_SHARED(Locks::mutator_lock_); + T AddLocalReference(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_); static Offset SegmentStateOffset(size_t pointer_size); static Offset LocalRefCookieOffset(size_t pointer_size); diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 1cfed741eb..cc088b8aa8 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -448,7 +448,7 @@ inline bool Class::CanAccessResolvedField(ObjPtr<Class> access_to, inline bool Class::CheckResolvedFieldAccess(ObjPtr<Class> access_to, ArtField* field, uint32_t field_idx) { - return ResolvedFieldAccessTest<true, true>(access_to.Ptr(), field, field_idx, nullptr); + return ResolvedFieldAccessTest<true, true>(access_to, field, field_idx, nullptr); } inline bool Class::CanAccessResolvedMethod(Class* access_to, ArtMethod* method, diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 87bff5ffc5..2a5c04d54b 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -677,11 +677,10 @@ static jobject Class_newInstance(JNIEnv* env, jobject javaThis) { if (caller.Get() == nullptr) { caller.Assign(GetCallingClass(soa.Self(), 1)); } - if (UNLIKELY(caller.Get() != nullptr && !VerifyAccess( - MakeObjPtr(receiver.Get()), - MakeObjPtr(declaring_class), - constructor->GetAccessFlags(), - MakeObjPtr(caller.Get())))) { + if (UNLIKELY(caller.Get() != nullptr && !VerifyAccess(receiver.Get(), + declaring_class, + constructor->GetAccessFlags(), + caller.Get()))) { soa.Self()->ThrowNewExceptionF( "Ljava/lang/IllegalAccessException;", "%s is not accessible from %s", PrettyMethod(constructor).c_str(), PrettyClass(caller.Get()).c_str()); diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 07b59dd907..90def4438f 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -31,8 +31,9 @@ namespace art { template<bool kIsSet> -ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self, mirror::Field* field, - mirror::Object* obj) +ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self, + ObjPtr<mirror::Field> field, + ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) { if (kIsSet && field->IsFinal()) { ThrowIllegalAccessException( @@ -45,8 +46,8 @@ ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self, mirror::Field* } ObjPtr<mirror::Class> calling_class; if (!VerifyAccess(self, - MakeObjPtr(obj), - MakeObjPtr(field->GetDeclaringClass()), + obj, + field->GetDeclaringClass(), field->GetAccessFlags(), &calling_class, 1)) { @@ -63,8 +64,10 @@ ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self, mirror::Field* } template<bool kAllowReferences> -ALWAYS_INLINE inline static bool GetFieldValue(mirror::Object* o, mirror::Field* f, - Primitive::Type field_type, JValue* value) +ALWAYS_INLINE inline static bool GetFieldValue(ObjPtr<mirror::Object> o, + ObjPtr<mirror::Field> f, + Primitive::Type field_type, + JValue* value) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_EQ(value->GetJ(), INT64_C(0)); MemberOffset offset(f->GetOffset()); @@ -108,27 +111,28 @@ ALWAYS_INLINE inline static bool GetFieldValue(mirror::Object* o, mirror::Field* } ALWAYS_INLINE inline static bool CheckReceiver(const ScopedFastNativeObjectAccess& soa, - jobject j_rcvr, mirror::Field** f, - mirror::Object** class_or_rcvr) + jobject j_rcvr, + ObjPtr<mirror::Field>* f, + ObjPtr<mirror::Object>* class_or_rcvr) REQUIRES_SHARED(Locks::mutator_lock_) { soa.Self()->AssertThreadSuspensionIsAllowable(); - mirror::Class* declaringClass = (*f)->GetDeclaringClass(); + ObjPtr<mirror::Class> declaring_class = (*f)->GetDeclaringClass(); if ((*f)->IsStatic()) { - if (UNLIKELY(!declaringClass->IsInitialized())) { + if (UNLIKELY(!declaring_class->IsInitialized())) { StackHandleScope<2> hs(soa.Self()); - HandleWrapper<mirror::Field> h_f(hs.NewHandleWrapper(f)); - HandleWrapper<mirror::Class> h_klass(hs.NewHandleWrapper(&declaringClass)); + HandleWrapperObjPtr<mirror::Field> h_f(hs.NewHandleWrapper(f)); + HandleWrapperObjPtr<mirror::Class> h_klass(hs.NewHandleWrapper(&declaring_class)); ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); if (UNLIKELY(!class_linker->EnsureInitialized(soa.Self(), h_klass, true, true))) { DCHECK(soa.Self()->IsExceptionPending()); return false; } } - *class_or_rcvr = declaringClass; + *class_or_rcvr = declaring_class; return true; } - *class_or_rcvr = soa.Decode<mirror::Object>(j_rcvr).Ptr(); - if (!VerifyObjectIsClass(MakeObjPtr(*class_or_rcvr), MakeObjPtr(declaringClass))) { + *class_or_rcvr = soa.Decode<mirror::Object>(j_rcvr); + if (!VerifyObjectIsClass(*class_or_rcvr, declaring_class)) { DCHECK(soa.Self()->IsExceptionPending()); return false; } @@ -137,8 +141,8 @@ ALWAYS_INLINE inline static bool CheckReceiver(const ScopedFastNativeObjectAcces static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) { ScopedFastNativeObjectAccess soa(env); - mirror::Field* f = soa.Decode<mirror::Field>(javaField).Ptr(); - mirror::Object* o = nullptr; + ObjPtr<mirror::Field> f = soa.Decode<mirror::Field>(javaField); + ObjPtr<mirror::Object> o; if (!CheckReceiver(soa, javaObj, &f, &o)) { DCHECK(soa.Self()->IsExceptionPending()); return nullptr; @@ -156,15 +160,16 @@ static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) { DCHECK(soa.Self()->IsExceptionPending()); return nullptr; } - return soa.AddLocalReference<jobject>(BoxPrimitive(field_type, value).Ptr()); + return soa.AddLocalReference<jobject>(BoxPrimitive(field_type, value)); } template<Primitive::Type kPrimitiveType> -ALWAYS_INLINE inline static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, +ALWAYS_INLINE inline static JValue GetPrimitiveField(JNIEnv* env, + jobject javaField, jobject javaObj) { ScopedFastNativeObjectAccess soa(env); - mirror::Field* f = soa.Decode<mirror::Field>(javaField).Ptr(); - mirror::Object* o = nullptr; + ObjPtr<mirror::Field> f = soa.Decode<mirror::Field>(javaField); + ObjPtr<mirror::Object> o; if (!CheckReceiver(soa, javaObj, &f, &o)) { DCHECK(soa.Self()->IsExceptionPending()); return JValue(); @@ -234,8 +239,10 @@ static jshort Field_getShort(JNIEnv* env, jobject javaField, jobject javaObj) { return GetPrimitiveField<Primitive::kPrimShort>(env, javaField, javaObj).GetS(); } -ALWAYS_INLINE inline static void SetFieldValue(mirror::Object* o, mirror::Field* f, - Primitive::Type field_type, bool allow_references, +ALWAYS_INLINE inline static void SetFieldValue(ObjPtr<mirror::Object> o, + ObjPtr<mirror::Field> f, + Primitive::Type field_type, + bool allow_references, const JValue& new_value) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(f->GetDeclaringClass()->IsInitialized()); @@ -307,14 +314,14 @@ ALWAYS_INLINE inline static void SetFieldValue(mirror::Object* o, mirror::Field* static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue) { ScopedFastNativeObjectAccess soa(env); - mirror::Field* f = soa.Decode<mirror::Field>(javaField).Ptr(); + ObjPtr<mirror::Field> f = soa.Decode<mirror::Field>(javaField); // Check that the receiver is non-null and an instance of the field's declaring class. - mirror::Object* o = nullptr; + ObjPtr<mirror::Object> o; if (!CheckReceiver(soa, javaObj, &f, &o)) { DCHECK(soa.Self()->IsExceptionPending()); return; } - mirror::Class* field_type; + ObjPtr<mirror::Class> field_type; const char* field_type_desciptor = f->GetArtField()->GetTypeDescriptor(); Primitive::Type field_prim_type = Primitive::GetType(field_type_desciptor[0]); if (field_prim_type == Primitive::kPrimNot) { @@ -328,7 +335,7 @@ static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject j ObjPtr<mirror::Object> boxed_value = soa.Decode<mirror::Object>(javaValue); JValue unboxed_value; if (!UnboxPrimitiveForField(boxed_value, - MakeObjPtr(field_type), + field_type, f->GetArtField(), &unboxed_value)) { DCHECK(soa.Self()->IsExceptionPending()); @@ -343,11 +350,13 @@ static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject j } template<Primitive::Type kPrimitiveType> -static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, +static void SetPrimitiveField(JNIEnv* env, + jobject javaField, + jobject javaObj, const JValue& new_value) { ScopedFastNativeObjectAccess soa(env); - mirror::Field* f = soa.Decode<mirror::Field>(javaField).Ptr(); - mirror::Object* o = nullptr; + ObjPtr<mirror::Field> f = soa.Decode<mirror::Field>(javaField); + ObjPtr<mirror::Object> o; if (!CheckReceiver(soa, javaObj, &f, &o)) { return; } @@ -439,11 +448,10 @@ static jobjectArray Field_getDeclaredAnnotations(JNIEnv* env, jobject javaField) ArtField* field = soa.Decode<mirror::Field>(javaField)->GetArtField(); if (field->GetDeclaringClass()->IsProxyClass()) { // Return an empty array instead of a null pointer. - mirror::Class* annotation_array_class = - soa.Decode<mirror::Class>( - WellKnownClasses::java_lang_annotation_Annotation__array).Ptr(); - mirror::ObjectArray<mirror::Object>* empty_array = - mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0); + ObjPtr<mirror::Class> annotation_array_class = + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array); + ObjPtr<mirror::ObjectArray<mirror::Object>> empty_array = + mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class.Ptr(), 0); return soa.AddLocalReference<jobjectArray>(empty_array); } return soa.AddLocalReference<jobjectArray>(annotations::GetAnnotationsForField(field)); @@ -458,7 +466,8 @@ static jobjectArray Field_getSignatureAnnotation(JNIEnv* env, jobject javaField) return soa.AddLocalReference<jobjectArray>(annotations::GetSignatureAnnotationForField(field)); } -static jboolean Field_isAnnotationPresentNative(JNIEnv* env, jobject javaField, +static jboolean Field_isAnnotationPresentNative(JNIEnv* env, + jobject javaField, jclass annotationType) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h index beb4d33a11..6688ba77ee 100644 --- a/runtime/obj_ptr.h +++ b/runtime/obj_ptr.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_OBJ_PTR_H_ #include <ostream> +#include <type_traits> #include "base/mutex.h" // For Locks::mutator_lock_. #include "globals.h" @@ -45,14 +46,23 @@ class ObjPtr { template <typename Type> ALWAYS_INLINE ObjPtr(Type* ptr) REQUIRES_SHARED(Locks::mutator_lock_) - : reference_(Encode(static_cast<MirrorType*>(ptr))) {} + : reference_(Encode(static_cast<MirrorType*>(ptr))) { + static_assert(std::is_base_of<MirrorType, Type>::value, + "Input type must be a subtype of the ObjPtr type"); + } template <typename Type> - ALWAYS_INLINE ObjPtr(const ObjPtr<Type>& other) REQUIRES_SHARED(Locks::mutator_lock_) - : reference_(Encode(static_cast<MirrorType*>(other.Ptr()))) {} + ALWAYS_INLINE ObjPtr(const ObjPtr<Type, kPoison>& other) REQUIRES_SHARED(Locks::mutator_lock_) + : reference_(Encode(static_cast<MirrorType*>(other.Ptr()))) { + static_assert(std::is_base_of<MirrorType, Type>::value, + "Input type must be a subtype of the ObjPtr type"); + } template <typename Type> - ALWAYS_INLINE ObjPtr& operator=(const ObjPtr& other) { + ALWAYS_INLINE ObjPtr& operator=(const ObjPtr<Type, kPoison>& other) + REQUIRES_SHARED(Locks::mutator_lock_) { + static_assert(std::is_base_of<MirrorType, Type>::value, + "Input type must be a subtype of the ObjPtr type"); reference_ = Encode(static_cast<MirrorType*>(other.Ptr())); return *this; } diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index a1a23619f3..bebc2fc396 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -38,7 +38,11 @@ #include "art_jvmti.h" #include "jni_env_ext-inl.h" +#include "object_tagging.h" +#include "obj_ptr-inl.h" #include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "thread_list.h" #include "transform.h" // TODO Remove this at some point by annotating all the methods. It was put in to make the skeleton @@ -47,6 +51,8 @@ namespace openjdkjvmti { +ObjectTagTable gObjectTagTable; + class JvmtiFunctions { private: static bool IsValidEnv(jvmtiEnv* env) { @@ -270,11 +276,42 @@ class JvmtiFunctions { } static jvmtiError GetTag(jvmtiEnv* env, jobject object, jlong* tag_ptr) { - return ERR(NOT_IMPLEMENTED); + if (object == nullptr || tag_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + JNIEnv* jni_env = GetJniEnv(env); + if (jni_env == nullptr) { + return ERR(INTERNAL); + } + + art::ScopedObjectAccess soa(jni_env); + art::ObjPtr<art::mirror::Object> obj = soa.Decode<art::mirror::Object>(object); + if (!gObjectTagTable.GetTag(obj.Ptr(), tag_ptr)) { + *tag_ptr = 0; + } + + return ERR(NONE); } static jvmtiError SetTag(jvmtiEnv* env, jobject object, jlong tag) { - return ERR(NOT_IMPLEMENTED); + if (object == nullptr) { + return ERR(NULL_POINTER); + } + + JNIEnv* jni_env = GetJniEnv(env); + if (jni_env == nullptr) { + return ERR(INTERNAL); + } + + art::ScopedObjectAccess soa(jni_env); + art::ObjPtr<art::mirror::Object> obj = soa.Decode<art::mirror::Object>(object); + gObjectTagTable.Remove(obj.Ptr(), /* tag* */ nullptr); + if (tag != 0) { + gObjectTagTable.Add(obj.Ptr(), tag); + } + + return ERR(NONE); } static jvmtiError GetObjectsWithTags(jvmtiEnv* env, diff --git a/runtime/openjdkjvmti/object_tagging.h b/runtime/openjdkjvmti/object_tagging.h new file mode 100644 index 0000000000..e276c524ca --- /dev/null +++ b/runtime/openjdkjvmti/object_tagging.h @@ -0,0 +1,137 @@ +/* + * 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_RUNTIME_OPENJDKJVMTI_OBJECT_TAGGING_H_ +#define ART_RUNTIME_OPENJDKJVMTI_OBJECT_TAGGING_H_ + +#include "base/mutex.h" +#include "gc/system_weak.h" +#include "gc_root-inl.h" +#include "mirror/object.h" +#include "thread-inl.h" + +namespace openjdkjvmti { + +class ObjectTagTable : public art::gc::SystemWeakHolder { + public: + ObjectTagTable() : art::gc::SystemWeakHolder(art::LockLevel::kAllocTrackerLock) { + } + + void Add(art::mirror::Object* obj, jlong tag) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!allow_disallow_lock_) { + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, allow_disallow_lock_); + Wait(self); + + if (first_free_ == tagged_objects_.size()) { + tagged_objects_.push_back(Entry(art::GcRoot<art::mirror::Object>(obj), tag)); + first_free_++; + } else { + DCHECK_LT(first_free_, tagged_objects_.size()); + DCHECK(tagged_objects_[first_free_].first.IsNull()); + tagged_objects_[first_free_] = Entry(art::GcRoot<art::mirror::Object>(obj), tag); + // TODO: scan for free elements. + first_free_ = tagged_objects_.size(); + } + } + + bool GetTag(art::mirror::Object* obj, jlong* result) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!allow_disallow_lock_) { + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, allow_disallow_lock_); + Wait(self); + + for (const auto& pair : tagged_objects_) { + if (pair.first.Read(nullptr) == obj) { + *result = pair.second; + return true; + } + } + + return false; + } + + bool Remove(art::mirror::Object* obj, jlong* tag) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!allow_disallow_lock_) { + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, allow_disallow_lock_); + Wait(self); + + for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) { + if (it->first.Read(nullptr) == obj) { + if (tag != nullptr) { + *tag = it->second; + } + it->first = art::GcRoot<art::mirror::Object>(nullptr); + + size_t index = it - tagged_objects_.begin(); + if (index < first_free_) { + first_free_ = index; + } + + // TODO: compaction. + + return true; + } + } + + return false; + } + + void Sweep(art::IsMarkedVisitor* visitor) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!allow_disallow_lock_) { + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, allow_disallow_lock_); + + for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) { + if (!it->first.IsNull()) { + art::mirror::Object* original_obj = it->first.Read(); + art::mirror::Object* target_obj = visitor->IsMarked(original_obj); + if (original_obj != target_obj) { + it->first = art::GcRoot<art::mirror::Object>(target_obj); + + if (target_obj == nullptr) { + HandleNullSweep(it->second); + } + } + } else { + size_t index = it - tagged_objects_.begin(); + if (index < first_free_) { + first_free_ = index; + } + } + } + } + + private: + using Entry = std::pair<art::GcRoot<art::mirror::Object>, jlong>; + + void HandleNullSweep(jlong tag ATTRIBUTE_UNUSED) { + // TODO: Handle deallocation reporting here. We'll have to enqueue tags temporarily, as we + // probably shouldn't call the callbacks directly (to avoid any issues). + } + + std::vector<Entry> tagged_objects_ GUARDED_BY(allow_disallow_lock_); + size_t first_free_ = 0; +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_OBJECT_TAGGING_H_ diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index 1119ccfc80..84985c2997 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -180,7 +180,7 @@ TEST_F(ProxyTest, ProxyFieldHelper) { ArtField* field = &static_fields->At(0); EXPECT_STREQ("interfaces", field->GetName()); EXPECT_STREQ("[Ljava/lang/Class;", field->GetTypeDescriptor()); - EXPECT_OBJ_PTR_EQ(MakeObjPtr(interfacesFieldClass.Get()), field->GetType<true>()); + EXPECT_OBJ_PTR_EQ(interfacesFieldClass.Get(), field->GetType<true>()); std::string temp; EXPECT_STREQ("L$Proxy1234;", field->GetDeclaringClass()->GetDescriptor(&temp)); EXPECT_FALSE(field->IsPrimitiveType()); @@ -189,7 +189,7 @@ TEST_F(ProxyTest, ProxyFieldHelper) { field = &static_fields->At(1); EXPECT_STREQ("throws", field->GetName()); EXPECT_STREQ("[[Ljava/lang/Class;", field->GetTypeDescriptor()); - EXPECT_OBJ_PTR_EQ(MakeObjPtr(throwsFieldClass.Get()), field->GetType<true>()); + EXPECT_OBJ_PTR_EQ(throwsFieldClass.Get(), field->GetType<true>()); EXPECT_STREQ("L$Proxy1234;", field->GetDeclaringClass()->GetDescriptor(&temp)); EXPECT_FALSE(field->IsPrimitiveType()); } @@ -224,10 +224,10 @@ TEST_F(ProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) { ASSERT_TRUE(static_fields1 != nullptr); ASSERT_EQ(2u, static_fields1->size()); - EXPECT_OBJ_PTR_EQ(static_fields0->At(0).GetDeclaringClass(), MakeObjPtr(proxyClass0.Get())); - EXPECT_OBJ_PTR_EQ(static_fields0->At(1).GetDeclaringClass(), MakeObjPtr(proxyClass0.Get())); - EXPECT_OBJ_PTR_EQ(static_fields1->At(0).GetDeclaringClass(), MakeObjPtr(proxyClass1.Get())); - EXPECT_OBJ_PTR_EQ(static_fields1->At(1).GetDeclaringClass(), MakeObjPtr(proxyClass1.Get())); + EXPECT_OBJ_PTR_EQ(static_fields0->At(0).GetDeclaringClass(), proxyClass0.Get()); + EXPECT_OBJ_PTR_EQ(static_fields0->At(1).GetDeclaringClass(), proxyClass0.Get()); + EXPECT_OBJ_PTR_EQ(static_fields1->At(0).GetDeclaringClass(), proxyClass1.Get()); + EXPECT_OBJ_PTR_EQ(static_fields1->At(1).GetDeclaringClass(), proxyClass1.Get()); ASSERT_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); ASSERT_FALSE(Runtime::Current()->IsActiveTransaction()); diff --git a/runtime/reflection.cc b/runtime/reflection.cc index de003e525b..d34b701e3e 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -676,8 +676,7 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM } // Box if necessary and return. - return soa.AddLocalReference<jobject>( - BoxPrimitive(Primitive::GetType(shorty[0]), result).Ptr()); + return soa.AddLocalReference<jobject>(BoxPrimitive(Primitive::GetType(shorty[0]), result)); } ObjPtr<mirror::Object> BoxPrimitive(Primitive::Type src_class, const JValue& value) { @@ -911,7 +910,7 @@ void UpdateReference(Thread* self, jobject obj, ObjPtr<mirror::Object> result) { IndirectRef ref = reinterpret_cast<IndirectRef>(obj); IndirectRefKind kind = GetIndirectRefKind(ref); if (kind == kLocal) { - self->GetJniEnv()->locals.Update(obj, result.Ptr()); + self->GetJniEnv()->locals.Update(obj, result); } else if (kind == kHandleScopeOrInvalid) { LOG(FATAL) << "Unsupported UpdateReference for kind kHandleScopeOrInvalid"; } else if (kind == kGlobal) { diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h index 2eb0bf75a1..8346550c93 100644 --- a/runtime/runtime-inl.h +++ b/runtime/runtime-inl.h @@ -21,11 +21,12 @@ #include "art_method.h" #include "class_linker.h" +#include "obj_ptr-inl.h" #include "read_barrier-inl.h" namespace art { -inline bool Runtime::IsClearedJniWeakGlobal(mirror::Object* obj) { +inline bool Runtime::IsClearedJniWeakGlobal(ObjPtr<mirror::Object> obj) { return obj == GetClearedJniWeakGlobal(); } diff --git a/runtime/runtime.h b/runtime/runtime.h index 84c6b6f247..5a95f78cad 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -33,6 +33,7 @@ #include "instrumentation.h" #include "jobject_comparator.h" #include "method_reference.h" +#include "obj_ptr.h" #include "object_callbacks.h" #include "offsets.h" #include "process_state.h" @@ -292,7 +293,7 @@ class Runtime { } // Is the given object the special object used to mark a cleared JNI weak global? - bool IsClearedJniWeakGlobal(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_); + bool IsClearedJniWeakGlobal(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_); // Get the special object used to mark a cleared JNI weak global. mirror::Object* GetClearedJniWeakGlobal() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h index 1d9f13246a..ac2575715e 100644 --- a/runtime/scoped_thread_state_change-inl.h +++ b/runtime/scoped_thread_state_change-inl.h @@ -72,19 +72,13 @@ inline ScopedThreadStateChange::~ScopedThreadStateChange() { } template<typename T> -inline T ScopedObjectAccessAlreadyRunnable::AddLocalReference(mirror::Object* obj) const { +inline T ScopedObjectAccessAlreadyRunnable::AddLocalReference(ObjPtr<mirror::Object> obj) const { Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK(IsRunnable()); // Don't work with raw objects in non-runnable states. DCHECK_NE(obj, Runtime::Current()->GetClearedJniWeakGlobal()); return obj == nullptr ? nullptr : Env()->AddLocalReference<T>(obj); } -template<typename T, typename MirrorType, bool kPoison> -inline T ScopedObjectAccessAlreadyRunnable::AddLocalReference( - ObjPtr<MirrorType, kPoison> obj) const { - return AddLocalReference<T>(obj.Ptr()); -} - template<typename T, bool kPoison> inline ObjPtr<T, kPoison> ScopedObjectAccessAlreadyRunnable::Decode(jobject obj) const { Locks::mutator_lock_->AssertSharedHeld(Self()); diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h index 175bec51d6..04fd9141ea 100644 --- a/runtime/scoped_thread_state_change.h +++ b/runtime/scoped_thread_state_change.h @@ -87,13 +87,8 @@ class ScopedObjectAccessAlreadyRunnable : public ValueObject { * This will be called on otherwise unreferenced objects. We cannot do GC allocations here, and * it's best if we don't grab a mutex. */ - template<typename T, typename MirrorType, bool kPoison = kIsDebugBuild> - T AddLocalReference(ObjPtr<MirrorType, kPoison> obj) const - REQUIRES_SHARED(Locks::mutator_lock_); - - // TODO: Delete template<typename T> - T AddLocalReference(mirror::Object* obj) const + T AddLocalReference(ObjPtr<mirror::Object> obj) const REQUIRES_SHARED(Locks::mutator_lock_); template<typename T, bool kPoison = kIsDebugBuild> diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index d75a788e96..6c3811b8e5 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -312,6 +312,37 @@ inline void Thread::PoisonObjectPointersIfDebug() { } } +inline bool Thread::ModifySuspendCount(Thread* self, + int delta, + AtomicInteger* suspend_barrier, + bool for_debugger) { + if (delta > 0 && ((kUseReadBarrier && this != self) || suspend_barrier != nullptr)) { + // When delta > 0 (requesting a suspend), ModifySuspendCountInternal() may fail either if + // active_suspend_barriers is full or we are in the middle of a thread flip. Retry in a loop. + while (true) { + if (LIKELY(ModifySuspendCountInternal(self, delta, suspend_barrier, for_debugger))) { + return true; + } else { + // Failure means the list of active_suspend_barriers is full or we are in the middle of a + // thread flip, we should release the thread_suspend_count_lock_ (to avoid deadlock) and + // wait till the target thread has executed or Thread::PassActiveSuspendBarriers() or the + // flip function. Note that we could not simply wait for the thread to change to a suspended + // state, because it might need to run checkpoint function before the state change or + // resumes from the resume_cond_, which also needs thread_suspend_count_lock_. + // + // The list of active_suspend_barriers is very unlikely to be full since more than + // kMaxSuspendBarriers threads need to execute SuspendAllInternal() simultaneously, and + // target thread stays in kRunnable in the mean time. + Locks::thread_suspend_count_lock_->ExclusiveUnlock(self); + NanoSleep(100000); + Locks::thread_suspend_count_lock_->ExclusiveLock(self); + } + } + } else { + return ModifySuspendCountInternal(self, delta, suspend_barrier, for_debugger); + } +} + } // namespace art #endif // ART_RUNTIME_THREAD_INL_H_ diff --git a/runtime/thread.cc b/runtime/thread.cc index 80542e8b55..7335e409bf 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -991,8 +991,10 @@ static void UnsafeLogFatalForSuspendCount(Thread* self, Thread* thread) NO_THREA LOG(FATAL) << ss.str(); } -bool Thread::ModifySuspendCount(Thread* self, int delta, AtomicInteger* suspend_barrier, - bool for_debugger) { +bool Thread::ModifySuspendCountInternal(Thread* self, + int delta, + AtomicInteger* suspend_barrier, + bool for_debugger) { if (kIsDebugBuild) { DCHECK(delta == -1 || delta == +1 || delta == -tls32_.debug_suspend_count) << delta << " " << tls32_.debug_suspend_count << " " << this; @@ -1007,6 +1009,12 @@ bool Thread::ModifySuspendCount(Thread* self, int delta, AtomicInteger* suspend_ return false; } + if (kUseReadBarrier && delta > 0 && this != self && tlsPtr_.flip_function != nullptr) { + // Force retry of a suspend request if it's in the middle of a thread flip to avoid a + // deadlock. b/31683379. + return false; + } + uint16_t flags = kSuspendRequest; if (delta > 0 && suspend_barrier != nullptr) { uint32_t available_barrier = kMaxSuspendBarriers; @@ -1854,7 +1862,7 @@ mirror::Object* Thread::DecodeJObject(jobject obj) const { } IndirectRef ref = reinterpret_cast<IndirectRef>(obj); IndirectRefKind kind = GetIndirectRefKind(ref); - mirror::Object* result; + ObjPtr<mirror::Object> result; bool expect_null = false; // The "kinds" below are sorted by the frequency we expect to encounter them. if (kind == kLocal) { @@ -1867,7 +1875,7 @@ mirror::Object* Thread::DecodeJObject(jobject obj) const { if (LIKELY(HandleScopeContains(obj))) { // Read from handle scope. result = reinterpret_cast<StackReference<mirror::Object>*>(obj)->AsMirrorPtr(); - VerifyObject(result); + VerifyObject(result.Ptr()); } else { tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of invalid jobject %p", obj); expect_null = true; @@ -1889,7 +1897,7 @@ mirror::Object* Thread::DecodeJObject(jobject obj) const { tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of deleted %s %p", ToStr<IndirectRefKind>(kind).c_str(), obj); } - return result; + return result.Ptr(); } bool Thread::IsJWeakCleared(jweak obj) const { diff --git a/runtime/thread.h b/runtime/thread.h index f2c22d17a9..97053decc0 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -226,7 +226,13 @@ class Thread { (state_and_flags.as_struct.flags & kSuspendRequest) != 0; } - bool ModifySuspendCount(Thread* self, int delta, AtomicInteger* suspend_barrier, bool for_debugger) + // If delta > 0 and (this != self or suspend_barrier is not null), this function may temporarily + // release thread_suspend_count_lock_ internally. + ALWAYS_INLINE + bool ModifySuspendCount(Thread* self, + int delta, + AtomicInteger* suspend_barrier, + bool for_debugger) REQUIRES(Locks::thread_suspend_count_lock_); bool RequestCheckpoint(Closure* function) @@ -1220,6 +1226,12 @@ class Thread { is_sensitive_thread_hook_ = is_sensitive_thread_hook; } + bool ModifySuspendCountInternal(Thread* self, + int delta, + AtomicInteger* suspend_barrier, + bool for_debugger) + REQUIRES(Locks::thread_suspend_count_lock_); + // 32 bits of atomically changed state and flags. Keeping as 32 bits allows and atomic CAS to // change from being Suspended to Runnable without a suspend request occurring. union PACKED(4) StateAndFlags { diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 6f952c462e..eba6666dec 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -590,24 +590,7 @@ void ThreadList::SuspendAllInternal(Thread* self, continue; } VLOG(threads) << "requesting thread suspend: " << *thread; - while (true) { - if (LIKELY(thread->ModifySuspendCount(self, +1, &pending_threads, debug_suspend))) { - break; - } else { - // Failure means the list of active_suspend_barriers is full, we should release the - // thread_suspend_count_lock_ (to avoid deadlock) and wait till the target thread has - // executed Thread::PassActiveSuspendBarriers(). Note that we could not simply wait for - // the thread to change to a suspended state, because it might need to run checkpoint - // function before the state change, which also needs thread_suspend_count_lock_. - - // This is very unlikely to happen since more than kMaxSuspendBarriers threads need to - // execute SuspendAllInternal() simultaneously, and target thread stays in kRunnable - // in the mean time. - Locks::thread_suspend_count_lock_->ExclusiveUnlock(self); - NanoSleep(100000); - Locks::thread_suspend_count_lock_->ExclusiveLock(self); - } - } + thread->ModifySuspendCount(self, +1, &pending_threads, debug_suspend); // Must install the pending_threads counter first, then check thread->IsSuspend() and clear // the counter. Otherwise there's a race with Thread::TransitionFromRunnableToSuspended() diff --git a/test/903-hello-tagging/build b/test/903-hello-tagging/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/903-hello-tagging/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/903-hello-tagging/expected.txt b/test/903-hello-tagging/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/903-hello-tagging/expected.txt diff --git a/test/903-hello-tagging/info.txt b/test/903-hello-tagging/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/903-hello-tagging/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/903-hello-tagging/run b/test/903-hello-tagging/run new file mode 100755 index 0000000000..5e3c0bd32a --- /dev/null +++ b/test/903-hello-tagging/run @@ -0,0 +1,43 @@ +#!/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. + +plugin=libopenjdkjvmtid.so +agent=libtiagentd.so +lib=tiagentd +if [[ "$@" == *"-O"* ]]; then + agent=libtiagent.so + plugin=libopenjdkjvmti.so + lib=tiagent +fi + +if [[ "$@" == *"--jvm"* ]]; then + arg="jvm" +else + arg="art" +fi + +if [[ "$@" != *"--debuggable"* ]]; then + other_args=" -Xcompiler-option --debuggable " +else + other_args="" +fi + +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ + --runtime-option -agentpath:${agent}=903-hello-tagging,${arg} \ + --android-runtime-option -Xplugin:${plugin} \ + ${other_args} \ + --args ${lib} diff --git a/test/903-hello-tagging/src/Main.java b/test/903-hello-tagging/src/Main.java new file mode 100644 index 0000000000..2856a398c9 --- /dev/null +++ b/test/903-hello-tagging/src/Main.java @@ -0,0 +1,73 @@ +/* + * 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. + */ + +import java.lang.ref.WeakReference; + +public class Main { + public static void main(String[] args) { + System.loadLibrary(args[1]); + doTest(); + } + + public static void doTest() { + WeakReference<Object> weak = test(); + + Runtime.getRuntime().gc(); + Runtime.getRuntime().gc(); + + if (weak.get() != null) { + throw new RuntimeException("WeakReference not cleared"); + } + } + + private static WeakReference<Object> test() { + Object o1 = new Object(); + setTag(o1, 1); + + Object o2 = new Object(); + setTag(o2, 2); + + checkTag(o1, 1); + checkTag(o2, 2); + + Runtime.getRuntime().gc(); + Runtime.getRuntime().gc(); + + checkTag(o1, 1); + checkTag(o2, 2); + + Runtime.getRuntime().gc(); + Runtime.getRuntime().gc(); + + setTag(o1, 10); + setTag(o2, 20); + + checkTag(o1, 10); + checkTag(o2, 20); + + return new WeakReference<Object>(o1); + } + + private static void checkTag(Object o, long expectedTag) { + long tag = getTag(o); + if (expectedTag != tag) { + throw new RuntimeException("Unexpected tag " + tag + ", expected " + expectedTag); + } + } + + private static native void setTag(Object o, long tag); + private static native long getTag(Object o); +} diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc new file mode 100644 index 0000000000..8ccdf49892 --- /dev/null +++ b/test/903-hello-tagging/tagging.cc @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2013 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 "tagging.h" + +#include <iostream> +#include <pthread.h> +#include <stdio.h> +#include <vector> + +#include "art_method-inl.h" +#include "base/logging.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" +#include "utils.h" + +namespace art { +namespace Test903HelloTagging { + +static jvmtiEnv* jvmti_env; + +extern "C" JNIEXPORT void JNICALL Java_Main_setTag(JNIEnv* env ATTRIBUTE_UNUSED, + jclass, + jobject obj, + jlong tag) { + jvmtiError ret = jvmti_env->SetTag(obj, tag); + if (ret != JVMTI_ERROR_NONE) { + char* err; + jvmti_env->GetErrorName(ret, &err); + printf("Error setting tag: %s\n", err); + } +} + +extern "C" JNIEXPORT jlong JNICALL Java_Main_getTag(JNIEnv* env ATTRIBUTE_UNUSED, + jclass, + jobject obj) { + jlong tag = 0; + jvmtiError ret = jvmti_env->GetTag(obj, &tag); + if (ret != JVMTI_ERROR_NONE) { + char* err; + jvmti_env->GetErrorName(ret, &err); + printf("Error getting tag: %s\n", err); + } + return tag; +} + +// Don't do anything +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; + } + return 0; +} + +} // namespace Test903HelloTagging +} // namespace art + diff --git a/test/903-hello-tagging/tagging.h b/test/903-hello-tagging/tagging.h new file mode 100644 index 0000000000..f062d44880 --- /dev/null +++ b/test/903-hello-tagging/tagging.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_903_HELLO_TAGGING_TAGGING_H_ +#define ART_TEST_903_HELLO_TAGGING_TAGGING_H_ + +#include <jni.h> + +namespace art { +namespace Test903HelloTagging { + +jint OnLoad(JavaVM* vm, char* options, void* reserved); + +} // namespace Test903HelloTagging +} // namespace art + +#endif // ART_TEST_903_HELLO_TAGGING_TAGGING_H_ diff --git a/test/Android.bp b/test/Android.bp index 628f3772e0..d17261cd68 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -245,6 +245,7 @@ art_cc_test_library { "ti-agent/common_load.cc", "901-hello-ti-agent/basics.cc", "902-hello-transformation/transform.cc", + "903-hello-tagging/tagging.cc", ], shared_libs: [ "libart", @@ -263,9 +264,11 @@ art_cc_test_library { "ti-agent/common_load.cc", "901-hello-ti-agent/basics.cc", "902-hello-transformation/transform.cc", + "903-hello-tagging/tagging.cc", ], shared_libs: [ "libartd", + "libbase", "libopenjdkjvmtid", ], } diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index 53bb1533e7..4c7df97374 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -25,6 +25,7 @@ #include "901-hello-ti-agent/basics.h" #include "902-hello-transformation/transform.h" +#include "903-hello-tagging/tagging.h" namespace art { @@ -41,6 +42,7 @@ struct AgentLib { AgentLib agents[] = { { "901-hello-ti-agent", Test901HelloTi::OnLoad, nullptr }, { "902-hello-transformation", Test902HelloTransformation::OnLoad, nullptr }, + { "903-hello-tagging", Test903HelloTagging::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index 7b5e9ed38a..53fe8fedc5 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -233,11 +233,5 @@ modes: [device], names: ["libcore.java.lang.ProcessBuilderTest#testRedirectInherit", "libcore.java.lang.ProcessBuilderTest#testRedirect_nullStreams"] -}, -{ - description: "Sometimes timeouts", - result: EXEC_FAILED, - bug: 31258002, - names: ["libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithJarHttpURLConnection"] } ] |