From a3d05a40de076aabf12ea284c67c99ff28b43dbf Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 20 Oct 2014 17:41:32 +0100 Subject: [PATCH] Implement array creation related DEX instructions. Implement new-array, filled-new-array, and fill-array-data. Change-Id: I405560d66777a57d881e384265322617ac5d3ce3 --- compiler/optimizing/builder.cc | 138 ++++++- compiler/optimizing/builder.h | 41 +- compiler/optimizing/code_generator_arm.cc | 23 ++ compiler/optimizing/code_generator_x86.cc | 32 +- compiler/optimizing/code_generator_x86_64.cc | 32 +- compiler/optimizing/nodes.h | 25 ++ test/412-new-array/expected.txt | 0 test/412-new-array/info.txt | 1 + test/412-new-array/smali/fill_array_data.smali | 81 ++++ test/412-new-array/smali/filled_new_array.smali | 45 +++ .../smali/filled_new_array_verify_error.smali | 10 + test/412-new-array/src/Main.java | 418 +++++++++++++++++++++ test/412-new-array/src/TestCase.java | 199 ++++++++++ test/Android.run-test.mk | 12 + test/etc/default-build | 9 +- 15 files changed, 1058 insertions(+), 8 deletions(-) create mode 100644 test/412-new-array/expected.txt create mode 100644 test/412-new-array/info.txt create mode 100644 test/412-new-array/smali/fill_array_data.smali create mode 100644 test/412-new-array/smali/filled_new_array.smali create mode 100644 test/412-new-array/smali/filled_new_array_verify_error.smali create mode 100644 test/412-new-array/src/Main.java create mode 100644 test/412-new-array/src/TestCase.java diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 2f1a092ea..1188ec094 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -171,6 +171,7 @@ HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { const uint16_t* code_ptr = code_item.insns_; const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_; + code_start_ = code_ptr; // Setup the graph with the entry block and exit block. graph_ = new (arena_) HGraph(arena_); @@ -416,6 +417,7 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, DCHECK_EQ(argument_index, number_of_arguments); current_block_->AddInstruction(invoke); + latest_result_ = invoke; return true; } @@ -503,6 +505,62 @@ void HGraphBuilder::BuildArrayAccess(const Instruction& instruction, } } +void HGraphBuilder::BuildFilledNewArray(uint32_t dex_offset, + uint32_t type_index, + uint32_t number_of_vreg_arguments, + bool is_range, + uint32_t* args, + uint32_t register_index) { + HInstruction* length = GetIntConstant(number_of_vreg_arguments); + HInstruction* object = new (arena_) HNewArray(length, dex_offset, type_index); + current_block_->AddInstruction(object); + + const char* descriptor = dex_file_->StringByTypeIdx(type_index); + DCHECK_EQ(descriptor[0], '[') << descriptor; + char primitive = descriptor[1]; + DCHECK(primitive == 'I' + || primitive == 'L' + || primitive == '[') << descriptor; + bool is_reference_array = (primitive == 'L') || (primitive == '['); + Primitive::Type type = is_reference_array ? Primitive::kPrimNot : Primitive::kPrimInt; + + Temporaries temps(graph_, 1); + temps.Add(object); + for (size_t i = 0; i < number_of_vreg_arguments; ++i) { + HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type); + HInstruction* index = GetIntConstant(i); + current_block_->AddInstruction( + new (arena_) HArraySet(object, index, value, type, dex_offset)); + } + latest_result_ = object; +} + +template +void HGraphBuilder::BuildFillArrayData(HInstruction* object, + const T* data, + uint32_t element_count, + Primitive::Type anticipated_type, + uint32_t dex_offset) { + for (uint32_t i = 0; i < element_count; ++i) { + HInstruction* index = GetIntConstant(i); + HInstruction* value = GetIntConstant(data[i]); + current_block_->AddInstruction(new (arena_) HArraySet( + object, index, value, anticipated_type, dex_offset)); + } +} + +void HGraphBuilder::BuildFillWideArrayData(HInstruction* object, + const uint64_t* data, + uint32_t element_count, + uint32_t dex_offset) { + for (uint32_t i = 0; i < element_count; ++i) { + HInstruction* index = GetIntConstant(i); + HInstruction* value = GetLongConstant(data[i]); + current_block_->AddInstruction(new (arena_) HArraySet( + object, index, value, Primitive::kPrimLong, dex_offset)); + } +} + void HGraphBuilder::PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_offset) { if (target_offset <= 0) { // Unconditionnally add a suspend check to backward branches. We can remove @@ -807,10 +865,88 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::NEW_ARRAY: { + HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt); + current_block_->AddInstruction( + new (arena_) HNewArray(length, dex_offset, instruction.VRegC_22c())); + UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction()); + break; + } + + case Instruction::FILLED_NEW_ARRAY: { + uint32_t number_of_vreg_arguments = instruction.VRegA_35c(); + uint32_t type_index = instruction.VRegB_35c(); + uint32_t args[5]; + instruction.GetVarArgs(args); + BuildFilledNewArray(dex_offset, type_index, number_of_vreg_arguments, false, args, 0); + break; + } + + case Instruction::FILLED_NEW_ARRAY_RANGE: { + uint32_t number_of_vreg_arguments = instruction.VRegA_3rc(); + uint32_t type_index = instruction.VRegB_3rc(); + uint32_t register_index = instruction.VRegC_3rc(); + BuildFilledNewArray( + dex_offset, type_index, number_of_vreg_arguments, true, nullptr, register_index); + break; + } + + case Instruction::FILL_ARRAY_DATA: { + Temporaries temps(graph_, 1); + HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot); + HNullCheck* null_check = new (arena_) HNullCheck(array, dex_offset); + current_block_->AddInstruction(null_check); + temps.Add(null_check); + + HInstruction* length = new (arena_) HArrayLength(null_check); + current_block_->AddInstruction(length); + + int32_t payload_offset = instruction.VRegB_31t() + dex_offset; + const Instruction::ArrayDataPayload* payload = + reinterpret_cast(code_start_ + payload_offset); + const uint8_t* data = payload->data; + uint32_t element_count = payload->element_count; + + // Implementation of this DEX instruction seems to be that the bounds check is + // done before doing any stores. + HInstruction* last_index = GetIntConstant(payload->element_count - 1); + current_block_->AddInstruction(new (arena_) HBoundsCheck(last_index, length, dex_offset)); + + switch (payload->element_width) { + case 1: + BuildFillArrayData(null_check, data, element_count, Primitive::kPrimByte, dex_offset); + break; + case 2: + BuildFillArrayData(null_check, + reinterpret_cast(data), + element_count, + Primitive::kPrimShort, + dex_offset); + break; + case 4: + BuildFillArrayData(null_check, + reinterpret_cast(data), + element_count, + Primitive::kPrimInt, + dex_offset); + break; + case 8: + BuildFillWideArrayData(null_check, + reinterpret_cast(data), + element_count, + dex_offset); + break; + default: + LOG(FATAL) << "Unknown element width for " << payload->element_width; + } + break; + } + case Instruction::MOVE_RESULT: case Instruction::MOVE_RESULT_WIDE: case Instruction::MOVE_RESULT_OBJECT: - UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); + UpdateLocal(instruction.VRegA(), latest_result_); + latest_result_ = nullptr; break; case Instruction::CMP_LONG: { diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 90e50ad95..c5e02db88 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -48,7 +48,9 @@ class HGraphBuilder : public ValueObject { dex_file_(dex_file), dex_compilation_unit_(dex_compilation_unit), compiler_driver_(driver), - return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])) {} + return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])), + code_start_(nullptr), + latest_result_(nullptr) {} // Only for unit testing. HGraphBuilder(ArenaAllocator* arena, Primitive::Type return_type = Primitive::kPrimInt) @@ -64,7 +66,9 @@ class HGraphBuilder : public ValueObject { dex_file_(nullptr), dex_compilation_unit_(nullptr), compiler_driver_(nullptr), - return_type_(return_type) {} + return_type_(return_type), + code_start_(nullptr), + latest_result_(nullptr) {} HGraph* BuildGraph(const DexFile::CodeItem& code); @@ -129,6 +133,31 @@ class HGraphBuilder : public ValueObject { uint32_t* args, uint32_t register_index); + // Builds a new array node and the instructions that fill it. + void BuildFilledNewArray(uint32_t dex_offset, + uint32_t type_index, + uint32_t number_of_vreg_arguments, + bool is_range, + uint32_t* args, + uint32_t register_index); + + // Fills the given object with data as specified in the fill-array-data + // instruction. Currently only used for non-reference and non-floating point + // arrays. + template + void BuildFillArrayData(HInstruction* object, + const T* data, + uint32_t element_count, + Primitive::Type anticipated_type, + uint32_t dex_offset); + + // Fills the given object with data as specified in the fill-array-data + // instruction. The data must be for long and double arrays. + void BuildFillWideArrayData(HInstruction* object, + const uint64_t* data, + uint32_t element_count, + uint32_t dex_offset); + ArenaAllocator* const arena_; // A list of the size of the dex code holding block information for @@ -151,6 +180,14 @@ class HGraphBuilder : public ValueObject { CompilerDriver* const compiler_driver_; const Primitive::Type return_type_; + // The pointer in the dex file where the instructions of the code item + // being currently compiled start. + const uint16_t* code_start_; + + // The last invoke or fill-new-array being built. Only to be + // used by move-result instructions. + HInstruction* latest_result_; + DISALLOW_COPY_AND_ASSIGN(HGraphBuilder); }; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index f07cb30a6..792ff45cf 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1305,6 +1305,29 @@ void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { DCHECK(!codegen_->IsLeafMethod()); } +void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetOut(Location::RegisterLocation(R0)); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { + InvokeRuntimeCallingConvention calling_convention; + LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); + + int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocArrayWithAccessCheck).Int32Value(); + __ LoadFromOffset(kLoadWord, LR, TR, offset); + __ blx(LR); + + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); +} + void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 2550518db..acf410367 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -491,17 +491,23 @@ void CodeGeneratorX86::Move(HInstruction* instruction, Location location, HInstr Immediate imm(instruction->AsIntConstant()->GetValue()); if (location.IsRegister()) { __ movl(location.As(), imm); - } else { + } else if (location.IsStackSlot()) { __ movl(Address(ESP, location.GetStackIndex()), imm); + } else { + DCHECK(location.IsConstant()); + DCHECK_EQ(location.GetConstant(), instruction); } } else if (instruction->IsLongConstant()) { int64_t value = instruction->AsLongConstant()->GetValue(); if (location.IsRegister()) { __ movl(location.AsRegisterPairLow(), Immediate(Low32Bits(value))); __ movl(location.AsRegisterPairHigh(), Immediate(High32Bits(value))); - } else { + } else if (location.IsDoubleStackSlot()) { __ movl(Address(ESP, location.GetStackIndex()), Immediate(Low32Bits(value))); __ movl(Address(ESP, location.GetHighStackIndex(kX86WordSize)), Immediate(High32Bits(value))); + } else { + DCHECK(location.IsConstant()); + DCHECK_EQ(location.GetConstant(), instruction); } } else if (instruction->IsLoadLocal()) { int slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); @@ -1310,6 +1316,28 @@ void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) { DCHECK(!codegen_->IsLeafMethod()); } +void LocationsBuilderX86::VisitNewArray(HNewArray* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + locations->SetOut(Location::RegisterLocation(EAX)); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) { + InvokeRuntimeCallingConvention calling_convention; + LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex())); + + __ fs()->call( + Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocArrayWithAccessCheck))); + + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); +} + void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 6174ac6be..58dda16d2 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -407,16 +407,22 @@ void CodeGeneratorX86_64::Move(HInstruction* instruction, Immediate imm(instruction->AsIntConstant()->GetValue()); if (location.IsRegister()) { __ movl(location.As(), imm); - } else { + } else if (location.IsStackSlot()) { __ movl(Address(CpuRegister(RSP), location.GetStackIndex()), imm); + } else { + DCHECK(location.IsConstant()); + DCHECK_EQ(location.GetConstant(), instruction); } } else if (instruction->IsLongConstant()) { int64_t value = instruction->AsLongConstant()->GetValue(); if (location.IsRegister()) { __ movq(location.As(), Immediate(value)); - } else { + } else if (location.IsDoubleStackSlot()) { __ movq(CpuRegister(TMP), Immediate(value)); __ movq(Address(CpuRegister(RSP), location.GetStackIndex()), CpuRegister(TMP)); + } else { + DCHECK(location.IsConstant()); + DCHECK_EQ(location.GetConstant(), instruction); } } else if (instruction->IsLoadLocal()) { switch (instruction->GetType()) { @@ -1229,6 +1235,28 @@ void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } +void LocationsBuilderX86_64::VisitNewArray(HNewArray* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetOut(Location::RegisterLocation(RAX)); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) { + InvokeRuntimeCallingConvention calling_convention; + LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1))); + __ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex())); + + __ gs()->call(Address::Absolute( + QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocArrayWithAccessCheck), true)); + + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); +} + void LocationsBuilderX86_64::VisitParameterValue(HParameterValue* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 7c933aa4f..9b7ff88b6 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -507,6 +507,7 @@ class HBasicBlock : public ArenaObject { M(Neg, UnaryOperation) \ M(FloatConstant, Constant) \ M(DoubleConstant, Constant) \ + M(NewArray, Instruction) \ #define FOR_EACH_INSTRUCTION(M) \ FOR_EACH_CONCRETE_INSTRUCTION(M) \ @@ -1608,6 +1609,30 @@ class HNeg : public HUnaryOperation { DISALLOW_COPY_AND_ASSIGN(HNeg); }; +class HNewArray : public HExpression<1> { + public: + HNewArray(HInstruction* length, uint32_t dex_pc, uint16_t type_index) + : HExpression(Primitive::kPrimNot, SideEffects::None()), + dex_pc_(dex_pc), + type_index_(type_index) { + SetRawInputAt(0, length); + } + + uint32_t GetDexPc() const { return dex_pc_; } + uint16_t GetTypeIndex() const { return type_index_; } + + // Calls runtime so needs an environment. + virtual bool NeedsEnvironment() const { return true; } + + DECLARE_INSTRUCTION(NewArray); + + private: + const uint32_t dex_pc_; + const uint16_t type_index_; + + DISALLOW_COPY_AND_ASSIGN(HNewArray); +}; + class HAdd : public HBinaryOperation { public: HAdd(Primitive::Type result_type, HInstruction* left, HInstruction* right) diff --git a/test/412-new-array/expected.txt b/test/412-new-array/expected.txt new file mode 100644 index 000000000..e69de29bb diff --git a/test/412-new-array/info.txt b/test/412-new-array/info.txt new file mode 100644 index 000000000..cb388b602 --- /dev/null +++ b/test/412-new-array/info.txt @@ -0,0 +1 @@ +Simple tests for new-array, filled-new-array and fill-array-data. diff --git a/test/412-new-array/smali/fill_array_data.smali b/test/412-new-array/smali/fill_array_data.smali new file mode 100644 index 000000000..34776db5d --- /dev/null +++ b/test/412-new-array/smali/fill_array_data.smali @@ -0,0 +1,81 @@ +.class public LFillArrayData; + +.super Ljava/lang/Object; + +.method public static intArray([I)V + .registers 1 + + fill-array-data v0, :ArrayData + return-void + +:ArrayData + .array-data 4 + 1 2 3 4 5 + .end array-data + +.end method + +.method public static shortArray([S)V + .registers 1 + + fill-array-data v0, :ArrayData + return-void + +:ArrayData + .array-data 2 + 1 2 3 4 5 + .end array-data + +.end method + +.method public static charArray([C)V + .registers 1 + + fill-array-data v0, :ArrayData + return-void + +:ArrayData + .array-data 2 + 1 2 3 4 5 + .end array-data + +.end method + +.method public static byteArray([B)V + .registers 1 + + fill-array-data v0, :ArrayData + return-void + +:ArrayData + .array-data 1 + 1 2 3 4 5 + .end array-data + +.end method + +.method public static booleanArray([Z)V + .registers 1 + + fill-array-data v0, :ArrayData + return-void + +:ArrayData + .array-data 1 + 0 1 1 + .end array-data + +.end method + +.method public static longArray([J)V + .registers 1 + + fill-array-data v0, :ArrayData + return-void + +:ArrayData + .array-data 8 + 1 2 3 4 5 + .end array-data + +.end method diff --git a/test/412-new-array/smali/filled_new_array.smali b/test/412-new-array/smali/filled_new_array.smali new file mode 100644 index 000000000..ed8683a14 --- /dev/null +++ b/test/412-new-array/smali/filled_new_array.smali @@ -0,0 +1,45 @@ +.class public LFilledNewArray; + +.super Ljava/lang/Object; + +.method public static newInt(III)[I + .registers 4 + filled-new-array {v1, v2, v3}, [I + move-result-object v0 + return-object v0 +.end method + +.method public static newRef(Ljava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object; + .registers 3 + filled-new-array {v1, v2}, [Ljava/lang/Object; + move-result-object v0 + return-object v0 +.end method + +.method public static newArray([I[I)[[I + .registers 3 + filled-new-array {v1, v2}, [[I + move-result-object v0 + return-object v0 +.end method + +.method public static newIntRange(III)[I + .registers 4 + filled-new-array/range {v1 .. v3}, [I + move-result-object v0 + return-object v0 +.end method + +.method public static newRefRange(Ljava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object; + .registers 3 + filled-new-array/range {v1 .. v2}, [Ljava/lang/Object; + move-result-object v0 + return-object v0 +.end method + +.method public static newArrayRange([I[I)[[I + .registers 3 + filled-new-array/range {v1 .. v2}, [[I + move-result-object v0 + return-object v0 +.end method diff --git a/test/412-new-array/smali/filled_new_array_verify_error.smali b/test/412-new-array/smali/filled_new_array_verify_error.smali new file mode 100644 index 000000000..b1470ec61 --- /dev/null +++ b/test/412-new-array/smali/filled_new_array_verify_error.smali @@ -0,0 +1,10 @@ +.class public LFilledNewArrayVerifyError; + +.super Ljava/lang/Object; + +.method public static newRef(Ljava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object; + .registers 3 + filled-new-array {v1, v2}, [Ljava/lang/Integer; + move-result-object v0 + return-object v0 +.end method diff --git a/test/412-new-array/src/Main.java b/test/412-new-array/src/Main.java new file mode 100644 index 000000000..3c7427583 --- /dev/null +++ b/test/412-new-array/src/Main.java @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +// Note that $opt$ is a marker for the optimizing compiler to ensure +// it does compile the method. + +public class Main extends TestCase { + public static void main(String[] args) throws Exception { + $opt$TestAllocations(); + $opt$TestWithInitializations(); + testSmaliFilledNewArray(); + testSmaliFillArrayData(); + testSmaliVerifyError(); + } + + static void $opt$TestAllocations() { + float[] a = new float[1]; + assertEquals(1, a.length); + + double[] b = new double[2]; + assertEquals(2, b.length); + + long[] c = new long[3]; + assertEquals(3, c.length); + + int[] d = new int[4]; + assertEquals(4, d.length); + + short[] e = new short[5]; + assertEquals(5, e.length); + + char[] f = new char[6]; + assertEquals(6, f.length); + + byte[] g = new byte[7]; + assertEquals(7, g.length); + + boolean[] h = new boolean[8]; + assertEquals(8, h.length); + + Object[] i = new Object[9]; + assertEquals(9, i.length); + } + + static void $opt$TestWithInitializations() { + float[] a = { 1.2f }; + assertEquals(1, a.length); + assertEquals(1.2f, a[0]); + + double[] b = { 4.3, 1.2 }; + assertEquals(2, b.length); + assertEquals(4.3, b[0]); + assertEquals(1.2, b[1]); + + long[] c = { 4L, 5L }; + assertEquals(2, c.length); + assertEquals(4L, c[0]); + assertEquals(5L, c[1]); + + int[] d = {1, 2, 3}; + assertEquals(3, d.length); + assertEquals(1, d[0]); + assertEquals(2, d[1]); + assertEquals(3, d[2]); + + short[] e = {4, 5, 6}; + assertEquals(3, e.length); + assertEquals(4, e[0]); + assertEquals(5, e[1]); + assertEquals(6, e[2]); + + char[] f = {'a', 'b'}; + assertEquals(2, f.length); + assertEquals('a', f[0]); + assertEquals('b', f[1]); + + byte[] g = {7, 8, 9}; + assertEquals(3, g.length); + assertEquals(7, g[0]); + assertEquals(8, g[1]); + assertEquals(9, g[2]); + + boolean[] h = {true, false}; + assertEquals(2, h.length); + assertEquals(true, h[0]); + assertEquals(false, h[1]); + + Object obj1 = new Object(); + Object obj2 = new Object(); + Object[] i = {obj1, obj2}; + assertEquals(2, i.length); + assertEquals(obj1, i[0]); + assertEquals(obj2, i[1]); + } + + public static void testSmaliFilledNewArray() throws Exception { + Class c = Class.forName("FilledNewArray"); + + { + Method m = c.getMethod("newInt", Integer.TYPE, Integer.TYPE, Integer.TYPE); + Object[] args = {new Integer(1), new Integer(2), new Integer(3)}; + int[] result = (int[])m.invoke(null, args); + assertEquals(3, result.length); + assertEquals(1, result[0]); + assertEquals(2, result[1]); + assertEquals(3, result[2]); + } + + { + Method m = c.getMethod("newRef", Object.class, Object.class); + Object[] args = {new Integer(1), new Integer(2)}; + Object[] result = (Object[])m.invoke(null, args); + assertEquals(2, result.length); + assertEquals(args[0], result[0]); + assertEquals(args[1], result[1]); + } + + { + Method m = c.getMethod("newArray", int[].class, int[].class); + Object[] args = {new int[0], new int[1]}; + Object[] result = (Object[])m.invoke(null, args); + assertEquals(2, result.length); + assertEquals(args[0], result[0]); + assertEquals(args[1], result[1]); + } + + { + Method m = c.getMethod("newIntRange", Integer.TYPE, Integer.TYPE, Integer.TYPE); + Object[] args = {new Integer(1), new Integer(2), new Integer(3)}; + int[] result = (int[])m.invoke(null, args); + assertEquals(3, result.length); + assertEquals(1, result[0]); + assertEquals(2, result[1]); + assertEquals(3, result[2]); + } + + { + Method m = c.getMethod("newRefRange", Object.class, Object.class); + Object[] args = {new Integer(1), new Integer(2)}; + Object[] result = (Object[])m.invoke(null, args); + assertEquals(2, result.length); + assertEquals(args[0], result[0]); + assertEquals(args[1], result[1]); + } + + { + Method m = c.getMethod("newArrayRange", int[].class, int[].class); + Object[] args = {new int[0], new int[1]}; + Object[] result = (Object[])m.invoke(null, args); + assertEquals(2, result.length); + assertEquals(args[0], result[0]); + assertEquals(args[1], result[1]); + } + } + + public static void testSmaliVerifyError() throws Exception { + Error error = null; + // Ensure the elements in filled-new-array must be assignable + // to the array component type. + try { + Class.forName("FilledNewArrayVerifyError"); + } catch (VerifyError e) { + error = e; + } + assertNotNull(error); + } + + public static void testSmaliFillArrayData() throws Exception { + Class c = Class.forName("FillArrayData"); + { + Method m = c.getMethod("intArray", int[].class); + int[] array = new int[7]; + Object[] args = { array }; + m.invoke(null, args); + assertEquals(7, array.length); + assertEquals(1, array[0]); + assertEquals(2, array[1]); + assertEquals(3, array[2]); + assertEquals(4, array[3]); + assertEquals(5, array[4]); + assertEquals(0, array[5]); + assertEquals(0, array[6]); + + array = new int[2]; + args[0] = array; + Throwable exception = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof IndexOutOfBoundsException); + } + assertNotNull(exception); + exception = null; + // Test that nothing has been written to the array. + assertEquals(0, array[0]); + assertEquals(0, array[1]); + + args[0] = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof NullPointerException); + } + assertNotNull(exception); + } + + { + Method m = c.getMethod("shortArray", short[].class); + short[] array = new short[7]; + Object[] args = { array }; + m.invoke(null, args); + assertEquals(7, array.length); + assertEquals(1, array[0]); + assertEquals(2, array[1]); + assertEquals(3, array[2]); + assertEquals(4, array[3]); + assertEquals(5, array[4]); + assertEquals(0, array[5]); + assertEquals(0, array[6]); + + array = new short[2]; + args[0] = array; + Throwable exception = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof IndexOutOfBoundsException); + } + assertNotNull(exception); + exception = null; + // Test that nothing has been written to the array. + assertEquals(0, array[0]); + assertEquals(0, array[1]); + + args[0] = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof NullPointerException); + } + assertNotNull(exception); + } + + { + Method m = c.getMethod("longArray", long[].class); + long[] array = new long[7]; + Object[] args = { array }; + m.invoke(null, args); + assertEquals(7, array.length); + assertEquals(1L, array[0]); + assertEquals(2L, array[1]); + assertEquals(3L, array[2]); + assertEquals(4L, array[3]); + assertEquals(5L, array[4]); + assertEquals(0L, array[5]); + assertEquals(0L, array[6]); + + array = new long[2]; + args[0] = array; + Throwable exception = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof IndexOutOfBoundsException); + } + assertNotNull(exception); + exception = null; + // Test that nothing has been written to the array. + assertEquals(0, array[0]); + assertEquals(0, array[1]); + + args[0] = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof NullPointerException); + } + assertNotNull(exception); + } + + { + Method m = c.getMethod("charArray", char[].class); + char[] array = new char[7]; + Object[] args = { array }; + m.invoke(null, args); + assertEquals(7, array.length); + assertEquals(1, array[0]); + assertEquals(2, array[1]); + assertEquals(3, array[2]); + assertEquals(4, array[3]); + assertEquals(5, array[4]); + assertEquals(0, array[5]); + assertEquals(0, array[6]); + + array = new char[2]; + args[0] = array; + Throwable exception = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof IndexOutOfBoundsException); + } + assertNotNull(exception); + exception = null; + // Test that nothing has been written to the array. + assertEquals(0, array[0]); + assertEquals(0, array[1]); + + args[0] = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof NullPointerException); + } + assertNotNull(exception); + } + + { + Method m = c.getMethod("byteArray", byte[].class); + byte[] array = new byte[7]; + Object[] args = { array }; + m.invoke(null, args); + assertEquals(7, array.length); + assertEquals(1, array[0]); + assertEquals(2, array[1]); + assertEquals(3, array[2]); + assertEquals(4, array[3]); + assertEquals(5, array[4]); + assertEquals(0, array[5]); + assertEquals(0, array[6]); + + array = new byte[2]; + args[0] = array; + Throwable exception = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof IndexOutOfBoundsException); + } + assertNotNull(exception); + exception = null; + // Test that nothing has been written to the array. + assertEquals(0, array[0]); + assertEquals(0, array[1]); + + args[0] = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof NullPointerException); + } + assertNotNull(exception); + } + + { + Method m = c.getMethod("booleanArray", boolean[].class); + boolean[] array = new boolean[5]; + Object[] args = { array }; + m.invoke(null, args); + assertEquals(5, array.length); + assertEquals(false, array[0]); + assertEquals(true, array[1]); + assertEquals(true, array[2]); + assertEquals(false, array[3]); + assertEquals(false, array[4]); + + array = new boolean[2]; + args[0] = array; + Throwable exception = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof IndexOutOfBoundsException); + } + assertNotNull(exception); + exception = null; + // Test that nothing has been written to the array. + assertEquals(false, array[0]); + assertEquals(false, array[1]); + + args[0] = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof NullPointerException); + } + assertNotNull(exception); + } + } +} diff --git a/test/412-new-array/src/TestCase.java b/test/412-new-array/src/TestCase.java new file mode 100644 index 000000000..ef77f71f3 --- /dev/null +++ b/test/412-new-array/src/TestCase.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common superclass for test cases. + */ + +import java.util.Arrays; + +public abstract class TestCase { + public static void assertSame(Object expected, Object value) { + if (expected != value) { + throw new AssertionError("Objects are not the same: expected " + + String.valueOf(expected) + ", got " + String.valueOf(value)); + } + } + + public static void assertNotSame(Object expected, Object value) { + if (expected == value) { + throw new AssertionError( + "Objects are the same: " + String.valueOf(expected)); + } + } + + public static void assertEquals(String message, int expected, int actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(int expected, int actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertTrue(String message, boolean condition) { + if (!condition) { + throw new AssertionError(message); + } + } + + public static void assertTrue(boolean condition) { + assertTrue("Expected true", condition); + } + + public static void assertFalse(String message, boolean condition) { + if (condition) { + throw new AssertionError(message); + } + } + + public static void assertFalse(boolean condition) { + assertFalse("Expected false", condition); + } + + public static void assertEquals(Object expected, Object actual) { + if (!expected.equals(actual)) { + String msg = "Expected \"" + expected + "\" but got \"" + actual + "\""; + throw new AssertionError(msg); + } + } + + public static void assertNotEquals(int expected, int actual) { + if (expected == actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertNotEquals(Object expected, Object actual) { + if (expected.equals(actual)) { + String msg = "Objects are the same: " + String.valueOf(expected); + throw new AssertionError(msg); + } + } + + public static void assertArrayEquals(T[] actual, T... expected) { + assertTrue(Arrays.equals(expected, actual)); + } + + public static void assertEquals( + String message, Object expected, Object actual) { + if (!expected.equals(actual)) { + throw new AssertionError(message); + } + } + + public static void assertEquals( + String message, long expected, long actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(long expected, long actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertEquals( + String message, boolean expected, boolean actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(boolean expected, boolean actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertEquals( + String message, float expected, float actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(float expected, float actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertEquals(float expected, float actual, + float tolerance) { + if ((actual < expected - tolerance) || (expected + tolerance < actual)) { + throw new AssertionError("Expected " + expected + " got " + actual + + " tolerance " + tolerance); + } + } + + public static void assertEquals( + String message, double expected, double actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(double expected, double actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertEquals(double expected, double actual, + double tolerance) { + if ((actual < expected - tolerance) || (expected + tolerance < actual)) { + throw new AssertionError("Expected " + expected + " got " + actual + + " tolerance " + tolerance); + } + } + + public static void assertSame( + String message, Object expected, Object actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertNull(String message, Object object) { + if (object != null) { + throw new AssertionError(message); + } + } + + public static void assertNull(Object object) { + assertNull("Expected null", object); + } + + public static void assertNotNull(String message, Object object) { + if (object == null) { + throw new AssertionError(message); + } + } + + public static void assertNotNull(Object object) { + assertNotNull("Expected non-null", object); + } + + public static void fail(String msg) { + throw new AssertionError(msg); + } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 97afd0097..5fff69e0b 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -283,6 +283,18 @@ endif TEST_ART_BROKEN_NDEBUG_TESTS := +# Known broken tests for the default compiler (Quick). +TEST_ART_BROKEN_DEFAULT_RUN_TESTS := \ + 412-new-array + +ifneq (,$(filter default,$(COMPILER_TYPES))) + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ + defaut,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ + $(IMAGE_TYPES),$(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES)) +endif + +TEST_ART_BROKEN_DEFAULT_RUN_TESTS := + # Clear variables ahead of appending to them when defining tests. $(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=)) $(foreach target, $(TARGET_TYPES), \ diff --git a/test/etc/default-build b/test/etc/default-build index faafc1fb2..009736b09 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -26,7 +26,12 @@ fi if [ ${NEED_DEX} = "true" ]; then ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes - zip $TEST_NAME.jar classes.dex +fi + +if [ -r smali ]; then + # Compile Smali classes + ${SMALI} -JXmx256m --output smali_classes.dex `find smali -name '*.smali'` + ${DXMERGER} classes.dex classes.dex smali_classes.dex fi if [ -r src-ex ]; then @@ -43,3 +48,5 @@ if [ -r src-ex ]; then mv classes-1.dex classes.dex fi fi + +zip $TEST_NAME.jar classes.dex -- 2.11.0