diff options
38 files changed, 321 insertions, 138 deletions
diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc index bc5c6cab87..510613ecf4 100644 --- a/compiler/driver/compiled_method_storage.cc +++ b/compiler/driver/compiled_method_storage.cc @@ -190,7 +190,8 @@ CompiledMethodStorage::~CompiledMethodStorage() { void CompiledMethodStorage::DumpMemoryUsage(std::ostream& os, bool extended) const { if (swap_space_.get() != nullptr) { - os << " swap=" << PrettySize(swap_space_->GetSize()); + const size_t swap_size = swap_space_->GetSize(); + os << " swap=" << PrettySize(swap_size) << " (" << swap_size << "B)"; } if (extended) { Thread* self = Thread::Current(); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index f078bf6507..670fe94988 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -2732,16 +2732,18 @@ bool CompilerDriver::RequiresConstructorBarrier(Thread* self, const DexFile* dex std::string CompilerDriver::GetMemoryUsageString(bool extended) const { std::ostringstream oss; Runtime* const runtime = Runtime::Current(); - const ArenaPool* arena_pool = runtime->GetArenaPool(); - gc::Heap* const heap = runtime->GetHeap(); - oss << "arena alloc=" << PrettySize(arena_pool->GetBytesAllocated()); - oss << " java alloc=" << PrettySize(heap->GetBytesAllocated()); + const ArenaPool* const arena_pool = runtime->GetArenaPool(); + const gc::Heap* const heap = runtime->GetHeap(); + const size_t arena_alloc = arena_pool->GetBytesAllocated(); + const size_t java_alloc = heap->GetBytesAllocated(); + oss << "arena alloc=" << PrettySize(arena_alloc) << " (" << arena_alloc << "B)"; + oss << " java alloc=" << PrettySize(java_alloc) << " (" << java_alloc << "B)"; #if defined(__BIONIC__) || defined(__GLIBC__) - struct mallinfo info = mallinfo(); + const struct mallinfo info = mallinfo(); const size_t allocated_space = static_cast<size_t>(info.uordblks); const size_t free_space = static_cast<size_t>(info.fordblks); - oss << " native alloc=" << PrettySize(allocated_space) << " free=" - << PrettySize(free_space); + oss << " native alloc=" << PrettySize(allocated_space) << " (" << allocated_space << "B)" + << " free=" << PrettySize(free_space) << " (" << free_space << "B)"; #endif compiled_method_storage_.DumpMemoryUsage(oss, extended); return oss.str(); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index ae5679eaf5..f032f51649 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1275,7 +1275,7 @@ void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond, } // Must be equal high, so compare the lows. codegen_->Compare32BitValue(left_low, val_low); - } else { + } else if (right.IsRegisterPair()) { Register right_high = right.AsRegisterPairHigh<Register>(); Register right_low = right.AsRegisterPairLow<Register>(); @@ -1290,6 +1290,19 @@ void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond, } // Must be equal high, so compare the lows. __ cmpl(left_low, right_low); + } else { + DCHECK(right.IsDoubleStackSlot()); + __ cmpl(left_high, Address(ESP, right.GetHighStackIndex(kX86WordSize))); + if (if_cond == kCondNE) { + __ j(X86Condition(true_high_cond), true_label); + } else if (if_cond == kCondEQ) { + __ j(X86Condition(false_high_cond), false_label); + } else { + __ j(X86Condition(true_high_cond), true_label); + __ j(X86Condition(false_high_cond), false_label); + } + // Must be equal high, so compare the lows. + __ cmpl(left_low, Address(ESP, right.GetStackIndex())); } // The last comparison might be unsigned. __ j(final_condition, true_label); @@ -1693,7 +1706,7 @@ void LocationsBuilderX86::HandleCondition(HCondition* cond) { switch (cond->InputAt(0)->GetType()) { case Primitive::kPrimLong: { locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1))); + locations->SetInAt(1, Location::Any()); if (!cond->IsEmittedAtUseSite()) { locations->SetOut(Location::RequiresRegister()); } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 68e96fba74..a5acab81ab 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -258,8 +258,9 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { } if (actual_method != nullptr) { - return TryInline(invoke_instruction, actual_method); + return TryInlineAndReplace(invoke_instruction, actual_method, /* do_rtp */ true); } + DCHECK(!invoke_instruction->IsInvokeStaticOrDirect()); // Check if we can use an inline cache. @@ -344,7 +345,7 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, HInstruction* cursor = invoke_instruction->GetPrevious(); HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); - if (!TryInline(invoke_instruction, resolved_method, /* do_rtp */ false)) { + if (!TryInlineAndReplace(invoke_instruction, resolved_method, /* do_rtp */ false)) { return false; } @@ -431,7 +432,7 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, HInstruction* cursor = invoke_instruction->GetPrevious(); HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); - if (!TryInline(invoke_instruction, actual_method, /* do_rtp */ false)) { + if (!TryInlineAndReplace(invoke_instruction, actual_method, /* do_rtp */ false)) { return false; } @@ -485,14 +486,29 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, return true; } -bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do_rtp) { +bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, ArtMethod* method, bool do_rtp) { + HInstruction* return_replacement = nullptr; + if (!TryBuildAndInline(invoke_instruction, method, &return_replacement)) { + return false; + } + if (return_replacement != nullptr) { + invoke_instruction->ReplaceWith(return_replacement); + } + invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction); + FixUpReturnReferenceType(invoke_instruction, method, return_replacement, do_rtp); + return true; +} + +bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, + ArtMethod* method, + HInstruction** return_replacement) { const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); // Check whether we're allowed to inline. The outermost compilation unit is the relevant // dex file here (though the transitivity of an inline chain would allow checking the calller). if (!compiler_driver_->MayInline(method->GetDexFile(), outer_compilation_unit_.GetDexFile())) { - if (TryPatternSubstitution(invoke_instruction, method, do_rtp)) { + if (TryPatternSubstitution(invoke_instruction, method, return_replacement)) { VLOG(compiler) << "Successfully replaced pattern of invoke " << PrettyMethod(method); MaybeRecordStat(kReplacedInvokeWithSimplePattern); return true; @@ -559,7 +575,7 @@ bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do return false; } - if (!TryBuildAndInline(method, invoke_instruction, same_dex_file, do_rtp)) { + if (!TryBuildAndInlineHelper(invoke_instruction, method, same_dex_file, return_replacement)) { return false; } @@ -586,27 +602,27 @@ static HInstruction* GetInvokeInputForArgVRegIndex(HInvoke* invoke_instruction, // Try to recognize known simple patterns and replace invoke call with appropriate instructions. bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction, ArtMethod* resolved_method, - bool do_rtp) { + HInstruction** return_replacement) { InlineMethod inline_method; if (!InlineMethodAnalyser::AnalyseMethodCode(resolved_method, &inline_method)) { return false; } - HInstruction* return_replacement = nullptr; switch (inline_method.opcode) { case kInlineOpNop: DCHECK_EQ(invoke_instruction->GetType(), Primitive::kPrimVoid); + *return_replacement = nullptr; break; case kInlineOpReturnArg: - return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, - inline_method.d.return_data.arg); + *return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, + inline_method.d.return_data.arg); break; case kInlineOpNonWideConst: if (resolved_method->GetShorty()[0] == 'L') { DCHECK_EQ(inline_method.d.data, 0u); - return_replacement = graph_->GetNullConstant(); + *return_replacement = graph_->GetNullConstant(); } else { - return_replacement = graph_->GetIntConstant(static_cast<int32_t>(inline_method.d.data)); + *return_replacement = graph_->GetIntConstant(static_cast<int32_t>(inline_method.d.data)); } break; case kInlineOpIGet: { @@ -621,7 +637,7 @@ bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction, DCHECK_EQ(iget->GetFieldOffset().Uint32Value(), data.field_offset); DCHECK_EQ(iget->IsVolatile() ? 1u : 0u, data.is_volatile); invoke_instruction->GetBlock()->InsertInstructionBefore(iget, invoke_instruction); - return_replacement = iget; + *return_replacement = iget; break; } case kInlineOpIPut: { @@ -639,7 +655,7 @@ bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction, invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction); if (data.return_arg_plus1 != 0u) { size_t return_arg = data.return_arg_plus1 - 1u; - return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, return_arg); + *return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, return_arg); } break; } @@ -694,19 +710,13 @@ bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction, HMemoryBarrier* barrier = new (graph_->GetArena()) HMemoryBarrier(kStoreStore, kNoDexPc); invoke_instruction->GetBlock()->InsertInstructionBefore(barrier, invoke_instruction); } + *return_replacement = nullptr; break; } default: LOG(FATAL) << "UNREACHABLE"; UNREACHABLE(); } - - if (return_replacement != nullptr) { - invoke_instruction->ReplaceWith(return_replacement); - } - invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction); - - FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp); return true; } @@ -760,10 +770,10 @@ HInstanceFieldSet* HInliner::CreateInstanceFieldSet(Handle<mirror::DexCache> dex return iput; } -bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, - HInvoke* invoke_instruction, - bool same_dex_file, - bool do_rtp) { +bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, + ArtMethod* resolved_method, + bool same_dex_file, + HInstruction** return_replacement) { ScopedObjectAccess soa(Thread::Current()); const DexFile::CodeItem* code_item = resolved_method->GetCodeItem(); const DexFile& callee_dex_file = *resolved_method->GetDexFile(); @@ -1016,16 +1026,12 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, } number_of_inlined_instructions_ += number_of_instructions; - HInstruction* return_replacement = callee_graph->InlineInto(graph_, invoke_instruction); - if (return_replacement != nullptr) { - DCHECK_EQ(graph_, return_replacement->GetBlock()->GetGraph()); - } - FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp); + *return_replacement = callee_graph->InlineInto(graph_, invoke_instruction); return true; } -void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method, - HInvoke* invoke_instruction, +void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction, + ArtMethod* resolved_method, HInstruction* return_replacement, bool do_rtp) { // Check the integrity of reference types and run another type propagation if needed. diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 7d343c62eb..9dd9bf5ad8 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -61,12 +61,25 @@ class HInliner : public HOptimization { bool TryInline(HInvoke* invoke_instruction); // Try to inline `resolved_method` in place of `invoke_instruction`. `do_rtp` is whether - // reference type propagation can run after the inlining. - bool TryInline(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp = true) + // reference type propagation can run after the inlining. If the inlining is successful, this + // method will replace and remove the `invoke_instruction`. + bool TryInlineAndReplace(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp) SHARED_REQUIRES(Locks::mutator_lock_); + bool TryBuildAndInline(HInvoke* invoke_instruction, + ArtMethod* resolved_method, + HInstruction** return_replacement) + SHARED_REQUIRES(Locks::mutator_lock_); + + bool TryBuildAndInlineHelper(HInvoke* invoke_instruction, + ArtMethod* resolved_method, + bool same_dex_file, + HInstruction** return_replacement); + // Try to recognize known simple patterns and replace invoke call with appropriate instructions. - bool TryPatternSubstitution(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp) + bool TryPatternSubstitution(HInvoke* invoke_instruction, + ArtMethod* resolved_method, + HInstruction** return_replacement) SHARED_REQUIRES(Locks::mutator_lock_); // Create a new HInstanceFieldGet. @@ -94,18 +107,13 @@ class HInliner : public HOptimization { const InlineCache& ic) SHARED_REQUIRES(Locks::mutator_lock_); - bool TryBuildAndInline(ArtMethod* resolved_method, - HInvoke* invoke_instruction, - bool same_dex_file, - bool do_rtp = true); - HInstanceFieldGet* BuildGetReceiverClass(ClassLinker* class_linker, HInstruction* receiver, uint32_t dex_pc) const SHARED_REQUIRES(Locks::mutator_lock_); - void FixUpReturnReferenceType(ArtMethod* resolved_method, - HInvoke* invoke_instruction, + void FixUpReturnReferenceType(HInvoke* invoke_instruction, + ArtMethod* resolved_method, HInstruction* return_replacement, bool do_rtp) SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index ab4f6f9d28..22cefe8aa5 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -261,7 +261,8 @@ static void CreateFloatToFloat(ArenaAllocator* arena, HInvoke* invoke) { locations->SetOut(Location::SameAsFirstInput()); HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect(); DCHECK(static_or_direct != nullptr); - if (invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { + if (static_or_direct->HasSpecialInput() && + invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { // We need addressibility for the constant area. locations->SetInAt(1, Location::RequiresRegister()); // We need a temporary to hold the constant. @@ -276,7 +277,7 @@ static void MathAbsFP(LocationSummary* locations, Location output = locations->Out(); DCHECK(output.IsFpuRegister()); - if (locations->InAt(1).IsValid()) { + if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) { DCHECK(locations->InAt(1).IsRegister()); // We also have a constant area pointer. Register constant_area = locations->InAt(1).AsRegister<Register>(); @@ -465,7 +466,7 @@ static void GenMinMaxFP(LocationSummary* locations, // NaN handling. __ Bind(&nan); // Do we have a constant area pointer? - if (locations->InAt(2).IsValid()) { + if (locations->GetInputCount() == 3 && locations->InAt(2).IsValid()) { DCHECK(locations->InAt(2).IsRegister()); Register constant_area = locations->InAt(2).AsRegister<Register>(); if (is_double) { @@ -510,7 +511,8 @@ static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { locations->SetOut(Location::SameAsFirstInput()); HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect(); DCHECK(static_or_direct != nullptr); - if (invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { + if (static_or_direct->HasSpecialInput() && + invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { locations->SetInAt(2, Location::RequiresRegister()); } } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index ca66f631a6..27015c0ac6 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2030,13 +2030,6 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { } } - if (return_value != nullptr) { - invoke->ReplaceWith(return_value); - } - - // Finally remove the invoke from the caller. - invoke->GetBlock()->RemoveInstruction(invoke); - return return_value; } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 18b256f48e..854854f238 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -345,8 +345,9 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { void ComputeTryBlockInformation(); // Inline this graph in `outer_graph`, replacing the given `invoke` instruction. - // Returns the instruction used to replace the invoke expression or null if the - // invoke is for a void method. + // Returns the instruction to replace the invoke expression or null if the + // invoke is for a void method. Note that the caller is responsible for replacing + // and removing the invoke instruction. HInstruction* InlineInto(HGraph* outer_graph, HInvoke* invoke); // Need to add a couple of blocks to test if the loop body is entered and @@ -3671,6 +3672,7 @@ class HInvokeStaticOrDirect : public HInvoke { // method pointer; otherwise there may be one platform-specific special input, // such as PC-relative addressing base. uint32_t GetSpecialInputIndex() const { return GetNumberOfArguments(); } + bool HasSpecialInput() const { return GetNumberOfArguments() != InputCount(); } InvokeType GetOptimizedInvokeType() const { return optimized_invoke_type_; } void SetOptimizedInvokeType(InvokeType invoke_type) { diff --git a/runtime/base/scoped_arena_containers.h b/runtime/base/scoped_arena_containers.h index 1236585afd..9b56856f11 100644 --- a/runtime/base/scoped_arena_containers.h +++ b/runtime/base/scoped_arena_containers.h @@ -201,22 +201,28 @@ inline ScopedArenaAllocatorAdapter<void> ScopedArenaAllocator::Adapter(ArenaAllo template <typename T> class ArenaDelete { static constexpr uint8_t kMagicFill = 0xCE; - public: - void operator()(T* ptr) const { - ptr->~T(); + protected: + // Used for variable sized objects such as RegisterLine. + ALWAYS_INLINE void ProtectMemory(T* ptr, size_t size) const { if (RUNNING_ON_MEMORY_TOOL > 0) { // Writing to the memory will fail if it we already destroyed the pointer with // DestroyOnlyDelete since we make it no access. - memset(ptr, kMagicFill, sizeof(T)); - MEMORY_TOOL_MAKE_NOACCESS(ptr, sizeof(T)); + memset(ptr, kMagicFill, size); + MEMORY_TOOL_MAKE_NOACCESS(ptr, size); } else if (kIsDebugBuild) { CHECK(ArenaStack::ArenaTagForAllocation(reinterpret_cast<void*>(ptr)) == ArenaFreeTag::kUsed) << "Freeing invalid object " << ptr; ArenaStack::ArenaTagForAllocation(reinterpret_cast<void*>(ptr)) = ArenaFreeTag::kFree; // Write a magic value to try and catch use after free error. - memset(ptr, kMagicFill, sizeof(T)); + memset(ptr, kMagicFill, size); } } + + public: + void operator()(T* ptr) const { + ptr->~T(); + ProtectMemory(ptr, sizeof(T)); + } }; // In general we lack support for arrays. We would need to call the destructor on each element, diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 9111ddf9f5..fc89bfd9b0 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -269,6 +269,14 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { } } } + for (auto it = osr_code_map_.begin(); it != osr_code_map_.end();) { + if (alloc.ContainsUnsafe(it->first)) { + // Note that the code has already been removed in the loop above. + it = osr_code_map_.erase(it); + } else { + ++it; + } + } for (auto it = profiling_infos_.begin(); it != profiling_infos_.end();) { ProfilingInfo* info = *it; if (alloc.ContainsUnsafe(info->GetMethod())) { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 1d31408cf0..a6cf9eaf86 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -1960,8 +1960,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { // We need to ensure the work line is consistent while performing validation. When we spot a // peephole pattern we compute a new line for either the fallthrough instruction or the // branch target. - ArenaUniquePtr<RegisterLine> branch_line; - ArenaUniquePtr<RegisterLine> fallthrough_line; + RegisterLineArenaUniquePtr branch_line; + RegisterLineArenaUniquePtr fallthrough_line; switch (inst->Opcode()) { case Instruction::NOP: @@ -4824,7 +4824,7 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, RegisterLine* merge_lin AdjustReturnLine(this, ret_inst, target_line); } } else { - ArenaUniquePtr<RegisterLine> copy; + RegisterLineArenaUniquePtr copy; if (kDebugVerify) { copy.reset(RegisterLine::Create(target_line->NumRegs(), this)); copy->CopyFromLine(target_line); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index c7d1e6bc90..b53a45cf41 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -30,6 +30,7 @@ #include "handle.h" #include "instruction_flags.h" #include "method_reference.h" +#include "register_line.h" #include "reg_type_cache.h" namespace art { @@ -45,6 +46,7 @@ namespace verifier { class DexPcToReferenceMap; class MethodVerifier; class RegisterLine; +using RegisterLineArenaUniquePtr = std::unique_ptr<RegisterLine, RegisterLineArenaDelete>; class RegType; /* @@ -127,7 +129,7 @@ class PcToRegisterLineTable { } private: - ScopedArenaVector<ArenaUniquePtr<RegisterLine>> register_lines_; + ScopedArenaVector<RegisterLineArenaUniquePtr> register_lines_; DISALLOW_COPY_AND_ASSIGN(PcToRegisterLineTable); }; @@ -771,14 +773,14 @@ class MethodVerifier { PcToRegisterLineTable reg_table_; // Storage for the register status we're currently working on. - ArenaUniquePtr<RegisterLine> work_line_; + RegisterLineArenaUniquePtr work_line_; // The address of the instruction we're currently working on, note that this is in 2 byte // quantities uint32_t work_insn_idx_; // Storage for the register status we're saving for later. - ArenaUniquePtr<RegisterLine> saved_line_; + RegisterLineArenaUniquePtr saved_line_; const uint32_t dex_method_idx_; // The method we're working on. // Its object representation if known. diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h index 330c06ab46..bfbb78f58a 100644 --- a/runtime/verifier/register_line-inl.h +++ b/runtime/verifier/register_line-inl.h @@ -185,9 +185,12 @@ inline void RegisterLine::VerifyMonitorStackEmpty(MethodVerifier* verifier) cons } } +inline size_t RegisterLine::ComputeSize(size_t num_regs) { + return OFFSETOF_MEMBER(RegisterLine, line_) + num_regs * sizeof(uint16_t); +} + inline RegisterLine* RegisterLine::Create(size_t num_regs, MethodVerifier* verifier) { - void* memory = verifier->GetArena().Alloc(OFFSETOF_MEMBER(RegisterLine, line_) + - (num_regs * sizeof(uint16_t))); + void* memory = verifier->GetArena().Alloc(ComputeSize(num_regs)); return new (memory) RegisterLine(num_regs, verifier); } @@ -200,6 +203,12 @@ inline RegisterLine::RegisterLine(size_t num_regs, MethodVerifier* verifier) SetResultTypeToUnknown(verifier); } +inline void RegisterLineArenaDelete::operator()(RegisterLine* ptr) const { + const size_t size = ptr != nullptr ? RegisterLine::ComputeSize(ptr->NumRegs()) : 0u; + ptr->~RegisterLine(); + ProtectMemory(ptr, size); +} + } // namespace verifier } // namespace art diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index b2f5555d8b..d50845421f 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -197,6 +197,9 @@ class RegisterLine { return num_regs_; } + // Return how many bytes of memory a register line uses. + ALWAYS_INLINE static size_t ComputeSize(size_t num_regs); + /* * Get the "this" pointer from a non-static method invocation. This returns the RegType so the * caller can decide whether it needs the reference to be initialized or not. (Can also return @@ -401,6 +404,13 @@ class RegisterLine { DISALLOW_COPY_AND_ASSIGN(RegisterLine); }; +class RegisterLineArenaDelete : public ArenaDelete<RegisterLine> { + public: + void operator()(RegisterLine* ptr) const; +}; + + + } // namespace verifier } // namespace art diff --git a/test/003-omnibus-opcodes/build b/test/003-omnibus-opcodes/build index faa298337c..56e87844c0 100644 --- a/test/003-omnibus-opcodes/build +++ b/test/003-omnibus-opcodes/build @@ -23,8 +23,8 @@ rm classes/UnresClass.class ${JAVAC} -d classes `find src2 -name '*.java'` if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} -JXmx256m --debug --dex --output=classes.dex classes fi diff --git a/test/005-annotations/build b/test/005-annotations/build index 057b351dab..93bee507df 100644 --- a/test/005-annotations/build +++ b/test/005-annotations/build @@ -29,8 +29,8 @@ rm 'classes/android/test/anno/MissingAnnotation.class' rm 'classes/android/test/anno/ClassWithInnerAnnotationClass$MissingInnerAnnotationClass.class' if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} -JXmx256m --debug --dex --output=classes.dex classes fi diff --git a/test/022-interface/build b/test/022-interface/build index 3f8915c27e..5cfc7f25b7 100644 --- a/test/022-interface/build +++ b/test/022-interface/build @@ -20,8 +20,8 @@ set -e # Use classes that are compiled with ecj that exposes an invokeinterface # issue when interfaces override methods in Object if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes fi diff --git a/test/085-old-style-inner-class/build b/test/085-old-style-inner-class/build index 6f50a76863..21dc66269d 100644 --- a/test/085-old-style-inner-class/build +++ b/test/085-old-style-inner-class/build @@ -23,8 +23,8 @@ mkdir classes ${JAVAC} -source 1.4 -target 1.4 -d classes `find src -name '*.java'` if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else # Suppress stderr to keep the inner class warnings out of the expected output. ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes 2>/dev/null diff --git a/test/091-override-package-private-method/build b/test/091-override-package-private-method/build index 5a340dcf6d..073a4ba9bc 100755 --- a/test/091-override-package-private-method/build +++ b/test/091-override-package-private-method/build @@ -24,14 +24,12 @@ mkdir classes-ex mv classes/OverridePackagePrivateMethodSuper.class classes-ex if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes-ex --output classes-ex.jack + jar cf classes.jill.jar -C classes . + jar cf classes-ex.jill.jar -C classes-ex . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jack --output-dex . + ${JACK} --import classes-ex.jill.jar --output-dex . zip ${TEST_NAME}-ex.jar classes.dex else if [ ${NEED_DEX} = "true" ]; then diff --git a/test/097-duplicate-method/build b/test/097-duplicate-method/build index a8558739de..4525549905 100644 --- a/test/097-duplicate-method/build +++ b/test/097-duplicate-method/build @@ -23,10 +23,10 @@ if [ ${USE_JACK} = "true" ]; then ${JACK} --output-jack src.jack src ${JASMIN} -d classes src/*.j - ${JILL} classes --output jasmin.jack + jar cf jasmin.jill.jar -C classes . # We set jack.import.type.policy=keep-first to consider class definitions from jasmin first. - ${JACK} --import jasmin.jack --import src.jack -D jack.import.type.policy=keep-first --output-dex . + ${JACK} --import jasmin.jill.jar --import src.jack -D jack.import.type.policy=keep-first --output-dex . else ${JAVAC} -d classes src/*.java ${JASMIN} -d classes src/*.j diff --git a/test/111-unresolvable-exception/build b/test/111-unresolvable-exception/build index e772fb812f..58ac26d836 100644 --- a/test/111-unresolvable-exception/build +++ b/test/111-unresolvable-exception/build @@ -22,8 +22,8 @@ ${JAVAC} -d classes `find src -name '*.java'` rm classes/TestException.class if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes fi diff --git a/test/113-multidex/build b/test/113-multidex/build index 8ef5c0eb0f..4557ccd22a 100644 --- a/test/113-multidex/build +++ b/test/113-multidex/build @@ -28,14 +28,12 @@ ${JAVAC} -d classes2 `find src -name '*.java'` rm classes2/Second.class classes2/FillerA.class classes2/FillerB.class classes2/Inf*.class if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes2 --output classes2.jack + jar cf classes.jill.jar -C classes . + jar cf classes2.jill.jar -C classes2 . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . mv classes.dex classes-1.dex - ${JACK} --import classes2.jack --output-dex . + ${JACK} --import classes2.jill.jar --output-dex . mv classes.dex classes2.dex mv classes-1.dex classes.dex else diff --git a/test/121-modifiers/build b/test/121-modifiers/build index 85b69e92a6..771dd51829 100644 --- a/test/121-modifiers/build +++ b/test/121-modifiers/build @@ -31,9 +31,9 @@ set -e # mv Main.class A.class A\$B.class A\$C.class classes/ if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack + jar cf classes.jill.jar -C classes . # Workaround b/19561685: disable sanity checks to produce a DEX file with invalid modifiers. - ${JACK} --sanity-checks off --import classes.jack --output-dex . + ${JACK} --sanity-checks off --import classes.jill.jar --output-dex . else ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes fi diff --git a/test/124-missing-classes/build b/test/124-missing-classes/build index b92ecf9382..0a340a26d6 100644 --- a/test/124-missing-classes/build +++ b/test/124-missing-classes/build @@ -27,8 +27,8 @@ rm 'classes/MissingClass.class' rm 'classes/Main$MissingInnerClass.class' if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} -JXmx256m --debug --dex --output=classes.dex classes fi diff --git a/test/126-miranda-multidex/build b/test/126-miranda-multidex/build index b7f2118d2f..00b9ba0ac2 100644 --- a/test/126-miranda-multidex/build +++ b/test/126-miranda-multidex/build @@ -28,14 +28,12 @@ ${JAVAC} -d classes2 `find src -name '*.java'` rm classes2/Main.class classes2/MirandaAbstract.class classes2/MirandaClass*.class classes2/MirandaInterface2*.class if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes2 --output classes2.jack + jar cf classes.jill.jar -C classes . + jar cf classes2.jill.jar -C classes2 . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . mv classes.dex classes-1.dex - ${JACK} --import classes2.jack --output-dex . + ${JACK} --import classes2.jill.jar --output-dex . mv classes.dex classes2.dex mv classes-1.dex classes.dex else diff --git a/test/127-checker-secondarydex/build b/test/127-checker-secondarydex/build index 0d9f4d6291..7ce46acfed 100755 --- a/test/127-checker-secondarydex/build +++ b/test/127-checker-secondarydex/build @@ -24,14 +24,12 @@ mkdir classes-ex mv classes/Super.class classes-ex if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes-ex --output classes-ex.jack + jar cf classes.jill.jar -C classes . + jar cf classes-ex.jill.jar -C classes-ex . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jack --output-dex . + ${JACK} --import classes-ex.jill.jar --output-dex . zip ${TEST_NAME}-ex.jar classes.dex else if [ ${NEED_DEX} = "true" ]; then diff --git a/test/529-checker-unresolved/build b/test/529-checker-unresolved/build index 8c3c4f8952..d85035b669 100644 --- a/test/529-checker-unresolved/build +++ b/test/529-checker-unresolved/build @@ -29,14 +29,12 @@ mv classes/UnresolvedInterface.class classes-ex mv classes/UnresolvedSuperClass.class classes-ex if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes-ex --output classes-ex.jack + jar cf classes.jill.jar -C classes . + jar cf classes-ex.jill.jar -C classes-ex . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jack --output-dex . + ${JACK} --import classes-ex.jill.jar --output-dex . zip ${TEST_NAME}-ex.jar classes.dex else if [ ${NEED_DEX} = "true" ]; then diff --git a/test/574-irreducible-and-constant-area/expected.txt b/test/574-irreducible-and-constant-area/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/574-irreducible-and-constant-area/expected.txt diff --git a/test/574-irreducible-and-constant-area/info.txt b/test/574-irreducible-and-constant-area/info.txt new file mode 100644 index 0000000000..e957a5ae6b --- /dev/null +++ b/test/574-irreducible-and-constant-area/info.txt @@ -0,0 +1,3 @@ +Regression test for intrinsics on x86, which used to wrongly assume +a HInvokeStaticOrDirect must have a special input (does not apply for irreducible +loops). diff --git a/test/574-irreducible-and-constant-area/run b/test/574-irreducible-and-constant-area/run new file mode 100755 index 0000000000..ffdbcc9cf2 --- /dev/null +++ b/test/574-irreducible-and-constant-area/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# 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. + +# Don't do relocation, as this affects this test. +exec ${RUN} "$@" --no-relocate diff --git a/test/574-irreducible-and-constant-area/smali/IrreducibleLoop.smali b/test/574-irreducible-and-constant-area/smali/IrreducibleLoop.smali new file mode 100644 index 0000000000..d7d43466d1 --- /dev/null +++ b/test/574-irreducible-and-constant-area/smali/IrreducibleLoop.smali @@ -0,0 +1,35 @@ +# 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; + +.method public static simpleLoop(I)I + .registers 5 + const/16 v0, 42 + const/16 v1, 42 + const-wide/high16 v2, 0x4000000000000000L + if-eq p0, v0, :other_loop_entry + :loop_entry + invoke-static {v1, v1}, LMain;->$inline$foo(FF)V + invoke-static {v2, v3, v2, v3}, LMain;->$inline$foo(DD)V + if-ne p0, v0, :exit + add-int v0, v0, v0 + :other_loop_entry + add-int v0, v0, v0 + goto :loop_entry + :exit + return v0 +.end method diff --git a/test/574-irreducible-and-constant-area/src/Main.java b/test/574-irreducible-and-constant-area/src/Main.java new file mode 100644 index 0000000000..3cdd92456c --- /dev/null +++ b/test/574-irreducible-and-constant-area/src/Main.java @@ -0,0 +1,41 @@ +/* + * 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.Method; + +public class Main { + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String[] args) throws Exception { + Class<?> c = Class.forName("IrreducibleLoop"); + Method m = c.getMethod("simpleLoop", int.class); + Object[] arguments = { 42 }; + m.invoke(null, arguments); + } + + public static void $inline$foo(float a, float b) { + Math.abs(a); + Math.max(a, b); + Math.min(a, b); + } + + public static void $inline$foo(double a, double b) { + Math.abs(a); + Math.max(a, b); + Math.min(a, b); + } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index b3560b634b..364be59919 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -42,8 +42,7 @@ TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES := ifeq ($(ANDROID_COMPILE_WITH_JACK),true) TEST_ART_RUN_TEST_DEPENDENCIES += \ - $(JACK) \ - $(JILL_JAR) + $(JACK) TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES += setup-jack-server endif @@ -72,8 +71,8 @@ $$(dmart_target): $(TEST_ART_RUN_TEST_DEPENDENCIES) $(TARGET_JACK_CLASSPATH_DEPE DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \ JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK=$(abspath $(JACK)) \ + JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK_CLASSPATH=$(TARGET_JACK_CLASSPATH) \ - JILL_JAR=$(abspath $(JILL_JAR)) \ $(LOCAL_PATH)/run-test $$(PRIVATE_RUN_TEST_OPTIONS) --output-path $$(abspath $$(dir $$@)) $(1) $(hide) touch $$@ @@ -962,8 +961,8 @@ $$(run_test_rule_name): $(TEST_ART_RUN_TEST_DEPENDENCIES) $(HOST_OUT_EXECUTABLES DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \ JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK=$(abspath $(JACK)) \ + JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK_CLASSPATH=$$(PRIVATE_JACK_CLASSPATH) \ - JILL_JAR=$(abspath $(JILL_JAR)) \ art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(12) \ && $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@) $$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \ diff --git a/test/run-test b/test/run-test index faa597e1ca..f1875d71a5 100755 --- a/test/run-test +++ b/test/run-test @@ -88,13 +88,7 @@ if [ -z "$JACK_CLASSPATH" ]; then export JACK_CLASSPATH="${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack" fi -# If JILL_JAR is not set, assume it is located in the prebuilts directory. -if [ -z "$JILL_JAR" ]; then - export JILL_JAR="$ANDROID_BUILD_TOP/prebuilts/sdk/tools/jill.jar" -fi - export JACK="$JACK -g -cp $JACK_CLASSPATH" -export JILL="java -jar $JILL_JAR" info="info.txt" build="build" diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt index da5225c066..d9b26bcf58 100644 --- a/tools/ahat/README.txt +++ b/tools/ahat/README.txt @@ -78,6 +78,7 @@ Things to move to perflib: Release History: 0.4 Pending + Annotate char[] objects with their string values. Show registered native allocations for heap dumps that support it. 0.3 Dec 15, 2015 diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java index 8b7f9ea41a..d7b64e2afd 100644 --- a/tools/ahat/src/InstanceUtils.java +++ b/tools/ahat/src/InstanceUtils.java @@ -76,11 +76,15 @@ class InstanceUtils { * If maxChars is negative, the returned string is not truncated. */ public static String asString(Instance inst, int maxChars) { - if (!isInstanceOfClass(inst, "java.lang.String")) { - return null; + // The inst object could either be a java.lang.String or a char[]. If it + // is a char[], use that directly as the value, otherwise use the value + // field of the string object. The field accesses for count and offset + // later on will work okay regardless of what type the inst object is. + Object value = inst; + if (isInstanceOfClass(inst, "java.lang.String")) { + value = getField(inst, "value"); } - Object value = getField(inst, "value"); if (!(value instanceof ArrayInstance)) { return null; } diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java index 701d60ef1f..d61a98da54 100644 --- a/tools/ahat/test-dump/Main.java +++ b/tools/ahat/test-dump/Main.java @@ -35,6 +35,7 @@ public class Main { // class and reading the desired field. public static class DumpedStuff { public String basicString = "hello, world"; + public char[] charArray = "char thing".toCharArray(); public String nullString = null; public Object anObject = new Object(); public ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>(); diff --git a/tools/ahat/test/InstanceUtilsTest.java b/tools/ahat/test/InstanceUtilsTest.java index 32f48ce560..59b1c90e10 100644 --- a/tools/ahat/test/InstanceUtilsTest.java +++ b/tools/ahat/test/InstanceUtilsTest.java @@ -32,6 +32,13 @@ public class InstanceUtilsTest { } @Test + public void asStringCharArray() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance str = (Instance)dump.getDumpedThing("charArray"); + assertEquals("char thing", InstanceUtils.asString(str)); + } + + @Test public void asStringTruncated() throws IOException { TestDump dump = TestDump.getTestDump(); Instance str = (Instance)dump.getDumpedThing("basicString"); @@ -39,6 +46,13 @@ public class InstanceUtilsTest { } @Test + public void asStringCharArrayTruncated() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance str = (Instance)dump.getDumpedThing("charArray"); + assertEquals("char ", InstanceUtils.asString(str, 5)); + } + + @Test public void asStringExactMax() throws IOException { TestDump dump = TestDump.getTestDump(); Instance str = (Instance)dump.getDumpedThing("basicString"); @@ -46,6 +60,13 @@ public class InstanceUtilsTest { } @Test + public void asStringCharArrayExactMax() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance str = (Instance)dump.getDumpedThing("charArray"); + assertEquals("char thing", InstanceUtils.asString(str, 10)); + } + + @Test public void asStringNotTruncated() throws IOException { TestDump dump = TestDump.getTestDump(); Instance str = (Instance)dump.getDumpedThing("basicString"); @@ -53,6 +74,13 @@ public class InstanceUtilsTest { } @Test + public void asStringCharArrayNotTruncated() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance str = (Instance)dump.getDumpedThing("charArray"); + assertEquals("char thing", InstanceUtils.asString(str, 50)); + } + + @Test public void asStringNegativeMax() throws IOException { TestDump dump = TestDump.getTestDump(); Instance str = (Instance)dump.getDumpedThing("basicString"); @@ -60,6 +88,13 @@ public class InstanceUtilsTest { } @Test + public void asStringCharArrayNegativeMax() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance str = (Instance)dump.getDumpedThing("charArray"); + assertEquals("char thing", InstanceUtils.asString(str, -3)); + } + + @Test public void asStringNull() throws IOException { TestDump dump = TestDump.getTestDump(); Instance obj = (Instance)dump.getDumpedThing("nullString"); |