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_arm.cc21
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc19
-rw-r--r--compiler/optimizing/intrinsics_list.h2
-rw-r--r--compiler/optimizing/intrinsics_mips.cc9
-rw-r--r--compiler/optimizing/intrinsics_mips64.cc9
-rw-r--r--compiler/optimizing/intrinsics_x86.cc7
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc7
-rw-r--r--compiler/optimizing/load_store_elimination.cc15
-rw-r--r--compiler/optimizing/nodes.cc101
-rw-r--r--compiler/optimizing/nodes.h16
-rw-r--r--compiler/optimizing/ssa_builder.cc4
-rw-r--r--compiler/optimizing/ssa_phi_elimination.cc20
-rw-r--r--profman/profman.cc6
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S112
-rw-r--r--runtime/dex_file_verifier.cc62
-rw-r--r--runtime/dex_file_verifier.h2
-rw-r--r--runtime/dex_file_verifier_test.cc149
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h21
-rw-r--r--runtime/java_vm_ext.cc40
-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.cc39
-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/004-JniTest/jni_test.cc334
-rw-r--r--test/004-ReferenceMap/stack_walk_refmap_jni.cc1
-rw-r--r--test/004-StackWalk/stack_walk_jni.cc1
-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/136-daemon-jni-shutdown/daemon_jni_shutdown.cc3
-rw-r--r--test/536-checker-intrinsic-optimization/src/Main.java60
-rw-r--r--test/566-polymorphic-inlining/src/Main.java1
-rw-r--r--test/570-checker-osr/osr.cc2
-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
-rw-r--r--test/594-load-string-regression/expected.txt1
-rw-r--r--test/594-load-string-regression/info.txt2
-rw-r--r--test/594-load-string-regression/src/Main.java77
-rw-r--r--test/595-profile-saving/profile-saving.cc1
-rw-r--r--test/596-app-images/app_images.cc1
-rw-r--r--test/598-checker-irreducible-dominance/expected.txt0
-rw-r--r--test/598-checker-irreducible-dominance/info.txt2
-rw-r--r--test/598-checker-irreducible-dominance/smali/IrreducibleLoop.smali52
-rw-r--r--test/598-checker-irreducible-dominance/src/Main.java25
-rw-r--r--test/Android.libarttest.mk7
-rwxr-xr-xtest/etc/run-test-jar3
-rwxr-xr-xtest/run-test3
-rw-r--r--tools/dmtracedump/tracedump.cc22
68 files changed, 1283 insertions, 396 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_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 146fea1fe0..4e3ace498d 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -1115,15 +1115,15 @@ static void GenerateVisitStringIndexOf(HInvoke* invoke,
ArenaAllocator* allocator,
bool start_at_zero) {
LocationSummary* locations = invoke->GetLocations();
- Register tmp_reg = locations->GetTemp(0).AsRegister<Register>();
// Note that the null check must have been done earlier.
DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
// Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
- // or directly dispatch if we have a constant.
+ // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
SlowPathCode* slow_path = nullptr;
- if (invoke->InputAt(1)->IsIntConstant()) {
+ HInstruction* code_point = invoke->InputAt(1);
+ if (code_point->IsIntConstant()) {
if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) >
std::numeric_limits<uint16_t>::max()) {
// Always needs the slow-path. We could directly dispatch to it, but this case should be
@@ -1134,16 +1134,18 @@ static void GenerateVisitStringIndexOf(HInvoke* invoke,
__ Bind(slow_path->GetExitLabel());
return;
}
- } else {
+ } else if (code_point->GetType() != Primitive::kPrimChar) {
Register char_reg = locations->InAt(1).AsRegister<Register>();
- __ LoadImmediate(tmp_reg, std::numeric_limits<uint16_t>::max());
- __ cmp(char_reg, ShifterOperand(tmp_reg));
+ // 0xffff is not modified immediate but 0x10000 is, so use `>= 0x10000` instead of `> 0xffff`.
+ __ cmp(char_reg,
+ ShifterOperand(static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()) + 1));
slow_path = new (allocator) IntrinsicSlowPathARM(invoke);
codegen->AddSlowPath(slow_path);
- __ b(slow_path->GetEntryLabel(), HI);
+ __ b(slow_path->GetEntryLabel(), HS);
}
if (start_at_zero) {
+ Register tmp_reg = locations->GetTemp(0).AsRegister<Register>();
DCHECK_EQ(tmp_reg, R2);
// Start-index = 0.
__ LoadImmediate(tmp_reg, 0);
@@ -1170,7 +1172,7 @@ void IntrinsicLocationsBuilderARM::VisitStringIndexOf(HInvoke* invoke) {
locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
locations->SetOut(Location::RegisterLocation(R0));
- // Need a temp for slow-path codepoint compare, and need to send start-index=0.
+ // Need to send start-index=0.
locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
}
@@ -1190,9 +1192,6 @@ void IntrinsicLocationsBuilderARM::VisitStringIndexOfAfter(HInvoke* invoke) {
locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
locations->SetOut(Location::RegisterLocation(R0));
-
- // Need a temp for slow-path codepoint compare.
- locations->AddTemp(Location::RequiresRegister());
}
void IntrinsicCodeGeneratorARM::VisitStringIndexOfAfter(HInvoke* invoke) {
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 1d8229674c..cc5fd65e2e 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1390,15 +1390,15 @@ static void GenerateVisitStringIndexOf(HInvoke* invoke,
ArenaAllocator* allocator,
bool start_at_zero) {
LocationSummary* locations = invoke->GetLocations();
- Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
// Note that the null check must have been done earlier.
DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
// Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
- // or directly dispatch if we have a constant.
+ // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
SlowPathCodeARM64* slow_path = nullptr;
- if (invoke->InputAt(1)->IsIntConstant()) {
+ HInstruction* code_point = invoke->InputAt(1);
+ if (code_point->IsIntConstant()) {
if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > 0xFFFFU) {
// Always needs the slow-path. We could directly dispatch to it, but this case should be
// rare, so for simplicity just put the full slow-path down and branch unconditionally.
@@ -1408,17 +1408,17 @@ static void GenerateVisitStringIndexOf(HInvoke* invoke,
__ Bind(slow_path->GetExitLabel());
return;
}
- } else {
+ } else if (code_point->GetType() != Primitive::kPrimChar) {
Register char_reg = WRegisterFrom(locations->InAt(1));
- __ Mov(tmp_reg, 0xFFFF);
- __ Cmp(char_reg, Operand(tmp_reg));
+ __ Tst(char_reg, 0xFFFF0000);
slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
codegen->AddSlowPath(slow_path);
- __ B(hi, slow_path->GetEntryLabel());
+ __ B(ne, slow_path->GetEntryLabel());
}
if (start_at_zero) {
// Start-index = 0.
+ Register tmp_reg = WRegisterFrom(locations->GetTemp(0));
__ Mov(tmp_reg, 0);
}
@@ -1442,7 +1442,7 @@ void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) {
locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
- // Need a temp for slow-path codepoint compare, and need to send start_index=0.
+ // Need to send start_index=0.
locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
}
@@ -1462,9 +1462,6 @@ void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
-
- // Need a temp for slow-path codepoint compare.
- locations->AddTemp(Location::RequiresRegister());
}
void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) {
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/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 46195c104a..20b61f8a1c 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2067,10 +2067,11 @@ static void GenerateStringIndexOf(HInvoke* invoke,
// Note that the null check must have been done earlier.
DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
- // Check for code points > 0xFFFF. Either a slow-path check when we
- // don't know statically, or directly dispatch if we have a constant.
+ // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
+ // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
SlowPathCodeMIPS* slow_path = nullptr;
- if (invoke->InputAt(1)->IsIntConstant()) {
+ HInstruction* code_point = invoke->InputAt(1);
+ if (code_point->IsIntConstant()) {
if (!IsUint<16>(invoke->InputAt(1)->AsIntConstant()->GetValue())) {
// Always needs the slow-path. We could directly dispatch to it,
// but this case should be rare, so for simplicity just put the
@@ -2081,7 +2082,7 @@ static void GenerateStringIndexOf(HInvoke* invoke,
__ Bind(slow_path->GetExitLabel());
return;
}
- } else {
+ } else if (code_point->GetType() != Primitive::kPrimChar) {
Register char_reg = locations->InAt(1).AsRegister<Register>();
// The "bltu" conditional branch tests to see if the character value
// fits in a valid 16-bit (MIPS halfword) value. If it doesn't then
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 1524e1e011..7188e1cc75 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1563,10 +1563,11 @@ static void GenerateStringIndexOf(HInvoke* invoke,
// Note that the null check must have been done earlier.
DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
- // Check for code points > 0xFFFF. Either a slow-path check when we
- // don't know statically, or directly dispatch if we have a constant.
+ // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
+ // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
SlowPathCodeMIPS64* slow_path = nullptr;
- if (invoke->InputAt(1)->IsIntConstant()) {
+ HInstruction* code_point = invoke->InputAt(1);
+ if (code_point->IsIntConstant()) {
if (!IsUint<16>(invoke->InputAt(1)->AsIntConstant()->GetValue())) {
// Always needs the slow-path. We could directly dispatch to it,
// but this case should be rare, so for simplicity just put the
@@ -1577,7 +1578,7 @@ static void GenerateStringIndexOf(HInvoke* invoke,
__ Bind(slow_path->GetExitLabel());
return;
}
- } else {
+ } else if (code_point->GetType() != Primitive::kPrimChar) {
GpuRegister char_reg = locations->InAt(1).AsRegister<GpuRegister>();
__ LoadConst32(tmp_reg, std::numeric_limits<uint16_t>::max());
slow_path = new (allocator) IntrinsicSlowPathMIPS64(invoke);
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 4aab3e2768..d0edecae22 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1418,9 +1418,10 @@ static void GenerateStringIndexOf(HInvoke* invoke,
DCHECK_EQ(out, EDI);
// Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
- // or directly dispatch if we have a constant.
+ // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
SlowPathCode* slow_path = nullptr;
- if (invoke->InputAt(1)->IsIntConstant()) {
+ HInstruction* code_point = invoke->InputAt(1);
+ if (code_point->IsIntConstant()) {
if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) >
std::numeric_limits<uint16_t>::max()) {
// Always needs the slow-path. We could directly dispatch to it, but this case should be
@@ -1431,7 +1432,7 @@ static void GenerateStringIndexOf(HInvoke* invoke,
__ Bind(slow_path->GetExitLabel());
return;
}
- } else {
+ } else if (code_point->GetType() != Primitive::kPrimChar) {
__ cmpl(search_value, Immediate(std::numeric_limits<uint16_t>::max()));
slow_path = new (allocator) IntrinsicSlowPathX86(invoke);
codegen->AddSlowPath(slow_path);
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 1d32dc7bc5..4ee2368ff5 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1517,9 +1517,10 @@ static void GenerateStringIndexOf(HInvoke* invoke,
DCHECK_EQ(out.AsRegister(), RDI);
// Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
- // or directly dispatch if we have a constant.
+ // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
SlowPathCode* slow_path = nullptr;
- if (invoke->InputAt(1)->IsIntConstant()) {
+ HInstruction* code_point = invoke->InputAt(1);
+ if (code_point->IsIntConstant()) {
if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) >
std::numeric_limits<uint16_t>::max()) {
// Always needs the slow-path. We could directly dispatch to it, but this case should be
@@ -1530,7 +1531,7 @@ static void GenerateStringIndexOf(HInvoke* invoke,
__ Bind(slow_path->GetExitLabel());
return;
}
- } else {
+ } else if (code_point->GetType() != Primitive::kPrimChar) {
__ cmpl(search_value, Immediate(std::numeric_limits<uint16_t>::max()));
slow_path = new (allocator) IntrinsicSlowPathX86_64(invoke);
codegen->AddSlowPath(slow_path);
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 6703695484..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());
@@ -206,17 +208,35 @@ HInstruction* HBasicBlock::GetFirstInstructionDisregardMoves() const {
return instruction;
}
+static bool UpdateDominatorOfSuccessor(HBasicBlock* block, HBasicBlock* successor) {
+ DCHECK(ContainsElement(block->GetSuccessors(), successor));
+
+ HBasicBlock* old_dominator = successor->GetDominator();
+ HBasicBlock* new_dominator =
+ (old_dominator == nullptr) ? block
+ : CommonDominator::ForPair(old_dominator, block);
+
+ if (old_dominator == new_dominator) {
+ return false;
+ } else {
+ successor->SetDominator(new_dominator);
+ return true;
+ }
+}
+
void HGraph::ComputeDominanceInformation() {
DCHECK(reverse_post_order_.empty());
reverse_post_order_.reserve(blocks_.size());
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_);
@@ -228,15 +248,7 @@ void HGraph::ComputeDominanceInformation() {
worklist.pop_back();
} else {
HBasicBlock* successor = current->GetSuccessors()[successors_visited[current_id]++];
-
- if (successor->GetDominator() == nullptr) {
- successor->SetDominator(current);
- } else {
- // The CommonDominator can work for multiple blocks as long as the
- // domination information doesn't change. However, since we're changing
- // that information here, we can use the finder only for pairs of blocks.
- successor->SetDominator(CommonDominator::ForPair(successor->GetDominator(), current));
- }
+ UpdateDominatorOfSuccessor(current, successor);
// Once all the forward edges have been visited, we know the immediate
// dominator of the block. We can then start visiting its successors.
@@ -248,6 +260,44 @@ void HGraph::ComputeDominanceInformation() {
}
}
+ // Check if the graph has back edges not dominated by their respective headers.
+ // If so, we need to update the dominators of those headers and recursively of
+ // their successors. We do that with a fix-point iteration over all blocks.
+ // The algorithm is guaranteed to terminate because it loops only if the sum
+ // of all dominator chains has decreased in the current iteration.
+ bool must_run_fix_point = false;
+ for (HBasicBlock* block : blocks_) {
+ if (block != nullptr &&
+ block->IsLoopHeader() &&
+ block->GetLoopInformation()->HasBackEdgeNotDominatedByHeader()) {
+ must_run_fix_point = true;
+ break;
+ }
+ }
+ if (must_run_fix_point) {
+ bool update_occurred = true;
+ while (update_occurred) {
+ update_occurred = false;
+ for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ for (HBasicBlock* successor : block->GetSuccessors()) {
+ update_occurred |= UpdateDominatorOfSuccessor(block, successor);
+ }
+ }
+ }
+ }
+
+ // Make sure that there are no remaining blocks whose dominator information
+ // needs to be updated.
+ if (kIsDebugBuild) {
+ for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ for (HBasicBlock* successor : block->GetSuccessors()) {
+ DCHECK(!UpdateDominatorOfSuccessor(block, successor));
+ }
+ }
+ }
+
// Populate `dominated_blocks_` information after computing all dominators.
// The potential presence of irreducible loops requires to do it after.
for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
@@ -595,20 +645,16 @@ void HLoopInformation::Populate() {
blocks_.SetBit(header_->GetBlockId());
header_->SetInLoop(this);
- bool is_irreducible_loop = false;
- for (HBasicBlock* back_edge : GetBackEdges()) {
- DCHECK(back_edge->GetDominator() != nullptr);
- if (!header_->Dominates(back_edge)) {
- is_irreducible_loop = true;
- break;
- }
- }
+ bool is_irreducible_loop = HasBackEdgeNotDominatedByHeader();
if (is_irreducible_loop) {
ArenaBitVector visited(graph->GetArena(),
graph->GetBlocks().size(),
/* expandable */ false,
kArenaAllocGraphBuilder);
+ // Stop marking blocks at the loop header.
+ visited.SetBit(header_->GetBlockId());
+
for (HBasicBlock* back_edge : GetBackEdges()) {
PopulateIrreducibleRecursive(back_edge, &visited);
}
@@ -667,6 +713,16 @@ size_t HLoopInformation::GetLifetimeEnd() const {
return last_position;
}
+bool HLoopInformation::HasBackEdgeNotDominatedByHeader() const {
+ for (HBasicBlock* back_edge : GetBackEdges()) {
+ DCHECK(back_edge->GetDominator() != nullptr);
+ if (!header_->Dominates(back_edge)) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool HBasicBlock::Dominates(HBasicBlock* other) const {
// Walk up the dominator tree from `other`, to find out if `this`
// is an ancestor.
@@ -2396,6 +2452,7 @@ void HLoadString::SetLoadKindInternal(LoadKind load_kind) {
}
if (!NeedsEnvironment()) {
RemoveEnvironment();
+ SetSideEffects(SideEffects::None());
}
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index afb995d438..934d355e82 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -725,6 +725,8 @@ class HLoopInformation : public ArenaObject<kArenaAllocLoopInfo> {
blocks_.ClearAllBits();
}
+ bool HasBackEdgeNotDominatedByHeader() const;
+
private:
// Internal recursive implementation of `Populate`.
void PopulateRecursive(HBasicBlock* block);
@@ -5226,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);
};
@@ -5530,6 +5545,7 @@ class HLoadString : public HExpression<1> {
SetPackedFlag<kFlagIsInDexCache>(true);
DCHECK(!NeedsEnvironment());
RemoveEnvironment();
+ SetSideEffects(SideEffects::None());
}
size_t InputCount() const OVERRIDE {
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 3e632bce8b..37a560d203 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -216,7 +216,11 @@ class ProfMan FINAL {
}
void LogCompletionTime() {
- LOG(INFO) << "profman took " << PrettyDuration(NanoTime() - start_ns_);
+ static constexpr uint64_t kLogThresholdTime = MsToNs(100); // 100ms
+ uint64_t time_taken = NanoTime() - start_ns_;
+ if (time_taken > kLogThresholdTime) {
+ LOG(WARNING) << "profman took " << PrettyDuration(time_taken);
+ }
}
std::vector<std::string> profile_files_;
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 562ee2d810..8064ed696f 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -894,57 +894,107 @@ DEFINE_FUNCTION art_quick_alloc_object_rosalloc
RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
END_FUNCTION art_quick_alloc_object_rosalloc
-// A handle-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB).
-DEFINE_FUNCTION art_quick_alloc_object_tlab
- // Fast path tlab allocation.
- // RDI: uint32_t type_idx, RSI: ArtMethod*
- // RDX, RCX, R8, R9: free. RAX: return val.
- // TODO: Add read barrier when this function is used.
- // Note this function can/should implement read barrier fast path only
- // (no read barrier slow path) because this is the fast path of tlab allocation.
- // We can fall back to the allocation slow path to do the read barrier slow path.
-#if defined(USE_READ_BARRIER)
- int3
- int3
-#endif
- // Might need a special macro since rsi and edx is 32b/64b mismatched.
- movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx // Load dex cache resolved types array
- // TODO: Add read barrier when this function is used.
- // Might need to break down into multiple instructions to get the base address in a register.
- // Load the class
- movl 0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx
+// The common fast path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab.
+//
+// RDI: type_idx, RSI: ArtMethod*, RDX/EDX: the class, RAX: return value.
+// RCX: scratch, r8: Thread::Current().
+MACRO1(ALLOC_OBJECT_TLAB_FAST_PATH, slowPathLabel)
testl %edx, %edx // Check null class
- jz .Lart_quick_alloc_object_tlab_slow_path
+ jz RAW_VAR(slowPathLabel)
// Check class status.
cmpl LITERAL(MIRROR_CLASS_STATUS_INITIALIZED), MIRROR_CLASS_STATUS_OFFSET(%rdx)
- jne .Lart_quick_alloc_object_tlab_slow_path
- // Check access flags has kAccClassIsFinalizable
+ jne RAW_VAR(slowPathLabel)
+ // No fake dependence needed on x86
+ // between status and flags load,
+ // since each load is a load-acquire,
+ // no loads reordering.
+ // Check access flags has
+ // kAccClassIsFinalizable
testl LITERAL(ACCESS_FLAGS_CLASS_IS_FINALIZABLE), MIRROR_CLASS_ACCESS_FLAGS_OFFSET(%rdx)
- jnz .Lart_quick_alloc_object_tlab_slow_path
+ jnz RAW_VAR(slowPathLabel)
+ movq %gs:THREAD_SELF_OFFSET, %r8 // r8 = thread
+ movq THREAD_LOCAL_END_OFFSET(%r8), %rax // Load thread_local_end.
+ subq THREAD_LOCAL_POS_OFFSET(%r8), %rax // Compute the remaining buffer size.
movl MIRROR_CLASS_OBJECT_SIZE_OFFSET(%rdx), %ecx // Load the object size.
+ cmpq %rax, %rcx // Check if it fits. OK to do this
+ // before rounding up the object size
+ // assuming the buf size alignment.
+ ja RAW_VAR(slowPathLabel)
addl LITERAL(OBJECT_ALIGNMENT_MASK), %ecx // Align the size by 8. (addr + 7) & ~7.
andl LITERAL(OBJECT_ALIGNMENT_MASK_TOGGLED), %ecx
- movq %gs:THREAD_SELF_OFFSET, %r8 // r8 = thread
- movq THREAD_LOCAL_POS_OFFSET(%r8), %rax // Load thread_local_pos.
+ movq THREAD_LOCAL_POS_OFFSET(%r8), %rax // Load thread_local_pos
+ // as allocated object.
addq %rax, %rcx // Add the object size.
- cmpq THREAD_LOCAL_END_OFFSET(%r8), %rcx // Check if it fits.
- ja .Lart_quick_alloc_object_tlab_slow_path
movq %rcx, THREAD_LOCAL_POS_OFFSET(%r8) // Update thread_local_pos.
- addq LITERAL(1), THREAD_LOCAL_OBJECTS_OFFSET(%r8) // Increment thread_local_objects.
+ addq LITERAL(1), THREAD_LOCAL_OBJECTS_OFFSET(%r8) // Increase thread_local_objects.
// Store the class pointer in the header.
// No fence needed for x86.
+ POISON_HEAP_REF edx
movl %edx, MIRROR_OBJECT_CLASS_OFFSET(%rax)
ret // Fast path succeeded.
-.Lart_quick_alloc_object_tlab_slow_path:
+END_MACRO
+
+// The common slow path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab.
+MACRO1(ALLOC_OBJECT_TLAB_SLOW_PATH, cxx_name)
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
// Outgoing argument set up
movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current()
- call SYMBOL(artAllocObjectFromCodeTLAB) // cxx_name(arg0, arg1, Thread*)
+ call VAR(cxx_name) // cxx_name(arg0, arg1, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+END_MACRO
+
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB).
+DEFINE_FUNCTION art_quick_alloc_object_tlab
+ // Fast path tlab allocation.
+ // RDI: uint32_t type_idx, RSI: ArtMethod*
+ // RDX, RCX, R8, R9: free. RAX: return val.
+#if defined(USE_READ_BARRIER)
+ int3
+ int3
+#endif
+ // Might need a special macro since rsi and edx is 32b/64b mismatched.
+ movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx // Load dex cache resolved types array
+ // Might need to break down into multiple instructions to get the base address in a register.
+ // Load the class
+ movl 0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx
+ ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_tlab_slow_path
+.Lart_quick_alloc_object_tlab_slow_path:
+ ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeTLAB
END_FUNCTION art_quick_alloc_object_tlab
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB)
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB).
+DEFINE_FUNCTION art_quick_alloc_object_region_tlab
+ // Fast path region tlab allocation.
+ // RDI: uint32_t type_idx, RSI: ArtMethod*
+ // RDX, RCX, R8, R9: free. RAX: return val.
+#if !defined(USE_READ_BARRIER)
+ int3
+ int3
+#endif
+ // Might need a special macro since rsi and edx is 32b/64b mismatched.
+ movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx // Load dex cache resolved types array
+ // Might need to break down into multiple instructions to get the base address in a register.
+ // Load the class
+ movl 0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx
+ cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET
+ jne .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path
+.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit:
+ ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path
+.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path:
+ // The read barrier slow path. Mark the class.
+ PUSH rdi
+ PUSH rsi
+ // Outgoing argument set up
+ movq %rdx, %rdi // Pass the class as the first param.
+ call SYMBOL(artReadBarrierMark) // cxx_name(mirror::Object* obj)
+ movq %rax, %rdx
+ POP rsi
+ POP rdi
+ jmp .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
+.Lart_quick_alloc_object_region_tlab_slow_path:
+ ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeRegionTLAB
+END_FUNCTION art_quick_alloc_object_region_tlab
ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 3df4e98c84..bbffbbb7b7 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -251,6 +251,14 @@ bool DexFileVerifier::CheckValidOffsetAndSize(uint32_t offset,
return true;
}
+bool DexFileVerifier::CheckSizeLimit(uint32_t size, uint32_t limit, const char* label) {
+ if (size > limit) {
+ ErrorStringPrintf("Size(%u) should not exceed limit(%u) for %s.", size, limit, label);
+ return false;
+ }
+ return true;
+}
+
bool DexFileVerifier::CheckHeader() {
// Check file size from the header.
uint32_t expected_size = header_->file_size_;
@@ -298,10 +306,12 @@ bool DexFileVerifier::CheckHeader() {
header_->type_ids_size_,
4,
"type-ids") &&
+ CheckSizeLimit(header_->type_ids_size_, DexFile::kDexNoIndex16, "type-ids") &&
CheckValidOffsetAndSize(header_->proto_ids_off_,
header_->proto_ids_size_,
4,
"proto-ids") &&
+ CheckSizeLimit(header_->proto_ids_size_, DexFile::kDexNoIndex16, "proto-ids") &&
CheckValidOffsetAndSize(header_->field_ids_off_,
header_->field_ids_size_,
4,
@@ -1786,13 +1796,8 @@ bool DexFileVerifier::CheckInterProtoIdItem() {
while (curr_it.HasNext() && prev_it.HasNext()) {
uint16_t prev_idx = prev_it.GetTypeIdx();
uint16_t curr_idx = curr_it.GetTypeIdx();
- if (prev_idx == DexFile::kDexNoIndex16) {
- break;
- }
- if (UNLIKELY(curr_idx == DexFile::kDexNoIndex16)) {
- ErrorStringPrintf("Out-of-order proto_id arguments");
- return false;
- }
+ DCHECK_NE(prev_idx, DexFile::kDexNoIndex16);
+ DCHECK_NE(curr_idx, DexFile::kDexNoIndex16);
if (prev_idx < curr_idx) {
break;
@@ -1804,6 +1809,12 @@ bool DexFileVerifier::CheckInterProtoIdItem() {
prev_it.Next();
curr_it.Next();
}
+ if (!curr_it.HasNext()) {
+ // Either a duplicate ProtoId or a ProtoId with a shorter argument list follows
+ // a ProtoId with a longer one. Both cases are forbidden by the specification.
+ ErrorStringPrintf("Out-of-order proto_id arguments");
+ return false;
+ }
}
}
@@ -2358,7 +2369,8 @@ static bool CheckAtMostOneOfPublicProtectedPrivate(uint32_t flags) {
static std::string GetStringOrError(const uint8_t* const begin,
const DexFile::Header* const header,
uint32_t string_idx) {
- if (header->string_ids_size_ < string_idx) {
+ // The `string_idx` is not guaranteed to be valid yet.
+ if (header->string_ids_size_ <= string_idx) {
return "(error)";
}
@@ -2375,9 +2387,11 @@ static std::string GetStringOrError(const uint8_t* const begin,
static std::string GetClassOrError(const uint8_t* const begin,
const DexFile::Header* const header,
uint32_t class_idx) {
- if (header->type_ids_size_ < class_idx) {
- return "(error)";
- }
+ // The `class_idx` is either `FieldId::class_idx_` or `MethodId::class_idx_` and
+ // it has already been checked in `DexFileVerifier::CheckClassDataItemField()`
+ // or `DexFileVerifier::CheckClassDataItemMethod()`, respectively, to match
+ // a valid defining class.
+ CHECK_LT(class_idx, header->type_ids_size_);
const DexFile::TypeId* type_id =
reinterpret_cast<const DexFile::TypeId*>(begin + header->type_ids_off_) + class_idx;
@@ -2390,9 +2404,8 @@ static std::string GetClassOrError(const uint8_t* const begin,
static std::string GetFieldDescriptionOrError(const uint8_t* const begin,
const DexFile::Header* const header,
uint32_t idx) {
- if (header->field_ids_size_ < idx) {
- return "(error)";
- }
+ // The `idx` has already been checked in `DexFileVerifier::CheckClassDataItemField()`.
+ CHECK_LT(idx, header->field_ids_size_);
const DexFile::FieldId* field_id =
reinterpret_cast<const DexFile::FieldId*>(begin + header->field_ids_off_) + idx;
@@ -2408,9 +2421,8 @@ static std::string GetFieldDescriptionOrError(const uint8_t* const begin,
static std::string GetMethodDescriptionOrError(const uint8_t* const begin,
const DexFile::Header* const header,
uint32_t idx) {
- if (header->method_ids_size_ < idx) {
- return "(error)";
- }
+ // The `idx` has already been checked in `DexFileVerifier::CheckClassDataItemMethod()`.
+ CHECK_LT(idx, header->method_ids_size_);
const DexFile::MethodId* method_id =
reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) + idx;
@@ -2608,7 +2620,13 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index,
*error_msg = StringPrintf("Constructor %" PRIu32 "(%s) is not flagged correctly wrt/ static.",
method_index,
GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- return false;
+ if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
+ return false;
+ } else {
+ // Allow in older versions, but warn.
+ LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
+ << *error_msg;
+ }
}
}
// Check that static and private methods, as well as constructors, are in the direct methods list,
@@ -2662,7 +2680,13 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index,
*error_msg = StringPrintf("Constructor %u(%s) must not be abstract or native",
method_index,
GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- return false;
+ if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
+ return false;
+ } else {
+ // Allow in older versions, but warn.
+ LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
+ << *error_msg;
+ }
}
if ((method_access_flags & kAccAbstract) != 0) {
// Abstract methods are not allowed to have the following flags.
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index be0e6d83f8..90409db44b 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -49,6 +49,8 @@ class DexFileVerifier {
// Checks whether the offset is zero (when size is zero) or that the offset falls within the area
// claimed by the file.
bool CheckValidOffsetAndSize(uint32_t offset, uint32_t size, size_t alignment, const char* label);
+ // Checks whether the size is less than the limit.
+ bool CheckSizeLimit(uint32_t size, uint32_t limit, const char* label);
bool CheckIndex(uint32_t field, uint32_t limit, const char* label);
bool CheckHeader();
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 344d186ad4..3741c1e76c 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -57,7 +57,14 @@ static const uint8_t kBase64Map[256] = {
255, 255, 255, 255
};
-static inline uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
+// Make the Dex file version 37.
+static void MakeDexVersion37(DexFile* dex_file) {
+ size_t offset = OFFSETOF_MEMBER(DexFile::Header, magic_) + 6;
+ CHECK_EQ(*(dex_file->Begin() + offset), '5');
+ *(const_cast<uint8_t*>(dex_file->Begin()) + offset) = '7';
+}
+
+static inline std::unique_ptr<uint8_t[]> DecodeBase64(const char* src, size_t* dst_size) {
std::vector<uint8_t> tmp;
uint32_t t = 0, y = 0;
int g = 3;
@@ -100,7 +107,7 @@ static inline uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
*dst_size = 0;
}
std::copy(tmp.begin(), tmp.end(), dst.get());
- return dst.release();
+ return dst;
}
static void FixUpChecksum(uint8_t* dex_file) {
@@ -113,25 +120,18 @@ static void FixUpChecksum(uint8_t* dex_file) {
header->checksum_ = adler_checksum;
}
-// Custom deleter. Necessary to clean up the memory we use (to be able to mutate).
-struct DexFileDeleter {
- void operator()(DexFile* in) {
- if (in != nullptr) {
- delete[] in->Begin();
- delete in;
- }
- }
-};
-
-using DexFileUniquePtr = std::unique_ptr<DexFile, DexFileDeleter>;
-
class DexFileVerifierTest : public CommonRuntimeTest {
protected:
void VerifyModification(const char* dex_file_base64_content,
const char* location,
std::function<void(DexFile*)> f,
const char* expected_error) {
- DexFileUniquePtr dex_file(WrapAsDexFile(dex_file_base64_content));
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes = DecodeBase64(dex_file_base64_content, &length);
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(
+ new DexFile(dex_bytes.get(), length, "tmp", 0, nullptr, nullptr));
f(dex_file.get());
FixUpChecksum(const_cast<uint8_t*>(dex_file->Begin()));
@@ -150,15 +150,6 @@ class DexFileVerifierTest : public CommonRuntimeTest {
}
}
}
-
- private:
- static DexFile* WrapAsDexFile(const char* dex_file_content_in_base_64) {
- // Decode base64.
- size_t length;
- uint8_t* dex_bytes = DecodeBase64(dex_file_content_in_base_64, &length);
- CHECK(dex_bytes != nullptr);
- return new DexFile(dex_bytes, length, "tmp", 0, nullptr, nullptr);
- }
};
static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
@@ -290,7 +281,9 @@ static const char kMethodFlagsTestDex[] =
// Find the method data for the first method with the given name (from class 0). Note: the pointer
// is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index
// delta.
-static const uint8_t* FindMethodData(const DexFile* dex_file, const char* name) {
+static const uint8_t* FindMethodData(const DexFile* dex_file,
+ const char* name,
+ /*out*/ uint32_t* method_idx = nullptr) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(0);
const uint8_t* class_data = dex_file->GetClassData(class_def);
@@ -316,6 +309,9 @@ static const uint8_t* FindMethodData(const DexFile* dex_file, const char* name)
const DexFile::StringId& string_id = dex_file->GetStringId(name_index);
const char* str = dex_file->GetStringData(string_id);
if (strcmp(name, str) == 0) {
+ if (method_idx != nullptr) {
+ *method_idx = method_index;
+ }
DecodeUnsignedLeb128(&trailing);
return trailing;
}
@@ -449,6 +445,7 @@ TEST_F(DexFileVerifierTest, MethodAccessFlagsConstructors) {
kMethodFlagsTestDex,
"method_flags_constructor_native_nocode",
[&](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
@@ -461,6 +458,7 @@ TEST_F(DexFileVerifierTest, MethodAccessFlagsConstructors) {
kMethodFlagsTestDex,
"method_flags_constructor_abstract_nocode",
[&](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
@@ -521,6 +519,7 @@ TEST_F(DexFileVerifierTest, MethodAccessFlagsConstructors) {
kMethodFlagsTestDex,
"init_not_allowed_flags",
[&](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
@@ -683,6 +682,22 @@ TEST_F(DexFileVerifierTest, MethodAccessFlagsIgnoredOK) {
}
}
+TEST_F(DexFileVerifierTest, B28552165) {
+ // Regression test for bad error string retrieval in different situations.
+ // Using invalid access flags to trigger the error.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "b28552165",
+ [](DexFile* dex_file) {
+ OrMaskToMethodFlags(dex_file, "foo", kAccPublic | kAccProtected);
+ uint32_t method_idx;
+ FindMethodData(dex_file, "foo", &method_idx);
+ auto* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(method_idx));
+ method_id->name_idx_ = dex_file->NumStringIds();
+ },
+ "Method may have only one of public/protected/private, LMethodFlags;.(error)");
+}
+
// Set of dex files for interface method tests. As it's not as easy to mutate method names, it's
// just easier to break up bad cases.
@@ -725,13 +740,6 @@ static uint32_t ApplyMaskShifted(uint32_t src_value, uint32_t mask) {
return result;
}
-// Make the Dex file version 37.
-static void MakeDexVersion37(DexFile* dex_file) {
- size_t offset = OFFSETOF_MEMBER(DexFile::Header, magic_) + 6;
- CHECK_EQ(*(dex_file->Begin() + offset), '5');
- *(const_cast<uint8_t*>(dex_file->Begin()) + offset) = '7';
-}
-
TEST_F(DexFileVerifierTest, MethodAccessFlagsInterfaces) {
VerifyModification(
kMethodFlagsInterface,
@@ -1436,4 +1444,81 @@ TEST_F(DexFileVerifierTest, SectionAlignment) {
}
}
+// Generated from
+//
+// .class LOverloading;
+//
+// .super Ljava/lang/Object;
+//
+// .method public static foo()V
+// .registers 1
+// return-void
+// .end method
+//
+// .method public static foo(I)V
+// .registers 1
+// return-void
+// .end method
+static const char kProtoOrderingTestDex[] =
+ "ZGV4CjAzNQA1L+ABE6voQ9Lr4Ci//efB53oGnDr5PinsAQAAcAAAAHhWNBIAAAAAAAAAAFgBAAAG"
+ "AAAAcAAAAAQAAACIAAAAAgAAAJgAAAAAAAAAAAAAAAIAAACwAAAAAQAAAMAAAAAMAQAA4AAAAOAA"
+ "AADjAAAA8gAAAAYBAAAJAQAADQEAAAAAAAABAAAAAgAAAAMAAAADAAAAAwAAAAAAAAAEAAAAAwAA"
+ "ABQBAAABAAAABQAAAAEAAQAFAAAAAQAAAAAAAAACAAAAAAAAAP////8AAAAASgEAAAAAAAABSQAN"
+ "TE92ZXJsb2FkaW5nOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAJWSQADZm9vAAAAAQAAAAAAAAAA"
+ "AAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAOAAAAAQABAAAAAAAAAAAAAQAAAA4AAAACAAAJpAIBCbgC"
+ "AAAMAAAAAAAAAAEAAAAAAAAAAQAAAAYAAABwAAAAAgAAAAQAAACIAAAAAwAAAAIAAACYAAAABQAA"
+ "AAIAAACwAAAABgAAAAEAAADAAAAAAiAAAAYAAADgAAAAARAAAAEAAAAUAQAAAxAAAAIAAAAcAQAA"
+ "ASAAAAIAAAAkAQAAACAAAAEAAABKAQAAABAAAAEAAABYAQAA";
+
+TEST_F(DexFileVerifierTest, ProtoOrdering) {
+ {
+ // The input dex file should be good before modification.
+ ScratchFile tmp;
+ std::string error_msg;
+ std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kProtoOrderingTestDex,
+ tmp.GetFilename().c_str(),
+ &error_msg));
+ ASSERT_TRUE(raw.get() != nullptr) << error_msg;
+ }
+
+ // Modify the order of the ProtoIds for two overloads of "foo" with the
+ // same return type and one having longer parameter list than the other.
+ for (size_t i = 0; i != 2; ++i) {
+ VerifyModification(
+ kProtoOrderingTestDex,
+ "proto_ordering",
+ [i](DexFile* dex_file) {
+ uint32_t method_idx;
+ const uint8_t* data = FindMethodData(dex_file, "foo", &method_idx);
+ CHECK(data != nullptr);
+ // There should be 2 methods called "foo".
+ CHECK_LT(method_idx + 1u, dex_file->NumMethodIds());
+ CHECK_EQ(dex_file->GetMethodId(method_idx).name_idx_,
+ dex_file->GetMethodId(method_idx + 1).name_idx_);
+ CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_ + 1u,
+ dex_file->GetMethodId(method_idx + 1).proto_idx_);
+ // Their return types should be the same.
+ uint32_t proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_;
+ const DexFile::ProtoId& proto1 = dex_file->GetProtoId(proto1_idx);
+ const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto1_idx + 1u);
+ CHECK_EQ(proto1.return_type_idx_, proto2.return_type_idx_);
+ // And the first should not have any parameters while the second should have some.
+ CHECK(!DexFileParameterIterator(*dex_file, proto1).HasNext());
+ CHECK(DexFileParameterIterator(*dex_file, proto2).HasNext());
+ if (i == 0) {
+ // Swap the proto parameters and shorties to break the ordering.
+ std::swap(const_cast<uint32_t&>(proto1.parameters_off_),
+ const_cast<uint32_t&>(proto2.parameters_off_));
+ std::swap(const_cast<uint32_t&>(proto1.shorty_idx_),
+ const_cast<uint32_t&>(proto2.shorty_idx_));
+ } else {
+ // Copy the proto parameters and shorty to create duplicate proto id.
+ const_cast<uint32_t&>(proto1.parameters_off_) = proto2.parameters_off_;
+ const_cast<uint32_t&>(proto1.shorty_idx_) = proto2.shorty_idx_;
+ }
+ },
+ "Out-of-order proto_id arguments");
+ }
+}
+
} // namespace art
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 3de718b397..d983a9fa19 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -74,10 +74,6 @@ class SharedLibrary {
if (self != nullptr) {
self->GetJniEnv()->DeleteWeakGlobalRef(class_loader_);
}
-
- if (!needs_native_bridge_) {
- android::CloseNativeLibrary(handle_);
- }
}
jweak GetClassLoader() const {
@@ -275,7 +271,8 @@ class Libraries {
REQUIRES(!Locks::jni_libraries_lock_)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedObjectAccessUnchecked soa(Thread::Current());
- std::vector<SharedLibrary*> unload_libraries;
+ typedef void (*JNI_OnUnloadFn)(JavaVM*, void*);
+ std::vector<JNI_OnUnloadFn> unload_functions;
{
MutexLock mu(soa.Self(), *Locks::jni_libraries_lock_);
for (auto it = libraries_.begin(); it != libraries_.end(); ) {
@@ -286,7 +283,15 @@ class Libraries {
// the native libraries of the boot class loader.
if (class_loader != nullptr &&
soa.Self()->IsJWeakCleared(class_loader)) {
- unload_libraries.push_back(library);
+ void* const sym = library->FindSymbol("JNI_OnUnload", nullptr);
+ if (sym == nullptr) {
+ VLOG(jni) << "[No JNI_OnUnload found in \"" << library->GetPath() << "\"]";
+ } else {
+ VLOG(jni) << "[JNI_OnUnload found for \"" << library->GetPath() << "\"]";
+ JNI_OnUnloadFn jni_on_unload = reinterpret_cast<JNI_OnUnloadFn>(sym);
+ unload_functions.push_back(jni_on_unload);
+ }
+ delete library;
it = libraries_.erase(it);
} else {
++it;
@@ -294,17 +299,9 @@ class Libraries {
}
}
// Do this without holding the jni libraries lock to prevent possible deadlocks.
- typedef void (*JNI_OnUnloadFn)(JavaVM*, void*);
- for (auto library : unload_libraries) {
- void* const sym = library->FindSymbol("JNI_OnUnload", nullptr);
- if (sym == nullptr) {
- VLOG(jni) << "[No JNI_OnUnload found in \"" << library->GetPath() << "\"]";
- } else {
- VLOG(jni) << "[JNI_OnUnload found for \"" << library->GetPath() << "\"]: Calling...";
- JNI_OnUnloadFn jni_on_unload = reinterpret_cast<JNI_OnUnloadFn>(sym);
- jni_on_unload(soa.Vm(), nullptr);
- }
- delete library;
+ for (JNI_OnUnloadFn fn : unload_functions) {
+ VLOG(jni) << "Calling JNI_OnUnload";
+ (*fn)(soa.Vm(), nullptr);
}
}
@@ -946,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) {
@@ -955,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 ddcaadefa3..54b8afd1f3 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -34,20 +34,38 @@ static jobject Constructor_getAnnotationNative(JNIEnv* env, jobject javaMethod,
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
- Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
- return soa.AddLocalReference<jobject>(
- method->GetDexFile()->GetAnnotationForMethod(method, klass));
+ if (method->IsProxyMethod()) {
+ return nullptr;
+ } else {
+ Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
+ return soa.AddLocalReference<jobject>(
+ method->GetDexFile()->GetAnnotationForMethod(method, klass));
+ }
}
static jobjectArray Constructor_getDeclaredAnnotations(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
- return soa.AddLocalReference<jobjectArray>(method->GetDexFile()->GetAnnotationsForMethod(method));
+ if (method->IsProxyMethod()) {
+ mirror::Class* class_class = mirror::Class::GetJavaLangClass();
+ mirror::Class* class_array_class =
+ Runtime::Current()->GetClassLinker()->FindArrayClass(soa.Self(), &class_class);
+ if (class_array_class == nullptr) {
+ return nullptr;
+ }
+ mirror::ObjectArray<mirror::Class>* empty_array =
+ mirror::ObjectArray<mirror::Class>::Alloc(soa.Self(), class_array_class, 0);
+ return soa.AddLocalReference<jobjectArray>(empty_array);
+ } else {
+ return soa.AddLocalReference<jobjectArray>(
+ method->GetDexFile()->GetAnnotationsForMethod(method));
+ }
}
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) {
@@ -69,7 +87,12 @@ static jobjectArray Constructor_getExceptionTypes(JNIEnv* env, jobject javaMetho
static jobjectArray Constructor_getParameterAnnotationsNative(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
- return soa.AddLocalReference<jobjectArray>(method->GetDexFile()->GetParameterAnnotations(method));
+ if (method->IsProxyMethod()) {
+ return nullptr;
+ } else {
+ return soa.AddLocalReference<jobjectArray>(
+ method->GetDexFile()->GetParameterAnnotations(method));
+ }
}
static jboolean Constructor_isAnnotationPresentNative(JNIEnv* env, jobject javaMethod,
@@ -77,6 +100,10 @@ static jboolean Constructor_isAnnotationPresentNative(JNIEnv* env, jobject javaM
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
+ if (method->IsProxyMethod()) {
+ // Proxies have no annotations.
+ return false;
+ }
Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
return method->GetDexFile()->IsMethodAnnotationPresent(method, klass);
}
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/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index 2bdf8d1e71..8619ff7f3e 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -14,23 +14,22 @@
* limitations under the License.
*/
-#include <assert.h>
#include <iostream>
#include <pthread.h>
#include <stdio.h>
#include <vector>
+#include "art_method-inl.h"
+#include "base/logging.h"
#include "jni.h"
-#if defined(NDEBUG)
-#error test code compiled without NDEBUG
-#endif
+namespace art {
static JavaVM* jvm = nullptr;
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void*) {
- assert(vm != nullptr);
- assert(jvm == nullptr);
+ CHECK(vm != nullptr);
+ CHECK(jvm == nullptr);
jvm = vm;
std::cout << "JNI_OnLoad called" << std::endl;
return JNI_VERSION_1_6;
@@ -39,24 +38,24 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void*) {
extern "C" JNIEXPORT void JNI_OnUnload(JavaVM*, void*) {
// std::cout since LOG(INFO) adds extra stuff like pid.
std::cout << "JNI_OnUnload called" << std::endl;
- // Clear jvm for assert in test 004-JniTest.
+ // Clear jvm for CHECK in test 004-JniTest.
jvm = nullptr;
}
static void* AttachHelper(void* arg) {
- assert(jvm != nullptr);
+ CHECK(jvm != nullptr);
JNIEnv* env = nullptr;
JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, nullptr };
int attach_result = jvm->AttachCurrentThread(&env, &args);
- assert(attach_result == 0);
+ CHECK_EQ(attach_result, 0);
typedef void (*Fn)(JNIEnv*);
Fn fn = reinterpret_cast<Fn>(arg);
fn(env);
int detach_result = jvm->DetachCurrentThread();
- assert(detach_result == 0);
+ CHECK_EQ(detach_result, 0);
return nullptr;
}
@@ -64,19 +63,19 @@ static void PthreadHelper(void (*fn)(JNIEnv*)) {
pthread_t pthread;
int pthread_create_result = pthread_create(&pthread, nullptr, AttachHelper,
reinterpret_cast<void*>(fn));
- assert(pthread_create_result == 0);
+ CHECK_EQ(pthread_create_result, 0);
int pthread_join_result = pthread_join(pthread, nullptr);
- assert(pthread_join_result == 0);
+ CHECK_EQ(pthread_join_result, 0);
}
static void testFindClassOnAttachedNativeThread(JNIEnv* env) {
jclass clazz = env->FindClass("Main");
- assert(clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jobjectArray array = env->NewObjectArray(0, clazz, nullptr);
- assert(array != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(array != nullptr);
+ CHECK(!env->ExceptionCheck());
}
// http://b/10994325
@@ -86,12 +85,12 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testFindClassOnAttachedNativeThread(
static void testFindFieldOnAttachedNativeThread(JNIEnv* env) {
jclass clazz = env->FindClass("Main");
- assert(clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jfieldID field = env->GetStaticFieldID(clazz, "testFindFieldOnAttachedNativeThreadField", "Z");
- assert(field != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(field != nullptr);
+ CHECK(!env->ExceptionCheck());
env->SetStaticBooleanField(clazz, field, JNI_TRUE);
}
@@ -103,38 +102,38 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testFindFieldOnAttachedNativeThreadN
static void testReflectFieldGetFromAttachedNativeThread(JNIEnv* env) {
jclass clazz = env->FindClass("Main");
- assert(clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jclass class_clazz = env->FindClass("java/lang/Class");
- assert(class_clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(class_clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID getFieldMetodId = env->GetMethodID(class_clazz, "getField",
"(Ljava/lang/String;)Ljava/lang/reflect/Field;");
- assert(getFieldMetodId != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(getFieldMetodId != nullptr);
+ CHECK(!env->ExceptionCheck());
jstring field_name = env->NewStringUTF("testReflectFieldGetFromAttachedNativeThreadField");
- assert(field_name != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(field_name != nullptr);
+ CHECK(!env->ExceptionCheck());
jobject field = env->CallObjectMethod(clazz, getFieldMetodId, field_name);
- assert(field != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(field != nullptr);
+ CHECK(!env->ExceptionCheck());
jclass field_clazz = env->FindClass("java/lang/reflect/Field");
- assert(field_clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(field_clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID getBooleanMetodId = env->GetMethodID(field_clazz, "getBoolean",
"(Ljava/lang/Object;)Z");
- assert(getBooleanMetodId != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(getBooleanMetodId != nullptr);
+ CHECK(!env->ExceptionCheck());
jboolean value = env->CallBooleanMethod(field, getBooleanMetodId, /* ignored */ clazz);
- assert(value == false);
- assert(!env->ExceptionCheck());
+ CHECK(value == false);
+ CHECK(!env->ExceptionCheck());
}
// http://b/15539150
@@ -148,22 +147,22 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testReflectFieldGetFromAttachedNativ
extern "C" JNIEXPORT void JNICALL Java_Main_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env,
jclass) {
jclass super_class = env->FindClass("Main$testCallStaticVoidMethodOnSubClass_SuperClass");
- assert(super_class != nullptr);
+ CHECK(super_class != nullptr);
jmethodID execute = env->GetStaticMethodID(super_class, "execute", "()V");
- assert(execute != nullptr);
+ CHECK(execute != nullptr);
jclass sub_class = env->FindClass("Main$testCallStaticVoidMethodOnSubClass_SubClass");
- assert(sub_class != nullptr);
+ CHECK(sub_class != nullptr);
env->CallStaticVoidMethod(sub_class, execute);
}
extern "C" JNIEXPORT jobject JNICALL Java_Main_testGetMirandaMethodNative(JNIEnv* env, jclass) {
jclass abstract_class = env->FindClass("Main$testGetMirandaMethod_MirandaAbstract");
- assert(abstract_class != nullptr);
+ CHECK(abstract_class != nullptr);
jmethodID miranda_method = env->GetMethodID(abstract_class, "inInterface", "()Z");
- assert(miranda_method != nullptr);
+ CHECK(miranda_method != nullptr);
return env->ToReflectedMethod(abstract_class, miranda_method, JNI_FALSE);
}
@@ -171,11 +170,11 @@ extern "C" JNIEXPORT jobject JNICALL Java_Main_testGetMirandaMethodNative(JNIEnv
extern "C" void JNICALL Java_Main_testZeroLengthByteBuffers(JNIEnv* env, jclass) {
std::vector<uint8_t> buffer(1);
jobject byte_buffer = env->NewDirectByteBuffer(&buffer[0], 0);
- assert(byte_buffer != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(byte_buffer != nullptr);
+ CHECK(!env->ExceptionCheck());
- assert(env->GetDirectBufferAddress(byte_buffer) == &buffer[0]);
- assert(env->GetDirectBufferCapacity(byte_buffer) == 0);
+ CHECK_EQ(env->GetDirectBufferAddress(byte_buffer), &buffer[0]);
+ CHECK_EQ(env->GetDirectBufferCapacity(byte_buffer), 0);
}
constexpr size_t kByteReturnSize = 7;
@@ -185,18 +184,18 @@ extern "C" jbyte JNICALL Java_Main_byteMethod(JNIEnv*, jclass, jbyte b1, jbyte b
jbyte b3, jbyte b4, jbyte b5, jbyte b6,
jbyte b7, jbyte b8, jbyte b9, jbyte b10) {
// We use b1 to drive the output.
- assert(b2 == 2);
- assert(b3 == -3);
- assert(b4 == 4);
- assert(b5 == -5);
- assert(b6 == 6);
- assert(b7 == -7);
- assert(b8 == 8);
- assert(b9 == -9);
- assert(b10 == 10);
-
- assert(0 <= b1);
- assert(b1 < static_cast<jbyte>(kByteReturnSize));
+ CHECK_EQ(b2, 2);
+ CHECK_EQ(b3, -3);
+ CHECK_EQ(b4, 4);
+ CHECK_EQ(b5, -5);
+ CHECK_EQ(b6, 6);
+ CHECK_EQ(b7, -7);
+ CHECK_EQ(b8, 8);
+ CHECK_EQ(b9, -9);
+ CHECK_EQ(b10, 10);
+
+ CHECK_LE(0, b1);
+ CHECK_LT(b1, static_cast<jbyte>(kByteReturnSize));
return byte_returns[b1];
}
@@ -210,18 +209,18 @@ extern "C" jshort JNICALL Java_Main_shortMethod(JNIEnv*, jclass, jshort s1, jsho
jshort s3, jshort s4, jshort s5, jshort s6,
jshort s7, jshort s8, jshort s9, jshort s10) {
// We use s1 to drive the output.
- assert(s2 == 2);
- assert(s3 == -3);
- assert(s4 == 4);
- assert(s5 == -5);
- assert(s6 == 6);
- assert(s7 == -7);
- assert(s8 == 8);
- assert(s9 == -9);
- assert(s10 == 10);
-
- assert(0 <= s1);
- assert(s1 < static_cast<jshort>(kShortReturnSize));
+ CHECK_EQ(s2, 2);
+ CHECK_EQ(s3, -3);
+ CHECK_EQ(s4, 4);
+ CHECK_EQ(s5, -5);
+ CHECK_EQ(s6, 6);
+ CHECK_EQ(s7, -7);
+ CHECK_EQ(s8, 8);
+ CHECK_EQ(s9, -9);
+ CHECK_EQ(s10, 10);
+
+ CHECK_LE(0, s1);
+ CHECK_LT(s1, static_cast<jshort>(kShortReturnSize));
return short_returns[s1];
}
@@ -231,17 +230,17 @@ extern "C" jboolean JNICALL Java_Main_booleanMethod(JNIEnv*, jclass, jboolean b1
jboolean b5, jboolean b6, jboolean b7,
jboolean b8, jboolean b9, jboolean b10) {
// We use b1 to drive the output.
- assert(b2 == JNI_TRUE);
- assert(b3 == JNI_FALSE);
- assert(b4 == JNI_TRUE);
- assert(b5 == JNI_FALSE);
- assert(b6 == JNI_TRUE);
- assert(b7 == JNI_FALSE);
- assert(b8 == JNI_TRUE);
- assert(b9 == JNI_FALSE);
- assert(b10 == JNI_TRUE);
-
- assert(b1 == JNI_TRUE || b1 == JNI_FALSE);
+ CHECK_EQ(b2, JNI_TRUE);
+ CHECK_EQ(b3, JNI_FALSE);
+ CHECK_EQ(b4, JNI_TRUE);
+ CHECK_EQ(b5, JNI_FALSE);
+ CHECK_EQ(b6, JNI_TRUE);
+ CHECK_EQ(b7, JNI_FALSE);
+ CHECK_EQ(b8, JNI_TRUE);
+ CHECK_EQ(b9, JNI_FALSE);
+ CHECK_EQ(b10, JNI_TRUE);
+
+ CHECK(b1 == JNI_TRUE || b1 == JNI_FALSE);
return b1;
}
@@ -252,17 +251,17 @@ extern "C" jchar JNICALL Java_Main_charMethod(JNIEnv*, jclass, jchar c1, jchar c
jchar c3, jchar c4, jchar c5, jchar c6, jchar c7,
jchar c8, jchar c9, jchar c10) {
// We use c1 to drive the output.
- assert(c2 == 'a');
- assert(c3 == 'b');
- assert(c4 == 'c');
- assert(c5 == '0');
- assert(c6 == '1');
- assert(c7 == '2');
- assert(c8 == 1234);
- assert(c9 == 2345);
- assert(c10 == 3456);
-
- assert(c1 < static_cast<jchar>(kCharReturnSize));
+ CHECK_EQ(c2, 'a');
+ CHECK_EQ(c3, 'b');
+ CHECK_EQ(c4, 'c');
+ CHECK_EQ(c5, '0');
+ CHECK_EQ(c6, '1');
+ CHECK_EQ(c7, '2');
+ CHECK_EQ(c8, 1234);
+ CHECK_EQ(c9, 2345);
+ CHECK_EQ(c10, 3456);
+
+ CHECK_LT(c1, static_cast<jchar>(kCharReturnSize));
return char_returns[c1];
}
@@ -281,39 +280,39 @@ static void testShallowGetCallingClassLoader(JNIEnv* env) {
// Test direct call.
{
jclass vmstack_clazz = env->FindClass("dalvik/system/VMStack");
- assert(vmstack_clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(vmstack_clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID getCallingClassLoaderMethodId = env->GetStaticMethodID(vmstack_clazz,
"getCallingClassLoader",
"()Ljava/lang/ClassLoader;");
- assert(getCallingClassLoaderMethodId != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(getCallingClassLoaderMethodId != nullptr);
+ CHECK(!env->ExceptionCheck());
jobject class_loader = env->CallStaticObjectMethod(vmstack_clazz,
getCallingClassLoaderMethodId);
- assert(class_loader == nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(class_loader == nullptr);
+ CHECK(!env->ExceptionCheck());
}
// Test one-level call. Use System.loadLibrary().
{
jclass system_clazz = env->FindClass("java/lang/System");
- assert(system_clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(system_clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID loadLibraryMethodId = env->GetStaticMethodID(system_clazz, "loadLibrary",
"(Ljava/lang/String;)V");
- assert(loadLibraryMethodId != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(loadLibraryMethodId != nullptr);
+ CHECK(!env->ExceptionCheck());
// Create a string object.
jobject library_string = env->NewStringUTF("non_existing_library");
- assert(library_string != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(library_string != nullptr);
+ CHECK(!env->ExceptionCheck());
env->CallStaticVoidMethod(system_clazz, loadLibraryMethodId, library_string);
- assert(env->ExceptionCheck());
+ CHECK(env->ExceptionCheck());
// We expect UnsatisfiedLinkError.
jthrowable thrown = env->ExceptionOccurred();
@@ -321,7 +320,7 @@ static void testShallowGetCallingClassLoader(JNIEnv* env) {
jclass unsatisfied_link_error_clazz = env->FindClass("java/lang/UnsatisfiedLinkError");
jclass thrown_class = env->GetObjectClass(thrown);
- assert(env->IsSameObject(unsatisfied_link_error_clazz, thrown_class));
+ CHECK(env->IsSameObject(unsatisfied_link_error_clazz, thrown_class));
}
}
@@ -333,31 +332,31 @@ extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetCallingClassLoad
static void testShallowGetStackClass2(JNIEnv* env) {
jclass vmstack_clazz = env->FindClass("dalvik/system/VMStack");
- assert(vmstack_clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(vmstack_clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
// Test direct call.
{
jmethodID getStackClass2MethodId = env->GetStaticMethodID(vmstack_clazz, "getStackClass2",
"()Ljava/lang/Class;");
- assert(getStackClass2MethodId != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(getStackClass2MethodId != nullptr);
+ CHECK(!env->ExceptionCheck());
jobject caller_class = env->CallStaticObjectMethod(vmstack_clazz, getStackClass2MethodId);
- assert(caller_class == nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(caller_class == nullptr);
+ CHECK(!env->ExceptionCheck());
}
// Test one-level call. Use VMStack.getStackClass1().
{
jmethodID getStackClass1MethodId = env->GetStaticMethodID(vmstack_clazz, "getStackClass1",
"()Ljava/lang/Class;");
- assert(getStackClass1MethodId != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(getStackClass1MethodId != nullptr);
+ CHECK(!env->ExceptionCheck());
jobject caller_class = env->CallStaticObjectMethod(vmstack_clazz, getStackClass1MethodId);
- assert(caller_class == nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(caller_class == nullptr);
+ CHECK(!env->ExceptionCheck());
}
// For better testing we would need to compile against libcore and have a two-deep stack
@@ -416,8 +415,8 @@ class JniCallNonvirtualVoidMethodTest {
env_->ExceptionDescribe();
env_->FatalError(__FUNCTION__);
}
- assert(!env_->ExceptionCheck());
- assert(c != nullptr);
+ CHECK(!env_->ExceptionCheck());
+ CHECK(c != nullptr);
return c;
}
@@ -429,7 +428,7 @@ class JniCallNonvirtualVoidMethodTest {
env_->ExceptionDescribe();
env_->FatalError(__FUNCTION__);
}
- assert(m != nullptr);
+ CHECK(m != nullptr);
return m;
}
@@ -439,7 +438,7 @@ class JniCallNonvirtualVoidMethodTest {
env_->ExceptionDescribe();
env_->FatalError(__FUNCTION__);
}
- assert(o != nullptr);
+ CHECK(o != nullptr);
return o;
}
@@ -467,7 +466,7 @@ class JniCallNonvirtualVoidMethodTest {
env_->ExceptionDescribe();
env_->FatalError(__FUNCTION__);
}
- assert(m != nullptr);
+ CHECK(m != nullptr);
return m;
}
@@ -508,21 +507,21 @@ class JniCallNonvirtualVoidMethodTest {
jobject sub_super = CallConstructor(sub_, super_constructor_);
jobject sub_sub = CallConstructor(sub_, sub_constructor_);
- assert(env_->IsInstanceOf(super_super, super_));
- assert(!env_->IsInstanceOf(super_super, sub_));
+ CHECK(env_->IsInstanceOf(super_super, super_));
+ CHECK(!env_->IsInstanceOf(super_super, sub_));
// Note that even though we called (and ran) the subclass
// constructor, we are not the subclass.
- assert(env_->IsInstanceOf(super_sub, super_));
- assert(!env_->IsInstanceOf(super_sub, sub_));
+ CHECK(env_->IsInstanceOf(super_sub, super_));
+ CHECK(!env_->IsInstanceOf(super_sub, sub_));
// Note that even though we called the superclass constructor, we
// are still the subclass.
- assert(env_->IsInstanceOf(sub_super, super_));
- assert(env_->IsInstanceOf(sub_super, sub_));
+ CHECK(env_->IsInstanceOf(sub_super, super_));
+ CHECK(env_->IsInstanceOf(sub_super, sub_));
- assert(env_->IsInstanceOf(sub_sub, super_));
- assert(env_->IsInstanceOf(sub_sub, sub_));
+ CHECK(env_->IsInstanceOf(sub_sub, super_));
+ CHECK(env_->IsInstanceOf(sub_sub, sub_));
}
void TestnonstaticCallNonvirtualMethod(bool super_object, bool super_class, bool super_method, const char* test_case) {
@@ -542,8 +541,8 @@ class JniCallNonvirtualVoidMethodTest {
CallMethod(o, c, m, true, test_case);
jboolean super_field = GetBooleanField(o, super_field_);
jboolean sub_field = GetBooleanField(o, sub_field_);
- assert(super_field == super_method);
- assert(sub_field != super_method);
+ CHECK_EQ(super_field, super_method);
+ CHECK_NE(sub_field, super_method);
}
void TestnonstaticCallNonvirtualMethod() {
@@ -565,20 +564,20 @@ extern "C" void JNICALL Java_Main_testCallNonvirtual(JNIEnv* env, jclass) {
extern "C" JNIEXPORT void JNICALL Java_Main_testNewStringObject(JNIEnv* env, jclass) {
jclass c = env->FindClass("java/lang/String");
- assert(c != nullptr);
+ CHECK(c != nullptr);
jmethodID mid1 = env->GetMethodID(c, "<init>", "()V");
- assert(mid1 != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(mid1 != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID mid2 = env->GetMethodID(c, "<init>", "([B)V");
- assert(mid2 != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(mid2 != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID mid3 = env->GetMethodID(c, "<init>", "([C)V");
- assert(mid3 != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(mid3 != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID mid4 = env->GetMethodID(c, "<init>", "(Ljava/lang/String;)V");
- assert(mid4 != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(mid4 != nullptr);
+ CHECK(!env->ExceptionCheck());
const char* test_array = "Test";
int byte_array_length = strlen(test_array);
@@ -587,22 +586,22 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testNewStringObject(JNIEnv* env, jcl
// Test NewObject
jstring s = reinterpret_cast<jstring>(env->NewObject(c, mid2, byte_array));
- assert(s != nullptr);
- assert(env->GetStringLength(s) == byte_array_length);
- assert(env->GetStringUTFLength(s) == byte_array_length);
+ CHECK(s != nullptr);
+ CHECK_EQ(env->GetStringLength(s), byte_array_length);
+ CHECK_EQ(env->GetStringUTFLength(s), byte_array_length);
const char* chars = env->GetStringUTFChars(s, nullptr);
- assert(strcmp(test_array, chars) == 0);
+ CHECK_EQ(strcmp(test_array, chars), 0);
env->ReleaseStringUTFChars(s, chars);
// Test AllocObject and Call(Nonvirtual)VoidMethod
jstring s1 = reinterpret_cast<jstring>(env->AllocObject(c));
- assert(s1 != nullptr);
+ CHECK(s1 != nullptr);
jstring s2 = reinterpret_cast<jstring>(env->AllocObject(c));
- assert(s2 != nullptr);
+ CHECK(s2 != nullptr);
jstring s3 = reinterpret_cast<jstring>(env->AllocObject(c));
- assert(s3 != nullptr);
+ CHECK(s3 != nullptr);
jstring s4 = reinterpret_cast<jstring>(env->AllocObject(c));
- assert(s4 != nullptr);
+ CHECK(s4 != nullptr);
jcharArray char_array = env->NewCharArray(5);
jstring string_arg = env->NewStringUTF("helloworld");
@@ -621,18 +620,18 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testNewStringObject(JNIEnv* env, jcl
// Test with global and weak global references
jstring s5 = reinterpret_cast<jstring>(env->AllocObject(c));
- assert(s5 != nullptr);
+ CHECK(s5 != nullptr);
s5 = reinterpret_cast<jstring>(env->NewGlobalRef(s5));
jstring s6 = reinterpret_cast<jstring>(env->AllocObject(c));
- assert(s6 != nullptr);
+ CHECK(s6 != nullptr);
s6 = reinterpret_cast<jstring>(env->NewWeakGlobalRef(s6));
env->CallVoidMethod(s5, mid1);
env->CallNonvirtualVoidMethod(s6, c, mid2, byte_array);
- assert(env->GetStringLength(s5) == 0);
- assert(env->GetStringLength(s6) == byte_array_length);
+ CHECK_EQ(env->GetStringLength(s5), 0);
+ CHECK_EQ(env->GetStringLength(s6), byte_array_length);
const char* chars6 = env->GetStringUTFChars(s6, nullptr);
- assert(strcmp(test_array, chars6) == 0);
+ CHECK_EQ(strcmp(test_array, chars6), 0);
env->ReleaseStringUTFChars(s6, chars6);
}
@@ -664,8 +663,8 @@ class JniCallDefaultMethodsTest {
public:
explicit JniCallDefaultMethodsTest(JNIEnv* env)
: env_(env), concrete_class_(env_->FindClass("ConcreteClass")) {
- assert(!env_->ExceptionCheck());
- assert(concrete_class_ != nullptr);
+ CHECK(!env_->ExceptionCheck());
+ CHECK(concrete_class_ != nullptr);
}
void Test() {
@@ -688,14 +687,14 @@ class JniCallDefaultMethodsTest {
void TestCalls(const char* declaring_class, std::vector<const char*> methods) {
jmethodID new_method = env_->GetMethodID(concrete_class_, "<init>", "()V");
jobject obj = env_->NewObject(concrete_class_, new_method);
- assert(!env_->ExceptionCheck());
- assert(obj != nullptr);
+ CHECK(!env_->ExceptionCheck());
+ CHECK(obj != nullptr);
jclass decl_class = env_->FindClass(declaring_class);
- assert(!env_->ExceptionCheck());
- assert(decl_class != nullptr);
+ CHECK(!env_->ExceptionCheck());
+ CHECK(decl_class != nullptr);
for (const char* method : methods) {
jmethodID method_id = env_->GetMethodID(decl_class, method, "()V");
- assert(!env_->ExceptionCheck());
+ CHECK(!env_->ExceptionCheck());
printf("Calling method %s->%s on object of type ConcreteClass\n", declaring_class, method);
env_->CallVoidMethod(obj, method_id);
if (env_->ExceptionCheck()) {
@@ -704,10 +703,10 @@ class JniCallDefaultMethodsTest {
jmethodID to_string = env_->GetMethodID(
env_->FindClass("java/lang/Object"), "toString", "()Ljava/lang/String;");
jstring exception_string = (jstring) env_->CallObjectMethod(thrown, to_string);
- assert(!env_->ExceptionCheck());
+ CHECK(!env_->ExceptionCheck());
const char* exception_string_utf8 = env_->GetStringUTFChars(exception_string, nullptr);
- assert(!env_->ExceptionCheck());
- assert(exception_string_utf8 != nullptr);
+ CHECK(!env_->ExceptionCheck());
+ CHECK(exception_string_utf8 != nullptr);
printf("EXCEPTION OCCURED: %s\n", exception_string_utf8);
env_->ReleaseStringUTFChars(exception_string, exception_string_utf8);
}
@@ -724,12 +723,12 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testCallDefaultMethods(JNIEnv* env)
static void InvokeSpecificMethod(JNIEnv* env, jobject obj, const char* method) {
jclass lambda_class = env->FindClass("LambdaInterface");
- assert(!env->ExceptionCheck());
- assert(lambda_class != nullptr);
+ CHECK(!env->ExceptionCheck());
+ CHECK(lambda_class != nullptr);
jmethodID method_id = env->GetMethodID(lambda_class, method, "()V");
- assert(!env->ExceptionCheck());
+ CHECK(!env->ExceptionCheck());
env->CallVoidMethod(obj, method_id);
- assert(!env->ExceptionCheck());
+ CHECK(!env->ExceptionCheck());
}
extern "C" JNIEXPORT void JNICALL Java_Main_testInvokeLambdaDefaultMethod(
@@ -740,3 +739,6 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testInvokeLambdaDefaultMethod(
extern "C" JNIEXPORT void JNICALL Java_Main_testInvokeLambdaMethod(JNIEnv* e, jclass, jobject l) {
InvokeSpecificMethod(e, l, "sayHi");
}
+
+} // namespace art
+
diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index 284e5544fb..5304590ad0 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "art_method-inl.h"
#include "check_reference_map_visitor.h"
#include "jni.h"
diff --git a/test/004-StackWalk/stack_walk_jni.cc b/test/004-StackWalk/stack_walk_jni.cc
index 51bb68f24a..420224dd21 100644
--- a/test/004-StackWalk/stack_walk_jni.cc
+++ b/test/004-StackWalk/stack_walk_jni.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "art_method-inl.h"
#include "check_reference_map_visitor.h"
#include "jni.h"
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/136-daemon-jni-shutdown/daemon_jni_shutdown.cc b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc
index 074c9b189e..c9110a905d 100644
--- a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc
+++ b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc
@@ -48,7 +48,8 @@ extern "C" JNIEXPORT void JNICALL Java_Main_destroyJavaVMAndExit(JNIEnv* env, jc
vm->DetachCurrentThread();
// Open ourself again to make sure the native library does not get unloaded from
// underneath us due to DestroyJavaVM. b/28406866
- dlopen(kIsDebugBuild ? "libarttestd.so" : "libarttest.so", RTLD_NOW);
+ void* handle = dlopen(kIsDebugBuild ? "libarttestd.so" : "libarttest.so", RTLD_NOW);
+ CHECK(handle != nullptr);
vm->DestroyJavaVM();
vm_was_shutdown.store(true);
// Give threads some time to get stuck in ExceptionCheck.
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/566-polymorphic-inlining/src/Main.java b/test/566-polymorphic-inlining/src/Main.java
index 286f0d996e..a59ce5b344 100644
--- a/test/566-polymorphic-inlining/src/Main.java
+++ b/test/566-polymorphic-inlining/src/Main.java
@@ -98,6 +98,7 @@ public class Main implements Itf {
public static native void ensureJittedAndPolymorphicInline();
public void increment() {
+ field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo
counter++;
}
public static int counter = 0;
diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc
index bd8f0a9520..2fa5800e5c 100644
--- a/test/570-checker-osr/osr.cc
+++ b/test/570-checker-osr/osr.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "art_method.h"
+#include "art_method-inl.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
#include "jit/profiling_info.h"
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/594-load-string-regression/expected.txt b/test/594-load-string-regression/expected.txt
new file mode 100644
index 0000000000..365b0e168f
--- /dev/null
+++ b/test/594-load-string-regression/expected.txt
@@ -0,0 +1 @@
+String: ""
diff --git a/test/594-load-string-regression/info.txt b/test/594-load-string-regression/info.txt
new file mode 100644
index 0000000000..6a07ace81b
--- /dev/null
+++ b/test/594-load-string-regression/info.txt
@@ -0,0 +1,2 @@
+Regression test for LoadString listing side effects when it doesn't have any
+and triggering a DCHECK() failure when merging ClinitCheck into NewInstance.
diff --git a/test/594-load-string-regression/src/Main.java b/test/594-load-string-regression/src/Main.java
new file mode 100644
index 0000000000..0b9f7b52a1
--- /dev/null
+++ b/test/594-load-string-regression/src/Main.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ static boolean doThrow = false;
+
+ // Note: We're not doing checker tests as we cannot do them specifically for a non-PIC
+ // configuration. The check here would be "prepare_for_register_allocation (before)"
+ // CHECK: LoadClass
+ // CHECK-NEXT: ClinitCheck
+ // CHECK-NEXT: LoadString load_kind:BootImageAddress
+ // CHECK-NEXT: NewInstance
+ // and "prepare_for_register_allocation (after)"
+ // CHECK: LoadString
+ // CHECK-NEXT: NewInstance
+ // but the order of instructions for non-PIC mode is different.
+ public static int $noinline$test() {
+ if (doThrow) { throw new Error(); }
+
+ int r = 0x12345678;
+ do {
+ // LICM pulls the LoadClass and ClinitCheck out of the loop, leaves NewInstance in the loop.
+ Helper h = new Helper();
+ // For non-PIC mode, LICM pulls the boot image LoadString out of the loop.
+ // (For PIC mode, the LoadString can throw and will not be moved out of the loop.)
+ String s = ""; // Empty string is known to be in the boot image.
+ r = r ^ (r >> 5);
+ h.$noinline$printString(s);
+ // During DCE after inlining, the loop back-edge disappears and the pre-header is
+ // merged with the body, leaving consecutive LoadClass, ClinitCheck, LoadString
+ // and NewInstance in non-PIC mode. The prepare_for_register_allocation pass
+ // merges the LoadClass and ClinitCheck with the NewInstance and checks that
+ // there are no instructions with side effects in between. This check used to
+ // fail because LoadString was always listing SideEffects::CanTriggerGC() even
+ // when it doesn't really have any side effects, i.e. for direct references to
+ // boot image Strings or for Strings known to be in the dex cache.
+ } while ($inline$shouldContinue());
+ return r;
+ }
+
+ static boolean $inline$shouldContinue() {
+ return false;
+ }
+
+ public static void main(String[] args) {
+ assertIntEquals(0x12345678 ^ (0x12345678 >> 5), $noinline$test());
+ }
+
+ public static void assertIntEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
+
+class Helper {
+ static boolean doThrow = false;
+
+ public void $noinline$printString(String s) {
+ if (doThrow) { throw new Error(); }
+
+ System.out.println("String: \"" + s + "\"");
+ }
+}
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
index a7e6f8ba8c..0d26f454c6 100644
--- a/test/595-profile-saving/profile-saving.cc
+++ b/test/595-profile-saving/profile-saving.cc
@@ -16,6 +16,7 @@
#include "dex_file.h"
+#include "art_method-inl.h"
#include "jit/offline_profiling_info.h"
#include "jit/profile_saver.h"
#include "jni.h"
diff --git a/test/596-app-images/app_images.cc b/test/596-app-images/app_images.cc
index 11c0f424ba..a5bbf5fbaa 100644
--- a/test/596-app-images/app_images.cc
+++ b/test/596-app-images/app_images.cc
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <assert.h>
#include <iostream>
#include <pthread.h>
#include <stdio.h>
diff --git a/test/598-checker-irreducible-dominance/expected.txt b/test/598-checker-irreducible-dominance/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/598-checker-irreducible-dominance/expected.txt
diff --git a/test/598-checker-irreducible-dominance/info.txt b/test/598-checker-irreducible-dominance/info.txt
new file mode 100644
index 0000000000..8ca4e63be9
--- /dev/null
+++ b/test/598-checker-irreducible-dominance/info.txt
@@ -0,0 +1,2 @@
+Regression test for HGraphBuilder which would compute wrong dominance information
+in the presence of irreducible loops. \ No newline at end of file
diff --git a/test/598-checker-irreducible-dominance/smali/IrreducibleLoop.smali b/test/598-checker-irreducible-dominance/smali/IrreducibleLoop.smali
new file mode 100644
index 0000000000..4d8b5152fd
--- /dev/null
+++ b/test/598-checker-irreducible-dominance/smali/IrreducibleLoop.smali
@@ -0,0 +1,52 @@
+# 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 LIrreducibleLoop;
+.super Ljava/lang/Object;
+
+# Test case in which `inner_back_edge` is not dominated by `inner_header` and
+# causes `outer_back_edge` to not be dominated by `outer_header`. HGraphBuilder
+# not do a fix-point iteration and would miss the path to `outer_back_edge`
+# through `inner_back_edge` and incorrectly label the outer loop non-irreducible.
+
+## CHECK-START: int IrreducibleLoop.dominance(int) builder (after)
+## CHECK: Add irreducible:true
+
+.method public static dominance(I)I
+ .registers 2
+
+ if-eqz p0, :outer_header
+ goto :inner_back_edge
+
+ :outer_header
+ if-eqz p0, :inner_header
+
+ :outer_branch_exit
+ if-eqz p0, :outer_merge
+ return p0
+
+ :inner_header
+ goto :outer_merge
+
+ :inner_back_edge
+ goto :inner_header
+
+ :outer_merge
+ if-eqz p0, :inner_back_edge
+
+ :outer_back_edge
+ add-int/2addr p0, p0
+ goto :outer_header
+
+.end method
diff --git a/test/598-checker-irreducible-dominance/src/Main.java b/test/598-checker-irreducible-dominance/src/Main.java
new file mode 100644
index 0000000000..38b2ab4384
--- /dev/null
+++ b/test/598-checker-irreducible-dominance/src/Main.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) {
+ // Nothing to run. This regression test merely makes sure the smali test
+ // case successfully compiles.
+ }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 1550cc4403..21f8141c66 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -93,7 +93,12 @@ define build-libarttest
include $(BUILD_SHARED_LIBRARY)
else # host
LOCAL_CLANG := $(ART_HOST_CLANG)
- LOCAL_CFLAGS := $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS)
+ LOCAL_CFLAGS := $(ART_HOST_CFLAGS)
+ ifeq ($$(suffix),d)
+ LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
+ else
+ LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS)
+ endif
LOCAL_ASFLAGS := $(ART_HOST_ASFLAGS)
LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -ldl -lpthread
LOCAL_IS_HOST_MODULE := true
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 01114b7690..aa45d40cb4 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -473,11 +473,14 @@ if [ "$HOST" = "n" ]; then
LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY
fi
+ PUBLIC_LIBS=libart.so:libartd.so
+
# Create a script with the command. The command can get longer than the longest
# allowed adb command and there is no way to get the exit status from a adb shell
# command.
cmdline="cd $DEX_LOCATION && \
export ANDROID_DATA=$DEX_LOCATION && \
+ export ANDROID_ADDITIONAL_PUBLIC_LIBRARIES=$PUBLIC_LIBS && \
export DEX_LOCATION=$DEX_LOCATION && \
export ANDROID_ROOT=$ANDROID_ROOT && \
$mkdir_cmdline && \
diff --git a/test/run-test b/test/run-test
index 424c54f2c9..047e3fb606 100755
--- a/test/run-test
+++ b/test/run-test
@@ -148,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"
@@ -467,7 +468,7 @@ elif [ "$runtime" = "art" ]; then
run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}"
else
guess_target_arch_name
- run_args="${run_args} --runtime-option -Djava.library.path=/data/art-test/${target_arch_name}"
+ run_args="${run_args} --runtime-option -Djava.library.path=/data/art-test/${target_arch_name}:/system/lib${suffix64}"
run_args="${run_args} --boot /data/art-test/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art"
fi
if [ "$relocate" = "yes" ]; 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);