diff options
| -rw-r--r-- | dexlayout/compact_dex_writer.cc | 7 | ||||
| -rw-r--r-- | dexlayout/dex_ir_builder.h | 4 | ||||
| -rw-r--r-- | dexlayout/dex_writer.cc | 108 | ||||
| -rw-r--r-- | dexlayout/dex_writer.h | 58 | ||||
| -rw-r--r-- | dexlayout/dexlayout.h | 2 | ||||
| -rw-r--r-- | dexlayout/dexlayout_test.cc | 4 | ||||
| -rw-r--r-- | runtime/Android.bp | 2 | ||||
| -rw-r--r-- | runtime/method_handles.cc | 17 | ||||
| -rw-r--r-- | runtime/method_handles_test.cc | 382 | ||||
| -rw-r--r-- | runtime/primitive.h | 58 | ||||
| -rw-r--r-- | runtime/primitive_test.cc | 123 | ||||
| -rw-r--r-- | test/959-invoke-polymorphic-accessors/src/Main.java | 4 |
12 files changed, 650 insertions, 119 deletions
diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc index 092539fe93..08438c4f4a 100644 --- a/dexlayout/compact_dex_writer.cc +++ b/dexlayout/compact_dex_writer.cc @@ -121,11 +121,14 @@ CompactDexWriter::ScopedDataSectionItem::~ScopedDataSectionItem() { const uint32_t deduped_offset = deduper_->Dedupe(start_offset_, stream_->Tell(), item_->GetOffset()); - // In case we dedupe to something with wrong alignment, just say we didn't dedupe. + // If we deduped, only use the deduped offset if the alignment matches the required alignment. + // Otherwise, return without deduping. if (deduped_offset != Deduper::kDidNotDedupe && IsAlignedParam(deduped_offset, alignment_)) { + // Update the IR offset to the offset of the deduped item. item_->SetOffset(deduped_offset); + // Clear the written data for the item so that the stream write doesn't abort in the future. stream_->Clear(start_offset_, stream_->Tell() - start_offset_); - // Undo the offset for all that we wrote since we deduped. + // Since we deduped, restore the offset to the original position. stream_->Seek(start_offset_); } } diff --git a/dexlayout/dex_ir_builder.h b/dexlayout/dex_ir_builder.h index 43b5290756..9f5377fe56 100644 --- a/dexlayout/dex_ir_builder.h +++ b/dexlayout/dex_ir_builder.h @@ -27,8 +27,8 @@ class Options; namespace dex_ir { -// Eagerly assign offsets assigns offsets based on the original offsets in the input dex file. If -// this not done, dex_ir::Item::GetOffset will abort when reading uninitialized offsets. +// Eagerly assign offsets based on the original offsets in the input dex file. If this is not done, +// dex_ir::Item::GetOffset will abort when reading uninitialized offsets. dex_ir::Header* DexIrBuilder(const DexFile& dex_file, bool eagerly_assign_offsets, const Options& options); diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc index 7a7529cb0b..778681e7b7 100644 --- a/dexlayout/dex_writer.cc +++ b/dexlayout/dex_writer.cc @@ -114,8 +114,7 @@ DexWriter::DexWriter(DexLayout* dex_layout, bool compute_offsets) dex_layout_(dex_layout), compute_offsets_(compute_offsets) {} -size_t DexWriter::WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encoded_value) { - size_t original_offset = stream->Tell(); +void DexWriter::WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encoded_value) { size_t start = 0; size_t length; uint8_t buffer[8]; @@ -166,39 +165,37 @@ size_t DexWriter::WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encode case DexFile::kDexAnnotationArray: WriteEncodedValueHeader(stream, type, 0); WriteEncodedArray(stream, encoded_value->GetEncodedArray()->GetEncodedValues()); - return stream->Tell() - original_offset; + return; case DexFile::kDexAnnotationAnnotation: WriteEncodedValueHeader(stream, type, 0); WriteEncodedAnnotation(stream, encoded_value->GetEncodedAnnotation()); - return stream->Tell() - original_offset; + return; case DexFile::kDexAnnotationNull: - return WriteEncodedValueHeader(stream, type, 0); + WriteEncodedValueHeader(stream, type, 0); + return; case DexFile::kDexAnnotationBoolean: - return WriteEncodedValueHeader(stream, type, encoded_value->GetBoolean() ? 1 : 0); + WriteEncodedValueHeader(stream, type, encoded_value->GetBoolean() ? 1 : 0); + return; default: - return 0; + return; } WriteEncodedValueHeader(stream, type, length - 1); stream->Write(buffer + start, length); - return stream->Tell() - original_offset; } -size_t DexWriter::WriteEncodedValueHeader(Stream* stream, int8_t value_type, size_t value_arg) { +void DexWriter::WriteEncodedValueHeader(Stream* stream, int8_t value_type, size_t value_arg) { uint8_t buffer[1] = { static_cast<uint8_t>((value_arg << 5) | value_type) }; - return stream->Write(buffer, sizeof(uint8_t)); + stream->Write(buffer, sizeof(uint8_t)); } -size_t DexWriter::WriteEncodedArray(Stream* stream, dex_ir::EncodedValueVector* values) { - size_t original_offset = stream->Tell(); +void DexWriter::WriteEncodedArray(Stream* stream, dex_ir::EncodedValueVector* values) { stream->WriteUleb128(values->size()); for (std::unique_ptr<dex_ir::EncodedValue>& value : *values) { WriteEncodedValue(stream, value.get()); } - return stream->Tell() - original_offset; } -size_t DexWriter::WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotation* annotation) { - size_t original_offset = stream->Tell(); +void DexWriter::WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotation* annotation) { stream->WriteUleb128(annotation->GetType()->GetIndex()); stream->WriteUleb128(annotation->GetAnnotationElements()->size()); for (std::unique_ptr<dex_ir::AnnotationElement>& annotation_element : @@ -206,11 +203,9 @@ size_t DexWriter::WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotati stream->WriteUleb128(annotation_element->GetName()->GetIndex()); WriteEncodedValue(stream, annotation_element->GetValue()); } - return stream->Tell() - original_offset; } -size_t DexWriter::WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields) { - size_t original_offset = stream->Tell(); +void DexWriter::WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields) { uint32_t prev_index = 0; for (std::unique_ptr<dex_ir::FieldItem>& field : *fields) { uint32_t index = field->GetFieldId()->GetIndex(); @@ -218,11 +213,9 @@ size_t DexWriter::WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fi stream->WriteUleb128(field->GetAccessFlags()); prev_index = index; } - return stream->Tell() - original_offset; } -size_t DexWriter::WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods) { - size_t original_offset = stream->Tell(); +void DexWriter::WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods) { uint32_t prev_index = 0; for (std::unique_ptr<dex_ir::MethodItem>& method : *methods) { uint32_t index = method->GetMethodId()->GetIndex(); @@ -232,12 +225,11 @@ size_t DexWriter::WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* stream->WriteUleb128(code_off); prev_index = index; } - return stream->Tell() - original_offset; } // TODO: Refactor this to remove duplicated boiler plate. One way to do this is adding // function that takes a CollectionVector<T> and uses overloading. -uint32_t DexWriter::WriteStringIds(Stream* stream, bool reserve_only) { +void DexWriter::WriteStringIds(Stream* stream, bool reserve_only) { const uint32_t start = stream->Tell(); for (std::unique_ptr<dex_ir::StringId>& string_id : header_->GetCollections().StringIds()) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeStringIdItem)); @@ -251,7 +243,6 @@ uint32_t DexWriter::WriteStringIds(Stream* stream, bool reserve_only) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetStringIdsOffset(start); } - return stream->Tell() - start; } void DexWriter::WriteStringData(Stream* stream, dex_ir::StringData* string_data) { @@ -263,7 +254,7 @@ void DexWriter::WriteStringData(Stream* stream, dex_ir::StringData* string_data) stream->Skip(1); } -uint32_t DexWriter::WriteStringDatas(Stream* stream) { +void DexWriter::WriteStringDatas(Stream* stream) { const uint32_t start = stream->Tell(); for (std::unique_ptr<dex_ir::StringData>& string_data : header_->GetCollections().StringDatas()) { WriteStringData(stream, string_data.get()); @@ -271,10 +262,9 @@ uint32_t DexWriter::WriteStringDatas(Stream* stream) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetStringDatasOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteTypeIds(Stream* stream) { +void DexWriter::WriteTypeIds(Stream* stream) { uint32_t descriptor_idx[1]; const uint32_t start = stream->Tell(); for (std::unique_ptr<dex_ir::TypeId>& type_id : header_->GetCollections().TypeIds()) { @@ -286,10 +276,9 @@ uint32_t DexWriter::WriteTypeIds(Stream* stream) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetTypeIdsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteTypeLists(Stream* stream) { +void DexWriter::WriteTypeLists(Stream* stream) { uint32_t size[1]; uint16_t list[1]; const uint32_t start = stream->Tell(); @@ -306,10 +295,9 @@ uint32_t DexWriter::WriteTypeLists(Stream* stream) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetTypeListsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteProtoIds(Stream* stream, bool reserve_only) { +void DexWriter::WriteProtoIds(Stream* stream, bool reserve_only) { uint32_t buffer[3]; const uint32_t start = stream->Tell(); for (std::unique_ptr<dex_ir::ProtoId>& proto_id : header_->GetCollections().ProtoIds()) { @@ -327,10 +315,9 @@ uint32_t DexWriter::WriteProtoIds(Stream* stream, bool reserve_only) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetProtoIdsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteFieldIds(Stream* stream) { +void DexWriter::WriteFieldIds(Stream* stream) { uint16_t buffer[4]; const uint32_t start = stream->Tell(); for (std::unique_ptr<dex_ir::FieldId>& field_id : header_->GetCollections().FieldIds()) { @@ -345,10 +332,9 @@ uint32_t DexWriter::WriteFieldIds(Stream* stream) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetFieldIdsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteMethodIds(Stream* stream) { +void DexWriter::WriteMethodIds(Stream* stream) { uint16_t buffer[4]; const uint32_t start = stream->Tell(); for (std::unique_ptr<dex_ir::MethodId>& method_id : header_->GetCollections().MethodIds()) { @@ -363,10 +349,9 @@ uint32_t DexWriter::WriteMethodIds(Stream* stream) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetMethodIdsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteEncodedArrays(Stream* stream) { +void DexWriter::WriteEncodedArrays(Stream* stream) { const uint32_t start = stream->Tell(); for (std::unique_ptr<dex_ir::EncodedArrayItem>& encoded_array : header_->GetCollections().EncodedArrayItems()) { @@ -377,10 +362,9 @@ uint32_t DexWriter::WriteEncodedArrays(Stream* stream) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetEncodedArrayItemsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteAnnotations(Stream* stream) { +void DexWriter::WriteAnnotations(Stream* stream) { uint8_t visibility[1]; const uint32_t start = stream->Tell(); for (std::unique_ptr<dex_ir::AnnotationItem>& annotation : @@ -394,10 +378,9 @@ uint32_t DexWriter::WriteAnnotations(Stream* stream) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetAnnotationItemsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteAnnotationSets(Stream* stream) { +void DexWriter::WriteAnnotationSets(Stream* stream) { uint32_t size[1]; uint32_t annotation_off[1]; const uint32_t start = stream->Tell(); @@ -415,10 +398,9 @@ uint32_t DexWriter::WriteAnnotationSets(Stream* stream) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetAnnotationSetItemsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteAnnotationSetRefs(Stream* stream) { +void DexWriter::WriteAnnotationSetRefs(Stream* stream) { uint32_t size[1]; uint32_t annotations_off[1]; const uint32_t start = stream->Tell(); @@ -436,10 +418,9 @@ uint32_t DexWriter::WriteAnnotationSetRefs(Stream* stream) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetAnnotationSetRefListsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteAnnotationsDirectories(Stream* stream) { +void DexWriter::WriteAnnotationsDirectories(Stream* stream) { uint32_t directory_buffer[4]; uint32_t annotation_buffer[2]; const uint32_t start = stream->Tell(); @@ -484,7 +465,6 @@ uint32_t DexWriter::WriteAnnotationsDirectories(Stream* stream) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetAnnotationsDirectoryItemsOffset(start); } - return stream->Tell() - start; } void DexWriter::WriteDebugInfoItem(Stream* stream, dex_ir::DebugInfoItem* debug_info) { @@ -493,7 +473,7 @@ void DexWriter::WriteDebugInfoItem(Stream* stream, dex_ir::DebugInfoItem* debug_ stream->Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize()); } -uint32_t DexWriter::WriteDebugInfoItems(Stream* stream) { +void DexWriter::WriteDebugInfoItems(Stream* stream) { const uint32_t start = stream->Tell(); for (std::unique_ptr<dex_ir::DebugInfoItem>& debug_info : header_->GetCollections().DebugInfoItems()) { @@ -502,13 +482,11 @@ uint32_t DexWriter::WriteDebugInfoItems(Stream* stream) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetDebugInfoItemsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteCodeItemPostInstructionData(Stream* stream, - dex_ir::CodeItem* code_item, - bool reserve_only) { - const uint32_t start_offset = stream->Tell(); +void DexWriter::WriteCodeItemPostInstructionData(Stream* stream, + dex_ir::CodeItem* code_item, + bool reserve_only) { if (code_item->TriesSize() != 0) { stream->AlignTo(DexFile::TryItem::kAlignment); // Write try items. @@ -540,7 +518,6 @@ uint32_t DexWriter::WriteCodeItemPostInstructionData(Stream* stream, } stream->Seek(max_offset); } - return stream->Tell() - start_offset; } void DexWriter::WriteCodeItem(Stream* stream, @@ -574,7 +551,7 @@ void DexWriter::WriteCodeItem(Stream* stream, } } -uint32_t DexWriter::WriteCodeItems(Stream* stream, bool reserve_only) { +void DexWriter::WriteCodeItems(Stream* stream, bool reserve_only) { DexLayoutSection* code_section = nullptr; if (!reserve_only && dex_layout_ != nullptr) { code_section = &dex_layout_->GetSections().sections_[static_cast<size_t>( @@ -598,10 +575,9 @@ uint32_t DexWriter::WriteCodeItems(Stream* stream, bool reserve_only) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetCodeItemsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteClassDefs(Stream* stream, bool reserve_only) { +void DexWriter::WriteClassDefs(Stream* stream, bool reserve_only) { const uint32_t start = stream->Tell(); uint32_t class_def_buffer[8]; for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_->GetCollections().ClassDefs()) { @@ -628,10 +604,9 @@ uint32_t DexWriter::WriteClassDefs(Stream* stream, bool reserve_only) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetClassDefsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteClassDatas(Stream* stream) { +void DexWriter::WriteClassDatas(Stream* stream) { const uint32_t start = stream->Tell(); for (const std::unique_ptr<dex_ir::ClassData>& class_data : header_->GetCollections().ClassDatas()) { @@ -649,10 +624,9 @@ uint32_t DexWriter::WriteClassDatas(Stream* stream) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetClassDatasOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteCallSiteIds(Stream* stream, bool reserve_only) { +void DexWriter::WriteCallSiteIds(Stream* stream, bool reserve_only) { const uint32_t start = stream->Tell(); uint32_t call_site_off[1]; for (std::unique_ptr<dex_ir::CallSiteId>& call_site_id : @@ -668,10 +642,9 @@ uint32_t DexWriter::WriteCallSiteIds(Stream* stream, bool reserve_only) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetCallSiteIdsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteMethodHandles(Stream* stream) { +void DexWriter::WriteMethodHandles(Stream* stream) { const uint32_t start = stream->Tell(); uint16_t method_handle_buff[4]; for (std::unique_ptr<dex_ir::MethodHandleItem>& method_handle : @@ -686,13 +659,11 @@ uint32_t DexWriter::WriteMethodHandles(Stream* stream) { if (compute_offsets_ && start != stream->Tell()) { header_->GetCollections().SetMethodHandleItemsOffset(start); } - return stream->Tell() - start; } -uint32_t DexWriter::WriteMapItems(Stream* stream, MapItemQueue* queue) { +void DexWriter::WriteMapItems(Stream* stream, MapItemQueue* queue) { // All the sections should already have been added. const uint32_t map_list_size = queue->size(); - const uint32_t start = stream->Tell(); stream->Write(&map_list_size, sizeof(map_list_size)); while (!queue->empty()) { const MapItem& item = queue->top(); @@ -704,10 +675,9 @@ uint32_t DexWriter::WriteMapItems(Stream* stream, MapItemQueue* queue) { stream->Write(&map_item, sizeof(map_item)); queue->pop(); } - return stream->Tell() - start; } -uint32_t DexWriter::GenerateAndWriteMapItems(Stream* stream) { +void DexWriter::GenerateAndWriteMapItems(Stream* stream) { dex_ir::Collections& collection = header_->GetCollections(); MapItemQueue queue; @@ -769,9 +739,7 @@ uint32_t DexWriter::GenerateAndWriteMapItems(Stream* stream) { queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationsDirectoryItem, collection.AnnotationsDirectoryItemsSize(), collection.AnnotationsDirectoryItemsOffset())); - - // Write the map items. - return WriteMapItems(stream, &queue); + WriteMapItems(stream, &queue); } void DexWriter::WriteHeader(Stream* stream) { diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h index dd613ed688..5df11116ee 100644 --- a/dexlayout/dex_writer.h +++ b/dexlayout/dex_writer.h @@ -224,43 +224,43 @@ class DexWriter { virtual void Write(DexContainer* output); virtual std::unique_ptr<DexContainer> CreateDexContainer() const; - size_t WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encoded_value); - size_t WriteEncodedValueHeader(Stream* stream, int8_t value_type, size_t value_arg); - size_t WriteEncodedArray(Stream* stream, dex_ir::EncodedValueVector* values); - size_t WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotation* annotation); - size_t WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields); - size_t WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods); + void WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encoded_value); + void WriteEncodedValueHeader(Stream* stream, int8_t value_type, size_t value_arg); + void WriteEncodedArray(Stream* stream, dex_ir::EncodedValueVector* values); + void WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotation* annotation); + void WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields); + void WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods); // Header and id section virtual void WriteHeader(Stream* stream); virtual size_t GetHeaderSize() const; // reserve_only means don't write, only reserve space. This is required since the string data // offsets must be assigned. - uint32_t WriteStringIds(Stream* stream, bool reserve_only); - uint32_t WriteTypeIds(Stream* stream); - uint32_t WriteProtoIds(Stream* stream, bool reserve_only); - uint32_t WriteFieldIds(Stream* stream); - uint32_t WriteMethodIds(Stream* stream); - uint32_t WriteClassDefs(Stream* stream, bool reserve_only); - uint32_t WriteCallSiteIds(Stream* stream, bool reserve_only); - - uint32_t WriteEncodedArrays(Stream* stream); - uint32_t WriteAnnotations(Stream* stream); - uint32_t WriteAnnotationSets(Stream* stream); - uint32_t WriteAnnotationSetRefs(Stream* stream); - uint32_t WriteAnnotationsDirectories(Stream* stream); + void WriteStringIds(Stream* stream, bool reserve_only); + void WriteTypeIds(Stream* stream); + void WriteProtoIds(Stream* stream, bool reserve_only); + void WriteFieldIds(Stream* stream); + void WriteMethodIds(Stream* stream); + void WriteClassDefs(Stream* stream, bool reserve_only); + void WriteCallSiteIds(Stream* stream, bool reserve_only); + + void WriteEncodedArrays(Stream* stream); + void WriteAnnotations(Stream* stream); + void WriteAnnotationSets(Stream* stream); + void WriteAnnotationSetRefs(Stream* stream); + void WriteAnnotationsDirectories(Stream* stream); // Data section. - uint32_t WriteDebugInfoItems(Stream* stream); - uint32_t WriteCodeItems(Stream* stream, bool reserve_only); - uint32_t WriteTypeLists(Stream* stream); - uint32_t WriteStringDatas(Stream* stream); - uint32_t WriteClassDatas(Stream* stream); - uint32_t WriteMethodHandles(Stream* stream); - uint32_t WriteMapItems(Stream* stream, MapItemQueue* queue); - uint32_t GenerateAndWriteMapItems(Stream* stream); - - virtual uint32_t WriteCodeItemPostInstructionData(Stream* stream, + void WriteDebugInfoItems(Stream* stream); + void WriteCodeItems(Stream* stream, bool reserve_only); + void WriteTypeLists(Stream* stream); + void WriteStringDatas(Stream* stream); + void WriteClassDatas(Stream* stream); + void WriteMethodHandles(Stream* stream); + void WriteMapItems(Stream* stream, MapItemQueue* queue); + void GenerateAndWriteMapItems(Stream* stream); + + virtual void WriteCodeItemPostInstructionData(Stream* stream, dex_ir::CodeItem* item, bool reserve_only); virtual void WriteCodeItem(Stream* stream, dex_ir::CodeItem* item, bool reserve_only); diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h index e66710fa55..d2f9cb9ce5 100644 --- a/dexlayout/dexlayout.h +++ b/dexlayout/dexlayout.h @@ -72,6 +72,8 @@ class Options { const char* output_dex_directory_ = nullptr; const char* output_file_name_ = nullptr; const char* profile_file_name_ = nullptr; + // Filter that removes classes that don't have a matching descriptor (during IR creation). + // This speeds up cases when the output only requires a single class. std::set<std::string> class_filter_; }; diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index e93ade1412..be272fcf2c 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -823,9 +823,9 @@ TEST_F(DexLayoutTest, ClassFilter) { &error_msg)); ASSERT_TRUE(output_dex_file != nullptr); - ASSERT_EQ(output_dex_file->NumClassDefs(), 1u); + ASSERT_EQ(output_dex_file->NumClassDefs(), options.class_filter_.size()); for (uint32_t i = 0; i < output_dex_file->NumClassDefs(); ++i) { - // Check that every class is in the filter. + // Check that every class in the output dex file is in the filter. const DexFile::ClassDef& class_def = output_dex_file->GetClassDef(i); ASSERT_TRUE(options.class_filter_.find(output_dex_file->GetClassDescriptor(class_def)) != options.class_filter_.end()); diff --git a/runtime/Android.bp b/runtime/Android.bp index f2f7c3e3d0..a759cf700f 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -691,6 +691,7 @@ art_cc_test { "leb128_test.cc", "mem_map_test.cc", "memory_region_test.cc", + "method_handles_test.cc", "mirror/dex_cache_test.cc", "mirror/method_type_test.cc", "mirror/object_test.cc", @@ -701,6 +702,7 @@ art_cc_test { "oat_file_assistant_test.cc", "parsed_options_test.cc", "prebuilt_tools_test.cc", + "primitive_test.cc", "reference_table_test.cc", "runtime_callbacks_test.cc", "subtype_check_info_test.cc", diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 88f30a8900..8bb3bec9ac 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -231,7 +231,7 @@ bool ConvertJValueCommon( StackHandleScope<2> hs(Thread::Current()); Handle<mirror::Class> h_to(hs.NewHandle(to)); Handle<mirror::Object> h_obj(hs.NewHandle(src_value.GetL())); - if (h_obj != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) { + if (UNLIKELY(!h_obj.IsNull() && !to->IsAssignableFrom(h_obj->GetClass()))) { ThrowClassCastException(h_to.Get(), h_obj->GetClass()); return false; } @@ -246,7 +246,7 @@ bool ConvertJValueCommon( Primitive::Type type; if (!GetUnboxedPrimitiveType(to, &type)) { ObjPtr<mirror::Class> boxed_from_class = GetBoxedPrimitiveClass(from_type); - if (boxed_from_class->IsSubClass(to)) { + if (LIKELY(boxed_from_class->IsSubClass(to))) { type = from_type; } else { ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); @@ -259,7 +259,7 @@ bool ConvertJValueCommon( return false; } - if (!ConvertPrimitiveValueNoThrow(from_type, type, src_value, value)) { + if (UNLIKELY(!ConvertPrimitiveValueNoThrow(from_type, type, src_value, value))) { ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); return false; } @@ -274,7 +274,7 @@ bool ConvertJValueCommon( DCHECK(IsPrimitiveType(to_type)); ObjPtr<mirror::Object> from_obj(src_value.GetL()); - if (UNLIKELY(from_obj == nullptr)) { + if (UNLIKELY(from_obj.IsNull())) { ThrowNullPointerException( StringPrintf("Expected to unbox a '%s' primitive type but was returned null", from->PrettyDescriptor().c_str()).c_str()); @@ -289,7 +289,14 @@ bool ConvertJValueCommon( } if (UNLIKELY(!ConvertPrimitiveValueNoThrow(unboxed_type, to_type, unboxed_value, value))) { - ThrowClassCastException(from, to); + if (from->IsAssignableFrom(GetBoxedPrimitiveClass(to_type))) { + // CallSite may be Number, but the Number object is + // incompatible, e.g. Number (Integer) for a short. + ThrowClassCastException(from, to); + } else { + // CallSite is incompatible, e.g. Integer for a short. + ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); + } return false; } diff --git a/runtime/method_handles_test.cc b/runtime/method_handles_test.cc new file mode 100644 index 0000000000..a9473421cb --- /dev/null +++ b/runtime/method_handles_test.cc @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2018 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 "method_handles.h" + +#include "class_linker-inl.h" +#include "common_runtime_test.h" +#include "handle_scope-inl.h" +#include "jvalue-inl.h" +#include "mirror/method_type.h" +#include "mirror/object_array-inl.h" +#include "reflection.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-current-inl.h" + +namespace art { + +namespace { + bool IsClassCastException(ObjPtr<mirror::Throwable> throwable) + REQUIRES_SHARED(Locks::mutator_lock_) { + return throwable->GetClass()->DescriptorEquals("Ljava/lang/ClassCastException;"); + } + + bool IsNullPointerException(ObjPtr<mirror::Throwable> throwable) + REQUIRES_SHARED(Locks::mutator_lock_) { + return throwable->GetClass()->DescriptorEquals("Ljava/lang/NullPointerException;"); + } + + bool IsWrongMethodTypeException(ObjPtr<mirror::Throwable> throwable) + REQUIRES_SHARED(Locks::mutator_lock_) { + return throwable->GetClass()->DescriptorEquals("Ljava/lang/invoke/WrongMethodTypeException;"); + } + + static mirror::MethodType* CreateVoidMethodType(Thread* self, + Handle<mirror::Class> parameter_type) + REQUIRES_SHARED(Locks::mutator_lock_) { + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(self); + ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass(); + ObjPtr<mirror::Class> class_array_type = cl->FindArrayClass(self, &class_type); + auto parameter_types = hs.NewHandle( + mirror::ObjectArray<mirror::Class>::Alloc(self, class_array_type, 1)); + parameter_types->Set(0, parameter_type.Get()); + Handle<mirror::Class> void_class = hs.NewHandle(cl->FindPrimitiveClass('V')); + return mirror::MethodType::Create(self, void_class, parameter_types); + } + + static bool TryConversion(Thread* self, + Handle<mirror::Class> from, + Handle<mirror::Class> to, + JValue* value) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<2> hs(self); + Handle<mirror::MethodType> from_mt = hs.NewHandle(CreateVoidMethodType(self, from)); + Handle<mirror::MethodType> to_mt = hs.NewHandle(CreateVoidMethodType(self, to)); + return ConvertJValueCommon(from_mt, to_mt, from.Get(), to.Get(), value); + } +} // namespace + +class MethodHandlesTest : public CommonRuntimeTest {}; + +// +// Primitive -> Primitive Conversions +// + +TEST_F(MethodHandlesTest, SupportedPrimitiveWideningBI) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('B')); + Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I')); + JValue value = JValue::FromPrimitive(static_cast<int8_t>(3)); + ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_EQ(3, value.GetI()); + ASSERT_FALSE(soa.Self()->IsExceptionPending()); +} + +TEST_F(MethodHandlesTest, SupportedPrimitiveWideningCJ) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('C')); + Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('J')); + uint16_t raw_value = 0x8000; + JValue value = JValue::FromPrimitive(raw_value); + ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(soa.Self()->IsExceptionPending()); + ASSERT_EQ(static_cast<int64_t>(raw_value), value.GetJ()); +} + +TEST_F(MethodHandlesTest, SupportedPrimitiveWideningIF) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I')); + Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('F')); + JValue value = JValue::FromPrimitive(-16); + ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(soa.Self()->IsExceptionPending()); + ASSERT_FLOAT_EQ(-16.0f, value.GetF()); +} + +TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningBC) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('B')); + Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('C')); + JValue value; + value.SetB(0); + ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(soa.Self()->IsExceptionPending()); + ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); + soa.Self()->ClearException(); +} + +TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningSC) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('S')); + Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('C')); + JValue value; + value.SetS(0x1234); + ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(soa.Self()->IsExceptionPending()); + ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); + soa.Self()->ClearException(); +} + +TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningDJ) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('D')); + Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('J')); + JValue value; + value.SetD(1e72); + ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(soa.Self()->IsExceptionPending()); + ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); + soa.Self()->ClearException(); +} + +TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningZI) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('Z')); + Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I')); + JValue value; + value.SetZ(true); + ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(soa.Self()->IsExceptionPending()); + ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); + soa.Self()->ClearException(); +} + +// +// Reference -> Reference Conversions +// + +TEST_F(MethodHandlesTest, SupportedReferenceCast) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<3> hs(soa.Self()); + static const int32_t kInitialValue = 101; + JValue value = JValue::FromPrimitive(kInitialValue); + Handle<mirror::Object> boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr()); + Handle<mirror::Class> from = hs.NewHandle(boxed_value->GetClass()); + Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;")); + value.SetL(boxed_value.Get()); + ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(soa.Self()->IsExceptionPending()); + JValue unboxed_value; + ASSERT_TRUE(UnboxPrimitiveForResult(value.GetL(), cl->FindPrimitiveClass('I'), &unboxed_value)); + ASSERT_EQ(kInitialValue, unboxed_value.GetI()); +} + +TEST_F(MethodHandlesTest, UnsupportedReferenceCast) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<3> hs(soa.Self()); + JValue value = JValue::FromPrimitive(3.733e2); + Handle<mirror::Object> boxed_value = + hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value).Ptr()); + Handle<mirror::Class> from = hs.NewHandle(boxed_value->GetClass()); + Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); + value.SetL(boxed_value.Get()); + ASSERT_FALSE(soa.Self()->IsExceptionPending()); + ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(soa.Self()->IsExceptionPending()); + ASSERT_TRUE(IsClassCastException(soa.Self()->GetException())); + soa.Self()->ClearException(); +} + +// +// Primitive -> Reference Conversions +// + +TEST_F(MethodHandlesTest, SupportedPrimitiveConversionPrimitiveToBoxed) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(soa.Self()); + const int32_t kInitialValue = 1; + JValue value = JValue::FromPrimitive(kInitialValue); + Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I')); + Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); + ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(soa.Self()->IsExceptionPending()); + JValue unboxed_to_value; + ASSERT_TRUE(UnboxPrimitiveForResult(value.GetL(), from.Get(), &unboxed_to_value)); + ASSERT_EQ(kInitialValue, unboxed_to_value.GetI()); +} + +TEST_F(MethodHandlesTest, SupportedPrimitiveConversionPrimitiveToBoxedSuper) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(soa.Self()); + const int32_t kInitialValue = 1; + JValue value = JValue::FromPrimitive(kInitialValue); + Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I')); + Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;")); + ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(soa.Self()->IsExceptionPending()); + JValue unboxed_to_value; + ASSERT_TRUE(UnboxPrimitiveForResult(value.GetL(), from.Get(), &unboxed_to_value)); + ASSERT_EQ(kInitialValue, unboxed_to_value.GetI()); +} + +TEST_F(MethodHandlesTest, UnsupportedPrimitiveConversionNotBoxable) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(soa.Self()); + const int32_t kInitialValue = 1; + JValue value = JValue::FromPrimitive(kInitialValue); + Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I')); + Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Runtime;")); + ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(soa.Self()->IsExceptionPending()); + ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); + soa.Self()->ClearException(); +} + +TEST_F(MethodHandlesTest, UnsupportedPrimitiveConversionPrimitiveToBoxedWider) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(soa.Self()); + const int32_t kInitialValue = 1; + JValue value = JValue::FromPrimitive(kInitialValue); + Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I')); + Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Long;")); + ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(soa.Self()->IsExceptionPending()); + ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); + soa.Self()->ClearException(); +} + +TEST_F(MethodHandlesTest, UnsupportedPrimitiveConversionPrimitiveToBoxedNarrower) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(soa.Self()); + const int32_t kInitialValue = 1; + JValue value = JValue::FromPrimitive(kInitialValue); + Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I')); + Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Byte;")); + ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(soa.Self()->IsExceptionPending()); + ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); + soa.Self()->ClearException(); +} + +// +// Reference -> Primitive Conversions +// + +TEST_F(MethodHandlesTest, SupportedBoxedToPrimitiveConversion) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<3> hs(soa.Self()); + const int32_t kInitialValue = 101; + JValue value = JValue::FromPrimitive(kInitialValue); + Handle<mirror::Object> boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr()); + Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); + Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I')); + value.SetL(boxed_value.Get()); + ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(soa.Self()->IsExceptionPending()); + ASSERT_EQ(kInitialValue, value.GetI()); +} + +TEST_F(MethodHandlesTest, SupportedBoxedToWiderPrimitiveConversion) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<3> hs(soa.Self()); + static const int32_t kInitialValue = 101; + JValue value = JValue::FromPrimitive(kInitialValue); + Handle<mirror::Object> boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr()); + Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); + Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('J')); + value.SetL(boxed_value.Get()); + ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_EQ(kInitialValue, value.GetJ()); +} + +TEST_F(MethodHandlesTest, UnsupportedNullBoxedToPrimitiveConversion) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<3> hs(soa.Self()); + JValue value = JValue::FromPrimitive(101); + ScopedNullHandle<mirror::Object> boxed_value; + Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); + Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I')); + value.SetL(boxed_value.Get()); + ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(soa.Self()->IsExceptionPending()); + ASSERT_TRUE(IsNullPointerException(soa.Self()->GetException())); + soa.Self()->ClearException(); +} + +TEST_F(MethodHandlesTest, UnsupportedNotBoxReferenceToPrimitiveConversion) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Class;")); + Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I')); + // Set value to be converted as some non-primitive type. + JValue value; + value.SetL(cl->FindPrimitiveClass('V')); + ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(soa.Self()->IsExceptionPending()); + ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); + soa.Self()->ClearException(); +} + +TEST_F(MethodHandlesTest, UnsupportedBoxedToNarrowerPrimitiveConversionNoCast) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<3> hs(soa.Self()); + static const int32_t kInitialValue = 101; + JValue value = JValue::FromPrimitive(kInitialValue); + Handle<mirror::Object> boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr()); + Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); + Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('S')); + value.SetL(boxed_value.Get()); + ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(soa.Self()->IsExceptionPending()); + ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); + soa.Self()->ClearException(); +} + +TEST_F(MethodHandlesTest, UnsupportedBoxedToNarrowerPrimitiveConversionWithCast) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + StackHandleScope<3> hs(soa.Self()); + static const double kInitialValue = 1e77; + JValue value = JValue::FromPrimitive(kInitialValue); + Handle<mirror::Object> boxed_value = + hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value).Ptr()); + Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;")); + Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('F')); + value.SetL(boxed_value.Get()); + ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(soa.Self()->IsExceptionPending()); + ASSERT_TRUE(IsClassCastException(soa.Self()->GetException())); + soa.Self()->ClearException(); +} + +} // namespace art diff --git a/runtime/primitive.h b/runtime/primitive.h index 5b163d8cbe..38ad68d13d 100644 --- a/runtime/primitive.h +++ b/runtime/primitive.h @@ -140,12 +140,30 @@ class Primitive { // Returns the descriptor corresponding to the boxed type of |type|. static const char* BoxedDescriptor(Type type); - // Return true if |type| is an numeric type. + // Returns true if |type| is an numeric type. static constexpr bool IsNumericType(Type type) { switch (type) { case Primitive::Type::kPrimNot: return false; case Primitive::Type::kPrimBoolean: return false; case Primitive::Type::kPrimByte: return true; + case Primitive::Type::kPrimChar: return true; + case Primitive::Type::kPrimShort: return true; + case Primitive::Type::kPrimInt: return true; + case Primitive::Type::kPrimLong: return true; + case Primitive::Type::kPrimFloat: return true; + case Primitive::Type::kPrimDouble: return true; + case Primitive::Type::kPrimVoid: return false; + } + LOG(FATAL) << "Invalid type " << static_cast<int>(type); + UNREACHABLE(); + } + + // Return trues if |type| is a signed numeric type. + static constexpr bool IsSignedNumericType(Type type) { + switch (type) { + case Primitive::Type::kPrimNot: return false; + case Primitive::Type::kPrimBoolean: return false; + case Primitive::Type::kPrimByte: return true; case Primitive::Type::kPrimChar: return false; case Primitive::Type::kPrimShort: return true; case Primitive::Type::kPrimInt: return true; @@ -158,17 +176,39 @@ class Primitive { UNREACHABLE(); } + // Returns the number of bits required to hold the largest + // positive number that can be represented by |type|. + static constexpr size_t BitsRequiredForLargestValue(Type type) { + switch (type) { + case Primitive::Type::kPrimNot: return 0u; + case Primitive::Type::kPrimBoolean: return 1u; + case Primitive::Type::kPrimByte: return 7u; + case Primitive::Type::kPrimChar: return 16u; + case Primitive::Type::kPrimShort: return 15u; + case Primitive::Type::kPrimInt: return 31u; + case Primitive::Type::kPrimLong: return 63u; + case Primitive::Type::kPrimFloat: return 128u; + case Primitive::Type::kPrimDouble: return 1024u; + case Primitive::Type::kPrimVoid: return 0u; + } + } + // Returns true if it is possible to widen type |from| to type |to|. Both |from| and // |to| should be numeric primitive types. static bool IsWidenable(Type from, Type to) { - static_assert(Primitive::Type::kPrimByte < Primitive::Type::kPrimShort, "Bad ordering"); - static_assert(Primitive::Type::kPrimShort < Primitive::Type::kPrimInt, "Bad ordering"); - static_assert(Primitive::Type::kPrimInt < Primitive::Type::kPrimLong, "Bad ordering"); - static_assert(Primitive::Type::kPrimLong < Primitive::Type::kPrimFloat, "Bad ordering"); - static_assert(Primitive::Type::kPrimFloat < Primitive::Type::kPrimDouble, "Bad ordering"); - // Widening is only applicable between numeric types, like byte - // and int. Non-numeric types, such as boolean, cannot be widened. - return IsNumericType(from) && IsNumericType(to) && from <= to; + if (!IsNumericType(from) || !IsNumericType(to)) { + // Widening is only applicable between numeric types. + return false; + } + if (IsSignedNumericType(from) && !IsSignedNumericType(to)) { + // Nowhere to store the sign bit in |to|. + return false; + } + if (BitsRequiredForLargestValue(from) > BitsRequiredForLargestValue(to)) { + // The from,to pair corresponds to a narrowing. + return false; + } + return true; } static bool Is64BitType(Type type) { diff --git a/runtime/primitive_test.cc b/runtime/primitive_test.cc new file mode 100644 index 0000000000..e433b15b61 --- /dev/null +++ b/runtime/primitive_test.cc @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2018 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 "primitive.h" + +#include "gtest/gtest.h" + +namespace art { + +namespace { + +void CheckPrimitiveTypeWidensTo(Primitive::Type from, + const std::vector<Primitive::Type>& expected_to_types) { + std::vector<Primitive::Type> actual_to_types; + int last = static_cast<int>(Primitive::Type::kPrimLast); + for (int i = 0; i <= last; ++i) { + Primitive::Type to = static_cast<Primitive::Type>(i); + if (Primitive::IsWidenable(from, to)) { + actual_to_types.push_back(to); + } + } + EXPECT_EQ(expected_to_types, actual_to_types); +} + +} // namespace + +TEST(PrimitiveTest, NotWidensTo) { + const std::vector<Primitive::Type> to_types = {}; + CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimNot, to_types); +} + +TEST(PrimitiveTest, BooleanWidensTo) { + const std::vector<Primitive::Type> to_types = {}; + CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimBoolean, to_types); +} + +TEST(PrimitiveTest, ByteWidensTo) { + const std::vector<Primitive::Type> to_types = { + Primitive::Type::kPrimByte, + Primitive::Type::kPrimShort, + Primitive::Type::kPrimInt, + Primitive::Type::kPrimLong, + Primitive::Type::kPrimFloat, + Primitive::Type::kPrimDouble, + }; + CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimByte, to_types); +} + +TEST(PrimitiveTest, CharWidensTo) { + const std::vector<Primitive::Type> to_types = { + Primitive::Type::kPrimChar, + Primitive::Type::kPrimInt, + Primitive::Type::kPrimLong, + Primitive::Type::kPrimFloat, + Primitive::Type::kPrimDouble, + }; + CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimChar, to_types); +} + +TEST(PrimitiveTest, ShortWidensTo) { + const std::vector<Primitive::Type> to_types = { + Primitive::Type::kPrimShort, + Primitive::Type::kPrimInt, + Primitive::Type::kPrimLong, + Primitive::Type::kPrimFloat, + Primitive::Type::kPrimDouble, + }; + CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimShort, to_types); +} + +TEST(PrimitiveTest, IntWidensTo) { + const std::vector<Primitive::Type> to_types = { + Primitive::Type::kPrimInt, + Primitive::Type::kPrimLong, + Primitive::Type::kPrimFloat, + Primitive::Type::kPrimDouble, + }; + CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimInt, to_types); +} + +TEST(PrimitiveTest, LongWidensTo) { + const std::vector<Primitive::Type> to_types = { + Primitive::Type::kPrimLong, + Primitive::Type::kPrimFloat, + Primitive::Type::kPrimDouble, + }; + CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimLong, to_types); +} + +TEST(PrimitiveTest, FloatWidensTo) { + const std::vector<Primitive::Type> to_types = { + Primitive::Type::kPrimFloat, + Primitive::Type::kPrimDouble, + }; + CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimFloat, to_types); +} + +TEST(PrimitiveTest, DoubleWidensTo) { + const std::vector<Primitive::Type> to_types = { + Primitive::Type::kPrimDouble, + }; + CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimDouble, to_types); +} + +TEST(PrimitiveTest, VoidWidensTo) { + const std::vector<Primitive::Type> to_types = {}; + CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimVoid, to_types); +} + +} // namespace art diff --git a/test/959-invoke-polymorphic-accessors/src/Main.java b/test/959-invoke-polymorphic-accessors/src/Main.java index cdde1de515..03fd285d46 100644 --- a/test/959-invoke-polymorphic-accessors/src/Main.java +++ b/test/959-invoke-polymorphic-accessors/src/Main.java @@ -901,6 +901,10 @@ public class Main { fail(); } catch (WrongMethodTypeException expected) {} try { + h0.invoke(Double.valueOf(0.33)); + fail(); + } catch (WrongMethodTypeException expected) {} + try { Number doubleNumber = getDoubleAsNumber(); h0.invoke(doubleNumber); fail(); |