summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/jni/jni_compiler_test.cc2
-rw-r--r--compiler/optimizing/code_generator.cc7
-rw-r--r--compiler/optimizing/code_generator.h5
-rw-r--r--compiler/optimizing/code_generator_arm.cc2
-rw-r--r--compiler/optimizing/code_generator_arm64.cc4
-rw-r--r--compiler/optimizing/code_generator_mips.cc2
-rw-r--r--compiler/optimizing/code_generator_mips64.cc2
-rw-r--r--compiler/optimizing/code_generator_x86.cc2
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc2
-rw-r--r--compiler/optimizing/dead_code_elimination.cc2
-rw-r--r--compiler/optimizing/graph_visualizer.cc5
-rw-r--r--compiler/optimizing/instruction_builder.cc5
-rw-r--r--compiler/optimizing/instruction_simplifier.cc26
-rw-r--r--compiler/optimizing/intrinsics.cc6
-rw-r--r--compiler/optimizing/intrinsics.h2
-rw-r--r--compiler/optimizing/intrinsics_list.h2
-rw-r--r--compiler/optimizing/load_store_elimination.cc15
-rw-r--r--compiler/optimizing/nodes.cc14
-rw-r--r--compiler/optimizing/nodes.h13
-rw-r--r--compiler/optimizing/ssa_builder.cc4
-rw-r--r--compiler/optimizing/ssa_phi_elimination.cc20
-rw-r--r--profman/profman.cc2
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h21
-rw-r--r--runtime/java_vm_ext.cc9
-rw-r--r--runtime/jit/jit.cc6
-rw-r--r--runtime/native/dalvik_system_DexFile.cc71
-rw-r--r--runtime/native/java_lang_reflect_Constructor.cc3
-rw-r--r--runtime/oat_file_assistant.cc32
-rw-r--r--runtime/oat_file_assistant.h9
-rw-r--r--runtime/verifier/method_verifier.cc4
-rw-r--r--test/044-proxy/expected.txt2
-rw-r--r--test/044-proxy/src/ConstructorProxy.java53
-rw-r--r--test/044-proxy/src/Main.java1
-rw-r--r--test/536-checker-intrinsic-optimization/src/Main.java60
-rw-r--r--test/586-checker-null-array-get/src/Main.java49
-rw-r--r--test/594-invoke-super/expected.txt7
-rw-r--r--test/594-invoke-super/info.txt1
-rw-r--r--test/594-invoke-super/smali/invoke-super.smali31
-rw-r--r--test/594-invoke-super/src/Main.java80
-rwxr-xr-xtest/run-test13
-rw-r--r--tools/dmtracedump/tracedump.cc22
41 files changed, 542 insertions, 76 deletions
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 251dc39864..c4c2399ccd 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -98,8 +98,8 @@ class JniCompilerTest : public CommonCompilerTest {
CompileForTest(class_loader_, direct, method_name, method_sig);
// Start runtime.
Thread::Current()->TransitionFromSuspendedToRunnable();
- bool started = runtime_->Start();
android::InitializeNativeLoader();
+ bool started = runtime_->Start();
CHECK(started);
}
// JNI operations after runtime start.
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index e7fa4e472b..51fbaea519 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -50,6 +50,7 @@
#include "mirror/array-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/object_reference.h"
+#include "mirror/string.h"
#include "parallel_move_resolver.h"
#include "ssa_liveness_analysis.h"
#include "utils/assembler.h"
@@ -139,6 +140,12 @@ size_t CodeGenerator::GetCachePointerOffset(uint32_t index) {
return pointer_size * index;
}
+uint32_t CodeGenerator::GetArrayLengthOffset(HArrayLength* array_length) {
+ return array_length->IsStringLength()
+ ? mirror::String::CountOffset().Uint32Value()
+ : mirror::Array::LengthOffset().Uint32Value();
+}
+
bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const {
DCHECK_EQ((*block_order_)[current_block_index_], current);
return GetNextBlockToEmit() == FirstNonEmptyBlock(next);
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index d69c41055b..6e75e3bb2e 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -340,6 +340,11 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> {
// Pointer variant for ArtMethod and ArtField arrays.
size_t GetCachePointerOffset(uint32_t index);
+ // Helper that returns the offset of the array's length field.
+ // Note: Besides the normal arrays, we also use the HArrayLength for
+ // accessing the String's `count` field in String intrinsics.
+ static uint32_t GetArrayLengthOffset(HArrayLength* array_length);
+
void EmitParallelMoves(Location from1,
Location to1,
Primitive::Type type1,
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 197e473473..e0106628c6 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -4742,7 +4742,7 @@ void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) {
void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) {
LocationSummary* locations = instruction->GetLocations();
- uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
+ uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
Register obj = locations->InAt(0).AsRegister<Register>();
Register out = locations->Out().AsRegister<Register>();
__ LoadFromOffset(kLoadWord, out, obj, offset);
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 9680f2bf45..261c04f062 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2118,9 +2118,9 @@ void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) {
}
void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) {
+ uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
BlockPoolsScope block_pools(GetVIXLAssembler());
- __ Ldr(OutputRegister(instruction),
- HeapOperand(InputRegisterAt(instruction, 0), mirror::Array::LengthOffset()));
+ __ Ldr(OutputRegister(instruction), HeapOperand(InputRegisterAt(instruction, 0), offset));
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 12d1164d03..fb50680c91 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1803,7 +1803,7 @@ void LocationsBuilderMIPS::VisitArrayLength(HArrayLength* instruction) {
void InstructionCodeGeneratorMIPS::VisitArrayLength(HArrayLength* instruction) {
LocationSummary* locations = instruction->GetLocations();
- uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
+ uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
Register obj = locations->InAt(0).AsRegister<Register>();
Register out = locations->Out().AsRegister<Register>();
__ LoadFromOffset(kLoadWord, out, obj, offset);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 56ac38ef84..e67d8d0dc5 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1426,7 +1426,7 @@ void LocationsBuilderMIPS64::VisitArrayLength(HArrayLength* instruction) {
void InstructionCodeGeneratorMIPS64::VisitArrayLength(HArrayLength* instruction) {
LocationSummary* locations = instruction->GetLocations();
- uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
+ uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
__ LoadFromOffset(kLoadWord, out, obj, offset);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 6dc480bbee..50892a9d48 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -5480,7 +5480,7 @@ void LocationsBuilderX86::VisitArrayLength(HArrayLength* instruction) {
void InstructionCodeGeneratorX86::VisitArrayLength(HArrayLength* instruction) {
LocationSummary* locations = instruction->GetLocations();
- uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
+ uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
Register obj = locations->InAt(0).AsRegister<Register>();
Register out = locations->Out().AsRegister<Register>();
__ movl(out, Address(obj, offset));
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 96ec09c2a8..56c5b06945 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -4956,7 +4956,7 @@ void LocationsBuilderX86_64::VisitArrayLength(HArrayLength* instruction) {
void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) {
LocationSummary* locations = instruction->GetLocations();
- uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
+ uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
CpuRegister out = locations->Out().AsRegister<CpuRegister>();
__ movl(out, Address(obj, offset));
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index 5f11024996..49cfff46d8 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -23,7 +23,7 @@
namespace art {
static void MarkReachableBlocks(HGraph* graph, ArenaBitVector* visited) {
- ArenaVector<HBasicBlock*> worklist(graph->GetArena()->Adapter());
+ ArenaVector<HBasicBlock*> worklist(graph->GetArena()->Adapter(kArenaAllocDCE));
constexpr size_t kDefaultWorlistSize = 8;
worklist.reserve(kDefaultWorlistSize);
visited->SetBit(graph->GetEntryBlock()->GetBlockId());
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 9efc13f61b..568b8a8df6 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -389,6 +389,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
<< instance_of->MustDoNullCheck() << std::noboolalpha;
}
+ void VisitArrayLength(HArrayLength* array_length) OVERRIDE {
+ StartAttributeStream("is_string_length") << std::boolalpha
+ << array_length->IsStringLength() << std::noboolalpha;
+ }
+
void VisitArraySet(HArraySet* array_set) OVERRIDE {
StartAttributeStream("value_can_be_null") << std::boolalpha
<< array_set->GetValueCanBeNull() << std::noboolalpha;
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index a834216d0c..aaddc01f1f 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -721,6 +721,11 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in
DCHECK(Runtime::Current()->IsAotCompiler());
return nullptr;
}
+ if (!methods_class->IsAssignableFrom(compiling_class.Get())) {
+ // We cannot statically determine the target method. The runtime will throw a
+ // NoSuchMethodError on this one.
+ return nullptr;
+ }
ArtMethod* actual_method;
if (methods_class->IsInterface()) {
actual_method = methods_class->FindVirtualMethodForInterfaceSuper(
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index d7b3856bf4..fd79901ffc 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -101,6 +101,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
void SimplifyCompare(HInvoke* invoke, bool is_signum, Primitive::Type type);
void SimplifyIsNaN(HInvoke* invoke);
void SimplifyFP2Int(HInvoke* invoke);
+ void SimplifyStringIsEmptyOrLength(HInvoke* invoke);
void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind);
OptimizingCompilerStats* stats_;
@@ -1673,6 +1674,27 @@ void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) {
invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0
}
+void InstructionSimplifierVisitor::SimplifyStringIsEmptyOrLength(HInvoke* invoke) {
+ HInstruction* str = invoke->InputAt(0);
+ uint32_t dex_pc = invoke->GetDexPc();
+ // We treat String as an array to allow DCE and BCE to seamlessly work on strings,
+ // so create the HArrayLength.
+ HArrayLength* length = new (GetGraph()->GetArena()) HArrayLength(str, dex_pc);
+ length->MarkAsStringLength();
+ HInstruction* replacement;
+ if (invoke->GetIntrinsic() == Intrinsics::kStringIsEmpty) {
+ // For String.isEmpty(), create the `HEqual` representing the `length == 0`.
+ invoke->GetBlock()->InsertInstructionBefore(length, invoke);
+ HIntConstant* zero = GetGraph()->GetIntConstant(0);
+ HEqual* equal = new (GetGraph()->GetArena()) HEqual(length, zero, dex_pc);
+ replacement = equal;
+ } else {
+ DCHECK_EQ(invoke->GetIntrinsic(), Intrinsics::kStringLength);
+ replacement = length;
+ }
+ invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, replacement);
+}
+
void InstructionSimplifierVisitor::SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind) {
uint32_t dex_pc = invoke->GetDexPc();
HMemoryBarrier* mem_barrier = new (GetGraph()->GetArena()) HMemoryBarrier(barrier_kind, dex_pc);
@@ -1719,6 +1741,10 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
case Intrinsics::kDoubleDoubleToLongBits:
SimplifyFP2Int(instruction);
break;
+ case Intrinsics::kStringIsEmpty:
+ case Intrinsics::kStringLength:
+ SimplifyStringIsEmptyOrLength(instruction);
+ break;
case Intrinsics::kUnsafeLoadFence:
SimplifyMemBarrier(instruction, MemBarrierKind::kLoadAny);
break;
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 5d4c4e2950..418d59c6cb 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -388,10 +388,8 @@ static Intrinsics GetIntrinsic(InlineMethod method) {
case kIntrinsicGetCharsNoCheck:
return Intrinsics::kStringGetCharsNoCheck;
case kIntrinsicIsEmptyOrLength:
- // The inliner can handle these two cases - and this is the preferred approach
- // since after inlining the call is no longer visible (as opposed to waiting
- // until codegen to handle intrinsic).
- return Intrinsics::kNone;
+ return ((method.d.data & kIntrinsicFlagIsEmpty) == 0) ?
+ Intrinsics::kStringLength : Intrinsics::kStringIsEmpty;
case kIntrinsicIndexOf:
return ((method.d.data & kIntrinsicFlagBase0) == 0) ?
Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf;
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 39a1313ba0..214250f337 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -239,6 +239,8 @@ UNREACHABLE_INTRINSIC(Arch, IntegerCompare) \
UNREACHABLE_INTRINSIC(Arch, LongCompare) \
UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \
UNREACHABLE_INTRINSIC(Arch, LongSignum) \
+UNREACHABLE_INTRINSIC(Arch, StringIsEmpty) \
+UNREACHABLE_INTRINSIC(Arch, StringLength) \
UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence) \
UNREACHABLE_INTRINSIC(Arch, UnsafeStoreFence) \
UNREACHABLE_INTRINSIC(Arch, UnsafeFullFence)
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
index dd9294d486..db60238fb4 100644
--- a/compiler/optimizing/intrinsics_list.h
+++ b/compiler/optimizing/intrinsics_list.h
@@ -107,6 +107,8 @@
V(StringGetCharsNoCheck, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \
V(StringIndexOf, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \
V(StringIndexOfAfter, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \
+ V(StringIsEmpty, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow) \
+ V(StringLength, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow) \
V(StringNewStringFromBytes, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
V(StringNewStringFromChars, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
V(StringNewStringFromString, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 2de41580b6..8a75a90cfd 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -733,19 +733,14 @@ class LSEVisitor : public HGraphVisitor {
if (Primitive::PrimitiveKind(heap_value->GetType())
!= Primitive::PrimitiveKind(instruction->GetType())) {
// The only situation where the same heap location has different type is when
- // we do an array get from a null constant. In order to stay properly typed
- // we do not merge the array gets.
+ // we do an array get on an instruction that originates from the null constant
+ // (the null could be behind a field access, an array access, a null check or
+ // a bound type).
+ // In order to stay properly typed on primitive types, we do not eliminate
+ // the array gets.
if (kIsDebugBuild) {
DCHECK(heap_value->IsArrayGet()) << heap_value->DebugName();
DCHECK(instruction->IsArrayGet()) << instruction->DebugName();
- HInstruction* array = instruction->AsArrayGet()->GetArray();
- DCHECK(array->IsNullCheck()) << array->DebugName();
- HInstruction* input = HuntForOriginalReference(array->InputAt(0));
- DCHECK(input->IsNullConstant()) << input->DebugName();
- array = heap_value->AsArrayGet()->GetArray();
- DCHECK(array->IsNullCheck()) << array->DebugName();
- input = HuntForOriginalReference(array->InputAt(0));
- DCHECK(input->IsNullConstant()) << input->DebugName();
}
return;
}
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index eb9c381c4e..1e6bf07e42 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -56,9 +56,11 @@ void HGraph::FindBackEdges(ArenaBitVector* visited) {
// Nodes that we're currently visiting, indexed by block id.
ArenaBitVector visiting(arena_, blocks_.size(), false, kArenaAllocGraphBuilder);
// Number of successors visited from a given node, indexed by block id.
- ArenaVector<size_t> successors_visited(blocks_.size(), 0u, arena_->Adapter());
+ ArenaVector<size_t> successors_visited(blocks_.size(),
+ 0u,
+ arena_->Adapter(kArenaAllocGraphBuilder));
// Stack of nodes that we're currently visiting (same as marked in "visiting" above).
- ArenaVector<HBasicBlock*> worklist(arena_->Adapter());
+ ArenaVector<HBasicBlock*> worklist(arena_->Adapter(kArenaAllocGraphBuilder));
constexpr size_t kDefaultWorklistSize = 8;
worklist.reserve(kDefaultWorklistSize);
visited->SetBit(entry_block_->GetBlockId());
@@ -228,11 +230,13 @@ void HGraph::ComputeDominanceInformation() {
reverse_post_order_.push_back(entry_block_);
// Number of visits of a given node, indexed by block id.
- ArenaVector<size_t> visits(blocks_.size(), 0u, arena_->Adapter());
+ ArenaVector<size_t> visits(blocks_.size(), 0u, arena_->Adapter(kArenaAllocGraphBuilder));
// Number of successors visited from a given node, indexed by block id.
- ArenaVector<size_t> successors_visited(blocks_.size(), 0u, arena_->Adapter());
+ ArenaVector<size_t> successors_visited(blocks_.size(),
+ 0u,
+ arena_->Adapter(kArenaAllocGraphBuilder));
// Nodes for which we need to visit successors.
- ArenaVector<HBasicBlock*> worklist(arena_->Adapter());
+ ArenaVector<HBasicBlock*> worklist(arena_->Adapter(kArenaAllocGraphBuilder));
constexpr size_t kDefaultWorklistSize = 8;
worklist.reserve(kDefaultWorklistSize);
worklist.push_back(entry_block_);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 829fe71a78..934d355e82 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -5228,9 +5228,22 @@ class HArrayLength : public HExpression<1> {
return obj == InputAt(0);
}
+ void MarkAsStringLength() { SetPackedFlag<kFlagIsStringLength>(); }
+ bool IsStringLength() const { return GetPackedFlag<kFlagIsStringLength>(); }
+
DECLARE_INSTRUCTION(ArrayLength);
private:
+ // We treat a String as an array, creating the HArrayLength from String.length()
+ // or String.isEmpty() intrinsic in the instruction simplifier. We can always
+ // determine whether a particular HArrayLength is actually a String.length() by
+ // looking at the type of the input but that requires holding the mutator lock, so
+ // we prefer to use a flag, so that code generators don't need to do the locking.
+ static constexpr size_t kFlagIsStringLength = kNumberOfExpressionPackedBits;
+ static constexpr size_t kNumberOfArrayLengthPackedBits = kFlagIsStringLength + 1;
+ static_assert(kNumberOfArrayLengthPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
DISALLOW_COPY_AND_ASSIGN(HArrayLength);
};
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index c2aa0c0075..f96ca321c9 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -233,7 +233,7 @@ bool SsaBuilder::UpdatePrimitiveType(HPhi* phi, ArenaVector<HPhi*>* worklist) {
}
void SsaBuilder::RunPrimitiveTypePropagation() {
- ArenaVector<HPhi*> worklist(graph_->GetArena()->Adapter());
+ ArenaVector<HPhi*> worklist(graph_->GetArena()->Adapter(kArenaAllocGraphBuilder));
for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
@@ -319,7 +319,7 @@ bool SsaBuilder::FixAmbiguousArrayOps() {
// uses (because they are untyped) and environment uses (if --debuggable).
// After resolving all ambiguous ArrayGets, we will re-run primitive type
// propagation on the Phis which need to be updated.
- ArenaVector<HPhi*> worklist(graph_->GetArena()->Adapter());
+ ArenaVector<HPhi*> worklist(graph_->GetArena()->Adapter(kArenaAllocGraphBuilder));
{
ScopedObjectAccess soa(Thread::Current());
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index 0978ae17f8..c67612e651 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -17,6 +17,7 @@
#include "ssa_phi_elimination.h"
#include "base/arena_containers.h"
+#include "base/arena_bit_vector.h"
#include "base/bit_vector-inl.h"
namespace art {
@@ -30,7 +31,7 @@ void SsaDeadPhiElimination::MarkDeadPhis() {
// Phis are constructed live and should not be revived if previously marked
// dead. This algorithm temporarily breaks that invariant but we DCHECK that
// only phis which were initially live are revived.
- ArenaSet<HPhi*> initially_live(graph_->GetArena()->Adapter());
+ ArenaSet<HPhi*> initially_live(graph_->GetArena()->Adapter(kArenaAllocSsaPhiElimination));
// Add to the worklist phis referenced by non-phi instructions.
for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
@@ -127,8 +128,11 @@ void SsaRedundantPhiElimination::Run() {
}
}
- ArenaSet<uint32_t> visited_phis_in_cycle(graph_->GetArena()->Adapter());
- ArenaVector<HPhi*> cycle_worklist(graph_->GetArena()->Adapter());
+ ArenaBitVector visited_phis_in_cycle(graph_->GetArena(),
+ graph_->GetCurrentInstructionId(),
+ /* expandable */ false,
+ kArenaAllocSsaPhiElimination);
+ ArenaVector<HPhi*> cycle_worklist(graph_->GetArena()->Adapter(kArenaAllocSsaPhiElimination));
while (!worklist_.empty()) {
HPhi* phi = worklist_.back();
@@ -146,11 +150,11 @@ void SsaRedundantPhiElimination::Run() {
}
HInstruction* candidate = nullptr;
- visited_phis_in_cycle.clear();
+ visited_phis_in_cycle.ClearAllBits();
cycle_worklist.clear();
cycle_worklist.push_back(phi);
- visited_phis_in_cycle.insert(phi->GetId());
+ visited_phis_in_cycle.SetBit(phi->GetId());
bool catch_phi_in_cycle = phi->IsCatchPhi();
bool irreducible_loop_phi_in_cycle = phi->IsIrreducibleLoopHeaderPhi();
@@ -182,9 +186,9 @@ void SsaRedundantPhiElimination::Run() {
if (input == current) {
continue;
} else if (input->IsPhi()) {
- if (!ContainsElement(visited_phis_in_cycle, input->GetId())) {
+ if (!visited_phis_in_cycle.IsBitSet(input->GetId())) {
cycle_worklist.push_back(input->AsPhi());
- visited_phis_in_cycle.insert(input->GetId());
+ visited_phis_in_cycle.SetBit(input->GetId());
catch_phi_in_cycle |= input->AsPhi()->IsCatchPhi();
irreducible_loop_phi_in_cycle |= input->IsIrreducibleLoopHeaderPhi();
} else {
@@ -233,7 +237,7 @@ void SsaRedundantPhiElimination::Run() {
// for elimination. Add phis that use this phi to the worklist.
for (const HUseListNode<HInstruction*>& use : current->GetUses()) {
HInstruction* user = use.GetUser();
- if (user->IsPhi() && !ContainsElement(visited_phis_in_cycle, user->GetId())) {
+ if (user->IsPhi() && !visited_phis_in_cycle.IsBitSet(user->GetId())) {
worklist_.push_back(user->AsPhi());
}
}
diff --git a/profman/profman.cc b/profman/profman.cc
index b3454fa2e7..37a560d203 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -219,7 +219,7 @@ class ProfMan FINAL {
static constexpr uint64_t kLogThresholdTime = MsToNs(100); // 100ms
uint64_t time_taken = NanoTime() - start_ns_;
if (time_taken > kLogThresholdTime) {
- LOG(WARNING) << "profman took " << PrettyDuration(NanoTime() - start_ns_);
+ LOG(WARNING) << "profman took " << PrettyDuration(time_taken);
}
}
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 16fbfaad32..fc6257302a 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -514,12 +514,18 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object** this_
CHECK(self->IsExceptionPending());
return nullptr;
} else if (!method_reference_class->IsInterface()) {
- // It is not an interface.
- mirror::Class* super_class = referring_class->GetSuperClass();
+ // It is not an interface. If the referring class is in the class hierarchy of the
+ // referenced class in the bytecode, we use its super class. Otherwise, we throw
+ // a NoSuchMethodError.
+ mirror::Class* super_class = nullptr;
+ if (method_reference_class->IsAssignableFrom(referring_class)) {
+ super_class = referring_class->GetSuperClass();
+ }
uint16_t vtable_index = resolved_method->GetMethodIndex();
if (access_check) {
// Check existence of super class.
- if (super_class == nullptr || !super_class->HasVTable() ||
+ if (super_class == nullptr ||
+ !super_class->HasVTable() ||
vtable_index >= static_cast<uint32_t>(super_class->GetVTableLength())) {
// Behavior to agree with that of the verifier.
ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(),
@@ -693,8 +699,13 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, mirror::Object* this_objec
// Need to do full type resolution...
return nullptr;
} else if (!method_reference_class->IsInterface()) {
- // It is not an interface.
- mirror::Class* super_class = referrer->GetDeclaringClass()->GetSuperClass();
+ // It is not an interface. If the referring class is in the class hierarchy of the
+ // referenced class in the bytecode, we use its super class. Otherwise, we cannot
+ // resolve the method.
+ if (!method_reference_class->IsAssignableFrom(referring_class)) {
+ return nullptr;
+ }
+ mirror::Class* super_class = referring_class->GetSuperClass();
if (resolved_method->GetMethodIndex() >= super_class->GetVTableLength()) {
// The super class does not have the method.
return nullptr;
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index 79c320309c..d983a9fa19 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -943,6 +943,11 @@ extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
if (!Runtime::Create(options, ignore_unrecognized)) {
return JNI_ERR;
}
+
+ // Initialize native loader. This step makes sure we have
+ // everything set up before we start using JNI.
+ android::InitializeNativeLoader();
+
Runtime* runtime = Runtime::Current();
bool started = runtime->Start();
if (!started) {
@@ -952,10 +957,6 @@ extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
return JNI_ERR;
}
- // Initialize native loader. This step makes sure we have
- // everything set up before we start using JNI.
- android::InitializeNativeLoader();
-
*p_env = Thread::Current()->GetJniEnv();
*p_vm = runtime->GetJavaVM();
return JNI_OK;
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index e9317a5435..dcc6300636 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -103,13 +103,13 @@ JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& opt
}
if (options.Exists(RuntimeArgumentMap::JITInvokeTransitionWeight)) {
+ jit_options->invoke_transition_weight_ =
+ *options.Get(RuntimeArgumentMap::JITInvokeTransitionWeight);
if (jit_options->invoke_transition_weight_ > jit_options->warmup_threshold_) {
LOG(FATAL) << "Invoke transition weight is above the warmup threshold.";
} else if (jit_options->invoke_transition_weight_ == 0) {
- LOG(FATAL) << "Invoke transition ratio cannot be 0.";
+ LOG(FATAL) << "Invoke transition weight cannot be 0.";
}
- jit_options->invoke_transition_weight_ =
- *options.Get(RuntimeArgumentMap::JITInvokeTransitionWeight);
} else {
jit_options->invoke_transition_weight_ = std::max(
jit_options->warmup_threshold_ / Jit::kDefaultInvokeTransitionWeightRatio,
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 0abe39d872..0126b4d0a4 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -16,6 +16,8 @@
#include "dalvik_system_DexFile.h"
+#include <sstream>
+
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/stringprintf.h"
@@ -27,6 +29,7 @@
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
#include "mirror/string.h"
+#include "oat_file.h"
#include "oat_file_assistant.h"
#include "oat_file_manager.h"
#include "os.h"
@@ -387,6 +390,61 @@ static jint GetDexOptNeeded(JNIEnv* env,
return oat_file_assistant.GetDexOptNeeded(filter);
}
+static jstring DexFile_getDexFileStatus(JNIEnv* env,
+ jclass,
+ jstring javaFilename,
+ jstring javaInstructionSet) {
+ ScopedUtfChars filename(env, javaFilename);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ ScopedUtfChars instruction_set(env, javaInstructionSet);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ const InstructionSet target_instruction_set = GetInstructionSetFromString(
+ instruction_set.c_str());
+ if (target_instruction_set == kNone) {
+ ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
+ std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str()));
+ env->ThrowNew(iae.get(), message.c_str());
+ return nullptr;
+ }
+
+ OatFileAssistant oat_file_assistant(filename.c_str(), target_instruction_set,
+ false /* profile_changed */,
+ false /* load_executable */);
+
+ std::ostringstream status;
+ bool oat_file_exists = false;
+ bool odex_file_exists = false;
+ if (oat_file_assistant.OatFileExists()) {
+ oat_file_exists = true;
+ status << *oat_file_assistant.OatFileName() << " [compilation_filter=";
+ status << CompilerFilter::NameOfFilter(oat_file_assistant.OatFileCompilerFilter());
+ status << ", status=" << oat_file_assistant.OatFileStatus();
+ }
+
+ if (oat_file_assistant.OdexFileExists()) {
+ odex_file_exists = true;
+ if (oat_file_exists) {
+ status << "] ";
+ }
+ status << *oat_file_assistant.OdexFileName() << " [compilation_filter=";
+ status << CompilerFilter::NameOfFilter(oat_file_assistant.OdexFileCompilerFilter());
+ status << ", status=" << oat_file_assistant.OdexFileStatus();
+ }
+
+ if (!oat_file_exists && !odex_file_exists) {
+ status << "invalid[";
+ }
+
+ status << "]";
+ return env->NewStringUTF(status.str().c_str());
+}
+
static jint DexFile_getDexOptNeeded(JNIEnv* env,
jclass,
jstring javaFilename,
@@ -481,6 +539,16 @@ static jstring DexFile_getNonProfileGuidedCompilerFilter(JNIEnv* env,
return env->NewStringUTF(new_filter_str.c_str());
}
+static jboolean DexFile_isBackedByOatFile(JNIEnv* env, jclass, jobject cookie) {
+ const OatFile* oat_file = nullptr;
+ std::vector<const DexFile*> dex_files;
+ if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) {
+ DCHECK(env->ExceptionCheck());
+ return false;
+ }
+ return oat_file != nullptr;
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
NATIVE_METHOD(DexFile,
@@ -506,6 +574,9 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(DexFile,
getNonProfileGuidedCompilerFilter,
"(Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, isBackedByOatFile, "(Ljava/lang/Object;)Z"),
+ NATIVE_METHOD(DexFile, getDexFileStatus,
+ "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;")
};
void register_dalvik_system_DexFile(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index e98331eea3..54b8afd1f3 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -64,7 +64,8 @@ static jobjectArray Constructor_getDeclaredAnnotations(JNIEnv* env, jobject java
static jobjectArray Constructor_getExceptionTypes(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
- ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
+ ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod)
+ ->GetInterfaceMethodIfProxy(sizeof(void*));
mirror::ObjectArray<mirror::Class>* result_array =
method->GetDexFile()->GetExceptionTypesForMethod(method);
if (result_array == nullptr) {
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index a508e87c87..713e2f3fa9 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -30,6 +30,7 @@
#include "base/logging.h"
#include "base/stringprintf.h"
+#include "compiler_filter.h"
#include "class_linker.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
@@ -43,6 +44,24 @@
namespace art {
+std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) {
+ switch (status) {
+ case OatFileAssistant::kOatOutOfDate:
+ stream << "kOatOutOfDate";
+ break;
+ case OatFileAssistant::kOatUpToDate:
+ stream << "kOatUpToDate";
+ break;
+ case OatFileAssistant::kOatNeedsRelocation:
+ stream << "kOatNeedsRelocation";
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return stream;
+}
+
OatFileAssistant::OatFileAssistant(const char* dex_location,
const InstructionSet isa,
bool profile_changed,
@@ -377,6 +396,12 @@ bool OatFileAssistant::OdexFileIsUpToDate() {
return cached_odex_file_is_up_to_date_;
}
+CompilerFilter::Filter OatFileAssistant::OdexFileCompilerFilter() {
+ const OatFile* odex_file = GetOdexFile();
+ CHECK(odex_file != nullptr);
+
+ return odex_file->GetCompilerFilter();
+}
std::string OatFileAssistant::ArtFileName(const OatFile* oat_file) const {
const std::string oat_file_location = oat_file->GetLocation();
// Replace extension with .art
@@ -455,6 +480,13 @@ bool OatFileAssistant::OatFileIsUpToDate() {
return cached_oat_file_is_up_to_date_;
}
+CompilerFilter::Filter OatFileAssistant::OatFileCompilerFilter() {
+ const OatFile* oat_file = GetOatFile();
+ CHECK(oat_file != nullptr);
+
+ return oat_file->GetCompilerFilter();
+}
+
OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
// TODO: This could cause GivenOatFileIsOutOfDate to be called twice, which
// is more work than we need to do. If performance becomes a concern, and
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 85f4a47868..f48cdf343c 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <memory>
+#include <sstream>
#include <string>
#include "arch/instruction_set.h"
@@ -211,6 +212,9 @@ class OatFileAssistant {
bool OdexFileIsOutOfDate();
bool OdexFileNeedsRelocation();
bool OdexFileIsUpToDate();
+ // Must only be called if the associated odex file exists, i.e, if
+ // |OdexFileExists() == true|.
+ CompilerFilter::Filter OdexFileCompilerFilter();
// When the dex files is compiled on the target device, the oat file is the
// result. The oat file will have been relocated to some
@@ -227,6 +231,9 @@ class OatFileAssistant {
bool OatFileIsOutOfDate();
bool OatFileNeedsRelocation();
bool OatFileIsUpToDate();
+ // Must only be called if the associated oat file exists, i.e, if
+ // |OatFileExists() == true|.
+ CompilerFilter::Filter OatFileCompilerFilter();
// Return image file name. Does not cache since it relies on the oat file.
std::string ArtFileName(const OatFile* oat_file) const;
@@ -436,6 +443,8 @@ class OatFileAssistant {
DISALLOW_COPY_AND_ASSIGN(OatFileAssistant);
};
+std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status);
+
} // namespace art
#endif // ART_RUNTIME_OAT_FILE_ASSISTANT_H_
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index d05ae42b0e..2b96328f80 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -4101,8 +4101,8 @@ ArtMethod* MethodVerifier::VerifyInvocationArgs(
<< " to super " << PrettyMethod(res_method);
return nullptr;
}
- mirror::Class* super_klass = super.GetClass();
- if (res_method->GetMethodIndex() >= super_klass->GetVTableLength()) {
+ if (!reference_class->IsAssignableFrom(GetDeclaringClass().GetClass()) ||
+ (res_method->GetMethodIndex() >= super.GetClass()->GetVTableLength())) {
Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from "
<< PrettyMethod(dex_method_idx_, *dex_file_)
<< " to super " << super
diff --git a/test/044-proxy/expected.txt b/test/044-proxy/expected.txt
index be7023e49d..2a5f0b90db 100644
--- a/test/044-proxy/expected.txt
+++ b/test/044-proxy/expected.txt
@@ -95,3 +95,5 @@ Proxy narrowed invocation return type passed
5.8
JNI_OnLoad called
callback
+Found constructor.
+Found constructors with 0 exceptions
diff --git a/test/044-proxy/src/ConstructorProxy.java b/test/044-proxy/src/ConstructorProxy.java
new file mode 100644
index 0000000000..95d150cbbd
--- /dev/null
+++ b/test/044-proxy/src/ConstructorProxy.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * Tests proxies when used with constructor methods.
+ */
+class ConstructorProxy implements InvocationHandler {
+ public static void main() {
+ try {
+ new ConstructorProxy().runTest();
+ } catch (Exception e) {
+ System.out.println("Unexpected failure occured");
+ e.printStackTrace();
+ }
+ }
+
+ public void runTest() throws Exception {
+ Class<?> proxyClass = Proxy.getProxyClass(
+ getClass().getClassLoader(),
+ new Class<?>[] { Runnable.class }
+ );
+ Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
+ System.out.println("Found constructor.");
+ // We used to crash when asking the exception types of the constructor, because the runtime was
+ // not using the non-proxy ArtMethod
+ Object[] exceptions = constructor.getExceptionTypes();
+ System.out.println("Found constructors with " + exceptions.length + " exceptions");
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return args[0];
+ }
+}
+
diff --git a/test/044-proxy/src/Main.java b/test/044-proxy/src/Main.java
index 1f23b95cf0..9dadb7c6ea 100644
--- a/test/044-proxy/src/Main.java
+++ b/test/044-proxy/src/Main.java
@@ -31,6 +31,7 @@ public class Main {
NarrowingTest.main(null);
FloatSelect.main(null);
NativeProxy.main(args);
+ ConstructorProxy.main();
}
// The following code maps from the actual proxy class names (eg $Proxy2) to their test output
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index be666e94fa..15a9504acf 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -16,9 +16,69 @@
public class Main {
+ public static boolean doThrow = false;
+
+ public static void assertIntEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void assertBooleanEquals(boolean expected, boolean result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
public static void main(String[] args) {
stringEqualsSame();
stringArgumentNotNull("Foo");
+
+ assertIntEquals(0, $opt$noinline$getStringLength(""));
+ assertIntEquals(3, $opt$noinline$getStringLength("abc"));
+ assertIntEquals(10, $opt$noinline$getStringLength("0123456789"));
+
+ assertBooleanEquals(true, $opt$noinline$isStringEmpty(""));
+ assertBooleanEquals(false, $opt$noinline$isStringEmpty("abc"));
+ assertBooleanEquals(false, $opt$noinline$isStringEmpty("0123456789"));
+ }
+
+ /// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) instruction_simplifier (before)
+ /// CHECK-DAG: <<Length:i\d+>> InvokeVirtual intrinsic:StringLength
+ /// CHECK-DAG: Return [<<Length>>]
+
+ /// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) instruction_simplifier (after)
+ /// CHECK-DAG: <<String:l\d+>> ParameterValue
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>]
+ /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true
+ /// CHECK-DAG: Return [<<Length>>]
+
+ /// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringLength
+
+ static public int $opt$noinline$getStringLength(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.length();
+ }
+
+ /// CHECK-START: boolean Main.$opt$noinline$isStringEmpty(java.lang.String) instruction_simplifier (before)
+ /// CHECK-DAG: <<IsEmpty:z\d+>> InvokeVirtual intrinsic:StringIsEmpty
+ /// CHECK-DAG: Return [<<IsEmpty>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$isStringEmpty(java.lang.String) instruction_simplifier (after)
+ /// CHECK-DAG: <<String:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>]
+ /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true
+ /// CHECK-DAG: <<IsEmpty:z\d+>> Equal [<<Length>>,<<Const0>>]
+ /// CHECK-DAG: Return [<<IsEmpty>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$isStringEmpty(java.lang.String) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringIsEmpty
+
+ static public boolean $opt$noinline$isStringEmpty(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.isEmpty();
}
/// CHECK-START: boolean Main.stringEqualsSame() instruction_simplifier (before)
diff --git a/test/586-checker-null-array-get/src/Main.java b/test/586-checker-null-array-get/src/Main.java
index 332cfb0a53..e0782bc84d 100644
--- a/test/586-checker-null-array-get/src/Main.java
+++ b/test/586-checker-null-array-get/src/Main.java
@@ -14,10 +14,20 @@
* limitations under the License.
*/
+class Test1 {
+ int[] iarr;
+}
+
+class Test2 {
+ float[] farr;
+}
+
public class Main {
public static Object[] getObjectArray() { return null; }
public static long[] getLongArray() { return null; }
public static Object getNull() { return null; }
+ public static Test1 getNullTest1() { return null; }
+ public static Test2 getNullTest2() { return null; }
public static void main(String[] args) {
try {
@@ -26,13 +36,25 @@ public class Main {
} catch (NullPointerException e) {
// Expected.
}
+ try {
+ bar();
+ throw new Error("Expected NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected.
+ }
+ try {
+ test1();
+ throw new Error("Expected NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected.
+ }
}
/// CHECK-START: void Main.foo() load_store_elimination (after)
- /// CHECK-DAG: <<Null:l\d+>> NullConstant
- /// CHECK-DAG: <<Check:l\d+>> NullCheck [<<Null>>]
- /// CHECK-DAG: <<Get1:j\d+>> ArrayGet [<<Check>>,{{i\d+}}]
- /// CHECK-DAG: <<Get2:l\d+>> ArrayGet [<<Check>>,{{i\d+}}]
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: <<Check:l\d+>> NullCheck [<<Null>>]
+ /// CHECK-DAG: <<Get1:j\d+>> ArrayGet [<<Check>>,{{i\d+}}]
+ /// CHECK-DAG: <<Get2:l\d+>> ArrayGet [<<Check>>,{{i\d+}}]
public static void foo() {
longField = getLongArray()[0];
objectField = getObjectArray()[0];
@@ -56,7 +78,7 @@ public class Main {
// elimination pass to add a HDeoptimize. Not having the bounds check helped
// the load store elimination think it could merge two ArrayGet with different
// types.
- String[] array = ((String[])getNull());
+ String[] array = (String[])getNull();
objectField = array[0];
objectField = array[1];
objectField = array[2];
@@ -68,6 +90,23 @@ public class Main {
longField = longArray[3];
}
+ /// CHECK-START: float Main.test1() load_store_elimination (after)
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: <<Check1:l\d+>> NullCheck [<<Null>>]
+ /// CHECK-DAG: <<FieldGet1:l\d+>> InstanceFieldGet [<<Check1>>] field_name:Test1.iarr
+ /// CHECK-DAG: <<Check2:l\d+>> NullCheck [<<FieldGet1>>]
+ /// CHECK-DAG: <<ArrayGet1:i\d+>> ArrayGet [<<Check2>>,{{i\d+}}]
+ /// CHECK-DAG: <<ArrayGet2:f\d+>> ArrayGet [<<Check2>>,{{i\d+}}]
+ /// CHECK-DAG: Return [<<ArrayGet2>>]
+ public static float test1() {
+ Test1 test1 = getNullTest1();
+ Test2 test2 = getNullTest2();;
+ int[] iarr = test1.iarr;
+ float[] farr = test2.farr;
+ iarr[0] = iarr[1];
+ return farr[0];
+ }
+
public static long longField;
public static Object objectField;
}
diff --git a/test/594-invoke-super/expected.txt b/test/594-invoke-super/expected.txt
new file mode 100644
index 0000000000..de26026c99
--- /dev/null
+++ b/test/594-invoke-super/expected.txt
@@ -0,0 +1,7 @@
+new A
+I am A's foo
+new B
+I am B's foo
+new A
+new B
+passed
diff --git a/test/594-invoke-super/info.txt b/test/594-invoke-super/info.txt
new file mode 100644
index 0000000000..440d8b8c66
--- /dev/null
+++ b/test/594-invoke-super/info.txt
@@ -0,0 +1 @@
+Invoke-super on various references.
diff --git a/test/594-invoke-super/smali/invoke-super.smali b/test/594-invoke-super/smali/invoke-super.smali
new file mode 100644
index 0000000000..6f787dd170
--- /dev/null
+++ b/test/594-invoke-super/smali/invoke-super.smali
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LZ;
+.super LA;
+
+.method public constructor <init>()V
+.registers 1
+ invoke-direct {v0}, LA;-><init>()V
+ return-void
+.end method
+
+.method public foo()V
+.registers 3
+ new-instance v0, LY;
+ invoke-direct {v0}, LY;-><init>()V
+ invoke-super {v0}, LY;->foo()V
+ return-void
+.end method
diff --git a/test/594-invoke-super/src/Main.java b/test/594-invoke-super/src/Main.java
new file mode 100644
index 0000000000..53f2bbf67a
--- /dev/null
+++ b/test/594-invoke-super/src/Main.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+//
+// Two classes A and B with method foo().
+//
+
+class A {
+ A() { System.out.println("new A"); }
+
+ public void foo() { System.out.println("I am A's foo"); }
+
+ // We previously used to invoke this method with a Y instance, due
+ // to invoke-super underspecified behavior.
+ public void bar() { System.out.println("I am A's bar"); }
+}
+
+class B {
+ B() { System.out.println("new B"); }
+
+ public void foo() { System.out.println("I am B's foo"); }
+}
+
+//
+// Two subclasses X and Y that call foo() on super.
+//
+
+class X extends A {
+ public void foo() { super.foo(); }
+}
+
+class Y extends B {
+ public void foo() { super.foo(); }
+}
+
+//
+// Driver class.
+//
+
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ // The normal stuff, X's super goes to A, Y's super goes to B.
+ new X().foo();
+ new Y().foo();
+
+ // And now it gets interesting.
+
+ // In bytecode, we define a class Z that is a subclass of A, and we call
+ // invoke-super on an instance of Y.
+ Class<?> z = Class.forName("Z");
+ Method m = z.getMethod("foo");
+ try {
+ m.invoke(z.newInstance());
+ throw new Error("Expected InvocationTargetException");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof NoSuchMethodError)) {
+ throw new Error("Expected NoSuchMethodError");
+ }
+ }
+
+ System.out.println("passed");
+ }
+}
diff --git a/test/run-test b/test/run-test
index 925d8f2c1e..047e3fb606 100755
--- a/test/run-test
+++ b/test/run-test
@@ -126,9 +126,6 @@ image_suffix=""
pic_image_suffix=""
multi_image_suffix=""
android_root="/system"
-# By default we will use optimizing.
-image_args="-Xcompiler-option --compiler-backend=Optimizing"
-image_suffix="-optimizing"
while true; do
if [ "x$1" = "x--host" ]; then
@@ -151,6 +148,7 @@ while true; do
elif [ "x$1" = "x--jvm" ]; then
target_mode="no"
runtime="jvm"
+ image_args=""
prebuild_mode="no"
NEED_DEX="false"
USE_JACK="false"
@@ -247,22 +245,22 @@ while true; do
run_args="${run_args} --zygote"
shift
elif [ "x$1" = "x--interpreter" ]; then
- image_args="--interpreter"
+ run_args="${run_args} --interpreter"
image_suffix="-interpreter"
shift
elif [ "x$1" = "x--jit" ]; then
- image_args="--jit"
+ run_args="${run_args} --jit"
image_suffix="-jit"
shift
elif [ "x$1" = "x--optimizing" ]; then
- image_args="-Xcompiler-option --compiler-backend=Optimizing"
+ run_args="${run_args} -Xcompiler-option --compiler-backend=Optimizing"
image_suffix="-optimizing"
shift
elif [ "x$1" = "x--no-verify" ]; then
run_args="${run_args} --no-verify"
shift
elif [ "x$1" = "x--verify-soft-fail" ]; then
- image_args="${run_args} --verify-soft-fail"
+ run_args="${run_args} --verify-soft-fail"
image_suffix="-interp-ac"
shift
elif [ "x$1" = "x--no-optimize" ]; then
@@ -351,7 +349,6 @@ while true; do
fi
done
-run_args="${run_args} ${image_args}"
# Allocate file descriptor real_stderr and redirect it to the shell's error
# output (fd 2).
if [ ${BASH_VERSINFO[1]} -ge 4 ] && [ ${BASH_VERSINFO[2]} -ge 1 ]; then
diff --git a/tools/dmtracedump/tracedump.cc b/tools/dmtracedump/tracedump.cc
index f70e2c2207..3afee6fdcb 100644
--- a/tools/dmtracedump/tracedump.cc
+++ b/tools/dmtracedump/tracedump.cc
@@ -512,10 +512,10 @@ int32_t compareUniqueExclusive(const void* a, const void* b) {
void freeDataKeys(DataKeys* pKeys) {
if (pKeys == nullptr) return;
- free(pKeys->fileData);
- free(pKeys->threads);
- free(pKeys->methods);
- free(pKeys);
+ delete[] pKeys->fileData;
+ delete[] pKeys->threads;
+ delete[] pKeys->methods;
+ delete pKeys;
}
/*
@@ -822,8 +822,8 @@ void sortMethodList(DataKeys* pKeys) {
DataKeys* parseKeys(FILE* fp, int32_t verbose) {
int64_t offset;
DataKeys* pKeys = new DataKeys();
- memset(pKeys, 0, sizeof(DataKeys));
if (pKeys == nullptr) return nullptr;
+ memset(pKeys, 0, sizeof(DataKeys));
/*
* We load the entire file into memory. We do this, rather than memory-
@@ -865,9 +865,13 @@ DataKeys* parseKeys(FILE* fp, int32_t verbose) {
return nullptr;
}
- /* Reduce our allocation now that we know where the end of the key section is. */
- pKeys->fileData = reinterpret_cast<char*>(realloc(pKeys->fileData, offset));
- pKeys->fileLen = offset;
+ /*
+ * Although it is tempting to reduce our allocation now that we know where the
+ * end of the key section is, there is a pitfall. The method names and
+ * signatures in the method list contain pointers into the fileData area.
+ * Realloc or free will result in corruption.
+ */
+
/* Leave fp pointing to the beginning of the data section. */
fseek(fp, offset, SEEK_SET);
@@ -2607,7 +2611,7 @@ int32_t main(int32_t argc, char** argv) {
if (gOptions.graphFileName != nullptr) {
createInclusiveProfileGraphNew(dataKeys);
}
- free(methods);
+ delete[] methods;
}
freeDataKeys(dataKeys);