Implement array creation related DEX instructions.
Implement new-array, filled-new-array, and fill-array-data.
Change-Id: I405560d66777a57d881e384265322617ac5d3ce3
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 2f1a092..1188ec0 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -171,6 +171,7 @@
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 @@
DCHECK_EQ(argument_index, number_of_arguments);
current_block_->AddInstruction(invoke);
+ latest_result_ = invoke;
return true;
}
@@ -503,6 +505,62 @@
}
}
+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 <typename T>
+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 @@
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<const Instruction::ArrayDataPayload*>(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<const uint16_t*>(data),
+ element_count,
+ Primitive::kPrimShort,
+ dex_offset);
+ break;
+ case 4:
+ BuildFillArrayData(null_check,
+ reinterpret_cast<const uint32_t*>(data),
+ element_count,
+ Primitive::kPrimInt,
+ dex_offset);
+ break;
+ case 8:
+ BuildFillWideArrayData(null_check,
+ reinterpret_cast<const uint64_t*>(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 90e50ad..c5e02db 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -48,7 +48,9 @@
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 @@
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 @@
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 <typename T>
+ 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 @@
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 f07cb30..792ff45 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1305,6 +1305,29 @@
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 2550518..acf4103 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -491,17 +491,23 @@
Immediate imm(instruction->AsIntConstant()->GetValue());
if (location.IsRegister()) {
__ movl(location.As<Register>(), 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<Register>(), Immediate(Low32Bits(value)));
__ movl(location.AsRegisterPairHigh<Register>(), 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 @@
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 6174ac6..58dda16 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -407,16 +407,22 @@
Immediate imm(instruction->AsIntConstant()->GetValue());
if (location.IsRegister()) {
__ movl(location.As<CpuRegister>(), 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<CpuRegister>(), 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 @@
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 7c933aa..9b7ff88 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -507,6 +507,7 @@
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 @@
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 0000000..e69de29
--- /dev/null
+++ b/test/412-new-array/expected.txt
diff --git a/test/412-new-array/info.txt b/test/412-new-array/info.txt
new file mode 100644
index 0000000..cb388b6
--- /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 0000000..34776db
--- /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 0000000..ed8683a
--- /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 0000000..b1470ec
--- /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 0000000..3c74275
--- /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 0000000..ef77f71
--- /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 <T> 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 97afd00..5fff69e 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -283,6 +283,18 @@
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 faafc1f..009736b 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -26,7 +26,12 @@
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 @@
mv classes-1.dex classes.dex
fi
fi
+
+zip $TEST_NAME.jar classes.dex