summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dexlayout/compact_dex_writer.cc7
-rw-r--r--dexlayout/dex_ir_builder.h4
-rw-r--r--dexlayout/dex_writer.cc108
-rw-r--r--dexlayout/dex_writer.h58
-rw-r--r--dexlayout/dexlayout.h2
-rw-r--r--dexlayout/dexlayout_test.cc4
-rw-r--r--runtime/Android.bp2
-rw-r--r--runtime/method_handles.cc17
-rw-r--r--runtime/method_handles_test.cc382
-rw-r--r--runtime/primitive.h58
-rw-r--r--runtime/primitive_test.cc123
-rw-r--r--test/959-invoke-polymorphic-accessors/src/Main.java4
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();